WebXR教学 05 项目3 太空飞船小游戏

news2025/4/24 11:41:21

准备工作

自动创建 package.json 文件

npm init -y 

安装Three.js 3D 图形库,安装现代前端构建工具Vite(用于开发/打包)

npm install three vite 

启动 Vite 开发服务器(推荐)(正式项目开发)

npm run dev

启动 Vite 开发服务器(快速测试或临时使用)

npx vite
npm init -y 

说明:

  1. 自动创建 package.json 文件
  2. -y 参数表示接受所有默认选项
  3. 生成包含项目基本信息、依赖和脚本的基础配置文件
    npm install three vite 说明:
  4. three - 安装 Three.js 3D 图形库(当前项目核心依赖)
  5. vite - 安装现代前端构建工具(用于开发/打包)
  6. 安装后会生成 node_modules 目录和 package-lock.json
    VS Code颜色高亮插件:Color Highlight

项目结构

在这里插入图片描述

代码

package.json

{
  "name": "test",
  "version": "1.0.0",
  "main": "main.js",
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "three": "^0.148.0",
    "vite": "^6.2.0"
  }
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>太空飞船小游戏</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <div id="score">0</div>
    <div id="gameOver">游戏结束</div>
    <script type="module" src="./main.js"></script>
</body>
</html>
style.css
body {
    margin: 0;
    /* background-color: black; */
    overflow: hidden;
}

#gameOver {
    position: absolute;
    /* 以自身宽度和高度向左、上移动一定距离,使其居中 */
    transform: translate(-50%, -50%);
    left: 50%;
    top: 50%;
    color: red;
    display: none;
    font-size: 48px;
}

#score {
    position: absolute;
    transform: translate(-50%,0);
    left: 50%;
    color: white;
    display: block;
    font-size: 50px;
    margin: 0 auto;
}

main.js

// ============== 全局声明区 ==============
import * as THREE from 'three';

// 游戏状态相关变量
let scene, camera, renderer, ship, stone;
let stones = [];
let moveLeft = false, moveRight = false;
let gameActive = true;
let score = 0; 
let lastScoreUpdate = Date.now();

// ============== 核心逻辑模块 ==============
// 初始化游戏基础设置
function init(){
    // 场景初始化三要素
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
    renderer = new THREE.WebGLRenderer();

    // 渲染基础配置
    scene.background = new THREE.Color(0);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    camera.position.z = 10;

    // 光照系统初始化
    const ambientLight = new THREE.AmbientLight(0x404040);
    scene.add(ambientLight);

    // 游戏实体初始化
    ship = new Ship(scene);
    stone = new Stone(scene);

    // 事件系统启动
    setupEventListeners();

    // 启动游戏主循环
    gameLoop();
}

// 主游戏循环(每帧执行)
function gameLoop(){
    if(!gameActive) return;
    requestAnimationFrame(gameLoop);

    // === 分数系统 ===
    const now = Date.now();
    if (now - lastScoreUpdate >= 1000) {
        score++;
        document.getElementById('score').textContent = score;
        lastScoreUpdate = now;
    }
    
    // === 玩家控制 ===
    if(moveLeft) ship.move('left');
    if(moveRight) ship.move('right');

    // === 陨石管理系统 ===
    // 生成逻辑(30%概率/帧)
    if(Math.random() < 0.3) {
        stones.push(new Stone(scene));
    }

    // 更新循环
    stones.forEach((stone, index) => {
        // 运动逻辑
        stone.move();

        // 碰撞检测
        if(checkCollision(ship.object, stone.object)) {
            endGame();
        }
        
        // 对象回收
        if(stone.isOutOfScreen()) {
            scene.remove(stone.object);
            stones.splice(index, 1);
        }
    });

    // 场景渲染
    renderer.render(scene, camera);
}

// ============== 输入控制模块 ==============
function setupEventListeners() {
    // 键盘事件监听
    document.addEventListener('keydown', (e) => {
        if(e.key === 'ArrowLeft') moveLeft = true;
        if(e.key === 'ArrowRight') moveRight = true;
    });

    document.addEventListener('keyup', (e) => {
        if(e.key === 'ArrowLeft') moveLeft = false;
        if(e.key === 'ArrowRight') moveRight = false;
    });

    // 窗口自适应
    window.addEventListener('resize', () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    });
}

// ============== 游戏逻辑模块 ==============
// 简易距离碰撞检测
function checkCollision(objA, objB) {
    return objA.position.distanceTo(objB.position) < 1.2;
}

// 游戏结束处理
function endGame() {
    gameActive = false;
    document.getElementById('gameOver').style.display = 'block';
}

// ============== 游戏对象类 ==============
// 玩家飞船实体
class Ship {
    constructor(scene) {
        this.object = this.createShip();
        this.speed = 0.2;
        scene.add(this.object);
    }
    
