Add vampire kill sounds
This commit is contained in:
parent
e5e8af1e9b
commit
74c1aca406
7 changed files with 128 additions and 63 deletions
BIN
assets/audio/vampiredie1.mp3
Normal file
BIN
assets/audio/vampiredie1.mp3
Normal file
Binary file not shown.
BIN
assets/audio/vampiredie2.mp3
Normal file
BIN
assets/audio/vampiredie2.mp3
Normal file
Binary file not shown.
13
creep.go
13
creep.go
|
@ -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
165
game.go
|
@ -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
3
go.mod
|
@ -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
6
go.sum
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue