Unity3D仿星露谷物语开发44之收集农作物

1、目标

在土地中挖掘后,洒下种子后逐渐成长,然后使用篮子收集成熟后的农作物,工具栏中也会相应地增加该农作物。

2、修改CropStandard的参数

Assets -> Prefabs -> Crop下的CropStandard,修改其Box Collider 2D的Size(Y)值为0.5。

3、修改GridCursor.cs脚本

添加如下代码:

[SerializeField] private SO_CropDetailsList so_CropDetailsList = null;

在IsCursorValidForTool函数中添加case ItemType.Collecting_tool代码:

private bool IsCursorValidForTool(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails)
{// Switch on toolswitch (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 cursorVector3 cursorWorldPosition = new Vector3(GetWorldPositionForCursor().x + 0.5f, GetWorldPositionForCursor().y + 0.5f, 0f);// Get list of items at cursor locationList<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 itemsbool 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;}case ItemType.Watering_tool:if(gridPropertyDetails.daysSinceDug > -1 && gridPropertyDetails.daysSinceWatered == -1){return true;}else{return false;}case ItemType.Collecting_tool:// Check if item can be harvested with item selected, check item is fully grown// Check if seed plantedif(gridPropertyDetails.seedItemCode != -1){// Get crop details for seed CropDetails cropDetails = so_CropDetailsList.GetCropDetails(gridPropertyDetails.seedItemCode);// if crop details foundif(cropDetails != null){// Check if crop fully grownif(gridPropertyDetails.growthDays >= cropDetails.totalGrowthDays){// Check if crop can be harvested with tool selectedif (cropDetails.CanUseToolToHarvestCrop(itemDetails.itemCode)){return true;}else{return false;}}else{return false;}}}return false;default:return false;}}

4、配置UIPanel的信息

Hierarchy -> PersistentScene -> UI -> MainGameUICanvas -> UICanvasGroup -> UIPanel,

配置SO_CropDetailsList的信息:

5、测试光标效果

种植完防风草,然后等待它们成长。

使用basket工具,发现只有成熟的防风草才会显示绿色光标,否则显示红色光标。

6、修改GridPropertiesManager.cs脚本

添加如下代码:

 /// <summary>/// Returns the Crop object at the gridX, gridY position or null if no crop was found/// </summary>/// <param name="gridPropertyDetails"></param>/// <returns></returns>public Crop GetCropObjectAtGridLocation(GridPropertyDetails gridPropertyDetails){Vector3 worldPosition = grid.GetCellCenterWorld(new Vector3Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY, 0));Collider2D[] collider2DArray = Physics2D.OverlapPointAll(worldPosition);// Loop through colliders to get crop game objectCrop crop = null;for(int i = 0; i < collider2DArray.Length; i++){crop = collider2DArray[i].gameObject.GetComponentInParent<Crop>();if (crop != null && crop.cropGridPosition == new Vector2Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY))break;crop = collider2DArray[i].gameObject.GetComponentInChildren<Crop>();if(crop != null && crop.cropGridPosition == new Vector2Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY))break;}return crop;}/// <summary>
/// Returns Crop Details for the provided seedItemCode
/// </summary>
/// <param name="seedItemCode"></param>
/// <returns></returns>
public CropDetails GetCropDetails(int seedItemCode)
{return so_CropDetailList.GetCropDetails(seedItemCode);
}

7、修改Settings.cs脚本

添加如下代码:

    public static float pickAnimationPause = 1f;public static float afterPickAnimationPause = 0.2f;

8、修改Player.cs脚本

添加两个属性

    private WaitForSeconds afterPickAnimationPause;private WaitForSeconds pickAnimationPause;

在Start函数中增加如下代码:

pickAnimationPause = new WaitForSeconds(Settings.pickAnimationPause);
afterPickAnimationPause = new WaitForSeconds(Settings.afterPickAnimationPause);

在ProcessPlayerClickInput函数中增加如下代码:

case ItemType.Collecting_tool:

在ProcessPlayerClickInputTool函数中添加如下代码:

case ItemType.Collecting_tool:if (gridCursor.CursorPositionIsValid){CollectInPlayerDirection(gridPropertyDetails, itemDetails, playerDirection);}break;

添加CollectInPlayerDirection函数代码如下:

private void CollectInPlayerDirection(GridPropertyDetails gridPropertyDetails, ItemDetails equippedItemDetails, Vector3Int playerDirection)
{StartCoroutine(CollectInPlayerDirectionRoutine(gridPropertyDetails, equippedItemDetails, playerDirection));
}

添加CollectInPlayerDirectionRoutine函数代码如下:

private IEnumerator CollectInPlayerDirectionRoutine(GridPropertyDetails gridPropertyDetails, ItemDetails equippedItemDetails, Vector3Int playerDirection)
{PlayerInputIsDisabled = true;playerToolUseDisabled = true;ProcessCropWithEquippedItemInPlayerDirection(playerDirection, equippedItemDetails, gridPropertyDetails);yield return pickAnimationPause; // 执行完ProcessCropWithEquippedItemInPlayerDirection的时间// After animation pauseyield return afterPickAnimationPause;PlayerInputIsDisabled = false;playerToolUseDisabled = false;
}

添加ProcessCropWithEquippedItemInPlayerDirection函数代码如下:

/// <summary>/// Method processes crop with equipped item in player direction/// </summary>/// <param name="playerDirection"></param>/// <param name="equippedItemDetails"></param>/// <param name="gridPropertyDetails"></param>private void ProcessCropWithEquippedItemInPlayerDirection(Vector3Int playerDirection, ItemDetails equippedItemDetails, GridPropertyDetails gridPropertyDetails){switch (equippedItemDetails.itemType){case ItemType.Collecting_tool:if(playerDirection == Vector3Int.right){isPickingRight = true;}else if(playerDirection == Vector3Int.left){isPickingLeft = true;}else if(playerDirection == Vector3Int.up){isPickingUp = true;}else if(playerDirection == Vector3Int.down){isPickingDown = true;}break;case ItemType.none:break;}// Get crop at cursor grid locationCrop crop = GridPropertiesManager.Instance.GetCropObjectAtGridLocation(gridPropertyDetails);// Execute Process Tool Action For cropif(crop != null){switch (equippedItemDetails.itemType){case ItemType.Collecting_tool:crop.ProcessToolAction(equippedItemDetails);break;}}}

此时,Player.cs完整的代码如下:

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;public class Player : SingletonMonobehaviour<Player>
{public GameObject canyonOakTreePrefab;private WaitForSeconds afterUseToolAnimationPause;private WaitForSeconds useToolAnimationPause;private WaitForSeconds afterLiftToolAnimationPause;private WaitForSeconds liftToolAnimationPause;private WaitForSeconds afterPickAnimationPause;private WaitForSeconds pickAnimationPause;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);pickAnimationPause = new WaitForSeconds(Settings.pickAnimationPause);afterPickAnimationPause = new WaitForSeconds(Settings.afterPickAnimationPause);}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(gridPropertyDetails, itemDetails);}break;case ItemType.Commodity:if (Input.GetMouseButtonDown(0)){ProcessPlayerClickInputCommodity(itemDetails);}break;case ItemType.Watering_tool:case ItemType.Hoeing_tool:case ItemType.Reaping_tool:case ItemType.Collecting_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(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails){if(itemDetails.canBeDropped && gridCursor.CursorPositionIsValid && gridPropertyDetails.daysSinceDug > -1 && gridPropertyDetails.seedItemCode == -1){PlantSeedAtCursor(gridPropertyDetails, itemDetails);}else if (itemDetails.canBeDropped && gridCursor.CursorPositionIsValid){EventHandler.CallDropSelectedItemEvent();}}private void PlantSeedAtCursor(GridPropertyDetails gridPropertyDetails, ItemDetails itemDetails){// update grid properties with seed detailsgridPropertyDetails.seedItemCode = itemDetails.itemCode;gridPropertyDetails.growthDays = 0;// Display planted crop at grid property detailsGridPropertiesManager.Instance.DisplayPlantedCrop(gridPropertyDetails);// Remove item from inventoryEventHandler.CallRemoveSelectedItemFromInventoryEvent();}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.Collecting_tool:if (gridCursor.CursorPositionIsValid){CollectInPlayerDirection(gridPropertyDetails, itemDetails, 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);// Trigger reaping effectEventHandler.CallHarvestActionEffectEvent(effectPosition, HarvestActionEffect.reaping);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 CollectInPlayerDirection(GridPropertyDetails gridPropertyDetails, ItemDetails equippedItemDetails, Vector3Int playerDirection){StartCoroutine(CollectInPlayerDirectionRoutine(gridPropertyDetails, equippedItemDetails, playerDirection));}private IEnumerator CollectInPlayerDirectionRoutine(GridPropertyDetails gridPropertyDetails, ItemDetails equippedItemDetails, Vector3Int playerDirection){PlayerInputIsDisabled = true;playerToolUseDisabled = true;ProcessCropWithEquippedItemInPlayerDirection(playerDirection, equippedItemDetails, gridPropertyDetails);yield return pickAnimationPause; // 执行完ProcessCropWithEquippedItemInPlayerDirection的时间// After animation pauseyield return afterPickAnimationPause;PlayerInputIsDisabled = false;playerToolUseDisabled = false;}/// <summary>/// Method processes crop with equipped item in player direction/// </summary>/// <param name="playerDirection"></param>/// <param name="equippedItemDetails"></param>/// <param name="gridPropertyDetails"></param>private void ProcessCropWithEquippedItemInPlayerDirection(Vector3Int playerDirection, ItemDetails equippedItemDetails, GridPropertyDetails gridPropertyDetails){switch (equippedItemDetails.itemType){case ItemType.Collecting_tool:if(playerDirection == Vector3Int.right){isPickingRight = true;}else if(playerDirection == Vector3Int.left){isPickingLeft = true;}else if(playerDirection == Vector3Int.up){isPickingUp = true;}else if(playerDirection == Vector3Int.down){isPickingDown = true;}break;case ItemType.none:break;}// Get crop at cursor grid locationCrop crop = GridPropertiesManager.Instance.GetCropObjectAtGridLocation(gridPropertyDetails);// Execute Process Tool Action For cropif(crop != null){switch (equippedItemDetails.itemType){case ItemType.Collecting_tool:crop.ProcessToolAction(equippedItemDetails);break;}}}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);}// Test object poolif (Input.GetMouseButtonDown(1)){GameObject tree = PoolManager.Instance.ReuseObject(canyonOakTreePrefab, mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y, -mainCamera.transform.position.z)), Quaternion.identity);tree.SetActive(true);}}
}

