Unity3D仿星露谷物语开发35之锄地动画

1、目标

在道具栏中选择锄头时,然后点击可挖掘的地面,角色会执行锄地的动画。

一旦某块地被锄过了,则地面的网格属性会标记 已被锄过。这意味着无法在这块地再次点击锄地。

2、优化Settings.cs脚本

添加如下代码:

cs 复制代码
public static Vector2 cursorSize = Vector2.one;

public static float useToolAnimationPause = 0.25f;
public static float afterUseToolAnimationPause = 0.2f;

cursorSize定义光标的大小,值就是(1,1)。

useToolAnimationPause:当玩家使用工具时,这就是我们要暂停的时间。

afterUseToolAnimationPause:使用工具后的动画暂停时间。

3、新建HelperMethods.cs脚本

在Assets -> Scripts 下新建目录命名为HelperClasses。

在其下创建新的脚本命名为HelperMethods。

其代码如下:

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public static class HelperMethods
{
    /// <summary>
    /// Gets Components of type T at box with center point and size and angle. Returns true if at least one found and the found components are returned in the list
    /// </summary>
    /// <returns></returns>
    public static bool GetComponentsAtBoxLocation<T>(out List<T> listComponentsAtBoxPosition, Vector2 point, Vector2 size, float angle)
    {

        bool found = false;
        List<T> componentList = new List<T>();

        Collider2D[] collider2DArray = Physics2D.OverlapBoxAll(point, size, angle);

        // Loop through all colliders to get an object of type T
        for(int i = 0; i < collider2DArray.Length; i++)
        {
            T tComponent = collider2DArray[i].gameObject.GetComponentInParent<T>();
            if(tComponent != null)
            {
                found = true;
                componentList.Add(tComponent);
            }
            else
            {
                tComponent = collider2DArray[i].gameObject.GetComponentInChildren<T>();
                if(tComponent != null)
                {
                    found = true;
                    componentList.Add(tComponent);
                }
            }
        }

        listComponentsAtBoxPosition = componentList;
        return found; 
    }
}

4、优化GridCursor.cs脚本

脚本位于Assets -> Scripts -> UI下。

添加代码如下:

cs 复制代码
// SetCursorValidity功能下:

case ItemType.Hoeing_tool:
    if(!IsCursorValidForTool(gridPropertyDetails, itemDetails))
    {
        SetCursorToInvalid();
        return;
    }
    break;


// IsCursorValidForTool的实现

private bool IsCursorValidForTool(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails)
{
    // Switch on tool
    switch (itemDetails.itemType)
    {
        case ItemType.Hoeing_tool:
            if (gridPropertyDetails.isDiggable == true && gridPropertyDetails.daysSinceDug == -1)
            {
                #region Need to get any items at location so we can check if they are reapable
                // Get world position for cursor
                Vector3 cursorWorldPosition = new Vector3(GetWorldPositionForCursor().x + 0.5f, GetWorldPositionForCursor().y + 0.5f, 0f);

                // Get list of items at cursor location
                List<Item> itemList = new List<Item>();

                HelperMethods.GetComponentsAtBoxLocation<Item>(out itemList, cursorWorldPosition, Settings.cursorSize, 0f);
                #endregion

                // Loop through items found to see if any are reapable type - we are not goint to let the player dig where there are reapable scenary items
                bool foundReapable = false;

                foreach(Item item in itemList)
                {
                    if(InventoryManager.Instance.GetItemDetails(item.ItemCode).itemType == ItemType.Reapable_scenary)
                    {
                        foundReapable = true;
                        break;
                    }
                }

                if (foundReapable)
                {
                    return false;
                }
                else
                {
                    return true;
                }

            }
            else
            {
                return false;
            }

        default:
            return false;
           
    }

}

// GetWorldPositionForCursor的实现

public Vector3 GetWorldPositionForCursor()
{
    return grid.CellToWorld(GetGridPositionForCursor());
}

grid.CellToWorld()会得到grid左下角的位置,+0.5f可以获得grid中间的世界坐标位置。

