Skip to content

Commit 20eb448

Browse files
committed
fff
1 parent 76087d4 commit 20eb448

File tree

1 file changed

+121
-27
lines changed

1 file changed

+121
-27
lines changed

roblox/index.html

Lines changed: 121 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<head>
99
<meta charset="UTF-8">
1010
<meta name="viewport" content="width=device-width, initial-scale=1.0">
11-
<title>Firebase Cube Game</title>
11+
<title>Firebase Ball Game</title>
1212
<style>
1313
/* Global styles for the page */
1414
body {
@@ -144,6 +144,12 @@
144144
// Three.js and Cannon.js variables
145145
let scene, camera, renderer, world;
146146
let playerBody, playerMesh;
147+
let cameraPivot; // New pivot for camera orbit
148+
149+
// Camera control state
150+
let isRightMouseDown = false;
151+
let lastMouseX = 0;
152+
let lastMouseY = 0;
147153

148154
// Keyboard input state
149155
const keyboard = {};
@@ -186,6 +192,45 @@
186192
}
187193
}
188194

195+
/**
196+
* Creates a new ball mesh with an attached face.
197+
* @param {number} radius The radius of the ball.
198+
* @param {string} color The hex color for the ball material.
199+
* @returns {THREE.Mesh} The new mesh object.
200+
*/
201+
function createPlayerMesh(radius, color) {
202+
const ballGeometry = new THREE.SphereGeometry(radius, 32, 32);
203+
const ballMaterial = new THREE.MeshStandardMaterial({ color: color, metalness: 0.5, roughness: 0.5 });
204+
const ballMesh = new THREE.Mesh(ballGeometry, ballMaterial);
205+
ballMesh.castShadow = true;
206+
207+
// Create the cute face texture using an SVG data URL
208+
const faceSvg = `
209+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
210+
<!-- Eyes -->
211+
<circle cx="30" cy="40" r="10" fill="#000"/>
212+
<circle cx="70" cy="40" r="10" fill="#000"/>
213+
<!-- Mouth -->
214+
<path d="M 35 65 Q 50 85, 65 65" stroke="#000" stroke-width="5" fill="none" stroke-linecap="round"/>
215+
</svg>
216+
`;
217+
const faceDataUrl = `data:image/svg+xml;utf8,${encodeURIComponent(faceSvg)}`;
218+
const faceTexture = new THREE.TextureLoader().load(faceDataUrl);
219+
const faceMaterial = new THREE.MeshBasicMaterial({ map: faceTexture, transparent: true });
220+
221+
// Create a plane for the face
222+
const facePlane = new THREE.Mesh(new THREE.PlaneGeometry(1.5, 1.5), faceMaterial);
223+
224+
// Position the face in front of the ball
225+
facePlane.position.z = radius;
226+
facePlane.rotation.y = Math.PI;
227+
228+
// Add the face to the ball mesh as a child
229+
ballMesh.add(facePlane);
230+
231+
return ballMesh;
232+
}
233+
189234
/**
190235
* Sets up the initial game world, including the 3D scene, physics,
191236
* and the player's cube. This runs only after authentication is complete.
@@ -197,8 +242,12 @@
197242

198243
// 2. Camera setup
199244
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
200-
camera.position.set(0, 10, 20);
201-
camera.lookAt(new THREE.Vector3(0, 0, 0));
245+
camera.position.set(0, 5, 15);
246+
247+
// Create a pivot for the camera and add it to the scene
248+
cameraPivot = new THREE.Object3D();
249+
scene.add(cameraPivot);
250+
cameraPivot.add(camera);
202251

203252
// 3. Renderer setup
204253
renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('gameCanvas'), antialias: true });
@@ -266,15 +315,13 @@
266315
wallRight.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), -Math.PI / 2);
267316
world.addBody(wallRight);
268317

269-
// 9. Player's cube (3D and Physics)
270-
const playerShape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
318+
// 9. Player's ball (3D and Physics)
319+
const playerShape = new CANNON.Sphere(1); // Radius of 1
271320
playerBody = new CANNON.Body({ mass: 5, shape: playerShape, position: new CANNON.Vec3(0, 10, 0) });
272321
world.addBody(playerBody);
273322

274-
const playerGeometry = new THREE.BoxGeometry(2, 2, 2);
275-
const playerMaterial = new THREE.MeshStandardMaterial({ color: 0x00ffaa, metalness: 0.5, roughness: 0.5 });
276-
playerMesh = new THREE.Mesh(playerGeometry, playerMaterial);
277-
playerMesh.castShadow = true;
323+
// Create the player's mesh using the new function
324+
playerMesh = createPlayerMesh(1, 0x00ffaa);
278325
scene.add(playerMesh);
279326

280327
// Start the animation loop
@@ -300,11 +347,11 @@
300347
}
301348

302349
if (change.type === "added" || change.type === "modified") {
303-
// Add or update the player's cube
350+
// Add or update the player's cube/ball
304351
if (!playerCubes[playerId]) {
305-
const otherPlayerGeometry = new THREE.BoxGeometry(2, 2, 2);
306-
const otherPlayerMaterial = new THREE.MeshStandardMaterial({ color: Math.random() * 0xffffff, metalness: 0.5, roughness: 0.5 });
307-
const otherPlayerMesh = new THREE.Mesh(otherPlayerGeometry, otherPlayerMaterial);
352+
// Create a sphere with a random color for other players
353+
const randomColor = Math.random() * 0xffffff;
354+
const otherPlayerMesh = createPlayerMesh(1, randomColor);
308355
scene.add(otherPlayerMesh);
309356
playerCubes[playerId] = otherPlayerMesh;
310357
}
@@ -328,7 +375,7 @@
328375
}
329376

330377
/**
331-
* Updates the current user's cube position and rotation in Firestore.
378+
* Updates the current user's ball position and rotation in Firestore.
332379
*/
333380
function updateMyPositionInFirestore() {
334381
if (!authReady) return;
@@ -360,25 +407,38 @@
360407
// Update physics world
361408
world.step(1/60);
362409

363-
// Update player cube position from physics body
410+
// Update player ball position from physics body
364411
playerMesh.position.copy(playerBody.position);
365-
playerMesh.quaternion.copy(playerBody.quaternion);
412+
413+
// Update the player mesh's rotation to match the camera's Y-axis rotation
414+
playerMesh.rotation.y = cameraPivot.rotation.y;
415+
416+
// Update the camera pivot's position to follow the player
417+
cameraPivot.position.copy(playerMesh.position);
366418

367419
// Handle keyboard input and apply forces
368420
const force = 50;
369421
const jumpForce = 400;
422+
423+
// Get the camera's forward and right directions based on its rotation
424+
const forwardVector = new THREE.Vector3(0, 0, -1).applyQuaternion(camera.quaternion);
425+
const rightVector = new THREE.Vector3(1, 0, 0).applyQuaternion(camera.quaternion);
426+
427+
// Normalize these vectors to ensure consistent speed
428+
forwardVector.normalize();
429+
rightVector.normalize();
370430

371431
if (keyboard['ArrowUp'] || keyboard['KeyW']) {
372-
playerBody.applyLocalForce(new CANNON.Vec3(0, 0, -force), playerBody.position);
432+
playerBody.applyImpulse(new CANNON.Vec3(forwardVector.x * force, 0, forwardVector.z * force), playerBody.position);
373433
}
374434
if (keyboard['ArrowDown'] || keyboard['KeyS']) {
375-
playerBody.applyLocalForce(new CANNON.Vec3(0, 0, force), playerBody.position);
435+
playerBody.applyImpulse(new CANNON.Vec3(-forwardVector.x * force, 0, -forwardVector.z * force), playerBody.position);
376436
}
377437
if (keyboard['ArrowLeft'] || keyboard['KeyA']) {
378-
playerBody.applyLocalForce(new CANNON.Vec3(-force, 0, 0), playerBody.position);
438+
playerBody.applyImpulse(new CANNON.Vec3(-rightVector.x * force, 0, -rightVector.z * force), playerBody.position);
379439
}
380440
if (keyboard['ArrowRight'] || keyboard['KeyD']) {
381-
playerBody.applyLocalForce(new CANNON.Vec3(force, 0, 0), playerBody.position);
441+
playerBody.applyImpulse(new CANNON.Vec3(rightVector.x * force, 0, rightVector.z * force), playerBody.position);
382442
}
383443
if (keyboard['Space']) {
384444
// Simple jump logic: check if on the ground
@@ -387,11 +447,6 @@
387447
}
388448
}
389449

390-
// Update camera position to follow the player
391-
const cameraOffset = new THREE.Vector3(0, 5, 15);
392-
camera.position.copy(playerMesh.position).add(cameraOffset);
393-
camera.lookAt(playerMesh.position);
394-
395450
// Render the scene
396451
renderer.render(scene, camera);
397452

@@ -428,6 +483,44 @@
428483
}
429484
});
430485

486+
// Camera control event listeners
487+
window.addEventListener('contextmenu', (event) => {
488+
event.preventDefault();
489+
});
490+
491+
window.addEventListener('mousedown', (event) => {
492+
if (event.button === 2) { // Right mouse button
493+
isRightMouseDown = true;
494+
lastMouseX = event.clientX;
495+
lastMouseY = event.clientY;
496+
}
497+
});
498+
499+
window.addEventListener('mouseup', (event) => {
500+
if (event.button === 2) {
501+
isRightMouseDown = false;
502+
}
503+
});
504+
505+
window.addEventListener('mousemove', (event) => {
506+
if (!isRightMouseDown) return;
507+
508+
const deltaX = event.clientX - lastMouseX;
509+
const deltaY = event.clientY - lastMouseY;
510+
const rotationSpeed = 0.005;
511+
512+
// Rotate the camera pivot around the Y-axis
513+
cameraPivot.rotation.y -= deltaX * rotationSpeed;
514+
515+
// Rotate the camera up and down, but clamp the values
516+
camera.rotation.x -= deltaY * rotationSpeed;
517+
camera.rotation.x = Math.max(-Math.PI / 4, Math.min(Math.PI / 4, camera.rotation.x));
518+
519+
lastMouseX = event.clientX;
520+
lastMouseY = event.clientY;
521+
});
522+
523+
431524
// Handle window resize
432525
window.addEventListener('resize', () => {
433526
if (renderer && camera) {
@@ -448,8 +541,9 @@
448541
<body>
449542
<!-- UI overlay for instructions and user ID -->
450543
<div id="ui">
451-
<h1>3D Cube Game</h1>
452-
<p>Move with Arrow Keys or WASD, Jump with Space</p>
544+
<h1>3D Ball Game</h1>
545+
<p>Move with Arrow Keys or WASD (relative to camera), Jump with Space</p>
546+
<p>Drag with Right Mouse to look around</p>
453547
<button id="tpButton">Teleport to Spawn</button>
454548
<p id="userIdDisplay">Connecting...</p>
455549
</div>

0 commit comments

Comments
 (0)