Added music and sfx.

This commit is contained in:
2024-11-11 14:07:02 -05:00
parent 56d1f62020
commit 658ae73c9b
15 changed files with 207 additions and 73 deletions

57
assets/audiobank.go Normal file
View File

@@ -0,0 +1,57 @@
package assets
import (
"bytes"
_ "embed"
"log"
"github.com/hajimehoshi/ebiten/v2/audio/wav"
)
type SndAssetName string
const (
MainLoop SndAssetName = "MainLoop"
Explode SndAssetName = "Explode"
sampleRate = 44100
)
var (
SoundBank map[SndAssetName]*wav.Stream
//go:embed loop.wav
mainloop_snd []byte
//go:embed explode.wav
explode_snd []byte
//go:embed explode.wav
Splode []byte
//go:embed hit.wav
TargetHit []byte
//go:embed herodeath.wav
HeroDeath []byte
//go:embed shot.wav
Shot []byte
//go:embed magic.wav
Magic []byte
//go:embed survive.wav
Survive []byte
)
func LoadSounds() {
SoundBank = make(map[SndAssetName]*wav.Stream)
SoundBank[MainLoop] = LoadSoundFatal(sampleRate, mainloop_snd)
SoundBank[Explode] = LoadSoundFatal(sampleRate, explode_snd)
}
func LoadSoundFatal(rate int, obj []byte) *wav.Stream {
stream, err := wav.DecodeWithSampleRate(rate, bytes.NewReader(obj))
if err != nil {
log.Fatal("dead, jim")
}
return stream
}

BIN
assets/explode.wav Normal file

Binary file not shown.

BIN
assets/herodeath.wav Normal file

Binary file not shown.

BIN
assets/hit.wav Normal file

Binary file not shown.

View File

@@ -10,27 +10,22 @@ import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
) )
type AssetName string type ImgAssetName string
const ( const (
FlyEyeNormal AssetName = "FlyEyeNormal" FlyEyeNormal ImgAssetName = "FlyEyeNormal"
FlyEyeDamaged AssetName = "FlyEyeDamaged" FlyEyeDamaged ImgAssetName = "FlyEyeDamaged"
FlyEyeDying AssetName = "FlyEyeDying" FlyEyeDying ImgAssetName = "FlyEyeDying"
FlyEyeShadow AssetName = "FlyEyeShadow" FlyEyeShadow ImgAssetName = "FlyEyeShadow"
HeroNormal AssetName = "HeroNormal" HeroNormal ImgAssetName = "HeroNormal"
HeroDying AssetName = "HeroDying" HeroDying ImgAssetName = "HeroDying"
TileSet AssetName = "TileSet" TileSet ImgAssetName = "TileSet"
Altar AssetName = "Altar" Altar ImgAssetName = "Altar"
Weapon AssetName = "Weapon" Weapon ImgAssetName = "Weapon"
) )
var ( var (
ImageBank map[AssetName]*ebiten.Image ImageBank map[ImgAssetName]*ebiten.Image
flyeyeImage *ebiten.Image
flyeyeImage2 *ebiten.Image
flyeyeImage3 *ebiten.Image
shadow *ebiten.Image
//go:embed fly-eye.png //go:embed fly-eye.png
flyeye_img []byte flyeye_img []byte
@@ -53,7 +48,7 @@ var (
) )
func LoadImages() { func LoadImages() {
ImageBank = make(map[AssetName]*ebiten.Image) ImageBank = make(map[ImgAssetName]*ebiten.Image)
ImageBank[FlyEyeNormal] = LoadImagesFatal(flyeye_img) ImageBank[FlyEyeNormal] = LoadImagesFatal(flyeye_img)
ImageBank[FlyEyeDamaged] = LoadImagesFatal(flyeye_img2) ImageBank[FlyEyeDamaged] = LoadImagesFatal(flyeye_img2)

BIN
assets/loop.wav Normal file

Binary file not shown.

BIN
assets/magic.wav Normal file

Binary file not shown.

BIN
assets/shot.wav Normal file

Binary file not shown.

BIN
assets/survive.wav Normal file

Binary file not shown.

View File

@@ -36,6 +36,7 @@ type Mover struct {
Toggled bool Toggled bool
Hit bool Hit bool
Touched bool Touched bool
SplodeInitiated bool
dyingcount int dyingcount int
} }
@@ -50,6 +51,7 @@ func NewMover() *Mover {
rotating: false, rotating: false,
Toggled: false, Toggled: false,
dyingcount: 0, dyingcount: 0,
SplodeInitiated: false,
} }
m.Maks.Fill(color.White) m.Maks.Fill(color.White)
@@ -82,14 +84,8 @@ func (m *Mover) Draw() {
switch m.Action { switch m.Action {
case MoverActionDefault: case MoverActionDefault:
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(14, 40)
m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeShadow], op)
m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeNormal].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil) m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeNormal].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
case MoverActionDamaged: case MoverActionDamaged:
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(14, 40)
m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeShadow], op)
m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeDamaged].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil) m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeDamaged].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
case MoverActionDying: case MoverActionDying:

