diff --git a/cmd/client/game_render.go b/cmd/client/game_render.go
index c45abd9..db4c98d 100644
--- a/cmd/client/game_render.go
+++ b/cmd/client/game_render.go
@@ -39,11 +39,22 @@ func (g *Game) UpdateGame() {
joyDir := 0.0
if g.joyActive {
diffX := g.joyStickX - g.joyBaseX
- if diffX < -20 {
- joyDir = -1
+ maxDist := 60.0 // Muss mit handleTouchInput() übereinstimmen
+
+ // Analoger Wert zwischen -1.0 und 1.0
+ joyDir = diffX / maxDist
+
+ // Clamp zwischen -1 und 1
+ if joyDir < -1.0 {
+ joyDir = -1.0
}
- if diffX > 20 {
- joyDir = 1
+ if joyDir > 1.0 {
+ joyDir = 1.0
+ }
+
+ // Deadzone für bessere Kontrolle
+ if joyDir > -0.15 && joyDir < 0.15 {
+ joyDir = 0
}
}
@@ -52,10 +63,11 @@ func (g *Game) UpdateGame() {
// Input State zusammenbauen
input := InputState{
Sequence: g.inputSequence,
- Left: keyLeft || joyDir == -1,
- Right: keyRight || joyDir == 1,
+ Left: keyLeft || joyDir < -0.1,
+ Right: keyRight || joyDir > 0.1,
Jump: keyJump || g.btnJumpActive,
Down: keyDown || isJoyDown,
+ JoyX: joyDir, // Analoge X-Achse speichern
}
g.btnJumpActive = false
@@ -72,8 +84,8 @@ func (g *Game) UpdateGame() {
// Lokale Physik sofort anwenden (Prediction)
g.ApplyInput(input)
- // Sanfte Korrektur anwenden (20% pro Frame)
- const smoothingFactor = 0.2
+ // Sanfte Korrektur anwenden (schnellere Interpolation für weniger Ruckeln)
+ const smoothingFactor = 0.4 // Erhöht von 0.2 auf 0.4
if g.correctionX != 0 || g.correctionY != 0 {
g.predictedX += g.correctionX * smoothingFactor
g.predictedY += g.correctionY * smoothingFactor
@@ -81,8 +93,8 @@ func (g *Game) UpdateGame() {
g.correctionX *= (1.0 - smoothingFactor)
g.correctionY *= (1.0 - smoothingFactor)
- // Korrektur beenden wenn sehr klein
- if g.correctionX*g.correctionX+g.correctionY*g.correctionY < 0.01 {
+ // Korrektur beenden wenn sehr klein (kleinerer Threshold)
+ if g.correctionX*g.correctionX+g.correctionY*g.correctionY < 0.25 {
g.correctionX = 0
g.correctionY = 0
}
diff --git a/cmd/client/main.go b/cmd/client/main.go
index 3b5d541..ff97511 100644
--- a/cmd/client/main.go
+++ b/cmd/client/main.go
@@ -49,6 +49,7 @@ type InputState struct {
Right bool
Jump bool
Down bool
+ JoyX float64 // Analoger Joystick-Wert (-1.0 bis 1.0)
}
// --- GAME STRUCT ---
diff --git a/cmd/client/prediction.go b/cmd/client/prediction.go
index 64c6624..f70bb11 100644
--- a/cmd/client/prediction.go
+++ b/cmd/client/prediction.go
@@ -7,7 +7,7 @@ import (
// ApplyInput wendet einen Input auf den vorhergesagten Zustand an
func (g *Game) ApplyInput(input InputState) {
- // Horizontale Bewegung
+ // Horizontale Bewegung mit analogem Joystick
moveX := 0.0
if input.Left {
moveX = -1.0
@@ -15,6 +15,12 @@ func (g *Game) ApplyInput(input InputState) {
moveX = 1.0
}
+ // Wenn Joystick benutzt wird, überschreibe moveX mit analogem Wert
+ if input.JoyX != 0 {
+ moveX = input.JoyX
+ }
+
+ // Geschwindigkeit skaliert mit Joystick-Intensität
speed := config.RunSpeed + (moveX * 4.0)
g.predictedX += speed
@@ -107,8 +113,8 @@ func (g *Game) ReconcileWithServer(serverState game.PlayerState) {
diffX := replayX - g.predictedX
diffY := replayY - g.predictedY
- // Nur korrigieren wenn Differenz signifikant (> 5 Pixel)
- const threshold = 5.0
+ // Nur korrigieren wenn Differenz signifikant (reduzierter Threshold für weniger Ruckeln)
+ const threshold = 2.0 // Reduziert von 5.0 auf 2.0
if diffX*diffX+diffY*diffY > threshold*threshold {
// Speichere Korrektur für sanfte Interpolation
g.correctionX = diffX
diff --git a/cmd/client/web/game.js b/cmd/client/web/game.js
index 32a6be8..631ac8d 100644
--- a/cmd/client/web/game.js
+++ b/cmd/client/web/game.js
@@ -34,6 +34,13 @@ function setUIState(newState) {
screen.classList.add('hidden');
});
if (loadingScreen) loadingScreen.style.display = 'none';
+
+ // Scroll alle overlay-screens nach oben
+ setTimeout(() => {
+ document.querySelectorAll('.overlay-screen:not(.hidden)').forEach(screen => {
+ screen.scrollTop = 0;
+ });
+ }, 10);
// Manage Canvas and Overlays based on state
switch(newState) {
diff --git a/cmd/client/web/index.html b/cmd/client/web/index.html
index dbd3f1d..3c7e72c 100644
--- a/cmd/client/web/index.html
+++ b/cmd/client/web/index.html
@@ -232,17 +232,19 @@
LOBBY
-
-
Raum-Code:
-
-
XXXXX
+
-
-
Spieler im Raum:
-
diff --git a/cmd/client/web/style.css b/cmd/client/web/style.css
index 66422d4..1fd58dc 100644
--- a/cmd/client/web/style.css
+++ b/cmd/client/web/style.css
@@ -44,33 +44,121 @@ input[type=range]{width:100%;max-width:300px}
#mute-btn:hover{background:rgba(255,255,255,.2);border-color:#fff}
#rotate-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:#222;z-index:99999;color:#fff;flex-direction:column;align-items:center;justify-content:center;text-align:center}
.icon{font-size:60px;margin-bottom:20px}
-/* Mobile First - Base Styles bereits für Mobile optimiert */
-@media screen and (max-width: 768px) {
- h1 { font-size: 20px; margin: 5px 0 15px; }
- button { font-size: 12px; padding: 12px 20px; margin: 8px; }
- .big-btn { font-size: 14px; padding: 14px 25px; }
- input[type=text] { font-size: 12px; padding: 10px; max-width: 280px; }
- .info-box { max-width: 280px; padding: 10px; font-size: 10px; }
- .info-box p { font-size: 11px; }
- .overlay-screen { padding: 10px; }
- #startScreen { flex-direction: column; gap: 20px; }
- .start-left, .start-right { max-width: 90%; flex: 1; }
- .center-box { max-width: 90%; }
- .leaderboard-box { max-width: 90%; padding: 10px; }
- .hall-of-fame-box { max-height: 200px; max-width: 90%; }
- .legal-bar { gap: 8px; }
- .legal-btn { font-size: 8px; padding: 6px 10px; }
- .back-btn { font-size: 10px; padding: 8px 16px; }
+
+/* Viewport-basierte dynamische Skalierung für Mobile */
+@media (min-width:1024px){
+ h1{font-size:48px;margin-bottom:40px}
+ button{font-size:22px;padding:20px 40px}
+ input[type=text]{width:350px;font-size:20px;padding:15px}
+ .info-box{max-width:500px}
+ .info-box p{font-size:16px}
+ .info-title{font-size:14px}
+ .hall-of-fame-box{max-height:400px}
}
-@media screen and (max-width: 480px) {
- h1 { font-size: 16px; line-height: 1.2; }
- button { font-size: 10px; padding: 10px 16px; margin: 6px; }
- .big-btn { font-size: 12px; padding: 12px 20px; }
- input[type=text] { font-size: 11px; padding: 8px; max-width: 240px; }
- .overlay-screen { padding: 8px; }
- body, html { font-size: 11px; }
+@media (max-height:500px){
+ #startScreen{
+ flex-direction:row;
+ align-items:center;
+ justify-content:center;
+ gap:20px;
+ padding:10px;
+ overflow-y:auto;
+ }
+ .start-left{
+ width:45%;
+ max-width:none;
+ align-items:center;
+ text-align:center;
+ margin:0;
+ }
+ .start-right{
+ width:45%;
+ max-width:none;
+ height:auto;
+ margin:0;
+ }
+ h1{font-size:18px;margin:0 0 10px 0;line-height:1.2}
+ .info-box{display:none}
+ .legal-bar{margin-top:5px;gap:10px}
+ button{padding:8px 12px;font-size:10px;margin:5px}
+ .hall-of-fame-box{max-height:180px}
+ .hall-of-fame-box h3{font-size:10px;margin-bottom:5px}
+
+ /* Game Over Screen */
+ #gameOverScreen{justify-content:center;align-items:center;padding:10px;overflow-y:auto}
+ #gameOverScreen h1{font-size:16px;margin:5px 0}
+ #gameOverScreen p{font-size:10px;margin:5px 0}
+ #finalScore{font-size:18px!important}
+
+ /* Coop Menu */
+ #coopMenu .center-box{max-width:90%;padding:10px}
+ #coopMenu h1{font-size:16px;margin:5px 0}
+ #coopMenu input{width:200px}
+
+ /* Lobby Screen */
+ #lobbyScreen .center-box{max-width:95%;padding:5px}
+ #lobbyScreen h1{font-size:14px;margin:3px 0}
+ #lobbyContent{gap:10px!important}
+ #lobbyContent>div{flex:1!important;max-width:48%!important}
+ #lobbyScreen p{font-size:9px!important;margin:3px 0!important}
+ #lobbyRoomCode{font-size:14px!important;padding:6px!important;letter-spacing:1px!important}
+ #lobbyPlayerList{padding:8px!important;min-height:60px!important;font-size:9px!important;max-height:100px!important}
+ #lobbyTeamName{padding:5px!important;font-size:10px!important}
+ #lobbyTeamNameBox{margin:5px 0!important;max-width:90%!important}
+ #lobbyHostControls{margin:5px 0!important}
+ #lobbyStatus{font-size:9px!important;margin:5px 0!important}
+ #currentTeamName{font-size:9px!important}
+ #teamNameDisplay{font-size:9px!important}
+
+ /* Settings Menu */
+ #settingsMenu .center-box{max-width:90%;padding:10px}
+ #settingsMenu h1{font-size:16px;margin:5px 0}
+ .settings-group{gap:10px;margin:10px 0}
+ .setting-item{gap:5px;flex-direction:column;align-items:center}
+ .setting-item label{font-size:10px}
+ .setting-item span{font-size:10px}
+ input[type=range]{max-width:200px;height:20px}
+
+ /* Leaderboard Menu */
+ #leaderboardMenu .center-box{max-width:90%;padding:10px}
+ #leaderboardMenu h1{font-size:16px;margin:5px 0}
+
+ /* My Codes Menu */
+ #myCodesMenu .center-box{max-width:90%;padding:10px}
+ #myCodesMenu h1{font-size:16px;margin:5px 0}
+
+ /* Impressum & Datenschutz */
+ #impressumMenu .center-box,#datenschutzMenu .center-box{max-width:95%;padding:10px}
+ #impressumMenu h1,#datenschutzMenu h1{font-size:14px;margin:5px 0}
+ #impressumMenu .leaderboard-box,#datenschutzMenu .leaderboard-box{
+ max-height:250px!important;
+ padding:10px!important;
+ font-size:9px!important;
+ line-height:1.3!important;
+ }
+ #impressumMenu h3,#datenschutzMenu h3{font-size:10px!important;margin:10px 0 5px 0!important}
+ #impressumMenu h4,#datenschutzMenu h4{font-size:9px!important;margin:8px 0 3px 0!important}
+ #impressumMenu p,#datenschutzMenu p{font-size:9px!important;margin:3px 0!important}
+ #impressumMenu ul,#datenschutzMenu ul{margin:3px 0!important;padding-left:15px!important}
+ #impressumMenu li,#datenschutzMenu li{font-size:9px!important;margin:2px 0!important}
+
+ /* Common */
+ input{padding:5px;font-size:12px;width:150px;margin-bottom:5px}
+ .leaderboard-box{max-height:120px;padding:8px;margin-top:5px;font-size:10px}
+ .leaderboard-item{font-size:9px;padding:3px 0}
+ .leaderboard-rank,.leaderboard-name,.leaderboard-score{font-size:9px}
+ .big-btn{padding:8px 12px;font-size:10px;margin:5px}
+ .back-btn{padding:8px 12px;font-size:10px;margin-top:10px}
+ .center-box{max-width:90%;padding:10px}
+ .legal-btn{padding:6px 10px;font-size:8px;margin:3px}
+ p{font-size:10px;margin:5px 0}
+
+ /* Overlay Screens */
+ .overlay-screen{padding:10px;overflow-y:auto}
}
-@media screen and (orientation:portrait){#rotate-overlay{display:flex}#game-container{display:none!important}}
-@media (min-width:1024px){h1{font-size:48px}button{font-size:22px;padding:20px 40px}input[type=text]{max-width:450px;font-size:20px;padding:15px}.info-box{max-width:500px}.hall-of-fame-box{max-height:400px}}
+@media screen and (orientation:portrait){
+ #rotate-overlay{display:flex}
+ #game-container{display:none!important}
+}