Refactor to use better interfaces and event callbacks.

This commit is contained in:
2024-11-15 16:11:45 -05:00
parent 4ced75d66c
commit cbc4ba5eb3
16 changed files with 1009 additions and 14 deletions

View File

@@ -62,6 +62,7 @@ func NewGame() *Game {
musicInitialized: false,
boss: elements.NewBoss(),
}
return g
}
@@ -195,6 +196,14 @@ func (g *Game) Draw(screen *ebiten.Image) {
op.GeoM.Translate(-MOVER_WIDTH, -MOVER_HEIGHT)
op.GeoM.Translate(g.boss.Pos.X, g.boss.Pos.Y)
screen.DrawImage(g.boss.Sprite, op)
//text.Draw(screen, fmt.Sprintf("%d", g.boss.Health), fonts.SurviveFont.Arcade, 100, 50, color.White)
//boss health bar
x0 := g.boss.Pos.X - 96
y0 := g.boss.Pos.Y - 60
vector.DrawFilledRect(screen, float32(x0), float32(y0), 204, 12, color.Black, true)
vector.DrawFilledRect(screen, float32(x0+2), float32(y0+2), float32(g.boss.Health)*2, 8, color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff}, true)
}
g.projectileMask.Clear()
@@ -280,14 +289,15 @@ func (g *Game) StepGame() {
//append new projectiles
g.AppendProjectiles()
//add new target with increasing frequency
g.SpawnEnemies()
//handle pulsewave updates
g.HandlePulseWaveUpdate()
if !g.boss.Spawned && g.counter > 600 {
g.SpawnBoss()
if !g.boss.Spawned {
//add new target with increasing frequency
g.SpawnEnemies()
if g.counter > 2000 {
g.SpawnBoss()
}
}
}
@@ -346,6 +356,20 @@ func (g *Game) HandlePulseWaveUpdate() {
//target.SetHit()
}
}
//check for boss
if g.boss.Spawned {
dx := g.boss.Pos.X - g.hero.Pos.X
dy := g.boss.Pos.Y - g.hero.Pos.Y
r := math.Sqrt(dx*dx + dy*dy)
if r >= g.explosion.Radius-40 && r <= g.explosion.Radius+40 &&
g.boss.Action <= elements.MoverActionDamaged && !g.boss.Touched {
g.boss.ToggleColor()
g.boss.Touched = true
//target.SetHit()
}
}
}
}
@@ -393,10 +417,10 @@ func (g *Game) UpdateProjectiles() {
}
}
//boss check first, boundary check
//boss check: first, boundary check
if p.Pos.X >= g.boss.Pos.X-MOVER_WIDTH && p.Pos.X <= g.boss.Pos.X+MOVER_WIDTH &&
p.Pos.Y >= g.boss.Pos.Y-MOVER_HEIGHT && p.Pos.Y <= g.boss.Pos.Y+MOVER_HEIGHT &&
g.boss.Action < elements.MoverActionDying {
g.boss.Action == elements.MoverActionDamaged {
//fmt.Println("potential collision")
//the following computes total collisions in the image using a projectile mask that is a duplicate of what is on screen
@@ -476,6 +500,7 @@ func (g *Game) ResetTargetTouches() {
for _, t := range g.targets {
t.Touched = false
}
g.boss.Touched = false
}
func (g *Game) AppendProjectiles() {
@@ -636,6 +661,7 @@ func (g *Game) UpdateBoss() {
g.boss.Update()
if g.boss.Action == elements.MoverActionExploding && !g.boss.SplodeInitiated {
g.score += 10
player := audioContext.NewPlayerFromBytes(assets.Splode)
player.Play()
g.boss.SplodeInitiated = true
@@ -697,3 +723,7 @@ func (g *Game) HasCollided(mask *ebiten.Image, size int) bool {
}
return result
}
func (g *Game) SetInputs(gamedata.GameInputs) {
}

227
screens/primary.go Normal file
View File

@@ -0,0 +1,227 @@
package screens
import (
"math"
"mover/assets"
"mover/gamedata"
"mover/gameelement"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/audio"
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
type Primary struct {
events map[ScreenManagerEvent]func()
dimensions gamedata.Area
elements []gameelement.GameElement
gameevents map[gamedata.GameEvent]bool
paused bool
gameover bool
musicInitialized bool
audioplayer *audio.Player
}
func NewPrimary() *Primary {
p := &Primary{
events: make(map[ScreenManagerEvent]func()),
paused: false,
gameover: false,
musicInitialized: false,
}
p.gameevents = make(map[gamedata.GameEvent]bool)
p.elements = append(p.elements, gameelement.NewBackground(gamedata.Area{Width: 640, Height: 480}))
canvas := gameelement.NewCanvas(gamedata.Area{Width: 640, Height: 480})
canvas.RegisterEvents(gamedata.GameEventPlayerDeath, p.EventHandlerPlayerDeath)
canvas.RegisterEvents(gamedata.GameEventCharge, p.EventHandlerCharge)
canvas.RegisterEvents(gamedata.GameEventNewShot, p.EventHandlerNewShot)
canvas.RegisterEvents(gamedata.GameEventTargetHit, p.EventHandlerTargetHit)
canvas.RegisterEvents(gamedata.GameEventExplosion, p.EventHandlerExplosion)
p.elements = append(p.elements, canvas)
return p
}
func (p *Primary) Update() error {
if !p.musicInitialized {
s := audio.NewInfiniteLoop(assets.SoundBank[assets.MainLoop], assets.SoundBank[assets.MainLoop].Length())
p.audioplayer, _ = audioContext.NewPlayer(s)
p.audioplayer.Play()
p.musicInitialized = true
}
//collect all inputs
inputs := p.CollectInputs()
if inputs.Quit {
p.events[EventEndgame]()
}
if inputs.Reset {
p.Reset()
}
p.ProcessEventAudio()
if inputs.Start {
if p.gameover {
p.Reset()
} else {
p.TogglePause()
}
}
//primary game loop, for each element pass along the inputs
//and process its update logic
if !p.paused {
for _, ge := range p.elements {
ge.SetInputs(inputs)
ge.Update()
}
}
return nil
}
func (p *Primary) Draw(screen *ebiten.Image) {
//here we simply call each game elements draw function
//as a layer on top of each other
for _, ge := range p.elements {
ge.Draw(screen)
}
}
func (p *Primary) SetEventHandler(e ScreenManagerEvent, f func()) {
p.events[e] = f
}
func (p *Primary) SetDimensions(a gamedata.Area) {
p.dimensions = a
}
func (p *Primary) CollectInputs() gamedata.GameInputs {
if inpututil.IsKeyJustPressed(ebiten.KeyQ) {
p.events[EventEndgame]()
}
gi := gamedata.GameInputs{}
//axes
inpx := ebiten.GamepadAxisValue(0, 0)
inpy := ebiten.GamepadAxisValue(0, 1)
//handle wasd input
if ebiten.IsKeyPressed(ebiten.KeyD) {
inpx = 1
}
if ebiten.IsKeyPressed(ebiten.KeyA) {
inpx = -1
}
if ebiten.IsKeyPressed(ebiten.KeyS) {
inpy = 1
}
if ebiten.IsKeyPressed(ebiten.KeyW) {
inpy = -1
}
gi.XAxis = inpx
gi.YAxis = inpy
xaxis := ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisRightStickHorizontal)
yaxis := ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisRightStickVertical)
if yaxis <= 0.09 && yaxis >= -0.09 {
yaxis = 0
}
if xaxis <= 0.09 && xaxis >= -0.09 {
xaxis = 0
}
gi.ShotAngle = math.Atan2(yaxis, xaxis)
gi.Charge = inpututil.IsStandardGamepadButtonJustPressed(0, ebiten.StandardGamepadButtonRightStick)
gi.Start = inpututil.IsStandardGamepadButtonJustPressed(0, ebiten.StandardGamepadButtonCenterRight)
gi.Shot = ebiten.IsStandardGamepadButtonPressed(0, ebiten.StandardGamepadButtonFrontBottomRight)
gi.Quit = inpututil.IsKeyJustPressed(ebiten.KeyQ)
gi.Reset = inpututil.IsKeyJustPressed(ebiten.KeyR)
return gi
}
func (p *Primary) TogglePause() {
p.paused = !p.paused
var player *audio.Player
if p.paused {
player = audioContext.NewPlayerFromBytes(assets.PauseIn)
p.audioplayer.Pause()
} else {
player = audioContext.NewPlayerFromBytes(assets.PauseOut)
p.audioplayer.Play()
}
player.Play()
}
func (p *Primary) Reset() {
p.paused = false
p.gameover = false
for _, ge := range p.elements {
ge.Initialize()
}
}
func (p *Primary) ProcessEventAudio() {
for event, occurred := range p.gameevents {
if occurred {
p.PlayAudio(event)
p.gameevents[event] = false
}
}
}
func (p *Primary) PlayAudio(e gamedata.GameEvent) {
switch e {
case gamedata.GameEventPlayerDeath:
player := audioContext.NewPlayerFromBytes(assets.HeroDeath)
player.Play()
case gamedata.GameEventCharge:
player := audioContext.NewPlayerFromBytes(assets.Magic)
player.Play()
case gamedata.GameEventNewShot:
player := audioContext.NewPlayerFromBytes(assets.Shot)
player.Play()
case gamedata.GameEventTargetHit:
player := audioContext.NewPlayerFromBytes(assets.TargetHit)
player.Play()
case gamedata.GameEventExplosion:
player := audioContext.NewPlayerFromBytes(assets.Splode)
player.Play()
}
}
func (p *Primary) EventHandlerPlayerDeath() {
p.gameevents[gamedata.GameEventPlayerDeath] = true
p.gameover = true
}
func (p *Primary) EventHandlerCharge() {
p.gameevents[gamedata.GameEventCharge] = true
}
func (p *Primary) EventHandlerNewShot() {
p.gameevents[gamedata.GameEventNewShot] = true
}
func (p *Primary) EventHandlerTargetHit() {
p.gameevents[gamedata.GameEventTargetHit] = true
}
func (p *Primary) EventHandlerExplosion() {
p.gameevents[gamedata.GameEventExplosion] = true
}