上一篇已经对绕非定轴转动有所了解,这篇郭先生继续说一说逻辑转体游戏的制作,这部分我们同样会遇到一些小问题,首先是根据数据渲染陷阱和目标区域,然后是对可以转动的判定,最后是获胜的判定。

1. 根据数据渲染陷阱和目标区域

首先我们P一张底图和陷阱图,如下图

就像这样,然后就是根据数据渲染陷阱和目标区域了,首先陷阱的个数是固定的,而目标区域是随小方块的数量而定,先看数据

end: [[-1, -4], [-1, -5]],
trap: [[-1, -7], [-6, -2]],

这里我们看一下Shader怎么写的

let texture1 = new THREE.TextureLoader().load('/static/images/base/luojizhuanti.png');
let texture2 = new THREE.TextureLoader().load('/static/images/base/stack.png');
let trapArray = [];
let targetArray = new Array(7).fill('').map(() => new THREE.Vector2(0,0));
square[this.game].trap.forEach(d => {
trapArray.push(new THREE.Vector2(d[0], d[1]));
})
square[this.game].end.forEach((d,i) => {
targetArray[i] = new THREE.Vector2(d[0], d[1]);
})
uniforms = {
texture1: {
value: texture1
},
texture2: {
value: texture2
},
point0: {
value: trapArray[0]
},
point1: {
value: trapArray[1]
},
target: {
value: targetArray
}
}
uniforms[ "texture2" ].value.wrapS = uniforms[ "texture2" ].value.wrapT = THREE.RepeatWrapping;
let planeMate = new THREE.ShaderMaterial({
side: THREE.DoubleSide,
uniforms: uniforms,
vertexShader: `
varying vec2 vUv;
varying vec3 pos;
void main() {
vUv = uv;
pos = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`,
fragmentShader: `
varying vec2 vUv;
varying vec3 pos;
uniform vec2 point0;
uniform vec2 point1;
uniform vec2 target[7];
uniform sampler2D texture1;
uniform sampler2D texture2;
void main() {
int index = 0;
vec2 newUv = vec2(vUv.x * 7.0, vUv.y * 8.0);
vec4 tcolor1 = texture2D( texture1, vUv );
vec4 tcolor2 = texture2D( texture2, newUv );
vec4 resultColor = tcolor1;
if (pos.x < point0.x * 10.0 + 45.0 && pos.x > point0.x * 10.0 + 35.0 && pos.y < - point0.y * 10.0 - 40.0 && pos.y > - point0.y * 10.0 - 50.0) {
resultColor = tcolor2;
} else if(pos.x < point1.x * 10.0 + 45.0 && pos.x > point1.x * 10.0 + 35.0 && pos.y < - point1.y * 10.0 - 40.0 && pos.y > - point1.y * 10.0 - 50.0) {
resultColor = tcolor2;
} else {
for(int i=0; i < 7; i++) {
if (pos.x < target[i].x * 10.0 + 45.0 && pos.x > target[i].x * 10.0 + 35.0 && pos.y < - target[i].y * 10.0 - 40.0 && pos.y > - target[i].y * 10.0 - 50.0) {
resultColor = vec4(1.0, 0.5, 0.0, 1.0);
}
}
}
gl_FragColor = resultColor;
}
`
})

texture1和texture2是两个纹理图,trapArray是盛放陷阱的数组,targetArray是目标区域,默认长度是7,且默认值都是new THREE.Vector2(0,0),然后我们将二维向量加到以上两个数组中,最后添加到uniforms中,最后传到ShaderMaterial中,顶点着色器我们只需要将position和ui传到片元着色器中,关键是片元着色器,首先我们先得到一个新uv,这个新uv是沿x方向重复7次,沿y方向重复8次,然后tcolor1和tcolor2分别是底图的颜色和重复了7*8的陷阱的颜色。if中是渲染第一个陷阱,else if是渲染第二个陷阱,else中循环target数组,渲染target区域,具体的判断其实很简单。这样我们就根据关卡渲染了陷阱。

2. 对是否可以旋转进行判定

因为小方块是不可以超过底图的边缘的,而且也不可以直接覆盖到陷阱上面,因为这个操作是在点击上下左右的时候就要先判断可行性,但是此时我们还没有转,所以我们就要先拷贝一个boxes,先进行旋转看看出没出界或者压没压到陷阱,我们是这样实现的。