1
go.mod
View File

@@ -13,6 +13,7 @@ require (
require ( require (
github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect
github.com/ebitengine/hideconsole v1.0.0 // indirect github.com/ebitengine/hideconsole v1.0.0 // indirect
github.com/ebitengine/oto/v3 v3.3.1 // indirect
github.com/ebitengine/purego v0.8.0 // indirect github.com/ebitengine/purego v0.8.0 // indirect
github.com/jezek/xgb v1.1.1 // indirect github.com/jezek/xgb v1.1.1 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.8.0 // indirect

View File

@@ -33,6 +33,7 @@ func main() {
func loadScreens(m *screenmanager.Manager) { func loadScreens(m *screenmanager.Manager) {
assets.LoadImages() assets.LoadImages()
assets.LoadSounds()
m.AddScene(screens.NewStartScreen()) m.AddScene(screens.NewStartScreen())
m.AddScene(screens.NewGame()) m.AddScene(screens.NewGame())
m.ResetScenes() m.ResetScenes()

View File

@@ -26,7 +26,7 @@ func NewManager() Manager {
return Manager{ return Manager{
Info: gamedata.GameInfo{ Info: gamedata.GameInfo{
Name: "survive", Name: "survive",
Version: "0.12", Version: "0.16",
Dimensions: gamedata.Area{ Dimensions: gamedata.Area{
Width: defaultWidth, Width: defaultWidth,
Height: defaultHeight, Height: defaultHeight,

View File

@@ -11,9 +11,8 @@ import (
"mover/fonts" "mover/fonts"
"mover/gamedata" "mover/gamedata"
_ "embed"
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/audio"
"github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/text" "github.com/hajimehoshi/ebiten/v2/text"
"github.com/hajimehoshi/ebiten/v2/vector" "github.com/hajimehoshi/ebiten/v2/vector"
@@ -22,6 +21,7 @@ import (
const ( const (
MOVER_WIDTH = 48 MOVER_WIDTH = 48
MOVER_HEIGHT = 48 MOVER_HEIGHT = 48
sampleRate = 44100
) )
type Game struct { type Game struct {
@@ -38,10 +38,13 @@ type Game struct {
initialized bool initialized bool
gameover bool gameover bool
reset bool reset bool
musicInitialized bool
runtime float64 runtime float64
hero *elements.Hero hero *elements.Hero
projectiles map[int]*elements.Projectile projectiles map[int]*elements.Projectile
explosion *elements.Explosion explosion *elements.Explosion
audioplayer *audio.Player
sfxplayer []*audio.Player
score int score int
counter int counter int
@@ -54,15 +57,39 @@ type Game struct {
//pressedButtons map[ebiten.GamepadID][]string //pressedButtons map[ebiten.GamepadID][]string
} }
var (
audioContext = audio.NewContext(sampleRate)
)
func NewGame() *Game { func NewGame() *Game {
g := &Game{ g := &Game{
events: make(map[ScreenManagerEvent]func()), events: make(map[ScreenManagerEvent]func()),
musicInitialized: false,
} }
return g return g
} }
func (g *Game) Initialize() { func (g *Game) Initialize() {
if !g.musicInitialized {
//s, _ := wav.DecodeWithSampleRate(sampleRate)
//d, _ := wav.DecodeWithSampleRate(sampleRate, bytes.NewReader(loop_wav))
s := audio.NewInfiniteLoop(assets.SoundBank[assets.MainLoop], assets.SoundBank[assets.MainLoop].Length())
/*
reader := bytes.NewReader(loop_wav)
s := audio.NewInfiniteLoopF32(reader, reader.Length())
*/
g.audioplayer, _ = audioContext.NewPlayer(s)
g.audioplayer.Play()
g.musicInitialized = true
}
origin := gamedata.Coordinates{X: 640 / 2, Y: 480 / 2} origin := gamedata.Coordinates{X: 640 / 2, Y: 480 / 2}
g.ConstructBackground() g.ConstructBackground()
@@ -143,17 +170,6 @@ func (g *Game) Draw(screen *ebiten.Image) {
g.runtime = float64(g.counter) / 60. 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.Translate(-MOVER_WIDTH/2, -MOVER_HEIGHT/2)
//op.GeoM.Rotate(g.hero.Angle) //op.GeoM.Rotate(g.hero.Angle)
op.GeoM.Translate(g.hero.Pos.X, g.hero.Pos.Y) op.GeoM.Translate(g.hero.Pos.X, g.hero.Pos.Y)
@@ -175,7 +191,15 @@ func (g *Game) Draw(screen *ebiten.Image) {
op.GeoM.Rotate(g.hero.Angle + math.Pi) op.GeoM.Rotate(g.hero.Angle + math.Pi)
op.GeoM.Translate(g.hero.Pos.X, g.hero.Pos.Y) op.GeoM.Translate(g.hero.Pos.X, g.hero.Pos.Y)
screen.DrawImage(weaponImage, op) screen.DrawImage(weaponImage, op)
}*/ }
*/
//draw shadows
for _, target := range g.targets {
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(target.Pos.X-10, target.Pos.Y+10)
screen.DrawImage(assets.ImageBank[assets.FlyEyeShadow], op)
}
for _, target := range g.targets { for _, target := range g.targets {
target.Draw() target.Draw()
@@ -201,6 +225,19 @@ func (g *Game) Draw(screen *ebiten.Image) {
/*for _, gamepad ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisRightStickHorizontal), /*for _, gamepad ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisRightStickHorizontal),
ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisRightStickVertical))*/ ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisRightStickVertical))*/
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)
}
text.Draw(screen, "PRESS START TO TRY AGAIN", fonts.SurviveFont.Arcade, 640/2-150, 480/2, color.White)
}
} }
} }
@@ -237,6 +274,10 @@ func (g *Game) StepGame() {
if !g.Paused { if !g.Paused {
if !g.audioplayer.IsPlaying() {
g.audioplayer.Play()
}
g.hero.Update() g.hero.Update()
g.explosion.Update() g.explosion.Update()
@@ -255,6 +296,8 @@ func (g *Game) StepGame() {
g.CleanupTargets() g.CleanupTargets()
g.counter++ g.counter++
} else {
g.audioplayer.Pause()
} }
} }
@@ -351,6 +394,13 @@ func (g *Game) UpdateProjectiles() {
target.SetHit() target.SetHit()
//target.SetOrigin(gamedata.Coordinates{X: rand.Float64() * 640, Y: rand.Float64() * 480}) //target.SetOrigin(gamedata.Coordinates{X: rand.Float64() * 640, Y: rand.Float64() * 480})
target.Hit = true target.Hit = true
//var err error
//player, err := audioContext.NewPlayer(assets.SoundBank[assets.Explode])
player := audioContext.NewPlayerFromBytes(assets.TargetHit)
player.Play()
break break
} }
} }
@@ -362,6 +412,12 @@ func (g *Game) UpdateProjectiles() {
func (g *Game) UpdateTargets() { func (g *Game) UpdateTargets() {
for _, target := range g.targets { for _, target := range g.targets {
if target.Action == elements.MoverActionExploding && !target.SplodeInitiated {
player := audioContext.NewPlayerFromBytes(assets.Splode)
player.Play()
target.SplodeInitiated = true
}
if !target.Hit && g.hero.Action < elements.HeroActionDying { if !target.Hit && g.hero.Action < elements.HeroActionDying {
dx := g.hero.Pos.X - target.Pos.X dx := g.hero.Pos.X - target.Pos.X
dy := g.hero.Pos.Y - target.Pos.Y dy := g.hero.Pos.Y - target.Pos.Y
@@ -394,6 +450,9 @@ func (g *Game) UpdateTargets() {
//fmt.Println("pixel death") //fmt.Println("pixel death")
g.hero.SetHit() g.hero.SetHit()
g.gameover = true g.gameover = true
player := audioContext.NewPlayerFromBytes(assets.HeroDeath)
player.Play()
break break
} }
} }
@@ -417,6 +476,9 @@ func (g *Game) AppendProjectiles() {
if g.hero.Upgrade { if g.hero.Upgrade {
g.projectiles[g.counter+1] = elements.NewProjectile(gamedata.Coordinates{X: g.hero.Pos.X, Y: g.hero.Pos.Y}, g.hero.Angle+math.Pi, 5.) g.projectiles[g.counter+1] = elements.NewProjectile(gamedata.Coordinates{X: g.hero.Pos.X, Y: g.hero.Pos.Y}, g.hero.Angle+math.Pi, 5.)
} }
player := audioContext.NewPlayerFromBytes(assets.Shot)
player.Play()
} }
} }
@@ -427,6 +489,8 @@ func (g *Game) HandleInput() {
g.explosion.SetOrigin(g.hero.Pos) g.explosion.SetOrigin(g.hero.Pos)
g.explosion.Reset() g.explosion.Reset()
g.explosion.ToggleActivate() g.explosion.ToggleActivate()
player := audioContext.NewPlayerFromBytes(assets.Magic)
player.Play()
} }
} }
@@ -453,6 +517,10 @@ func (g *Game) HandleInput() {
inputangle := math.Atan2(yaxis, xaxis) inputangle := math.Atan2(yaxis, xaxis)
g.hero.SetAngle(inputangle) g.hero.SetAngle(inputangle)
} }
if inpututil.IsKeyJustPressed(ebiten.KeyQ) {
g.events[EventEndgame]()
}
//} //}
} }

View File

@@ -3,6 +3,7 @@ package screens
import ( import (
"image/color" "image/color"
"math" "math"
"mover/assets"
"mover/fonts" "mover/fonts"
"mover/gamedata" "mover/gamedata"
@@ -17,6 +18,8 @@ type StartScreen struct {
target gamedata.Coordinates target gamedata.Coordinates
current gamedata.Coordinates current gamedata.Coordinates
targetreached bool targetreached bool
audioplayed bool
cycle int
} }
func NewStartScreen() *StartScreen { func NewStartScreen() *StartScreen {
@@ -25,6 +28,8 @@ func NewStartScreen() *StartScreen {
target: gamedata.Coordinates{X: 640/2 - 150, Y: 480 / 2}, target: gamedata.Coordinates{X: 640/2 - 150, Y: 480 / 2},
current: gamedata.Coordinates{X: 640/2 - 150, Y: -100}, current: gamedata.Coordinates{X: 640/2 - 150, Y: -100},
targetreached: false, targetreached: false,
cycle: 0,
audioplayed: false,
} }
return s return s
} }
@@ -36,13 +41,24 @@ func (s *StartScreen) Update() error {
s.eHandler[EventCompleted]() s.eHandler[EventCompleted]()
} }
if !s.audioplayed {
player := audioContext.NewPlayerFromBytes(assets.Survive)
player.Play()
s.audioplayed = true
}
if !s.targetreached {
s.current.X += (s.target.X - s.current.X) / 8 s.current.X += (s.target.X - s.current.X) / 8
s.current.Y += (s.target.Y - s.current.Y) / 8 s.current.Y += (s.target.Y - s.current.Y) / 8
} else {
s.current.Y += 0.5 * math.Sin(float64(s.cycle)/(math.Pi*8))
}
if math.Abs(s.current.Y-s.target.Y) < 1 && !s.targetreached { if math.Abs(s.current.Y-s.target.Y) < 1 && !s.targetreached {
s.targetreached = true s.targetreached = true
} }
s.cycle++
return nil return nil
} }