threejs做个空中飞车

小游戏灵感来自于抖音的奶龙飞车4小游戏,感兴趣的可以搜索查看下 ~

这里尝试基于 threejs 实现一个空中飞车小游戏,可以通过键盘方向键控制小车的行驶和转向,经过前方多段不规则的路后到达终点。先看下需要实现哪些效果:

  • 首先整体场景是在半空中悬浮的,需要做个天空背景
  • 然后要实现几段悬浮的倾斜度不一样的道路
  • 第二段道路中间设定一些道路是左右移动的,稍微增加点游戏难度
  • 玩家可以根据方向键控制小车方向
  • 小车和道路的碰撞处理 。这部分逻辑沿用前文的小车碰撞处理逻辑,继续用cannon-es 这个库,以下代码也是基于前文的代码,所以物理库的相关逻辑请参照前文或代码注释,这篇文章不会赘述

还是拿之前自己实现的小车来当主角吧 ~

天空背景

其实做个天空盒子就可以了,盒子就是一个比较大的正方体 BoxGeometry,给六面材质都加上贴图来模拟天空,然后将场景包裹在里面。这里简单点就用一张浅蓝色背景的图,如果还想做的更好点,那就得准备6张不同的图,图里加一些云彩和渐变色

js 复制代码
 // 创建天空盒子
const skyGeometry = new THREE.BoxGeometry(1000, 1000, 1000);
const materialArray = [];
for (let i = 0; i < 6; i++)
  materialArray.push(
    new THREE.MeshBasicMaterial({
      // 加载贴图
      map: textureLoader.load("./gta/sky.jpg"),
      // 只渲染背面就行
      side: THREE.BackSide,
    })
  );
const skyBox = new THREE.Mesh(skyGeometry, materialArray);
scene.add(skyBox);

制作道路

一段道路其实就是一个平面多边形,可以用 PlaneGeometry 实现,实现代码参考:

js 复制代码
 // 加载道路的纹理贴图
function texturePromise() {
  return new Promise((resolve, reject) => {
    textureLoader.load("/gta/floor.jpg", (texture) => {
      resolve(texture);
    });
  });
}
// 第一段道路
const planeGeometry = new THREE.PlaneGeometry(10, 50);
// 设置道路材质
const texture: any = await texturePromise();
const planeMaterial = new THREE.MeshLambertMaterial({
  map: texture,
  // 渲染两面,因为道路是悬浮的,位置有高低,相机可能会观察到俩面
  side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
// 接收阴影
plane.receiveShadow = true;
// 设置倾斜度
plane.rotation.x = Math.PI / 2;
// 增加到3d场景里
scene.add(plane);

然后咱们参考上述的代码多实现几段道路,分别赋予不同的位置(position)、长宽和倾斜度(rotation)

接下来就是创建每段道路的刚体,下面以一段道路为例:

js 复制代码
import * as CANNON from "cannon-es";
// ...
// 第一段刚体
const q = plane.quaternion;
// 类比3d场景的Mesh
const roadShape = new CANNON.Box(new CANNON.Vec3(5, 25, 0.01));
// 类比3d场景的Geometry
const roadBody = new CANNON.Body({ mass: 0 });
roadBody.addShape(roadShape);
// 刚体位置和3d对象对齐
roadBody.position.set(0, 0, 0);
// 倾斜度和3d对象对齐
roadBody.quaternion = new CANNON.Quaternion(q._x, q._y, q._z, q._w);
// 添加到物理世界里
this.world.addBody(roadBody);

其他道路刚体的实现类似。ok,接下来调整下相机参数,使其在运动的时候跟随自车:

js 复制代码
const cameraOffsetY = 4;
const cameraOffsetZ = 16;
// ...
/***** 创建一个具有透视效果的摄像机 *****/
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 800);
const animate = () => {
  // ...
  // 相机跟随自车
  camera.position.y = egoCar.position.y + cameraOffsetY;
  camera.position.z = egoCar.position.z + cameraOffsetZ;
  camera.lookAt(egoCar.position);
  renderer.render(scene, camera);
  // ...
};

然后第二段道路再加点倾斜度,这样两段路的效果如下图:

然后第二段道路设计成有几段小道路组成,穿插有左右移动的动画,怎么做这个动画呢?可以借助 tween.js 做个平滑动画。tween.js 其实是一个补间动画库,提供了丰富的缓动函数,可以确保在两个值之间平滑地过渡,经常配合 threejs 来实现一些动画效果,比如物体平移和旋转、镜头推进等效果

bash 复制代码
npm install @tweenjs/tween.js

第二段道路再分出几段小路做左右平移的动画,需要设置对应的动画参数,其中一段道路的代码如下:

js 复制代码
import * as TWEEN from "@tweenjs/tween.js";
// ...
const tweenRoad1Start = new TWEEN.Tween(plane21.position)
  // 在1s内向x为-2的位置移动
  .to({ x: -2 }, 1000)
  // 延迟动画
  .delay(500)
  // 重复动画的次数
  .repeat(Infinity)
  // 也就是在初始位置和目标位置来回运动
  .yoyo(true)
  // 更新回调
  .onUpdate((data) => {
    // 更新刚体数据
    roadBody21.position.x = data.x;
  })
  // 启动动画
  .start();
  
// ...循环里更新动画
const animate = () => {
  // ...
  updatePhysics();
  // 更新tween动画
  TWEEN.update();
  // ...
};

其他几段道路做动画的逻辑类似,最终效果如图:

ok,到这里就可以自己开一段路试试了。掉下去或者想重新开始,自己刷新下界面,懒得做边界逻辑...forgive me。具体的自车控制逻辑,可以参考前文或者源码 ~

  • 体验地址(仅支持pc端,通过方向键控制行驶和转向)

最后

当然,这一版实现还是很简单的,完全可以加些更复杂的场景元素或者障碍物,实现飞车2.0?

相关推荐
cs_dn_Jie8 分钟前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic42 分钟前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿1 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具1 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
清灵xmf2 小时前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据2 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
qq_390161772 小时前
防抖函数--应用场景及示例
前端·javascript
334554323 小时前
element动态表头合并表格
开发语言·javascript·ecmascript
John.liu_Test3 小时前
js下载excel示例demo
前端·javascript·excel
Yaml43 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理