9、修改Crop.cs脚本

完整代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Crop : MonoBehaviour
{[HideInInspector]public Vector2Int cropGridPosition;private int harvestActionCount = 0;public void ProcessToolAction(ItemDetails equippedItemDetails){// Get grid property detailsGridPropertyDetails gridPropertyDetails = GridPropertiesManager.Instance.GetGridPropertyDetails(cropGridPosition.x, cropGridPosition.y);if (gridPropertyDetails == null)return;// Get seed item detailsItemDetails seedItemDetails = InventoryManager.Instance.GetItemDetails(gridPropertyDetails.seedItemCode);if(seedItemDetails == null) return;// Get crop detailsCropDetails cropDetails = GridPropertiesManager.Instance.GetCropDetails(seedItemDetails.itemCode);if (cropDetails == null) return;// Get required harvest actions for tool(收获此农作物所需的操作次数)int requiredHarvestActions = cropDetails.RequiredHarvestActionsForTool(equippedItemDetails.itemCode);if (requiredHarvestActions == -1) return;// Increment harvest action countharvestActionCount++;// Check if required harvest actions madeif (harvestActionCount >= requiredHarvestActions)HarvestCrop(cropDetails, gridPropertyDetails);}private void HarvestCrop(CropDetails cropDetails, GridPropertyDetails gridPropertyDetails){// Delete crop from grid propertiesgridPropertyDetails.seedItemCode = -1;gridPropertyDetails.growthDays = -1;gridPropertyDetails.daysSinceLastHarvest = -1;gridPropertyDetails.daysSinceWatered = -1;GridPropertiesManager.Instance.SetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY, gridPropertyDetails);HarvestActions(cropDetails, gridPropertyDetails);}private void HarvestActions(CropDetails cropDetails, GridPropertyDetails gridPropertyDetails){SpawnHarvestedItems(cropDetails);Destroy(gameObject);  // destory当前Crop类所挂载的实例}private void SpawnHarvestedItems(CropDetails cropDetails){// Spawn the item(s) to be producedfor(int i = 0; i < cropDetails.cropProducedItemCode.Length; i++){int cropsToProduce;// Calculate how many crops to produceif (cropDetails.cropProducedMinQuantity[i] == cropDetails.cropProducedMaxQuantity[i] ||cropDetails.cropProducedMaxQuantity[i] < cropDetails.cropProducedMinQuantity[i]){cropsToProduce = cropDetails.cropProducedMinQuantity[i];}else{cropsToProduce = Random.Range(cropDetails.cropProducedMinQuantity[i], cropDetails.cropProducedMaxQuantity[i] + 1);}for(int j = 0; j < cropsToProduce; j++){Vector3 spawnPosition;if (cropDetails.spawnCropProducedAtPlayerPosition){// Add item to the players inventoryInventoryManager.Instance.AddItem(InventoryLocation.player, cropDetails.cropProducedItemCode[i]);}else{// Random positionspawnPosition = new Vector3(transform.position.x + Random.Range(-1f, 1f), transform.position.y + Random.Range(-1f, 1f), 0f);SceneItemsManager.Instance.InstantiateSceneItem(cropDetails.cropProducedItemCode[i], spawnPosition);}}}}}

10、修改InventoryManager.cs脚本

添加如下代码:

public void AddItem(InventoryLocation inventoryLocation, int itemCode)
{List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];// Check if inventory already contains the itemint itemPosition = FindItemInInventory(inventoryLocation, itemCode);if(itemPosition != -1){AddItemPosition(inventoryList, itemCode, itemPosition);}else{AddItemPosition(inventoryList, itemCode);}// Send event that inventory has been updatedEventHandler.CallInventoryUpdatedEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);
}

11、修改SceneItemsManager.cs脚本

增加如下代码:

private void InstantiateSceneItem(int ItemCode, Vector3 position)
{GameObject itemGameObject = Instantiate(itemPrefab, position, Quaternion.identity, parentItem);Item item = itemGameObject.GetComponent<Item>();item.ItemCode = ItemCode;
}

12、修改SO_CropDetailsList的配置

修改10006的配置:

勾选spawnCropProducedAtPlayerPosition,使得收获的农作物在Inventory中展示。

13、运行游戏

防风草种子成熟后,点击篮子进行采摘,则防风草会出现在最后一个Slot中。

如果Slot之前已经有防风草了,则会直接往上加数量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/905969.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

list重点接口及模拟实现

list功能介绍 c中list是使用双向链表实现的一个容器&#xff0c;这个容器可以实现。插入&#xff0c;删除等的操作。与vector相比&#xff0c;vector适合尾插和尾删&#xff08;vector的实现是使用了动态数组的方式。在进行头删和头插的时候后面的数据会进行挪动&#xff0c;时…

CE17.【C++ Cont】练习题组17(堆专题)

目录 1.P2085 最小函数值 题目 分析 方法1:暴力求解 方法2:二次函数的性质(推荐!) 代码 提交结果 2.P1631 序列合并 分析 方法1:建两个堆 第一版代码 提交结果 第二版代码 提交结果 第三版代码 提交结果 方法2:只建一个堆 代码 提交结果 1.P2085 最小函数值…

题单:表达式求值1

题目描述 给定一个只包含 “加法” 和 “乘法” 的算术表达式&#xff0c;请你编程计算表达式的值。 输入格式 输入仅有一行&#xff0c;为需要计算的表达式&#xff0c;表达式中只包含数字、加法运算符 和乘法运算符 *&#xff0c;且没有括号。 所有参与运算的数字不超过…

DeepSeek超大模型的高效训练策略

算力挑战 训练DeepSeek此类千亿乃至万亿级别参数模型,对算力资源提出了极高要求。以DeepSeek-V3为例,其基础模型参数量为67亿,采用专家混合(MoE)架构后实际激活参数可达几百亿。如此规模的模型远超单张GPU显存容量极限,必须借助分布式并行才能加载和训练。具体挑战主要包…

MFC中DoDataExchange的简明指南

基本概念 DoDataExchange 是 MFC 框架中实现数据自动同步的核心函数&#xff0c;主要用于对话框中控件与成员变量的双向绑定。它能让控件中的数据和成员变量自动保持一致&#xff0c;无需手动读写控件数据。 使用示例 1&#xff09;变量声明 在对话框头文件中声明与控件对应…

FreeCAD源码分析: Transaction实现原理

本文阐述FreeCAD中Transaction的实现原理。 注1&#xff1a;限于研究水平&#xff0c;分析难免不当&#xff0c;欢迎批评指正。 注2&#xff1a;文章内容会不定期更新。 一、概念 Ref. from What is a Transaction? A transaction is a group of operations that have the f…

C++类与对象--1 特性一:封装

C面向对象三大特性&#xff1a; &#xff08;1&#xff09;封装&#xff1b;&#xff08;2&#xff09;继承&#xff1b;&#xff08;3&#xff09;多态&#xff1b; C认为万物皆是对象&#xff0c;对象上有对应的属性&#xff08;数据&#xff09;和行为&#xff08;方法&…

初探Reforcement Learning强化学习【QLearning/Sarsa/DQN】

文章目录 一、Q-learning现实理解&#xff1a;举例&#xff1a;回顾&#xff1a; 二、Sarsa和Q-learning的区别 三、Deep Q-NetworkDeep Q-Network是如何工作的&#xff1f;前处理&#xff1a;Convolution NetworksExperience Replay 一、Q-learning 是RL中model-free、value-…

WebRTC技术EasyRTC嵌入式音视频通信SDK打造远程实时视频通话监控巡检解决方案

一、方案概述​ 在现代工业生产、基础设施维护等领域&#xff0c;远程监控与巡检工作至关重要。传统的监控与巡检方式存在效率低、成本高、实时性差等问题。EasyRTC作为一种先进的实时音视频通信技术&#xff0c;具备低延迟、高稳定性、跨平台等特性&#xff0c;能够有效解决这…

专题四:综合练习(括号组合算法深度解析)

以leetcode22题为例 题目分析&#xff1a; 给一个数字n&#xff0c;返回合法的所有的括号组合 算法原理分析&#xff1a; 你可以先考虑如何不重不漏的罗列所有的括号组合 清楚什么是有效的括号组合&#xff1f;&#xff1f;&#xff1f; 1.所有的左括号的数量等于右括号的…

星云智控自定义物联网实时监控模板-为何成为痛点?物联网设备的多样化-优雅草卓伊凡

星云智控自定义物联网实时监控模板-为何成为痛点&#xff1f;物联网设备的多样化-优雅草卓伊凡 引言&#xff1a;物联网监控的模板革命 在万物互联的时代&#xff0c;设备监控已成为保障物联网系统稳定运行的核心环节。传统的标准化监控方案正面临着设备类型爆炸式增长带来的…

5.27本日总结

一、英语 复习list2list29 二、数学 学习14讲部分内容 三、408 学习计组1.2内容 四、总结 高数和计网明天结束当前章节&#xff0c;计网内容学完之后主要学习计组和操作系统 五、明日计划 英语&#xff1a;复习lsit3list28&#xff0c;完成07年第二篇阅读 数学&#…

几种运放典型应用电路

运算放大器简称:OP、OPA、OPAMP、运放。 一、电压跟随器 电压跟随器顾名思义运放的输入端电压与运放的输出电压相等 这个电路一般应用目的是增加电压驱动能力: 比如说有个3V电源,借一个负载,随着负载电流变大,3V就会变小说明3V电源带负载能力小,驱动能力弱,这个时候…

Android核心系统服务:AMS、WMS、PMS 与 system_server 进程解析

1. 引言 在 Android 系统中&#xff0c;ActivityManagerService (AMS)、WindowManagerService (WMS) 和 PackageManagerService (PMS) 是三个最核心的系统服务&#xff0c;它们分别管理着应用的生命周期、窗口显示和应用包管理。 但你是否知道&#xff0c;这些服务并不是独立…

从另一个视角理解TCP握手、挥手与可靠传输

本文将深入探讨 TCP 协议中三次握手、四次挥手的原理&#xff0c;以及其保证可靠传输的机制。 一、三次握手&#xff1a;为何是三次&#xff0c;而非两次&#xff1f; 建立 TCP 连接的过程犹如一场严谨的 “对话”&#xff0c;需要经过三次握手才能确保通信双方的可靠连接。 三…

将Docker compose 部署的夜莺V6版本升到V7版本的详细步骤、常见问题解答及相关镜像下载地址

环境说明 夜莺官网&#xff1a;首页 - 快猫星云Flashcat 夜莺安装程序下载地址&#xff1a;快猫星云下载中心 夜莺v7.7.2镜像&#xff08;X86架构&#xff09;&#xff1a; https://download.csdn.net/download/jjk_02027/90851161 夜莺ibex v1.2.0镜像&#xff08;X86架构…

JavaScript【4】数组和其他内置对象(API)

1.数组: 1.概述: js中数组可理解为一个存储数据的容器,但与java中的数组不太一样;js中的数组更像java中的集合,因为此集合在创建的时候,不需要定义数组长度,它可以实现动态扩容;js中的数组存储元素时,可以存储任意类型的元素,而java中的数组一旦创建后,就只能存储定义类型的元…

永久免费!专为 Apache Doris 打造的可视化数据管理工具 SelectDB Studio V1.1.0 重磅发布!

作为全球领先的开源实时数据仓库&#xff0c; Apache Doris Github Stars 已超过 13.6k&#xff0c;并在 5000 余家中大型企业生产环境得到广泛应用&#xff0c;支撑业务核心场景&#xff0c;成为众多企业数据分析基础设施不可或缺的重要基座。过去&#xff0c;Apache Doris 用…

数字万用表与指针万用表使用方法及注意事项

在电子测量领域&#xff0c;万用表是极为常用的工具&#xff0c;数字万用表和指针万用表各具特点。熟练掌握它们的使用方法与注意事项&#xff0c;能确保测量的准确性与安全性。下面为您详细介绍&#xff1a; 一 、数字万用表按钮功能 > 进入及退出手动量程模式 每 按 […

深度学习Dropout实现

深度学习中的 Dropout 技术在代码层面上的实现通常非常直接。其核心思想是在训练过程中&#xff0c;对于网络中的每个神经元&#xff08;或者更精确地说&#xff0c;是每个神经元的输出&#xff09;&#xff0c;以一定的概率 p 随机将其输出置为 0。在反向传播时&#xff0c;这…