From d033c63e18c385433bfb01d7690a0925d5531b9f Mon Sep 17 00:00:00 2001 From: nicholas-eden Date: Mon, 16 Dec 2019 08:04:39 -0800 Subject: [PATCH] Initial in-game ui and keyboard controls (#254) Move player movement out of `Game`, add arrow key movement. Render bottom panel. --- d2core/d2scene/game.go | 15 ++-- d2core/engine.go | 10 ++- d2player/game_controls.go | 177 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 10 deletions(-) create mode 100644 d2player/game_controls.go diff --git a/d2core/d2scene/game.go b/d2core/d2scene/game.go index 2e43395a..628d5899 100644 --- a/d2core/d2scene/game.go +++ b/d2core/d2scene/game.go @@ -1,8 +1,6 @@ package d2scene import ( - "image/color" - "github.com/OpenDiablo2/D2Shared/d2common/d2enum" "github.com/OpenDiablo2/D2Shared/d2common/d2interface" "github.com/OpenDiablo2/D2Shared/d2common/d2resource" @@ -11,10 +9,12 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2core" "github.com/OpenDiablo2/OpenDiablo2/d2corecommon/d2coreinterface" + "github.com/OpenDiablo2/OpenDiablo2/d2player" "github.com/OpenDiablo2/OpenDiablo2/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine" "github.com/OpenDiablo2/OpenDiablo2/d2render/d2ui" "github.com/hajimehoshi/ebiten" + "image/color" ) type Game struct { @@ -28,6 +28,7 @@ type Game struct { testLabel d2ui.Label mapEngine *d2mapengine.MapEngine hero *d2core.Hero + gameControls *d2player.GameControls } func CreateGame( @@ -85,6 +86,10 @@ func (v *Game) Load() []func() { ) v.mapEngine.AddEntity(v.hero) }, + func() { + v.gameControls = d2player.NewGameControls(v.fileProvider, v.hero, v.mapEngine) + v.gameControls.Load() + }, } } @@ -94,6 +99,7 @@ func (v *Game) Unload() { func (v Game) Render(screen *ebiten.Image) { screen.Fill(color.Black) v.mapEngine.Render(screen) + v.gameControls.Render(screen) } func (v *Game) Update(tickTime float64) { @@ -102,8 +108,5 @@ func (v *Game) Update(tickTime float64) { rx, ry := v.mapEngine.WorldToOrtho(v.hero.AnimatedEntity.LocationX/5, v.hero.AnimatedEntity.LocationY/5) v.mapEngine.MoveCameraTo(rx, ry) - if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { - px, py := v.mapEngine.ScreenToWorld(ebiten.CursorPosition()) - v.hero.AnimatedEntity.SetTarget(px*5, py*5, 1) - } + v.gameControls.Move(tickTime) } diff --git a/d2core/engine.go b/d2core/engine.go index 2bb93825..f194fa6e 100644 --- a/d2core/engine.go +++ b/d2core/engine.go @@ -242,10 +242,12 @@ func (v Engine) Draw(screen *ebiten.Image) { ebitenutil.DebugPrintAt(screen, "vsync:"+strconv.FormatBool(ebiten.IsVsyncEnabled())+"\nFPS:"+strconv.Itoa(int(ebiten.CurrentFPS())), 5, 565) var m runtime.MemStats runtime.ReadMemStats(&m) - ebitenutil.DebugPrintAt(screen, "Alloc "+strconv.FormatInt(int64(m.Alloc)/1024/1024, 10), 700, 0) - ebitenutil.DebugPrintAt(screen, "Pause "+strconv.FormatInt(int64(m.PauseTotalNs/1024/1024), 10), 700, 10) - ebitenutil.DebugPrintAt(screen, "HeapSys "+strconv.FormatInt(int64(m.HeapSys/1024/1024), 10), 700, 20) - ebitenutil.DebugPrintAt(screen, "NumGC "+strconv.FormatInt(int64(m.NumGC), 10), 700, 30) + ebitenutil.DebugPrintAt(screen, "Alloc "+strconv.FormatInt(int64(m.Alloc)/1024/1024, 10), 680, 0) + ebitenutil.DebugPrintAt(screen, "Pause "+strconv.FormatInt(int64(m.PauseTotalNs/1024/1024), 10), 680, 10) + ebitenutil.DebugPrintAt(screen, "HeapSys "+strconv.FormatInt(int64(m.HeapSys/1024/1024), 10), 680, 20) + ebitenutil.DebugPrintAt(screen, "NumGC "+strconv.FormatInt(int64(m.NumGC), 10), 680, 30) + cx, cy := ebiten.CursorPosition() + ebitenutil.DebugPrintAt(screen, "Coords "+strconv.FormatInt(int64(cx), 10) + ","+strconv.FormatInt(int64(cy), 10), 680, 40) } } diff --git a/d2player/game_controls.go b/d2player/game_controls.go new file mode 100644 index 00000000..2e190af4 --- /dev/null +++ b/d2player/game_controls.go @@ -0,0 +1,177 @@ +package d2player + +import ( + "github.com/OpenDiablo2/D2Shared/d2common/d2enum" + "github.com/OpenDiablo2/D2Shared/d2common/d2interface" + "github.com/OpenDiablo2/D2Shared/d2common/d2resource" + "github.com/OpenDiablo2/D2Shared/d2data/d2datadict" + "github.com/OpenDiablo2/D2Shared/d2data/d2dc6" + "github.com/OpenDiablo2/OpenDiablo2/d2core" + "github.com/OpenDiablo2/OpenDiablo2/d2render" + "github.com/OpenDiablo2/OpenDiablo2/d2render/d2mapengine" + "github.com/hajimehoshi/ebiten" + "log" +) + +type GameControls struct { + fileProvider d2interface.FileProvider + hero *d2core.Hero + mapEngine *d2mapengine.MapEngine + + // UI + globeSprite *d2render.Sprite + mainPanel *d2render.Sprite + menuButton *d2render.Sprite + skillIcon *d2render.Sprite +} + +func NewGameControls(fileProvider d2interface.FileProvider, hero *d2core.Hero, mapEngine *d2mapengine.MapEngine) *GameControls { + return &GameControls{ + fileProvider: fileProvider, + hero: hero, + mapEngine: mapEngine, + } +} + +func (g *GameControls) Move(tickTime float64) { + + if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { + px, py := g.mapEngine.ScreenToWorld(ebiten.CursorPosition()) + g.hero.AnimatedEntity.SetTarget(px*5, py*5, 1) + } + + arrowDistance := 1.0 + moveX := 0.0 + moveY := 0.0 + if ebiten.IsKeyPressed(ebiten.KeyW) || ebiten.IsKeyPressed(ebiten.KeyUp) { + moveY -= arrowDistance + moveX -= arrowDistance + } + if ebiten.IsKeyPressed(ebiten.KeyS) || ebiten.IsKeyPressed(ebiten.KeyDown) { + moveY += arrowDistance + moveX += arrowDistance + } + if ebiten.IsKeyPressed(ebiten.KeyA) || ebiten.IsKeyPressed(ebiten.KeyLeft) { + moveY += arrowDistance + moveX -= arrowDistance + } + if ebiten.IsKeyPressed(ebiten.KeyD) || ebiten.IsKeyPressed(ebiten.KeyRight) { + moveY -= arrowDistance + moveX += arrowDistance + } + + if moveY != 0 || moveX != 0 { + g.hero.AnimatedEntity.SetTarget(g.hero.AnimatedEntity.LocationX+moveX, g.hero.AnimatedEntity.LocationY+moveY, 1) + } + +} + +func (g *GameControls) Load() { + dc6, err := d2dc6.LoadDC6(g.fileProvider.LoadFile(d2resource.GameGlobeOverlap), d2datadict.Palettes[d2enum.Sky]) + if err != nil { + log.Panicf("failed to load %s: %v", d2resource.GameGlobeOverlap, err) + } + globeSprite := d2render.CreateSpriteFromDC6(dc6) + g.globeSprite = &globeSprite + + dc6, err = d2dc6.LoadDC6(g.fileProvider.LoadFile(d2resource.GamePanels), d2datadict.Palettes[d2enum.Sky]) + if err != nil { + log.Panicf("failed to load %s: %v", d2resource.GamePanels, err) + } + mainPanel := d2render.CreateSpriteFromDC6(dc6) + g.mainPanel = &mainPanel + + dc6, err = d2dc6.LoadDC6(g.fileProvider.LoadFile(d2resource.MenuButton), d2datadict.Palettes[d2enum.Sky]) + if err != nil { + log.Panicf("failed to load %s: %v", d2resource.MenuButton, err) + } + menuButton := d2render.CreateSpriteFromDC6(dc6) + g.menuButton = &menuButton + + dc6, err = d2dc6.LoadDC6(g.fileProvider.LoadFile(d2resource.GenericSkills), d2datadict.Palettes[d2enum.Sky]) + if err != nil { + log.Panicf("failed to load %s: %v", d2resource.GenericSkills, err) + } + skillIcon := d2render.CreateSpriteFromDC6(dc6) + g.skillIcon = &skillIcon + +} + + +// TODO: consider caching the panels to single image that is reused. +func (g *GameControls) Render(target *ebiten.Image) { + width, height := target.Size() + offset := uint32(0) + + // Left globe holder + g.mainPanel.Frame = 0 + w, _ := g.mainPanel.GetSize() + g.mainPanel.MoveTo(int(offset), height) + g.mainPanel.Draw(target) + + // Left globe + g.globeSprite.Frame = 0 + g.globeSprite.MoveTo(int(offset+28), height - 5) + g.globeSprite.Draw(target) + offset += w + + // Left skill + g.skillIcon.Frame = 2 + w, _ = g.skillIcon.GetSize() + g.skillIcon.MoveTo(int(offset), height) + g.skillIcon.Draw(target) + offset += w + + // Left skill selector + g.mainPanel.Frame = 1 + w, _ = g.mainPanel.GetSize() + g.mainPanel.MoveTo(int(offset), height) + g.mainPanel.Draw(target) + offset += w + + // Stamina + g.mainPanel.Frame = 2 + w, _ = g.mainPanel.GetSize() + g.mainPanel.MoveTo(int(offset), height) + g.mainPanel.Draw(target) + offset += w + + // Center menu button + g.menuButton.Frame = 0 + w, _ = g.mainPanel.GetSize() + g.menuButton.MoveTo((width / 2) - 8 , height - 16) + g.menuButton.Draw(target) + + // Potions + g.mainPanel.Frame = 3 + w, _ = g.mainPanel.GetSize() + g.mainPanel.MoveTo(int(offset), height) + g.mainPanel.Draw(target) + offset += w + + // Right skill selector + g.mainPanel.Frame = 4 + w, _ = g.mainPanel.GetSize() + g.mainPanel.MoveTo(int(offset), height) + g.mainPanel.Draw(target) + offset += w + + // Right skill + g.skillIcon.Frame = 10 + w, _ = g.skillIcon.GetSize() + g.skillIcon.MoveTo(int(offset), height) + g.skillIcon.Draw(target) + offset += w + + // Right globe holder + g.mainPanel.Frame = 5 + w, _ = g.mainPanel.GetSize() + g.mainPanel.MoveTo(int(offset), height) + g.mainPanel.Draw(target) + + // Right globe + g.globeSprite.Frame = 1 + g.globeSprite.MoveTo(int(offset) + 8, height - 8) + g.globeSprite.Draw(target) + +}