一、导入和整理资源
1、把导入资源拖到Import Assets文件夹中
2、打开Assets-Import Assets-LowPolyFarmLite-Prefabs文件夹,找到WateringCan,制作WateringCan.png,保存到Import Assets-UI文件夹中,更改Texture Type。Apply
3、打开Asset-Data-Tools,选择WateringCan,更改缩略图
4、打开Assets-Import Assets-LowPolyFarmLite-Prefabs文件夹,编辑Cabbage_01预制体
(1) 复制Cabbage_01下的子物体
(2) 粘贴到Hierarchy面板上,重命名为Cabbage
(3) 将Hierarchy面板上的Cabbage拖到Asset-Prefabs文件夹中制成预制体
5、打开Asset-Data-Items,选择Cabbage,设置GameModle
6、把Standard Asset文件夹拖到Cartoon_Farm_Crops文件夹中
7、再把Cartoon_Farm_Crops文件夹转移到Assets-Import Assets文件夹中
8、打开Assets-Import Assets-Cartoon_Farm_Crops-Prefabs-Standard文件夹,看到各种素材果实
9、复制Carrot_Plant重命名为Cabbage Seedling,将这个预制体转移到Asset-Prefabs文件夹中
二、创建一个作物
1、Create Empty,重命名为Crop,Reset它的Transform
2、给Crop添加子物体Dirt_Pile、Cabbage Seeding和Cabbage,表示卷心菜的三个阶段
3、将Dirt_Pile重命名为Seed,将Crop制成预制体,把相关的预制体都放在新建的文件夹Crops中
4、编辑Crop预制体中的Seed
5、编辑Crop预制体中的Cabbage Seeding预制体
6、取消勾选
7、编辑SeedData.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Items/Seed")]
public class SeedData : ItemData
{
public int daysToGrow;
public ItemData cropToYield;
//幼苗
public GameObject seedling;
}
8、设置Item对象Cabbage Seed
9、删除Crop预制体中的子物体
三、作物生长的状态
1、在Scripts文件夹中新建 Farming 文件夹,将Land.cs拖入Farming文件夹中
2、给预制体Crop添加CropBehaviour.cs组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CropBehaviour : MonoBehaviour
{
//作物生长的状态
SeedData seedToGrow;
[Header("Stages of Life")]
public GameObject Seed;
private GameObject seedling;
private GameObject harvestable;
public enum CropState { Seed, Seedling, Havestable }
//作物初始化,播下一粒种子时
public void Plant(SeedData seedToGrow)
{
//储存种子的状态
this.seedToGrow = seedToGrow;
}
public void Grow()
{
}
}
3、赋值
4、更改ItemData.cs的一个拼写错误
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName ="Items/Item")]
public class ItemData : ScriptableObject
{
public string description;
public Sprite thumbnail;
public GameObject gameModel;//原有的gameModle,拼写有误
}
5、设置作物的生长状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CropBehaviour : MonoBehaviour
{
SeedData seedToGrow;
[Header("Stages of Life")]
public GameObject seed;
private GameObject seedling;
private GameObject harvestable;
public enum CropState { Seed, Seedling, Harvestable }
//当前作物的生长状态
public CropState cropState;
public void Plant(SeedData seedToGrow)
{
// 将传入的种子数据赋值给seedToGrow
this.seedToGrow = seedToGrow;
//在transform下实例化幼苗和可收获的游戏对象
seedling = Instantiate(seedToGrow.seedling, transform);
//获取ItemData对象
ItemData cropToYield = seedToGrow.cropToYield;
//实例化该ItemData对象
harvestable = Instantiate(cropToYield.gameModel, transform);
//初始状态设为Seed
SwitchState(CropState.Seed);
}
public void Grow()
{
}
//根据作物的生长状态来切换显示不同阶段的游戏对象
void SwitchState(CropState stateToSwitch)
{
seed.SetActive(false);
seedling.SetActive(false);
harvestable.SetActive(false);
switch (stateToSwitch)
{
case CropState.Seed: seed.SetActive(true); break;
case CropState.Seedling: seedling.SetActive(true); break;
case CropState.Harvestable: harvestable.SetActive(true); break;
}
// 更新作物的生长状态
cropState = stateToSwitch;
}
}
四、播种
1、编辑Land.cs,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Land : MonoBehaviour, ITimeTracker
{
public enum LandStatus { Soil, Farmland, Watered }
public LandStatus landStatus;
public Material soilMat, farmlandMat, wateredMat;
new Renderer renderer;
public GameObject select;
GameTimestamp timeWatered;
//为Land分配Crop预制体,初始化cropPlanted
[Header("Crops")]
public GameObject cropPrefab;
CropBehaviour cropPlanted = null;
void Start()
{
renderer = GetComponent<Renderer>();
SwitchLandStatus(LandStatus.Soil);
//select.SetActive(false);
Select(false);
TimeManager.Instance.RegisterTracker(this);
}
public void SwitchLandStatus(LandStatus statusToSwitch)
{
landStatus = statusToSwitch;
Material materialToSwitch = soilMat;
switch (statusToSwitch)
{
case LandStatus.Soil: materialToSwitch = soilMat; break;
case LandStatus.Farmland: materialToSwitch = farmlandMat; break;
case LandStatus.Watered:
materialToSwitch = wateredMat;
timeWatered = TimeManager.Instance.GetGameTimestamp();
break;
}
renderer.material = materialToSwitch;
}
public void Select(bool toggle)
{
select.SetActive(toggle);
}
public void Interact()
{
ItemData toolSlot = InventoryManager.Instance.equippedTool;
EquipmentData equipmentTool = toolSlot as EquipmentData;
if (equipmentTool != null)
{
EquipmentData.ToolType toolType = equipmentTool.toolType;
switch (toolType)
{
case EquipmentData.ToolType.Hoe:
SwitchLandStatus(LandStatus.Farmland); break;
case EquipmentData.ToolType.WateringCan:
SwitchLandStatus(LandStatus.Watered); break;
}
//增加
return;
}
//播种
SeedData seedTool = toolSlot as SeedData;
if (seedTool != null && landStatus !=LandStatus.Soil && cropPlanted==null)
{
GameObject cropObject =Instantiate(cropPrefab ,transform);
}
}
public void ClockUpdate(GameTimestamp timestamp)
{
if (landStatus == LandStatus.Watered)
{
int hoursElapsed = GameTimestamp.CompareTimestamp(timeWatered, timestamp);
Debug.Log(hoursElapsed + "上次灌溉时间");
if (hoursElapsed > 24)
{
SwitchLandStatus(LandStatus.Farmland);
}
}
}
}
2、赋值
8、编辑种子的位置
(1) 打开Land预制体,添加Crop预制体
(2) 编辑大小和位置,观察种子的position的值
3、在Hierarchy面板上删除Crop预制体
4、编辑Land.cs,设置种子出现的位置
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Land : MonoBehaviour, ITimeTracker
{
public enum LandStatus { Soil, Farmland, Watered }
public LandStatus landStatus;
public Material soilMat, farmlandMat, wateredMat;
new Renderer renderer;
public GameObject select;
GameTimestamp timeWatered;
//为Land分配Crop预制体,初始化cropPlanted
[Header("Crops")]
public GameObject cropPrefab;
CropBehaviour cropPlanted = null;
void Start()
{
renderer = GetComponent<Renderer>();
SwitchLandStatus(LandStatus.Soil);
//select.SetActive(false);
Select(false);
TimeManager.Instance.RegisterTracker(this);
}
public void SwitchLandStatus(LandStatus statusToSwitch)
{
landStatus = statusToSwitch;
Material materialToSwitch = soilMat;
switch (statusToSwitch)
{
case LandStatus.Soil: materialToSwitch = soilMat; break;
case LandStatus.Farmland: materialToSwitch = farmlandMat; break;
case LandStatus.Watered:
materialToSwitch = wateredMat;
timeWatered = TimeManager.Instance.GetGameTimestamp();
break;
}
renderer.material = materialToSwitch;
}
public void Select(bool toggle)
{
select.SetActive(toggle);
}
public void Interact()
{
ItemData toolSlot = InventoryManager.Instance.equippedTool;
if (toolSlot == null)
{
return;
}
EquipmentData equipmentTool = toolSlot as EquipmentData;
if (equipmentTool != null)
{
EquipmentData.ToolType toolType = equipmentTool.toolType;
switch (toolType)
{
case EquipmentData.ToolType.Hoe:
SwitchLandStatus(LandStatus.Farmland); break;
case EquipmentData.ToolType.WateringCan:
SwitchLandStatus(LandStatus.Watered); break;
}
}
//播种
SeedData seedTool = toolSlot as SeedData;
if (seedTool != null && landStatus !=LandStatus.Soil && cropPlanted==null)
{
GameObject cropObject =Instantiate(cropPrefab ,transform);
//种子的位置
cropObject.transform.position = new Vector3(transform.position.x,0f,transform.position.z);
//幼苗
cropPlanted = cropObject.GetComponent<CropBehaviour>();
//播种
cropPlanted.Plant(seedTool);
}
}
public void ClockUpdate(GameTimestamp timestamp)
{
if (landStatus == LandStatus.Watered)
{
int hoursElapsed = GameTimestamp.CompareTimestamp(timeWatered, timestamp);
Debug.Log(hoursElapsed + "上次灌溉时间");
if (hoursElapsed > 24)
{
SwitchLandStatus(LandStatus.Farmland);
}
}
}
}
5、选中Hierarchy面板上的Manager,找到它的InventoryManager.cs组件上的Tools,赋值
6、删除Hierarchy面板上的Crop预制体
7、打开Crop预制体,删除子物体Seed的Mesh Collider组件
五、作物的生长
1、编辑CropBehaviour.cs,编辑作物生长的逻辑
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CropBehaviour : MonoBehaviour
{
SeedData seedToGrow;
[Header("Stages of Life")]
public GameObject Seed;
private GameObject seedling;
private GameObject harvestable;
//作物的生长
int growth;
int maxGrowth;
public enum CropState { Seed, Seedling, Harvestable }
public CropState cropState;
public void Plant(SeedData seedToGrow)
{
this.seedToGrow = seedToGrow;
seedling = Instantiate(seedToGrow.seedling, transform);
ItemData cropToYield = seedToGrow.cropToYield;
harvestable = Instantiate(cropToYield.gameModel, transform);
SwitchState(CropState.Seed);
}
public void Grow()
{
//植物生长天数
growth++;
//植物成熟
if (growth > maxGrowth)
{
SwitchState(CropState.Harvestable);
}
}
void SwitchState(CropState stateToSwitch)
{
Seed.SetActive(false);
seedling.SetActive(false);
harvestable.SetActive(false);
switch (stateToSwitch)
{
case CropState.Seed: Seed.SetActive(true); break;
case CropState.Seedling: seedling.SetActive(true); break;
case CropState.Harvestable: harvestable.SetActive(true); break;
}
cropState = stateToSwitch;
}
}
2、编辑Land.cs,灌溉后作物才生长
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Land : MonoBehaviour, ITimeTracker
{
public enum LandStatus { Soil, Farmland, Watered }
public LandStatus landStatus;
public Material soilMat, farmlandMat, wateredMat;
new Renderer renderer;
public GameObject select;
GameTimestamp timeWatered;
[Header("Crops")]
public GameObject cropPrefab;
CropBehaviour cropPlanted = null;
void Start()
{
renderer = GetComponent<Renderer>();
SwitchLandStatus(LandStatus.Soil);
//select.SetActive(false);
Select(false);
TimeManager.Instance.RegisterTracker(this);
}
public void SwitchLandStatus(LandStatus statusToSwitch)
{
landStatus = statusToSwitch;
Material materialToSwitch = soilMat;
switch (statusToSwitch)
{
case LandStatus.Soil: materialToSwitch = soilMat; break;
case LandStatus.Farmland: materialToSwitch = farmlandMat; break;
case LandStatus.Watered:
materialToSwitch = wateredMat;
timeWatered = TimeManager.Instance.GetGameTimestamp();
break;
}
renderer.material = materialToSwitch;
}
public void Select(bool toggle)
{
select.SetActive(toggle);
}
public void Interact()
{
ItemData toolSlot = InventoryManager.Instance.equippedTool;
//
if(toolSlot == null)
{
return;
}
EquipmentData equipmentTool = toolSlot as EquipmentData;
if (equipmentTool != null)
{
EquipmentData.ToolType toolType = equipmentTool.toolType;
switch (toolType)
{
case EquipmentData.ToolType.Hoe:
SwitchLandStatus(LandStatus.Farmland); break;
case EquipmentData.ToolType.WateringCan:
SwitchLandStatus(LandStatus.Watered); break;
}
}
SeedData seedTool = toolSlot as SeedData;
if (seedTool != null && landStatus !=LandStatus.Soil && cropPlanted==null)
{
GameObject cropObject =Instantiate(cropPrefab ,transform);
cropObject.transform.position = new Vector3(transform.position.x,0f,transform.position.z);
cropPlanted = cropObject.GetComponent<CropBehaviour>();
cropPlanted.Plant(seedTool);
}
}
public void ClockUpdate(GameTimestamp timestamp)
{
if (landStatus == LandStatus.Watered)
{
int hoursElapsed = GameTimestamp.CompareTimestamp(timeWatered, timestamp);
Debug.Log(hoursElapsed + "上次灌溉时间");
//增加作物生长的方法
if(cropPlanted != null)
{
cropPlanted.Grow();
}
if (hoursElapsed > 24)
{
SwitchLandStatus(LandStatus.Farmland);
}
}
}
}
3、编辑CropBehaviour.cs,编辑作物生长
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CropBehaviour : MonoBehaviour
{
SeedData seedToGrow;
[Header("Stages of Life")]
public GameObject seed;
private GameObject seedling, harvestable;
int growth,maxGrowth;
public enum CropState { Seed, Seedling, Harvestable }
public CropState cropState;
public void Plant(SeedData seedToGrow)
{
this.seedToGrow = seedToGrow;
seedling = Instantiate(seedToGrow.seedling, transform);
ItemData cropToYield = seedToGrow.cropToYield;
harvestable = Instantiate(cropToYield.gameModle, transform);
//作物生长需要的小时数
int hoursToGrow = GameTimestamp.DaysToHours(seedToGrow.daysToGrow);
//种子的最大生长时间,以分钟为单位
maxGrowth = GameTimestamp.HoursToMinutes(hoursToGrow);
SwitchState(CropState.Seed);
}
public void Grow()
{
growth++;
//幼苗
if (growth >= maxGrowth/2&&cropState==CropState.Seed)
{
SwitchState(CropState.Seedling);
}
//成熟
if (growth >= maxGrowth && cropState==CropState.Seedling)
{
SwitchState(CropState.Harvestable);
}
}
void SwitchState(CropState stateToSwitch)
{
seed.SetActive(false);
seedling.SetActive(false);
harvestable.SetActive(false);
switch (stateToSwitch)
{
case CropState.Seed:
seed.SetActive(true);
break;
case CropState.Seedling:
seedling.SetActive(true);
break;
case CropState.Harvestable:
harvestable.SetActive(true);
break;
}
cropState = stateToSwitch;
}
}
4、编辑预制体Crop,删除子物体Seed的Mesh Collider
未验证的作物生长的方法
使用一个变量存储上次浇水的时间,并在每次调用ClockUpdate
方法时更新它。然后,在ClockUpdate
方法中,你可以检查到上次浇水的时间是否超过了一定的时间间隔(例如24小时),如果是,则将landStatus
的状态更改为"Farmland"。并在每次检查landStatus
之后检查是否有作物种植,如果有,则执行cropPlanted.Grow()
方法。
示例代码:
private DateTime lastWateredTime;
private bool cropGrowing = false;
private int wateredHours = 0;
public void ClockUpdate(GameTimestamp timestamp)
{
if (landStatus == LandStatus.Watered)
{
int hoursElapsed = GameTimestamp.CompareTimestamp(lastWateredTime, timestamp);
wateredHours += hoursElapsed;
if (cropGrowing)
{
if (wateredHours >= 72)
{
// 一个完整的cropPlanted.Grow()时间是3天,已经达到时间,可以收获或执行其他操作
HarvestCrop();
cropGrowing = false;
}
}
else
{
if (hoursElapsed > 24)
{
SwitchLandStatus(LandStatus.Farmland);
}
else
{
cropGrowing = true;
cropPlanted.Grow();
}
}
}
else if (landStatus == LandStatus.Farmland)
{
// 在这里可以执行其他与Farmland状态相关的操作
}
}
public void WaterLand()
{
landStatus = LandStatus.Watered;
lastWateredTime = DateTime.Now;
wateredHours = 0;
}
public void HarvestCrop()
{
// 收获作物的操作
}
在这个示例中,lastWateredTime
变量用于存储上次浇水的时间,cropGrowing
变量用于跟踪作物是否正在生长。wateredHours
变量用于累计所有的landStatus
为"Watered"的时间。当作物开始生长时,将cropGrowing
设置为true
并调用cropPlanted.Grow()
方法,然后在达到72个小时的时间后,可以执行收获或其他操作。
每次调用ClockUpdate
方法时,会检查上次浇水的时间与当前时间的差值,并根据情况执行相应的操作。一旦土地状态为"Farmland",可以在相关的分支中执行与"Farmland"状态相关的操作。
六、收割作物
1、编辑CropBehaviour.cs,设置销毁作物
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CropBehaviour : MonoBehaviour
{
SeedData seedToGrow;
[Header("Stages of Life")]
public GameObject seed;
private GameObject seedling;
private GameObject harvestable;
//作物的生长
int growth;
int maxGrowth;
public enum CropState { Seed, Seedling, Harvestable }
public CropState cropState;
public void Plant(SeedData seedToGrow)
{
this.seedToGrow = seedToGrow;
seedling = Instantiate(seedToGrow.seedling, transform);
ItemData cropToYield = seedToGrow.cropToYield;
harvestable = Instantiate(cropToYield.gameModel, transform);
int hoursToGrow = GameTimestamp.DaysToHours(seedToGrow.daysToGrow);
maxGrowth = GameTimestamp.HoursToMinutes(hoursToGrow);
SwitchState(CropState.Seed);
}
public void Grow()
{
growth++;
if (growth >= maxGrowth/2 &&cropState == CropState.Seed)
{
SwitchState(CropState.Seedling);
}
if (growth >= maxGrowth && cropState == CropState.Seedling)
{
SwitchState(CropState.Harvestable);
}
}
void SwitchState(CropState stateToSwitch)
{
seed.SetActive(false);
seedling.SetActive(false);
harvestable.SetActive(false);
switch (stateToSwitch)
{
case CropState.Seed: seed.SetActive(true); break;
case CropState.Seedling: seedling.SetActive(true); break;
case CropState.Harvestable:
harvestable.SetActive(true);
//收割作物
harvestable.transform.parent = null;
Destroy(gameObject);
break;
}
cropState = stateToSwitch;
}
}
2、编辑Player预制体,以mixamorig6:RightHand为父物体,Create Empty,重命名为Hand Point
3、编辑InventoryManager.cs,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static InventorySlot;
public class InventoryManager : MonoBehaviour
{
public static InventoryManager Instance { get; private set; }
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(this);
}
else
{
Instance = this;
}
}
[Header("Tools")]
public ItemData[] tools = new ItemData[8];
public ItemData equippedTool = null;
[Header("Items")]
public ItemData[] items = new ItemData[8];
public ItemData equippedItem = null;
//收获作物的位置
public Transform handPoint;
public void InventoryToHand(int slotIndex, InventorySlot.InventoryType inventoryType)
{
if (inventoryType == InventorySlot.InventoryType.Item)
{
ItemData itemToEquip = items[slotIndex];
items[slotIndex] = equippedItem;
equippedItem = itemToEquip;
}
else
{
ItemData toolToEquip = tools[slotIndex];
tools[slotIndex] = equippedTool;
equippedTool = toolToEquip;
}
UIManager.Instance.RenderInventory();
}
public void HandToInventory(InventorySlot.InventoryType inventoryType)
{
if (inventoryType == InventorySlot.InventoryType.Item)
{
for (int i = 0; i < items.Length; i++)
{
if (items[i] == null)
{
items[i] = equippedItem;
equippedItem = null;
break;
}
}
}
else
{
for(int i = 0;i < tools.Length; i++)
{
if(tools[i] == null)
{
tools[i] = equippedTool;
equippedTool = null;
break;
}
}
}
UIManager.Instance.RenderInventory();
}
//收获作物(实例化作物)
public void RenderHand()
{
Instantiate(equippedItem.gameModel);
}
}
4、赋值(Hand Point)
5、编辑InventoryManager.cs,点击装备卷心菜时,角色手持卷心菜,点击装备栏时,卷心菜回到物品栏
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static InventorySlot;
public class InventoryManager : MonoBehaviour
{
public static InventoryManager Instance { get; private set; }
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(this);
}
else
{
Instance = this;
}
}
[Header("Tools")]
public ItemData[] tools = new ItemData[8];
public ItemData equippedTool = null;
[Header("Items")]
public ItemData[] items = new ItemData[8];
public ItemData equippedItem = null;
//收获作物的位置
public Transform handPoint;
public void InventoryToHand(int slotIndex, InventorySlot.InventoryType inventoryType)
{
if (inventoryType == InventorySlot.InventoryType.Item)
{
ItemData itemToEquip = items[slotIndex];
items[slotIndex] = equippedItem;
equippedItem = itemToEquip;
//调用收割作物的方法
RenderHand();
}
else
{
ItemData toolToEquip = tools[slotIndex];
tools[slotIndex] = equippedTool;
equippedTool = toolToEquip;
}
UIManager.Instance.RenderInventory();
}
public void HandToInventory(InventorySlot.InventoryType inventoryType)
{
if (inventoryType == InventorySlot.InventoryType.Item)
{
for (int i = 0; i < items.Length; i++)
{
if (items[i] == null)
{
items[i] = equippedItem;
equippedItem = null;
break;
}
}
//调用收割作物的方法
RenderHand();
}
else
{
for(int i = 0;i < tools.Length; i++)
{
if(tools[i] == null)
{
tools[i] = equippedTool;
equippedTool = null;
break;
}
}
}
UIManager.Instance.RenderInventory();
}
//收获作物(实例化作物)
public void RenderHand()
{
//若手持有作物(之前实例化了作物)
if(handPoint.childCount>0)
{
//销毁之前实例化的作物
Destroy(handPoint.GetChild(0).gameObject);
}
if(equippedItem != null)
{
//作物的位置
Instantiate(equippedItem.gameModel, handPoint);
}
}
}
七、交互
1、编辑PlayerInteractor.cs,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerInteractor : MonoBehaviour
{
PlayerController playerController;
Land selectLand = null;
void Start()
{
playerController = transform.parent.GetComponent<PlayerController>();
}
void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, Vector3.down, out hit, 1))
{
OnInteractableHit(hit);
}
}
void OnInteractableHit(RaycastHit hit)
{
Collider other = hit.collider;
if (other != null && other.CompareTag("Land"))
{
Land land = other.GetComponent<Land>();
if (land != null)
{
SelectLand(land);
return;
}
else { Debug.Log("没选中任何田地"); }
}
if (selectLand != null)
{
DeselectLand(selectLand);
}
}
void SelectLand(Land land)
{
if (land == null)
{
Debug.Log("你没有选择田地!");
return;
}
if (selectLand != null)
{
selectLand.Select(false);
}
selectLand = land;
land.Select(true);
}
void DeselectLand(Land land)
{
if (land == null)
{
Debug.Log("你没有选择田地!");
return;
}
land.Select(false);
selectLand = null;
}
public void Interact()
{
if(selectLand != null)
{
selectLand.Interact();
return;
}
Debug.Log("没站在田地里");
}
//收割交互
public void ItemInteract()
{
//如果角色手持物品,物品返回物品栏
if(InventoryManager.Instance.equippedItem != null)
{
InventoryManager.Instance.HandToInventory(InventorySlot.InventoryType.Item);
return;
}
}
}
2、编辑PlayerController.cs,点击 左Alt键,物品返回物品栏
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
PlayerInteractor playerInteractor;
private CharacterController controller;
private Animator animator;
private float moveSpeed = 1f;
[Header("Movement System")]
public float walkSpeed = 1f;
public float runSpeed = 3f;
void Start()
{
controller = GetComponent<CharacterController>();
animator = GetComponent<Animator>();
//从当前脚本挂载的物体的子物体上获取PlayerInteractor.cs组件
playerInteractor = GetComponentInChildren<PlayerInteractor>();
}
void Update()
{
Move();
//持续运行Interact方法
Interact();
if (Input.GetKey(KeyCode.RightBracket))
{
TimeManager.Instance.Tick();
}
}
public void Interact()
{
if (Input.GetButtonDown("Fire1"))
{
playerInteractor.Interact();
}
//点击左Alt
if (Input.GetButtonDown("Fire2"))
{
playerInteractor.ItemInteract();
}
}
public void Move()
{
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
Vector3 dir = new Vector3(horizontal, 0, vertical).normalized;
Vector3 velocity = moveSpeed * Time.deltaTime * dir;
if (Input.GetButton("Sprint"))
{
moveSpeed = runSpeed;
animator.SetBool("Running",true);
}
else
{
moveSpeed = walkSpeed;
animator.SetBool("Running", false);
}
if (dir.magnitude > 0.1f)
{
transform.rotation = Quaternion.LookRotation(dir);
controller.Move(velocity);
}
animator.SetFloat("Speed", velocity.magnitude);
}
}
3、在Scripts-Inventory文件夹下新建InteractableObject.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InteractableObject : MonoBehaviour
{
//游戏对象应该表示的项目信息
public ItemData item;
public void Pickup()
{
InventoryManager.Instance.equippedItem = item;
InventoryManager.Instance.RenderHand();
Destroy(gameObject);
}
}
4、给预制体Cabbage,添加InteractableObject.cs组件,赋值,添加标签Item
给预制体Cabbage添加Collider
5、编辑PlayerInteractor.cs,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerInteractor : MonoBehaviour
{
PlayerController playerController;
Land selectLand = null;
//选择的收获作物
InteractableObject selectedInteractable = null;
void Start()
{
playerController = transform.parent.GetComponent<PlayerController>();
}
void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, Vector3.down, out hit, 1))
{
OnInteractableHit(hit);
}
}
void OnInteractableHit(RaycastHit hit)
{
Collider other = hit.collider;
if (other != null && other.CompareTag("Land"))
{
Land land = other.GetComponent<Land>();
if (land != null)
{
SelectLand(land);
return;
}
}
//
if (other.CompareTag("Item"))
{
//将接触到的作物的InteractableObject组件赋值给selectedInteractable(选择收获的作物)
selectedInteractable = other.GetComponent<InteractableObject>();
return;
}
//取消选择
if(selectedInteractable != null)
{
selectedInteractable = null;
}
//有更改
if (selectLand != null)
{
selectLand.Select(false);
selectLand = null;
}
}
void SelectLand(Land land)
{
if (land == null)
{
Debug.Log("你没有选择田地!");
return;
}
if (selectLand != null)
{
selectLand.Select(false);
}
selectLand = land;
land.Select(true);
}
public void Interact()
{
if(selectLand != null)
{
selectLand.Interact();
return;
}
Debug.Log("没站在田地里");
}
//收割交互
public void ItemInteract()
{
//如果角色手持物品,返回物品栏
if(InventoryManager.Instance.equippedItem != null)
{
InventoryManager.Instance.HandToInventory(InventorySlot.InventoryType.Item);
return;
}
//捡起作物
if(selectedInteractable != null)
{
selectedInteractable.Pickup();
}
}
}
6、编辑PlayerInteractor.cs,手持卷心菜时禁用工具
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerInteractor : MonoBehaviour
{
PlayerController playerController;
Land selectLand = null;
//选择的收获作物
InteractableObject selectedInteractable = null;
void Start()
{
playerController = transform.parent.GetComponent<PlayerController>();
}
void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, Vector3.down, out hit, 1))
{
OnInteractableHit(hit);
}
}
void OnInteractableHit(RaycastHit hit)
{
Collider other = hit.collider;
if (other != null && other.CompareTag("Land"))
{
Land land = other.GetComponent<Land>();
if (land != null)
{
SelectLand(land);
return;
}
}
//
if (other.CompareTag("Item"))
{
selectedInteractable = other.GetComponent<InteractableObject>();
return;
}
if(selectedInteractable != null)
{
selectedInteractable = null;
}
//有更改
if (selectLand != null)
{
selectLand.Select(false);
selectLand = null;
}
}
void SelectLand(Land land)
{
if (land == null)
{
Debug.Log("你没有选择田地!");
return;
}
if (selectLand != null)
{
selectLand.Select(false);
}
selectLand = land;
land.Select(true);
}
public void Interact()
{
//手持收获作物时,禁用工具
if(InventoryManager.Instance.equippedItem != null)
{
return;
}
if(selectLand != null)
{
selectLand.Interact();
return;
}
Debug.Log("没站在田地里");
}
public void ItemInteract()
{
if(InventoryManager.Instance.equippedItem != null)
{
InventoryManager.Instance.HandToInventory(InventorySlot.InventoryType.Item);
return;
}
if(selectedInteractable != null)
{
selectedInteractable.Pickup();
}
}
}
八、禁止角色上升
1、编辑预制体Cabbage,添加新Layer
2、添加另一Layer,上右图
3、编辑预制体Player
4、禁用Item与Player层的交互
九、制作番茄预制体
1、编辑SeedData.cs,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Items/Seed")]
public class SeedData : ItemData
{
public int daysToGrow;
public ItemData cropToYield;
//幼苗
public GameObject seedling;
//重新生长
[Header("Regrowable")]
public bool regrowable;
public int daysTogrow;
}
2、打开Data-Item文件夹,Create-Items-Item ,创建另一种植物 Tomato,
Technically a fruit
3、打开Data-Tools-Seeds文件夹,Create-Items-Seed,创建另一种植物的种子Tomato Seed
Seeds to grow Tomato
4、打开文件夹,将Tomato_Plant拖放到Hierarchy,重命名为Tomato Seedling
5、将Tomato_Fruit拖放到Hierarchy,更改标签为Item,Layer为Item,Transform,名为Tomato
6、 给Tomato_Fruit添加InteractableObject.cs组件,赋值(见上图)
7、将Tomato制成预制体,删除Hierarchy面板上的Tomato。
8、移除Tomato_Seedling上的Mesh Collider,结果如图。制成预制体
9、设置Tomato Seed
10、复制Hierarchy面板上的Tomato_Seedling,重命名为Tomato Harvestable,制成预制体
11、编辑Tomato Harvestable预制体,
(1) 添加子物体Tomato
(2) 更改标签和Layer为Item
(3) 为Tomato Harvestable预制体添加 Mesh Collider组件
(4) 删除Hierarchy面板上的Tomato_Seedling和Tomato Harvestable
十、播种和收获番茄
1、打开Data-Item文件夹,Create-Items-Item ,创建一个新项目 Tomato Crop,
The crop the player will harvest first
2、打开Data-Tools-Seed文件夹,设置Tomato Seed
3、选中Hierarchy面板上的Manager,设置它的InventoryManager.cs组件中的番茄种子
4、测试(9天)
5、打开Farming文件夹,新建RegrowableHarvestBehaviour.cs
6、编辑Tomato Harvestable预制体,添加RegrowableHarvestBehaviour.cs组件
7、编辑InteractableObject.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InteractableObject : MonoBehaviour
{
public ItemData item;
//增加virtual以便重写
public virtual void Pickup()
{
InventoryManager.Instance.equippedItem = item;
InventoryManager.Instance.RenderHand();
Destroy(gameObject);
}
}
8、编辑CropBehaviour.cs,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CropBehaviour : MonoBehaviour
{
SeedData seedToGrow;
[Header("Stages of Life")]
public GameObject seed;
private GameObject seedling;
private GameObject harvestable;
int growth;
int maxGrowth;
public enum CropState { Seed, Seedling, Harvestable }
public CropState cropState;
public void Plant(SeedData seedToGrow)
{
this.seedToGrow = seedToGrow;
seedling = Instantiate(seedToGrow.seedling, transform);
ItemData cropToYield = seedToGrow.cropToYield;
harvestable = Instantiate(cropToYield.gameModel, transform);
int hoursToGrow = GameTimestamp.DaysToHours(seedToGrow.daysToGrow);
maxGrowth = GameTimestamp.HoursToMinutes(hoursToGrow);
//第一次种植时,检查它是否能生长
if (seedToGrow.regrowable)
{
}
SwitchState(CropState.Seed);
}
public void Grow()
{
growth++;
if (growth >= maxGrowth/2 &&cropState == CropState.Seed)
{
SwitchState(CropState.Seedling);
}
if (growth >= maxGrowth && cropState == CropState.Seedling)
{
SwitchState(CropState.Harvestable);
}
}
void SwitchState(CropState stateToSwitch)
{
seed.SetActive(false);
seedling.SetActive(false);
harvestable.SetActive(false);
switch (stateToSwitch)
{
case CropState.Seed: seed.SetActive(true); break;
case CropState.Seedling: seedling.SetActive(true); break;
case CropState.Harvestable:
harvestable.SetActive(true);
harvestable.transform.parent = null;
Destroy(gameObject);
break;
}
cropState = stateToSwitch;
}
}
9、编辑RegrowableHarvestBehaviour.cs,创建一个具有可重生收获行为的对象
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//改变继承
public class RegrowableHarvestBehaviour : InteractableObject
{
//存储一个作物的引用
CropBehaviour parentCrop;
//接受一个"CropBehaviour"对象作为参数,将这个对象设置为"parentCrop"的值
public void SetParent(CropBehaviour parentCrop)
{
//将一个CropBehaviour对象设置为当前RegrowableHarvestBehaviour对象的父级对象
this.parentCrop = parentCrop;
}
public override void Pickup()
{
InventoryManager.Instance.equippedItem = item;
InventoryManager.Instance.RenderHand();
}
}
赋值
10、编辑CropBehaviour.cs,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CropBehaviour : MonoBehaviour
{
SeedData seedToGrow;
[Header("Stages of Life")]
public GameObject seed;
private GameObject seedling;
private GameObject harvestable;
int growth;
int maxGrowth;
public enum CropState { Seed, Seedling, Harvestable }
public CropState cropState;
public void Plant(SeedData seedToGrow)
{
this.seedToGrow = seedToGrow;
seedling = Instantiate(seedToGrow.seedling, transform);
ItemData cropToYield = seedToGrow.cropToYield;
harvestable = Instantiate(cropToYield.gameModel, transform);
int hoursToGrow = GameTimestamp.DaysToHours(seedToGrow.daysToGrow);
maxGrowth = GameTimestamp.HoursToMinutes(hoursToGrow);
//第一次种植时,检查它是否能生长
if (seedToGrow.regrowable)
{
//从游戏对象获取 RegrowableHarvestBehaviour
RegrowableHarvestBehaviour regrowableHarvest = harvestable.GetComponent<RegrowableHarvestBehaviour>();
//初始化可收获项(将"this"对象作为当前对象的父级对象)
regrowableHarvest.SetParent(this);
}
SwitchState(CropState.Seed);
}
public void Grow()
{
growth++;
if (growth >= maxGrowth/2 &&cropState == CropState.Seed)
{
SwitchState(CropState.Seedling);
}
if (growth >= maxGrowth && cropState == CropState.Seedling)
{
SwitchState(CropState.Harvestable);
}
}
void SwitchState(CropState stateToSwitch)
{
seed.SetActive(false);
seedling.SetActive(false);
harvestable.SetActive(false);
switch (stateToSwitch)
{
case CropState.Seed: seed.SetActive(true); break;
case CropState.Seedling: seedling.SetActive(true); break;
case CropState.Harvestable:
harvestable.SetActive(true);
//如果种子不可再生,将可收获的种子与该作物游戏对象分离并销毁它。
if (!seedToGrow.regrowable)
{
//作物(harvestable)的父级对象为null
harvestable.transform.parent = null;
Destroy(gameObject);
}
break;
}
cropState = stateToSwitch;
}
//当玩家收获可再生作物时调用。将状态重置为幼苗
public void Regrow()
{
//重置生长点
//获取再生时间(以小时为单位)
int hoursToRegrow = GameTimestamp.DaysToHours(seedToGrow.daysToGrow);
growth = maxGrowth-GameTimestamp.HoursToMinutes(hoursToRegrow);
//将状态切换回幼苗状态
SwitchState(CropState.Seedling);
}
}
11、编辑RegrowableHarvestBehaviour.cs,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//改变继承
public class RegrowableHarvestBehaviour : InteractableObject
{
//存储一个作物的引用
CropBehaviour parentCrop;
//接受一个"CropBehaviour"对象作为参数,将这个对象设置为"parentCrop"的值
public void SetParent(CropBehaviour parentCrop)
{
//将一个CropBehaviour对象设置为当前RegrowableHarvestBehaviour对象的父级对象
this.parentCrop = parentCrop;
}
public override void Pickup()
{
InventoryManager.Instance.equippedItem = item;
//更新场景中的更改
InventoryManager.Instance.RenderHand();
//将亲本作物重置为幼苗以种植它
parentCrop.Regrow();
}
}
12、 打开Data-Item文件夹,设置Tomato Crop的模型
十一、枯萎系统
1、制作枯萎的植物
(1) 打开Assets-Import Assets-Farmland-Dirt-Materials,复制Dirt材质,重命名为Wilted Plant
(2) 更改Wilted Plant材质的颜色9A2603,去掉光滑度
2、编辑Crop预制体
(1) 打开Assets-Import Assets-Cartoon_Farm_Crops-Prefabs-Standard文件夹,将Pumpkin_Plant作为子物体拖放到Crop下,重命名为wilted,将Wilted Plant材质设置给Wilted
(2) 设置它的transform
3、编辑CropBehaviour.cs,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CropBehaviour : MonoBehaviour
{
SeedData seedToGrow;
[Header("Stages of Life")]
public GameObject seed;
//添加枯萎植株
public GameObject wilted;
private GameObject seedling;
private GameObject harvestable;
int growth;
int maxGrowth;
//增加枯萎时间(植株正常生长的最大时间为48小时)
int maxHealth = GameTimestamp.HoursToMinutes(48);
int health;
//添加枯萎类
public enum CropState { Seed, Seedling, Harvestable, Wilted }
public CropState cropState;
public void Plant(SeedData seedToGrow)
{
this.seedToGrow = seedToGrow;
seedling = Instantiate(seedToGrow.seedling, transform);
ItemData cropToYield = seedToGrow.cropToYield;
harvestable = Instantiate(cropToYield.gameModel, transform);
int hoursToGrow = GameTimestamp.DaysToHours(seedToGrow.daysToGrow);
maxGrowth = GameTimestamp.HoursToMinutes(hoursToGrow);
if (seedToGrow.regrowable)
{
RegrowableHarvestBehaviour regrowableHarvest = harvestable.GetComponent<RegrowableHarvestBehaviour>();
regrowableHarvest.SetParent(this);
}
SwitchState(CropState.Seed);
}
public void Grow()
{
growth++;
//植株枯萎前的健康时长
if (health < maxHealth)
{
health++;
}
if (growth >= maxGrowth / 2 && cropState == CropState.Seed)
{
SwitchState(CropState.Seedling);
}
if (growth >= maxGrowth && cropState == CropState.Seedling)
{
SwitchState(CropState.Harvestable);
}
}
//枯萎逻辑
public void Wilter()
{
health--;
if (health <= 0 && cropState != CropState.Seed)
{
SwitchState(CropState.Wilted);
}
}
void SwitchState(CropState stateToSwitch)
{
seed.SetActive(false);
seedling.SetActive(false);
harvestable.SetActive(false);
//初始不显示
wilted.SetActive(false);
switch (stateToSwitch)
{
case CropState.Seed: seed.SetActive(true); break;
case CropState.Seedling:
seedling.SetActive(true);
//将作物的健康度重置为最初的状态(有48小时的正常生长状态
//48小时以后作物会枯萎
health = maxHealth; break;
case CropState.Harvestable:
harvestable.SetActive(true);
if (!seedToGrow.regrowable)
{
harvestable.transform.parent = null;
Destroy(gameObject);
}
break;
//增加选择枯萎植株选项
case CropState Wilted: wilted.SetActive(true); break;
}
cropState = stateToSwitch;
}
public void Regrow()
{
int hoursToRegrow = GameTimestamp.DaysToHours(seedToGrow.daysToGrow);
growth = maxGrowth - GameTimestamp.HoursToMinutes(hoursToRegrow);
SwitchState(CropState.Seedling);
}
}
赋值
4、编辑Land.cs
public void ClockUpdate(GameTimestamp timestamp)
{
if (landStatus == LandStatus.Watered)
{
int hoursElapsed = GameTimestamp.CompareTimestamp(timeWatered, timestamp);
if(cropPlanted != null)
{
cropPlanted.Grow();
}
if (hoursElapsed > 24)
{
SwitchLandStatus(LandStatus.Farmland);
}
}
//当土壤不是处于灌溉状态时,处理植物枯萎
if(landStatus != LandStatus.Watered && cropPlanted != null)
{
//已经发芽且开始枯萎
if(cropPlanted.cropState != CropBehaviour.CropState.Seed)
{
cropPlanted.Wilter();
}
}
}
十二、清除枯萎植株
1、创建清除枯萎植株的工具(ItemData对象)命名为shovel
2、编辑EquipmentData.cs
[CreateAssetMenu(menuName = "Items/Equipment")]
public class EquipmentData : ItemData
{
public enum ToolType
{
//添加铲子工具
Hoe, WateringCan, Axe, Pickaxe, Shovel
}
public ToolType toolType;
}
3、设置shovel,Clear plant material
4、选中Hierarchy面板上的Manger,找到组件InventoryManager.cs,赋值
5、编辑Land.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Land : MonoBehaviour, ITimeTracker
{
public enum LandStatus { Soil, Farmland, Watered }
public LandStatus landStatus;
public Material soilMat, farmlandMat, wateredMat;
new Renderer renderer;
public GameObject select;
GameTimestamp timeWatered;
[Header("Crops")]
public GameObject cropPrefab;
CropBehaviour cropPlanted = null;
void Start()
{
renderer = GetComponent<Renderer>();
SwitchLandStatus(LandStatus.Soil);
//select.SetActive(false);
Select(false);
TimeManager.Instance.RegisterTracker(this);
}
public void SwitchLandStatus(LandStatus statusToSwitch)
{
landStatus = statusToSwitch;
Material materialToSwitch = soilMat;
switch (statusToSwitch)
{
case LandStatus.Soil: materialToSwitch = soilMat; break;
case LandStatus.Farmland: materialToSwitch = farmlandMat; break;
case LandStatus.Watered:
materialToSwitch = wateredMat;
timeWatered = TimeManager.Instance.GetGameTimestamp();
break;
}
renderer.material = materialToSwitch;
}
public void Select(bool toggle)
{
select.SetActive(toggle);
}
public void Interact()
{
ItemData toolSlot = InventoryManager.Instance.equippedTool;
if(toolSlot == null)
{
return;
}
EquipmentData equipmentTool = toolSlot as EquipmentData;
if (equipmentTool != null)
{
EquipmentData.ToolType toolType = equipmentTool.toolType;
switch (toolType)
{
case EquipmentData.ToolType.Hoe:
SwitchLandStatus(LandStatus.Farmland); break;
case EquipmentData.ToolType.WateringCan:
SwitchLandStatus(LandStatus.Watered); break;
//铲除作物
case EquipmentData.ToolType.Shovel:
if(cropPlanted != null)
{
Destroy(cropPlanted.gameObject);
}
break;
}
return;
}
SeedData seedTool = toolSlot as SeedData;
if (seedTool != null && landStatus !=LandStatus.Soil && cropPlanted==null)
{
GameObject cropObject =Instantiate(cropPrefab ,transform);
cropObject.transform.position = new Vector3(transform.position.x,0f,transform.position.z);
cropPlanted = cropObject.GetComponent<CropBehaviour>();
cropPlanted.Plant(seedTool);
}
}
public void ClockUpdate(GameTimestamp timestamp)
{
if (landStatus == LandStatus.Watered)
{
int hoursElapsed = GameTimestamp.CompareTimestamp(timeWatered, timestamp);
if(cropPlanted != null)
{
cropPlanted.Grow();
}
if (hoursElapsed > 24)
{
SwitchLandStatus(LandStatus.Farmland);
}
if(landStatus != LandStatus.Watered && cropPlanted != null)
{
if(cropPlanted.cropState != CropBehaviour.CropState.Seed)
{
cropPlanted.Wilter();
}
}
}
}