Add collision and gravity systems

This commit is contained in:
Trevor Slocum 2023-01-13 15:31:17 -08:00
parent b340d821f7
commit ea60aa70d4
6 changed files with 157 additions and 56 deletions

View file

@ -26,7 +26,7 @@ type FrameData struct {
R image.Rectangle
}
const playerSize = 20
const PlayerSize = 20
// AllPlayerFrames defines all frame data for the game. Frames are defined in reverse order.
var AllPlayerFrames = [][][]FrameData{
@ -34,48 +34,48 @@ var AllPlayerFrames = [][][]FrameData{
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
R: image.Rect(0, 0, PlayerSize, PlayerSize),
},
},
}, { // ActionPunch
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
R: image.Rect(0, 0, PlayerSize, PlayerSize),
},
{
T: HitboxHurt,
R: image.Rect(0, 0, playerSize, playerSize),
R: image.Rect(0, 0, PlayerSize, PlayerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
R: image.Rect(0, 0, PlayerSize, PlayerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
R: image.Rect(0, 0, PlayerSize, PlayerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
R: image.Rect(0, 0, PlayerSize, PlayerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
R: image.Rect(0, 0, PlayerSize, PlayerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
R: image.Rect(0, 0, PlayerSize, PlayerSize),
},
},
},
@ -101,7 +101,7 @@ func (p *Player) Clone() Player {
return result
}
func OffsetRect(r image.Rectangle, x int, y int) image.Rectangle {
func TranslateRect(r image.Rectangle, x int, y int) image.Rectangle {
r.Min.X, r.Min.Y = r.Min.X+x, r.Min.Y+y
r.Max.X, r.Max.Y = r.Max.X+x, r.Max.Y+y
return r

View file

@ -49,6 +49,7 @@ func NewGame() (*Game, error) {
if !addedGame {
entity.NewOnceEntity()
gohan.AddSystem(&system.MapSystem{})
gohan.AddSystem(&system.PlayerSystem{})
gohan.AddSystem(&system.UISystem{})
addedGame = true
@ -134,48 +135,11 @@ func (g *Game) startNetworkGame() {
world.ConnectionActive = true
}
func (g *Game) Update() error {
if ebiten.IsWindowBeingClosed() || (!world.WASM && ebiten.IsKeyPressed(ebiten.KeyEscape)) {
g.Exit()
return nil
}
if world.ConnectPromptConfirmed && !world.ConnectionActive {
if world.ConnectPromptText == "" {
g.startLocalGame()
} else {
g.startNetworkGame()
}
g.playerStateUpdated()
}
if world.ConnectionActive {
err := world.Backend.Idle(0)
if err != nil {
panic(err)
}
g.RunFrame()
}
return gohan.Update()
}
func (g *Game) playerStateUpdated() {
world.Player1, world.Player2 = g.Players[0], g.Players[1]
}
func (g *Game) Draw(screen *ebiten.Image) {
err := gohan.Draw(screen)
if err != nil {
log.Fatal(err)
}
}
func (g *Game) Exit() {
os.Exit(0)
}
func (g *Game) InitNetworking(localPort int, numPlayers int, players []ggpo.Player, numSpectators int) {
var result error
var inputBits InputBits = 0
@ -249,18 +213,37 @@ func (g *Game) AdvanceFrame(inputs []InputBits, disconnectFlags int) {
}
}
func (g *Game) applyPhysics() {
// Apply gravity.
for i := 0; i < 2; i++ {
p := &g.Players[i]
p.Y -= world.Gravity
if p.Y-component.PlayerSize < 0 {
p.Y = component.PlayerSize
}
}
}
func (g *Game) UpdateByInputs(inputs []InputBits) {
for i, input := range inputs {
if input.isButtonOn(ButtonUp) {
opp := 0
if i == 0 {
opp = 1
}
playerRect := world.FloatRect(g.Players[i].X, g.Players[i].Y, g.Players[i].X+float64(component.PlayerSize), g.Players[i].Y+float64(component.PlayerSize))
oppRect := world.FloatRect(g.Players[opp].X, g.Players[opp].Y, g.Players[opp].X+float64(component.PlayerSize), g.Players[opp].Y+float64(component.PlayerSize))
if input.isButtonOn(ButtonUp) && !component.TranslateRect(playerRect, 0, -1).Overlaps(oppRect) {
g.Players[i].Y--
}
if input.isButtonOn(ButtonDown) {
if input.isButtonOn(ButtonDown) && !component.TranslateRect(playerRect, 0, 1).Overlaps(oppRect) {
g.Players[i].Y++
}
if input.isButtonOn(ButtonLeft) {
if input.isButtonOn(ButtonLeft) && !component.TranslateRect(playerRect, -1, 0).Overlaps(oppRect) {
g.Players[i].X--
}
if input.isButtonOn(ButtonRight) {
if input.isButtonOn(ButtonRight) && !component.TranslateRect(playerRect, 1, 0).Overlaps(oppRect) {
g.Players[i].X++
}
@ -287,6 +270,8 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
}
}
}
g.applyPhysics()
}
func (g *Game) ReadInputs() InputBits {
@ -316,7 +301,6 @@ func (g *Game) ReadInputsP2() InputBits {
var in InputBits
// TODO Support local multiplayer?
return in
}
@ -331,3 +315,48 @@ func (g *Game) Checksum() int {
}
return sum
}
func (g *Game) Update() error {
if ebiten.IsWindowBeingClosed() || (!world.WASM && ebiten.IsKeyPressed(ebiten.KeyEscape)) {
g.Exit()
return nil
}
if inpututil.IsKeyJustPressed(ebiten.KeyV) && ebiten.IsKeyPressed(ebiten.KeyControl) {
world.Debug++
if world.Debug > world.MaxDebug {
world.Debug = 0
}
}
if world.ConnectPromptConfirmed && !world.ConnectionActive {
if world.ConnectPromptText == "" {
g.startLocalGame()
} else {
g.startNetworkGame()
}
g.playerStateUpdated()
}
if world.ConnectionActive {
err := world.Backend.Idle(0)
if err != nil {
panic(err)
}
g.RunFrame()
}
return gohan.Update()
}
func (g *Game) Draw(screen *ebiten.Image) {
err := gohan.Draw(screen)
if err != nil {
log.Fatal(err)
}
}
func (g *Game) Exit() {
os.Exit(0)
}

47
system/map.go Normal file
View file

@ -0,0 +1,47 @@
package system
import (
"image"
"image/color"
"code.rocketnine.space/tslocum/boxbrawl/component"
"code.rocketnine.space/tslocum/boxbrawl/world"
"code.rocketnine.space/tslocum/gohan"
"github.com/hajimehoshi/ebiten/v2"
)
type MapSystem struct {
*component.Once
initialized bool
}
func (s *MapSystem) initialize() {
// TODO
}
func (s *MapSystem) Update(e gohan.Entity) error {
if !s.initialized {
s.initialize()
}
if world.ConnectPromptVisible {
return nil
}
return nil
}
func (s *MapSystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
if world.ConnectPromptVisible {
return nil
}
const groundSize = 10 // TODO
groundColor := color.RGBA{255, 255, 255, 255}
r := image.Rect(-world.ScreenWidth*2, 0, world.ScreenWidth*2, groundSize)
screen.SubImage(world.GameRectToScreen(r)).(*ebiten.Image).Fill(groundColor)
return nil
}

View file

@ -46,8 +46,9 @@ func (s *PlayerSystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
p = &world.Player2
}
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)
r := image.Rect(int(p.X), -int(p.Y), int(p.X)+size, -int(p.Y)+size)
screen.SubImage(world.GameRectToScreen(r)).(*ebiten.Image).Fill(p.Color)
// TODO animate
/*if p.ActionTicksLeft != 0 {

View file

@ -189,7 +189,7 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
if err != nil {
return err
}
} else if world.Debug != 0 { // In-game and debug mode is enabled
} else if world.Debug > 1 { // In-game and debug mode is enabled
var p *component.Player
for i := 0; i < 2; i++ {
if i == 0 {
@ -215,8 +215,10 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
u.hitboxImg.Clear()
u.hitboxImg.Fill(fillColor)
drawX, drawY := world.GameCoordsToScreen(p.X, p.Y)
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(p.X, p.Y)
op.GeoM.Translate(float64(drawX), float64(drawY))
op.ColorM.Scale(1, 1, 1, 1)
screen.DrawImage(u.hitboxImg, op)
}

View file

@ -1,6 +1,8 @@
package world
import (
"image"
"code.rocketnine.space/tslocum/boxbrawl/component"
"github.com/assemblaj/ggpo"
)
@ -12,6 +14,12 @@ const (
DefaultScreenHeight = 720
InternalScreenWidth, InternalScreenHeight = 854, 480
Gravity = 1.0
GroundHeight = 100 // TODO
MaxDebug = 2
)
var (
@ -19,6 +27,8 @@ var (
ScreenWidth, ScreenHeight = 0, 0
CamX, CamY = 0, 0 // TODO currently static
LocalPort int
ConnectPromptVisible = true // When false, we are connected
@ -38,3 +48,15 @@ var (
Backend ggpo.Backend
)
func GameCoordsToScreen(x, y float64) (int, int) {
return int(x) - CamX + (ScreenWidth / 2), -int(y) - CamY + (ScreenHeight - GroundHeight)
}
func GameRectToScreen(r image.Rectangle) image.Rectangle {
return component.TranslateRect(r, -CamX+(ScreenWidth/2), -CamY+(ScreenHeight-GroundHeight))
}
func FloatRect(x1, y1, x2, y2 float64) image.Rectangle {
return image.Rect(int(x1), int(y1), int(x2), int(y2))
}