1、目标
当Player选择Scythe后,鼠标悬浮在草上,会显示绿色光标。鼠标左击,会触发割草的动画。
2、优化Settings.cs脚本
添加以下两行代码:
// Reaping(收割)
public const int maxCollidersToTestPerReapSwing = 15; // 每次收割检测的最多的碰撞体数量
public const int maxTargetComponentsToDestroyPerReapSwing = 2; // 每次收割销毁的最多的组件数量
3、优化Player.cs脚本
1)添加一个变量:
2)Awake方法中添加新的定义
3)Start方法中添加新的定义
4)PlayerClickInput方法中增加一个if条件
5)ProcessPlayerClickInput方法增加一个case条件
6)ProcessPlayerClickInputTool方法增加case条件
7)添加判断角色方向的方法:
计算的方式如下:
private Vector3Int GetPlayerDirection(Vector3 cursorPosition, Vector3 playerPosition)
{if(cursorPosition.x > playerPosition.x&&cursorPosition.y < (playerPosition.y + cursor.ItemUseRadius / 2f)&&cursorPosition.y > (playerPosition.y - cursor.ItemUseRadius / 2f)){return Vector3Int.right;}else if(cursorPosition.x < playerPosition.x&&cursorPosition.y < (playerPosition.y + cursor.ItemUseRadius / 2f)&&cursorPosition.y > (playerPosition.y - cursor.ItemUseRadius / 2f)){return Vector3Int.left;}else if(cursorPosition.y > playerPosition.y){return Vector3Int.up;}else{return Vector3Int.down;}
}
8)添加ReapInPlayerDirectionAtCursor方法
private void ReapInPlayerDirectionAtCursor(ItemDetails itemDetails, Vector3Int playerDirection)
{StartCoroutine(ReapInPlayerDirectionAtCursorRoutine(itemDetails, playerDirection));
}
9)添加ReapInPlayerDirectionAtCursorRoutine方法
private IEnumerator ReapInPlayerDirectionAtCursorRoutine(ItemDetails itemDetails, Vector3Int playerDirection)
{PlayerInputIsDisabled = true;playerToolUseDisabled = true;// Set tool animation to scythe in override animationtoolCharacterAttribute.partVariantType = PartVariantType.scythe;characterAttributeCustomisationList.Clear();characterAttributeCustomisationList.Add(toolCharacterAttribute);animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);// Reap in player directionUseToolInPlayerDirection(itemDetails, playerDirection);yield return useToolAnimationPause;PlayerInputIsDisabled = false;playerToolUseDisabled = false;
}
10)添加UseToolInPlayerDirection方法
private void UseToolInPlayerDirection(ItemDetails equippedItemDetails, Vector3Int playerDirection)
{if (Input.GetMouseButton(0)){switch (equippedItemDetails.itemType){case ItemType.Reaping_tool:if(playerDirection == Vector3Int.right){isSwingToolRight = true;}else if(playerDirection == Vector3Int.left){isSwingToolLeft = true;}else if(playerDirection == Vector3Int.up){isSwingToolUp = true;}else if(playerDirection == Vector3Int.down){isSwingToolDown = true;}break;}// Define centre point of square which will be used for collision testing,检测碰撞的中心点位置Vector2 point = new Vector2(GetPlayerCentrePosition().x + playerDirection.x * (equippedItemDetails.itemUseRadius / 2f),GetPlayerCentrePosition().y + playerDirection.y * (equippedItemDetails.itemUseRadius / 2f));// Define size of the square which will be used for collision testingVector2 size = new Vector2(equippedItemDetails.itemUseRadius, equippedItemDetails.itemUseRadius);// Get Item components with 2D collider located in the square at the centre point defined (2d colliders tested limited to maxCollidersToTestPerReapSwing)Item[] itemArray = HelperMethods.GetComponentsAtBoxLocationNonAlloc<Item>(Settings.maxCollidersToTestPerReapSwing, point, size, 0f);int reapableItemCount = 0;// Loop through all items retrievedfor(int i = itemArray.Length - 1; i >= 0; i--){if(itemArray[i] != null){// Destory item game object if reapableif (InventoryManager.Instance.GetItemDetails(itemArray[i].ItemCode).itemType == ItemType.Reapable_scenary){// Effect positionVector3 effectPosition = new Vector3(itemArray[i].transform.position.x, itemArray[i].transform.position.y + Settings.gridCellSize / 2f,itemArray[i].transform.position.z);Destroy(itemArray[i].gameObject);reapableItemCount++;if (reapableItemCount >= Settings.maxTargetComponentsToDestroyPerReapSwing)break;}}}}
}
11)添加onEnable和onDisable方法
private void OnDisable()
{EventHandler.BeforeSceneUnloadFadeOutEvent -= DisablePlayerInputAndResetMovement;EventHandler.AfterSceneLoadFadeInEvent -= EnablePlayerInput;
}private void OnEnable()
{EventHandler.BeforeSceneUnloadFadeOutEvent += DisablePlayerInputAndResetMovement;EventHandler.AfterSceneLoadFadeInEvent += EnablePlayerInput;
}
Player.cs完整代码如下:
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;public class Player : SingletonMonobehaviour<Player>
{private WaitForSeconds afterUseToolAnimationPause;private WaitForSeconds useToolAnimationPause;private WaitForSeconds afterLiftToolAnimationPause;private WaitForSeconds liftToolAnimationPause;private bool playerToolUseDisabled = false; // 如果玩家正在使用某个道具,其他道具将被禁用private AnimationOverrides animationOverrides; // 动画重写控制器private GridCursor gridCursor;private Cursor cursor;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 swappedprivate 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 attributesarmsCharacterAttribute = new CharacterAttribute(CharacterPartAnimator.arms, PartVariantColour.none, PartVariantType.none);toolCharacterAttribute = new CharacterAttribute(CharacterPartAnimator.tool, PartVariantColour.none, PartVariantType.none);// Initialise character attribute listcharacterAttributeCustomisationList = new List<CharacterAttribute>();mainCamera = Camera.main;}private void OnDisable(){EventHandler.BeforeSceneUnloadFadeOutEvent -= DisablePlayerInputAndResetMovement;EventHandler.AfterSceneLoadFadeInEvent -= EnablePlayerInput;}private void OnEnable(){EventHandler.BeforeSceneUnloadFadeOutEvent += DisablePlayerInputAndResetMovement;EventHandler.AfterSceneLoadFadeInEvent += EnablePlayerInput;}private void Start(){gridCursor = FindObjectOfType<GridCursor>();cursor = FindObjectOfType<Cursor>();useToolAnimationPause = new WaitForSeconds(Settings.useToolAnimationPause);afterUseToolAnimationPause = new WaitForSeconds(Settings.afterUseToolAnimationPause);liftToolAnimationPause = new WaitForSeconds(Settings.liftToolAnimationPause);afterLiftToolAnimationPause = new WaitForSeconds(Settings.afterLiftToolAnimationPause);}private void Update(){#region Player Inputif (!PlayerInputIsDisabled){ResetAnimationTrigger();PlayerMovementInput();PlayerWalkInput();PlayerClickInput();PlayerTestInput();// Send event to any listeners for player movement inputEventHandler.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 customisationarmsCharacterAttribute.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 customisationarmsCharacterAttribute.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 gameif (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键移动为walkprivate 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 || cursor.CursorIsEnable){// Get Cursor Grid PositionVector3Int cursorGridPosition = gridCursor.GetGridPositionForCursor();// Get Player Grid PositionVector3Int playerGridPosition = gridCursor.GetGridPositionForPlayer();ProcessPlayerClickInput(cursorGridPosition, playerGridPosition);}}}}private void ProcessPlayerClickInput(Vector3Int cursorGridPosition, Vector3Int playerGridPosition){ResetMovement();Vector3Int playerDirection = GetPlayerClickDirection(cursorGridPosition, playerGridPosition);// Get Selected item detailsItemDetails 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.Watering_tool:case ItemType.Hoeing_tool:case ItemType.Reaping_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 Vector3Int GetPlayerDirection(Vector3 cursorPosition, Vector3 playerPosition){if(cursorPosition.x > playerPosition.x&&cursorPosition.y < (playerPosition.y + cursor.ItemUseRadius / 2f)&&cursorPosition.y > (playerPosition.y - cursor.ItemUseRadius / 2f)){return Vector3Int.right;}else if(cursorPosition.x < playerPosition.x&&cursorPosition.y < (playerPosition.y + cursor.ItemUseRadius / 2f)&&cursorPosition.y > (playerPosition.y - cursor.ItemUseRadius / 2f)){return Vector3Int.left;}else if(cursorPosition.y > playerPosition.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 toolswitch (itemDetails.itemType){case ItemType.Hoeing_tool:if (gridCursor.CursorPositionIsValid){HoeGroundAtCursor(gridPropertyDetails, playerDirection);}break;case ItemType.Watering_tool:if (gridCursor.CursorPositionIsValid){WaterGroundAtCursor(gridPropertyDetails, playerDirection);}break;case ItemType.Reaping_tool:if (cursor.CursorPositionIsValid){playerDirection = GetPlayerDirection(cursor.GetWorldPositionForCursor(), GetPlayerCentrePosition());ReapInPlayerDirectionAtCursor(itemDetails, playerDirection);}break;default:break;}}private void ReapInPlayerDirectionAtCursor(ItemDetails itemDetails, Vector3Int playerDirection){StartCoroutine(ReapInPlayerDirectionAtCursorRoutine(itemDetails, playerDirection));}private IEnumerator ReapInPlayerDirectionAtCursorRoutine(ItemDetails itemDetails, Vector3Int playerDirection){PlayerInputIsDisabled = true;playerToolUseDisabled = true;// Set tool animation to scythe in override animationtoolCharacterAttribute.partVariantType = PartVariantType.scythe;characterAttributeCustomisationList.Clear();characterAttributeCustomisationList.Add(toolCharacterAttribute);animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);// Reap in player directionUseToolInPlayerDirection(itemDetails, playerDirection);yield return useToolAnimationPause;PlayerInputIsDisabled = false;playerToolUseDisabled = false;}private void UseToolInPlayerDirection(ItemDetails equippedItemDetails, Vector3Int playerDirection){if (Input.GetMouseButton(0)){switch (equippedItemDetails.itemType){case ItemType.Reaping_tool:if(playerDirection == Vector3Int.right){isSwingToolRight = true;}else if(playerDirection == Vector3Int.left){isSwingToolLeft = true;}else if(playerDirection == Vector3Int.up){isSwingToolUp = true;}else if(playerDirection == Vector3Int.down){isSwingToolDown = true;}break;}// Define centre point of square which will be used for collision testing,检测碰撞的中心点位置Vector2 point = new Vector2(GetPlayerCentrePosition().x + playerDirection.x * (equippedItemDetails.itemUseRadius / 2f),GetPlayerCentrePosition().y + playerDirection.y * (equippedItemDetails.itemUseRadius / 2f));// Define size of the square which will be used for collision testingVector2 size = new Vector2(equippedItemDetails.itemUseRadius, equippedItemDetails.itemUseRadius);// Get Item components with 2D collider located in the square at the centre point defined (2d colliders tested limited to maxCollidersToTestPerReapSwing)Item[] itemArray = HelperMethods.GetComponentsAtBoxLocationNonAlloc<Item>(Settings.maxCollidersToTestPerReapSwing, point, size, 0f);int reapableItemCount = 0;// Loop through all items retrievedfor(int i = itemArray.Length - 1; i >= 0; i--){if(itemArray[i] != null){// Destory item game object if reapableif (InventoryManager.Instance.GetItemDetails(itemArray[i].ItemCode).itemType == ItemType.Reapable_scenary){// Effect positionVector3 effectPosition = new Vector3(itemArray[i].transform.position.x, itemArray[i].transform.position.y + Settings.gridCellSize / 2f,itemArray[i].transform.position.z);Destroy(itemArray[i].gameObject);reapableItemCount++;if (reapableItemCount >= Settings.maxTargetComponentsToDestroyPerReapSwing)break;}}}}}private void WaterGroundAtCursor(GridPropertyDetails gridPropertyDetails, Vector3Int playerDirection) { // Trigger animationStartCoroutine(WaterGroundAtCursorRoutine(playerDirection, gridPropertyDetails));}private IEnumerator WaterGroundAtCursorRoutine(Vector3Int playerDirection, GridPropertyDetails gridPropertyDetails) {PlayerInputIsDisabled = true;playerToolUseDisabled = true;// Set tool animation to watering can in override animationtoolCharacterAttribute.partVariantType = PartVariantType.wateringCan;characterAttributeCustomisationList.Clear();characterAttributeCustomisationList.Add(toolCharacterAttribute);animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);toolEffect = ToolEffect.watering;if(playerDirection == Vector3Int.right){isLiftingToolRight = true;}else if(playerDirection == Vector3Int.left){isLiftingToolLeft = true;}else if(playerDirection == Vector3Int.up){isLiftingToolUp = true;}else if(playerDirection == Vector3Int.down){isLiftingToolDown = true;}yield return liftToolAnimationPause;// Set Grid property details for watered groundif(gridPropertyDetails.daysSinceWatered == -1){gridPropertyDetails.daysSinceWatered = 0;}// Set grid property to wateredGridPropertiesManager.Instance.SetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY, gridPropertyDetails);// Display watered grid tilesGridPropertiesManager.Instance.DisplayWateredGround(gridPropertyDetails);// After animation pauseyield return afterLiftToolAnimationPause;PlayerInputIsDisabled = false;playerToolUseDisabled = false;}private void HoeGroundAtCursor(GridPropertyDetails gridPropertyDetails, Vector3Int playerDirection) {// Trigger animationStartCoroutine(HoeGroundAtCursorRoutine(playerDirection, gridPropertyDetails));}private IEnumerator HoeGroundAtCursorRoutine(Vector3Int playerDirection, GridPropertyDetails gridPropertyDetails){PlayerInputIsDisabled = true;playerToolUseDisabled = true;// Set tool animation to hoe in override animationtoolCharacterAttribute.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 groundif(gridPropertyDetails.daysSinceDug == -1){gridPropertyDetails.daysSinceDug = 0;}// Set grid property to dugGridPropertiesManager.Instance.SetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY, gridPropertyDetails);// Display dug grid tilesGridPropertiesManager.Instance.DisplayDugGround(gridPropertyDetails);// After animation pauseyield return afterUseToolAnimationPause;PlayerInputIsDisabled = false;playerToolUseDisabled = false;}public Vector3 GetPlayerViewportPosition(){// Vector3 viewport position for player (0,0) viewport bottom left, (1,1) viewport top rightreturn mainCamera.WorldToViewportPoint(gameObject.transform.position);}public Vector3 GetPlayerCentrePosition(){return new Vector3(transform.position.x, transform.position.y + Settings.playerCentreYOffset, transform.position.z);}public void DisablePlayerInputAndResetMovement(){DisablePlayerInpupt();ResetMovement();// Send event to any listeners for player movement inputEventHandler.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 movementxInput = 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 Timeif (Input.GetKeyDown(KeyCode.T)){TimeManager.Instance.TestAdvanceGameMinute();}// Trigger Advance Dayif (Input.GetKeyDown(KeyCode.G)){TimeManager.Instance.TestAdvanceGameDay();}// Test scene unload / loadif (Input.GetKeyDown(KeyCode.L)){SceneControllerManager.Instance.FadeAndLoadScene(SceneName.Scene1_Farm.ToString(), transform.position);}}
}