Add camera and render systems
This commit is contained in:
parent
65816fd427
commit
6c76e9e5cc
14
game/game.go
14
game/game.go
|
@ -1,6 +1,7 @@
|
|||
package game
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
|
@ -16,6 +17,13 @@ type Game struct{}
|
|||
func NewGame() (*Game, error) {
|
||||
g := &Game{}
|
||||
|
||||
redSprite := ebiten.NewImage(16, 16)
|
||||
redSprite.Fill(color.RGBA{255, 0, 0, 255})
|
||||
|
||||
greenSprite := ebiten.NewImage(16, 16)
|
||||
greenSprite.Fill(color.RGBA{0, 255, 0, 255})
|
||||
|
||||
gohan.AddSystem(&system.HandleInput{})
|
||||
gohan.AddSystem(&system.RenderEnvironment{})
|
||||
gohan.AddSystem(&system.RenderDebug{})
|
||||
|
||||
|
@ -23,6 +31,12 @@ func NewGame() (*Game, error) {
|
|||
once := gohan.NewEntity()
|
||||
once.AddComponent(&component.Once{})
|
||||
|
||||
// TODO populate map for testing
|
||||
world.Map = world.NewGameMap(128, 128)
|
||||
world.Map[128].Sprite = greenSprite
|
||||
world.Map[129].Sprite = redSprite
|
||||
world.Map[130].Sprite = greenSprite
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
|
|
1
main.go
1
main.go
|
@ -20,7 +20,6 @@ func main() {
|
|||
ebiten.SetFPSMode(ebiten.FPSModeVsyncOn)
|
||||
ebiten.SetTPS(world.TPS)
|
||||
ebiten.SetRunnableOnUnfocused(true)
|
||||
ebiten.SetCursorMode(ebiten.CursorModeHidden)
|
||||
|
||||
parseFlags()
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"code.rocketnine.space/tslocum/commandeuropa/component"
|
||||
"code.rocketnine.space/tslocum/commandeuropa/world"
|
||||
"code.rocketnine.space/tslocum/gohan"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
type HandleInput struct {
|
||||
Once *component.Once
|
||||
}
|
||||
|
||||
func (r *HandleInput) Update(e gohan.Entity) error {
|
||||
const panDistance = 3
|
||||
|
||||
// Pan with keyboard.
|
||||
if ebiten.IsKeyPressed(ebiten.KeyLeft) {
|
||||
world.CamX -= panDistance
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyRight) {
|
||||
world.CamX += panDistance
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyUp) {
|
||||
world.CamY -= panDistance
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyDown) {
|
||||
world.CamY += panDistance
|
||||
}
|
||||
|
||||
// Zoom with mouse.
|
||||
const minZoom = 1
|
||||
const maxZoom = 7
|
||||
_, scroll := ebiten.Wheel()
|
||||
if scroll < 0 {
|
||||
world.CamScale -= 1
|
||||
if world.CamScale < minZoom {
|
||||
world.CamScale = minZoom
|
||||
}
|
||||
} else if scroll > 0 {
|
||||
world.CamScale += 1
|
||||
if world.CamScale > maxZoom {
|
||||
world.CamScale = maxZoom
|
||||
}
|
||||
}
|
||||
|
||||
// Pan with mouse.
|
||||
const panArea = 7
|
||||
x, y := ebiten.CursorPosition()
|
||||
if x <= panArea {
|
||||
world.CamX -= panDistance
|
||||
} else if x >= world.ScreenWidth-panArea {
|
||||
world.CamX += panDistance
|
||||
}
|
||||
if y <= panArea {
|
||||
world.CamY -= panDistance
|
||||
} else if y >= world.ScreenHeight-panArea {
|
||||
world.CamY += panDistance
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *HandleInput) Draw(e gohan.Entity, screen *ebiten.Image) error {
|
||||
return gohan.ErrUnregister
|
||||
}
|
|
@ -1,15 +1,21 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"code.rocketnine.space/tslocum/commandeuropa/component"
|
||||
"code.rocketnine.space/tslocum/commandeuropa/world"
|
||||
"code.rocketnine.space/tslocum/gohan"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
type RenderEnvironment struct {
|
||||
Once *component.Once
|
||||
|
||||
op *ebiten.DrawImageOptions
|
||||
initialized bool
|
||||
}
|
||||
|
||||
func (r *RenderEnvironment) Initialize() {
|
||||
r.op = &ebiten.DrawImageOptions{}
|
||||
}
|
||||
|
||||
func (r *RenderEnvironment) Update(e gohan.Entity) error {
|
||||
|
@ -17,6 +23,50 @@ func (r *RenderEnvironment) Update(e gohan.Entity) error {
|
|||
}
|
||||
|
||||
func (r *RenderEnvironment) Draw(e gohan.Entity, screen *ebiten.Image) error {
|
||||
screen.Fill(color.RGBA{0, 0, 255, 255})
|
||||
if !r.initialized {
|
||||
r.Initialize()
|
||||
}
|
||||
|
||||
x, y := -1, 0
|
||||
for _, t := range world.Map {
|
||||
x++
|
||||
if x == 128 {
|
||||
y++
|
||||
x = 0
|
||||
}
|
||||
|
||||
drawX, drawY := world.LevelCoordinatesToScreen(x, y)
|
||||
|
||||
// Skip drawing off-screen tiles.
|
||||
padding := world.TileWidth * world.CamScale
|
||||
width, height := world.TileWidth*world.CamScale, world.TileWidth*world.CamScale
|
||||
left := drawX
|
||||
right := drawX + width
|
||||
top := drawY
|
||||
bottom := drawY + height
|
||||
if (left < -padding || left > world.ScreenWidth+padding) || (top < -padding || top > world.ScreenHeight+padding) ||
|
||||
(right < -padding || right > world.ScreenWidth+padding) || (bottom < -padding || bottom > world.ScreenHeight+padding) {
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
r.renderTile(t, x, y, screen)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RenderEnvironment) renderTile(t world.MapTile, x int, y int, target *ebiten.Image) int {
|
||||
r.op.GeoM.Reset()
|
||||
|
||||
// Move to current position.
|
||||
r.op.GeoM.Translate(float64(x*16), float64(y*16))
|
||||
// Translate camera position.
|
||||
r.op.GeoM.Translate(float64(-world.CamX), float64(-world.CamY))
|
||||
// Zoom.
|
||||
r.op.GeoM.Scale(float64(world.CamScale), float64(world.CamScale))
|
||||
// Center.
|
||||
r.op.GeoM.Translate(float64(world.ScreenWidth/2), float64(world.ScreenHeight/2))
|
||||
|
||||
target.DrawImage(t.Sprite, r.op)
|
||||
return 1
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package world
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
type MapTile struct {
|
||||
Sprite *ebiten.Image
|
||||
}
|
||||
|
||||
func NewGameMap(w, h int) []MapTile {
|
||||
tiles := make([]MapTile, w*h)
|
||||
for i := range tiles {
|
||||
tiles[i].Sprite = blankSprite
|
||||
}
|
||||
return tiles
|
||||
}
|
|
@ -1,10 +1,24 @@
|
|||
package world
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
const TPS = 144
|
||||
|
||||
const TileWidth = 16
|
||||
|
||||
var (
|
||||
ScreenWidth, ScreenHeight = 800, 600
|
||||
|
||||
CamX, CamY = 0, 0
|
||||
|
||||
CamScale = 1
|
||||
|
||||
Map []MapTile
|
||||
|
||||
Debug int
|
||||
|
||||
StartMuted bool
|
||||
|
@ -13,3 +27,13 @@ var (
|
|||
|
||||
DisableEsc bool
|
||||
)
|
||||
|
||||
var blankSprite = ebiten.NewImage(16, 16)
|
||||
|
||||
func init() {
|
||||
blankSprite.Fill(color.RGBA{0, 0, 255, 255})
|
||||
}
|
||||
|
||||
func LevelCoordinatesToScreen(x, y int) (int, int) {
|
||||
return (x*16-CamX)*CamScale + ScreenWidth/2, (y*16-CamY)*CamScale + ScreenHeight/2
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue