Add vampire kill sounds

This commit is contained in:
Trevor Slocum 2021-10-06 17:29:28 -07:00
parent e5e8af1e9b
commit 74c1aca406
7 changed files with 128 additions and 63 deletions

Binary file not shown.

Binary file not shown.

View file

@ -20,16 +20,19 @@ type gameCreep struct {
health int
killScore int
sync.Mutex
}
func NewCreep(sprite *ebiten.Image, level *Level) *gameCreep {
return &gameCreep{
x: float64(1 + rand.Intn(108)),
y: float64(1 + rand.Intn(108)),
sprite: sprite,
level: level,
health: 1,
x: float64(1 + rand.Intn(108)),
y: float64(1 + rand.Intn(108)),
sprite: sprite,
level: level,
health: 1,
killScore: 50,
}
}

165
game.go
View file

@ -1,6 +1,7 @@
package main
import (
"bytes"
"fmt"
"image"
"image/color"
@ -9,12 +10,15 @@ import (
"os"
"time"
"github.com/hajimehoshi/ebiten/v2/audio/mp3"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/audio"
"github.com/hajimehoshi/ebiten/v2/audio/mp3"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"golang.org/x/image/colornames"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
var spinner = []byte(`-\|/`)
@ -22,6 +26,8 @@ var spinner = []byte(`-\|/`)
var bulletImage *ebiten.Image
var flashImage *ebiten.Image
var numberPrinter = message.NewPrinter(language.English)
type projectile struct {
x, y float64
angle float64
@ -34,9 +40,11 @@ type game struct {
w, h int
currentLevel *Level
audioPlayerGunshot *audio.Player
audioPlayerGib *audio.Player
audioPlayerDie *audio.Player
soundGunshot []byte
soundVampireDie1 []byte
soundVampireDie2 []byte
soundGib []byte
soundDie []byte
player *gamePlayer
@ -58,7 +66,10 @@ type game struct {
overlayImg *ebiten.Image
op *ebiten.DrawImageOptions
godMode bool
audioContext *audio.Context
godMode bool
debugMode bool
}
const sampleRate = 48000
@ -75,8 +86,6 @@ func NewGame() (*game, error) {
return nil, err
}
audioContext := audio.NewContext(sampleRate)
g := &game{
currentLevel: l,
camScale: 2,
@ -87,6 +96,8 @@ func NewGame() (*game, error) {
op: &ebiten.DrawImageOptions{},
}
g.audioContext = audio.NewContext(sampleRate)
g.player.x = float64(rand.Intn(108))
g.player.y = float64(rand.Intn(108))
@ -118,41 +129,40 @@ func NewGame() (*game, error) {
flashImage = ebiten.NewImageFromImage(img)
loadSound := func(p string) (*audio.Player, error) {
f, err := assetsFS.Open(p)
loadSound := func(p string) ([]byte, error) {
b, err := assetsFS.ReadFile(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)
return b, nil
}
g.audioPlayerGunshot, err = loadSound("assets/audio/gunshot.mp3")
if err != nil {
return nil, err
}
g.audioPlayerGunshot.SetVolume(0.6)
g.audioPlayerGib, err = loadSound("assets/audio/gib.mp3")
if err != nil {
return nil, err
}
g.audioPlayerGib.SetVolume(1.0)
g.audioPlayerDie, err = loadSound("assets/audio/die.mp3")
g.soundGunshot, err = loadSound("assets/audio/gunshot.mp3")
if err != nil {
return nil, err
}
// The death sound will have a delay without this.
g.audioPlayerDie.SetVolume(0)
g.audioPlayerDie.Play()
g.soundGib, err = loadSound("assets/audio/gib.mp3")
if err != nil {
return nil, err
}
g.soundVampireDie1, err = loadSound("assets/audio/vampiredie1.mp3")
if err != nil {
return nil, err
}
g.soundVampireDie2, err = loadSound("assets/audio/vampiredie2.mp3")
if err != nil {
return nil, err
}
g.soundDie, err = loadSound("assets/audio/die.mp3")
if err != nil {
return nil, err
}
f, err = assetsFS.Open("assets/creeps/vampire.png")
if err != nil {
@ -188,14 +198,24 @@ 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
}
func (g *game) playSound(sound []byte, volume float64) error {
stream, err := mp3.DecodeWithSampleRate(sampleRate, bytes.NewReader(sound))
if err != nil {
return err
}
player, err := g.audioContext.NewPlayer(stream)
if err != nil {
return err
}
player.SetVolume(volume)
player.Play()
return nil
}
// Update reads current user input and updates the game state.
func (g *game) Update() error {
if ebiten.IsKeyPressed(ebiten.KeyEscape) || ebiten.IsWindowBeingClosed() {
@ -289,12 +309,19 @@ func (g *game) Update() error {
// Killed creep.
if c.health == 0 {
g.player.score += c.killScore
g.addBloodSplatter(cx, cy)
// Play gib sound.
g.audioPlayerGib.Pause()
g.audioPlayerGib.Rewind()
g.audioPlayerGib.Play()
// Play vampire die sound.
dieSound := g.soundVampireDie1
if rand.Intn(2) == 1 {
dieSound = g.soundVampireDie2
}
err := g.playSound(dieSound, 0.25)
if err != nil {
return err
}
}
// Remove projectile
@ -320,12 +347,16 @@ func (g *game) Update() error {
g.player.weapon.lastFire = time.Now()
// Play gunshot sound.
g.audioPlayerGunshot.Pause()
g.audioPlayerGunshot.Rewind()
g.audioPlayerGunshot.Play()
err := g.playSound(g.soundGunshot, 0.4)
if err != nil {
return err
}
}
// TODO debug only
if inpututil.IsKeyJustPressed(ebiten.KeyV) {
g.debugMode = !g.debugMode
}
if inpututil.IsKeyJustPressed(ebiten.KeyG) {
g.godMode = !g.godMode
}
@ -353,12 +384,21 @@ func (g *game) addBloodSplatter(x, y float64) {
if rand.Intn(2) != 0 {
continue
}
for x := 12; x < 20; x++ {
if rand.Intn(5) != 0 {
continue
}
splatterSprite.Set(x, y, colornames.Red)
}
}
for y := 2; y < 26; y++ {
if rand.Intn(5) != 0 {
continue
}
for x := 2; x < 26; x++ {
if rand.Intn(12) != 0 {
continue
}
splatterSprite.Set(x, y, colornames.Red)
}
}
@ -371,8 +411,13 @@ 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 && !g.godMode {
gameOver := g.player.health <= 0 && !g.godMode
var drawn int
if !gameOver {
drawn = g.renderLevel(screen)
} else {
// Game over.
screen.Fill(color.RGBA{102, 0, 0, 255})
if time.Since(g.gameOverTime).Milliseconds()%2000 < 1500 {
@ -381,19 +426,27 @@ func (g *game) Draw(screen *ebiten.Image) {
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)
g.op.GeoM.Translate(float64(g.w/2)-495, float64(g.h/2)-200)
screen.DrawImage(g.overlayImg, g.op)
}
}
scoreLabel := numberPrinter.Sprintf("%d", g.player.score)
g.overlayImg.Clear()
ebitenutil.DebugPrint(g.overlayImg, scoreLabel)
g.op.GeoM.Reset()
g.op.GeoM.Scale(8, 8)
g.op.GeoM.Translate(float64(g.w/2)-float64(24*len(scoreLabel)), float64(g.h-150))
screen.DrawImage(g.overlayImg, g.op)
if !g.debugMode {
return
}
// Render level.
drawn := g.renderLevel(screen)
// Print game info.
g.overlayImg.Clear()
ebitenutil.DebugPrint(g.overlayImg, fmt.Sprintf("FPS %0.0f\nTPS %0.0f\nSPR %d\nSCA %0.2f\nPOS %0.0f,%0.0f", ebiten.CurrentFPS(), ebiten.CurrentTPS(), drawn, g.camScale, g.player.x, g.player.y))
ebitenutil.DebugPrint(g.overlayImg, fmt.Sprintf("SPR %d\nTPS %0.0f\nFPS %0.0f", drawn, ebiten.CurrentTPS(), ebiten.CurrentFPS()))
g.op.GeoM.Reset()
g.op.GeoM.Translate(3, 0)
g.op.GeoM.Scale(2, 2)
@ -474,7 +527,7 @@ func (g *game) renderLevel(screen *ebiten.Image) int {
}
}
biteThreshold := 0.5
biteThreshold := 0.75
for _, c := range g.creeps {
if c.health == 0 {
continue
@ -491,7 +544,11 @@ func (g *game) renderLevel(screen *ebiten.Image) int {
g.gameOverTime = time.Now()
// Play die sound.
g.audioPlayerDie.Play()
err := g.playSound(g.soundDie, 1.6)
if err != nil {
// TODO return err
panic(err)
}
}
}

3
go.mod
View file

@ -5,6 +5,7 @@ go 1.17
require (
github.com/hajimehoshi/ebiten/v2 v2.3.0-alpha.0.20211005153847-3f5d1762bb36
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
golang.org/x/text v0.3.7
)
require (
@ -15,5 +16,5 @@ require (
golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee // indirect
golang.org/x/mobile v0.0.0-20210924032853-1c027f395ef7 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef // indirect
golang.org/x/sys v0.0.0-20211006225509-1a26e0398eed // indirect
)

6
go.sum
View file

@ -369,13 +369,15 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef h1:fPxZ3Umkct3LZ8gK9nbk+DWDJ9fstZa2grBn+lWVKPs=
golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211006225509-1a26e0398eed h1:E159xujlywdAeN3FqsTBPzRKGUq/pDHolXbuttkC36E=
golang.org/x/sys v0.0.0-20211006225509-1a26e0398eed/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View file

@ -14,6 +14,8 @@ type gamePlayer struct {
weapon *playerWeapon
score int
health int
}
@ -34,7 +36,7 @@ func NewPlayer() (*gamePlayer, error) {
sprite: uziSprite,
cooldown: 100 * time.Millisecond,
},
health: 7,
health: 1,
}
return p, nil
}