[英]JavaScript collision detection with objects in a multi-dimensional array
I'm currently coding a Pac-man clone with p5.js, and have ran into an issue. 我目前正在用p5.js编写一个吃豆人克隆,并遇到了一个问题。 I have created a function which draws the map by using a multi-dimensional array, drawing a wall block where a 1 is, and nothing where a 0 is. 我创建了一个函数,它通过使用多维数组绘制地图,绘制一个1是的墙块,而0是什么。
This works fine, however i'm struggling to detect collision between the player and the walls. 这很好,但是我很难发现玩家和墙壁之间的碰撞。 I have tried to use a for loop to go through the array, checking the x and y co-ordinates to see if there is a collision, however it doesn't register at all.This is the code i have used to detect collision: 我试图使用for循环遍历数组,检查x和y坐标以查看是否存在冲突,但它根本没有注册。这是我用来检测碰撞的代码:
for(i=0;i<walls.length;i++){
walls[i].draw();
if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){
console.log('collision')
}
} }
I can't see where the issue is here, as it seems to have worked in other programs. 我无法看到问题出在哪里,因为它似乎在其他程序中有效。 This runs in the Draw() function, meaning it loops 30 times a second. 这在Draw()函数中运行,这意味着它每秒循环30次。
This is the full code, incase the issue lies elsewhere: 这是完整的代码,问题出在其他地方:
var gridsize = 20;
var walls = [];
var dots = [];
var player;
var score =0;
var maps = [[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,1,1,0,1,1,1,1,1,1,0,1,1,0,1],
[1,0,1,1,0,0,0,0,0,0,0,0,1,1,0,1],
[1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1],
[1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1],
[1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1],
[1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1],
[1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1],
[1,0,1,0,0,1,1,1,1,1,1,0,0,1,0,1],
[1,0,1,0,0,0,0,2,0,0,0,0,0,1,0,1],
[1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]];
function setup(){
createCanvas(320,320);
frameRate(30);
createMap();
}
function draw(){
background(51);
for(i=0;i<walls.length;i++){
walls[i].draw();
if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y
> walls[i].y && player.y < walls[i].y + gridsize){
console.log('collision')
}
}
fill('white');
text('Score: ' + score, 5,10);
for(i=0;i<dots.length;i++){
if(player.x == dots[i].x && player.y == dots[i].y && dots[i].collect ==
false){
dots[i].collect = true;
score++;
}
dots[i].draw();
}
player.update();
player.draw();
}
function Block(x,y){
this.x = x;
this.y = y;
this.draw = function(){
fill('black');
rect(this.x,this.y,gridsize,gridsize);
}
}
function Dot(x,y){
this.x = x;
this.y = y;
this.collect = false;
this.draw = function(){
if(!this.collect){
fill('yellow');
ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/3,gridsize/3);
}else if(this.collect){
noFill();
noStroke();
ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/3,gridsize/3);
}
}
}
function Player(x,y){
this.x = x;
this.y = y;
this.update = function(){
if(keyIsDown(UP_ARROW) && frameCount%5 == 0){
player.y -= gridsize;
}
if(keyIsDown(DOWN_ARROW) && frameCount%5 == 0){
player.y += gridsize;
}
if(keyIsDown(LEFT_ARROW) && frameCount%5 == 0){
player.x -= gridsize;
}
if(keyIsDown(RIGHT_ARROW) && frameCount%5 == 0){
player.x += gridsize;
}
}
this.draw = function(){
fill('blue');
ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/1.2,gridsize/1.2);
}
}
function createMap(){
for(i=0;i<maps.length;i++){
for(j=0;j<maps[i].length;j++){
if (maps[i][j] == 1){
walls.push(new Block(j*gridsize,i*gridsize));
}else if(maps[i][j] == 0){
dots.push(new Dot(j*gridsize,i*gridsize))
}else if(maps[i][j] = 2){
player = new Player(j*gridsize,i*gridsize)
}
}
}
}
I presume the issue lies with the fact that the walls are stored in an array, however i have done very similar programs in which the same code works. 我认为问题在于墙存储在一个数组中,但是我已经做了非常类似的程序,其中相同的代码工作。
The best way to check for this type of map is to use the player's input. 检查此类地图的最佳方法是使用播放器的输入。
The player must line up with the walls so assuming the player position is relative to the top left and the player is one map unit wide and deep. 玩家必须与墙壁对齐,因此假设玩家位置相对于左上角而玩家是一个宽而深的地图单位。
Key input requests a direction to move dx
, dy
hold the directions which could be more than one at a time. 键输入请求移动方向dx
, dy
保持一次可以多于一个的方向。 If dx
or dy
are not 0 then first check if the player is lined up with a passage, if so then check if a block is in the direction of travel. 如果dx
或dy
不为0,则首先检查玩家是否排列了一个段落,如果是,则检查一个块是否在行进方向。 If the player is not lined up or blocked set the movement var to 0 如果玩家没有排队或阻挡,则将移动变量设置为0
After checking both x and y directions, then if dx
or dy
have a value then that must be a valid move. 在检查x和y方向之后,如果dx
或dy
具有值,则必须是有效移动。
Remove the player collision checking code from the main loop and call the player update function with the current map as the 2D original. 从主循环中删除播放器冲突检查代码,并使用当前地图作为2D原始调用播放器更新功能。
player.update(maps); // move the player
Change the Player and update function 更改播放器和更新功能
function Player(x,y){
this.x = x;
this.y = y;
var dx = 0; // hold current movement
var dy = 0;
const speed = 1; // per Frame pixel speed best as an integer (whole number) and evenly divisible into gridSize
// need the map so that must be passed to the update function
this.update = function(map){
// assuming keys are held to move up to stop
dx = 0; // stop by default
dy = 0;
if (keyIsDown(UP_ARROW)) { dy = -speed }
if (keyIsDown(DOWN_ARROW)) { dy = speed }
if (keyIsDown(LEFT_ARROW)) { dx = -speed }
if (keyIsDown(RIGHT_ARROW)){ dx = speed }
// get player map coords
var x = Math.floor(this.x / gridSize); // get map coord
var y = Math.floor(this.y / gridSize); // get map coord
// the two if statement are best aas function
// so you can select which one to call first. Depending on the latest
// new keys down and if the result allows movement in that
// direction then set the other direction to zero.
if (dy !== 0) { // is moving up or down?
if (this.y % gridsize === 0) { // only if lined up
if (dy > 0){ // is moving down
if (map[y + 1][x] === 1) { // down blocked
dy = 0;
}
}else if (map[y - 1][x] === 1) { // up blocked
dy = 0;
}
} else { // block move if not lined up with passage
dy = 0;
}
}
if(dx !== 0){ // is moving left or right?
if (this.x % gridsize === 0) { // only if lined up
if (dx > 0) { // is moving right
if (map[y][x + 1] === 1) { // right blocked
dx = 0;
}
} else if (map[y][x - 1] === 1) { // left blocked
dx = 0;
}
} else { // block move if not lined up with passage
dx = 0;
}
}
// could have two moves, but can only move left right or up down
// you need to add some input smarts to pick which one
// this just favours up down
if(dy !== 0) { dx = 0 };
// only valid moves will come through the filter above.
// so move the player.
this.x += dx;
this.y += dy;
}
Note I have changed the way the player moves, I set a speed per frame (1 pixel) that must be an even divisor of gridSize
. 注意我已经改变了玩家移动的方式,我设置每帧的速度(1像素),它必须是gridSize
的偶数除数。
The code above is the simplest implementation. 上面的代码是最简单的实现。 This type of games needs some extra smarts in controls. 这种类型的游戏需要一些额外的智能控件。 You should check in the direction of the newest key down. 您应该检查最新键的方向。 Ie if the player traveling down and right is pressed then moving right should have priority. 即如果按下向右和向右行进的玩家,那么向右移动应该具有优先权。 If player moving right and left is pressed then you should move left, not keep moving right. 如果按下左右移动的玩家,则应向左移动,而不是向右移动。
While looking at this question I wanted to visualize the map. 在看这个问题时,我想要想象地图。 Maps as arrays are painful to create and modify, and very hard to find mistakes in. Much easier as aa set of strings that gets converted to an array at run time. 作为数组的映射很难创建和修改,并且很难发现错误。作为一组在运行时转换为数组的字符串会更容易。
As i have done the conversion no point wasting it. 正如我已经完成了转换,没有任何一点浪费它。 maps
is identical to the original array but now easier to read and change. maps
与原始数组相同,但现在更容易阅读和更改。
const maps = [
"################",
"# #",
"# ## ###### ## #",
"# ## ## #",
"# ###### #",
"#### ####",
"# ## ## #",
"# # # # # #",
"# # # # # #",
"# # ###### # #",
"# # 2 # #",
"# ### #### ### #",
"# #",
"# ######## #",
"# #",
"################"
].map(row => row.split("").map(c => c === "#" ? 1 : c === " " ? 0 : 2));
I'm not quite sure why you're using rectangle-rectangle collision detection when you could just use grid-based collision detection. 当你可以使用基于网格的碰撞检测时,我不太清楚你为什么要使用矩形矩形碰撞检测。 You could just use the array directly. 你可以直接使用数组。
But since you are using rectangle-rectangle collision, this line looks a little bit off: 但是由于你使用矩形 - 矩形碰撞,这条线看起来有点偏:
if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){
You're checking whether the left edge of the player is inside the wall and whether the top edge of the player is inside the wall. 您正在检查播放器的左边缘是否在墙内,以及播放器的上边缘是否在墙内。 But you aren't detecting the other edges. 但是你没有检测到其他边缘。 Usually you'd want to do something like this: 通常你想做这样的事情:
if(rectOneRight > rectTwoLeft && rectOneLeft < rectTwoRight && rectOneBottom > rectTwoTop && rectOneTop < rectTwoBottom){
Notice how this if
statement checks all of the edges, not just the top and left. 注意这个if
语句如何检查所有边缘,而不仅仅是顶部和左边。 But like I said, you might be better off just using grid collision detection, since you already have a grid of walls. 但就像我说的那样,你可能最好只使用网格碰撞检测,因为你已经有了网格。
Shameless self-promotion: here is a tutorial on collision detection. 无耻的自我推销: 这是一个关于碰撞检测的教程。 It's written for Processing, but everything should translate pretty directly to P5.js. 它是为Processing编写的,但是所有内容都应该直接转换为P5.js.
if the player is not sprite here then point-in-rect collision detection will be appropriate here. 如果玩家不是精灵,那么点对点碰撞检测将适用于此。
// point in rect collision detection
function pointInRect (x, y, rect) {
return inRange(x, rect.x, rect.x + gridsize) &&
inRange(y, rect.y, rect.y + gridsize);
}
// check a value is in range or not
function inRange (value, min, max) {
return value >= Math.min(min, max) && value <= Math.max(min, max);
}
// checking player is hitting the wall or not
if(pointInRect(player.x,player.y,walls[i].x,walls[i].y))
{
console.log('collision')
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.