New hero sprite, timer and score added, death added, reset added.

This commit is contained in:
2024-11-06 07:42:21 -05:00
parent 4b53b19e74
commit 89349f466f
9 changed files with 319 additions and 42 deletions

147
game.go
View File

@@ -1,13 +1,16 @@
package main
import (
"fmt"
"image/color"
"log"
"math"
"math/rand/v2"
"mover/fonts"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/text"
"github.com/hajimehoshi/ebiten/v2/vector"
)
@@ -17,16 +20,22 @@ const (
)
type Game struct {
collisionMask *ebiten.Image
projectileMask *ebiten.Image
collisionMask *ebiten.Image
projectileMask *ebiten.Image
heroCollisionMask *ebiten.Image
heroCollisionCpy *ebiten.Image
Pos Coordinates
Paused bool
initialized bool
mover *Mover
gameover bool
reset bool
runtime float64
hero *Hero
projectiles map[int]*Projectile
explosion *Explosion
score int
counter int
timer int
targets []*Mover
@@ -41,15 +50,31 @@ func (g *Game) Initialize() {
origin := Coordinates{X: 640 / 2, Y: 480 / 2}
g.mover = NewMover()
g.mover.SetOrigin(origin)
g.mover.ToggleRotate()
g.hero = NewHero()
g.hero.SetOrigin(origin)
g.hero.ToggleRotate()
g.gameover = false
g.collisionMask = ebiten.NewImage(screenWidth, screenHeight)
g.projectileMask = ebiten.NewImage(screenWidth, screenHeight)
g.heroCollisionMask = ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT)
g.heroCollisionCpy = ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT)
g.explosion = NewExplosion()
g.explosion.SetOrigin(origin)
g.score = 0
g.reset = false
for j := 0; j < len(g.targets); j++ {
g.targets[j] = nil
}
g.targets = g.targets[:0]
g.score = 0
g.counter = 0
g.timer = 0
g.runtime = 0.
}
@@ -71,11 +96,12 @@ func (g *Game) Update() error {
}
}
if !g.initialized {
if !g.initialized || g.reset {
g.Initialize()
g.projectiles = make(map[int]*Projectile)
g.initialized = true
g.reset = false
} else {
g.StepGame()
}
@@ -87,19 +113,29 @@ func (g *Game) Update() error {
func (g *Game) Draw(screen *ebiten.Image) {
g.mover.Draw()
g.hero.Draw()
op := &ebiten.DrawImageOptions{}
/*
dx := 40 * math.Cos(float64(g.counter)/16)
dy := 40 * math.Sin(float64(g.counter)/16)
a := float64(g.counter) / (math.Pi * 2)
*/
if !g.gameover {
g.runtime = float64(g.counter) / 60.
}
s := fmt.Sprintf("%02.3f", g.runtime)
if !g.gameover {
text.Draw(screen, "TIME: "+s, fonts.SurviveFont.Arcade, 640/2-250, 25, color.White)
text.Draw(screen, fmt.Sprintf("SCORE: %d", g.score*10), fonts.SurviveFont.Arcade, 640/2+100, 25, color.White)
} else {
if (g.counter/30)%2 == 0 {
text.Draw(screen, "TIME: "+s, fonts.SurviveFont.Arcade, 640/2-250, 25, color.White)
text.Draw(screen, fmt.Sprintf("SCORE: %d", g.score*10), fonts.SurviveFont.Arcade, 640/2+100, 25, color.White)
}
}
op.GeoM.Translate(-MOVER_WIDTH/2, -MOVER_HEIGHT/2)
op.GeoM.Rotate(g.mover.Angle)
op.GeoM.Translate(g.mover.Pos.X, g.mover.Pos.Y)
screen.DrawImage(g.mover.Sprite, op)
op.GeoM.Rotate(g.hero.Angle)
op.GeoM.Translate(g.hero.Pos.X, g.hero.Pos.Y)
screen.DrawImage(g.hero.Sprite, op)
for _, target := range g.targets {
target.Draw()
@@ -142,10 +178,13 @@ func (g *Game) CleanupTargets() {
i++
}
}
//then culling the last elements of the slice
/*if len(g.targets)-i > 0 {
fmt.Printf("Removing %d elements\n", len(g.targets)-i)
}*/
if len(g.targets)-i > 0 {
// fmt.Printf("Removing %d elements\n", len(g.targets)-i)
g.score += len(g.targets) - i
}
for j := i; j < len(g.targets); j++ {
g.targets[j] = nil
}
@@ -160,20 +199,20 @@ func (g *Game) StepGame() {
g.UpdateHeroPosition()
g.mover.Update()
g.hero.Update()
g.explosion.Update()
g.UpdateTargets()
g.UpdateProjectiles()
//append new projectiles
g.AppendProjectiles()
//add new target with increasing frequency
g.AddNewTargets()
//handle pulsewave updates
g.HandlePulseWaveUpdate()
if !g.gameover {
//append new projectiles
g.AppendProjectiles()
//add new target with increasing frequency
g.AddNewTargets()
//handle pulsewave updates
g.HandlePulseWaveUpdate()
}
g.CleanupTargets()
g.counter++
@@ -198,8 +237,8 @@ func (g *Game) HandlePulseWaveUpdate() {
//check collisions
for _, target := range g.targets {
dx := target.Pos.X - g.mover.Pos.X
dy := target.Pos.Y - g.mover.Pos.Y
dx := target.Pos.X - g.hero.Pos.X
dy := target.Pos.Y - g.hero.Pos.Y
r := math.Sqrt(dx*dx + dy*dy)
if r >= g.explosion.Radius-5 && r <= g.explosion.Radius+5 && target.Action <= MoverActionDamaged && !target.Touched {
@@ -261,9 +300,9 @@ func (g *Game) UpdateProjectiles() {
func (g *Game) UpdateTargets() {
for _, target := range g.targets {
if !target.Hit {
dx := g.mover.Pos.X - target.Pos.X
dy := g.mover.Pos.Y - target.Pos.Y
if !target.Hit && g.hero.Action < MoverActionDying {
dx := g.hero.Pos.X - target.Pos.X
dy := g.hero.Pos.Y - target.Pos.Y
angle := math.Atan2(dy, dx)
maxspeed := 3.
@@ -271,6 +310,30 @@ func (g *Game) UpdateTargets() {
target.Pos.Y += maxspeed * math.Sin(angle)
}
//compute collision with hero
if g.hero.Pos.X >= target.Pos.X-MOVER_WIDTH/2 && g.hero.Pos.X <= target.Pos.X+MOVER_WIDTH/2 && g.hero.Pos.Y >= target.Pos.Y-MOVER_HEIGHT/2 && g.hero.Pos.Y <= target.Pos.Y+MOVER_HEIGHT/2 && target.Action < MoverActionDying {
g.heroCollisionMask.Clear()
g.heroCollisionMask.DrawImage(g.hero.Sprite, nil)
op := &ebiten.DrawImageOptions{}
op.GeoM.Reset()
op.Blend = ebiten.BlendSourceIn
op.GeoM.Translate((g.hero.Pos.X-target.Pos.X)-MOVER_WIDTH/2, (g.hero.Pos.Y-target.Pos.Y)-MOVER_HEIGHT/2)
g.heroCollisionMask.DrawImage(target.Sprite, op)
//var pixels []byte = make([]byte, MOVER_WIDTH*MOVER_HEIGHT*4)
var pixels []byte = make([]byte, MOVER_HEIGHT*MOVER_HEIGHT*4)
g.heroCollisionMask.ReadPixels(pixels)
for i := 0; i < len(pixels); i = i + 4 {
if pixels[i+3] != 0 {
fmt.Println("pixel death")
g.hero.Action = MoverActionDying
g.gameover = true
break
}
}
}
target.Update()
}
}
@@ -284,8 +347,8 @@ func (g *Game) ResetTargetTouches() {
func (g *Game) AppendProjectiles() {
if g.counter%14 == 0 && ebiten.IsGamepadButtonPressed(0, ebiten.GamepadButton7) {
g.projectiles[g.counter] = NewProjectile(Coordinates{X: g.mover.Pos.X, Y: g.mover.Pos.Y}, g.mover.Angle, 5.)
g.projectiles[g.counter+1] = NewProjectile(Coordinates{X: g.mover.Pos.X, Y: g.mover.Pos.Y}, g.mover.Angle+math.Pi, 5.)
g.projectiles[g.counter] = NewProjectile(Coordinates{X: g.hero.Pos.X, Y: g.hero.Pos.Y}, g.hero.Angle, 5.)
g.projectiles[g.counter+1] = NewProjectile(Coordinates{X: g.hero.Pos.X, Y: g.hero.Pos.Y}, g.hero.Angle+math.Pi, 5.)
}
}
@@ -293,14 +356,18 @@ func (g *Game) HandleInput() {
if len(g.gamepadIDs) > 0 {
if ebiten.IsGamepadButtonPressed(0, ebiten.GamepadButton11) {
if !g.explosion.Active {
g.explosion.SetOrigin(g.mover.Pos)
g.explosion.SetOrigin(g.hero.Pos)
g.explosion.Reset()
g.explosion.ToggleActivate()
}
}
if inpututil.IsGamepadButtonJustPressed(0, ebiten.GamepadButton9) {
g.Paused = !g.Paused
if g.gameover {
g.reset = true
} else {
g.Paused = !g.Paused
}
}
//account for controller sensitivity
@@ -315,7 +382,7 @@ func (g *Game) HandleInput() {
}
inputangle := math.Atan2(yaxis, xaxis)
g.mover.SetAngle(inputangle)
g.hero.SetAngle(inputangle)
}
}
@@ -324,10 +391,10 @@ func (g *Game) UpdateHeroPosition() {
inpx := ebiten.GamepadAxisValue(0, 0)
inpy := ebiten.GamepadAxisValue(0, 1)
if inpx >= 0.15 || inpx <= -0.15 {
g.mover.Pos.X += ebiten.GamepadAxisValue(0, 0) * 5
g.hero.Pos.X += ebiten.GamepadAxisValue(0, 0) * 5
}
if inpy >= 0.15 || inpy <= -0.15 {
g.mover.Pos.Y += ebiten.GamepadAxisValue(0, 1) * 5
g.hero.Pos.Y += ebiten.GamepadAxisValue(0, 1) * 5
}
}