Add sound effects
This commit is contained in:
parent
705217676a
commit
7a6467a498
8 changed files with 129 additions and 6 deletions
BIN
game/asset/audio/destroy.ogg
Normal file
BIN
game/asset/audio/destroy.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/fire.ogg
Normal file
BIN
game/asset/audio/fire.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/hit.ogg
Normal file
BIN
game/asset/audio/hit.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/select.ogg
Normal file
BIN
game/asset/audio/select.ogg
Normal file
Binary file not shown.
96
game/game.go
96
game/game.go
|
@ -16,13 +16,15 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/audio"
|
||||
"github.com/hajimehoshi/ebiten/v2/audio/vorbis"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
)
|
||||
|
||||
const screenWidth, screenHeight = 1920, 1080
|
||||
|
||||
const worldWidth, worldHeight = 8, 4
|
||||
const worldWidth, worldHeight = 6, 2
|
||||
|
||||
const worldTileSize = 1536
|
||||
|
||||
|
@ -104,7 +106,48 @@ const rad = math.Pi * 2
|
|||
|
||||
const gamepadDeadZone = 0.2
|
||||
|
||||
const sampleRate = 44100
|
||||
|
||||
var audioContext *audio.Context
|
||||
|
||||
var (
|
||||
Sounds [][]byte
|
||||
)
|
||||
|
||||
type soundEffect int
|
||||
|
||||
const (
|
||||
SoundSelect soundEffect = iota
|
||||
SoundFire
|
||||
SoundHit
|
||||
SoundDestroy
|
||||
)
|
||||
|
||||
func LoadBytes(p string) []byte {
|
||||
b, err := assetFS.ReadFile(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func loadBytes(p string) []byte {
|
||||
b, err := assetFS.ReadFile(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func loadAssets() {
|
||||
audioContext = audio.NewContext(sampleRate)
|
||||
p := "asset/audio/"
|
||||
|
||||
Sounds = append(Sounds, loadBytes(p+"select.ogg"))
|
||||
Sounds = append(Sounds, loadBytes(p+"fire.ogg"))
|
||||
Sounds = append(Sounds, loadBytes(p+"hit.ogg"))
|
||||
Sounds = append(Sounds, loadBytes(p+"destroy.ogg"))
|
||||
|
||||
tankHullImages = make([]*ebiten.Image, len(tankNames))
|
||||
tankTurretImages = make([]*ebiten.Image, len(tankNames))
|
||||
for i, name := range tankNames {
|
||||
|
@ -128,6 +171,28 @@ func loadAssets() {
|
|||
}
|
||||
}
|
||||
|
||||
func playSound(effect soundEffect, div float64) {
|
||||
sound := Sounds[effect]
|
||||
stream, err := vorbis.DecodeWithoutResampling(bytes.NewReader(sound))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
player, err := audioContext.NewPlayer(&oggStream{stream})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
volume := 0.4
|
||||
switch effect {
|
||||
case SoundSelect, SoundHit:
|
||||
volume = 0.25
|
||||
}
|
||||
volume /= div
|
||||
player.SetVolume(volume)
|
||||
player.Play()
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
sim *Simulation
|
||||
treadMarks [][2]float64
|
||||
|
@ -333,18 +398,24 @@ func (g *Game) Update() error {
|
|||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyQ) {
|
||||
g.size1++
|
||||
} else if inpututil.IsKeyJustPressed(ebiten.KeyZ) {
|
||||
go playSound(SoundSelect, 1)
|
||||
} else if inpututil.IsKeyJustPressed(ebiten.KeyA) {
|
||||
g.size1--
|
||||
if g.size1 < 1 {
|
||||
g.size1 = 1
|
||||
} else {
|
||||
go playSound(SoundSelect, 1)
|
||||
}
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyE) {
|
||||
g.size2++
|
||||
} else if inpututil.IsKeyJustPressed(ebiten.KeyC) {
|
||||
go playSound(SoundSelect, 1)
|
||||
} else if inpututil.IsKeyJustPressed(ebiten.KeyD) {
|
||||
g.size2--
|
||||
if g.size2 < 0 {
|
||||
g.size2 = 0
|
||||
} else {
|
||||
go playSound(SoundSelect, 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,10 +425,13 @@ func (g *Game) Update() error {
|
|||
if lastLeft >= -gamepadDeadZone && lastLeft <= gamepadDeadZone {
|
||||
if g.leftStick < -gamepadDeadZone {
|
||||
g.size1++
|
||||
go playSound(SoundSelect, 1)
|
||||
} else if g.leftStick > gamepadDeadZone {
|
||||
g.size1--
|
||||
if g.size1 < 1 {
|
||||
g.size1 = 1
|
||||
} else {
|
||||
go playSound(SoundSelect, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,10 +440,13 @@ func (g *Game) Update() error {
|
|||
if lastRight >= -gamepadDeadZone && lastRight <= gamepadDeadZone {
|
||||
if g.rightStick < -gamepadDeadZone {
|
||||
g.size2++
|
||||
go playSound(SoundSelect, 1)
|
||||
} else if g.rightStick > gamepadDeadZone {
|
||||
g.size2--
|
||||
if g.size2 < 0 {
|
||||
g.size2 = 0
|
||||
} else {
|
||||
go playSound(SoundSelect, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -465,6 +542,7 @@ func (g *Game) Update() error {
|
|||
input = input.Set(InputFire)
|
||||
}
|
||||
}
|
||||
|
||||
g.sim.SetInput(0, input)
|
||||
|
||||
g.setComputerInputs()
|
||||
|
@ -606,7 +684,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|||
}
|
||||
|
||||
{
|
||||
label := "ADJUST TEAM 1: Q / Z OR LEFT JOY UP / DOWN"
|
||||
label := "ADJUST TEAM 1: Q / A OR LEFT JOY UP / DOWN"
|
||||
w := len(label)*6 + 5
|
||||
h := 15
|
||||
g.scratchImage.Clear()
|
||||
|
@ -619,7 +697,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|||
}
|
||||
|
||||
{
|
||||
label := "ADJUST TEAM 2: E / C OR RIGHT JOY UP / DOWN"
|
||||
label := "ADJUST TEAM 2: E / D OR RIGHT JOY UP / DOWN"
|
||||
w := len(label)*6 + 5
|
||||
h := 15
|
||||
g.scratchImage.Clear()
|
||||
|
@ -983,6 +1061,14 @@ func offscreen(x float64, y float64, size float64) bool {
|
|||
return x < viewX-size || y < viewY-size || x >= viewX+screenWidth+size || y >= viewY+screenHeight+size
|
||||
}
|
||||
|
||||
type oggStream struct {
|
||||
*vorbis.Stream
|
||||
}
|
||||
|
||||
func (s *oggStream) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var startButtons = []ebiten.StandardGamepadButton{
|
||||
ebiten.StandardGamepadButtonRightBottom,
|
||||
ebiten.StandardGamepadButtonRightRight,
|
||||
|
|
|
@ -167,7 +167,7 @@ func (s *Simulation) generateMap() {
|
|||
}
|
||||
const treeCount = 64
|
||||
{
|
||||
const bufferSize = 25
|
||||
const bufferSize = 125
|
||||
for i := 0; i < treeCount; i++ {
|
||||
TREES:
|
||||
for {
|
||||
|
@ -273,10 +273,19 @@ func (s *Simulation) tickObjects() {
|
|||
r = r.Inset(10)
|
||||
}
|
||||
if p.In(r) {
|
||||
playHitSound := func() {
|
||||
div := 1.0
|
||||
if offscreen(playerState.X, playerState.Y, tankSize) {
|
||||
div = 10
|
||||
}
|
||||
go playSound(SoundHit, div)
|
||||
}
|
||||
switch newObject.O {
|
||||
case ObjectTree:
|
||||
playHitSound()
|
||||
newObject.O = ObjectTreeDestroyed
|
||||
case ObjectSnowman:
|
||||
playHitSound()
|
||||
newObject.O = ObjectSnowmanDestroyed
|
||||
s.killFeed = append(s.killFeed, &killFeedEntry{
|
||||
Killer: playerName(player),
|
||||
|
@ -315,11 +324,20 @@ ROUNDS:
|
|||
w /= 2
|
||||
}
|
||||
if p.In(image.Rect(x, y, x+w, y+h)) {
|
||||
playHitSound := func() {
|
||||
div := 1.0
|
||||
if offscreen(state.X, state.Y, tankSize) {
|
||||
div = 10
|
||||
}
|
||||
go playSound(SoundHit, div)
|
||||
}
|
||||
switch object.O {
|
||||
case ObjectTree:
|
||||
playHitSound()
|
||||
object.O = ObjectTreeDestroyed
|
||||
continue ROUNDS
|
||||
case ObjectSnowman:
|
||||
playHitSound()
|
||||
object.O = ObjectSnowmanDestroyed
|
||||
s.killFeed = append(s.killFeed, &killFeedEntry{
|
||||
Killer: playerName(state.Player),
|
||||
|
@ -339,6 +357,11 @@ ROUNDS:
|
|||
if playerState.Team == state.Team || (s.godMode && player == 0) {
|
||||
continue ROUNDS
|
||||
}
|
||||
div := 1.0
|
||||
if offscreen(state.X, state.Y, tankSize) {
|
||||
div = 10
|
||||
}
|
||||
go playSound(SoundDestroy, div)
|
||||
playerState.Hit = true
|
||||
s.killFeed = append(s.killFeed, &killFeedEntry{
|
||||
Killer: playerName(state.Player),
|
||||
|
@ -380,6 +403,11 @@ func (s *Simulation) tickInputs() {
|
|||
if lastFire == 0 || s.offset+s.frame-lastFire >= fireCooldown {
|
||||
state := s.states[s.frame][player]
|
||||
if state != nil {
|
||||
div := 1.0
|
||||
if offscreen(state.X, state.Y, tankSize) {
|
||||
div = 10
|
||||
}
|
||||
go playSound(SoundFire, div)
|
||||
r := state.R + state.T
|
||||
s.rounds[s.frame] = append(s.rounds[s.frame], &roundState{
|
||||
X: state.X + float64(roundOffset)*math.Cos(r) + tankSize/2,
|
||||
|
|
3
go.mod
3
go.mod
|
@ -5,8 +5,11 @@ go 1.21.6
|
|||
require github.com/hajimehoshi/ebiten/v2 v2.6.5
|
||||
|
||||
require (
|
||||
github.com/ebitengine/oto/v3 v3.1.0 // indirect
|
||||
github.com/ebitengine/purego v0.5.2 // indirect
|
||||
github.com/jezek/xgb v1.1.1 // indirect
|
||||
github.com/jfreymuth/oggvorbis v1.0.5 // indirect
|
||||
github.com/jfreymuth/vorbis v1.0.2 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20240205201215-2c58cdc269a3 // indirect
|
||||
golang.org/x/image v0.15.0 // indirect
|
||||
golang.org/x/mobile v0.0.0-20240112133503-c713f31d574b // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -1,9 +1,15 @@
|
|||
github.com/ebitengine/oto/v3 v3.1.0 h1:9tChG6rizyeR2w3vsygTTTVVJ9QMMyu00m2yBOCch6U=
|
||||
github.com/ebitengine/oto/v3 v3.1.0/go.mod h1:IK1QTnlfZK2GIB6ziyECm433hAdTaPpOsGMLhEyEGTg=
|
||||
github.com/ebitengine/purego v0.5.2 h1:r2MQEtkGzZ4LRtFZVAg5bjYKnUbxxloaeuGxH0t7qfs=
|
||||
github.com/ebitengine/purego v0.5.2/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.5 h1:lALv+qhEK3CBWViyiGpz4YcR6slVJEjCiS7sExKZ9OE=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.5/go.mod h1:TZtorL713an00UW4LyvMeKD8uXWnuIuCPtlH11b0pgI=
|
||||
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
|
||||
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||
github.com/jfreymuth/oggvorbis v1.0.5 h1:u+Ck+R0eLSRhgq8WTmffYnrVtSztJcYrl588DM4e3kQ=
|
||||
github.com/jfreymuth/oggvorbis v1.0.5/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
|
||||
github.com/jfreymuth/vorbis v1.0.2 h1:m1xH6+ZI4thH927pgKD8JOH4eaGRm18rEE9/0WKjvNE=
|
||||
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
|
||||
golang.org/x/exp/shiny v0.0.0-20240205201215-2c58cdc269a3 h1:tImqKNm/Iclm3Rqb6GffLiURSp3m1iRx/C4mturH8Ys=
|
||||
golang.org/x/exp/shiny v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
|
||||
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
||||
|
|
Loading…
Reference in a new issue