1、目的
收集农作物的特效制作。
2、动画制作
(1)已有动画系统
在Assets -> Animation -> Crop -> Standard下有Crop的动画信息。
harvestleft/harvestright:比如防风草收获时的动效
usetoolleft/usetoolright:需要借助工具才能收获的动销,比如使用斧头砍树,树会左右晃动
点击CropPickedLeft的Animation Clip,选择Unity Model,点击播放,没有任何反应。我们需要根据这个Clip进行定制。
在Animation界面中,CropHarvestLeft使用了CropPickedLeft动画,CropHarvestRight使用了CropPickedRight动画,但是都使用了同一个Sprite游戏对象。
我们需要修改标准预制作物(Prefabs下的CropStandard),在其中增加一些额外的东西,同时符合原有的动画结构。
(2)修改Prefab
Assets -> Prefabs -> Crop,给CropSprite添加动画器组件。
我们需要一个子游戏对象,在这个游戏对象的根级别下面,创建新物体命名为CropHarvestedSprite,这个游戏对象就是Animator要操纵的游戏物体。
添加Sprite Renderer组件,并且修改Sorting Layer为Instances。
3、修改Crop.cs脚本
[Tooltip("This should be populated from child gameobject")]
[SerializeField] private SpriteRenderer cropHarvestedSpriteRenderer = null;
接下来就是用代码来触发动画特效。
添加其他修改后,完整的代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Crop : MonoBehaviour
{
[HideInInspector]
public Vector2Int cropGridPosition;
[Tooltip("This should be populated from child gameobject")]
[SerializeField] private SpriteRenderer cropHarvestedSpriteRenderer = null;
private int harvestActionCount = 0;
public void ProcessToolAction(ItemDetails equippedItemDetails, bool isToolRight, bool isToolLeft, bool isToolDown, bool isToolUp)
{
// 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 animator for crop if present
Animator animator = GetComponentInChildren<Animator>();
// Trigger tool animation
if(animator != null)
{
if(isToolRight || isToolUp)
{
animator.SetTrigger("usetoolright");
}
else if(isToolLeft || isToolDown)
{
animator.SetTrigger("usetoolleft");
}
}
// 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(isToolRight, isToolUp, cropDetails, gridPropertyDetails, animator);
}
private void HarvestCrop(bool isUsingToolRight, bool isUsingToolUp, CropDetails cropDetails, GridPropertyDetails gridPropertyDetails, Animator animator)
{
// Is there a harvested animation
if(cropDetails.isHarvestedAnimation && animator != null)
{
// if harvest sprite then add to sprite renderer
if(cropDetails.harvestedSprite != null)
{
if(cropHarvestedSpriteRenderer != null)
{
cropHarvestedSpriteRenderer.sprite = cropDetails.harvestedSprite; // 一张图片
}
}
if(isUsingToolRight || isUsingToolUp)
{
animator.SetTrigger("harvestright");
}
else
{
animator.SetTrigger("harvestleft");
}
}
// Delete crop from grid properties
gridPropertyDetails.seedItemCode = -1;
gridPropertyDetails.growthDays = -1;
gridPropertyDetails.daysSinceLastHarvest = -1;
gridPropertyDetails.daysSinceWatered = -1;
// Should the crop be hidden before the harvested animation
if (cropDetails.hideCropBeforeHarvestedAnimation)
{
GetComponentInChildren<SpriteRenderer>().enabled = false;
}
GridPropertiesManager.Instance.SetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY, gridPropertyDetails);
// Is there a harvested animation - Destory this crop game object after animation completed
if(cropDetails.isHarvestedAnimation && animator != null)
{
StartCoroutine(ProcessHarvestedActionsAfterAnimation(cropDetails, gridPropertyDetails, animator));
}
else
{
HarvestActions(cropDetails, gridPropertyDetails);
}
}
private IEnumerator ProcessHarvestedActionsAfterAnimation(CropDetails cropDetails, GridPropertyDetails gridPropertyDetails, Animator animator)
{
while (!animator.GetCurrentAnimatorStateInfo(0).IsName("Harvested"))
{
// 等待动画播放完毕
yield return null;
}
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);
}
}
}
}
}
4、修改Player.cs脚本
修改ProcessCropWithEquippedItemInPlayerDirection函数,根据ProcessToolAction的调整,相应增加4个参数如下:
5、修改CropStandard预制体信息
6、运行游戏
此时收集防风草,就看到了防风草升起来的动效了。
7、优化脚本
当种植防风草种子的时候,程序运行正常。
当种植其他中的时候,程序就会报错,报错信息如下:
这是因为我们的So_CropDetailsList只配置了10006这一个种子的信息,所以获取不到其他种子的信息。
(1)优化Play.cs脚本
针对PlantSeedAtCursor函数添加校验:
(2)优化GridPropertiesManager.cs脚本
针对DisplayPlantedCrop函数添加校验:
此时,种植橡木种子不会报错,当然也不会有任何效果。