    // 飞船建模
    createShip() {
        const geometry = new THREE.ConeGeometry(0.5, 1, 8);
        const material = new THREE.MeshBasicMaterial({color: 0x00ff00});
        const ship = new THREE.Mesh(geometry, material);
        geometry.rotateX(Math.PI/2);
        ship.position.set(0, -4, 0);
        
        // 线框增强显示
        const wireframe = new THREE.LineSegments(
            new THREE.EdgesGeometry(geometry),
            new THREE.LineBasicMaterial({ color: 0xffffff })
        );
        ship.add(wireframe);
        
        return ship;
    }

    // 移动控制逻辑
    move(direction) { 
        const maxX = 10;
        if(direction === 'left' && this.object.position.x > -maxX) {
            this.object.position.x -= this.speed;
        }
        if(direction === 'right' && this.object.position.x < maxX) {
            this.object.position.x += this.speed;
        }
    }
}

// 陨石实体
class Stone {
    constructor(scene) {
        this.object = this.create();
        this.speed = 0.1;
        scene.add(this.object);
        this.resetPosition();
    }

    // 陨石建模
    create() {
        return new THREE.Mesh(
            new THREE.IcosahedronGeometry(0.5, 1),
            new THREE.MeshPhongMaterial({
                color: 0xff4500,
                emissive: 0xff6347,
                emissiveIntensity: 0.6,
                specular: 0xffffff,
                shininess: 50,
                wireframe: true
            })
        );
    }

    // 位置初始化
    resetPosition() {
        this.object.position.set(
            (Math.random() - 0.5) * 20,
            9,
            0
        );
    }

    // 下落逻辑
    move() {
        this.object.position.y -= this.speed;
    }

    // 边界检测
    isOutOfScreen() {
        return this.object.position.y < -5;
    }
}

// ============== 程序入口 ==============
init();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2341432.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

达梦统计信息收集情况检查

查询达梦某个对象上是否有统计信息 select id,T_TOTAL,N_SMAPLE,N_DISTINCT,N_NULL,BLEVEL,N_LEAF_PAGES,N_LEAF_USED_PAGES,LAST_GATHERED from sysstats where id IN (select id from sysobjects where upper(name)upper(&objname));可能有系统对象&#xff0c;可以增加…

【matlab】气泡图的应用

【matlab】气泡图的应用 .rtcContent { padding: 30px; } .lineNode {font-size: 12pt; font-family: "Times New Roman", Menlo, Monaco, Consolas, "Courier New", monospace; font-style: normal; font-weight: normal; } clear load zb_equi.mat load …

飞帆控件:在编辑模式下额外加载的库

飞帆是一个自由的控件设计平台。在飞帆中&#xff0c;我们可以很方便地创建基于 Vue 2 组件的控件&#xff0c;并使用控件来搭建网页。 他山之石&#xff0c;可以攻玉。在创建控件中&#xff0c;使用 js 、css 依赖库能让我们的控件更强大。 有些时候&#xff0c;在编辑模式下…

Super-Vlan和MUX-Vlan的原理、配置、区别

Super-Vlan 原理 Super-Vlan也叫Aggregate-Vlan。 一般的三层交换机中&#xff0c;通常是采用一个VLAN对应一个vlanif接口的方式实现广播域之间的互通&#xff0c;这在某些情况下导致了IP地址的浪费。因为一个VLAN对应的子网中&#xff0c;子网号、子网定向广播地址、子网缺…

el-table怎么显示 特殊单元格的值

1. 在 el-table-column 上绑定了 formatter 方法 formatEntityName &#xff0c;它会对每一行该列的数据&#xff08; cellValue &#xff09;进行处理。 2. 在 formatEntityName 方法中&#xff0c;尝试对传入的 cellValue 进行 JSON.parse 操作&#xff0c;并根…

2025-04-23 Python深度学习3——Tensor

文章目录 1 张量1.1 数学定义1.2 PyTorch中的张量 2 创建 Tensor2.1 直接创建**torch.tensor()****torch.from_numpy()** 2.2 依据数值创建**torch.zeros() / torch.zeros_like()****torch.ones() / torch.ones_like()****torch.full() / torch.full_like()****torch.arange() …

在统信UOS/麒麟Kylin OS操作系统中配置APT和GIT代理

在统信UOS/麒麟Kylin OS操作系统中配置APT和GIT代理 在内网环境中&#xff0c;直接访问外部资源可能会受到限制&#xff0c;这时候配置APT和GIT的代理就显得尤为重要。本文将详细介绍如何在统信UOS和麒麟Kylin OS操作系统中配置APT和GIT的代理。 为什么需要配置APT和GIT代理&…

第十七讲、Isaaclab中使用操作空间控制器

0 前言 官方教程&#xff1a;https://isaac-sim.github.io/IsaacLab/main/source/tutorials/05_controllers/run_osc.html IsaacsimIsaaclab安装&#xff1a;https://blog.csdn.net/m0_47719040/article/details/146389391?spm1001.2014.3001.5502 有时候&#xff0c;仅使用…

