Add camera and render systems

This commit is contained in:
Trevor Slocum 2022-11-09 16:36:36 -08:00
parent 65816fd427
commit 6c76e9e5cc
6 changed files with 173 additions and 4 deletions

View File

@ -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
}

View File

@ -20,7 +20,6 @@ func main() {
ebiten.SetFPSMode(ebiten.FPSModeVsyncOn)
ebiten.SetTPS(world.TPS)
ebiten.SetRunnableOnUnfocused(true)
ebiten.SetCursorMode(ebiten.CursorModeHidden)
parseFlags()

65
system/handleinput.go Normal file
View File

@ -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
}

View File

@ -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
}

17
world/map.go Normal file
View File

@ -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
}

View File

@ -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
}