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.4
  coalSeed = this.seed * 0.5
  treeSeed = this.seed * 0.7
  leafSeed = this.seed * 0.8
  
  get = (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 worker
  generate = () => {
   
    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.mfbz.cn/a/704937.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Web基础和HTTP协议

1、Web基础 &#xff08;1&#xff09;域名概述 域名空间结构 域名注册 2、网页 &#xff08;1&#xff09;网页概述 网页 纯文本格式文件 编写语言为HTML 在用户的浏览器中被“翻译”成网页形式显示出来 网站 由一个一个页面构成的&#xff0c;是多个网页的结合体 主页…

ChatGPT-4o引领医学革命:临床科研创新与效率的新纪元

2024年5月12日&#xff0c;更强版本的ChatGPT-4o上线&#xff0c;文本、语音、图像等多模态交互方式使其在各行各业的应用呈现了更多的可能性。因此&#xff0c;帮助广大临床医学相关的医院管理人员、医生、学生、科研人员更加熟练地掌握ChatGPT-4o在临床医学日常生活、工作与学…

Tabby:一款革新的Mac/Win现代化终端模拟器

在信息技术日新月异的今天&#xff0c;终端操作已成为众多开发者、系统管理员和技术爱好者的日常必备工具。然而&#xff0c;传统的终端模拟器往往功能单一、界面陈旧&#xff0c;无法满足用户对于高效、便捷操作体验的追求。Tabby应运而生&#xff0c;作为一款现代化、功能强大…

WebGL渲染引擎优化方向 -- 内存管理的优化

作者&#xff1a;caven chen 对此系列感兴趣还可以看前文&#xff1a; WebGL渲染引擎优化方向 -- 加载性能优化 WebGL渲染引擎优化方向——渲染帧率的优化 前言 WebGL 是一种强大的图形渲染技术&#xff0c;可以在浏览器中快速渲染复杂的 3D 场景。但是&#xff0c;由于 W…

先导小型五轴联动数控加工中心

先导小型五轴联动加工中心可以作为学校或培训机构的教学工具&#xff0c;帮助学生了解数控加工的基本原理和操作方法。它特别适用于机械、自动化、工业设计等相关专业的学生进行实践操作和课程项目。 小型五轴联动加工中心是一种能够同时控制五个自由度进行联动的加工设备。这五…

VBA实现关闭Excel自动计算,关闭屏幕刷新

Excel代码提速神器 涉及到提取表格大量数据操作&#xff0c;复制粘贴多个单元格时&#xff0c;尽量避免一个个单元格提取&#xff0c;或者一行行一列列提取数值&#xff0c;设计大量IO流操作非常浪费时间。尽量找出数据之间的规律&#xff0c;批量选中复制粘贴&#xff0c;找到…

字符集相关变量理解

建表 创建一个新表&#xff0c;想让他的字符集是 gbk&#xff0c;怎么弄? 尝试1&#xff1a; 失败&#xff01;原因&#xff1a; set names gbk; 等价于&#xff1a;set character_set_client gbk; set character_set_connection gbk; set character_set_results gbk;尝…

LSS 和 BEVDepth算法解读

前言 当前BEV的研究大都基于深度学习的方法&#xff0c;从组织BEV特征信息的方式来看&#xff0c;主流方法分属两类&#xff1a;自底向上方法和自顶向下方法。 自底向上方法比较早的代表工作是LSS&#xff0c;后来BEVDet、BEVDepth等也是基于LSS的框架来进行优化。自底向上方…

redis 一些笔记1

redis 一、redis事务二、管道2.1 事务与管道的区别 三、主从复制3.13.2 权限细节3.3 基本操作命令3.4 常用3.4.1 一主几从3.4.2 薪火相传3.4.3 反客为主 3.5 步骤3.6 缺点 一、redis事务 放在一个队列里&#xff0c;依次执行&#xff0c;并不保证一致性。与mysql事务不同。 命…

滚雪球学Java(82):快速上手Java线程通信:零基础学习指南

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴 bug菌&#xff0c;今天又来给大家手把手教学Java SE系列知识点啦&#xff0c;赶紧出来哇&#xff0c;别躲起来啊&#xff0c;听我讲干货记得点点赞&#xff0c;赞多了我就更有动力讲得更欢哦&#xff01;所以呀&…

泰迪智能科技携手广西科技大学理学院共建“上进双创工作室”

6月12日&#xff0c;广东泰迪智能科技股份有限公司携手广西科技大学理学院在泰迪智能科技产教融合实训中心举行“上进双创工作室”签约揭牌仪式&#xff0c;标志“泰迪科技广西科大上进双创工作室”的正式启动。 仪式由泰迪智能科技运营中心总监翁梦婷主持。广西科技大学理学院…

组件二次封装,通过属性事件透传,插槽使用,组件实例方法的绑定,深入理解 Vue.js 组件扩展与插槽

透传&#xff0c;插槽&#xff0c;组件实例方法的绑定&#xff0c;深入理解 Vue.js 组件扩展与插槽 前言 Vue.js 提供了强大的组件化系统&#xff0c;允许开发者构建可复用、可组合的UI组件。在实际项目中&#xff0c;直接使用第三方库提供的基础组件&#xff08;如Element UI…

如何筑牢防线,抵御.anony勒索病毒攻击?

引言 近年来&#xff0c;随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显。其中&#xff0c;勒索病毒作为一种新型的网络威胁&#xff0c;给企业和个人用户带来了极大的困扰。在众多勒索病毒中&#xff0c;.anony勒索病毒以其独特的加密方式和勒索手段&#xff0c;引…

2024/6/11 英语每日一段

They found that, regardless of culture, greater mental well-being is linked with feeling emotions that we believe are appropriate to our situation, rather than just having positive emotions regardless of context--“feeling right” as opposed to “feeling g…

深度学习500问——Chapter10:迁移学习(3)

文章目录 11.3 迁移学习的常用方法 11.3.1 数据分布自适应 11.3.2 边缘分布自适应 11.3.3 条件分布自适应 11.3.4 联合分布自适应 11.3.5 概率分布自适应方法优劣性比较 11.3.6 特征选择 11.3.7 统计特征对齐方法 11.3 迁移学习的常用方法 11.3.1 数据分布自适应 数据分布自适…

【配置教程】Linux在企业端为何如此重要

目录 本节重点 先见一下什么是Linux 后台vs前台 企业为何选择使用Linux作为后台服务器 国内企业后台和用户使用Linux现状 1. IT服务器Linux系统应用领域 2. 嵌入式Linux系统应用领域 3. 个人桌面应用领域 Linux时代发展 版本更新 ​编辑 就个人找工作/能力提升来说…

sklearn深度学习指南:掌握机器学习的利器

sklearn深度学习指南&#xff1a;掌握机器学习的利器&#xff01; 1. 简介1.1 什么是sklearn&#xff1f;1.2 sklearn的优势和应用领域1.3 为什么要学习和使用sklearn&#xff1f; 2. 安装和环境设置2.1 如何安装sklearn&#xff1f;安装Anaconda&#xff08;Windows/macOS/Lin…

利用泽攸科技原位TEM技术揭示真空击穿过程中电场与电极材料相互作用

在高能物理设备和许多其他设备中&#xff0c;真空击穿&#xff08;VBD&#xff09;现象对高能物理设备的性能造成了严重的阻碍&#xff0c;包括真空断路器、X射线源、聚变反应堆以及粒子加速器等。然而由于对导致VBD的机制缺乏足够的科学理解&#xff0c;这些问题至今无法得到缓…

618哪些数码产品比较好?2024超高人气产品推荐!

随着6.18大促的脚步渐近&#xff0c;你是否已经按捺不住内心的激动&#xff0c;想要在网络购物的海洋中畅游&#xff0c;尽情享受购物的狂欢&#xff1f;然而&#xff0c;面对繁多的商品和各式各样的优惠活动&#xff0c;你是否感到了一丝迷茫&#xff1f;作为一位经验丰富的网…

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

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