Add gib and die sounds

This commit is contained in:
Trevor Slocum 2021-10-06 07:18:38 -07:00
parent aba5ddbd99
commit 5b1bf05d12
6 changed files with 129 additions and 37 deletions

BIN
assets/audio/die.mp3 Normal file

Binary file not shown.

BIN
assets/audio/gib.mp3 Normal file

Binary file not shown.

View File

@ -7,7 +7,7 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
type creep struct {
type gameCreep struct {
x, y float64
sprite *ebiten.Image
@ -16,17 +16,28 @@ type creep struct {
tick int
nextAction int
level *Level
sync.Mutex
}
func (c *creep) doNextAction() {
func NewCreep(sprite *ebiten.Image, level *Level) *gameCreep {
return &gameCreep{
x: float64(1 + rand.Intn(64)),
y: float64(1 + rand.Intn(64)),
sprite: sprite,
level: level,
}
}
func (c *gameCreep) doNextAction() {
c.moveX = (rand.Float64() - 0.5) / 10
c.moveY = (rand.Float64() - 0.5) / 10
c.nextAction = 400 + rand.Intn(1000)
}
func (c *creep) Update() {
func (c *gameCreep) Update() {
c.Lock()
defer c.Unlock()
@ -36,10 +47,10 @@ func (c *creep) Update() {
c.tick = 0
}
c.x, c.y = c.x+c.moveX, c.y+c.moveY
c.x, c.y = c.level.Clamp(c.x+c.moveX, c.y+c.moveY)
}
func (c *creep) Position() (float64, float64) {
func (c *gameCreep) Position() (float64, float64) {
c.Lock()
defer c.Unlock()
return c.x, c.y

129
game.go
View File

@ -36,10 +36,14 @@ type game struct {
w, h int
currentLevel *Level
audioPlayer *audio.Player
audioPlayerGunshot *audio.Player
audioPlayerGib *audio.Player
audioPlayerDie *audio.Player
player *gamePlayer
gameOverTime time.Time
camScale float64
camScaleTo float64
@ -47,7 +51,7 @@ type game struct {
spinnerIndex int
creeps []*creep
creeps []*gameCreep
projectiles []*projectile
@ -114,20 +118,40 @@ func NewGame() (*game, error) {
flashImage = ebiten.NewImageFromImage(img)
f, err = assetsFS.Open("assets/audio/gunshot.mp3")
loadSound := func(p string) (*audio.Player, error) {
f, err := assetsFS.Open(p)
if err != nil {
return nil, err
}
defer f.Close()
gunshotSound, err := mp3.DecodeWithSampleRate(sampleRate, f)
if err != nil {
return nil, err
}
return audioContext.NewPlayer(gunshotSound)
}
g.audioPlayerGunshot, err = loadSound("assets/audio/gunshot.mp3")
if err != nil {
return nil, err
}
gunshotSound, err := mp3.DecodeWithSampleRate(sampleRate, f)
g.audioPlayerGib, err = loadSound("assets/audio/gib.mp3")
if err != nil {
return nil, err
}
g.audioPlayer, err = audioContext.NewPlayer(gunshotSound)
g.audioPlayerDie, err = loadSound("assets/audio/die.mp3")
if err != nil {
return nil, err
}
// The death sound will have a delay without this.
g.audioPlayerDie.SetVolume(0)
g.audioPlayerDie.Play()
f, err = assetsFS.Open("assets/creeps/vampire.png")
if err != nil {
return nil, err
@ -141,15 +165,18 @@ func NewGame() (*game, error) {
addedCreeps := make(map[string]bool)
for i := 0; i < 1000; i++ {
c := &creep{
x: float64(1 + rand.Intn(64)),
y: float64(1 + rand.Intn(64)),
sprite: vampireImage,
c := NewCreep(vampireImage, g.currentLevel)
safeSpace := 7.0
dx, dy := deltaXY(g.player.x, g.player.y, c.x, c.y)
if dx <= safeSpace || dy <= safeSpace {
// Too close to the spawn point.
continue
}
addedCreep := fmt.Sprintf("%0.0f-%0.0f", c.x, c.y)
if addedCreeps[addedCreep] {
// Already added a creep here
// Already added a gameCreep here.
continue
}
@ -159,6 +186,11 @@ func NewGame() (*game, error) {
ebiten.SetCursorShape(ebiten.CursorShapeCrosshair)
// The death sound will have a delay without this.
g.audioPlayerDie.Pause()
g.audioPlayerDie.Rewind()
g.audioPlayerDie.SetVolume(1.6)
return g, nil
}
@ -169,6 +201,11 @@ func (g *game) Update() error {
return nil
}
if g.player.health <= 0 {
// Game over.
return nil
}
for _, c := range g.creeps {
c.Update()
}
@ -239,21 +276,23 @@ func (g *game) Update() error {
for _, c := range g.creeps {
cx, cy := c.Position()
dx, dy := p.x-cx, p.y-cy
if dx < 0 {
dx *= -1
}
if dy < 0 {
dy *= -1
}
dx, dy := deltaXY(p.x, p.y, cx, cy)
if dx <= bulletHitThreshold && dy <= bulletHitThreshold {
// Kill creep
// Kill gameCreep
g.addBloodSplatter(cx, cy)
c.x = 1000
c.y = 1000
// Remove projectile
g.projectiles = append(g.projectiles[:i-removed], g.projectiles[i-removed+1:]...)
removed++
// Play gib sound.
g.audioPlayerGib.SetVolume(1.0)
g.audioPlayerGib.Pause()
g.audioPlayerGib.Rewind()
g.audioPlayerGib.Play()
break
}
}
@ -271,13 +310,15 @@ func (g *game) Update() error {
g.projectiles = append(g.projectiles, p)
v := 0.75 + (rand.Float64() / 4)
v = 1.0 // TODO
v = 0.6 // TODO
g.player.weapon.lastFire = time.Now()
g.audioPlayer.SetVolume(v)
g.audioPlayer.Pause()
g.audioPlayer.Rewind()
g.audioPlayer.Play()
// Play gunshot sound.
g.audioPlayerGunshot.SetVolume(v)
g.audioPlayerGunshot.Pause()
g.audioPlayerGunshot.Rewind()
g.audioPlayerGunshot.Play()
}
return nil
@ -321,8 +362,20 @@ func (g *game) addBloodSplatter(x, y float64) {
// Draw draws the game on the screen.
func (g *game) Draw(screen *ebiten.Image) {
// Game over.
if g.player.health <= 0 {
screen.Fill(colornames.Red)
screen.Fill(color.RGBA{102, 0, 0, 255})
if time.Since(g.gameOverTime).Milliseconds()%2000 < 1500 {
g.overlayImg.Clear()
ebitenutil.DebugPrint(g.overlayImg, "GAME OVER")
g.op.GeoM.Reset()
g.op.GeoM.Translate(3, 0)
g.op.GeoM.Scale(16, 16)
g.op.GeoM.Translate(float64(g.w/2)-485, float64(g.h/2)-200)
screen.DrawImage(g.overlayImg, g.op)
}
return
}
@ -415,15 +468,18 @@ func (g *game) renderLevel(screen *ebiten.Image) int {
biteThreshold := 0.5
for _, c := range g.creeps {
cx, cy := c.Position()
dx, dy := g.player.x-cx, g.player.y-cy
if dx < 0 {
dx *= -1
}
if dy < 0 {
dy *= -1
}
dx, dy := deltaXY(g.player.x, g.player.y, cx, cy)
if dx <= biteThreshold && dy <= biteThreshold {
g.player.health--
if g.player.health == 0 {
ebiten.SetCursorShape(ebiten.CursorShapeDefault)
g.gameOverTime = time.Now()
// Play die sound.
g.audioPlayerDie.Play()
}
}
drawn += g.renderSprite(c.x, c.y, 0, 0, 0, c.sprite, screen)
@ -459,3 +515,14 @@ func (g *game) renderLevel(screen *ebiten.Image) int {
func (g *game) exit() {
os.Exit(0)
}
func deltaXY(x1, y1, x2, y2 float64) (dx float64, dy float64) {
dx, dy = x1-x2, y1-y2
if dx < 0 {
dx *= -1
}
if dy < 0 {
dy *= -1
}
return dx, dy
}

View File

@ -27,6 +27,20 @@ func (l *Level) Size() (width, height int) {
return l.w, l.h
}
func (l *Level) Clamp(x, y float64) (float64, float64) {
if x < 0 {
x = 0
} else if x > float64(l.w)-1 {
x = float64(l.w)
}
if y < 0 {
y = 0
} else if y > float64(l.h)-1 {
y = float64(l.h)
}
return x, y
}
// NewLevel returns a new randomly generated Level.
func NewLevel() (*Level, error) {
// Create a 108x108 Level.

View File

@ -34,7 +34,7 @@ func NewPlayer() (*gamePlayer, error) {
sprite: uziSprite,
cooldown: 100 * time.Millisecond,
},
health: 70,
health: 7,
}
return p, nil
}