judge(num) {
judgeGroup = new THREE.Group();
boxesCopy = [];
for(let i=0; i<boxes.length; i++) {
let geom = new THREE.BoxGeometry(ratio, ratio, ratio);
let mate = new THREE.MeshBasicMaterial({color: 0xffaa00, transparent: true, opacity: .8});
let mesh = new THREE.Mesh(geom, mate);
mesh.position.copy(boxes[i].position);
boxesCopy[i] = mesh;
}
if(num == 1) {
var offset = new THREE.Vector3(box3.max.x, 0, 0);
judgeGroup.position.copy(offset);
boxesCopy.forEach(d => {
d.position.sub(offset);
judgeGroup.add(d);
})
judgeGroup.rotation.z = - Math.PI / 2;
} else if(num == 2) {
var offset = new THREE.Vector3(box3.min.x, 0, 0);
judgeGroup.position.copy(offset);
boxesCopy.forEach(d => {
d.position.sub(offset);
judgeGroup.add(d);
})
judgeGroup.rotation.z = Math.PI / 2;
} else if(num == 3) {
var offset = new THREE.Vector3(0, 0, box3.min.z);
judgeGroup.position.copy(offset);
boxesCopy.forEach(d => {
d.position.sub(offset);
judgeGroup.add(d);
})
judgeGroup.rotation.x = - Math.PI / 2;
} else if(num == 4) {
var offset = new THREE.Vector3(0, 0, box3.max.z);
judgeGroup.position.copy(offset);
boxesCopy.forEach(d => {
d.position.sub(offset);
judgeGroup.add(d);
})
judgeGroup.rotation.x = Math.PI / 2;
}
judgeGroup.updateMatrixWorld();
let canPass = true;
boxesCopy.forEach(d => {
var trans = new THREE.Vector3();
d.matrixWorld.decompose(trans, new THREE.Quaternion(), new THREE.Vector3());
let x = Math.round((trans.x - 5) / 10);
let z = Math.round((trans.z - 5) / 10);
let y = Math.round((trans.y + 5) / 10);
if(x > -1 || x < -7 || z > -1 || z < -8) {
canPass = false;
} else {
square[this.game].trap.forEach(d => {
if(d[0] == x && d[1] == z && y == 1) {
canPass = false;
}
})
}
})
return canPass;
},

boxesCopy就是对boxes进行的拷贝,num就是我们的上下左右操作,最后一个循环就是判断是否可翻转,x,y,z值分别对应我们的格子,if判断时候出界,因为x的界限就是[-1,-7],z的界限就是[-1,-8]。else是判断是否压到陷阱,只要有一个成立,canPass就会变成false。这就完成了简单的旋转判断。

3. 获胜的判定

获胜的判定很简单,在每一个旋转之后,比较boxes和end数组,如果两个数组一样,那么就说明胜利了,代码如下

computedWin() {
let win = true;
let temp = [];
boxes.forEach(d => {
let x = Math.round((d.position.x - 5) / 10);
let z = Math.round((d.position.z - 5) / 10);
temp.push([x, z]);
})
square[this.game].end.forEach(d => {
if(!temp.some(dd => dd[0] == d[0] && dd[1] == d[1])) {
win = false;
}
})
if(win) {
this.win();
}
},

最后加上一点tween动画,这样我们就完成了一个逻辑转体的游戏,游戏玩起来还是比较有意思的。

转载请注明地址:郭先生的博客

