Integrate shared physics engine for player movement and collision handling, refine 20 TPS gameplay logic, and enhance client prediction with server-reconciliation updates.
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 7m51s
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 7m51s
This commit is contained in:
@@ -114,6 +114,7 @@ type GameState struct {
|
||||
CollectedPowerups map[string]bool `json:"collected_powerups"` // Welche Powerups wurden eingesammelt
|
||||
MovingPlatforms []MovingPlatformSync `json:"moving_platforms"` // Bewegende Plattformen
|
||||
Sequence uint32 `json:"sequence"` // Sequenznummer für Out-of-Order-Erkennung
|
||||
CurrentSpeed float64 `json:"current_speed"` // Aktuelle Scroll-Geschwindigkeit (für Client-Prediction)
|
||||
}
|
||||
|
||||
// MovingPlatformSync: Synchronisiert die Position einer bewegenden Plattform
|
||||
|
||||
@@ -94,9 +94,6 @@ func (w *World) GenerateColliders(activeChunks []ActiveChunk) []Collider {
|
||||
Type: def.Type,
|
||||
}
|
||||
list = append(list, c)
|
||||
fmt.Printf("✅ Collider generiert: Type=%s, Asset=%s, Pos=(%.0f,%.0f), DrawOff=(%.0f,%.0f), HitboxOff=(%.0f,%.0f)\n",
|
||||
def.Type, obj.AssetID, c.Rect.OffsetX, c.Rect.OffsetY,
|
||||
def.DrawOffX, def.DrawOffY, def.Hitbox.OffsetX, def.Hitbox.OffsetY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
256
pkg/physics/physics.go
Normal file
256
pkg/physics/physics.go
Normal file
@@ -0,0 +1,256 @@
|
||||
package physics
|
||||
|
||||
import (
|
||||
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/config"
|
||||
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/game"
|
||||
)
|
||||
|
||||
// PlayerPhysicsState enthält den kompletten Physik-Zustand eines Spielers
|
||||
type PlayerPhysicsState struct {
|
||||
X float64
|
||||
Y float64
|
||||
VX float64
|
||||
VY float64
|
||||
OnGround bool
|
||||
OnWall bool
|
||||
}
|
||||
|
||||
// PhysicsInput enthält alle Inputs für einen Physik-Tick
|
||||
type PhysicsInput struct {
|
||||
InputX float64 // -1.0 bis 1.0 (Links/Rechts)
|
||||
Jump bool
|
||||
Down bool
|
||||
}
|
||||
|
||||
// CollisionChecker ist ein Interface für Kollisionserkennung
|
||||
// Server und Client können unterschiedliche Implementierungen haben
|
||||
type CollisionChecker interface {
|
||||
CheckCollision(x, y, w, h float64) (hit bool, collisionType string)
|
||||
}
|
||||
|
||||
// PlayerConstants enthält alle Spieler-spezifischen Konstanten
|
||||
type PlayerConstants struct {
|
||||
DrawOffX float64
|
||||
DrawOffY float64
|
||||
HitboxOffX float64
|
||||
HitboxOffY float64
|
||||
Width float64
|
||||
Height float64
|
||||
}
|
||||
|
||||
// DefaultPlayerConstants gibt die Standard-Spieler-Konstanten zurück
|
||||
// WICHTIG: Diese Werte müssen EXAKT mit assets.json übereinstimmen!
|
||||
func DefaultPlayerConstants() PlayerConstants {
|
||||
return PlayerConstants{
|
||||
DrawOffX: -56.0, // Aus assets.json "player"
|
||||
DrawOffY: -231.0, // Aus assets.json "player"
|
||||
HitboxOffX: 68.0, // Aus assets.json "player" Hitbox.OffsetX
|
||||
HitboxOffY: 42.0, // Aus assets.json "player" Hitbox.OffsetY
|
||||
Width: 73.0,
|
||||
Height: 184.0,
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyPhysics wendet einen Physik-Tick auf den Spieler an
|
||||
// Diese Funktion wird 1:1 von Server und Client verwendet
|
||||
func ApplyPhysics(
|
||||
state *PlayerPhysicsState,
|
||||
input PhysicsInput,
|
||||
currentSpeed float64,
|
||||
collisionChecker CollisionChecker,
|
||||
playerConst PlayerConstants,
|
||||
) {
|
||||
// --- HORIZONTALE BEWEGUNG MIT KOLLISION ---
|
||||
playerMovement := input.InputX * config.PlayerSpeed
|
||||
speed := currentSpeed + playerMovement
|
||||
nextX := state.X + speed
|
||||
|
||||
// Horizontale Kollisionsprüfung
|
||||
checkX := nextX + playerConst.DrawOffX + playerConst.HitboxOffX
|
||||
checkY := state.Y + playerConst.DrawOffY + playerConst.HitboxOffY
|
||||
|
||||
xHit, xCollisionType := collisionChecker.CheckCollision(
|
||||
checkX,
|
||||
checkY,
|
||||
playerConst.Width,
|
||||
playerConst.Height,
|
||||
)
|
||||
|
||||
// Nur X-Bewegung blockieren wenn es eine Wand/Plattform ist (nicht Obstacle)
|
||||
if xHit && (xCollisionType == "wall" || xCollisionType == "platform") {
|
||||
// Kollision in X-Richtung -> X nicht ändern
|
||||
// state.X bleibt wie es ist
|
||||
} else {
|
||||
// Keine Kollision -> X-Bewegung durchführen
|
||||
state.X = nextX
|
||||
}
|
||||
|
||||
// --- VERTIKALE BEWEGUNG ---
|
||||
// An der Wand: Reduzierte Gravität + Klettern
|
||||
if state.OnWall {
|
||||
state.VY += config.Gravity * 0.3 // 30% Gravität an der Wand
|
||||
if state.VY > config.WallSlideMax {
|
||||
state.VY = config.WallSlideMax
|
||||
}
|
||||
// Hochklettern wenn Bewegung vorhanden
|
||||
if input.InputX != 0 {
|
||||
state.VY = -config.WallClimbSpeed
|
||||
}
|
||||
} else {
|
||||
// Normal: Volle Gravität
|
||||
state.VY += config.Gravity
|
||||
if state.VY > config.MaxFall {
|
||||
state.VY = config.MaxFall
|
||||
}
|
||||
}
|
||||
|
||||
// Fast Fall
|
||||
if input.Down {
|
||||
state.VY = config.FastFall
|
||||
}
|
||||
|
||||
// Sprung
|
||||
if input.Jump && state.OnGround {
|
||||
state.VY = -config.JumpVelocity
|
||||
state.OnGround = false
|
||||
}
|
||||
|
||||
// Vertikale Bewegung mit Kollisionserkennung
|
||||
nextY := state.Y + state.VY
|
||||
|
||||
// Kollision für AKTUELLE Position prüfen (um OnGround richtig zu setzen)
|
||||
checkX = state.X + playerConst.DrawOffX + playerConst.HitboxOffX
|
||||
currentCheckY := state.Y + playerConst.DrawOffY + playerConst.HitboxOffY
|
||||
|
||||
currentHit, currentType := collisionChecker.CheckCollision(
|
||||
checkX,
|
||||
currentCheckY,
|
||||
playerConst.Width,
|
||||
playerConst.Height,
|
||||
)
|
||||
|
||||
// Wenn Spieler aktuell bereits auf dem Boden steht
|
||||
if currentHit && currentType == "platform" && state.VY >= 0 {
|
||||
state.OnGround = true
|
||||
state.VY = 0
|
||||
state.OnWall = false
|
||||
// Y-Position bleibt wo sie ist
|
||||
return
|
||||
}
|
||||
|
||||
// Kollision für NÄCHSTE Position prüfen
|
||||
nextCheckY := nextY + playerConst.DrawOffY + playerConst.HitboxOffY
|
||||
|
||||
hit, collisionType := collisionChecker.CheckCollision(
|
||||
checkX,
|
||||
nextCheckY,
|
||||
playerConst.Width,
|
||||
playerConst.Height,
|
||||
)
|
||||
|
||||
if hit {
|
||||
if collisionType == "wall" {
|
||||
// An der Wand: Position halten (Y nicht ändern)
|
||||
state.VY = 0
|
||||
state.OnWall = true
|
||||
state.OnGround = false
|
||||
} else if collisionType == "obstacle" {
|
||||
// Obstacle: Spieler bewegt sich in Obstacle (Server killt dann)
|
||||
state.Y = nextY
|
||||
state.VY = 0
|
||||
state.OnGround = false
|
||||
state.OnWall = false
|
||||
} else if collisionType == "platform" {
|
||||
// Platform: Blockiert vertikale Bewegung
|
||||
if state.VY > 0 {
|
||||
state.OnGround = true
|
||||
}
|
||||
state.VY = 0
|
||||
state.OnWall = false
|
||||
// Y-Position bleibt unverändert
|
||||
}
|
||||
} else {
|
||||
// Keine Kollision: Bewegung durchführen
|
||||
state.Y = nextY
|
||||
state.OnGround = false
|
||||
state.OnWall = false
|
||||
}
|
||||
}
|
||||
|
||||
// ClientCollisionChecker implementiert CollisionChecker für den Client
|
||||
type ClientCollisionChecker struct {
|
||||
World *game.World
|
||||
ActiveChunks []game.ActiveChunk
|
||||
MovingPlatforms []game.MovingPlatformSync
|
||||
}
|
||||
|
||||
func (c *ClientCollisionChecker) CheckCollision(x, y, w, h float64) (bool, string) {
|
||||
playerRect := game.Rect{
|
||||
OffsetX: x,
|
||||
OffsetY: y,
|
||||
W: w,
|
||||
H: h,
|
||||
}
|
||||
|
||||
// 0. Basis-Boden-Collider (wie Server) - UNTER dem sichtbaren Gras bis tief in die Erde
|
||||
floorCollider := game.Rect{
|
||||
OffsetX: -10000,
|
||||
OffsetY: 540, // Startet bei Y=540 (Gras-Oberfläche)
|
||||
W: 100000000,
|
||||
H: 5000, // Geht tief nach UNTEN (bis Y=5540) - gesamte Erdschicht
|
||||
}
|
||||
if game.CheckRectCollision(playerRect, floorCollider) {
|
||||
return true, "platform"
|
||||
}
|
||||
|
||||
// 1. Statische Colliders aus Chunks
|
||||
for _, activeChunk := range c.ActiveChunks {
|
||||
if chunk, ok := c.World.ChunkLibrary[activeChunk.ChunkID]; ok {
|
||||
for _, obj := range chunk.Objects {
|
||||
if assetDef, ok := c.World.Manifest.Assets[obj.AssetID]; ok {
|
||||
if assetDef.Hitbox.W > 0 && assetDef.Hitbox.H > 0 {
|
||||
colliderRect := game.Rect{
|
||||
OffsetX: activeChunk.X + obj.X + assetDef.DrawOffX + assetDef.Hitbox.OffsetX,
|
||||
OffsetY: obj.Y + assetDef.DrawOffY + assetDef.Hitbox.OffsetY,
|
||||
W: assetDef.Hitbox.W,
|
||||
H: assetDef.Hitbox.H,
|
||||
}
|
||||
|
||||
if game.CheckRectCollision(playerRect, colliderRect) {
|
||||
return true, assetDef.Hitbox.Type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Bewegende Plattformen
|
||||
for _, mp := range c.MovingPlatforms {
|
||||
if assetDef, ok := c.World.Manifest.Assets[mp.AssetID]; ok {
|
||||
if assetDef.Hitbox.W > 0 && assetDef.Hitbox.H > 0 {
|
||||
mpRect := game.Rect{
|
||||
OffsetX: mp.X + assetDef.DrawOffX + assetDef.Hitbox.OffsetX,
|
||||
OffsetY: mp.Y + assetDef.DrawOffY + assetDef.Hitbox.OffsetY,
|
||||
W: assetDef.Hitbox.W,
|
||||
H: assetDef.Hitbox.H,
|
||||
}
|
||||
|
||||
if game.CheckRectCollision(playerRect, mpRect) {
|
||||
return true, "platform"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// ServerCollisionChecker ist ein Wrapper für die Server CheckCollision-Methode
|
||||
type ServerCollisionChecker struct {
|
||||
CheckCollisionFunc func(x, y, w, h float64) (bool, string)
|
||||
}
|
||||
|
||||
func (s *ServerCollisionChecker) CheckCollision(x, y, w, h float64) (bool, string) {
|
||||
return s.CheckCollisionFunc(x, y, w, h)
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/config"
|
||||
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/game"
|
||||
"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/physics"
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
@@ -21,6 +22,8 @@ type ServerPlayer struct {
|
||||
OnWall bool // Ist an einer Wand
|
||||
OnMovingPlatform *MovingPlatform // Referenz zur Plattform auf der der Spieler steht
|
||||
InputX float64 // -1 (Links), 0, 1 (Rechts)
|
||||
InputJump bool // Sprung-Input (für Physik-Engine)
|
||||
InputDown bool // Nach-Unten-Input (für Fast Fall)
|
||||
LastInputSeq uint32 // Letzte verarbeitete Input-Sequenz
|
||||
Score int
|
||||
DistanceScore int // Score basierend auf zurückgelegter Distanz
|
||||
@@ -122,6 +125,8 @@ func NewRoom(id string, nc *nats.Conn, w *game.World) *Room {
|
||||
r.pDrawOffY = def.DrawOffY
|
||||
r.pHitboxOffX = def.Hitbox.OffsetX
|
||||
r.pHitboxOffY = def.Hitbox.OffsetY
|
||||
log.Printf("🎮 Player Hitbox geladen: DrawOff=(%.1f, %.1f), HitboxOff=(%.1f, %.1f), Size=(%.1f, %.1f)",
|
||||
r.pDrawOffX, r.pDrawOffY, r.pHitboxOffX, r.pHitboxOffY, r.pW, r.pH)
|
||||
}
|
||||
|
||||
// Start-Chunk
|
||||
@@ -163,7 +168,7 @@ func NewRoom(id string, nc *nats.Conn, w *game.World) *Room {
|
||||
// --- MAIN LOOP ---
|
||||
|
||||
func (r *Room) RunLoop() {
|
||||
// 60 Tick pro Sekunde
|
||||
// 20 Tick pro Sekunde
|
||||
ticker := time.NewTicker(time.Second / 20)
|
||||
defer ticker.Stop()
|
||||
|
||||
@@ -172,6 +177,17 @@ func (r *Room) RunLoop() {
|
||||
case <-r.stopChan:
|
||||
return
|
||||
case <-ticker.C:
|
||||
r.Mutex.RLock()
|
||||
status := r.Status
|
||||
r.Mutex.RUnlock()
|
||||
|
||||
// Stoppe Updates wenn Spiel vorbei ist
|
||||
if status == "GAMEOVER" {
|
||||
r.Broadcast() // Ein letztes Mal broadcasten
|
||||
time.Sleep(5 * time.Second) // Kurz warten damit Clients den GAMEOVER State sehen
|
||||
return // Beende Loop
|
||||
}
|
||||
|
||||
r.Update()
|
||||
r.Broadcast()
|
||||
}
|
||||
@@ -196,7 +212,7 @@ func (r *Room) AddPlayer(id, name string) {
|
||||
ID: id,
|
||||
Name: name,
|
||||
X: spawnX,
|
||||
Y: 200,
|
||||
Y: 400, // Spawn über dem Gras (Y=540), fällt dann auf den Boden
|
||||
OnGround: false,
|
||||
Score: 0,
|
||||
IsAlive: true,
|
||||
@@ -261,18 +277,15 @@ func (r *Room) HandleInput(input game.ClientInput) {
|
||||
|
||||
switch input.Type {
|
||||
case "JUMP":
|
||||
if p.OnGround {
|
||||
p.VY = -config.JumpVelocity
|
||||
p.OnGround = false
|
||||
p.DoubleJumpUsed = false // Reset double jump on ground jump
|
||||
} else if p.HasDoubleJump && !p.DoubleJumpUsed {
|
||||
// Double Jump in der Luft
|
||||
p.InputJump = true // Setze Jump-Flag für Physik-Engine
|
||||
// Double Jump spezial-Logik (außerhalb der Physik-Engine)
|
||||
if !p.OnGround && p.HasDoubleJump && !p.DoubleJumpUsed {
|
||||
p.VY = -config.JumpVelocity
|
||||
p.DoubleJumpUsed = true
|
||||
log.Printf("⚡ %s verwendet Double Jump!", p.Name)
|
||||
}
|
||||
case "DOWN":
|
||||
p.VY = config.FastFall
|
||||
p.InputDown = true // Setze Down-Flag für Fast Fall
|
||||
case "LEFT_DOWN":
|
||||
p.InputX = -1
|
||||
case "LEFT_UP":
|
||||
@@ -355,44 +368,67 @@ func (r *Room) Update() {
|
||||
continue
|
||||
}
|
||||
|
||||
// X Bewegung
|
||||
// Spieler bewegt sich relativ zum Scroll
|
||||
// Scroll-Geschwindigkeit + Links/Rechts Bewegung
|
||||
playerMovement := p.InputX * config.PlayerSpeed
|
||||
currentSpeed := r.CurrentSpeed + playerMovement
|
||||
nextX := p.X + currentSpeed
|
||||
// === PHYSIK MIT GEMEINSAMER ENGINE (1:1 wie Client) ===
|
||||
|
||||
hitX, typeX := r.CheckCollision(nextX+r.pDrawOffX+r.pHitboxOffX, p.Y+r.pDrawOffY+r.pHitboxOffY, r.pW, r.pH)
|
||||
if hitX {
|
||||
if typeX == "wall" {
|
||||
// Wand getroffen - kann klettern!
|
||||
p.OnWall = true
|
||||
// X-Position NICHT ändern (bleibt vor der Wand stehen)
|
||||
} else if typeX == "obstacle" {
|
||||
// Godmode prüfen
|
||||
if p.HasGodMode && time.Now().Before(p.GodModeEndTime) {
|
||||
// Mit Godmode - Obstacle wird zerstört, Spieler überlebt
|
||||
p.X = nextX
|
||||
// TODO: Obstacle aus colliders entfernen (benötigt Referenz zum Obstacle)
|
||||
log.Printf("🛡️ %s zerstört Obstacle mit Godmode!", p.Name)
|
||||
} else {
|
||||
// Ohne Godmode - Spieler stirbt
|
||||
p.X = nextX
|
||||
r.KillPlayer(p)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// Platform blockiert
|
||||
p.OnWall = false
|
||||
}
|
||||
} else {
|
||||
p.X = nextX
|
||||
p.OnWall = false
|
||||
// Physik-State vorbereiten
|
||||
state := physics.PlayerPhysicsState{
|
||||
X: p.X,
|
||||
Y: p.Y,
|
||||
VX: p.VX,
|
||||
VY: p.VY,
|
||||
OnGround: p.OnGround,
|
||||
OnWall: p.OnWall,
|
||||
}
|
||||
|
||||
// Physik-Input vorbereiten
|
||||
physicsInput := physics.PhysicsInput{
|
||||
InputX: p.InputX,
|
||||
Jump: p.InputJump,
|
||||
Down: p.InputDown,
|
||||
}
|
||||
|
||||
// Kollisions-Checker vorbereiten
|
||||
collisionChecker := &physics.ServerCollisionChecker{
|
||||
CheckCollisionFunc: r.CheckCollision,
|
||||
}
|
||||
|
||||
// Gemeinsame Physik anwenden (1:1 wie Client!)
|
||||
physics.ApplyPhysics(&state, physicsInput, r.CurrentSpeed, collisionChecker, physics.DefaultPlayerConstants())
|
||||
|
||||
// Ergebnis zurückschreiben
|
||||
p.X = state.X
|
||||
p.Y = state.Y
|
||||
p.VX = state.VX
|
||||
p.VY = state.VY
|
||||
p.OnGround = state.OnGround
|
||||
p.OnWall = state.OnWall
|
||||
|
||||
// Input-Flags zurücksetzen für nächsten Tick
|
||||
p.InputJump = false
|
||||
p.InputDown = false
|
||||
|
||||
// Double Jump Reset wenn wieder am Boden
|
||||
if p.OnGround {
|
||||
p.DoubleJumpUsed = false
|
||||
}
|
||||
|
||||
// === SERVER-SPEZIFISCHE LOGIK ===
|
||||
|
||||
// Obstacle-Kollision prüfen -> Spieler töten
|
||||
hitObstacle, obstacleType := r.CheckCollision(
|
||||
p.X+r.pDrawOffX+r.pHitboxOffX,
|
||||
p.Y+r.pDrawOffY+r.pHitboxOffY,
|
||||
r.pW,
|
||||
r.pH,
|
||||
)
|
||||
if hitObstacle && obstacleType == "obstacle" {
|
||||
r.KillPlayer(p)
|
||||
continue
|
||||
}
|
||||
|
||||
// Grenzen
|
||||
if p.X > r.GlobalScrollX+1200 {
|
||||
p.X = r.GlobalScrollX + 1200
|
||||
if p.X > r.GlobalScrollX+2000 {
|
||||
p.X = r.GlobalScrollX + 2000
|
||||
} // Rechts Block
|
||||
if p.X < r.GlobalScrollX-50 {
|
||||
r.KillPlayer(p)
|
||||
@@ -403,53 +439,11 @@ func (r *Room) Update() {
|
||||
maxX = p.X
|
||||
}
|
||||
|
||||
// Y Bewegung
|
||||
// An der Wand: Reduzierte Gravität + Klettern mit InputX
|
||||
if p.OnWall {
|
||||
// Wandrutschen (langsame Fallgeschwindigkeit)
|
||||
p.VY += config.Gravity * 0.3 // 30% Gravität an der Wand
|
||||
if p.VY > config.WallSlideMax {
|
||||
p.VY = config.WallSlideMax
|
||||
}
|
||||
|
||||
// Hochklettern wenn nach oben gedrückt (InputX in Wandrichtung)
|
||||
if p.InputX != 0 {
|
||||
p.VY = -config.WallClimbSpeed
|
||||
}
|
||||
// Prüfe ob auf bewegender Plattform (für Platform-Mitbewegung)
|
||||
if p.OnGround {
|
||||
platform := r.CheckMovingPlatformLanding(p.X+r.pDrawOffX+r.pHitboxOffX, p.Y+r.pDrawOffY+r.pHitboxOffY, r.pW, r.pH)
|
||||
p.OnMovingPlatform = platform
|
||||
} else {
|
||||
// Normal: Volle Gravität
|
||||
p.VY += config.Gravity
|
||||
if p.VY > config.MaxFall {
|
||||
p.VY = config.MaxFall
|
||||
}
|
||||
}
|
||||
|
||||
nextY := p.Y + p.VY
|
||||
|
||||
hitY, typeY := r.CheckCollision(p.X+r.pDrawOffX+r.pHitboxOffX, nextY+r.pDrawOffY+r.pHitboxOffY, r.pW, r.pH)
|
||||
if hitY {
|
||||
if typeY == "wall" {
|
||||
// An der Wand: Nicht töten, Position halten und klettern ermöglichen
|
||||
p.VY = 0
|
||||
p.OnWall = true
|
||||
} else if typeY == "obstacle" {
|
||||
// Obstacle - immer töten
|
||||
p.Y = nextY
|
||||
r.KillPlayer(p)
|
||||
continue
|
||||
} else {
|
||||
// Platform blockiert
|
||||
if p.VY > 0 {
|
||||
p.OnGround = true
|
||||
// Prüfe ob auf bewegender Plattform
|
||||
platform := r.CheckMovingPlatformLanding(p.X+r.pDrawOffX+r.pHitboxOffX, nextY+r.pDrawOffY+r.pHitboxOffY, r.pW, r.pH)
|
||||
p.OnMovingPlatform = platform
|
||||
}
|
||||
p.VY = 0
|
||||
}
|
||||
} else {
|
||||
p.Y += p.VY
|
||||
p.OnGround = false
|
||||
p.OnMovingPlatform = nil
|
||||
}
|
||||
|
||||
@@ -535,10 +529,6 @@ func (r *Room) CheckCollision(x, y, w, h float64) (bool, string) {
|
||||
// 1. Statische Colliders (Chunks)
|
||||
for _, c := range r.Colliders {
|
||||
if game.CheckRectCollision(playerRect, c.Rect) {
|
||||
log.Printf("🔴 COLLISION! Type=%s, Player: (%.1f, %.1f, %.1f x %.1f), Collider: (%.1f, %.1f, %.1f x %.1f)",
|
||||
c.Type,
|
||||
playerRect.OffsetX, playerRect.OffsetY, playerRect.W, playerRect.H,
|
||||
c.Rect.OffsetX, c.Rect.OffsetY, c.Rect.W, c.Rect.H)
|
||||
return true, c.Type
|
||||
}
|
||||
}
|
||||
@@ -802,6 +792,7 @@ func (r *Room) Broadcast() {
|
||||
CollectedPowerups: r.CollectedPowerups,
|
||||
MovingPlatforms: make([]game.MovingPlatformSync, 0, len(r.MovingPlatforms)),
|
||||
Sequence: r.sequence,
|
||||
CurrentSpeed: r.CurrentSpeed,
|
||||
}
|
||||
|
||||
for id, p := range r.Players {
|
||||
|
||||
Reference in New Issue
Block a user