Flip player orientation when they are on the right side

This commit is contained in:
Trevor Slocum 2023-01-20 16:13:05 -08:00
parent 92c1c72668
commit aad5185f94
6 changed files with 65 additions and 19 deletions

View File

@ -99,6 +99,17 @@ func FrameDataForActionTick(a PlayerAction, tick int) []FrameData {
return actionFrames[tick-1]
}
func FlipRect(r image.Rectangle, flip bool) image.Rectangle {
if !flip {
return r
}
return image.Rect(-r.Min.X+PlayerSize, r.Min.Y, -r.Max.X+PlayerSize, r.Max.Y)
}
func PlayerOnRightSide(p Player, o Player) bool {
return p.X > o.X
}
type Player struct {
X, Y float64
VX, VY float64

View File

@ -21,6 +21,7 @@ func parseFlags() {
printDebug bool
)
flag.BoolVar(&fullscreen, "fullscreen", false, "run in fullscreen mode")
flag.BoolVar(&world.DisableVsync, "no-vsync", false, "turn off vsync and run at maximum fps")
flag.StringVar(&hostAddress, "host", "", "start hosting a match on specified address:port")
flag.StringVar(&connectAddress, "connect", "", "connect to a match at specified address:port")
flag.IntVar(&world.LocalPort, "local", 0, "set local port (this is not normally required)")

View File

@ -4,6 +4,7 @@ import (
"crypto/sha1"
"image/color"
"log"
"math"
"os"
"strconv"
"strings"
@ -210,7 +211,7 @@ func (g *Game) applyPhysics() {
// Apply gravity.
if p.VY > -world.Gravity {
p.VY -= 1
p.VY -= math.Max(math.Abs(p.VY/2.5), 0.1)
if p.VY < -world.Gravity {
p.VY = -world.Gravity
}
@ -240,6 +241,7 @@ func (g *Game) applyPhysics() {
func (g *Game) UpdateByInputs(inputs []InputBits) {
var player, opponent *component.Player
var playerFlipped, oppFlipped bool
for i, input := range inputs {
opp := 0
if i == 0 {
@ -248,6 +250,15 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
player = &g.Players[i]
opponent = &g.Players[opp]
if component.PlayerOnRightSide(*player, *opponent) { // Player is on the right side.
playerFlipped = true
oppFlipped = false
} else { // Opponent is on the right side.
playerFlipped = false
oppFlipped = true
}
_ = oppFlipped // TODO
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))
@ -256,10 +267,9 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
if player.Action != component.ActionStunned {
if input.isButtonOn(ButtonUp) && !component.TranslateRect(playerRect, 0, -1).Overlaps(oppRect) {
grounded := g.Players[i].Y == float64(component.PlayerSize)
// TODO check when last jump, grounded
grounded := g.Players[i].Y > -world.FloatValueThreshold && g.Players[i].Y < world.FloatValueThreshold
if grounded {
g.Players[i].VY = 20
g.Players[i].VY = world.JumpVelocity
}
}
if input.isButtonOn(ButtonDown) && !component.TranslateRect(playerRect, 0, 1).Overlaps(oppRect) {
@ -290,10 +300,12 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
allFrameData := component.FrameDataForActionTick(g.Players[i].Action, g.Players[i].ActionTicksLeft)
for _, frame := range allFrameData {
frameRect := component.FlipRect(frame.R, playerFlipped)
// Apply hitbox.
if frame.T == component.HitboxHurt {
// Hit opponent.
if oppRect.Overlaps(component.TranslateRect(frame.R, int(player.X), int(player.Y))) {
if oppRect.Overlaps(component.TranslateRect(frameRect, int(player.X), int(player.Y))) {
// Send the opponent flying in some direction.
if opponent.X <= player.X { // Opponent is to the left of the player.
opponent.VX = -4
@ -439,7 +451,11 @@ func (g *Game) Update() error {
g.RunFrame()
}
return gohan.Update()
err := gohan.Update()
if err != nil {
return err
}
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {

View File

@ -16,13 +16,18 @@ func main() {
ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
ebiten.SetWindowSize(world.DefaultScreenWidth, world.DefaultScreenHeight)
ebiten.SetWindowClosingHandled(true)
ebiten.SetFPSMode(ebiten.FPSModeVsyncOn)
ebiten.SetRunnableOnUnfocused(true)
parseFlags()
ebiten.SetTPS(world.TPS)
if world.DisableVsync {
ebiten.SetFPSMode(ebiten.FPSModeVsyncOffMaximum)
} else {
ebiten.SetFPSMode(ebiten.FPSModeVsyncOn)
}
g, err := game.NewGame()
if err != nil {
log.Fatal(err)

View File

@ -180,17 +180,17 @@ func (u *UISystem) Update(e gohan.Entity) error {
return etk.Update()
}
func (u *UISystem) drawBox(screen *ebiten.Image, fillColor color.Color, x float64, y float64, w int, h int) {
func (u *UISystem) drawBox(screen *ebiten.Image, fillColor color.Color, r image.Rectangle) {
bounds := u.hitboxImg.Bounds()
if bounds.Dx() != w || bounds.Dy() != h {
u.hitboxImg = ebiten.NewImage(w, h)
if bounds.Dx() != r.Dx() || bounds.Dy() != r.Dy() {
u.hitboxImg = ebiten.NewImage(r.Dx(), r.Dy())
}
u.hitboxImg.Clear()
u.hitboxImg.Fill(color.RGBA{255, 255, 255, 255})
u.hitboxImg.SubImage(image.Rect(2, 2, w-2, h-2)).(*ebiten.Image).Fill(fillColor)
u.hitboxImg.SubImage(image.Rect(2, 2, r.Dx()-2, r.Dy()-2)).(*ebiten.Image).Fill(fillColor)
// Get screen position of top left corner of player.
drawX, drawY := world.GameCoordsToScreen(x, y)
drawX, drawY := world.GameCoordsToScreen(float64(r.Min.X), float64(r.Min.Y+r.Dy()))
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(float64(drawX), float64(drawY))
@ -209,31 +209,38 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
return err
}
} else if world.Debug > 1 { // In-game and debug mode is enabled
var p *component.Player
var p, o *component.Player
for i := 0; i < 2; i++ {
if i == 0 {
p = &world.Player1
o = &world.Player2
} else {
p = &world.Player2
o = &world.Player1
}
if p.ActionTicksLeft != 0 {
// Draw a rect over stunned players.
if p.Action == component.ActionStunned {
playerRect := world.FloatRect(p.X, p.Y, p.X+float64(component.PlayerSize), p.Y+float64(component.PlayerSize))
fillColor := color.RGBA{123, 30, 255, 255}
u.drawBox(screen, fillColor, p.X, p.Y+component.PlayerSize, component.PlayerSize, component.PlayerSize)
u.drawBox(screen, fillColor, playerRect)
continue
}
// Draw frame data rects.
allData := component.FrameDataForActionTick(p.Action, p.ActionTicksLeft)
for _, data := range allData {
for _, frame := range allData {
frameRect := component.FlipRect(frame.R, component.PlayerOnRightSide(*p, *o))
frameRect = component.TranslateRect(frameRect, int(p.X), int(p.Y))
fillColor := color.RGBA{0, 255, 0, 255}
switch data.T {
switch frame.T {
case component.HitboxHurt:
fillColor = color.RGBA{0, 0, 255, 255}
}
u.drawBox(screen, fillColor, p.X, p.Y+float64(data.R.Dy()), data.R.Dx(), data.R.Dy())
u.drawBox(screen, fillColor, frameRect)
}
}
}

View File

@ -15,16 +15,22 @@ const (
InternalScreenWidth, InternalScreenHeight = 854, 480
Gravity = 6.0
Gravity = 3
GroundHeight = 100 // TODO
JumpVelocity = 30
GroundHeight = 100
MaxDebug = 2
FloatValueThreshold = 0.00000001
)
var (
TPS = DefaultTPS
DisableVsync bool
ScreenWidth, ScreenHeight = 0, 0
CamX, CamY = 0, 0 // TODO currently static