3D微信小游戏 轮盘控制模块 three.js

在这里插入图片描述

一、场景构造

three.js基础

如果是刚接触3D引擎,关于three.js的基础知识可以参考之前的博客Three.js+tween.js 基础(一) 中的基本概念篇部分。(场景 、相机 、渲染器 、添加对象)

微信小游戏中使用three

使用微信开发者工具创建新的小游戏模板,AppID选择测试号,得到的是飞机大战的源文件,在这个基础上进行修改。

在这里插入图片描述
game.js:

1
2
3
4
5
6
import './js/libs/weapp-adapter'
import './js/libs/symbol'

import Main from './js/main'

new Main()

game.json:

1
2
3
{
"deviceOrientation": "landscapeRight"
}

改为横屏游戏。

最后,清空images、audio和js文件夹。添加 libs 和main.js空文件到js文件夹。

最终得到:
在这里插入图片描述
代码部分的修改在main.js。

静止的场景

main.js

代码主体部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 引入three
import * as THREE from 'libs/three.js'

// 一些全局变量
var Colors = { ... }
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;

var Sea = function () { ... }
var Cloud = function () { ... }
var Sky = function () { ... }
var AirPlane = function () { ... }
var Pilot = function () { ... }

/**
* 游戏主函数
*/
export default class Main {
constructor() {
// 创建场景,相机和渲染器
this.createScene();
// 添加光源
this.createLights();
this.start()
}
createScene() { ... }
createLights() { ... }
createPlane() { ... }
createSea() { ... }
createSky() { ... }

start() {
// 添加对象
this.createPlane();
this.createSea();
this.createSky();

window.requestAnimationFrame(this.loop.bind(this), canvas);
}
update() {

}
loop() {
this.update()
this.renderer.render(this.scene, this.camera);
window.requestAnimationFrame(this.loop.bind(this), canvas);
}
}

在这里插入图片描述

动画渲染

main.js

修改update():

1
2
3
4
5
6
7
8
9
10
11
update() {
// 转动大海和云
this.sea.mesh.rotation.z += .005;
this.sky.rotation.z += .01;

// 更新每帧的飞机
this.updatePlane();

// 更新每帧的海浪
this.sea.moveWaves();
}

添加 updatePlane(),更新每帧的飞机

1
2
3
4
updatePlane() {
this.airplane.propeller.rotation.x += 0.3;
this.airplane.pilot.updateHairs();
}

为 Pilot 添加原型函数 updateHairs() ,让头发飘起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Pilot.prototype.updateHairs = function () {

// 获得头发
var hairs = this.hairsTop.children;

// 根据 angleHairs 的角度更新头发
var l = hairs.length;
for (var i = 0; i < l; i++) {
var h = hairs[i];
// 每根头发将周期性的基础上原始大小的75%至100%之间作调整。
h.scale.y = .75 + Math.cos(this.angleHairs + i / 3) * .25;
}
// 在下一帧增加角度
this.angleHairs += 0.16;
}

更新每帧的海浪,为 Sea 添加原型函数 moveWaves() :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Sea.prototype.moveWaves = function () {

// 获取顶点
var verts = this.mesh.geometry.vertices;
var l = verts.length;

for (var i = 0; i < l; i++) {
var v = verts[i];

// 获取关联的值
var vprops = this.waves[i];

// 更新顶点的位置
v.x = vprops.x + Math.cos(vprops.ang) * vprops.amp;
v.y = vprops.y + Math.sin(vprops.ang) * vprops.amp;

// 下一帧自增一个角度
vprops.ang += vprops.speed;
}

// 告诉渲染器代表大海的几何体发生改变
// 事实上,为了维持最好的性能
// Three.js 会缓存几何体和忽略一些修改
// 除非加上这句
this.mesh.geometry.verticesNeedUpdate = true;

this.mesh.rotation.z += .005;
}

在这里插入图片描述

二、轮盘控制

创建UI部分

main.js

轮盘控制,分数信息,技能按钮这些部分和游戏主体分开,都创建在UI部分,使用新的场景和正交相机,需要保证在各个机型位置相对不变。这里只添加轮盘控制,分数信息,技能按钮以后再写。。。。
修改 start() 、loop() :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
start() {
// 添加对象
this.createPlane();
this.createSea();
this.createSky();
this.createUI();

window.requestAnimationFrame(this.loop.bind(this), canvas);
}
loop() {
this.update()
this.renderer.render(this.scene, this.camera);
this.ui.render(this.renderer);
window.requestAnimationFrame(this.loop.bind(this), canvas);
}

添加 createUI()

