Okay, let's set up the initial structure for the Go-Kart game.
Okay, it seems the steering input isn't being correctly applied or used. Let's refine the PlayerController update logic slightly. We'll ensure steering is only set when a key is actively pressed and rely on the Kart class to reset it otherwise.
diff --- a/playerController.js +++ b/playerController.js @@ -24,13 +24,11 @@
update(deltaTime) {
// Steering
update(deltaTime) {
// Steering
// Kart.update resets steering to 0 each frame, so we only need to set it if a key is pressed.
if (this.keys.ArrowLeft) {
this.kart.steering = 1; // Turn left
} else if (this.keys.ArrowRight) {
this.kart.steering = -1; // Turn right
// Kart.update resets steering to 0 each frame, so we only need to set it if a key is pressed.
if (this.keys.ArrowLeft) {
this.kart.steering = 1; // Turn left
} else if (this.keys.ArrowRight) {
this.kart.steering = -1; // Turn right
} else {
} else {
this.kart.steering = 0; // Go straight
}
// Item use is handled in keyup event listener
this.kart.steering = 0; // Go straight
}
// Item use is handled in keyup event listener
This change removes the explicit setting of steering = 0 in the PlayerController when no keys are pressed. The Kart.update method already resets steering to 0 at the end of each frame, so this shouldn't change the behavior, but it simplifies the controller logic and eliminates a potential (though unlikely) source of conflict.
Make sure the browser window has focus when you press the arrow keys during the race (after the "GO!" message). Let me know if this resolves the issue.
Okay, let's tackle the glitchy item state display and the missing blast effect.
1. Item State Glitchiness:
The logic for updating the item state seems mostly correct, but timing or floating-point inaccuracies might cause the display to flicker between "Ready" and "Xs" when the cooldown is very close to zero. We can add a small tolerance. Also, let's add some logging to be sure.
2. Missing Blast:
The blast logic calculates hits but lacks visual feedback, making it hard to confirm if it's working. The effect (reducing speed directly) might also be immediately counteracted by the kart's acceleration.
Let's:
isBlasted) similar to isZapped for a more noticeable impact.diff --- a/kart.js +++ b/kart.js @@ -50,6 +50,8 @@ this.shieldTimer = 0; this.isZapped = false; this.zapTimer = 0;
this.isBlasted = false; // New state for blast effect
this.isBlasted = false; // New state for blast effect
this.blastTimer = 0;
}
setSpeedMultiplier(multiplier) {
this.blastTimer = 0;
}
setSpeedMultiplier(multiplier) {
@@ -73,6 +75,11 @@ effectiveSpeed *= 0.3; // Zap speed reduction this.zapTimer -= deltaTime; if (this.zapTimer <= 0) this.isZapped = false;
}
}
if (this.isBlasted) {
if (this.isBlasted) {
effectiveSpeed *= 0.1; // Blast speed reduction (more severe)
effectiveSpeed *= 0.1; // Blast speed reduction (more severe)
this.blastTimer -= deltaTime;
this.blastTimer -= deltaTime;
if (this.blastTimer <= 0) this.isBlasted = false;
}
// --- Movement ---
if (this.blastTimer <= 0) this.isBlasted = false;
}
// --- Movement ---
@@ -151,6 +158,22 @@ } }
applyBlast(duration) {
applyBlast(duration) {
if (this.isShielded) {
if (this.isShielded) {
console.log("Blast Blocked by Shield!");
console.log("Blast Blocked by Shield!");
this.isShielded = false; // Shield breaks
this.isShielded = false; // Shield breaks
this.shieldTimer = 0;
this.shieldTimer = 0;
// Remove shield visual if implemented
// Remove shield visual if implemented
return;
return;
}
}
if (!this.isBlasted) {
if (!this.isBlasted) {
this.isBlasted = true;
this.isBlasted = true;
this.blastTimer = duration;
this.blastTimer = duration;
this.mesh.rotateY((Math.random() - 0.5) * Math.PI * 0.5); // Random spin
this.mesh.rotateY((Math.random() - 0.5) * Math.PI * 0.5); // Random spin
console.log("Blasted!");
console.log("Blasted!");
}
}
}
}
// --- Collision/Track Boundaries (Basic) ---
// Simple check to keep karts roughly within track bounds
constrainToTrack(radius, width) {
// --- Collision/Track Boundaries (Basic) ---
// Simple check to keep karts roughly within track bounds
constrainToTrack(radius, width) {
--- a/items.js +++ b/items.js @@ -1,4 +1,5 @@ import * as THREE from 'three'; // Needed for blast/zap target finding +import { scene } from './main.js'; // Import scene for blast visual (use cautiously)
// Item Configuration const ITEM_COOLDOWN = 10.0; // seconds @@ -6,8 +7,10 @@ const SHIELD_DURATION = 5.0; const ZAP_DURATION = 3.0; const BLAST_RANGE = 20.0; -const BLAST_RADIUS = 2.0; // How wide the blast effect is +const BLAST_RADIUS = 2.5; // How wide the blast effect is +const BLAST_DURATION = 1.5; // How long the blast effect lasts on hit karts
+const COOLDOWN_DISPLAY_TOLERANCE = 0.05; // Small tolerance for display export class ItemSystem { constructor(kart) { this.kart = kart; @@ -28,7 +31,7 @@ }
isReady() {
isReady() {
return this.currentItemType !== null && this.cooldownTimer <= 0;
return this.currentItemType !== null && this.cooldownTimer <= 0;
return this.currentItemType !== null && this.cooldownTimer <= COOLDOWN_DISPLAY_TOLERANCE;
}
update(deltaTime) {
return this.currentItemType !== null && this.cooldownTimer <= COOLDOWN_DISPLAY_TOLERANCE;
}
update(deltaTime) {
@@ -118,6 +121,7 @@ const blastOrigin = this.kart.mesh.position.clone(); const blastDirection = new THREE.Vector3(0, 0, 1).applyQuaternion(this.kart.mesh.quaternion);
this.createBlastVisual(blastOrigin, blastDirection); // Add visual effect
// Check for hits within a forward cone/cylinder
otherKarts.forEach(k => {
if (k.isShielded) {
this.createBlastVisual(blastOrigin, blastDirection); // Add visual effect
// Check for hits within a forward cone/cylinder
otherKarts.forEach(k => {
if (k.isShielded) {
@@ -134,19 +138,35 @@ if (distanceAlongBlast > 0 && distanceAlongBlast < BLAST_RANGE) { const perpendicularDistSq = toTarget.lengthSq() - distanceAlongBlast * distanceAlongBlast; // Check if target is within the blast radius
if (perpendicularDistSq < BLAST_RADIUS * BLAST_RADIUS) {
if (perpendicularDistSq < BLAST_RADIUS * BLAST_RADIUS) {
const blastRadiusSq = BLAST_RADIUS * BLAST_RADIUS;
const blastRadiusSq = BLAST_RADIUS * BLAST_RADIUS;
console.log(`Blast Check: Kart ${k.mesh.uuid.substring(0,4)} | DistAlong: ${distanceAlongBlast.toFixed(2)} | PerpDistSq: ${perpendicularDistSq.toFixed(2)} | BlastRadiusSq: ${blastRadiusSq.toFixed(2)}`);
console.log(`Blast Check: Kart ${k.mesh.uuid.substring(0,4)} | DistAlong: ${distanceAlongBlast.toFixed(2)} | PerpDistSq: ${perpendicularDistSq.toFixed(2)} | BlastRadiusSq: ${blastRadiusSq.toFixed(2)}`);
if (perpendicularDistSq < blastRadiusSq) {
console.log(`Blast hit kart ${k.mesh.uuid.substring(0,4)}`);
if (perpendicularDistSq < blastRadiusSq) {
console.log(`Blast hit kart ${k.mesh.uuid.substring(0,4)}`);
// Effect of blast: Spin out? Slow down heavily? Let's slow down.
// Effect of blast: Spin out? Slow down heavily? Let's slow down.
k.currentSpeed *= 0.2; // Drastic speed reduction
k.currentSpeed *= 0.2; // Drastic speed reduction
// Maybe add a small random rotation?
// Maybe add a small random rotation?
k.mesh.rotateY((Math.random() - 0.5) * Math.PI * 0.5);
k.mesh.rotateY((Math.random() - 0.5) * Math.PI * 0.5);
k.applyBlast(BLAST_DURATION); // Apply timed blast effect
}
}
});
k.applyBlast(BLAST_DURATION); // Apply timed blast effect
}
}
});
// Add visual effect later (e.g., expanding particle effect)
// Add visual effect later (e.g., expanding particle effect)
}
}
createBlastVisual(origin, direction) {
createBlastVisual(origin, direction) {
// Simple visual: A semi-transparent cylinder moving forward
// Simple visual: A semi-transparent cylinder moving forward
const visualGeo = new THREE.CylinderGeometry(BLAST_RADIUS, BLAST_RADIUS, 0.5, 16);
const visualGeo = new THREE.CylinderGeometry(BLAST_RADIUS, BLAST_RADIUS, 0.5, 16);
visualGeo.rotateX(Math.PI / 2); // Align with Z-axis
visualGeo.rotateX(Math.PI / 2); // Align with Z-axis
const visualMat = new THREE.MeshBasicMaterial({
const visualMat = new THREE.MeshBasicMaterial({
color: 0xffff00,
color: 0xffff00,
transparent: true,
transparent: true,
opacity: 0.5
opacity: 0.5
});
});
const visualMesh = new THREE.Mesh(visualGeo, visualMat);
const visualMesh = new THREE.Mesh(visualGeo, visualMat);
visualMesh.position.copy(origin).add(direction.clone().multiplyScalar(0.5)); // Start slightly in front
visualMesh.position.copy(origin).add(direction.clone().multiplyScalar(0.5)); // Start slightly in front
visualMesh.quaternion.setFromUnitVectors(new THREE.Vector3(0,0,1), direction); // Orient it
visualMesh.quaternion.setFromUnitVectors(new THREE.Vector3(0,0,1), direction); // Orient it
scene.add(visualMesh); // Add to the main scene (requires scene import)
scene.add(visualMesh); // Add to the main scene (requires scene import)
// Remove the visual after a short time
// Remove the visual after a short time
setTimeout(() => {
setTimeout(() => {
scene.remove(visualMesh);
scene.remove(visualMesh);
visualMat.dispose();
visualMat.dispose();
visualGeo.dispose();
visualGeo.dispose();
}, 300); // Remove after 300ms
}
getStatus() {
}, 300); // Remove after 300ms
}
getStatus() {
// console.log(`Item: ${this.currentItemType}, Cooldown: ${this.cooldownTimer.toFixed(2)}, Ready: ${this.isReady()}`); // Logging for debug
if (this.currentItemType === null) return "No Item";
if (this.isReady()) return `${this.currentItemType} (Ready)`;
const remaining = Math.ceil(this.cooldownTimer);
// console.log(`Item: ${this.currentItemType}, Cooldown: ${this.cooldownTimer.toFixed(2)}, Ready: ${this.isReady()}`); // Logging for debug
if (this.currentItemType === null) return "No Item";
if (this.isReady()) return `${this.currentItemType} (Ready)`;
const remaining = Math.ceil(this.cooldownTimer);
--- a/main.js +++ b/main.js @@ -10,7 +10,7 @@ import { UIManager } from './ui.js';
// --- Scene Setup ---
Summary of Changes:
kart.js:
isBlasted boolean state and blastTimer.applyBlast(duration) method to handle being hit by a blast (checks shield, applies state, spins kart).update() to check isBlasted and reduce speed significantly if true.items.js:
scene from main.js (note: this creates a tighter coupling, but is simple for adding temporary visuals).BLAST_DURATION. Increased BLAST_RADIUS slightly.COOLDOWN_DISPLAY_TOLERANCE and used it in isReady().fireBlast():
createBlastVisual() to show the effect.applyBlast(BLAST_DURATION) method instead of directly modifying speed.createBlastVisual() function to generate and temporarily add a yellow cylinder to the scene.getStatus() if you need detailed debugging of the cooldown timer.main.js:
scene variable so items.js can access it.Now, when you use the blast item, you should see a brief yellow cylinder appear in front of your kart, and any karts hit by it (within range and radius, not shielded) should spin slightly and slow down considerably for 1.5 seconds. The item status display should also be slightly more stable near the cooldown completion point.