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 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;
}
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 planted
if(gridPropertyDetails.seedItemCode != -1)
{
// Get crop details for seed
CropDetails cropDetails = so_CropDetailsList.GetCropDetails(gridPropertyDetails.seedItemCode);
// if crop details found
if(cropDetails != null)
{
// Check if crop fully grown
if(gridPropertyDetails.growthDays >= cropDetails.totalGrowthDays)
{
// Check if crop can be harvested with tool selected
if (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 object
Crop 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 pause
yield 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 location
Crop crop = GridPropertiesManager.Instance.GetCropObjectAtGridLocation(gridPropertyDetails);
// Execute Process Tool Action For crop
if(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 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);
toolCharacterAttribute = new CharacterAttribute(CharacterPartAnimator.tool, PartVariantColour.none, PartVariantType.none);
// Initialise character attribute list
characterAttributeCustomisationList = 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 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 || cursor.CursorIsEnable)
{
// 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(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 details
gridPropertyDetails.seedItemCode = itemDetails.itemCode;
gridPropertyDetails.growthDays = 0;
// Display planted crop at grid property details
GridPropertiesManager.Instance.DisplayPlantedCrop(gridPropertyDetails);
// Remove item from inventory
EventHandler.CallRemoveSelectedItemFromInventoryEvent();
}
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;
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 animation
toolCharacterAttribute.partVariantType = PartVariantType.scythe;
characterAttributeCustomisationList.Clear();
characterAttributeCustomisationList.Add(toolCharacterAttribute);
animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);
// Reap in player direction
UseToolInPlayerDirection(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 testing
Vector2 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 retrieved
for(int i = itemArray.Length - 1; i >= 0; i--)
{
if(itemArray[i] != null)
{
// Destory item game object if reapable
if (InventoryManager.Instance.GetItemDetails(itemArray[i].ItemCode).itemType == ItemType.Reapable_scenary)
{
// Effect position
Vector3 effectPosition = new Vector3(itemArray[i].transform.position.x, itemArray[i].transform.position.y + Settings.gridCellSize / 2f,
itemArray[i].transform.position.z);
// Trigger reaping effect
EventHandler.CallHarvestActionEffectEvent(effectPosition, HarvestActionEffect.reaping);
Destroy(itemArray[i].gameObject);
reapableItemCount++;
if (reapableItemCount >= Settings.maxTargetComponentsToDestroyPerReapSwing)
break;
}
}
}
}
}
private void WaterGroundAtCursor(GridPropertyDetails gridPropertyDetails, Vector3Int playerDirection)
{
// Trigger animation
StartCoroutine(WaterGroundAtCursorRoutine(playerDirection, gridPropertyDetails));
}
private IEnumerator WaterGroundAtCursorRoutine(Vector3Int playerDirection, GridPropertyDetails gridPropertyDetails)
{
PlayerInputIsDisabled = true;
playerToolUseDisabled = true;
// Set tool animation to watering can in override animation
toolCharacterAttribute.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 ground
if(gridPropertyDetails.daysSinceWatered == -1)
{
gridPropertyDetails.daysSinceWatered = 0;
}
// Set grid property to watered
GridPropertiesManager.Instance.SetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY, gridPropertyDetails);
// Display watered grid tiles
GridPropertiesManager.Instance.DisplayWateredGround(gridPropertyDetails);
// After animation pause
yield 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 pause
yield 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 location
Crop crop = GridPropertiesManager.Instance.GetCropObjectAtGridLocation(gridPropertyDetails);
// Execute Process Tool Action For crop
if(crop != null)
{
switch (equippedItemDetails.itemType)
{
case ItemType.Collecting_tool:
crop.ProcessToolAction(equippedItemDetails);
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);
// Display dug grid tiles
GridPropertiesManager.Instance.DisplayDugGround(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 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 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);
}
// Test object pool
if (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 details
GridPropertyDetails gridPropertyDetails = GridPropertiesManager.Instance.GetGridPropertyDetails(cropGridPosition.x, cropGridPosition.y);
if (gridPropertyDetails == null)
return;
// Get seed item details
ItemDetails seedItemDetails = InventoryManager.Instance.GetItemDetails(gridPropertyDetails.seedItemCode);
if(seedItemDetails == null) return;
// Get crop details
CropDetails 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 count
harvestActionCount++;
// Check if required harvest actions made
if (harvestActionCount >= requiredHarvestActions)
HarvestCrop(cropDetails, gridPropertyDetails);
}
private void HarvestCrop(CropDetails cropDetails, GridPropertyDetails gridPropertyDetails)
{
// Delete crop from grid properties
gridPropertyDetails.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 produced
for(int i = 0; i < cropDetails.cropProducedItemCode.Length; i++)
{
int cropsToProduce;
// Calculate how many crops to produce
if (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 inventory
InventoryManager.Instance.AddItem(InventoryLocation.player, cropDetails.cropProducedItemCode[i]);
}
else
{
// Random position
spawnPosition = 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 item
int itemPosition = FindItemInInventory(inventoryLocation, itemCode);
if(itemPosition != -1)
{
AddItemPosition(inventoryList, itemCode, itemPosition);
}
else
{
AddItemPosition(inventoryList, itemCode);
}
// Send event that inventory has been updated
EventHandler.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之前已经有防风草了,则会直接往上加数量。