JS解决圆形与矩形碰撞的函数

问题描述 投票:0回答:1

我正在制作一款自上而下的 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;
        }
      }

代码也未优化,所以如果有明显不必要的东西,请在您的答案中包含它,谢谢(:

javascript game-physics game-development collision 2d-games
1个回答
0
投票

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)
  };
}
© www.soinside.com 2019 - 2024. All rights reserved.