当前位置: 首页 > news >正文

WebXR教学 07 项目5 贪吃蛇小游戏

WebXR教学 07 项目5 贪吃蛇小游戏

index.html

<!DOCTYPE html>
<html>
<head><title>3D贪吃蛇小游戏</title><style>body { margin: 0; }canvas { display: block; }#score {position: absolute;top: 20px;left: 20px;color: white;font-size: 24px;}</style>
</head>
<body><div id="score">Score: 0</div><script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script><script src="game.js"></script>
</body>
</html>

game.js

/*** 场景相关变量定义*/
// Three.js 场景对象
let scene, camera, renderer;
// 游戏主要对象:蛇和食物
let snake, food;
// 游戏得分
let score = 0;
// 游戏场地大小(网格单位)
const gridSize = 20;
// 蛇身和食物的单位大小(立方体边长)
const cubeSize = 1;
// 游戏循环时间控制
let lastTime = 0;
// 蛇移动的时间间隔(毫秒)
const moveInterval = 200;
// 游戏运行状态标志
let gameLoop = false;/*** 初始化游戏场景* 创建THREE.js场景、相机、渲染器等基本组件*/
function init() {// 创建THREE.js场景scene = new THREE.Scene();// 设置透视相机:视角75度,屏幕宽高比,近裁剪面0.1,远裁剪面1000camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);// 创建WebGL渲染器renderer = new THREE.WebGLRenderer();// 设置渲染器尺寸为窗口大小renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);// 设置相机位置和视角camera.position.set(10, 15, 20);camera.lookAt(0, 0, 0);// 初始化游戏场景组件createFloor();         // 创建地板snake = new Snake();   // 创建蛇food = createFood();   // 创建第一个食物// 添加键盘事件监听document.addEventListener('keydown', onKeyPress);// 启动游戏循环gameLoop = true;requestAnimationFrame(animate);
}/*** 贪吃蛇类定义* 包含蛇的属性和行为*/
class Snake {constructor() {// 存储蛇身体段的数组this.body = [];// 初始移动方向this.direction = 'right';// 创建蛇头(绿色立方体)const geometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });const head = new THREE.Mesh(geometry, material);this.body.push(head);scene.add(head);}/*** 蛇的移动逻辑* 包含移动、碰撞检测和生长机制*/move() {const head = this.body[0];const newHead = head.clone();// 根据当前方向更新头部位置switch(this.direction) {case 'right': newHead.position.x += cubeSize; break;case 'left': newHead.position.x -= cubeSize; break;case 'up': newHead.position.z -= cubeSize; break;case 'down': newHead.position.z += cubeSize; break;}// 边界碰撞检测const halfGrid = (gridSize * cubeSize) / 2 - cubeSize/2;if (newHead.position.x > halfGrid || newHead.position.x < -halfGrid || newHead.position.z > halfGrid || newHead.position.z < -halfGrid) {gameOver("离开地板范围!");return;}// 自身碰撞检测if (this.checkSelfCollision(newHead)) {gameOver("撞到自己了!");return;}// 食物碰撞检测及处理if (this.checkCollisionWithFood(newHead)) {scene.remove(food);food = createFood();score += 10;document.getElementById('score').textContent = `Score: ${score}`;} else {// 如果没有吃到食物,移除尾部scene.remove(this.body.pop());}// 添加新头部this.body.unshift(newHead);scene.add(newHead);}/*** 检测蛇头是否与身体碰撞* @param {THREE.Mesh} head - 新的蛇头位置* @returns {boolean} - 是否发生碰撞*/checkSelfCollision(head) {return this.body.some((segment, index) => {if (index <= 0) return false;const collision = Math.abs(head.position.x - segment.position.x) < 0.5 &&Math.abs(head.position.z - segment.position.z) < 0.5;return collision;});}/*** 检测是否吃到食物* @param {THREE.Mesh} head - 蛇头位置* @returns {boolean} - 是否吃到食物*/checkCollisionWithFood(head) {return Math.abs(head.position.x - food.position.x) < 0.1 &&Math.abs(head.position.z - food.position.z) < 0.1;}
}/*** 创建食物* 随机位置生成一个红色立方体* @returns {THREE.Mesh} - 食物对象*/
function createFood() {const geometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });const food = new THREE.Mesh(geometry, material);// 计算食物可生成的范围const maxRange = (gridSize / 2) - 1;// 随机生成食物位置food.position.x = (Math.floor(Math.random() * (maxRange * 2 + 1)) - maxRange) * cubeSize;food.position.z = (Math.floor(Math.random() * (maxRange * 2 + 1)) - maxRange) * cubeSize;// 确保食物不会出现在蛇身上while (snake.body.some(segment => Math.abs(segment.position.x - food.position.x) < 0.1 && Math.abs(segment.position.z - food.position.z) < 0.1)) {food.position.x = (Math.floor(Math.random() * (maxRange * 2 + 1)) - maxRange) * cubeSize;food.position.z = (Math.floor(Math.random() * (maxRange * 2 + 1)) - maxRange) * cubeSize;}scene.add(food);return food;
}/*** 创建游戏地板* 创建一个灰色平面作为游戏场地*/
function createFloor() {const geometry = new THREE.PlaneGeometry(gridSize * cubeSize, gridSize * cubeSize);const material = new THREE.MeshBasicMaterial({ color: 0x808080,side: THREE.DoubleSide});const floor = new THREE.Mesh(geometry, material);floor.rotation.x = Math.PI / 2;scene.add(floor);
}/*** 键盘按键处理* 处理方向键控制蛇的移动方向* @param {KeyboardEvent} event - 键盘事件对象*/
function onKeyPress(event) {switch(event.key) {case 'ArrowRight': snake.direction = 'right'; break;case 'ArrowLeft': snake.direction = 'left'; break;case 'ArrowUp': snake.direction = 'up'; break;case 'ArrowDown': snake.direction = 'down'; break;}
}/*** 游戏结束处理* @param {string} reason - 游戏结束原因*/
function gameOver(reason) {gameLoop = false;alert(`游戏结束!${reason}\n最终得分:${score}`);location.reload(); // 重置游戏
}/*** 游戏动画循环* @param {number} currentTime - 当前时间戳*/
function animate(currentTime) {if (!gameLoop) return;if (!lastTime) lastTime = currentTime;const deltaTime = currentTime - lastTime;// 控制蛇的移动速度if (deltaTime >= moveInterval) {snake.move();lastTime = currentTime;}renderer.render(scene, camera);requestAnimationFrame(animate);
}// 启动游戏
init();

