Three.js做了一个网页版的我的世界

在这里插入图片描述

前言

笔者在前一阵子接触到 Three.js 后, 发现了它能为前端 3D 可视化 / 动画 / 游戏方向带来的无限可能, 正好最近在与朋友重温我的世界, 便有了用 Three.js 来仿制 MineCraft 的想法, 正好也可以通过一个有趣的项目来学习一下前端 3D 领域

介绍

游戏介绍

相信大家对我的世界应该都不太陌生, 他是一款 3D 像素风的生存类游戏, 本项目是模仿我的世界的来进行实现的, 目前大致支持了以下的功能:

  • 方块的放置 / 破坏
  • 选择不同的方块类型
  • 移动和碰撞检测
  • 随机的地形和树木生成
  • 无限的世界
  • 保存 / 读取游戏
  • 音效和背景音乐
  • 可调节的渲染距离和视野范围
  • 基本的 UI

除此之外, 笔者目前也在尝试着为项目添加一些别的特性:

  • 生成水
  • 更多的保存栏位
  • 手机支持

玩法介绍

玩法介绍

玩法介绍也可以在 游戏体验地址 中的操作介绍下找到

技术栈介绍

因为项目的初衷便是探索一下 Three.js, 所以除了 Three.js 之外并没有别的第三方库依赖, 因此最后的打包大小其实是十分轻量级的, gzipped 后仅有 140kb 左右.

在此之上笔者还添加了 TypeScript 来进行类型检测, 并且使用了 Vite 进行的开发

源码介绍

源码结构

上图是整体项目的源码架构, 开发主要基于了 Class 写法, 主要有五大类以及一些子类, 分别为:

  • Core: 包含了 Three.js 中的一些核心内容, 并且进行一些初始化设定

  • Player: 包含了玩家的一些基础属性以及当前模式(行走, 奔跑, 作弊等)

  • Audio: 主要进行声音的导入, 并且暴露了一些用于播放音乐的 api

  • Terrain
    

    : 包含了与地形相关的各种内容:

    • Noise: 通过柏林噪音实现了地形, 树木, 方块类型的随机生成算法

    • GenerateWorker: 通过 web worker 的方式实现了地形动态生成的算法

    • Mesh
      

      : 场景中基础的网格体(方块)

      • Blocks: 自定义方块类
      • Materials: 各种方块的材质加载
    • Highlight: 实现了实时高亮准心位置方块的算法

  • Control
    

    : 包含了各种与操作相关的算法, 比如移动, 镜头转动, 碰撞检测等

    • CollideWorker: 在 web worker 中实现碰撞检测以提高运行效率
  • ui:包含了 ui 界面以及其功能:

    • Bag: 放置不同方块的背包
    • FPS: 实时展示当前 fps

核心技术点

笔者会在这一部分深入的分析一下项目的核心技术点以及一些遇到的难点, 如果大家只是来试玩看看 / 图一乐儿的话, 这一部分就可以跳过啦~ 不过要是有同学对底层的实现或者 Three.js 感兴趣的话, 也可以看看这一部分

对于文中大部分的涉及代码部分, 笔者尽可能的删去了不相关的内容, 让代码更加的易懂, 有兴趣的小伙伴也可以直接去 GitHub 上查看源码

随机的地形生成

地形生成采用了柏林噪音来进行实现的, Three.js 中自带了噪音的底层算法实现, 所以笔者只对其进行了一下简单的封装:

ts复制代码import { ImprovedNoise } from 'three/examples/jsm/math/ImprovedNoise'export default class Noise {noise = new ImprovedNoise()seed = Math.random()stoneSeed = this.seed * 0.4coalSeed = this.seed * 0.5treeSeed = this.seed * 0.7leafSeed = this.seed * 0.8get = (x: number, y: number, z: number) => {return this.noise.noise(x, y, z)}
}

然后在地形生成的模块下, 首先为不同的方块类型创建了对应数量的 InstancedMesh, 然后将其存放到名为 blocks 的数组中:

ts复制代码const blocks: THREE.InstancedMesh[] = []
blocks[i].instanceMatrix = new THREE.InstancedBufferAttribute(new Float32Array(maxCount * blocksFactor[i] * 16),16
)

然后在具体的循环中首先依据前面的种子来判断地形的高度, 然后依据具体方块类型的种子来判断具体该渲染什么样的方块类型, 最后将每一个方块的位移量写入对应的 InstancedMesh 中:

