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


在这里插入图片描述

一、场景构造

three.js基础

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

微信小游戏中使用three

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

import './js/libs/weapp-adapter'
import './js/libs/symbol'

import Main from './js/main'

new Main()

game.json:

{
    "deviceOrientation": "landscapeRight"
}

改为横屏游戏。

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

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

静止的场景

main.js

代码主体部分:

// 引入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():

  update() {
    // 转动大海和云
    this.sea.mesh.rotation.z += .005;
    this.sky.rotation.z += .01;

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

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

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

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

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

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() :

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() :

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()

  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()

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

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()中更新每帧的鼠标位置和轮盘

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

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() ,更新当前轮盘位置

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() ,更新当前飞机位置

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();
  }

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

// 引入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 翻译

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值