其完整代码如下:

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GridCursor : MonoBehaviour
{
    private Canvas canvas; // 存储白色画布,UI光标位于其中
    private Grid grid;  // tilemap地图
    private Camera mainCamera; // 对主相机的引用
    [SerializeField] private Image cursorImage = null;
    [SerializeField] private RectTransform cursorRectTransform = null;  // 光标对象的引用
    [SerializeField] private Sprite greenCursorSprite = null;
    [SerializeField] private Sprite redCursorSprite = null;

    private bool _cursorPositionIsValid = false;

    public bool CursorPositionIsValid { get => _cursorPositionIsValid; set => _cursorPositionIsValid = value; }

    private int _itemUseGridRadius = 0;

    public int ItemUseGridRadius { get => _itemUseGridRadius; set => _itemUseGridRadius = value; }

    private ItemType _selectedItemType;
    public ItemType SelectedItemType { get => _selectedItemType; set => _selectedItemType = value; }

    private bool _cursorIsEnabled = false;
    public bool CursorIsEnabled { get => _cursorIsEnabled; set => _cursorIsEnabled = value; }

    private void OnDisable()
    {
        EventHandler.AfterSceneLoadEvent -= SceneLoaded;
    }

    private void OnEnable()
    {
        EventHandler.AfterSceneLoadEvent += SceneLoaded;
    }

    // Start is called before the first frame update
    void Start()
    {
        mainCamera = Camera.main;
        canvas = GetComponentInParent<Canvas>();
    }

    // Update is called once per frame
    void Update()
    {
        if (CursorIsEnabled)
        {
            DisplayCursor();
        }
    }

    private Vector3Int DisplayCursor()
    {
        if (grid != null)
        {   
            // 之所以需要Grid是因为某些定位是基于grid的
            // Get grid position for cursor
            Vector3Int gridPosition = GetGridPositionForCursor();

            // Get grid position for player
            Vector3Int playerGridPosition = GetGridPositionForPlayer();

            // Set cursor sprite,基于gridPosition和playerGridPosition设置光标的有效性
            SetCursorValidity(gridPosition, playerGridPosition);

            // Get rect transform position for cursor
            cursorRectTransform.position = GetRectTransformPositionForCursor(gridPosition);

            return gridPosition;
        }
        else
        {
            return Vector3Int.zero;
        }
    }

    public Vector3Int GetGridPositionForCursor()
    {
        // z is how far the objects are in front of the camera - camera is at -10 so objects are(-)-10 in front = 10
        Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -mainCamera.transform.position.z));
        return grid.WorldToCell(worldPosition);
    }

    public Vector3Int GetGridPositionForPlayer()
    {
        return grid.WorldToCell(Player.Instance.transform.position);
    }

    public Vector2 GetRectTransformPositionForCursor(Vector3Int gridPosition)
    {
        Vector3 gridWorldPosition = grid.CellToWorld(gridPosition);
        Vector2 gridScreenPosition = mainCamera.WorldToScreenPoint(gridWorldPosition);
        return RectTransformUtility.PixelAdjustPoint(gridScreenPosition, cursorRectTransform, canvas);
    }


    private void SceneLoaded()
    {
        grid = GameObject.FindObjectOfType<Grid>();
    }


    private void SetCursorValidity(Vector3Int cursorGridPosition, Vector3Int playerGridPosition)
    {
        SetCursorToValid();

        // Check item use radius is valid
        if(Mathf.Abs(cursorGridPosition.x -  playerGridPosition.x) > ItemUseGridRadius
            || Mathf.Abs(cursorGridPosition.y - playerGridPosition.y) > ItemUseGridRadius)
        {
            SetCursorToInvalid();
            return;
        }

        // Get selected item details 
        ItemDetails itemDetails = InventoryManager.Instance.GetSelectedInventoryItemDetails(InventoryLocation.player);

        if(itemDetails == null)
        {
            SetCursorToInvalid();
            return;
        }

        // Get grid property details at cursor position
        GridPropertyDetails gridPropertyDetails = GridPropertiesManager.Instance.GetGridPropertyDetails(cursorGridPosition.x, cursorGridPosition.y);

        if(gridPropertyDetails != null)
        {
            // Determine cursor validity based on inventory item selected and grid property details
            switch (itemDetails.itemType)
            {
                case ItemType.Seed:
                    if (!IsCursorValidForSeed(gridPropertyDetails))
                    {
                        SetCursorToInvalid();
                        return;
                    }
                    break;
                case ItemType.Commodity:
                    if (!IsCursorValidForCommodity(gridPropertyDetails))
                    {
                        SetCursorToInvalid();
                        return;
                    }
                    break;

                case ItemType.Hoeing_tool:
                    if(!IsCursorValidForTool(gridPropertyDetails, itemDetails))
                    {
                        SetCursorToInvalid();
                        return;
                    }
                    break;

                case ItemType.none:
                    break;
                case ItemType.count:
                    break;

                default:
                    break;

            }


        }
        else
        {
            SetCursorToInvalid();
            return;
        }

    }

    private bool IsCursorValidForSeed(GridPropertyDetails gridPropertyDetails)
    {
        return gridPropertyDetails.canDropItem;
    }

    private bool IsCursorValidForCommodity(GridPropertyDetails gridPropertyDetails)
    {
        return gridPropertyDetails.canDropItem;
    }

    /// <summary>
    /// Sets the cursor as either valid or invalid for the tool for the target gridPropertyDetails. 
    /// Returns true if valid or false if invalid
    /// </summary>
    /// <param name="gridPropertyDetails"></param>
    /// <param name="itemDetails"></param>
    /// <returns></returns>
    private bool IsCursorValidForTool(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails)
    {
        // Switch on tool
        switch (itemDetails.itemType)
        {
            case ItemType.Hoeing_tool:
                if (gridPropertyDetails.isDiggable == true && gridPropertyDetails.daysSinceDug == -1)
                {
                    #region Need to get any items at location so we can check if they are reapable
                    // Get world position for cursor
                    Vector3 cursorWorldPosition = new Vector3(GetWorldPositionForCursor().x + 0.5f, GetWorldPositionForCursor().y + 0.5f, 0f);

                    // Get list of items at cursor location
                    List<Item> itemList = new List<Item>();

                    HelperMethods.GetComponentsAtBoxLocation<Item>(out itemList, cursorWorldPosition, Settings.cursorSize, 0f);
                    #endregion

                    // Loop through items found to see if any are reapable type - we are not goint to let the player dig where there are reapable scenary items
                    bool foundReapable = false;

                    foreach(Item item in itemList)
                    {
                        if(InventoryManager.Instance.GetItemDetails(item.ItemCode).itemType == ItemType.Reapable_scenary)
                        {
                            foundReapable = true;
                            break;
                        }
                    }

                    if (foundReapable)
                    {
                        return false;
                    }
                    else
                    {
                        return true;
                    }

                }
                else
                {
                    return false;
                }

            default:
                return false;
               
        }

    }


    private void SetCursorToValid()
    {
        cursorImage.sprite = greenCursorSprite;
        CursorPositionIsValid = true;
    }

    private void SetCursorToInvalid()
    {
        cursorImage.sprite = redCursorSprite;
        CursorPositionIsValid = false;
    }

    /// <summary>
    /// DisableCursor is called in the UIInventorySlot.ClearCursors() method when an inventory slot item is no longer selected
    /// </summary>
    public void DisableCursor()
    {
        cursorImage.color = Color.clear;
        CursorIsEnabled = false;
    }

    /// <summary>
    /// EnableCursor is called in the UIInventorySlot.SetSelectedItem() method when an inventory slot item is selected and its itemUseGrid radius>0
    /// </summary>
    public void EnableCursor()
    {
        cursorImage.color = new Color(1f, 1f, 1f, 1f);
        CursorIsEnabled = true;
    }

    public Vector3 GetWorldPositionForCursor()
    {
        return grid.CellToWorld(GetGridPositionForCursor());
    }



}