1
2
3
4
5
6
7
8
9
10
11
createUI() {
// 利用新建一个场景,并创建一个正交相机
// 把UI元素放在这个场景里,渲染的时候同时渲染多个场景
this.ui = new UI();
// 创建轮盘
var controller = new Controller();
// console.log(controller);
controller.mesh.position.set(30 + controller.controllerRadius - WIDTH * 0.5, 20 + controller.controllerRadius - HEIGHT * 0.5, 0);
this.controller = controller;
this.ui.add(this.controller.mesh);
}

添加 UI 及其原型函数 add()、render()

1
2
3
4
5
6
7
8
9
10
11
12
var UI = function () {
this.scene = new THREE.Scene();
this.camera = new THREE.OrthographicCamera(WIDTH / -2, WIDTH / 2, HEIGHT / 2, HEIGHT / -2, 0, 10000);
this.camera.position.z = 10000;
}
UI.prototype.add = function (obj) {
this.scene.add(obj);
}
UI.prototype.render = function (renderer) {
renderer.clearDepth();
renderer.render(this.scene, this.camera);
}

添加轮盘 Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var Controller = function () {
this.mesh = new THREE.Object3D();
var controllerRadius = HEIGHT * 0.17 > 105 ? 105 : HEIGHT * 0.17;
this.controllerRadius = controllerRadius;

// 创建轮盘
var geometry = new THREE.CircleGeometry(controllerRadius, 32);
var material = new THREE.LineBasicMaterial({ color: 0xf7d9aa });
geometry.vertices.shift();
var circle = new THREE.LineLoop(geometry, material);
this.mesh.add(circle);

var geometry = new THREE.CircleGeometry(controllerRadius, 32);
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.1
});
var bgcircle = new THREE.Mesh(geometry, material);
this.mesh.add(bgcircle);

// 创建当前位置
var wheel = new THREE.Object3D();
wheel.name = "wheel";
var geometry = new THREE.CircleGeometry(controllerRadius * 0.33, 32);
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.1
});
var circle1 = new THREE.Mesh(geometry, material);
wheel.add(circle1);

var geometry = new THREE.CircleGeometry(controllerRadius * 0.33 - 2, 32);
var material = new THREE.MeshBasicMaterial({
color: 0x201D13,
transparent: true,
opacity: 0.1
});
var circle2 = new THREE.Mesh(geometry, material);
wheel.add(circle2);

this.mesh.add(wheel);

// 创建指向标
}

在这里插入图片描述

轮盘控制

main.js

start()中添加事件监听,update()中更新每帧的鼠标位置和轮盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
start() {
// 添加对象
this.createPlane();
this.createSea();
this.createSky();
this.createUI();

this.mousePos = { x: 0, y: 0 };
// 初始化事件监听
this.touchEvent();

window.requestAnimationFrame(this.loop.bind(this), canvas);
}
update() {
// 转动大海和云
this.sea.mesh.rotation.z += .005;
this.sky.rotation.z += .01;

// 更新每帧的飞机
this.updatePlane();

// 更新每帧的海浪
this.sea.moveWaves();

// 更新每帧的鼠标位置
this.updatePosition();
}

添加 touchEvent() 事件监听,实时更新 mousePos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
touchEvent() {
canvas.addEventListener('touchstart', ((e) => {
e.preventDefault()

var radius = this.controller.controllerRadius;

var tx = (e.touches[0].clientX - 30 - radius) / radius;
var ty = (HEIGHT -e.touches[0].clientY - 20 - radius) / radius;

if (this.checkIsFingerOnController(tx, ty)) {
this.touched = true;
this.controller.mesh.children[2].children[1].material.opacity = 0.3;
this.mousePos = { x: tx, y: ty };
}

}).bind(this))

canvas.addEventListener('touchmove', ((e) => {
e.preventDefault()
if (this.touched) {
var radius = this.controller.controllerRadius;

var tx = (e.touches[0].clientX - 30 - radius) / radius;
var ty = (HEIGHT-e.touches[0].clientY - 20 - radius) / radius;

if (this.checkIsFingerOnController(tx, ty)){
this.mousePos = { x: tx, y: ty };
}
else{
let k = ty/tx;
if (tx > 0 && ty > 0) {tx = 1 / Math.sqrt(k * k + 1);ty = k * tx;}
else if (tx < 0 && ty > 0) { tx = -1 / Math.sqrt(k * k + 1); ty = k * tx; }
else if (tx > 0 && ty < 0) { tx = 1 / Math.sqrt(k * k + 1); ty = k * tx; }
else { tx = -1 / Math.sqrt(k * k + 1); ty = k * tx; }
this.mousePos = { x: tx, y: ty };
}
}
}).bind(this))

canvas.addEventListener('touchend', ((e) => {
e.preventDefault()
this.touched = false;
this.controller.mesh.children[2].children[1].material.opacity = 0.1;
this.mousePos = { x: 0, y: 0 };
}).bind(this))
}

