
WebXR教学 06 项目4 跳跃小游戏(1)介绍
准备
bash
npm install vite three
代码
vite.config.js
javascript
import { defineConfig } from 'vite';
export default defineConfig({
build: {
emptyOutDir: false,
},
server: {
middleware: (req, res, next) => {
if (req.url === '/favicon.ico') {
res.statusCode = 404;
res.end();
return;
}
next();
},
},
});
package.json
json
{
"name": "three-vite",
"version": "0.0.1",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"three": "^0.174.0",
"vite": "^6.2.1"
}
}
index.html
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>跳跃小游戏</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="score">得分:0</div>
<div id="game-over" class="hidden">游戏结束!得分:0</div>
<canvas id="gameCanvas"></canvas>
<script type="module" src="./main.js"></script>
</body>
</html>
style.css
css
body {
margin: 0;
overflow: hidden;
background-color: white;
}
#canvas {
display: block;
}
#score {
position: absolute;
left: 50%;
top: 10px;
transform: translateX(-50%);
font-size: 30px;
}
#game-over {
position: absolute;
left: 50%;
top: 50%;
font-size: 30px;
transform: translate(-50%, -50%);
}
.hidden {
display: none;
}
main.js
javascript
import * as THREE from 'three';
import { distance } from 'three/tsl';
let mario; // 玩家(马里奥)
let gameOver = false; // 游戏是否结束
let velocity = 0; // 玩家垂直速度
let isJumping = false; // 跳跃状态
let jumpVelocity = 0.7; // 初始跳跃速度
let score = 0; // 分数
const gravity = 0.03; // 重力加速度
const moveSpeed = 0.1; // 移动速度
const coinScore = 1; // 吃掉金币增加分数
const coinRate = 0.005; // 金币生成概率
const enemyRate = 0.005; // 敌人生成概率
const enemySpeed = 0.05; // 敌人移动速度
const enemyScore = 1; // 敌人死亡增加分数
const enemies = []; // 敌人数组
const coins = []; // 金币数组
const keys = { left: false, right: false, jump: false }; // 按键状态
// ========================
// 场景初始化
// ========================
// 创建基础场景元素
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
const renderer = new THREE.WebGLRenderer({ antialias: true, canvas: document.getElementById('gameCanvas') });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xe0e0e0); // 设置背景色
camera.position.set(0, 5, 15); // 设置相机初始位置
// ========================
// 游戏对象创建
// ========================
// 创建地面对象
function createGround() {
const groundGeometry = new THREE.PlaneGeometry(20, 1);
const groundMaterial = new THREE.MeshBasicMaterial({ color: 0x89ffff });
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.position.y = 0;
scene.add(ground);
}
// 创建玩家角色
function createPlayer(){
const marioGeometry = new THREE.BoxGeometry(1, 2, 1);
const marioMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const mario = new THREE.Mesh(marioGeometry, marioMaterial);
mario.position.set(-9, 1, 0);
scene.add(mario);
return mario;
}
// 金币生成逻辑
function spawnCoin() {
const coinGeometry = new THREE.SphereGeometry(0.5, 16, 16);
const coinMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
const coin = new THREE.Mesh(coinGeometry, coinMaterial);
coin.position.set(
Math.random() * 18 - 9, // X轴随机位置
6, // Y轴在地面以上
0
);
scene.add(coin);
coins.push(coin);
}
// 敌人生成逻辑
function spawnEnemy() {
const enemyGeometry = new THREE.BoxGeometry(1, 1, 1);
const enemyMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
const enemy = new THREE.Mesh(enemyGeometry, enemyMaterial);
enemy.position.set(10, 0.5, 0); // 从右侧生成
scene.add(enemy);
enemies.push(enemy);
}
// 游戏结束处理
function endGame() {
gameOver = true;
document.getElementById('game-over').textContent = `游戏结束!得分:${score}`;
document.getElementById('game-over').classList.remove('hidden');
}
// 碰撞检测系统
function checkCollisions() {
enemies.forEach((enemy, index) => {
const dx = mario.position.x - enemy.position.x;
const dy = mario.position.y - enemy.position.y;
// 新增踩踏敌人判断(玩家在敌人正上方且在下落)
if (
Math.abs(dx) < 0.8 && // X轴接近
dy > 0.5 && // 玩家Y坐标高于敌人
dy < 1.5 && // 有效踩踏高度范围
velocity < 0 // 玩家处于下落状态
) {
scene.remove(enemy);
enemies.splice(index, 1);
updateScore(1);
return; // 结束当前敌人的检测
}
// 侧面碰撞(玩家y坐标在敌人范围内)
if (
Math.abs(dx) < 0.8 &&
Math.abs(dy) < 1.5
) {
endGame();
}
});
}
// 更新分数
function updateScore(value) {
score += value;
document.getElementById('score').textContent = `得分:${score}`;
}
// 窗口自适应逻辑
function handleResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// ========================
// 监听事件
// ========================
function listenKey() {
// 键盘按下事件
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft' || e.key === 'a') keys.left = true;
if (e.key === 'ArrowRight' || e.key === 'd') keys.right = true;
if (e.key === ' ') keys.jump = true;
});
// 键盘释放事件
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowLeft' || e.key === 'a') keys.left = false;
if (e.key === 'ArrowRight' || e.key === 'd') keys.right = false;
if (e.key === ' ') keys.jump = false;
});
// 窗口自适应逻辑
window.addEventListener('resize', handleResize);
}
// ========================
// 游戏主循环
// ========================
// 初始化游戏
function init() {
createGround();
mario = createPlayer();
listenKey();
}
function animate() {
requestAnimationFrame(animate);
if (gameOver) return;
// 玩家移动
if (keys.left && mario.position.x > -9) {
mario.position.x -= moveSpeed;
}
if (keys.right && mario.position.x < 9) {
mario.position.x += moveSpeed;
}
// 跳跃逻辑
if (keys.jump && !isJumping && mario.position.y <= 1) {
velocity = jumpVelocity;
isJumping = true;
}
// 重力
velocity -= gravity;
mario.position.y += velocity;
if (mario.position.y <= 1) {
mario.position.y = 1;
velocity = 0;
isJumping = false;
}
// 生成金币
if (Math.random() < coinRate) {
spawnCoin();
}
// 更新金币位置
coins.forEach((coin, index) => {
let distance = mario.position.distanceTo(coin.position);
if (distance < 1) {
scene.remove(coin);
coins.splice(index, 1);
updateScore(coinScore);//加分
}
});
// 生成敌人
if (Math.random() < enemyRate) {
spawnEnemy();
}
// 更新敌人位置
enemies.forEach((enemy, index) => {
enemy.position.x -= enemySpeed; // 向左移动
if (enemy.position.x < -10) {
scene.remove(enemy);
enemies.splice(index, 1);
}
});
// 碰撞检测
checkCollisions();
// 渲染
renderer.render(scene, camera);
}
// ========================
// 游戏启动
// ========================
init(); // 初始化游戏
animate(); // 启动游戏循环