这篇文章上次修改于 284 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
"要查看键盘快捷键,按下问号
查看键盘快捷键
Grok 对话
查看新帖子
Prompt: Build a 3D Maze Game Demo with a Soldier Character
Develop a web-based 3D game demo that features a procedurally generated maze and a soldier character with smooth movement and proper orientation. Follow these specifications:
Libraries & Dependencies:
Three.js (r128): Use Three.js (version r128) via a CDN, for example, cdnjs.
GLTFLoader: Load the 3D soldier model using GLTFLoader from jsDelivr.
OrbitControls: Implement camera control with OrbitControls from jsDelivr.
Scene Setup:
Create a Three.js scene with a perspective camera.
Set up a WebGL renderer to cover the entire browser window.
Include lighting (e.g., HemisphereLight and DirectionalLight) to properly illuminate the scene.
Add a debug overlay (an HTML element) that shows real-time data like the soldier’s position and rotation.
Maze Generation:
Build a 16×16 grid maze with each cell sized 4 units.
Always mark the outer boundary cells as walls.
Reserve a central open area (for example, cells 6–9 on both axes) without walls.
Randomly place walls in the remaining cells with a 15% chance.
Create procedural textures using canvas:
Floor Texture: A texture with a brown tone and grid lines.
Wall Texture: A texture with a base color and randomized patterns.
Wall & Collision Setup:
For each wall cell, create a box geometry mesh sized to match the cell dimensions and add it to the scene.
Compute bounding boxes for each wall to facilitate collision detection.
Implement collision detection by checking if the soldier’s position (using a collision radius) intersects any wall’s bounding box.
Soldier Character & Animations:
Load the soldier model from https://threejs.org/examples/models/gltf/Soldier.glb using GLTFLoader.
Scale and position the soldier within the open area of the maze.
Set up an AnimationMixer for the soldier with at least two actions:
Idle Animation: Plays when no movement keys are pressed.
Run Animation: Activates when the soldier is moving.
Smoothly transition between animations using fade-in and fade-out techniques.
Movement & Controls:
Implement WASD keyboard controls for moving the soldier.
Calculate movement direction using the camera’s forward (XZ-plane) and left vectors.
Rotation Fix: To ensure the soldier faces the direction of movement, compute the angle with:
js
const angle = Math.atan2(moveDirection.x, moveDirection.z);
soldier.rotation.y = angle + Math.PI;
This adjustment (adding π) is crucial for the correct orientation of the character.
Update the OrbitControls target to follow the soldier, keeping the character centered on screen.
Responsive Design:
Handle window resize events to update the camera’s aspect ratio and the renderer’s size.
Animation Loop:
Create a main animation loop that:
Updates the AnimationMixer.
Processes soldier movement and collision detection.
Updates the debug overlay.
Renders the scene continuously.
Give mw whole HTML file!
想法
展开详情
Below is the complete HTML file for the 3D Maze Game Demo with a Soldier Character, built according to the specifications provided in the prompt. This file includes all necessary HTML, CSS, and JavaScript to create a web-based game using Three.js, featuring a procedurally generated maze, a soldier with movement and animations, collision detection, and a debug overlay.
html
<!DOCTYPE html>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Maze Game Demo</title>
<style>
body {
margin: 0;
overflow: hidden; /* Prevents scrolling */
}
canvas {
display: block; /* Ensures canvas takes full space */
}
#debug {
position: absolute;
top: 10px;
left: 10px;
color: white;
background: rgba(0, 0, 0, 0.5);
padding: 5px;
font-family: monospace;
font-size: 14px;
}
</style>
<!-- Three.js r128 from CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<!-- GLTFLoader for Three.js r128 -->
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script>
<!-- OrbitControls for Three.js r128 -->
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script><canvas id="gameCanvas"></canvas>
<div id="debug">Position: 0, 0, 0<br>Rotation: 0 degrees</div>
<script>
// Get the canvas element
const canvas = document.getElementById('gameCanvas');
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.setSize(window.innerWidth, window.innerHeight);
// OrbitControls setup
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 5;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2;
// Lighting
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(10, 20, 10);
scene.add(dirLight);
// Floor texture generation
function createFloorTexture() {
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#8B4513'; // Brown tone
ctx.fillRect(0, 0, 64, 64);
ctx.strokeStyle = '#000000'; // Grid lines
ctx.lineWidth = 2;
ctx.strokeRect(0, 0, 64, 64);
return canvas;
}
const floorTexture = new THREE.CanvasTexture(createFloorTexture());
floorTexture.wrapS = THREE.RepeatWrapping;
floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set(16, 16);
const floorMaterial = new THREE.MeshLambertMaterial({ map: floorTexture });
const floorGeometry = new THREE.PlaneGeometry(64, 64);
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
scene.add(floor);
// Wall texture generation
function createWallTexture() {
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#808080'; // Base gray color
ctx.fillRect(0, 0, 64, 64);
ctx.fillStyle = '#707070'; // Slightly darker gray for patterns
for (let i = 0; i < 100; i++) {
const x = Math.random() * 64;
const y = Math.random() * 64;
const size = Math.random() * 5;
ctx.fillRect(x, y, size, size); // Randomized patterns
}
return canvas;
}
const wallTexture = new THREE.CanvasTexture(createWallTexture());
const wallMaterial = new THREE.MeshLambertMaterial({ map: wallTexture });
// Maze generation
const gridSize = 16;
const cellSize = 4;
const maze = [];
for (let i = 0; i < gridSize; i++) {
maze[i] = [];
for (let j = 0; j < gridSize; j++) {
if (i === 0 || i === gridSize - 1 || j === 0 || j === gridSize - 1) {
maze[i][j] = 1; // Outer boundary walls
} else if (i >= 6 && i <= 9 && j >= 6 && j <= 9) {
maze[i][j] = 0; // Central open area
} else {
maze[i][j] = Math.random() < 0.15 ? 1 : 0; // 15% chance of wall
}
}
}
// Wall meshes and collision boxes
const wallBoxes = [];
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
if (maze[i][j] === 1) {
const wall = new THREE.Mesh(
new THREE.BoxGeometry(cellSize, cellSize, cellSize),
wallMaterial
);
wall.position.set(
i * cellSize + cellSize / 2,
cellSize / 2,
j * cellSize + cellSize / 2
);
scene.add(wall);
const box = new THREE.Box3().setFromObject(wall);
wallBoxes.push(box);
}
}
}
// Soldier setup
let soldier;
let mixer;
let idleAction, runAction;
let currentAction;
const loader = new THREE.GLTFLoader();
loader.load('https://threejs.org/examples/models/gltf/Soldier.glb', (gltf) => {
soldier = gltf.scene;
soldier.scale.set(2, 2, 2); // Adjust scale to fit maze
soldier.position.set(30, 0, 30); // Start in central open area
scene.add(soldier);
// Animation setup
mixer = new THREE.AnimationMixer(soldier);
const clips = gltf.animations;
const idleClip = THREE.AnimationClip.findByName(clips, 'Idle');
idleAction = mixer.clipAction(idleClip);
const runClip = THREE.AnimationClip.findByName(clips, 'Run');
runAction = mixer.clipAction(runClip);
idleAction.play();
currentAction = idleAction;
// Initial camera and controls setup
controls.target.copy(soldier.position);
camera.position.set(30, 10, 40); // Above and behind soldier
没有评论