5、优化Player.cs脚本

位于Assets -> Scripts -> Player -> Player.cs。

添加内容:

cs 复制代码
private WaitForSeconds afterUseToolAnimationPause;
private WaitForSeconds useToolAnimationPause;
private bool playerToolUseDisabled = false;  // 如果玩家正在使用某个道具,其他道具将被禁用


// Start函数更新为:
private void Start()
{
    gridCursor = FindObjectOfType<GridCursor>();
    useToolAnimationPause = new WaitForSeconds(Settings.useToolAnimationPause);
    afterUseToolAnimationPause = new WaitForSeconds(Settings.afterUseToolAnimationPause);
}

// 更新PlayerClickInput函数
private void PlayerClickInput()
{
    if (!playerToolUseDisabled)
    {

        if (Input.GetMouseButton(0))
        {

            if (gridCursor.CursorIsEnabled)
            {
                // Get Cursor Grid Position
                Vector3Int cursorGridPosition = gridCursor.GetGridPositionForCursor();

                // Get Player Grid Position
                Vector3Int playerGridPosition = gridCursor.GetGridPositionForPlayer();
                ProcessPlayerClickInput(cursorGridPosition, playerGridPosition);
            }
        }
    }
}

// 新增函数GetPlayerClickDirection
private Vector3Int GetPlayerClickDirection(Vector3Int cursorGridPosition, Vector3Int playerGridPosition) 
{
    if(cursorGridPosition.x > playerGridPosition.x)
    {
        return Vector3Int.right;
    }
    else if(cursorGridPosition.x < playerGridPosition.x)
    {
        return Vector3Int.left;
    }
    else if(cursorGridPosition.y > playerGridPosition.y)
    {
        return Vector3Int.up;
    }
    else
    {
        return Vector3Int.down;
    }
}