three.js 制作逻辑转体游戏(下)的更多相关文章

  1. three.js 制作逻辑转体游戏(上)

    今天郭先生又出来制作游戏了,最近有小伙伴要做一个逻辑转体小游戏,我怎么能不先来试试呢.玩法可以看上面的连接,下面附几张图.线案例请点击博客原文. 游戏规则不懂得可以看自行百度哈,其实玩起来还挺有难度的 ...

  2. three.js 制作一个三维的推箱子游戏

    今天郭先生发现大家更喜欢看我发的three.js小作品,今天我就发一个3d版本推箱子的游戏,其实webGL有很多框架,three.js并不合适做游戏引擎,但是可以尝试一些小游戏.在线案例请点击博客原文 ...

  3. 用Phaser来制作一个html5游戏——flappy bird (二)

    在上一篇教程中我们完成了boot.preload.menu这三个state的制作,下面我们就要进入本游戏最核心的一个state的制作了.play这个state的代码比较多,我不会一一进行说明,只会把一 ...

  4. js制作带有遮罩弹出层实现登录小窗口

    要实现的效果如下 点击“登录”按钮后,弹出登录小窗口,并且有遮罩层(这个名词还是百度知道的,以前只知道效果,却不知道名字) 在没有点击“登录”按钮之前登录小窗口不显示,点击“登录”按钮后小窗口显示,并 ...

  5. 基于node.js制作爬虫教程

    前言:最近想学习node.js,突然在网上看到基于node的爬虫制作教程,所以简单学习了一下,把这篇文章分享给同样初学node.js的朋友. 目标:爬取 http://tweixin.yueyishu ...

  6. Pygame制作答题类游戏的实现

    代码地址如下:http://www.demodashi.com/demo/13495.html 概述 个人比较喜欢玩这些答题类的游戏,在这类的游戏中其实存在着一些冷知识在里面.练习pygame的过程中 ...

  7. 用 JS 做一个数独游戏(一)

    用 JS 做一个数独游戏(一) 数独的棋盘由 9x9 的方格组成,每一行的数字包含 1 ~ 9 九个数字,并且每一列包含 1 ~ 9 这 9 个不重复的数字,另外,整个棋盘分为 9 个 3x3 的块, ...

  8. Unreal Engine 4 系列教程 Part 10:制作简单FPS游戏

    .katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...

  9. 用Phaser来制作一个html5游戏——flappy bird (一)

    Phaser是一个简单易用且功能强大的html5游戏框架,利用它可以很轻松的开发出一个html5游戏.在这篇文章中我就教大家如何用Phaser来制作一个前段时间很火爆的游戏:Flappy Bird,希 ...

随机推荐

  1. python---filecmp 实现文件,目录,遍历子目录的差异对比功能。

    python---filecmp ilecmp可以实现文件,目录,遍历子目录的差异对比功能. 自带filecmp模块,无需安装. 常用方法说明 filecmp提供3个操作方法,cmp(单文件对比),c ...

  2. laravel报错1071 Specified key was too long; max key length is 1000 bytes

    Laravel 默认使用utf8mb4字符编码,而不是的utf8编码.因此运行php artisan migrate会出现如下错误: [Illuminate\Database\QueryExcepti ...

  3. java JDBC自我总结

    preparedstatement和statement的区别 当不需要预编译时(不需要占位符)可以选用statement,存在不安全 当有占位符(?)时,需要选用preparedstatement s ...

  4. Ionic 移动端

    <body ng-app="testApp"> <ion-header-bar align-title="left" class=" ...

  5. Redis系列(九):Redis的事务机制

    提到事务,相信大家都不陌生,事务的ACID四大特性,也是面试时经常问的,不过一般情况下,我们可能想到的是传统关系型数据库的事务,其实,Redis也是提供了事务机制的,本篇博客就来讲解下Redis的事务 ...

  6. Alink漫谈(十五) :多层感知机 之 迭代优化

    Alink漫谈(十五) :多层感知机 之 迭代优化 目录 Alink漫谈(十五) :多层感知机 之 迭代优化 0x00 摘要 0x01 前文回顾 1.1 基本概念 1.2 误差反向传播算法 1.3 总 ...

  7. Flask+微信公众号开发(接入指南)

    目录 一.注册公众号 二.启用开发者 三.配置服务器配置 四.开发自己的需求 五.写在最后 一.注册公众号 具体的注册过程,根据官方文档一步一步来即可.这里需注意的是订阅号还是服务号:有些比较好的开发 ...

  8. MOS 预夹断到底是什么

    https://www.cnblogs.com/yeungchie/ MOS管就像一个开关,栅极(Gate)决定源极(Souce)到漏极(Drain)的沟道(Channel)是开还是关.以NMOS为例 ...

  9. 5.15 省选模拟赛 容斥 生成函数 dp

    LINK:5.15 T2 个人感觉生成函数更无脑 容斥也好推的样子. 容易想到每次放数和数字的集合无关 所以得到一个dp f[i][j]表示前i个数字 逆序对为j的方案数. 容易得到转移 使用前缀和优 ...

  10. 一本通 1783 矩阵填数 状压dp 容斥 计数

    LINK:矩阵填数 刚看到题目的时候感觉是无从下手的. 可以看到有n<=2的点 两个矩形. 如果只有一个矩形 矩形外的方案数容易计算考虑 矩形内的 必须要存在x这个最大值 且所有值<=x. ...