Add player action and tick system
This commit is contained in:
parent
ea3c90eb46
commit
85f6b3ebd1
|
@ -5,11 +5,21 @@ import (
|
|||
"image/color"
|
||||
)
|
||||
|
||||
type PlayerAction int
|
||||
|
||||
const (
|
||||
ActionIdle PlayerAction = iota
|
||||
ActionPunch
|
||||
)
|
||||
|
||||
type Player struct {
|
||||
X float64
|
||||
Y float64
|
||||
Color color.Color
|
||||
PlayerNum int
|
||||
|
||||
Action PlayerAction
|
||||
ActionTicksLeft int
|
||||
}
|
||||
|
||||
func (p *Player) String() string {
|
||||
|
|
97
game/game.go
97
game/game.go
|
@ -16,6 +16,7 @@ import (
|
|||
"code.rocketnine.space/tslocum/gohan"
|
||||
"github.com/assemblaj/ggpo"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
)
|
||||
|
||||
var backend ggpo.Backend
|
||||
|
@ -66,12 +67,13 @@ func (g *Game) clone() (result *Game) {
|
|||
return
|
||||
}
|
||||
|
||||
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
|
||||
if outsideWidth != world.ScreenWidth || outsideHeight != world.ScreenHeight {
|
||||
func (g *Game) Layout(_, _ int) (screenWidth, screenHeight int) {
|
||||
// Maintain constant internal resolution.
|
||||
if world.InternalScreenWidth != world.ScreenWidth || world.InternalScreenHeight != world.ScreenHeight {
|
||||
if world.ScreenWidth != 0 || world.ScreenHeight != 0 {
|
||||
etk.Layout(outsideWidth, outsideHeight)
|
||||
etk.Layout(world.InternalScreenWidth, world.InternalScreenHeight)
|
||||
}
|
||||
world.ScreenWidth, world.ScreenHeight = outsideWidth, outsideHeight
|
||||
world.ScreenWidth, world.ScreenHeight = world.InternalScreenWidth, world.InternalScreenHeight
|
||||
}
|
||||
return world.ScreenWidth, world.ScreenHeight
|
||||
}
|
||||
|
@ -101,7 +103,7 @@ func (g *Game) Update() error {
|
|||
|
||||
localPort := world.LocalPort
|
||||
if localPort == 0 {
|
||||
localPort = p
|
||||
localPort = p + 1
|
||||
}
|
||||
|
||||
numPlayers := 2
|
||||
|
@ -131,6 +133,8 @@ func (g *Game) Update() error {
|
|||
|
||||
g.InitNetworking(l, numPlayers, players, 0)
|
||||
|
||||
g.playerStateUpdated()
|
||||
|
||||
world.ConnectPromptActive = true
|
||||
}
|
||||
|
||||
|
@ -139,15 +143,14 @@ func (g *Game) Update() error {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.RunFrame()
|
||||
|
||||
g.updatePlayerState() // TODO only after advanceframe?
|
||||
g.RunFrame()
|
||||
}
|
||||
|
||||
return gohan.Update()
|
||||
}
|
||||
|
||||
func (g *Game) updatePlayerState() {
|
||||
func (g *Game) playerStateUpdated() {
|
||||
world.Player1, world.Player2 = g.Players[0], g.Players[1]
|
||||
}
|
||||
|
||||
|
@ -225,45 +228,44 @@ func (g *Game) RunFrame() {
|
|||
}
|
||||
|
||||
func (g *Game) AdvanceFrame(inputs []InputBits, disconnectFlags int) {
|
||||
if world.ConnectPromptVisible {
|
||||
// We are connected now.
|
||||
|
||||
world.ConnectPromptVisible = false
|
||||
log.Println("Connected successfully")
|
||||
}
|
||||
|
||||
g.UpdateByInputs(inputs)
|
||||
err := backend.AdvanceFrame(uint32(g.Checksum()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for i := range g.Players {
|
||||
if g.Players[i].ActionTicksLeft != 0 {
|
||||
g.Players[i].ActionTicksLeft--
|
||||
if g.Players[i].ActionTicksLeft == 0 {
|
||||
g.Players[i].Action = component.ActionIdle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.playerStateUpdated()
|
||||
}
|
||||
|
||||
func (g *Game) UpdateByInputs(inputs []InputBits) {
|
||||
for i, input := range inputs {
|
||||
if input.isButtonOn(int(ebiten.KeyArrowUp)) {
|
||||
if input.isButtonOn(ButtonUp) {
|
||||
g.Players[i].Y--
|
||||
}
|
||||
if input.isButtonOn(int(ebiten.KeyArrowDown)) {
|
||||
if input.isButtonOn(ButtonDown) {
|
||||
g.Players[i].Y++
|
||||
}
|
||||
if input.isButtonOn(int(ebiten.KeyArrowLeft)) {
|
||||
if input.isButtonOn(ButtonLeft) {
|
||||
g.Players[i].X--
|
||||
}
|
||||
if input.isButtonOn(int(ebiten.KeyArrowRight)) {
|
||||
if input.isButtonOn(ButtonRight) {
|
||||
g.Players[i].X++
|
||||
}
|
||||
if input.isButtonOn(int(ebiten.KeyW)) {
|
||||
g.Players[i].Y--
|
||||
}
|
||||
if input.isButtonOn(int(ebiten.KeyS)) {
|
||||
g.Players[i].Y++
|
||||
}
|
||||
if input.isButtonOn(int(ebiten.KeyA)) {
|
||||
g.Players[i].X--
|
||||
}
|
||||
if input.isButtonOn(int(ebiten.KeyD)) {
|
||||
g.Players[i].X++
|
||||
|
||||
if g.Players[i].Action == component.ActionIdle {
|
||||
if input.isButtonOn(ButtonPunch) {
|
||||
g.Players[i].Action = component.ActionPunch
|
||||
g.Players[i].ActionTicksLeft = 25 // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -271,36 +273,31 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
|
|||
func (g *Game) ReadInputs() InputBits {
|
||||
var in InputBits
|
||||
|
||||
if ebiten.IsKeyPressed(ebiten.KeyArrowUp) {
|
||||
in.setButton(int(ebiten.KeyArrowUp))
|
||||
if ebiten.IsKeyPressed(ebiten.KeyArrowUp) || ebiten.IsKeyPressed(ebiten.KeyW) {
|
||||
in.setButton(ButtonUp)
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyArrowDown) {
|
||||
in.setButton(int(ebiten.KeyArrowDown))
|
||||
if ebiten.IsKeyPressed(ebiten.KeyArrowDown) || ebiten.IsKeyPressed(ebiten.KeyS) {
|
||||
in.setButton(ButtonDown)
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyArrowLeft) {
|
||||
in.setButton(int(ebiten.KeyArrowLeft))
|
||||
if ebiten.IsKeyPressed(ebiten.KeyArrowLeft) || ebiten.IsKeyPressed(ebiten.KeyA) {
|
||||
in.setButton(ButtonLeft)
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyArrowRight) {
|
||||
in.setButton(int(ebiten.KeyArrowRight))
|
||||
if ebiten.IsKeyPressed(ebiten.KeyArrowRight) || ebiten.IsKeyPressed(ebiten.KeyD) {
|
||||
in.setButton(ButtonRight)
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyH) {
|
||||
in.setButton(ButtonPunch)
|
||||
}
|
||||
|
||||
return in
|
||||
}
|
||||
|
||||
func (g *Game) ReadInputsP2() InputBits {
|
||||
var in InputBits
|
||||
|
||||
if ebiten.IsKeyPressed(ebiten.KeyW) {
|
||||
in.setButton(int(ebiten.KeyW))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyS) {
|
||||
in.setButton(int(ebiten.KeyS))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyA) {
|
||||
in.setButton(int(ebiten.KeyA))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyD) {
|
||||
in.setButton(int(ebiten.KeyD))
|
||||
}
|
||||
// TODO Support local multiplayer?
|
||||
|
||||
return in
|
||||
|
||||
}
|
||||
|
|
|
@ -13,11 +13,21 @@ type Input struct {
|
|||
|
||||
type InputBits int
|
||||
|
||||
func (i *InputBits) isButtonOn(button int) bool {
|
||||
type InputButton int
|
||||
|
||||
const (
|
||||
ButtonLeft InputButton = iota + 1
|
||||
ButtonRight
|
||||
ButtonDown
|
||||
ButtonUp
|
||||
ButtonPunch
|
||||
)
|
||||
|
||||
func (i *InputBits) isButtonOn(button InputButton) bool {
|
||||
return *i&(1<<button) > 0
|
||||
}
|
||||
|
||||
func (i *InputBits) setButton(button int) {
|
||||
func (i *InputBits) setButton(button InputButton) {
|
||||
*i |= (1 << button)
|
||||
}
|
||||
|
||||
|
@ -33,11 +43,11 @@ func writeI32(i32 int32) []byte {
|
|||
return b
|
||||
}
|
||||
|
||||
func (i *Input) isButtonOn(button int) bool {
|
||||
func (i *Input) isButtonOn(button InputButton) bool {
|
||||
return i.ButtonMap&(1<<button) > 0
|
||||
}
|
||||
|
||||
func (i *Input) setButton(button int) {
|
||||
func (i *Input) setButton(button InputButton) {
|
||||
i.ButtonMap |= (1 << button)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"code.rocketnine.space/tslocum/boxbrawl/world"
|
||||
|
||||
"github.com/assemblaj/ggpo"
|
||||
)
|
||||
|
||||
|
@ -64,35 +66,31 @@ func (g *Game) String() string {
|
|||
}
|
||||
|
||||
func (g *GameSession) AdvanceFrame(flags int) {
|
||||
fmt.Println("Advancing frame from callback. ")
|
||||
var discconectFlags int
|
||||
//fmt.Println("Advancing frame from callback.")
|
||||
var disconnectFlags int
|
||||
|
||||
// Make sure we fetch the inputs from GGPO and use these to update
|
||||
// the game state instead of reading from the keyboard.
|
||||
inputs, result := g.backend.SyncInput(&discconectFlags)
|
||||
inputs, result := g.backend.SyncInput(&disconnectFlags)
|
||||
if result == nil {
|
||||
input := decodeInputs(inputs)
|
||||
g.game.AdvanceFrame(input, discconectFlags)
|
||||
g.game.AdvanceFrame(input, disconnectFlags)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GameSession) OnEvent(info *ggpo.Event) {
|
||||
switch info.Code {
|
||||
case ggpo.EventCodeConnectedToPeer:
|
||||
log.Println("EventCodeConnectedToPeer")
|
||||
case ggpo.EventCodeSynchronizingWithPeer:
|
||||
log.Println("EventCodeSynchronizingWithPeer")
|
||||
case ggpo.EventCodeSynchronizedWithPeer:
|
||||
log.Println("EventCodeSynchronizedWithPeer")
|
||||
case ggpo.EventCodeRunning:
|
||||
log.Println("EventCodeRunning")
|
||||
case ggpo.EventCodeDisconnectedFromPeer:
|
||||
log.Println("EventCodeDisconnectedFromPeer")
|
||||
case ggpo.EventCodeTimeSync:
|
||||
log.Println("EventCodeTimeSync")
|
||||
// We are fully connected now.
|
||||
if world.ConnectPromptVisible {
|
||||
log.Println("Connection established")
|
||||
world.ConnectPromptVisible = false
|
||||
}
|
||||
case ggpo.EventCodeConnectionInterrupted:
|
||||
log.Println("EventCodeconnectionInterrupted")
|
||||
log.Println("Connection interrupted")
|
||||
case ggpo.EventCodeConnectionResumed:
|
||||
log.Println("EventCodeconnectionInterrupted")
|
||||
log.Println("Connection resumed")
|
||||
case ggpo.EventCodeDisconnectedFromPeer:
|
||||
log.Println("Connection lost")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"code.rocketnine.space/tslocum/boxbrawl/world"
|
||||
"code.rocketnine.space/tslocum/gohan"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
)
|
||||
|
||||
type PlayerSystem struct {
|
||||
|
@ -47,6 +48,11 @@ func (s *PlayerSystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
|
|||
|
||||
r := image.Rect(int(p.X), int(p.Y), int(p.X)+size, int(p.Y)+size)
|
||||
screen.SubImage(r).(*ebiten.Image).Fill(p.Color)
|
||||
|
||||
switch p.Action {
|
||||
case component.ActionPunch:
|
||||
ebitenutil.DebugPrintAt(screen, "PUNCH", int(p.X), int(p.Y))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
11
system/ui.go
11
system/ui.go
|
@ -31,7 +31,6 @@ type UISystem struct {
|
|||
|
||||
initialized bool
|
||||
buffer *etk.Text
|
||||
debugImg *ebiten.Image
|
||||
}
|
||||
|
||||
func (u *UISystem) initialize() {
|
||||
|
@ -44,9 +43,7 @@ func (u *UISystem) initialize() {
|
|||
inputDemo.AddChild(u.buffer)
|
||||
|
||||
etk.SetRoot(inputDemo)
|
||||
etk.Layout(world.ScreenWidth, world.ScreenHeight)
|
||||
|
||||
u.debugImg = ebiten.NewImage(128, 128)
|
||||
etk.Layout(world.InternalScreenWidth, world.InternalScreenHeight)
|
||||
|
||||
u.initialized = true
|
||||
}
|
||||
|
@ -174,11 +171,7 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
|
|||
}
|
||||
|
||||
if world.Debug != 0 {
|
||||
u.debugImg.Clear()
|
||||
ebitenutil.DebugPrintAt(u.debugImg, fmt.Sprintf("ENT %d\nUPD %d\nDRA %d\nTPS %0.0f\nFPS %0.0f", gohan.CurrentEntities(), gohan.CurrentUpdates(), gohan.CurrentDraws(), ebiten.ActualTPS(), ebiten.ActualFPS()), 2, 0)
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(2, 2)
|
||||
screen.DrawImage(u.debugImg, op)
|
||||
ebitenutil.DebugPrintAt(screen, fmt.Sprintf("ENT %d\nUPD %d\nDRA %d\nTPS %0.0f\nFPS %0.0f", gohan.CurrentEntities(), gohan.CurrentUpdates(), gohan.CurrentDraws(), ebiten.ActualTPS(), ebiten.ActualFPS()), 2, 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ const TPS = 60
|
|||
const (
|
||||
DefaultScreenWidth = 1280
|
||||
DefaultScreenHeight = 720
|
||||
|
||||
InternalScreenWidth, InternalScreenHeight = 854, 480
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
Loading…
Reference in New Issue