// 新增ProcessPlayerClickInputTool函数
private void ProcessPlayerClickInputTool(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails, Vector3Int playerDirection)
{
    // Switch on tool
    switch (itemDetails.itemType)
    {
        case ItemType.Hoeing_tool:
            if (gridCursor.CursorPositionIsValid)
            {
                HoeGroundAtCursor(gridPropertyDetails, playerDirection);
            }
            break;

        default:
            break;

    }
}

// 新增HoeGroundAtCursor函数

private void HoeGroundAtCursor(GridPropertyDetails gridPropertyDetails, Vector3Int playerDirection) 
{
    // Trigger animation
    StartCoroutine(HoeGroundAtCursorRoutine(gridPropertyDetails, playerDirection));
}

// 新增HoeGroundAtCursorRoutine函数
private IEnumerator HoeGroundAtCursorRoutine(Vector3Int playerDirection, GridPropertyDetails gridPropertyDetails)
{
    PlayerInputIsDisabled = true;
    playerToolUseDisabled = true;

    // Set tool animation to hoe in override animation
    toolCharacterAttribute.partVariantType = PartVariantType.hoe;
    characterAttributeCustomisationList.Clear();
    characterAttributeCustomisationList.Add(toolCharacterAttribute);
    animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);

    if(playerDirection == Vector3Int.right)
    {
        isUsingToolRight = true;
    }
    else if(playerDirection == Vector3Int.left)
    {
        isUsingToolLeft = true;
    }
    else if(playerDirection == Vector3Int.up)
    {
        isUsingToolUp = true;
    }
    else if(playerDirection == Vector3Int.down)
    {
        isUsingToolDown = true;
    }

    yield return useToolAnimationPause;

    // Set Grid property details for dug ground
    if(gridPropertyDetails.daysSinceDug == -1)
    {
        gridPropertyDetails.daysSinceDug = 0;
    }

    // Set grid property to dug
    GridPropertiesManager.Instance.SetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY, gridPropertyDetails);

    // After animation pause
    yield return afterUseToolAnimationPause;

    PlayerInputIsDisabled = false;
    playerToolUseDisabled = false;
}



// 更新ProcessPlayerClickInput函数,修改入参(从无到有2个)
private void ProcessPlayerClickInput(Vector3Int cursorGridPosition, Vector3Int playerGridPosition)
// 其他修改内容:
 Vector3Int playerDirection = GetPlayerClickDirection(cursorGridPosition, playerGridPosition);
