• three.js 制作逻辑转体游戏(下)


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

    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动画,这样我们就完成了一个逻辑转体的游戏,游戏玩起来还是比较有意思的。

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

  • 相关阅读:
    vue 当前页跳转并强制刷新
    (转)vue项目刷新当前页面
    查询sqlserver中表信息
    (转) 自旋锁和互斥锁
    Web API 自定义文件内容的定制类
    (转)缓存
    (转) redis的事务和watch
    ASP.NET MVC , ASP.NET Web API 的路由系统与 ASP.NET 的路由系统是怎么衔接的?
    (转) 分布式系统关注点——99%的人都能看懂的「熔断」以及最佳实践
    php项目权限系统设计
  • 原文地址:https://www.cnblogs.com/vadim-web/p/13534869.html
Copyright © 2020-2023  润新知