0基础项目全流程实战!Code Monkey 2023系列基础课程中文版持续更新中
课代表笔记
炉灶预制件
首先制作一个柜台,创建一个柜台的预制件变体,并拖入 StoveCounter_Visual。这里还需要复制一遍这一预制件变体,并为其中的 Counter、Stove 和 Frying Pan 换上 CounterSelected 偏白色的材质,用于表示对象被选中。
public class StoveCounter : BaseCounter {
[SerializeField] private FryingRecipeSO[] fryingRecipeSOArray;
public override void Interact (Player player) {
if (!HasKitchenObject()) {
// There is no KitchenObject here
if (player. HasKitchenObject()) {
// Player is carrying something
if (HasRecipeWithInput(player. GetKitchenObject().GetKitchenObjectSO())) {
// Player carrying something that can be Fried
player.GetKitchenObject().SetKitchenObjectParent(this);
fryingRecipeSO = GetFryingRecipeSOWithInput(GetKitchenObject().GetKitchenObjectSO());
}
} else {
// Player not carrying anything
}
} else {
// There is a KitchenObject here
if (player. HasKitchenObject ()) {
// Player is carrying something
} else {
// Player is not carrying anything
GetKitchenObject().SetKitchenObjectParent(player);
}
}
}
煎炸计时器有两种写法,一种是简单的浮点计时器,当计时器的时间大于此前设定的最大值,肉就煎好了:
private float fryingTimer;
private FryingRecipeSO fryingRecipeSO;
private void Update() {
if (HasKitchenObject()) {
fryingTimer += Time.deltaTime;
if (fryingTimer > fryingRecipeSO.fryingTimerMax) {
// Fried
fryingTimer = of;
Debug.Log("Fried!");
GetKitchenObject().DestroySelf();
KitchenObject.SpawnKitchenObject(fryingRecipeSO.output,this);
}
Debug. Log(fryingTimer);}
}
}
private void Start() {
StartCoroutine(HandleFryTimer());
}
private IEnumerator HandleFryTimer () {
yield return new WaitForSeconds(1f);
}
private enum State {
Idle,
Frying,
Fried,
Burned,
}
private State state;
private float fryingTimer;
private FryingRecipeSO fryingRecipeSO;
private void Start() {
state = State.Idle;
}
private void Update() {
if (HasKitchenObject ()) {
switch (state) {
case State.Idle:
break;
case State.Frying:
fryingTimer += Time.deltaTime;
if (fryingTimer › fryingRecipeSO.fryingTimerMax) {
// Fried
GetKitchenObject () .DestroySelf();
KitchenObject.SpawnKitchenObject(fryingRecipeSO.output,this);
Debug. Log("Object fried!");
state = State. Fried;
}
break;
case State.Fried:
break;
case State. Burned:
break;
}
Debug. Log (state);
}}
并在玩家与炉灶互动的逻辑中也添加上,如果放下的物品是可以煎炸的,就把状态设为 State.Frying,并且重置计时器。
if (HasRecipeWithInput(player.GetKitchenObject().GetKitchenObjectSO())) {
// Player carrying something that can be Fried
player.GetKitchenObject().SetKitchenObjectParent(this);
fryingRecipeSO = GetFryingRecipeSOWithInput(GetKitchenObject().GetKitchenObjectSO());
state = State.Frying;
fryingTimer = Of;
}
继续制作烤焦状态,与煎炸状态的逻辑类似,只需创建一个新的 FryingTimer 计时器,及 BurningRecipeSO 可编程对象 MeatPattyCooked-MeatPattyBurned,将输入与输出分别配置为 MeatPattyCooked 和 MeatPattyBurned,计时器最长时间设为 5 秒。
state = State. Fried;
burningTimer = Of;
burningRecipeSO = GetBurningRecipesOWithInput(GetKitchenObject () .GetKitchenObjectSO ());
}
break;
case State.Fried:
burningTimer += Time.deltaTime;
if (burningTimer > burningRecipeSO.burningTimerMax) {
GetKitchenObject ().DestroySelf();
KitchenObject.SpawnKitchenObject(burningRecipesO.output,this);
Debug.Log("Object Burned!");
state = State.Burned;
}
添加火炉开启的图像预制件 StoveOnVisual (加上泛光 Bloom 效果),并用粒子系统制作火星的粒子效果 SizzlingParticles。
为它们新建脚本 StoveCounterVisual,以实现炉子开启时就出现发光及火星的效果。这里用事件 OnStageChanged 来触发效果,每当煎肉炉的状态变化,就触发该事件;当状态为 Frying 或 Fried 时,就显示这两个效果对象。
public class StoveCounterVisual : MonoBehaviour {
[SerializeField] private StoveCounter stoveCounter;
[SerializeField] private GameObject stoveOnGameObject;
[SerializeField] private GameObject particlesGameObject;
private void Start() {
stoveCounter.OnStateChanged += StoveCounter_OnStateChanged;
} private void StoveCounter_OnStateChanged(object sender, StoveCounter.OnStateChangedEventArgs e) {
bool showVisual = e.state == StoveCounter.State.Frying || e.state == StoveCounter.State. Fried;
stoveOnGameObject.SetActive(showVisual);
particlesGameObject. SetActive(showVisual);
}
}
进度条
public interface IHasProgress {
public event EventHandler<OnProgressChangedEventArgs> OnProgressChanged;
public class OnProgressChangedEventArgs : EventArgs {
public float progressNormalized;
}
}
将其应用到炉灶柜上,把 ProgressBarUI 预制件拖入到 StoveCounter 预制件,再把 StoveCounter 拖入 ProgressBarUI 脚本中的 Has Progress Game Object 字段。在 StoveCounter 脚本中应用这个接口:
public class StoveCounter: BaseCounter, IHasProgress {
public event EventHandler<IHasProgress.OnProgressChangedEventArgs>OnProgressChanged;
在每次进入 Frying 和 Fried 状态时触发这个事件,修改进度量,并显示进度条。
OnProgressChanged?. Invoke (this, new IHasProgress. OnProgressChangedEventArgs {
progressNormalized = fryingTimer / fryingRecipeSO.fryingTimerMax
});
在 Burned 状态下隐藏进度条。当玩家中途拿起食材时,状态重置,进度条隐藏。
OnProgressChanged?. Invoke(this, new IHasProgress.OnProgressChangedEventArgs {
progressNormalized = 0f
});
这套教程目前已更新至第 30 课,完成了一款烹饪游戏中厨房物品、切菜柜、垃圾柜、炉灶柜、盘子等系统和功能的制作。点击阅读原文到 Unity 中文课堂立即免费学习吧!
长按关注
第一时间了解Unity引擎动向,学习进阶开发技能
点击“阅读原文”,前往课程主页