// Get Grid property details at cursor position (the GridCursor validation routine ensures that grid property details are not null)
GridPropertyDetails gridPropertyDetails = GridPropertiesManager.Instance.GetGridPropertyDetails(cursorGridPosition.x, cursorGridPosition.y);

case ItemType.Hoeing_tool:
    ProcessPlayerClickInputTool(gridPropertyDetails, itemDetails, playerDirection);
    break;

HoeGroundAtCursorRoutine中配置好动画属性后,在Update中通过EventHandler.CallMovementEvent()触发执行动画。

完整代码如下:

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : SingletonMonobehaviour<Player>
{
    private WaitForSeconds afterUseToolAnimationPause;
    private WaitForSeconds useToolAnimationPause;
    private bool playerToolUseDisabled = false;  // 如果玩家正在使用某个道具,其他道具将被禁用

    private AnimationOverrides animationOverrides;  // 动画重写控制器
    private GridCursor gridCursor;
    private List<CharacterAttribute> characterAttributeCustomisationList;  // 目标动作列表

    [Tooltip("Should be populated in the prefab with the equippped item sprite renderer")]
    [SerializeField] private SpriteRenderer equippedItemSpriteRenderer = null; // 武器的图片

    // Player attributes that can be swapped
    private CharacterAttribute armsCharacterAttribute;
    private CharacterAttribute toolCharacterAttribute;


    private float xInput;
    private float yInput;
    private bool isWalking;
    private bool isRunning;
    private bool isIdle;
    private bool isCarrying = false;
    private ToolEffect toolEffect = ToolEffect.none;
    private bool isUsingToolRight;
    private bool isUsingToolLeft;
    private bool isUsingToolUp;
    private bool isUsingToolDown;
    private bool isLiftingToolRight;
    private bool isLiftingToolLeft;
    private bool isLiftingToolUp;
    private bool isLiftingToolDown;
    private bool isPickingRight;
    private bool isPickingLeft;
    private bool isPickingUp;
    private bool isPickingDown;
    private bool isSwingToolRight;
    private bool isSwingToolLeft;
    private bool isSwingToolUp;
    private bool isSwingToolDown;

    private Camera mainCamera;

    private Rigidbody2D rigidbody2D;

    private Direction playerDirection;

    private float movementSpeed;

    private bool _playerInputIsDisabled = false;

    public bool PlayerInputIsDisabled { get => _playerInputIsDisabled; set => _playerInputIsDisabled = value; }

    protected override void Awake()
    {
        base.Awake();

        rigidbody2D = GetComponent<Rigidbody2D>();

        animationOverrides = GetComponentInChildren<AnimationOverrides>();

        // Initialise swappable character attributes
        armsCharacterAttribute = new CharacterAttribute(CharacterPartAnimator.arms, PartVariantColour.none, PartVariantType.none);

        // Initialise character attribute list
        characterAttributeCustomisationList = new List<CharacterAttribute>();

        mainCamera = Camera.main;
    }

    private void Start()
    {
        gridCursor = FindObjectOfType<GridCursor>();
        useToolAnimationPause = new WaitForSeconds(Settings.useToolAnimationPause);
        afterUseToolAnimationPause = new WaitForSeconds(Settings.afterUseToolAnimationPause);
    }

    private void Update()
    {
        #region  Player Input

        if (!PlayerInputIsDisabled)
        {
            ResetAnimationTrigger();

            PlayerMovementInput();

            PlayerWalkInput();

            PlayerClickInput();

            PlayerTestInput();

            // Send event to any listeners for player movement input
            EventHandler.CallMovementEvent(xInput, yInput, isWalking, isRunning, isIdle, isCarrying, toolEffect,
                    isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,
                    isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,
                    isPickingRight, isPickingLeft, isPickingUp, isPickingDown,
                    isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,
                    false, false, false, false);
        }


        #endregion Player Input
    }

    private void FixedUpdate()
    {
        PlayerMovement();
    }

    /// <summary>
    /// 展示拿东西的动画
    /// </summary>
    /// <param name="itemCode"></param>
    public void ShowCarriedItem(int itemCode)
    {
        ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(itemCode);
        if(itemDetails != null)
        {
            equippedItemSpriteRenderer.sprite = itemDetails.itemSprite;
            equippedItemSpriteRenderer.color = new Color(1f, 1f, 1f, 1f);

            // Apply 'carry' character arms customisation
            armsCharacterAttribute.partVariantType = PartVariantType.carry;
            characterAttributeCustomisationList.Clear();
            characterAttributeCustomisationList.Add(armsCharacterAttribute);
            animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);

            isCarrying = true;
        }
    }


    public void ClearCarriedItem()
    {
        equippedItemSpriteRenderer.sprite = null;
        equippedItemSpriteRenderer.color = new Color(1f, 1f, 1f, 0f);

        // Apply base character arms customisation
        armsCharacterAttribute.partVariantType = PartVariantType.none;
        characterAttributeCustomisationList.Clear();
        characterAttributeCustomisationList.Add(armsCharacterAttribute);
        animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);

        isCarrying = false;
    }

    private void PlayerMovement()
    {
        Vector2 move = new Vector2(xInput * movementSpeed * Time.deltaTime, yInput * movementSpeed * Time.deltaTime);
        rigidbody2D.MovePosition(rigidbody2D.position + move);
    }


    private void ResetAnimationTrigger()
    {
        toolEffect = ToolEffect.none;
        isUsingToolRight = false;
        isUsingToolLeft = false;
        isUsingToolUp = false;
        isUsingToolDown = false;
        isLiftingToolRight = false;
        isLiftingToolLeft = false;
        isLiftingToolUp = false;
        isLiftingToolDown = false;
        isPickingRight = false;
        isPickingLeft = false;
        isPickingUp = false;
        isPickingDown = false;
        isSwingToolRight = false;
        isSwingToolLeft = false;
        isSwingToolUp = false;
        isSwingToolDown = false;
    }

    private void PlayerMovementInput()
    {
        xInput = Input.GetAxisRaw("Horizontal");
        yInput = Input.GetAxisRaw("Vertical");

        // 斜着移动
        if (xInput != 0 && yInput != 0) 
        {
            xInput = xInput * 0.71f;
            yInput = yInput * 0.71f;
        }

        // 在移动
        if (xInput != 0 || yInput != 0) 
        {
            isRunning = true;
            isWalking = false;
            isIdle = false;
            movementSpeed = Settings.runningSpeed;

            // Capture player direction for save game
            if (xInput < 0)
            {
                playerDirection = Direction.left;
            }
            else if (xInput > 0)
            {
                playerDirection = Direction.right;
            }
            else if (yInput < 0)
            {
                playerDirection = Direction.down;
            }
            else
            {
                playerDirection = Direction.up;
            }

        }
        else if(xInput == 0 && yInput == 0)
        {
            isRunning = false;
            isWalking = false;
            isIdle = true;
        }
    }

    // 按住Shift键移动为walk
    private void PlayerWalkInput()
    {
        if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
        {
            isRunning = false;
            isWalking = true;
            isIdle = false;
            movementSpeed = Settings.walkingSpeed;
        }
        else
        {
            isRunning = true;
            isWalking = false;
            isIdle = false;
            movementSpeed = Settings.runningSpeed;
        }
    }

    private void PlayerClickInput()
    {
        if (!playerToolUseDisabled)
        {

            if (Input.GetMouseButton(0))
            {

                if (gridCursor.CursorIsEnabled)
                {
                    // Get Cursor Grid Position
                    Vector3Int cursorGridPosition = gridCursor.GetGridPositionForCursor();

                    // Get Player Grid Position
                    Vector3Int playerGridPosition = gridCursor.GetGridPositionForPlayer();
                    ProcessPlayerClickInput(cursorGridPosition, playerGridPosition);
                }
            }
        }
    }

    private void ProcessPlayerClickInput(Vector3Int cursorGridPosition, Vector3Int playerGridPosition)
    {
        ResetMovement();

        Vector3Int playerDirection = GetPlayerClickDirection(cursorGridPosition, playerGridPosition);

        // Get Selected item details
        ItemDetails itemDetails = InventoryManager.Instance.GetSelectedInventoryItemDetails(InventoryLocation.player);

        // Get Grid property details at cursor position (the GridCursor validation routine ensures that grid property details are not null)
        GridPropertyDetails gridPropertyDetails = GridPropertiesManager.Instance.GetGridPropertyDetails(cursorGridPosition.x, cursorGridPosition.y);



        if(itemDetails != null)
        {
            switch (itemDetails.itemType)
            {
                case ItemType.Seed:
                    if (Input.GetMouseButtonDown(0))
                    {
                        ProcessPlayerClickInputSeed(itemDetails);
                    }
                    break;
                case ItemType.Commodity:
                    if (Input.GetMouseButtonDown(0))
                    {
                        ProcessPlayerClickInputCommodity(itemDetails);
                    }
                    break;
                case ItemType.Hoeing_tool:
                    ProcessPlayerClickInputTool(gridPropertyDetails, itemDetails, playerDirection);
                    break;
                case ItemType.none:
                    break;
                case ItemType.count:
                    break;

                default:
                    break;
            }
        }
    }


    private Vector3Int GetPlayerClickDirection(Vector3Int cursorGridPosition, Vector3Int playerGridPosition) 
    {
        if(cursorGridPosition.x > playerGridPosition.x)
        {
            return Vector3Int.right;
        }
        else if(cursorGridPosition.x < playerGridPosition.x)
        {
            return Vector3Int.left;
        }
        else if(cursorGridPosition.y > playerGridPosition.y)
        {
            return Vector3Int.up;
        }
        else
        {
            return Vector3Int.down;
        }
    }

    private void ProcessPlayerClickInputSeed(ItemDetails itemDetails)
    {
        if(itemDetails.canBeDropped && gridCursor.CursorPositionIsValid)
        {
            EventHandler.CallDropSelectedItemEvent();
        }
    }

    private void ProcessPlayerClickInputCommodity(ItemDetails itemDetails)
    {
        if(itemDetails.canBeDropped && gridCursor.CursorPositionIsValid)
        {
            EventHandler.CallDropSelectedItemEvent();
        }
    }

    private void ProcessPlayerClickInputTool(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails, Vector3Int playerDirection)
    {
        // Switch on tool
        switch (itemDetails.itemType)
        {
            case ItemType.Hoeing_tool:
                if (gridCursor.CursorPositionIsValid)
                {
                    HoeGroundAtCursor(gridPropertyDetails, playerDirection);
                }
                break;

            default:
                break;

        }
    }

    private void HoeGroundAtCursor(GridPropertyDetails gridPropertyDetails, Vector3Int playerDirection) 
    {
        // Trigger animation
        StartCoroutine(HoeGroundAtCursorRoutine(playerDirection, gridPropertyDetails));
    }

    private IEnumerator HoeGroundAtCursorRoutine(Vector3Int playerDirection, GridPropertyDetails gridPropertyDetails)
    {
        PlayerInputIsDisabled = true;
        playerToolUseDisabled = true;

        // Set tool animation to hoe in override animation
        toolCharacterAttribute.partVariantType = PartVariantType.hoe;
        characterAttributeCustomisationList.Clear();
        characterAttributeCustomisationList.Add(toolCharacterAttribute);
        animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);

        if(playerDirection == Vector3Int.right)
        {
            isUsingToolRight = true;
        }
        else if(playerDirection == Vector3Int.left)
        {
            isUsingToolLeft = true;
        }
        else if(playerDirection == Vector3Int.up)
        {
            isUsingToolUp = true;
        }
        else if(playerDirection == Vector3Int.down)
        {
            isUsingToolDown = true;
        }

        yield return useToolAnimationPause;

        // Set Grid property details for dug ground
        if(gridPropertyDetails.daysSinceDug == -1)
        {
            gridPropertyDetails.daysSinceDug = 0;
        }

        // Set grid property to dug
        GridPropertiesManager.Instance.SetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY, gridPropertyDetails);

        // After animation pause
        yield return afterUseToolAnimationPause;

        PlayerInputIsDisabled = false;
        playerToolUseDisabled = false;
    }


    public Vector3 GetPlayerViewportPosition()
    {
        // Vector3 viewport position for player (0,0) viewport bottom left, (1,1) viewport top right
        return mainCamera.WorldToViewportPoint(gameObject.transform.position);
    }

    public void DisablePlayerInputAndResetMovement()
    {
        DisablePlayerInpupt();
        ResetMovement();

        // Send event to any listeners for player movement input
        EventHandler.CallMovementEvent(xInput, yInput, isWalking, isRunning, isIdle, isCarrying, toolEffect,
                isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,
                isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,
                isPickingRight, isPickingLeft, isPickingUp, isPickingDown,
                isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,
                false, false, false, false);
    }

    private void ResetMovement()
    {
        // Reset movement
        xInput = 0f;
        yInput = 0f;
        isRunning = false;
        isWalking = false;
        isIdle = true;
    }

    public void DisablePlayerInpupt()
    {
        PlayerInputIsDisabled = true;
    }


    public void EnablePlayerInput()
    {
        PlayerInputIsDisabled = false;
    }

    /// <summary>
    /// Temp routine for test input
    /// </summary>
    private void PlayerTestInput()
    {
        // Trigger Advance Time
        if (Input.GetKeyDown(KeyCode.T))
        {
            TimeManager.Instance.TestAdvanceGameMinute();
        }

        // Trigger Advance Day
        if (Input.GetKeyDown(KeyCode.G))
        {
            TimeManager.Instance.TestAdvanceGameDay();
        }

        // Test scene unload / load
        if (Input.GetKeyDown(KeyCode.L))
        {
            SceneControllerManager.Instance.FadeAndLoadScene(SceneName.Scene1_Farm.ToString(), transform.position);
        }
    }
}

