当前位置: 首页 > 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();

http://www.xdnf.cn/news/496693.html

相关文章:

  • 亲测有效!OGG 创建抽取进程报错 OGG-08241,如何解决?
  • 简单神经网络(ANN)实现:从零开始构建第一个模型
  • 【第二篇】 初步解析Spring Boot
  • 第9讲、深入理解Scaled Dot-Product Attention
  • 【漫话机器学习系列】264.内距(又称四分位差)Interquartile Range
  • 抽奖系统-抽奖
  • uni-app小程序登录后…
  • 数据分析_Python
  • arduino平台读取鼠标光电传感器
  • MATLAB学习笔记(七):MATLAB建模城市的雨季防洪排污的问题
  • Elasticsearch 性能优化面试宝典
  • LabVIEW声音与振动测量分析
  • STM32实战指南:SG90舵机控制原理与代码详解
  • Qt与Hid设备通信
  • 392. Is Subsequence
  • 天拓四方锂电池卷绕机 PLC 物联网解决方案
  • 从零开始认识 Node.js:异步非阻塞的魅力
  • Go语言 GORM框架 使用指南
  • c/c++的opencv模糊
  • exit耗时高
  • PYTHON训练营DAY28
  • AMD Vivado™ 设计套件生成加密比特流和加密密钥
  • 【React中虚拟DOM与Diff算法详解】
  • 免费商用字体下载
  • STM32IIC协议基础及Cube配置
  • 创建react工程并集成tailwindcss
  • C++(20): 文件输入输出库 —— <fstream>
  • Pytorch实现常用代码笔记
  • 从代码学习深度学习 - 词嵌入(word2vec)PyTorch版
  • 05、基础入门-SpringBoot-HelloWorld