- Refactor dirt and stone generation to optimize visible depth and adjust randomization.
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 8m32s
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 8m32s
- Remove unused `StartWebSocketGateway` function from `websocket_gateway.go`. - Add security checks to track player-room mapping, enforce valid input, and prevent ID spoofing in `gateway.go`. - Refactor touch control logic to dynamically position joystick and buttons above gameplay floor. - Introduce dynamic floor Y-coordinate calculation (`GetFloorYFromHeight`) for better scaling across different screen sizes. - Adjust rendering logic to align assets, particles, and debug visuals with dynamic screen height transformations. - Update canvas CSS to support fullscreen scaling without center alignment.
This commit is contained in:
@@ -115,6 +115,10 @@ func (g *Game) UpdateGame() {
|
||||
func (g *Game) handleTouchInput() {
|
||||
touches := ebiten.TouchIDs()
|
||||
|
||||
// WICHTIG: Joystick-Base-Position wird jetzt beim Rendering gesetzt (DrawGame)
|
||||
// um sicherzustellen dass die gleiche Canvas-Höhe verwendet wird!
|
||||
// Wir verwenden hier nur die gecachte Position.
|
||||
|
||||
// Reset, wenn keine Finger mehr auf dem Display sind
|
||||
if len(touches) == 0 {
|
||||
g.joyActive = false
|
||||
@@ -228,6 +232,10 @@ func (g *Game) DrawGame(screen *ebiten.Image) {
|
||||
}
|
||||
g.stateMutex.Unlock()
|
||||
|
||||
// Canvas-Größe und Scale-Faktor
|
||||
canvasW, canvasH := screen.Size()
|
||||
viewScale := GetScaleFromHeight(canvasH)
|
||||
|
||||
// 1. Hintergrund (wechselt alle 5000 Punkte)
|
||||
backgroundID := "background"
|
||||
if myScore >= 10000 {
|
||||
@@ -240,8 +248,6 @@ func (g *Game) DrawGame(screen *ebiten.Image) {
|
||||
if bgImg, exists := g.assetsImages[backgroundID]; exists && bgImg != nil {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
|
||||
// Tatsächliche Canvas-Größe verwenden (nicht nur ScreenWidth/Height)
|
||||
canvasW, canvasH := screen.Size()
|
||||
bgW, bgH := bgImg.Size()
|
||||
|
||||
// Skalierung berechnen, um Canvas komplett zu füllen
|
||||
@@ -265,12 +271,17 @@ func (g *Game) DrawGame(screen *ebiten.Image) {
|
||||
}
|
||||
|
||||
// Boden zeichnen (prozedural mit Dirt und Steinen, bewegt sich mit Kamera)
|
||||
g.RenderGround(screen, g.camX)
|
||||
// Mit viewScale multiplizieren damit auf Mobile mehr Welt sichtbar ist
|
||||
effectiveCamX := g.camX / viewScale
|
||||
g.RenderGround(screen, effectiveCamX)
|
||||
|
||||
// State Locken für Datenzugriff
|
||||
g.stateMutex.Lock()
|
||||
defer g.stateMutex.Unlock()
|
||||
|
||||
// Screen-Höhe für Y-Transformation (canvasH bereits oben definiert)
|
||||
// _, canvasH = screen.Size() // nicht nötig, bereits definiert
|
||||
|
||||
// 2. Chunks (Welt-Objekte)
|
||||
for _, activeChunk := range g.gameState.WorldChunks {
|
||||
chunkDef, exists := g.world.ChunkLibrary[activeChunk.ChunkID]
|
||||
@@ -303,19 +314,19 @@ func (g *Game) DrawGame(screen *ebiten.Image) {
|
||||
}
|
||||
}
|
||||
|
||||
// Asset zeichnen
|
||||
g.DrawAsset(screen, obj.AssetID, activeChunk.X+obj.X, obj.Y)
|
||||
// Asset zeichnen (mit Welt-zu-Screen-Y-Transformation)
|
||||
g.DrawAsset(screen, obj.AssetID, activeChunk.X+obj.X, WorldToScreenYWithHeight(obj.Y, canvasH))
|
||||
}
|
||||
}
|
||||
|
||||
// 2.5 Bewegende Plattformen (von Server synchronisiert)
|
||||
for _, mp := range g.gameState.MovingPlatforms {
|
||||
g.DrawAsset(screen, mp.AssetID, mp.X, mp.Y)
|
||||
g.DrawAsset(screen, mp.AssetID, mp.X, WorldToScreenYWithHeight(mp.Y, canvasH))
|
||||
}
|
||||
|
||||
// 2.6 DEBUG: Basis-Boden-Collider visualisieren (GRÜN) - NUR IM DEBUG-MODUS
|
||||
if g.showDebug {
|
||||
vector.StrokeRect(screen, float32(-g.camX), float32(540), 10000, float32(5000), float32(2), color.RGBA{0, 255, 0, 255}, false)
|
||||
vector.StrokeRect(screen, float32(-g.camX), float32(WorldToScreenYWithHeight(540, canvasH)), 10000, float32(5000), float32(2), color.RGBA{0, 255, 0, 255}, false)
|
||||
}
|
||||
|
||||
// 3. Spieler
|
||||
@@ -351,31 +362,35 @@ func (g *Game) DrawGame(screen *ebiten.Image) {
|
||||
}
|
||||
}
|
||||
|
||||
g.DrawAsset(screen, sprite, posX, posY)
|
||||
// Konvertiere Welt-Y zu Screen-Y für korrektes Rendering
|
||||
_, canvasH := screen.Size()
|
||||
screenY := WorldToScreenYWithHeight(posY, canvasH)
|
||||
|
||||
g.DrawAsset(screen, sprite, posX, screenY)
|
||||
|
||||
// Name Tag
|
||||
name := p.Name
|
||||
if name == "" {
|
||||
name = id
|
||||
}
|
||||
text.Draw(screen, name, basicfont.Face7x13, int(posX-g.camX), int(posY-25), ColText)
|
||||
text.Draw(screen, name, basicfont.Face7x13, int(posX-g.camX), int(screenY-25), ColText)
|
||||
|
||||
// HITBOX VISUALISIERUNG (NUR IM DEBUG-MODUS)
|
||||
if g.showDebug {
|
||||
if def, ok := g.world.Manifest.Assets["player"]; ok {
|
||||
// Spieler-Hitbox (ROT)
|
||||
// Spieler-Hitbox (ROT) - mit Screen-Y-Transformation
|
||||
hx := float32(posX + def.DrawOffX + def.Hitbox.OffsetX - g.camX)
|
||||
hy := float32(posY + def.DrawOffY + def.Hitbox.OffsetY)
|
||||
hy := float32(screenY + def.DrawOffY + def.Hitbox.OffsetY)
|
||||
vector.StrokeRect(screen, hx, hy, float32(def.Hitbox.W), float32(def.Hitbox.H), 3, color.RGBA{255, 0, 0, 255}, false)
|
||||
|
||||
// Spieler-Position als Punkt (GELB)
|
||||
vector.DrawFilledCircle(screen, float32(posX-g.camX), float32(posY), 5, color.RGBA{255, 255, 0, 255}, false)
|
||||
// Spieler-Position als Punkt (GELB) - mit Screen-Y-Transformation
|
||||
vector.DrawFilledCircle(screen, float32(posX-g.camX), float32(screenY), 5, color.RGBA{255, 255, 0, 255}, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. UI Status (Canvas-relativ)
|
||||
canvasW, canvasH := screen.Size()
|
||||
canvasW, canvasH = screen.Size()
|
||||
|
||||
if g.gameState.Status == "COUNTDOWN" {
|
||||
msg := fmt.Sprintf("GO IN: %d", g.gameState.TimeLeft)
|
||||
@@ -430,25 +445,37 @@ func (g *Game) DrawGame(screen *ebiten.Image) {
|
||||
|
||||
// 8. TOUCH CONTROLS OVERLAY (nur wenn Tastatur nicht benutzt wurde)
|
||||
if !g.keyboardUsed {
|
||||
canvasW, _ := screen.Size()
|
||||
canvasW, canvasH := screen.Size()
|
||||
|
||||
// Cache Canvas-Höhe für Touch-Input (wird in UpdateGame/handleTouchInput verwendet)
|
||||
g.lastCanvasHeight = canvasH
|
||||
|
||||
// A) Joystick Base (unten links, über dem Boden positioniert)
|
||||
// WICHTIG: Verwende die gleiche Berechnung wie in handleTouchInput()!
|
||||
g.joyBaseX = 150.0
|
||||
floorY := GetFloorYFromHeight(canvasH)
|
||||
g.joyBaseY = floorY - 50.0 // 50px über dem Boden (sichtbar über der Erde)
|
||||
|
||||
// Wenn Joystick nicht aktiv, Stick = Base
|
||||
if !g.joyActive {
|
||||
g.joyStickX = g.joyBaseX
|
||||
g.joyStickY = g.joyBaseY
|
||||
}
|
||||
|
||||
// A) Joystick Base (unten links, relativ zu Viewport)
|
||||
joyX := 150.0
|
||||
joyY := float64(ScreenHeight) - 150.0 // ScreenHeight statt canvasH!
|
||||
baseCol := color.RGBA{80, 80, 80, 50}
|
||||
vector.DrawFilledCircle(screen, float32(joyX), float32(joyY), 60, baseCol, false)
|
||||
vector.StrokeCircle(screen, float32(joyX), float32(joyY), 60, 2, color.RGBA{100, 100, 100, 100}, false)
|
||||
vector.DrawFilledCircle(screen, float32(g.joyBaseX), float32(g.joyBaseY), 60, baseCol, false)
|
||||
vector.StrokeCircle(screen, float32(g.joyBaseX), float32(g.joyBaseY), 60, 2, color.RGBA{100, 100, 100, 100}, false)
|
||||
|
||||
// B) Joystick Knob (relativ zu Base, nicht zu Canvas)
|
||||
// B) Joystick Knob (relativ zu Base)
|
||||
knobCol := color.RGBA{100, 100, 100, 80}
|
||||
if g.joyActive {
|
||||
knobCol = color.RGBA{100, 255, 100, 120}
|
||||
}
|
||||
vector.DrawFilledCircle(screen, float32(g.joyStickX), float32(g.joyStickY), 30, knobCol, false)
|
||||
|
||||
// C) Jump Button (unten rechts, relativ zu Viewport)
|
||||
// C) Jump Button (unten rechts, über dem Boden positioniert)
|
||||
jumpX := float32(canvasW) - 150
|
||||
jumpY := float32(ScreenHeight) - 150 // ScreenHeight statt canvasH!
|
||||
jumpY := float32(floorY) - 50 // 50px über dem Boden
|
||||
vector.DrawFilledCircle(screen, jumpX, jumpY, 50, color.RGBA{255, 0, 0, 50}, false)
|
||||
vector.StrokeCircle(screen, jumpX, jumpY, 50, 2, color.RGBA{255, 0, 0, 100}, false)
|
||||
text.Draw(screen, "JUMP", basicfont.Face7x13, int(jumpX)-15, int(jumpY)+5, color.RGBA{255, 255, 255, 150})
|
||||
@@ -465,13 +492,17 @@ func (g *Game) DrawAsset(screen *ebiten.Image, assetID string, worldX, worldY fl
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Screen Position berechnen (Welt - Kamera)
|
||||
screenX := worldX - g.camX
|
||||
// Canvas-Größe und View-Scale
|
||||
canvasW, canvasH := screen.Size()
|
||||
viewScale := GetScaleFromHeight(canvasH)
|
||||
|
||||
// 2. Screen Position berechnen (Welt - Kamera, mit Scale)
|
||||
effectiveCamX := g.camX / viewScale
|
||||
screenX := (worldX - effectiveCamX) * viewScale
|
||||
screenY := worldY
|
||||
|
||||
// Optimierung: Nicht zeichnen, wenn komplett außerhalb (Canvas-Breite verwenden)
|
||||
// Großzügiger Culling-Bereich für früheres Spawning (800px statt 200px)
|
||||
canvasW, _ := screen.Size()
|
||||
if screenX < -800 || screenX > float64(canvasW)+800 {
|
||||
return
|
||||
}
|
||||
@@ -485,13 +516,14 @@ func (g *Game) DrawAsset(screen *ebiten.Image, assetID string, worldX, worldY fl
|
||||
// Filter für bessere Skalierung (besonders bei großen Sprites)
|
||||
op.Filter = ebiten.FilterLinear
|
||||
|
||||
// Skalieren
|
||||
op.GeoM.Scale(def.Scale, def.Scale)
|
||||
// Skalieren: Asset-Scale * View-Scale (auf Mobile kleiner)
|
||||
finalScale := def.Scale * viewScale
|
||||
op.GeoM.Scale(finalScale, finalScale)
|
||||
|
||||
// Positionieren: ScreenPos + DrawOffset
|
||||
// Positionieren: ScreenPos + DrawOffset (auch skaliert)
|
||||
op.GeoM.Translate(
|
||||
screenX+def.DrawOffX,
|
||||
screenY+def.DrawOffY,
|
||||
screenX+(def.DrawOffX*viewScale),
|
||||
screenY+(def.DrawOffY*viewScale),
|
||||
)
|
||||
|
||||
// Farbe anwenden (nur wenn explizit gesetzt)
|
||||
|
||||
Reference in New Issue
Block a user