在 Unity 中实现平铺捕捉移动系统的问题

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

我正在努力为我的角色融入基于图块的运动,目标是让角色与运动方向上的下一个图块对齐。目前,存在一个问题,即无论输入方向如何,玩家都会始终捕捉到顶部和右侧的单元格。因此,当我向左或底部移动角色时,它会捕捉到右侧或上部的单元格,而不是沿预期方向的下一个单元格。我正在使用新的玩家输入系统。

当前逻辑:

目标网格位置是根据当前玩家位置(rb.position)除以网格大小计算得出的。这给出了玩家当前所在的网格单元。 通过将movementInput向量添加到目标网格位置来计算移动方向上下一个图块的中心。 然后使用 rb.MovePosition 方法将玩家平滑地移动到下一个图块的计算中心。它使用 Vector2.Lerp 在当前位置和目标位置之间进行插值。 nextTileCenter * gridSize 将网格位置转换回世界空间。 添加新的 Vector2(gridSize / 2, gridSize / 2) 以确保玩家移动到下一个图块的中心。

`script:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    public float moveSpeed = 1f;
    public float snapSpeed = 10.0f;
    public LayerMask collisionLayer;

    Vector2 movementInput;
    SpriteRenderer spriteRenderer;
    Rigidbody2D rb;
    Animator animator;
    Transform gridTransform; // Reference to the grid game object

    bool canMove = true;
    float gridSize = 0.16f;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
        spriteRenderer = GetComponent<SpriteRenderer>();

        // Find the grid game object
        gridTransform = GameObject.Find("Grid").transform;
    }

    private void FixedUpdate()
    {
    if (canMove)
    {
        // Get raw input without normalization
        Vector2 rawInput = movementInput;

        // Normalize the input vector to limit diagonal movement
        movementInput = new Vector2(Mathf.Round(rawInput.x), Mathf.Round(rawInput.y)).normalized;

        if (movementInput != Vector2.zero)
        {
            // Calculate the target position on the grid
            Vector2 targetPosition = CalculateTargetPosition(rb.position, movementInput);

            // Check for potential collisions
            RaycastHit2D hit = Physics2D.Raycast(targetPosition, Vector2.zero, gridSize, collisionLayer);
            if (hit.collider == null)
            {
                // Move towards the target position using Rigidbody2D.MovePosition()
                rb.MovePosition(rb.position + movementInput * moveSpeed * Time.deltaTime);
            }
            else
            {
                // Player hit something, try to snap to the grid smoothly
                SmoothSnapToGrid();
            }

            animator.SetBool("isMoving", true);
        }
        else
        {
            // Player is not moving, try to snap to the grid smoothly
            SmoothSnapToGrid();

            animator.SetBool("isMoving", false);
        }

        // Set direction of sprite to movement direction
        if (movementInput.x < 0)
        {
            spriteRenderer.flipX = true;
        }
        else if (movementInput.x > 0)
        {
            spriteRenderer.flipX = false;
        }
    }
}

    private void SmoothSnapToGrid()
    {
        // Calculate the target grid position
        Vector2 targetGridPosition = new Vector2(
            Mathf.Round(rb.position.x / gridSize),
            Mathf.Round(rb.position.y / gridSize)
        );

        // Calculate the center of the next tile in the movement direction
        Vector2 nextTileCenter = targetGridPosition + movementInput;

        // Smoothly move towards the center of the next tile in the movement direction
        rb.MovePosition(Vector2.Lerp(rb.position, nextTileCenter * gridSize + new Vector2(gridSize / 2, gridSize / 2), snapSpeed * Time.deltaTime));
    }

    private Vector2 CalculateTargetPosition(Vector2 currentPosition, Vector2 direction)
    {
        // Calculate the target position based on the movement direction
        Vector2 targetPosition = currentPosition + direction * moveSpeed * Time.deltaTime;

        return targetPosition;
    }

    void OnMove(InputValue movementValue)
{
    Vector2 rawInput = movementValue.Get<Vector2>();
    float horizontal = Mathf.Round(rawInput.x);
    float vertical = Mathf.Round(rawInput.y);

    // Ensure only one direction is considered
    if (Mathf.Abs(horizontal) > Mathf.Abs(vertical))
    {
        movementInput = new Vector2(horizontal, 0f).normalized;
    }
    else
    {
        movementInput = new Vector2(0f, vertical).normalized;
    }
}


    public void LockMovement()
    {
        canMove = false;
    }

    public void UnlockMovement()
    {
        canMove = true;
    }
}`

有人可以帮忙吗? 谢谢你

c# unity-game-engine unityscript
1个回答
0
投票

Mathf.Round函数四舍五入到最接近的整数,这意味着如果玩家的位置恰好位于两个单元格的中间,则可能不会四舍五入到预期方向。这可能会导致顶部和右侧单元格一致对齐。

要解决此问题,您可以修改

SmoothSnapToGrid 方法以在计算目标网格位置时考虑移动方向。您可以使用 Mathf.FloorMathf.Ceil,而不是使用 Mathf.Round,具体取决于移动方向:

private void SmoothSnapToGrid() { // Use Floor or Ceil depending on the direction to ensure snapping to the correct cell Vector2 targetGridPosition = new Vector2( (movementInput.x > 0) ? Mathf.Floor(rb.position.x / gridSize) : Mathf.Ceil(rb.position.x / gridSize), (movementInput.y > 0) ? Mathf.Floor(rb.position.y / gridSize) : Mathf.Ceil(rb.position.y / gridSize) ); // Calculate the center of the next tile in the movement direction Vector2 nextTileCenter = targetGridPosition + movementInput; // Smoothly move towards the center of the next tile in the movement direction rb.MovePosition(Vector2.Lerp(rb.position, nextTileCenter * gridSize + new Vector2(gridSize / 2, gridSize / 2), snapSpeed * Time.deltaTime)); }
此更改可确保向左或向下移动时(movementInput.x 

0 或 movingInput.y > 0),Mathf.Floor 用于捕捉到右侧或顶部单元格。< 0 or movementInput.y < 0), Mathf.Ceil is used to get the upper bound, effectively snapping to the left or bottom cell. Conversely, when moving right or up (movementInput.x >

此外,确保您的 OnMove 方法正确设置 movingInput 也很重要。如果您遇到对角线移动问题或想确保一次仅移动一个轴,那么您当前的实现似乎是正确的。

最后,始终确保您的网格大小(gridSize)与游戏世界中图块的实际大小相匹配。如果不匹配,可能会导致错误的捕捉。

© www.soinside.com 2019 - 2024. All rights reserved.