制作3D小汽车游戏(下)
书接上回,这一节我们分模块说一说怎么写一个这样的游戏
1. 初始化场景、相机和渲染器
这几乎是绘制three必须做的事情,我们有两套场景和相机,一个是主场景和相机,另一个是小地图的场景和相机(用来俯视建筑和小汽车),渲染器设置一级曝光,输出编码设置为sRGBEncoding,代码如下。
scene = new THREE.Scene();
scene.background = new THREE.Color(0x8FBCD4);
scene.fog = new THREE.Fog(0x8FBCD4, 3000, 4000); camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.1, 10000);
camera.position.set(10,10,10); scene2 = new THREE.Scene();
scene2.background = new THREE.Color(0xffffff); camera2 = new THREE.OrthographicCamera(-400, 400, 400, -400, 1, 1000);
camera2.position.set(0, 1000, 0);
camera2.lookAt(0,0,0); renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
this.$refs.box.appendChild(renderer.domElement);
2. 设置地面和建筑
地面很简单,就是一个plane
initGround() {
const ground_geom = new THREE.PlaneBufferGeometry(8000, 8000);
const ground_mate = new THREE.MeshLambertMaterial({color: 0xBCD48F, side: THREE.DoubleSide});
const ground_mesh = new THREE.Mesh(ground_geom, ground_mate);
ground_mesh.rotation.x = - Math.PI / 2;
scene.add(ground_mesh);
},
设置建筑,我们需要给每一个建筑设置长宽高、颜色、位置,并把它们放到一个组里,然后然需要给每一个建筑初始化一个OBB,并把这些OBB信息添加到一个数组中,便于我们日后做碰撞检测
initBuild(num) {
let color = new THREE.Color();
let build = new THREE.Group();
for(let i=0; i<num; i++) {
let w = Math.random() * 50 + 50;
let h = Math.random() * 100 + 100;
let d = Math.random() * 50 + 50;
let x = Math.random() * 8000 - 4000;
let z = Math.random() * 8000 - 4000;
if((x * x + z * z) < Math.pow(140, 2)) {
//40为车半长的估计值
x = Math.pow(140, 2) / x;
z = Math.pow(140, 2) / z;
}
let geometry = new THREE.BoxBufferGeometry(w, h, d);
let material = new THREE.MeshStandardMaterial({color: new THREE.Color().setHSL(Math.random(), 1.0, 0.6)});
let mesh = new THREE.Mesh(geometry, material);
mesh.position.set(x, h / 2, z);
build.add(mesh);
let obb = new OBB();
buildObbArray.push(obb.set(new THREE.Vector3(x, h / 2, z), new THREE.Vector3(w/2, h/2, d/2), new THREE.Matrix3()));
}
scene.add(build);
scene2.add(build.clone());
},
3. 初始化小汽车
这里我们要下载好一个小汽车的模型,首先把模型设置成我们想要的大小,这里车高设置成10,其他维度等比例改变,然后找到方向盘,轮子等部分,添加到全局的组中,便于我们控制。
initCar() {
const shadowTexture = new THREE.TextureLoader().load('/static/gltf/super_car/super_car_ao.png');
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/static/gltf/');
loader.setDRACOLoader(dracoLoader);
loader.load('/static/gltf/super_car/super_car.glb', gltf => {
const model = gltf.scene.children[0];
model.rotation.y = -Math.PI / 2;
steering_wheel = model.getObjectByName('steering_wheel');[]
const shadow = new THREE.Mesh(
new THREE.PlaneBufferGeometry( 0.655 * 4, 1.3 * 4 ),
new THREE.MeshBasicMaterial( {
map: shadowTexture, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true
} )
);
shadow.position.y = 0.1;
shadow.rotation.x = - Math.PI / 2;
model.add(shadow);
const size = new THREE.Box3().setFromObject(model).getSize(new THREE.Vector3());
model.scale.copy(new THREE.Vector3().addScalar(carHeight / size.y));
carHalfSize = new THREE.Box3().setFromObject(model).getSize(new THREE.Vector3()).multiplyScalar(0.4);
car.add(model);
tyreArray.push(car.getObjectByName('wheel_fl'),car.getObjectByName('wheel_fr'),car.getObjectByName('wheel_rl'),car.getObjectByName('wheel_rr'));
car.userData.obb = new OBB(new THREE.Vector3(0,5,0), carHalfSize, new THREE.Matrix3());
scene.add(car);
orthoCar = new THREE.Mesh(new THREE.SphereBufferGeometry(20, 20), new THREE.MeshBasicMaterial({color: 0xff0000, side: THREE.DoubleSide}));
orthoCar.rotation.x = - Math.PI / 2;
scene2.add(orthoCar);
} );
},
4. 添加事件、转弯、增减速和切换视角
这里我们主要使用q–切换视角,a,d–转弯,w,s–加减速。
document.addEventListener('keypress', event => {
if(event.key == 'd') {
this.turn(0);
} else if(event.key == 'a') {
this.turn(1);
} else if(event.key == 'w') {
this.speed(1);
} else if(event.key == 's') {
this.speed(0)
} else if(event.key == 'q') {
view = view == 0 ? 1 : 0;
}
})
对于速度的控制,sp代表左右方向
speed(sp) {
if(sp == 0 && speed > 0) {
speed -= 2;
} else if(sp == 0 && speed > -10) {
speed -= 0.5;
} else if(sp == 1 && speed < 40) {
speed += 0.5;
}
},
对于转弯的控制,我们用多段控制模拟非线性
turn(direct) {
//模拟非线性转向
if(direct == 0 && rotateTyre > -rotateMax * 0.5) {
rotateTyre -= 0.02;
} else if (direct == 0 && rotateTyre > -rotateMax * 0.8) {
rotateTyre -= 0.04;
} else if (direct == 0 && rotateTyre > -rotateMax) {
rotateTyre -= 0.06;
} else if(direct == 1 && rotateTyre < rotateMax * 0.5) {
rotateTyre += 0.02;
} else if(direct == 1 && rotateTyre < rotateMax * 0.8) {
rotateTyre += 0.04;
} else if(direct == 1 && rotateTyre < rotateMax) {
rotateTyre += 0.06;
}
tyreArray[0].rotation.y = rotateTyre;
tyreArray[1].rotation.y = rotateTyre;
//方向盘
steering_wheel.rotation.y = - rotateTyre;
},
5. 渲染
因为我们有两个场景要渲染,这里就选择渲染两次
render() {
stats.update();
this.run();
renderer.setScissor( 0, 0, window.innerWidth, window.innerWidth );
renderer.setViewport( 0, 0, window.innerWidth, window.innerHeight );
renderer.setScissorTest(true);
renderer.render( scene, camera );
renderer.setScissor( 0, 0, window.innerHeight/4, window.innerHeight/4 );
renderer.setViewport( 0, 0, window.innerHeight/4, window.innerHeight/4);
renderer.setScissorTest(true);
renderer.render( scene2, camera2 );
this.globalID = requestAnimationFrame(this.render);
}
run方法里面控制着车的角度,车子的位置,轮子的传动,相机的位置,相机的lookAt,以及碰撞检测,这里面有我们上一节复习的有向包围盒OBB和欧拉角的使用
run() {
let delta = - clock.getDelta();
//轮胎转动∝速度
tyreArray.forEach(d => d.rotation.copy(new THREE.Euler(delta * speed + d.rotation.x, d.rotation.y, d.rotation.z, 'ZYX')));
//rotateOffset 旋转偏移量 rotateTyre轮胎偏转 rotateCorrection偏转系数 speed车速
let rotateOffset = Math.sin(rotateTyre) * rotateCorrection * speed;
//rotateRun 旋转偏移总量
rotateRun += rotateOffset;
//rotateVector 车前进方向向量(不断乘offset得到)
rotateVector.applyAxisAngle(new THREE.Vector3(0,1,0), rotateOffset);
//车x和z方向增加量 ∝车速
car.position.x += speed * speedCorrection * rotateVector.x;
car.position.z += speed * speedCorrection * rotateVector.z;
camera2.position.set(car.position.x, 1000, car.position.z);
camera2.lookAt(car.position.x, 10, car.position.z);
orthoCar.position.copy(car.position);
//车身旋转 使用 旋转偏移总量rotateRun
car.rotation.y = rotateRun;
//切换视角
if(view == 0) {
camera.position.set(car.position.x - 3 * Math.sin(rotateRun), 8, car.position.z - 3 * Math.cos(rotateRun));
camera.lookAt(camera.position.x + Math.cos(rotateRun), 8, camera.position.z - Math.sin(rotateRun));
} else {
camera.position.set(car.position.x + 50 * Math.cos(rotateRun + Math.PI * 0.9), 20, car.position.z - 50 * Math.sin(rotateRun + Math.PI * 0.9));
camera.lookAt(camera.position.x + Math.cos(rotateRun), 19.9, camera.position.z - Math.sin(rotateRun));
}
//判断是否碰撞
car.userData.obb.set(car.position, carHalfSize, new THREE.Matrix3().setFromMatrix4(car.matrixWorld));
const obb = car.userData.obb;
for(let i=0; i<buildObbArray.length; i++) {
const obbTest = buildObbArray[i];
if(obb.intersectsOBB(obbTest) === true) {
speed = 0;
}
}
},
这里我们直接遍历建筑的OBB数组然后通过intersectsOBB方法,判断是否相撞就可以了。
转载请注明地址:郭先生的博客
制作3D小汽车游戏(下)的更多相关文章
- 制作3D小汽车游戏(上)
之前一段时间家里和公司的事太多,一直没有时间写博客,最近腾出一段时间,看了一遍官方的examples,收货颇多,想整理一点东西出来,又苦于没有好的东西,three写点东西真是太难了.好吧,今天郭先生就 ...
- 张瀚荣:如何用UE4制作3D动作游戏
转自:http://www.gamelook.com.cn/2015/06/218267 GameLook报道/ 6月5日,2015年第三期GameLook开放日‧虚幻引擎专场活动在上海正式举行,此次 ...
- 基于html5制作3D拳击游戏源码下载
今天给大家分享一款基于HTML5实现的3d拳王游戏源码.这款实例适用浏览器:360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界之窗. 不支持IE8及以下浏览器. 在线预览 ...
- Unity3D实现3D立体游戏原理及过程
Unity3D实现3D立体游戏原理及过程 183 0 0 下面的教程是我今天整理的资料,教大家一步步完成自己的3D立体游戏,并向大家介绍一些3D成像的原理. 理论上,每个普通的非立体3 ...
- Unity3D制作3D虚拟漫游场景(二)
传送门: Unity3D制作3D虚拟漫游场景(一) -------------------------------------------------------------------------- ...
- 用Phaser来制作一个html5游戏——flappy bird (二)
在上一篇教程中我们完成了boot.preload.menu这三个state的制作,下面我们就要进入本游戏最核心的一个state的制作了.play这个state的代码比较多,我不会一一进行说明,只会把一 ...
- CSS3特效----制作3D旋转导航
大致思路:首先给 three-d-box 设置一个 transition是不可少的 然后每个 a 标签里面有两个 span 一个叫 font 一个叫 back,默认状态下 font 旋转0度,也就是没 ...
- 用webgl打造自己的3D迷宫游戏
用webgl打造自己的3D迷宫游戏 2016/09/19 · JavaScript · WebGL 原文出处: AlloyTeam 背景:前段时间自己居然迷路了,有感而发就想到写一个可以让人迷路 ...
- Love2D游戏引擎制作贪吃蛇游戏
代码地址如下:http://www.demodashi.com/demo/15051.html Love2D游戏引擎制作贪吃蛇游戏 内附有linux下的makefile,windows下的生成方法请查 ...
随机推荐
- 机场&代理商-关系图
机场&代理商-关系图 思路 ①首先统计机场活跃度Top10的机场名称,以下是我的表结构,以及查询语句 表结构: 查询语句:SELECT * from 2020csale ORDER BY cn ...
- spring boot 访问外部http请求
以前 访问外部请求都要经过 要用 httpClient 需要专门写一个方法 来发送http请求 这个这里就不说了 网上一搜全都是现成的方法 springboot 实现外部http请求 是通过F ...
- 【2014广州市选day1】JZOJ2020年9月12日提高B组T3 消除游戏
[2014广州市选day1]JZOJ2020年9月12日提高B组T3 消除游戏 题目 Description 相信大家玩过很多网络上的消除类型的游戏,一般来说就是在一个大拼图内找出相同的部分进行最大程 ...
- Centos7安装Kubernetes k8s v1.16.0 国内环境
一. 为什么是k8s v1.16.0? 最新版的v1.16.2试过了,一直无法安装完成,安装到kubeadm init那一步执行后,报了很多错,如:node xxx not found等.centos ...
- 第11.23节 Python 中re模块的搜索替换功能:sub及subn函数
一. 引言 在<第11.3节 Python正则表达式搜索支持函数search.match.fullmatch.findall.finditer>重点介绍了几个搜索函数,除了搜索,re模块也 ...
- PyQt(Python+Qt)学习随笔:QListView的wordWrap属性
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QListView的wordWrap属性与QTableView的wordWrap属性功能完全相同,用 ...
- PyQt学习随笔:截获窗口Widget组件的关闭事件
在PyQt中,QWidget类对应基础的窗口组件,如果要在窗口组件关闭时截获关闭事件,提供自己的控制机制,则可以通过在自定义的派生类中重写closeEvent方法. 重写closeEvent方法的语法 ...
- 性能测试学习之路 (一)认识jmeter(性能测试流程 && 性能测试通过标准 &&jmeter体系结构)
性能测试是通过自动化的测试工具模拟多种正常.峰值以及异常负载条件来对系统的各项性能指标进行测试. 1 性能测试技能树 性能测试是一项综合性的工作,致力于暴露性能问题,评估系统性能趋势.性能测试工作实质 ...
- 赶紧收藏!王者级别的Java多线程技术笔记,我java小菜鸡愿奉你为地表最强!
Java多线程技术概述 介绍多线程之前要介绍线程,介绍线程则离不开进程. 首先 , 进程 :是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元: 线程:就 ...
- go学习第四天
昨天通宵加班,暂停了一天学习,今天再偷懒下,学习半个小时