Add sound effects
This commit is contained in:
parent
cb8feff508
commit
ba4aa7af86
20 changed files with 241 additions and 15 deletions
|
@ -1,3 +1,6 @@
|
|||
1.0.5:
|
||||
- Add sound effects
|
||||
|
||||
1.0.4:
|
||||
- Optimize user interface for mobile devices
|
||||
|
||||
|
|
BIN
game/asset/audio/dice1.ogg
Normal file
BIN
game/asset/audio/dice1.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/dice2.ogg
Normal file
BIN
game/asset/audio/dice2.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/dice3.ogg
Normal file
BIN
game/asset/audio/dice3.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/dice4.ogg
Normal file
BIN
game/asset/audio/dice4.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/die1.ogg
Normal file
BIN
game/asset/audio/die1.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/die2.ogg
Normal file
BIN
game/asset/audio/die2.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/die3.ogg
Normal file
BIN
game/asset/audio/die3.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/joinleave.ogg
Normal file
BIN
game/asset/audio/joinleave.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/move1.ogg
Normal file
BIN
game/asset/audio/move1.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/move2.ogg
Normal file
BIN
game/asset/audio/move2.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/move3.ogg
Normal file
BIN
game/asset/audio/move3.ogg
Normal file
Binary file not shown.
BIN
game/asset/audio/say.ogg
Normal file
BIN
game/asset/audio/say.ogg
Normal file
Binary file not shown.
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
@ -826,7 +826,7 @@ func (b *board) setRect(x, y, w, h int) {
|
|||
b.innerW = int(float64(b.w) - (b.horizontalBorderSize * 2))
|
||||
b.innerH = int(float64(b.h) - (b.verticalBorderSize * 2))
|
||||
|
||||
loadAssets(int(b.spaceWidth))
|
||||
loadImageAssets(int(b.spaceWidth))
|
||||
|
||||
for i := 0; i < b.Sprites.num; i++ {
|
||||
s := b.Sprites.sprites[i]
|
||||
|
|
236
game/game.go
236
game/game.go
|
@ -7,7 +7,9 @@ import (
|
|||
"image"
|
||||
"image/color"
|
||||
_ "image/png"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
|
@ -20,6 +22,9 @@ import (
|
|||
"code.rocketnine.space/tslocum/kibodo"
|
||||
"code.rocketnine.space/tslocum/messeji"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/audio"
|
||||
"github.com/hajimehoshi/ebiten/v2/audio/vorbis"
|
||||
"github.com/hajimehoshi/ebiten/v2/audio/wav"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
|
@ -35,8 +40,8 @@ const MaxDebug = 1
|
|||
|
||||
var onlyNumbers = regexp.MustCompile(`[0-9]+`)
|
||||
|
||||
//go:embed assets
|
||||
var assetsFS embed.FS
|
||||
//go:embed asset
|
||||
var assetFS embed.FS
|
||||
|
||||
var debugExtra []byte
|
||||
|
||||
|
@ -119,6 +124,18 @@ var (
|
|||
listGamesFrame *etk.Frame
|
||||
)
|
||||
|
||||
const sampleRate = 44100
|
||||
|
||||
var (
|
||||
audioContext *audio.Context
|
||||
|
||||
SoundDie1, SoundDie2, SoundDie3 []byte
|
||||
SoundDice1, SoundDice2, SoundDice3, SoundDice4 []byte
|
||||
SoundMove1, SoundMove2, SoundMove3 []byte
|
||||
SoundJoinLeave []byte
|
||||
SoundSay []byte
|
||||
)
|
||||
|
||||
func l(s string) {
|
||||
m := time.Now().Format("15:04") + " " + s
|
||||
if statusLogged {
|
||||
|
@ -182,10 +199,9 @@ func init() {
|
|||
inputBuffer.Field.SetBackgroundColor(bufferBackgroundColor)
|
||||
inputBuffer.Field.SetSuffix("")
|
||||
}
|
||||
|
||||
func loadAssets(width int) {
|
||||
imgCheckerLight = loadAsset("assets/checker_white.png", width)
|
||||
imgCheckerDark = loadAsset("assets/checker_white.png", width)
|
||||
func loadImageAssets(width int) {
|
||||
imgCheckerLight = loadAsset("asset/image/checker_white.png", width)
|
||||
imgCheckerDark = loadAsset("asset/image/checker_white.png", width)
|
||||
//imgCheckerDark = loadAsset("assets/checker_black.png", width)
|
||||
|
||||
resizeDice := func(img image.Image) *ebiten.Image {
|
||||
|
@ -198,7 +214,7 @@ func loadAssets(width int) {
|
|||
}
|
||||
|
||||
const size = 184
|
||||
imgDice = ebiten.NewImageFromImage(loadImage("assets/dice.png"))
|
||||
imgDice = ebiten.NewImageFromImage(loadImage("asset/image/dice.png"))
|
||||
imgDice1 = resizeDice(imgDice.SubImage(image.Rect(0, 0, size*1, size*1)))
|
||||
imgDice2 = resizeDice(imgDice.SubImage(image.Rect(size*1, 0, size*2, size*1)))
|
||||
imgDice3 = resizeDice(imgDice.SubImage(image.Rect(size*2, 0, size*3, size*1)))
|
||||
|
@ -206,9 +222,56 @@ func loadAssets(width int) {
|
|||
imgDice5 = resizeDice(imgDice.SubImage(image.Rect(size*1, size*1, size*2, size*2)))
|
||||
imgDice6 = resizeDice(imgDice.SubImage(image.Rect(size*2, size*1, size*3, size*2)))
|
||||
}
|
||||
func loadAudioAssets() {
|
||||
audioContext = audio.NewContext(sampleRate)
|
||||
p := "asset/audio/"
|
||||
|
||||
SoundDie1 = LoadBytes(p + "die1.ogg")
|
||||
SoundDie2 = LoadBytes(p + "die2.ogg")
|
||||
SoundDie3 = LoadBytes(p + "die3.ogg")
|
||||
|
||||
SoundDice1 = LoadBytes(p + "dice1.ogg")
|
||||
SoundDice2 = LoadBytes(p + "dice2.ogg")
|
||||
SoundDice3 = LoadBytes(p + "dice3.ogg")
|
||||
SoundDice4 = LoadBytes(p + "dice4.ogg")
|
||||
|
||||
SoundMove1 = LoadBytes(p + "move1.ogg")
|
||||
SoundMove2 = LoadBytes(p + "move2.ogg")
|
||||
SoundMove3 = LoadBytes(p + "move3.ogg")
|
||||
|
||||
SoundJoinLeave = LoadBytes(p + "joinleave.ogg")
|
||||
SoundSay = LoadBytes(p + "say.ogg")
|
||||
|
||||
dieSounds = [][]byte{
|
||||
SoundDie1,
|
||||
SoundDie2,
|
||||
SoundDie3,
|
||||
}
|
||||
randomizeByteSlice(dieSounds)
|
||||
|
||||
diceSounds = [][]byte{
|
||||
SoundDice1,
|
||||
SoundDice2,
|
||||
SoundDice3,
|
||||
SoundDice4,
|
||||
}
|
||||
randomizeByteSlice(diceSounds)
|
||||
|
||||
moveSounds = [][]byte{
|
||||
SoundMove1,
|
||||
SoundMove2,
|
||||
SoundMove3,
|
||||
}
|
||||
randomizeByteSlice(moveSounds)
|
||||
}
|
||||
|
||||
func loadAssets(width int) {
|
||||
loadImageAssets(width)
|
||||
loadAudioAssets()
|
||||
}
|
||||
|
||||
func loadImage(assetPath string) image.Image {
|
||||
f, err := assetsFS.Open(assetPath)
|
||||
f, err := assetFS.Open(assetPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -231,6 +294,65 @@ func loadAsset(assetPath string, width int) *ebiten.Image {
|
|||
return ebiten.NewImageFromImage(img)
|
||||
}
|
||||
|
||||
func LoadBytes(p string) []byte {
|
||||
b, err := assetFS.ReadFile(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func LoadWAV(context *audio.Context, p string) *audio.Player {
|
||||
f, err := assetFS.Open(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
stream, err := wav.DecodeWithSampleRate(sampleRate, f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
player, err := audio.NewPlayer(audioContext, io.NopCloser(stream))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Workaround to prevent delays when playing for the first time.
|
||||
player.SetVolume(0)
|
||||
player.Play()
|
||||
player.Pause()
|
||||
player.Rewind()
|
||||
player.SetVolume(1)
|
||||
|
||||
return player
|
||||
}
|
||||
|
||||
type oggStream struct {
|
||||
*vorbis.Stream
|
||||
}
|
||||
|
||||
func (s *oggStream) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadOGG(context *audio.Context, p string) *audio.Player {
|
||||
b := LoadBytes(p)
|
||||
|
||||
stream, err := vorbis.DecodeWithSampleRate(sampleRate, bytes.NewReader(b))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
player, err := audio.NewPlayer(audioContext, &oggStream{Stream: stream})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return player
|
||||
}
|
||||
|
||||
func initializeFonts() {
|
||||
tt, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
|
||||
if err != nil {
|
||||
|
@ -393,6 +515,8 @@ type Game struct {
|
|||
|
||||
lobby *lobby
|
||||
|
||||
volume float64 // Volume range is 0-1.
|
||||
|
||||
runeBuffer []rune
|
||||
userInput string
|
||||
|
||||
|
@ -434,6 +558,7 @@ func NewGame() *Game {
|
|||
TouchInput: AutoEnableTouchInput,
|
||||
|
||||
debugImg: ebiten.NewImage(200, 200),
|
||||
volume: 1,
|
||||
}
|
||||
game = g
|
||||
|
||||
|
@ -671,6 +796,7 @@ func (g *Game) handleEvents() {
|
|||
l(fmt.Sprintf("*** %s", ev.Message))
|
||||
case *bgammon.EventSay:
|
||||
l(fmt.Sprintf("<%s> %s", ev.Player, ev.Message))
|
||||
playSoundEffect(effectSay)
|
||||
case *bgammon.EventList:
|
||||
g.lobby.setGameList(ev.Games)
|
||||
if !viewBoard {
|
||||
|
@ -692,6 +818,7 @@ func (g *Game) handleEvents() {
|
|||
gameLogged = false
|
||||
} else {
|
||||
lg(fmt.Sprintf("%s joined the match.", ev.Player))
|
||||
playSoundEffect(effectJoinLeave)
|
||||
}
|
||||
case *bgammon.EventFailedJoin:
|
||||
l(fmt.Sprintf("*** Failed to join match: %s", ev.Reason))
|
||||
|
@ -709,10 +836,9 @@ func (g *Game) handleEvents() {
|
|||
g.Board.Unlock()
|
||||
if ev.Player == g.Client.Username {
|
||||
setViewBoard(false)
|
||||
}
|
||||
|
||||
if ev.Player != g.Client.Username {
|
||||
} else {
|
||||
lg(fmt.Sprintf("%s left the match.", ev.Player))
|
||||
playSoundEffect(effectJoinLeave)
|
||||
}
|
||||
case *bgammon.EventBoard:
|
||||
g.Board.Lock()
|
||||
|
@ -732,8 +858,10 @@ func (g *Game) handleEvents() {
|
|||
} else {
|
||||
diceFormatted = fmt.Sprintf("%d", g.Board.gameState.Roll2)
|
||||
}
|
||||
playSoundEffect(effectDie)
|
||||
} else {
|
||||
diceFormatted = fmt.Sprintf("%d-%d", g.Board.gameState.Roll1, g.Board.gameState.Roll2)
|
||||
playSoundEffect(effectDice)
|
||||
}
|
||||
g.Board.processState()
|
||||
g.Board.Unlock()
|
||||
|
@ -743,6 +871,7 @@ func (g *Game) handleEvents() {
|
|||
l(fmt.Sprintf("*** Failed to roll: %s", ev.Reason))
|
||||
case *bgammon.EventMoved:
|
||||
lg(fmt.Sprintf("%s moved %s.", ev.Player, bgammon.FormatMoves(ev.Moves)))
|
||||
playSoundEffect(effectMove)
|
||||
if ev.Player == g.Client.Username {
|
||||
continue
|
||||
}
|
||||
|
@ -1281,3 +1410,88 @@ func (g *Game) toggleProfiling() error {
|
|||
func (g *Game) Exit() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
type SoundEffect int
|
||||
|
||||
const (
|
||||
effectJoinLeave SoundEffect = iota
|
||||
effectSay
|
||||
effectDie
|
||||
effectDice
|
||||
effectMove
|
||||
)
|
||||
|
||||
var (
|
||||
dieSounds [][]byte
|
||||
dieSoundPlays int
|
||||
diceSounds [][]byte
|
||||
diceSoundPlays int
|
||||
moveSounds [][]byte
|
||||
moveSoundPlays int
|
||||
)
|
||||
|
||||
func playSoundEffect(effect SoundEffect) {
|
||||
if game.volume == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var b []byte
|
||||
switch effect {
|
||||
case effectSay:
|
||||
b = SoundSay
|
||||
case effectJoinLeave:
|
||||
b = SoundJoinLeave
|
||||
case effectDie:
|
||||
b = dieSounds[dieSoundPlays]
|
||||
|
||||
dieSoundPlays++
|
||||
if dieSoundPlays == len(dieSounds)-1 {
|
||||
randomizeByteSlice(dieSounds)
|
||||
dieSoundPlays = 0
|
||||
}
|
||||
case effectDice:
|
||||
b = diceSounds[diceSoundPlays]
|
||||
|
||||
diceSoundPlays++
|
||||
if diceSoundPlays == len(diceSounds)-1 {
|
||||
randomizeByteSlice(diceSounds)
|
||||
diceSoundPlays = 0
|
||||
}
|
||||
case effectMove:
|
||||
b = moveSounds[moveSoundPlays]
|
||||
|
||||
moveSoundPlays++
|
||||
if moveSoundPlays == len(moveSounds)-1 {
|
||||
randomizeByteSlice(moveSounds)
|
||||
moveSoundPlays = 0
|
||||
}
|
||||
default:
|
||||
log.Panicf("unknown sound effect: %d", effect)
|
||||
return
|
||||
}
|
||||
|
||||
stream, err := vorbis.DecodeWithoutResampling(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
player, err := audioContext.NewPlayer(&oggStream{stream})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if effect == effectSay {
|
||||
player.SetVolume(game.volume / 2)
|
||||
} else {
|
||||
player.SetVolume(game.volume)
|
||||
}
|
||||
|
||||
player.Play()
|
||||
}
|
||||
|
||||
func randomizeByteSlice(b [][]byte) {
|
||||
for i := range b {
|
||||
j := rand.Intn(i + 1)
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
}
|
||||
|
|
5
go.mod
5
go.mod
|
@ -4,7 +4,7 @@ go 1.17
|
|||
|
||||
require (
|
||||
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231027191341-991fd6d481ca
|
||||
code.rocket9labs.com/tslocum/etk v0.0.0-20231028192430-e54b05cdd05a
|
||||
code.rocket9labs.com/tslocum/etk v0.0.0-20231028200807-4833fa2b2761
|
||||
code.rocketnine.space/tslocum/kibodo v1.0.2-0.20231024233002-77bb43ba6fe8
|
||||
code.rocketnine.space/tslocum/messeji v1.0.5-0.20231028192343-ebfed687fb71
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.2
|
||||
|
@ -15,9 +15,12 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/ebitengine/oto/v3 v3.1.0 // indirect
|
||||
github.com/ebitengine/purego v0.5.0 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/jezek/xgb v1.1.0 // 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-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe // indirect
|
||||
golang.org/x/sync v0.4.0 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -1,11 +1,13 @@
|
|||
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231027191341-991fd6d481ca h1:JClRU4ONLpMiyW/BoI3O8Sb9yiYcySTZjcUfQ48GWvw=
|
||||
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231027191341-991fd6d481ca/go.mod h1:U8qo60VHGzKFUHLZZJcvT0yDzwWybJBabsCw3Lyqx4s=
|
||||
code.rocket9labs.com/tslocum/etk v0.0.0-20231028192430-e54b05cdd05a h1:Gs/Xku+fPLNNlaYEAubFNI0j5QtlYEcQjxo47phkK0E=
|
||||
code.rocket9labs.com/tslocum/etk v0.0.0-20231028192430-e54b05cdd05a/go.mod h1:C+pdWMPmOOPGk6adunX6PxE40F8CSg0h8LLwWIO2qeM=
|
||||
code.rocket9labs.com/tslocum/etk v0.0.0-20231028200807-4833fa2b2761 h1:vSfheD/jWR0QkdlSME6mz2cJEET0d0DHiRMMASxL2N0=
|
||||
code.rocket9labs.com/tslocum/etk v0.0.0-20231028200807-4833fa2b2761/go.mod h1:C+pdWMPmOOPGk6adunX6PxE40F8CSg0h8LLwWIO2qeM=
|
||||
code.rocketnine.space/tslocum/kibodo v1.0.2-0.20231024233002-77bb43ba6fe8 h1:i1NzTMQA1DAAUIpFh2bnHVnH5j9hUkG6F3tqzsdD16Y=
|
||||
code.rocketnine.space/tslocum/kibodo v1.0.2-0.20231024233002-77bb43ba6fe8/go.mod h1:C7M1NUuVi0Mv+/xraUurjl4XSLRIILmWDWCBBOY4UeM=
|
||||
code.rocketnine.space/tslocum/messeji v1.0.5-0.20231028192343-ebfed687fb71 h1:mcvrQKHfec2Fb4dBXb879v0lBeHFzEBqs15ETnPv4pw=
|
||||
code.rocketnine.space/tslocum/messeji v1.0.5-0.20231028192343-ebfed687fb71/go.mod h1:xszLyTZtpyjCVaGmznizLSAlnvraPOSoanlzUBeqGco=
|
||||
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.0 h1:JrMGKfRIAM4/QVKaesIIT7m/UVjTj5GYhRSQYwfVdpo=
|
||||
github.com/ebitengine/purego v0.5.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
|
@ -15,6 +17,10 @@ github.com/hajimehoshi/ebiten/v2 v2.6.2 h1:tVa3ZJbp4Uz/VSjmpgtQIOvwd7aQH290XehHB
|
|||
github.com/hajimehoshi/ebiten/v2 v2.6.2/go.mod h1:TZtorL713an00UW4LyvMeKD8uXWnuIuCPtlH11b0pgI=
|
||||
github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk=
|
||||
github.com/jezek/xgb v1.1.0/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=
|
||||
github.com/llgcode/draw2d v0.0.0-20231022063514-1acb54133d2a h1:aP1ySrs3EYBaKOF+1hEUbIMNjT8FZlGGbB73cRAukZw=
|
||||
github.com/llgcode/draw2d v0.0.0-20231022063514-1acb54133d2a/go.mod h1:zNlGqkQNLxAN7D2uihSJsrEzrkWrSIK5kmSZU/dN5NY=
|
||||
github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb h1:61ndUreYSlWFeCY44JxDDkngVoI7/1MVhEl98Nm0KOk=
|
||||
|
|
Loading…
Reference in a new issue