1、目标
把防风草种子种在地里,并展示植物种子,防风草种子将随着时间变化而生长成植株。
2、创建Crop.cs脚本
在Assets -> Scripts下创建新的目录命名为Crop,在其下创建新的脚本命名为Crop.cs。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Crop : MonoBehaviour
{
[HideInInspector]
public Vector2Int cropGridPosition;
}
3、创建CropDetails.cs脚本
在Assets -> Scripts -> Crop下创建CropDetails.cs脚本。
代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class CropDetails
{
[ItemCodeDescription]
public int seedItemCode; // this is the item code for the corresponding seed
public int[] growthDays; // days growth for each stage
public int totalGrowthDays; // total growth days
public GameObject[] growthPrefab; // prefab to use when instantiating growth stages
public Sprite[] growthSprite; // growth sprite
public Season[] seasons; // growth seasons
public Sprite harvestedSprite; // sprite used once harvested
[ItemCodeDescription]
public int harvestedTransformItemCode; // if the item transform into another item when harvested this item code will be populated
public bool hideCropBeforeHarvestedAnimation; // if the crop should be disabled before the harvested animation
public bool disableCropCollidersBeforeHarvestedAnimation; // if colliders on crop should be disabled to avoid the harvested animation effecting any other game objects
public bool isHarvestedAnimation; // true if harvested animation to be played on final growth stage prefab
public bool isHarvestActionEffect = false; // flag to determine whether there is a harvest action effect
public bool spawnCropProducedAtPlayerPosition;
public HarvestActionEffect harvestActionEffect; // the harvest action effect for the crop
[ItemCodeDescription]
public int[] harvestToolItemCode; // array of item codes for the tools that can harvest or 0 array elements if no tool required
public int[] requiredHarvestActions; // number of harvest actions required for corresponding tool in harvest tool item code array
[ItemCodeDescription]
public int[] cropProducedItemCode; // array of item codes produced for the harvested crop
public int[] cropProducedMinQuantity; // array of minimum quantities produced for the harvested crop
public int[] cropProducedMaxQuantity; // if max quantity is > min quantity then a random number of crops between min and max are produced
public int daysToRegrow; // days to regrow next crop or -1 if a single crop
/// <summary>
/// returns true if the tool item code can be used to harvest this crop, else returns false
/// </summary>
/// <param name="toolItemCode"></param>
/// <returns></returns>
public bool CanUseToolToHarvestCrop(int toolItemCode)
{
if(RequiredHarvestActionsForTool(toolItemCode) == -1)
{
return false;
}
else
{
return true;
}
}
/// <summary>
/// returns -1 if the tool can't be used to harvest this crop, else returns thhe number of harvest actions required by this tool
/// </summary>
/// <param name="toolItemCode"></param>
/// <returns></returns>
public int RequiredHarvestActionsForTool(int toolItemCode)
{
for(int i = 0; i < harvestToolItemCode.Length; i++)
{
if (harvestToolItemCode[i] == toolItemCode)
{
return requiredHarvestActions[i];
}
}
return -1;
}
}
4、创建SO_CropDetailsList.cs脚本
在Assets -> Scripts -> Crop下创建SO_CropDetailsList.cs脚本。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName ="CropDetailsList", menuName ="Scriptable Objects/Crop/Crop Details List")]
public class SO_CropDetailList : ScriptableObject
{
[SerializeField]
public List<CropDetails> cropDetails;
public CropDetails GetCropDetails(int seedItemCode)
{
return cropDetails.Find(x => x.seedItemCode == seedItemCode);
}
}
5、填充防风草作物的数据
(1)制作Crop预制体
第1步,在Hierarchy -> PersistentScene下创建新的物体命名为CropStandard。
第2步,给该物体添加Box Collider 2D组件,并设置参数如下:
第3步,添加Crop组件和Item Nudge组件。
第4步,在CropStandard下创建子物体命名为CropSprite。
第5步,给CropSprite添加Sprite Renderer组件,并且设置参数如下:
如果Sorting Layer未设置未Instances,将导致seed无法正确显示(因为优先级不够)。
第6步,将Hierarchy下的CropStandard拖到Assets -> Prefabs -> Crop目录下,并且删除Hierarchy下的CropStandard。
(2)创建Crop的Scriptable资源
第1步,在Assets -> Scriptable Object Assets 下创建新的目录命名为Crop。
第2步,右击Crop目录,选择Scriptable Objects -> Crop -> Crop Details List,命名为so_CropDetailsList。
第3步,配置so_CropDetailsList的信息如下:
6、防风草生长
(1)优化Tags.cs脚本
添加一个变量:
public const string CropsParentTransform = "CropsParentTransform";
(2)添加Crops对象
首先,加载Scene1_Farm场景
然后,在Hierarchy -> Scene1_Farm下创建新的物体命名为Crops
接着,在Inspector界面下创建新的Tag:CropsParentTransform,并且指定Crops的Tag为新Tag。
最后,Unload Scene1_Farm场景。
对Scene2_Field场景进行同样的操作。
(3)优化EventHandler.cs脚本
添加新的事件如下:
// Remove selected item from inventory
public static event Action RemoveSelectedItemFromInventoryEvent;
public static void CallRemoveSelectedItemFromInventoryEvent()
{
if(RemoveSelectedItemFromInventoryEvent != null)
{
RemoveSelectedItemFromInventoryEvent();
}
}
(4)优化GridPropertiesManager.cs脚本
添加变量1:
private Transform cropParentTransform;
修改AfterSceneLoaded函数,添加如下代码:
if(GameObject.FindGameObjectWithTag(Tags.CropsParentTransform) != null)
{
cropParentTransform = GameObject.FindGameObjectWithTag(Tags.CropsParentTransform).transform;
}
else
{
cropParentTransform = null;
}
添加变量2:
[SerializeField] private SO_CropDetailList so_CropDetailList = null;
修改ClearDisplayGridPropertyDetails函数,添加如下代码:
private void ClearDisplayAllPlantedCrops()
{
// Destory all crops in scene
Crop[] cropArray;
cropArray = FindObjectsOfType<Crop>();
foreach(Crop crop in cropArray)
{
Destroy(crop.gameObject);
}
}
private void ClearDisplayGridPropertyDetails()
{
ClearDisplayGroundDecorations();
ClearDisplayAllPlantedCrops();
}
修改DisplayGridPropertyDetails函数,代码如下:
private void DisplayGridPropertyDetails()
{
// Loop throught all grid items
foreach(KeyValuePair<string, GridPropertyDetails> item in gridPropertyDictionary)
{
GridPropertyDetails gridPropertyDetails = item.Value;
DisplayDugGround(gridPropertyDetails);
DisplayWateredGround(gridPropertyDetails);
DisplayPlantedCrop(gridPropertyDetails);
}
}
public void DisplayPlantedCrop(GridPropertyDetails gridPropertyDetails)
{
if(gridPropertyDetails.seedItemCode > -1)
{
// get crop details
CropDetails cropDetails = so_CropDetailList.GetCropDetails(gridPropertyDetails.seedItemCode);
// prefab to use
GameObject cropPrefab;
// instantiate crop prefab at grid location
int growthStages = cropDetails.growthDays.Length;
int currentGrowthStage = 0;
int daysCounter = cropDetails.totalGrowthDays;
// 找出目前所处的成长阶段
for(int i = growthStages - 1; i >= 0; i--)
{
if(gridPropertyDetails.growthDays >= daysCounter)
{
currentGrowthStage = i;
break;
}
daysCounter = daysCounter - cropDetails.growthDays[i];
}
cropPrefab = cropDetails.growthPrefab[currentGrowthStage];
Sprite growthSprite = cropDetails.growthSprite[currentGrowthStage];
Vector3 worldPosition = groundDecoration2.CellToWorld(new Vector3Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY, 0));
worldPosition = new Vector3(worldPosition.x + Settings.gridCellSize / 2, worldPosition.y, worldPosition.z);
GameObject cropInstance = Instantiate(cropPrefab, worldPosition, Quaternion.identity);
cropInstance.GetComponentInChildren<SpriteRenderer>().sprite = growthSprite;
cropInstance.transform.SetParent(cropParentTransform);
cropInstance.GetComponent<Crop>().cropGridPosition = new Vector2Int(gridPropertyDetails.gridX, gridPropertyDetails.gridY);
}
}
修改AdvanceDay函数,代码如下:
private void AdvanceDay(int gameYear, Season gameSeason, int gameDay, string gameDayyOfWeek, int gameHour, int gameMinute, int gameSecond)
{
// Clear Display All Grid Property Details
ClearDisplayGridPropertyDetails();
// loop through all scenes - by looping through all gridproperty in the array
foreach(SO_GridProperties so_GridProperties in so_gridPropertiesArray)
{
// Get gridpropertydetails dictionary for scene
if(GameObjectSave.sceneData.TryGetValue(so_GridProperties.sceneName.ToString(), out SceneSave sceneSave))
{
if(sceneSave.gridPropertyDetailsDictionary != null)
{
for(int i = sceneSave.gridPropertyDetailsDictionary.Count - 1; i >= 0; i--)
{
KeyValuePair<string, GridPropertyDetails> item = sceneSave.gridPropertyDetailsDictionary.ElementAt(i);
GridPropertyDetails gridPropertyDetails = item.Value;
#region Update all grid properties to reflect the advance in the day
// if a crop is planted
if(gridPropertyDetails.growthDays > -1)
{
gridPropertyDetails.growthDays += 1;
}
// if ground is watered, then clear water
if(gridPropertyDetails.daysSinceWatered > -1)
{
gridPropertyDetails.daysSinceWatered = -1;
}
// Set gridpropertydetails
SetGridPropertyDetails(gridPropertyDetails.gridX, gridPropertyDetails.gridY, gridPropertyDetails, sceneSave.gridPropertyDetailsDictionary);
#endregion Update all grid properties to reflect the advance in the day
}
}
}
}
// Display grid property details to reflect changed values
DisplayGridPropertyDetails();
}
(5)优化Player.cs脚本
优化ProcessPlayerClickInput函数,代码如下:
修改ProcessPlayerClickInputSeed函数如下:
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();
}
}
添加PlantSeedAtCursor函数如下:
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();
}
(6)优化UIInventorySlot.cs脚本
修改OnEnable代码,
private void OnEnable()
{
EventHandler.AfterSceneLoadEvent += SceneLoaded;
EventHandler.RemoveSelectedItemFromInventoryEvent += RemoveSelectedItemFromInventory;
EventHandler.DropSelectedItemEvent += DropSelectedItemAtMousePosition;
}
修改OnDisable代码:
private void OnDisable()
{
EventHandler.AfterSceneLoadEvent -= SceneLoaded;
EventHandler.RemoveSelectedItemFromInventoryEvent -= RemoveSelectedItemFromInventory;
EventHandler.DropSelectedItemEvent -= DropSelectedItemAtMousePosition;
}
添加RemoveSelectedItemFromInventory函数代码:
private void RemoveSelectedItemFromInventory()
{
if(itemDetails != null && isSelected)
{
int itemCode = itemDetails.itemCode;
// Remove item from players inventory
InventoryManager.Instance.RemoveItem(InventoryLocation.player, itemCode);
// If no more of iitem then clear selected
if(InventoryManager.Instance.FindItemInInventory(InventoryLocation.player, itemCode) == -1)
{
ClearSelectedItem();
}
}
}
(7)补充配置信息
配置GridPropertiesManager的So_Crop Detail List信息如下:
7、运行游戏
先用Hoe挖一块地
然后洒下Parsnip的种子
按下G键可以加速时间,此时就可以看到防风草生长的过程。