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

@@ -24,32 +24,34 @@ const (
type MoverAction uint type MoverAction uint
type Mover struct { type Mover struct {
Sprite *ebiten.Image Sprite *ebiten.Image
Maks *ebiten.Image Maks *ebiten.Image
MaksDest *ebiten.Image MaksDest *ebiten.Image
Angle float64 Angle float64
Pos gamedata.Coordinates Pos gamedata.Coordinates
Origin gamedata.Coordinates Origin gamedata.Coordinates
Action MoverAction Action MoverAction
cycles int cycles int
rotating bool rotating bool
Toggled bool Toggled bool
Hit bool Hit bool
Touched bool Touched bool
dyingcount int SplodeInitiated bool
dyingcount int
} }
func NewMover() *Mover { func NewMover() *Mover {
m := &Mover{ m := &Mover{
Sprite: ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT), Sprite: ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT),
Maks: ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT), Maks: ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT),
MaksDest: ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT), MaksDest: ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT),
Action: MoverActionDefault, Action: MoverActionDefault,
cycles: 4, cycles: 4,
Angle: 0, Angle: 0,
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 {
@@ -32,16 +32,19 @@ type Game struct {
heroCollisionMask *ebiten.Image heroCollisionMask *ebiten.Image
heroCollisionCpy *ebiten.Image heroCollisionCpy *ebiten.Image
dimensions gamedata.Area dimensions gamedata.Area
Pos gamedata.Coordinates Pos gamedata.Coordinates
Paused bool Paused bool
initialized bool initialized bool
gameover bool gameover bool
reset bool reset bool
runtime float64 musicInitialized bool
hero *elements.Hero runtime float64
projectiles map[int]*elements.Projectile hero *elements.Hero
explosion *elements.Explosion projectiles map[int]*elements.Projectile
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]()
} }
s.current.X += (s.target.X - s.current.X) / 8 if !s.audioplayed {
s.current.Y += (s.target.Y - s.current.Y) / 8 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.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
} }