我想要达到的目标:
移动玩家时,它以 1 的步长移动,因此在瓷砖中。当对着盒子行走时,只要在您想要移动的方向上没有碰撞器,它就可以移动这些盒子。(现在你可以推多少个盒子没有限制)。当一个盒子(或玩家)被推入一个空中区域时,它将向空中区域的相同方向移动,直到他们离开空中区域。如果它们与空气区末端的盒子发生碰撞,它们会推动这些盒子。
到目前为止我所拥有的:
到目前为止,空气区正在工作,它将箱子推向所需的方向,并在到达终点时停止移动。玩家还可以推动所有的盒子,一旦它们撞到墙上就不能再推动它们了。
问题:
然而,问题主要在于碰撞检测。推箱子时,有可能不再推箱子并作为玩家逐步穿过箱子。我不知道如何解决这个问题。
我尝试过的事情:
我已经尝试了几件事,我什至不知道我已经尝试了什么。
一些动图:
想要同时在同一个位置时进行角点检测(工作......足够好)
将箱子推入空气区,有时逐步通过。(不起作用,但前一个更重要)
主要代码:
public abstract class Movement : MonoBehaviour
{
//=========================================================================================
// > Variables <
//=========================================================================================
//------------------------ public ------------------------
public Vector3 toBePosition; //position it will be in after lerp
public Vector3 leftDirection;
public Vector3 rightDirection;
//----------------------- private ------------------------
private int terrainLayer = 6;
private int movableLayer = 7;
protected Vector3 _currentPosition;
private Vector3 _targetPosition;
protected float _travelTime = 0.1f;
private float timer;
public bool canMove = true;
//=========================================================================================
// > Start/Update <
//=========================================================================================
protected virtual void Start()
{
toBePosition = transform.position;
_currentPosition = transform.position;
_targetPosition = transform.position;
}
protected virtual void Update()
{
//lerp position
timer += Time.deltaTime;
float ratio = timer / _travelTime;
transform.position = Vector3.Lerp(_currentPosition, _targetPosition, ratio);
//gravity
checkForFalling();
//inputs
checkForMovement();
}
public void checkForFalling()
{
if (canMove)
{
if (Physics.Raycast(_currentPosition, -Vector3.up, out RaycastHit hit, 1f))
{
//if we hit something that isnt a airchannel nor a terrain, it will move down.
if (hit.collider.gameObject.tag != "AirChannel" && hit.collider.gameObject.layer != terrainLayer)
{
moveToTile(-Vector3.up);
}
}
//didnt detect anything, thus we need to fall
else
{
moveToTile(-Vector3.up);
}
}
}
//=========================================================================================
// > Public Tool Functions <
//=========================================================================================
public void moveToTile(Vector3 pDirection)
{
if (canMove)
{
//get normalized direction just makes sure the direction on the xyz is always either 0 or 1. (sometimes it would be 0.0000001)
pDirection = getNormalizedDirection(pDirection);
//if there isnt a wall update our target position to where we want to go.
if (!wallCheck(_currentPosition + pDirection, _currentPosition))
{
_targetPosition = pDirection + _currentPosition;
toBePosition = _targetPosition;
timer = 0f;
}
}
}
protected void checkForMovement()
{
//makes sure we dont move multiple tiles within the same amount of time
//because when holding the button id would stack if we wouldnt do this.
if ((_targetPosition - transform.position).magnitude < 0.001f)
{
canMove = true;
transform.position = _targetPosition;
_currentPosition = transform.position;
}
else
{
canMove = false;
}
}
//=========================================================================================
// > Private Tool Functions <
//=========================================================================================
virtual public bool wallCheck(Vector3 pTargetPosition, Vector3 pCurrentPosition)
{
//get the direction and make sure they are either 0 or 1 again.
Vector3 moveDirection = (pTargetPosition - pCurrentPosition).normalized;
moveDirection = getNormalizedDirection(moveDirection);
//calculate the left and right tile of the forward tile.
leftDirection = getLeftFromDirection(moveDirection);
rightDirection = getRightFromDirection(moveDirection);
//debug rays to visualize the raycasts.
Debug.DrawRay(pCurrentPosition - moveDirection * 0.1f, moveDirection * 1.4f, Color.green , 5);
Debug.DrawRay(pCurrentPosition - moveDirection * 0.1f, leftDirection * 1.4f, Color.green , 5);
Debug.DrawRay(pCurrentPosition - moveDirection * 0.1f, rightDirection * 1.4f, Color.green, 5);
//============================== Collision Checks ===================================
//first we check right in front of us.
if (Physics.Raycast(pCurrentPosition, moveDirection, out RaycastHit frontHit, 1.4f))
{
//if we hit terrain we return true because it means we hit a wall
if (frontHit.collider.gameObject.layer == terrainLayer)
{
return true;
}
//if we hit a something on a movable layer, we will start a recursive loop to check if there is a empty spot to move into.
if (frontHit.collider.gameObject.layer == movableLayer)
{
return frontHit.collider.gameObject.GetComponent<Movement>().wallCheck(pTargetPosition + moveDirection, pTargetPosition);
}
else { return false; }
}
//now we check on the left side (for the corner collision)
if (Physics.Raycast(pCurrentPosition, leftDirection, out RaycastHit leftHit, 1.45f))
{
if (leftHit.collider.gameObject.layer == movableLayer)
{
//if we hit a movable layer and its also moving into the same position as we are, then move us back 1 tile.
//even though i return true, and the code should stop and not move anymore, this would still happen, thus needed to move 1 back
//a fix for this would be appreciated.
if (pTargetPosition == leftHit.collider.gameObject.GetComponent<Movement>().toBePosition)
{
moveToTile(moveDirection *= -1f);
return true;
}
else { return false; }
}
else { return false; }
}
//We do the same for the right side as we did with the left side.
if (Physics.Raycast(pCurrentPosition, rightDirection, out RaycastHit rightHit, 1.45f))
{
if (rightHit.collider.gameObject.layer == movableLayer)
{
if (pTargetPosition == rightHit.collider.gameObject.GetComponent<Movement>().toBePosition)
{
moveToTile(moveDirection *= -1f);
return true;
}
else { return false; }
}
else { return false; }
}
else { return false; }
}
protected Vector3 getNormalizedDirection(Vector3 oldDirection)
{
//makes sure everything is either 0 or 1.
Vector3 newDirection = oldDirection;
if (newDirection.x > 0.1f)
{
newDirection.x = 1;
}
else if (newDirection.x < -0.1f)
{
newDirection.x = -1;
}
else
{
newDirection.x = 0;
}
if (newDirection.z > 0.1f)
{
newDirection.z = 1;
}
else if (newDirection.z < -0.1f)
{
newDirection.z = -1;
}
else
{
newDirection.z = 0;
}
return newDirection;
}
private Vector3 getLeftFromDirection(Vector3 pDirection)
{
//calulcates the tile on the left side from given direction
Vector3 left = pDirection;
if(left.x == 0)
{
left.x -= 1;
}
if(left.z == 0)
{
left.z -= 1;
}
return left;
}
private Vector3 getRightFromDirection(Vector3 pDirection)
{
//calculates the tile on the right side from the given direction.
Vector3 left = pDirection;
if (left.x == 0)
{
left.x += 1;
}
if (left.z == 0)
{
left.z += 1;
}
return left;
}
玩家移动代码:
public class PlayerMovement : Movement
{
[SerializeField] private float _moveSpeed;
private InputManager _inputManager;
public int playerPushWeight;
private void Awake()
{
serviceLocator.AddToList("Player1", this.gameObject);
}
protected override void Start()
{
base.Start();
_inputManager = serviceLocator.GetFromList("InputManager").GetComponent<InputManager>();
}
// Update is called once per frame
protected override void Update()
{
base.Update();
if (_inputManager.GetAction(InputManager.Action.HORIZONTAL))
{
moveToTile(new Vector3(_inputManager.getHorizontalInput(),0,0));
}
if (_inputManager.GetAction(InputManager.Action.VERTICAL))
{
moveToTile(new Vector3(0, 0, _inputManager.getVerticalInput()));
}
//this allows other objects to move this object. (Boxes now can move the player, useful for airchannels)
if (wallCheckCalled)
{
moveToTile(_direction);
}
wallCheckCalled = false;
}
private Vector3 _direction;
private bool wallCheckCalled;
override public bool wallCheck(Vector3 pTargetPosition, Vector3 pCurrentPosition)
{
bool isWall = base.wallCheck(pTargetPosition, pCurrentPosition);
if (!isWall)
{
wallCheckCalled = true;
_direction = pTargetPosition - pCurrentPosition;
}
return isWall;
}
盒子运动:
public class WeightMovement : Movement
{
//=========================================================================================
// > Variables <
//=========================================================================================
//------------------------ public ------------------------
//----------------------- private ------------------------
private Vector3 _direction;
private bool wallCheckCalled;
//=========================================================================================
// > Start/Update <
//=========================================================================================
//=========================================================================================
// > Public Tool Functions <
//=========================================================================================
override public bool wallCheck(Vector3 pTargetPosition, Vector3 pCurrentPosition)
{
bool isWall = base.wallCheck(pTargetPosition, pCurrentPosition);
if (!isWall)
{
wallCheckCalled = true;
_direction = pTargetPosition - pCurrentPosition;
}
return isWall;
}
protected override void Update()
{
if (wallCheckCalled)
{
moveToTile(_direction);
}
base.Update();
wallCheckCalled = false;
}