携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情
上一篇文章中讲到了这个推箱子小游戏的初场景生成,今天就来实现实际的游玩移动操作了。
监听键盘事件
//监听键盘事件
function addKeyboardEvent() {
document.onkeydown = function(event) {
var choice = ghostdirection(event.key);
if (choice == "↑" || choice == "↓" || choice == "←" || choice == "→") {
document.getElementById("answer1").style.visibility = "hidden";
document.getElementById("answer2").style.visibility = "hidden";
document.getElementById("answer3").style.visibility = "hidden";
ghostmove(choice);
document.getElementById("num").innerHTML = step.length;
}
};
}
addKeyboardEvent();
游戏中使用的是键盘的wasd四个按键,但是由于使用了一个OrbitControls视角控制插件,可以通过鼠标随意调整视角,因此wasd需要根据视角来判断是要往哪个方向移动,所以首先调用了ghostdirection这个方法:
function ghostdirection(eventkey) {
var x = camera.position.x;
var z = camera.position.z;
var tmepele; //用于改变数组顺序的变量
var choice;
var choiceArray = ["↑", "←", "↓", "→"];
if (z < 100 - x) {
if (z > x) {
choiceArray = ["→", "↑", "←", "↓"];
} else {
choiceArray = ["↓", "→", "↑", "←"];
}
} else {
if (z > x) {
choiceArray = ["↑", "←", "↓", "→"];
} else {
choiceArray = ["←", "↓", "→", "↑"];
}
}
switch (eventkey) {
case "w":
choice = choiceArray[0];
break;
case "a":
choice = choiceArray[1];
break;
case "s":
choice = choiceArray[2];
break;
case "d":
choice = choiceArray[3];
break;
}
return choice;
}
判断运动方向主要看当前相机视角的x轴和z轴的关系,根据不同的视角生成wasd对应的方向。
人物移动
方向判断完成之后,就可以开始做人物运动的判断,调用ghostmove方法:
function ghostmove(choice) {
switch (choice) {
case "↑":
ghost.cube.rotation.z = Math.PI;
if (ghost.isavailable("w")) {
step.push(choice); //更新步数
move(ghost.cube, "↑"); //人物移动
cubes[ghost.row][ghost.column].state = 0; //更新原人物位置状态信息
var state = cubes[ghost.row - 1][ghost.column].state;
if (state == 2) {
stepflag.push(true);
var box = cubes[ghost.row - 1][ghost.column].cube;
move(box, "↑"); //箱子移动
cubes[ghost.row - 2][ghost.column].state = 2; //更新数组中箱子信息
cubes[ghost.row - 2][ghost.column].cube = box;
if (descube.checkcube(ghost.row - 2, ghost.column)) {
setTimeout(function() {
box.scale.set(0.5, 0.5, 0.5);
}, 500);
} else {
setTimeout(function() {
box.scale.set(1, 1, 1);
}, 500);
}
} else {
stepflag.push(false);
}
cubes[ghost.row - 1][ghost.column].state = 3; //更新数组中人物信息
ghost.row -= 1; //更新人物位置信息
checkdesall();
}
break;
case "↓":
ghost.cube.rotation.z = 0;
if (ghost.isavailable("s")) {
step.push(choice); //更新步数
move(ghost.cube, "↓"); //人物移动
cubes[ghost.row][ghost.column].state = 0; //更新原人物位置状态信息
var state = cubes[ghost.row + 1][ghost.column].state;
if (state == 2) {
stepflag.push(true);
var box = cubes[ghost.row + 1][ghost.column].cube;
move(box, "↓"); //箱子移动
cubes[ghost.row + 2][ghost.column].state = 2; //更新数组中箱子信息
cubes[ghost.row + 2][ghost.column].cube = box;
if (descube.checkcube(ghost.row + 2, ghost.column)) {
setTimeout(function() {
box.scale.set(0.5, 0.5, 0.5);
}, 500);
} else {
setTimeout(function() {
box.scale.set(1, 1, 1);
}, 500);
}
} else {
stepflag.push(false);
}
cubes[ghost.row + 1][ghost.column].state = 3; //更新数组中人物信息
ghost.row += 1; //更新人物位置信息
checkdesall();
}
break;
case "←":
ghost.cube.rotation.z = -Math.PI / 2;
if (ghost.isavailable("a")) {
step.push(choice); //更新步数
move(ghost.cube, "←"); //人物移动
cubes[ghost.row][ghost.column].state = 0; //更新原人物位置状态信息
var state = cubes[ghost.row][ghost.column - 1].state;
if (state == 2) {
stepflag.push(true);
var box = cubes[ghost.row][ghost.column - 1].cube;
move(box, "←"); //箱子移动
cubes[ghost.row][ghost.column - 2].state = 2; //更新数组中箱子信息
cubes[ghost.row][ghost.column - 2].cube = box;
if (descube.checkcube(ghost.row, ghost.column - 2)) {
setTimeout(function() {
box.scale.set(0.5, 0.5, 0.5);
}, 500);
} else {
setTimeout(function() {
box.scale.set(1, 1, 1);
}, 500);
}
} else {
stepflag.push(false);
}
cubes[ghost.row][ghost.column - 1].state = 3; //更新数组中人物信息
ghost.column -= 1; //更新人物位置信息
checkdesall();
}
break;
case "→":
ghost.cube.rotation.z = Math.PI / 2;
if (ghost.isavailable("d")) {
step.push(choice); //更新步数
move(ghost.cube, "→"); //人物移动
cubes[ghost.row][ghost.column].state = 0; //更新原人物位置状态信息
var state = cubes[ghost.row][ghost.column + 1].state;
if (state == 2) {
stepflag.push(true);
var box = cubes[ghost.row][ghost.column + 1].cube;
move(box, "→"); //箱子移动
cubes[ghost.row][ghost.column + 2].state = 2; //更新数组中箱子信息
cubes[ghost.row][ghost.column + 2].cube = box;
if (descube.checkcube(ghost.row, ghost.column + 2)) {
setTimeout(function() {
box.scale.set(0.5, 0.5, 0.5);
}, 500);
} else {
setTimeout(function() {
box.scale.set(1, 1, 1);
}, 500);
}
} else {
stepflag.push(false);
}
cubes[ghost.row][ghost.column + 1].state = 3; //更新数组中人物信息
ghost.column += 1; //更新人物位置信息
checkdesall();
}
break;
default:
break;
}
}
当时的代码结构还有些复杂,实际上是可以简化一部分的,人物的移动首先是将人物按照移动方向进行转向,之后对是否可以移动进行判断,这个判断方法在上一篇方块生成的文章中有介绍过。如果可以移动,则开始记录步骤,值得一提的是,记录步骤的时候使用了另一个数组stepflag来记录实际推动了箱子的步骤,以便在步骤回退的时候跳过没有移动箱子的步骤,直接退回到上一次改变箱子位置的步骤上去。之后,对箱子和人物的当前位置都进行记录以便为下一次判断做好准备;调用move方法开启移动动画;对箱子是否达到终点进行判断,如果到达终点则改变其scale属性调整大小,最后调用checkdesall方法判定是否通关。
其中大部分的方法都在上一篇文章中讲过了,或者实现起来比较简答,移动动画的生成着重说两句。
移动动画
人物和箱子的移动实际上就是对其坐标进行重新设置,但是由于是3D的视觉,如果没有过渡动画的话,会显得很突兀,因此使用了setInterval实现了一个动画的效果:
function move(obj, opt) {
var mover;
switch (opt) {
case "↑":
if (mover) {
clearInterval(mover);
}
var count = 0;
mover = setInterval(function() {
if (count == 50) {
clearInterval(mover);
} else {
obj.position.z -= .2;
count++;
}
}, 5);
break;
case "↓":
if (mover) {
clearInterval(mover);
}
var count = 0;
mover = setInterval(function() {
if (count == 50) {
clearInterval(mover);
} else {
obj.position.z += .2;
count++;
}
}, 5);
break;
case "←":
if (mover) {
clearInterval(mover);
}
var count = 0;
mover = setInterval(function() {
if (count == 50) {
clearInterval(mover);
} else {
obj.position.x -= .2;
count++;
}
}, 5);
break;
case "→":
if (mover) {
clearInterval(mover);
}
var count = 0;
mover = setInterval(function() {
if (count == 50) {
clearInterval(mover);
} else {
obj.position.x += .2;
count++;
}
}, 5);
break;
default:
return;
}
renderer.render(scene, camera);
}
这样,这个小游戏就完成了!