基于SpringBoot的校园二手商品在线交易系统+含项目运行说明文档

基于SpringBoot的校园二手商品在线交易系统含项目运行说明文档 专注校园二手交易平台是一个基于Java的在线市场&#xff0c;专为学生设计&#xff0c;便于买卖二手商品。平台提供全面的用户管理功能&#xff0c;包括学生、管理员和二手商品卖家账户管理。商品管理功能允许用户…

详解springcloud gateway工作原理、断言、filter、uri、id、全局跨域、globalfilter等以及关键源码实现

1.gateway概念 网关就是当前微服务项目的"统一入口"程序中的网关就是当前微服务项目对外界开放的统一入口所有外界的请求都需要先经过网关才能访问到我们的程序提供了统一入口之后,方便对所有请求进行统一的检查和管理 2. 网关的主要功能 将所有请求统一经过网关网…

C++面向对象特性之继承篇

C语音是面向过程的语言&#xff0c;而C在其之上多了面向对象的特性&#xff0c;面向对象三大特性:封装性、继承性、多态性。今天主包来讲讲自己学到的关于C继承特性的知识。 一、继承是什么 继承是提高代码复用的一种重要手段。正如C的模版、泛型编程等等都是为了实现代码复用…

【AI News | 20250423】每日AI进展

AI Repos 1、suna Suna是一款完全开源的AI助手&#xff0c;旨在通过自然对话帮助用户轻松完成现实世界的任务。它作为您的数字伙伴&#xff0c;提供研究、数据分析和日常问题解决等功能&#xff0c;并结合强大的能力与直观的界面&#xff0c;理解您的需求并交付成果。Suna的工…

【学习准备】算法和开发知识大纲

1 缘起 今年&#xff08;2025年&#xff09;的职业升级结果&#xff1a;不通过。没办法升职加薪了。 需要开始完善学习&#xff0c;以应对不同的发展趋势&#xff0c;为了督促自己学习&#xff0c;梳理出相关学习大纲。 分为算法和开发两部分。 算法&#xff0c;包括基础算法和…

第七篇:linux之基本权限、进程管理、系统服务

第七篇&#xff1a;linux之基本权限、进程管理、系统服务 文章目录 第七篇&#xff1a;linux之基本权限、进程管理、系统服务一、基本权限1、什么是权限&#xff1f;2、为什么要有权限&#xff1f;3、权限与用户之间的关系&#xff1f;4、权限对应的数字含义5、使用chmod设定权…

爬虫案例-爬取某企数据

文章目录 1、准备要爬取企业名称数据表2、爬取代码3、查看效果 1、准备要爬取企业名称数据表 企业名称绍兴市袍江王新国家庭农场绍兴市郑杜粮油专业合作社绍兴市越城区兴华家庭农场绍兴市越城区锐意家庭农场绍兴市越城区青甸畈家庭农场绍兴市袍江王新国家庭农场绍兴市袍江月明…

学习笔记—C++—string(一)

目录 string 为什么学习string的类 string类的常用接口 string类对象的常见构造 string类对象的访问及遍历操作 operator[] 迭代器 范围for auto 迭代器&#xff08;二&#xff09; string类对象的容量操作 size,length,max_size,capacity,clear基本用法 reserve 提…

GPLT-2025年第十届团体程序设计天梯赛总决赛题解(共计266分)

今天偶然发现天梯赛的代码还保存着&#xff0c;于是决定写下这篇题解&#xff0c;也算是复盘一下了 L1本来是打算写的稳妥点&#xff0c;最后在L1-6又想省时间&#xff0c;又忘记了insert&#xff0c;replace这些方法怎么用&#xff0c;也不想花时间写一个文件测试&#xff0c…

MySQL数据库精研之旅第十期:打造高效联合查询的实战宝典(一)

专栏&#xff1a;MySQL数据库成长记 个人主页&#xff1a;手握风云 目录 一、简介 1.1. 为什么要使用联合查询 1.2. 多表联合查询时的计算 1.3. 示例 二、内连接 2.1. 语法 2.2. 示例 三、外连接 4.1. 语法 4.2. 示例 一、简介 1.1. 为什么要使用联合查询 一次查询需…

15.FineReport动态展示需要的列

1.首先连接自带的sqlite数据库&#xff0c;具体方法参考下面的链接 点击查看连接sqlite数据库 2.文件 – 新建普通报表 3.新建数据库查询 4.查询自带的销售明细表 5.把数据添加到格子中&#xff0c;并设置边框颜色等格式 6.查询新的数据集&#xff1a;column 7.点笔 8.全部添…

Windows云主机远程连接提示“出现了内部错误”

今天有人反馈说有个服务器突然连不上了&#xff0c;让我看下什么问题&#xff0c;我根据他给的账号密码试了下发现提示“出现了内部错误”&#xff0c;然后就是一通排查 先是查看安全组&#xff0c;没发现特别的问题&#xff0c;因为也没有调过这块的配置 然后通过控制台登录进…