6、设置so_ItemList信息

在Assets -> Scriptable Object Assets -> Item中,打开so_ItemList,

设置Hoeing_tool的Item Use Radius为1。

7、绘制Diggable的tilemap

加载Scene1_Farm场景

勾选GridProperties组件

点击BoolDiggable

点击Tile Palette

绘制的区域如下:

然后再反勾选GridProperties组件,此时tilemap信息就会生效。

8、运行效果

不同的方向,会展示不同的动画。

当某个grid被dug之后,就无法再被dug了。

有植物的地方,也无法被dug,因为这些位置有ItemType.Reapable_scenary属性。后续这些植物要先被割掉,然后再进行种植。

code=10018/10019/10021。

相关推荐
web守墓人2 小时前
【gpt生成-其一】以go语言为例,详细描述一下 :语法规范BNF/EBNF形式化描述
前端·gpt·golang
上海云盾商务经理杨杨2 小时前
游戏APP如何抵御DDoS攻击与黑客勒索?实战防护全攻略
游戏·ddos
pink大呲花4 小时前
使用 Axios 进行 API 请求与接口封装:打造高效稳定的前端数据交互
前端·vue.js·交互
samuel9184 小时前
uniapp通过uni.addInterceptor实现路由拦截
前端·javascript·uni-app
泯泷4 小时前
JavaScript随机数生成技术实践 | 为什么Math.random不是安全的随机算法?
前端·javascript·安全
WebInfra5 小时前
🔥 Midscene 重磅更新:支持 AI 驱动的 Android 自动化
android·前端·测试
八了个戒5 小时前
「数据可视化 D3系列」入门第八章:动画效果详解(让图表动起来)
开发语言·前端·javascript·数据可视化
拉不动的猪6 小时前
无缝适配 PC 和移动端‌我们要注意哪些点呢
前端·javascript·面试
酱酱们的每日掘金7 小时前
🔥 4 月精选:AICoding Cursor上新与 MCP 实战揭秘!- AI Coding 周刊第 5 期
前端·ai编程·mcp