我正在制作一款自上而下的 2D 游戏,为玩家提供圆圈。我一直在尝试编写一个函数来在检测到碰撞后(以及在渲染之前)获取有关碰撞的详细信息,以获得要移动到的适当坐标以解决碰撞,但是当玩家碰撞时它会出现问题与矩形的角。修正时,玩家会夹入矩形,只要按住多个方向,就会在拐角处弹跳。任何和所有修复都非常有帮助,因为我的三角机已经生锈了,而且我已经尝试了太久了。到目前为止我已经:
玩家:圆圈代表碰撞后的玩家
object/collider: 玩家碰撞的对象/collider
oldCoords:碰撞前的玩家坐标
输入:表示两个轴上的玩家移动输入的 json 结构,其值从 -1 到 1,例如。 {x:0,y:1}
function resolvePlayerCollision(player, object, oldCoords, input, deltaTime){
let solvedCoords;
const collider = object.collider;
const rectCorners = [
{x: (collider.x - (collider.width / 2)), y: (collider.y + (collider.height / 2))},
{x: (collider.x + (collider.width / 2)), y: (collider.y + (collider.height / 2))},
{x: (collider.x - (collider.width / 2)), y: (collider.y - (collider.height / 2))},
{x: (collider.x + (collider.width / 2)), y: (collider.y - (collider.height / 2))}
];
const nearestPoint = nearestPointOnRect(player, collider);
let cornerCollision = false;
for(let corner of rectCorners){
if((corner.x == nearestPoint.x) && (corner.y == nearestPoint.y)) cornerCollision = true;
}
if(cornerCollision == true){
// Corner collision
solvedCoords = oldCoords;
// Ignore input on the axis closest to the collider if both axis are being input
let goodInput = input;
console.log('\n', goodInput.x, goodInput.y)
if((input.x != 0) && (input.y != 0)){
const Xdistance = Math.abs(collider.x - player.x);
const Ydistance = Math.abs(collider.y - player.y);
goodInput[(Xdistance > Ydistance)? 'y' : 'x'] = 0;
}
// Calculate direction to player
const directionToPlayer = (90 + (Math.sign(player.y - collider.y) * 45)) * -1 * Math.sign(player.x - collider.x) / 180 * Math.PI;
if(goodInput.x == 1){
// Right
const solvedAngle = Math.sin(directionToPlayer);
solvedCoords.x += deltaTime * Constants.playerSpeed * Math.sin(solvedAngle + (Math.PI / 2));
solvedCoords.y += Math.sign(player.y - collider.y) * deltaTime * Constants.playerSpeed * Math.cos(solvedAngle);
} else if(goodInput.x == -1){
// Left
const solvedAngle = Math.sin(directionToPlayer);
solvedCoords.x += deltaTime * Constants.playerSpeed * Math.sin(solvedAngle);
solvedCoords.y += Math.sign(player.y - collider.y) * deltaTime * Constants.playerSpeed * Math.cos(solvedAngle);
} else if(goodInput.y == 1){
// Up
const solvedAngle = Math.tan(directionToPlayer + (Math.PI / 2));
solvedCoords.x += deltaTime * Constants.playerSpeed * Math.sin(solvedAngle);
solvedCoords.y += deltaTime * Constants.playerSpeed * Math.cos(solvedAngle);
} else{
// Down
const solvedAngle = Math.tan(directionToPlayer);
solvedCoords.x += deltaTime * Constants.playerSpeed * Math.sin(solvedAngle);
solvedCoords.y += deltaTime * Constants.playerSpeed * Math.cos(solvedAngle);
}
} else {
// Side collision
solvedCoords = {x: player.x, y: player.y};
// Get axis and dimension of penetration
const axisOfPenetration = (Math.abs(collider.x - player.x) > Math.abs(collider.y - player.y))? 'x' : 'y';
const dimensionOfPenetration = (axisOfPenetration == 'x')? 'width' : 'height';
// Solve the collision on the axis of penetration
solvedCoords[axisOfPenetration] = reverseClamp(player[axisOfPenetration], (collider[axisOfPenetration] - (collider[dimensionOfPenetration] / 2) - Constants.playerRadius), (collider[axisOfPenetration] + (collider[dimensionOfPenetration] / 2) + Constants.playerRadius));
console.log(solvedCoords)
}
return solvedCoords;
}
在玩家类别中:
move(deltaTime, collidables){
// Freeze input
const input = this.input;
// Return if there is no movement input
if((input.x == 0) && (input.y == 0)) return;
// Calculate movement direction (I don't fully understand how this works, and should probably figure it out)
this.dir = (Math.PI / 2) - Math.atan2(-input.y, input.x);
// Move
const oldX = this.x;
const oldY = this.y;
this.x += deltaTime * Constants.playerSpeed * Math.sin(this.dir);
if(Math.abs(this.x) > (Constants.mapSize / 2)) this.x = oldX;
this.y -= deltaTime * Constants.playerSpeed * Math.cos(this.dir);
if(Math.abs(this.y) > (Constants.mapSize / 2)) this.y = oldY;
const collision = getFirstPlayerCollision(this, collidables);
if(collision != false){
const solvedCoords = resolvePlayerCollision(this, collision, {x: oldX, y: oldY}, input, deltaTime);
this.x = solvedCoords.x;
this.y = solvedCoords.y;
if(Math.abs(this.x) > (Constants.mapSize / 2)) this.x = oldX;
if(Math.abs(this.y) > (Constants.mapSize / 2)) this.y = oldY;
}
}
代码也未优化,所以如果有明显不必要的东西,请在您的答案中包含它,谢谢(:
ChatGPT 编写的这个功能非常棒。有点吓人。
// Returns the necessary coords to avoid collision (Written by ChatGPT 😬)
function resolvePlayerCollision(player, object, oldCoords, input, deltaTime){
const collider = object.collider;
const nearestPoint = nearestPointOnRect(player, collider);
const cornerDirection = normalize({ x: player.x - nearestPoint.x, y: player.y - nearestPoint.y });
return {
x: nearestPoint.x + cornerDirection.x * (Constants.playerRadius + 1),
y: nearestPoint.y + cornerDirection.y * (Constants.playerRadius + 1)
};
}