Add collision and gravity systems
This commit is contained in:
parent
b340d821f7
commit
ea60aa70d4
6 changed files with 157 additions and 56 deletions
|
@ -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
|
||||
|
|
113
game/game.go
113
game/game.go
|
@ -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
47
system/map.go
Normal 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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue