Spaces:
Running
Running
i think the render loop is not working
Browse files- index.html +65 -49
index.html
CHANGED
|
@@ -290,7 +290,6 @@
|
|
| 290 |
container.appendChild(card);
|
| 291 |
});
|
| 292 |
}
|
| 293 |
-
|
| 294 |
function initThreeJS() {
|
| 295 |
// Set up Three.js scene
|
| 296 |
scene = new THREE.Scene();
|
|
@@ -301,9 +300,15 @@
|
|
| 301 |
camera.position.set(0, 5, 10);
|
| 302 |
|
| 303 |
// Renderer
|
| 304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 306 |
renderer.shadowMap.enabled = true;
|
|
|
|
| 307 |
|
| 308 |
// Clock for animations
|
| 309 |
clock = new THREE.Clock();
|
|
@@ -312,14 +317,20 @@
|
|
| 312 |
loader = new THREE.GLTFLoader();
|
| 313 |
|
| 314 |
// Add lights
|
| 315 |
-
const ambientLight = new THREE.AmbientLight(0xffffff, 0.
|
| 316 |
scene.add(ambientLight);
|
| 317 |
|
| 318 |
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
| 319 |
-
directionalLight.position.set(
|
| 320 |
directionalLight.castShadow = true;
|
| 321 |
directionalLight.shadow.mapSize.width = 2048;
|
| 322 |
directionalLight.shadow.mapSize.height = 2048;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 323 |
scene.add(directionalLight);
|
| 324 |
|
| 325 |
// Create ground
|
|
@@ -353,8 +364,7 @@
|
|
| 353 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 354 |
});
|
| 355 |
}
|
| 356 |
-
|
| 357 |
-
function addEnvironmentObjects() {
|
| 358 |
// Add some trees
|
| 359 |
const treeGeometry = new THREE.ConeGeometry(1, 3, 8);
|
| 360 |
const treeMaterial = new THREE.MeshStandardMaterial({ color: 0x2e7d32 });
|
|
@@ -468,44 +478,39 @@
|
|
| 468 |
// Set initial animation
|
| 469 |
setPlayerAnimation('idle');
|
| 470 |
}
|
| 471 |
-
|
| 472 |
function createPlayerAnimations() {
|
| 473 |
// In a real app, these would come from the GLTF model
|
| 474 |
-
// For this example, we'll create simple animations
|
| 475 |
|
| 476 |
// Idle animation (slight bounce)
|
| 477 |
-
const idleTrack = new THREE.
|
| 478 |
-
'.position',
|
| 479 |
[0, 0.5, 1],
|
| 480 |
-
[
|
| 481 |
-
0, 0, 0, // Start position
|
| 482 |
-
0, 0.05, 0, // Up position
|
| 483 |
-
0, 0, 0 // Back to start
|
| 484 |
-
]
|
| 485 |
);
|
| 486 |
const idleClip = new THREE.AnimationClip('idle', 1, [idleTrack]);
|
| 487 |
gameState.player.animations.idle = idleClip;
|
| 488 |
|
| 489 |
// Walk animation (arm and leg movement)
|
| 490 |
-
const leftArmTrack = new THREE.
|
| 491 |
'.children[2].rotation[z]',
|
| 492 |
[0, 0.5, 1],
|
| 493 |
-
[0.
|
| 494 |
);
|
| 495 |
-
const rightArmTrack = new THREE.
|
| 496 |
'.children[3].rotation[z]',
|
| 497 |
[0, 0.5, 1],
|
| 498 |
-
[-0.
|
| 499 |
);
|
| 500 |
-
const leftLegTrack = new THREE.
|
| 501 |
'.children[4].position[y]',
|
| 502 |
[0, 0.5, 1],
|
| 503 |
-
[-0.4, -0.
|
| 504 |
);
|
| 505 |
-
const rightLegTrack = new THREE.
|
| 506 |
'.children[5].position[y]',
|
| 507 |
[0, 0.5, 1],
|
| 508 |
-
[-0.
|
| 509 |
);
|
| 510 |
const walkClip = new THREE.AnimationClip('walk', 0.5, [
|
| 511 |
leftArmTrack, rightArmTrack, leftLegTrack, rightLegTrack
|
|
@@ -519,15 +524,14 @@
|
|
| 519 |
gameState.player.animations.run = runClip;
|
| 520 |
|
| 521 |
// Jump animation
|
| 522 |
-
const jumpTrack = new THREE.
|
| 523 |
'.position[y]',
|
| 524 |
[0, 0.2, 0.4, 0.6, 0.8, 1],
|
| 525 |
-
[0,
|
| 526 |
);
|
| 527 |
const jumpClip = new THREE.AnimationClip('jump', 1, [jumpTrack]);
|
| 528 |
gameState.player.animations.jump = jumpClip;
|
| 529 |
}
|
| 530 |
-
|
| 531 |
function setPlayerAnimation(name) {
|
| 532 |
if (gameState.player.currentAnimation === name) return;
|
| 533 |
|
|
@@ -538,14 +542,14 @@
|
|
| 538 |
if (clip) {
|
| 539 |
const action = mixer.clipAction(clip);
|
| 540 |
action.setLoop(THREE.LoopRepeat);
|
|
|
|
| 541 |
action.play();
|
| 542 |
}
|
| 543 |
|
| 544 |
gameState.player.currentAnimation = name;
|
| 545 |
}
|
| 546 |
}
|
| 547 |
-
|
| 548 |
-
function loadNPCCharacters() {
|
| 549 |
gameState.selectedWorldCharacters.forEach((character, index) => {
|
| 550 |
// In a real app, we would load the GLTF model from the URL
|
| 551 |
// For this example, we'll create a simple placeholder
|
|
@@ -593,7 +597,6 @@
|
|
| 593 |
});
|
| 594 |
});
|
| 595 |
}
|
| 596 |
-
|
| 597 |
function animate() {
|
| 598 |
requestAnimationFrame(animate);
|
| 599 |
|
|
@@ -612,47 +615,62 @@
|
|
| 612 |
|
| 613 |
renderer.render(scene, camera);
|
| 614 |
}
|
| 615 |
-
|
| 616 |
function handlePlayerMovement(delta) {
|
| 617 |
if (!gameState.player) return;
|
| 618 |
|
| 619 |
const player = gameState.player;
|
| 620 |
let moving = false;
|
|
|
|
| 621 |
|
| 622 |
// Forward/backward movement
|
| 623 |
if (gameState.keys.w) {
|
| 624 |
-
|
| 625 |
moving = true;
|
| 626 |
}
|
| 627 |
if (gameState.keys.s) {
|
| 628 |
-
|
| 629 |
moving = true;
|
| 630 |
}
|
| 631 |
|
| 632 |
// Left/right movement
|
| 633 |
if (gameState.keys.a) {
|
| 634 |
-
|
| 635 |
moving = true;
|
| 636 |
}
|
| 637 |
if (gameState.keys.d) {
|
| 638 |
-
|
| 639 |
moving = true;
|
| 640 |
}
|
| 641 |
|
| 642 |
-
//
|
| 643 |
if (moving) {
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
| 648 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 649 |
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 656 |
}
|
| 657 |
// Jumping - only trigger when not showing dialog and not already jumping
|
| 658 |
if (gameState.keys.space && !gameState.isJumping && !document.getElementById('dialog-box').classList.contains('translate-y-full')) {
|
|
@@ -671,12 +689,10 @@
|
|
| 671 |
|
| 672 |
// Update camera position to follow player
|
| 673 |
const cameraOffset = new THREE.Vector3(0, 5, 10);
|
| 674 |
-
cameraOffset.applyQuaternion(player.model.quaternion);
|
| 675 |
camera.position.copy(player.model.position.clone().add(cameraOffset));
|
| 676 |
camera.lookAt(player.model.position);
|
| 677 |
}
|
| 678 |
-
|
| 679 |
-
function updatePlayerAnimation() {
|
| 680 |
if (gameState.isJumping) return;
|
| 681 |
|
| 682 |
if (!gameState.player.isMoving) {
|
|
|
|
| 290 |
container.appendChild(card);
|
| 291 |
});
|
| 292 |
}
|
|
|
|
| 293 |
function initThreeJS() {
|
| 294 |
// Set up Three.js scene
|
| 295 |
scene = new THREE.Scene();
|
|
|
|
| 300 |
camera.position.set(0, 5, 10);
|
| 301 |
|
| 302 |
// Renderer
|
| 303 |
+
const canvas = document.getElementById('canvas');
|
| 304 |
+
renderer = new THREE.WebGLRenderer({
|
| 305 |
+
canvas: canvas,
|
| 306 |
+
antialias: true,
|
| 307 |
+
alpha: true
|
| 308 |
+
});
|
| 309 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 310 |
renderer.shadowMap.enabled = true;
|
| 311 |
+
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
| 312 |
|
| 313 |
// Clock for animations
|
| 314 |
clock = new THREE.Clock();
|
|
|
|
| 317 |
loader = new THREE.GLTFLoader();
|
| 318 |
|
| 319 |
// Add lights
|
| 320 |
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
| 321 |
scene.add(ambientLight);
|
| 322 |
|
| 323 |
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
| 324 |
+
directionalLight.position.set(10, 20, 15);
|
| 325 |
directionalLight.castShadow = true;
|
| 326 |
directionalLight.shadow.mapSize.width = 2048;
|
| 327 |
directionalLight.shadow.mapSize.height = 2048;
|
| 328 |
+
directionalLight.shadow.camera.near = 0.5;
|
| 329 |
+
directionalLight.shadow.camera.far = 50;
|
| 330 |
+
directionalLight.shadow.camera.left = -20;
|
| 331 |
+
directionalLight.shadow.camera.right = 20;
|
| 332 |
+
directionalLight.shadow.camera.top = 20;
|
| 333 |
+
directionalLight.shadow.camera.bottom = -20;
|
| 334 |
scene.add(directionalLight);
|
| 335 |
|
| 336 |
// Create ground
|
|
|
|
| 364 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 365 |
});
|
| 366 |
}
|
| 367 |
+
function addEnvironmentObjects() {
|
|
|
|
| 368 |
// Add some trees
|
| 369 |
const treeGeometry = new THREE.ConeGeometry(1, 3, 8);
|
| 370 |
const treeMaterial = new THREE.MeshStandardMaterial({ color: 0x2e7d32 });
|
|
|
|
| 478 |
// Set initial animation
|
| 479 |
setPlayerAnimation('idle');
|
| 480 |
}
|
|
|
|
| 481 |
function createPlayerAnimations() {
|
| 482 |
// In a real app, these would come from the GLTF model
|
| 483 |
+
// For this example, we'll create simple animations using NumberKeyframeTrack
|
| 484 |
|
| 485 |
// Idle animation (slight bounce)
|
| 486 |
+
const idleTrack = new THREE.NumberKeyframeTrack(
|
| 487 |
+
'.position[y]',
|
| 488 |
[0, 0.5, 1],
|
| 489 |
+
[0, 0.1, 0]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 490 |
);
|
| 491 |
const idleClip = new THREE.AnimationClip('idle', 1, [idleTrack]);
|
| 492 |
gameState.player.animations.idle = idleClip;
|
| 493 |
|
| 494 |
// Walk animation (arm and leg movement)
|
| 495 |
+
const leftArmTrack = new THREE.NumberKeyframeTrack(
|
| 496 |
'.children[2].rotation[z]',
|
| 497 |
[0, 0.5, 1],
|
| 498 |
+
[0.3, -0.3, 0.3]
|
| 499 |
);
|
| 500 |
+
const rightArmTrack = new THREE.NumberKeyframeTrack(
|
| 501 |
'.children[3].rotation[z]',
|
| 502 |
[0, 0.5, 1],
|
| 503 |
+
[-0.3, 0.3, -0.3]
|
| 504 |
);
|
| 505 |
+
const leftLegTrack = new THREE.NumberKeyframeTrack(
|
| 506 |
'.children[4].position[y]',
|
| 507 |
[0, 0.5, 1],
|
| 508 |
+
[-0.4, -0.3, -0.4]
|
| 509 |
);
|
| 510 |
+
const rightLegTrack = new THREE.NumberKeyframeTrack(
|
| 511 |
'.children[5].position[y]',
|
| 512 |
[0, 0.5, 1],
|
| 513 |
+
[-0.3, -0.4, -0.3]
|
| 514 |
);
|
| 515 |
const walkClip = new THREE.AnimationClip('walk', 0.5, [
|
| 516 |
leftArmTrack, rightArmTrack, leftLegTrack, rightLegTrack
|
|
|
|
| 524 |
gameState.player.animations.run = runClip;
|
| 525 |
|
| 526 |
// Jump animation
|
| 527 |
+
const jumpTrack = new THREE.NumberKeyframeTrack(
|
| 528 |
'.position[y]',
|
| 529 |
[0, 0.2, 0.4, 0.6, 0.8, 1],
|
| 530 |
+
[0, 1.5, 1, 0.3, 0, 0]
|
| 531 |
);
|
| 532 |
const jumpClip = new THREE.AnimationClip('jump', 1, [jumpTrack]);
|
| 533 |
gameState.player.animations.jump = jumpClip;
|
| 534 |
}
|
|
|
|
| 535 |
function setPlayerAnimation(name) {
|
| 536 |
if (gameState.player.currentAnimation === name) return;
|
| 537 |
|
|
|
|
| 542 |
if (clip) {
|
| 543 |
const action = mixer.clipAction(clip);
|
| 544 |
action.setLoop(THREE.LoopRepeat);
|
| 545 |
+
action.clampWhenFinished = false;
|
| 546 |
action.play();
|
| 547 |
}
|
| 548 |
|
| 549 |
gameState.player.currentAnimation = name;
|
| 550 |
}
|
| 551 |
}
|
| 552 |
+
function loadNPCCharacters() {
|
|
|
|
| 553 |
gameState.selectedWorldCharacters.forEach((character, index) => {
|
| 554 |
// In a real app, we would load the GLTF model from the URL
|
| 555 |
// For this example, we'll create a simple placeholder
|
|
|
|
| 597 |
});
|
| 598 |
});
|
| 599 |
}
|
|
|
|
| 600 |
function animate() {
|
| 601 |
requestAnimationFrame(animate);
|
| 602 |
|
|
|
|
| 615 |
|
| 616 |
renderer.render(scene, camera);
|
| 617 |
}
|
|
|
|
| 618 |
function handlePlayerMovement(delta) {
|
| 619 |
if (!gameState.player) return;
|
| 620 |
|
| 621 |
const player = gameState.player;
|
| 622 |
let moving = false;
|
| 623 |
+
let moveDirection = new THREE.Vector3();
|
| 624 |
|
| 625 |
// Forward/backward movement
|
| 626 |
if (gameState.keys.w) {
|
| 627 |
+
moveDirection.z = -1;
|
| 628 |
moving = true;
|
| 629 |
}
|
| 630 |
if (gameState.keys.s) {
|
| 631 |
+
moveDirection.z = 1;
|
| 632 |
moving = true;
|
| 633 |
}
|
| 634 |
|
| 635 |
// Left/right movement
|
| 636 |
if (gameState.keys.a) {
|
| 637 |
+
moveDirection.x = -1;
|
| 638 |
moving = true;
|
| 639 |
}
|
| 640 |
if (gameState.keys.d) {
|
| 641 |
+
moveDirection.x = 1;
|
| 642 |
moving = true;
|
| 643 |
}
|
| 644 |
|
| 645 |
+
// Normalize direction vector if moving diagonally
|
| 646 |
if (moving) {
|
| 647 |
+
moveDirection.normalize();
|
| 648 |
+
|
| 649 |
+
// Calculate speed
|
| 650 |
+
const speed = gameState.keys.shift ? player.runSpeed : player.speed;
|
| 651 |
+
|
| 652 |
+
// Apply movement relative to player's rotation
|
| 653 |
+
const forward = new THREE.Vector3(0, 0, -1);
|
| 654 |
+
forward.applyQuaternion(player.model.quaternion);
|
| 655 |
+
forward.y = 0;
|
| 656 |
+
forward.normalize();
|
| 657 |
+
|
| 658 |
+
const right = new THREE.Vector3(1, 0, 0);
|
| 659 |
+
right.applyQuaternion(player.model.quaternion);
|
| 660 |
+
right.y = 0;
|
| 661 |
+
right.normalize();
|
| 662 |
|
| 663 |
+
const moveVector = new THREE.Vector3();
|
| 664 |
+
moveVector.addScaledVector(forward, moveDirection.z * speed);
|
| 665 |
+
moveVector.addScaledVector(right, moveDirection.x * speed);
|
| 666 |
+
|
| 667 |
+
player.model.position.add(moveVector);
|
| 668 |
+
|
| 669 |
+
// Update rotation only if moving significantly
|
| 670 |
+
if (moveDirection.length() > 0.1) {
|
| 671 |
+
const targetRotation = Math.atan2(moveDirection.x, moveDirection.z);
|
| 672 |
+
player.model.rotation.y = targetRotation;
|
| 673 |
+
}
|
| 674 |
}
|
| 675 |
// Jumping - only trigger when not showing dialog and not already jumping
|
| 676 |
if (gameState.keys.space && !gameState.isJumping && !document.getElementById('dialog-box').classList.contains('translate-y-full')) {
|
|
|
|
| 689 |
|
| 690 |
// Update camera position to follow player
|
| 691 |
const cameraOffset = new THREE.Vector3(0, 5, 10);
|
|
|
|
| 692 |
camera.position.copy(player.model.position.clone().add(cameraOffset));
|
| 693 |
camera.lookAt(player.model.position);
|
| 694 |
}
|
| 695 |
+
function updatePlayerAnimation() {
|
|
|
|
| 696 |
if (gameState.isJumping) return;
|
| 697 |
|
| 698 |
if (!gameState.player.isMoving) {
|