相关文章:

  • 阿里巴巴开源移动端多模态LLM工具——MNN
  • 北京市工程技术人才职称评价基本标准条件解读
  • 力扣HOT100之二叉树:98. 验证二叉搜索树
  • JAVA的常见API文档(上)
  • AtCoder AT_abc406_c [ABC406C] ~
  • 蓝牙耳机什么牌子好?倍思值得冲不?
  • Typecho博客为文章添加AI摘要功能(Handsome主题优化版)
  • AGI大模型(20):混合检索之rank_bm25库来实现词法搜索
  • Redis配置与优化:提升NoSQL数据库性能的关键策略
  • 【AI算法工程师面试指北】ResNet为什么用avgpool结构?
  • 超长文本能取代RAG吗
  • 图像超分-CVPR2022-Multi-scale Attention Network for Single Image Super-Resolution
  • 黑马k8s(十)
  • 打造文本差异对比工具 TextDiffX:从想法到实现的完整过程
  • 企业级电商数据对接:1688 商品详情 API 接口开发与优化实践
  • 信道编码技术介绍
  • 数值分析知识重构
  • 我与 CodeBuddy 携手打造 FontFlow 字体预览工坊
  • OpenHarmony平台驱动使用 (二),Camera
  • java.lang.ArithmeticException
  • AG600“鲲龙”批生产首架机完成生产试飞
  • 美联储官员:美国经济增速可能放缓,现行关税政策仍将导致物价上涨
  • 英德宣布开发射程超2000公里导弹,以防务合作加强安全、促进经济
  • 普京调整俄陆军高层人事任命
  • 《大风杀》导演张琪:为了不算计观众,拍了部不讨好的警匪片
  • 1至4月全国铁路发送旅客14.6亿人次,创同期历史新高