ts复制代码for (let x = -chunkSize * distance + chunkSize * chunk.x;x < chunkSize * distance + chunkSize + chunkSize * chunk.x;x++) {for (let z = -chunkSize * distance + chunkSize * chunk.y;z < chunkSize * distance + chunkSize + chunkSize * chunk.y;z++) const yOffset = Math.floor(noise.get(x / noise.gap, z / noise.gap, noise.seed) * noise.amp)matrix.setPosition(x, y + yOffset, z)// 如果为草方块blocks[BlockType.grass].setMatrixAt(blocksCount[BlockType.grass]++,matrix)// 如果为其他方块...}}

除了地形外, 对于树和树叶的生成也是大同小异, 这里就不展开了.

无限动态地形生成

除去最基本的地形外, 笔者还添加了无限动态地形生成的算法从而实现的一个无限大小的世界, 这样就不至于说会走到地形的边界然后掉出世界了 XD

具体的实现的话是通过在 requestAnimationFrame (以下简称 raf) 的回调函数中判断玩家是否移动到了新的区块, 如果区块发生了变化, 则会触发一次渲染:

ts复制代码  update = () => {this.chunk.set(Math.floor(this.camera.position.x / this.chunkSize),Math.floor(this.camera.position.z / this.chunkSize))// 当进入新的区块时, 触发一次渲染if (this.chunk.x !== this.previousChunk.x ||this.chunk.y !== this.previousChunk.y) {this.generate()}this.previousChunk.copy(this.chunk)}

对于具体的 generate 部分, 因为对于地形的位置的计算是一个比较耗时的过程, 所以如果直接在主线程中进行运算的话则会带来卡顿的感觉, 所以具体计算的部分则是移动到了 web worker 中去实现的, 然后只在主线程中行进最后的渲染.

笔者也是在这个项目中发现了 web worker 不允许传输函数, 所以像各种 Three.js 中的类都无法直接进行传输, 最后不得不封装了一些自定义的数据结构来进行数据的沟通:

ts复制代码  // 将数据传入 web workergenerate = () => {this.blocksCount = new Array(this.blocks.length).fill(0)this.generateWorker.postMessage({distance: this.distance,chunk: this.chunk,noiseSeed: this.noise.seed,treeSeed: this.noise.treeSeed,stoneSeed: this.noise.stoneSeed,coalSeed: this.noise.coalSeed,idMap: new Map<string, number>(<

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

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

相关文章

vue3+ Element-Plus 点击勾选框往input中动态添加多个tag

实现效果&#xff1a; template&#xff1a; <!--产品白名单--><div class"con-item" v-if"current 0"><el-form-item label"平台名称"><div class"contaion" click"onclick"><!-- 生成的标签 …

Unity HoloLens2 MRTK 空间锚点 基础教程

Unity HoloLens2 MRTK 空间锚点 基础教程 Unity HoloLens2 空间锚点MRTK 空间锚点 准备Unity 工程创建设置切换 UWP 平台UWP 平台设置 下载并安装混合现实功能工具导入混合现实工具包和 OpenXR 包 Unity 编辑器 UWP 设置Unity 2019.4.40 设置Unity 2022.3.0 设置Unity 2022.3.0…

KCC@深圳-升压手电制作活动

这次我们将制作一款工业风升压手电。电路简单&#xff0c;适合入门型选手。也会进行原理讲解&#xff0c;方便大家升级改造。 活动概览 活动主题升压手电制作活动时间6月16日&#xff08;周日&#xff09;13:30活动地点月亮湾山庄A13人数限制15合作伙伴706深圳&#xff08;706青…

C语言----字符函数和字符串函数

在编程的过程中&#xff0c;我们要经常处理字符和字符串&#xff0c;为了方便操作字符和字符串&#xff0c;c语言标准库中提供的一系列库函数&#xff0c;接下来我们就开始学习与认识他们 1.字符分类函数 c语言中有一系列的函数是专门做字符分类的&#xff0c;也就是一个字符…

「漏洞复现」I Doc View 在线文档预览 qJvqhFt.json 任意文件读取漏洞(XVE-2024-2115)

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

解决electron设置透明背景后,引入element-plus样式问题

首先给当前窗口设置自定义窗口以及背景色。 const mainWindow new BrowserWindow({width: 900,height: 670,show: false,autoHideMenuBar: true,...(process.platform linux ? { icon } : {}),webPreferences: {preload: join(__dirname, ../preload/index.js),sandbox: fal…

【 EI会议 | 西南大学主办 | 往届均已实现检索】第三届神经形态计算国际会议(ICNC 2024)

第三届神经形态计算国际会议&#xff08;ICNC 2024) 2024 3rd International Conference on Neuromorphic Computing (ICNC 2024) 一、重要信息 大会官网&#xff1a;www.ic-nc.org&#xff08;点击投稿/参会/了解会议详情&#xff09; 会议时间&#xff1a;2024年12月13-15…

Elasticsearch:智能 RAG,获取周围分块

作者&#xff1a;来自 Elastic Sunile Manjee 在检索增强生成 (RAG) 领域&#xff0c;一个持续存在的挑战是找到输入大型语言模型 (LLM) 的最佳数据量。数据太少会导致响应不足或不准确&#xff0c;而数据太多会导致答案模糊。这种微妙的平衡启发我开发了一个专注于智能分块和利…

社区论坛圈子软件APP ,提供互动交流、知识共享和专业交流的社交平台。

社区论坛圈子软件APP的开发能够为用户提供一个互动交流的社交平台&#xff0c;促进用户之间的知识分享、交流和互助。本文将突出社区论坛圈子软件APP的前景、作用和特点&#xff0c;以帮助您了解该系统的潜力和优势。 一、前景&#xff1a; 知识共享&#xff1a;社区论坛圈子软…

力扣每日一题-419

题目 给你一个大小为 m x n 的矩阵 board 表示甲板&#xff0c;其中&#xff0c;每个单元格可以是一艘战舰 X 或者是一个空位 . &#xff0c;返回在甲板 board 上放置的 战舰 的数量。 战舰 只能水平或者垂直放置在 board 上。换句话说&#xff0c;战舰只能按 1 x k&#xff…

一带一路情 相逢《中国缘》-诗琳探访湘西墨戎苗寨交流有感

一带一路情 相逢《中国缘》 诗琳探访湘西墨戎苗寨交流有感 5月21日至25日&#xff0c;《中国缘》栏目组组织的走进湘西苗疆边陲的文化交流活动&#xff0c;在群山环抱、绿树成荫、人文厚重的湘西古丈墨戎苗寨美丽绽放。这场以民间角度推演的中国和中亚人民的文化交流活动&am…

有一个主域名跟多个二级子域名时该怎么申请SSL证书?

当您拥有主域名以及多个子域名时&#xff0c;选择合适的SSL证书类型对于确保网站的安全性至关重要。以下是三种SSL证书类型的简要介绍&#xff1a; 单域名SSL证书&#xff1a; 功能&#xff1a;只能绑定单个域名&#xff0c;无论是主域名还是子域名。 适用场景&#xff1a;仅…

常用 磁力搜索 磁力链接 工具使用教程

一、什么是磁力链接&#xff1f; 磁力链接&#xff08;Magnet link&#xff09;是一种链接&#xff0c;它利用磁力编码来识别和获取文件的信息。它通常由一串以“magnet:?xturn:btih:”开头的字符串组成&#xff0c;后面跟着文件的哈希值。 二、如何使用磁力链接&#xff1f…

一篇文章看懂Redission原理

文章目录 ☃️可重入锁原理☃️锁重试和WatchDog机制☃️MutiLock原理 上一篇文章讲解了 Rediision的使用 ,这篇文章讲解其原理 ☃️可重入锁原理 在Lock锁中&#xff0c;他是借助于底层的一个voaltile的一个state变量来记录重入的状态的&#xff0c;比如当前没有人持有这把锁…

探索 cartesian_product:更深入理解范围库

理解范围库中的cartesian_product适配器 一、简介二、cartesian_product 适配器的动机三、将行为封装到算法中四、算法的局限性五、总结 一、简介 view::cartesian_product 适配器是range-v3 库一个新的组件。本文主要理解这个组件的功能以及它背后的设计理念&#xff0c;可以…

罗森伯格1800M 2000M 2400M 900M无源互调分析仪

在无线通信领域&#xff0c;频段是宝贵的资源&#xff0c;不同的通信系统通常会采用不同的频段以满足其传输需求。随着技术的发展&#xff0c;越来越多的通信系统被部署在各种频段上。为了准确、高效地测试和调试这些 信系统&#xff0c;各种测试设备也应运而生。源互调分析仪便…

Llama-3安装方法及应用

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

Stable Diffusion直接生成IP三视图,一天设计100个?

AI都能直接生成IP形象三视图了&#xff01; SD生成一个动物Q版IP三视图模型。标准的三视图&#xff0c;并且极富设计感&#xff0c;IP设计师的好帮手&#xff0c;用来辅助创意&#xff0c;建模参考。这个模型主要是动物类&#xff0c;一般不需堆叠复杂的质量词&#xff0c;直接…

资源付费系统小程序APP公众号h5源码

&#x1f510; 揭秘“资源付费系统”&#xff1a;知识、技能与价值的交汇点 &#x1f48e; &#x1f31f; 引言&#xff1a;为何资源需要付费&#xff1f; 在数字化时代&#xff0c;我们周围充斥着大量的信息。但并非所有信息都具有同等的价值。其中&#xff0c;那些经过精心…

入门 Axure RP 9 | 原型设计基础教程

选择正确的原型设计工具并非易事&#xff0c;Axure RP 9能够快速完成原型设计。原型设计是一种经过时间考验的方法&#xff0c;可以将你的设计快速放置在用户的设备并交到他们手中。替代Axure RP 9的原型设计工具即时设计是一个完全集成的协同设计工具&#xff0c;无需使用不同…