checkIsFingerOnController(x, y) {
return x*x+y*y<1;
}

添加 updatePosition() ,更新当前轮盘位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
updatePosition() {
var radius = this.controller.controllerRadius;
// 在x轴上-radius至radius之间移动点标
// 根据鼠标的位置在-1与1之间的范围,使用 normalize 函数实现(如下)
var targetX = this.normalize(this.mousePos.x, -1, 1, (-1 + 0.33) * radius, (1 - 0.33)*radius);
var targetY = this.normalize(this.mousePos.y, -1, 1, (-1 + 0.33) * radius, (1 - 0.33)*radius);

// 在每帧通过添加剩余距离的一小部分的值移动点标
this.controller.mesh.children[2].position.x += (targetX - this.controller.mesh.children[2].position.x) * 0.25;
this.controller.mesh.children[2].position.y += (targetY - this.controller.mesh.children[2].position.y) * 0.25;
}
normalize(v, vmin, vmax, tmin, tmax) {
var nv = Math.max(Math.min(v, vmax), vmin);
var dv = vmax - vmin;
var pc = (nv - vmin) / dv;
var dt = tmax - tmin;
var tv = tmin + (pc * dt);
return tv;
}

修改 updatePlane() ,更新当前飞机位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
updatePlane() {
// 在x轴上-140至140之间和y轴25至175之间移动飞机
// 根据鼠标的位置在-1与1之间的范围,使用 normalize 函数实现(如下)

var targetX = this.normalize(this.mousePos.x, -1, 1, -140, 140);
var targetY = this.normalize(this.mousePos.y, -1, 1, 25, 175);

// 更新飞机的位置
//this.airplane.mesh.position.x = targetX;

// 在每帧通过添加剩余距离的一小部分的值移动飞机
this.airplane.mesh.position.y += (targetY - this.airplane.mesh.position.y) * 0.1;
this.airplane.mesh.position.x += (targetX - this.airplane.mesh.position.x) * 0.05;

// 剩余的距离按比例转动飞机
this.airplane.mesh.rotation.z = (targetY - this.airplane.mesh.position.y) * 0.0128;
this.airplane.mesh.rotation.x = (this.airplane.mesh.position.y - targetY) * 0.0064;

this.airplane.propeller.rotation.x += 0.3;
this.airplane.pilot.updateHairs();
}

在这里插入图片描述
代码主体部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// 引入three
import * as THREE from 'libs/three.js'

// 一些全局变量
var Colors = { ... }
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;

var Sea = function () { ... }
Sea.prototype.moveWaves = function () { ... }
var Cloud = function () { ... }
var Sky = function () { ... }
var AirPlane = function () { ... }
var Pilot = function () { ... }
Pilot.prototype.updateHairs = function () { ... }
var UI = function () { ... }
UI.prototype.add = function (obj) { ... }
UI.prototype.render = function (renderer) { ... }
var Controller = function () { ... }
/**
* 游戏主函数
*/
export default class Main {
constructor() {
// 创建场景,相机和渲染器
this.createScene();
// 添加光源
this.createLights();
this.start()
}
createScene() { ... }
createLights() { ... }
createPlane() { ... }
createSea() { ... }
createSky() { ... }

createUI() { ... }
updatePlane() { ... }
touchEvent() { ... }
checkIsFingerOnController(x, y) { ... }
updatePosition() { ... }
normalize(v, vmin, vmax, tmin, tmax) { ... }

start() {
// 添加对象
this.createPlane();
this.createSea();
this.createSky();

this.createUI();

this.mousePos = { x: 0, y: 0 };
// 初始化事件监听
this.touchEvent();

window.requestAnimationFrame(this.loop.bind(this), canvas);
}
update() {
// 转动大海和云
this.sea.mesh.rotation.z += .005;
this.sky.rotation.z += .01;

// 更新每帧的飞机
this.updatePlane();

// 更新每帧的海浪
this.sea.moveWaves();

// 更新每帧的鼠标位置
this.updatePosition();
}
loop() {
this.update()
this.renderer.render(this.scene, this.camera);
window.requestAnimationFrame(this.loop.bind(this), canvas);
}
}

相关链接:
Github项目地址
three.js官方文档
The Making of “The Aviator”: Animating a Basic 3D Scene with Three.js 翻译

0%