胡闹厨房练习(三)
ScriptableObject
一、初步了解
1、实质:是一种特殊类型的Unity对象,
2、作用:用于存储大量数据,而不必依附于游戏场景中的某个GameObject。
3、特点:
可以在不增加场景中对象数量的情况下,管理和存储复杂的数据结构、配置信息、游戏状态等。
4、适用:非常适合用来管理游戏中的各种数据,如角色属性、关卡配置、道具列表等。
5、意义:
使数据的管理更加集中和高效,避免将大量数据直接硬编码在脚本中或在多个GameObject上重复配置相同的数据。
还可以用于实现游戏数据的序列化,方便在游戏运行时保存和加载游戏状态。
二、创建方法
创建一个ScriptableObject.cs,名字自取,例如
using UnityEngine;[CreateAssetMenu(fileName = "NewItem", menuName = "Inventory/Item")]
public class Item : ScriptableObject
{public new string name;public Sprite icon = null;public string description = "";public int value;
}
三、使用方法
1、在Unity编辑器中通过CreateAssetMenu
定义的菜单项创建ScriptableObject
实例
2、在其他脚本中,通过Resources.Load<T>()
或其他方式加载这个ScriptableObject
实例,并使用它的属性,例如
Item myItem = Resources.Load<Item>("Path/To/Your/Item");
Debug.Log(myItem.name);
菜单管理
一、食谱
1、食谱名称和食谱成分:在Scripts/ScriptableObject文件夹下,新建RecipeSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class RecipeSO : ScriptableObject
{public List<KitchenObjectSO> kitchenObjectSOList;public string recipeName;
}
2、在ScriptableObjects文件夹下,新建RecipeSO文件夹
3、新建RecipeSO对象:Burger
3、复制Burger,重命名为Cheeseburger,更改名称、添加CheeseSlicesSO(如上图)
4、同样的方法制作RecipeSO对象:MEGAburger、Salad
二、菜单
1、菜单列表:
(1) 在Scripts/ScriptableObjects中新建RecipeListSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class RecipeListSO : ScriptableObject
{public List<RecipeSO> recipeSOList;
}
(2) 创建RecipeListSO对象,命名为_RecipeListSO,设置菜单
2、取消创建项:编辑RecipeListSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[CreateAssetMenu()]
public class RecipeListSO : ScriptableObject
{public List<RecipeSO> recipeSOList;
}
三、订单
1、Create Empty,命名为DeliveryManager,Reset它的Transform
2、随机订单:为DeliveryManager添加DeliveryManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DeliveryManager : MonoBehaviour
{[SerializeField] private RecipeListSO recipeListSO;private List<RecipeSO> waitingRecipeSOList;private float spawnRecipeTimer;private float spawnRecipeTimeMax = 4f;private int waitingRecipesMax = 4;private void Awake(){waitingRecipeSOList = new List<RecipeSO>();}private void Update(){// 时间计时器spawnRecipeTimer -= Time.deltaTime;// 检查倒计时是否结束if (spawnRecipeTimer <= 0f){// 当倒计时结束时,将spawnRecipeTimer重置为spawnRecipeTimeMaxspawnRecipeTimer = spawnRecipeTimeMax;if (waitingRecipeSOList.Count < waitingRecipesMax){// 随机选择食谱并添加到等待列表RecipeSO waitingRecipeSO = recipeListSO.recipeSOList[Random.Range(0, recipeListSO.recipeSOList.Count)];// 在控制台打印出所选食谱的名称Debug.Log(waitingRecipeSO.recipeName);waitingRecipeSOList.Add(waitingRecipeSO);}}}
}
3、检查玩家提交食品与订单是否匹配
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DeliveryManager : MonoBehaviour
{public static DeliveryManager Instance { get; private set; }[SerializeField] private RecipeListSO recipeListSO;private List<RecipeSO> waitingRecipeSOList;private float spawnRecipeTimer;private float spawnRecipeTimeMax = 4f;private int waitingRecipesMax = 4;private void Awake(){Instance = this;waitingRecipeSOList = new List<RecipeSO>();}private void Update(){spawnRecipeTimer -= Time.deltaTime;if (spawnRecipeTimer <= 0f){spawnRecipeTimer = spawnRecipeTimeMax;if (waitingRecipeSOList.Count < waitingRecipesMax){RecipeSO waitingRecipeSO = recipeListSO.recipeSOList[Random.Range(0, recipeListSO.recipeSOList.Count)];Debug.Log(waitingRecipeSO.recipeName);waitingRecipeSOList.Add(waitingRecipeSO);}}}// 检查玩家提交食品是否与订单相匹配的方法public void DeliverRecipe(PlateKitchenObject plateKitchenObject){// 遍历订单列表中的每一个订单for (int i = 0; i < waitingRecipeSOList.Count; i++){// 获取一个订单RecipeSO waitingRecipeSO = waitingRecipeSOList[i];// 检查食材数量与订单数量是否匹配if (waitingRecipeSO.kitchenObjectSOList.Count == plateKitchenObject.GetKitchenObjectsSOList().Count){// Has the same number of ingredientsbool plateContentsMatchesRecipe = true;// 遍历订单中的每一个食材foreach (KitchenObjectSO recipeKitchenObjectSO in waitingRecipeSO.kitchenObjectSOList){// Cycling through all ingredients in the Recipe// 初始化一个标志,用于表示是否在当前盘子上找到了食谱中的食材bool ingredientFound = false;// 遍历盘子上的每一个食材foreach (KitchenObjectSO plateKitchenObjectSO in plateKitchenObject.GetKitchenObjectsSOList()){// Cycling through all ingredients in the plate// 如果盘子上的食材与订单中的食材相同if (plateKitchenObjectSO == recipeKitchenObjectSO){// Ingredients matches!// 设置标志为true,表示找到了食材ingredientFound = true;// 跳出内层循环,因为已经找到了匹配的食材break;}}// 如果在当前盘子上没有找到食谱中的这个食材if (!ingredientFound){// This Recipe ingredient was not found on the plate// 设置盘子与食谱不匹配的标志plateContentsMatchesRecipe = false;}}// 如果盘子与食谱匹配if (plateContentsMatchesRecipe){// Player delivered the correct recipe!// 在控制台打印消息表示玩家提交了正确的食谱Debug.Log("Player delivered the correct recipe!");// 从订单列表中移除这个食谱waitingRecipeSOList.RemoveAt(i);// 退出方法,因为已经找到了匹配的食谱return;}}}// No matches found! 遍历完订单列表后没有找到匹配的食谱// Player did not deliver a correct recipe在控制台打印消息表示玩家没有提交正确的食谱Debug.Log("Player did not deliver a correct recipe!");}
}
关于if(){}
bool a = false;if(a){Debug.Log("如果a为true,则输出文字。");}//这段代码不会被执行,因为不符合条件
bool a = false;if(!a){Debug.Log("如果!a为true,则输出文字。");}//这段代码会被执行
// 因为a = false,那么!a的结果为true
4、编辑DeliveryCounter.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DeliveryCounter : BaseCounter
{public override void Interact(Player player){if (player.HasKitchenObject()){if (player.GetKitchenObject().TryGetPlate(out PlateKitchenObject plateKitchenObject)){// Only accepts PlatesDeliveryManager.Instance.DeliverRecipe(plateKitchenObject);player.GetKitchenObject().DestroySelf();}}}
}
四、订单面板
1、新建并设置Hierarchy面板上的Canvas
(1) 打开Inspector面板,将Canvas下的Render Mode 设置为Screen Space - Overlay,
(2) Canvas Scaler 中UI Scale Mode 设置为Scale With Screen Size,
(3) Reference Resolution 中x = 1920,y = 1080。
(4) Match=1(完全匹配高度)
设置结果:Canvas上的UI在游戏窗口宽度改变时不会改变,在高度改变时会等比例缩小,
设置目的:只关心UI的横向排布,而当纵向改变时该组件会自动缩放
2、UI-Image,设置Image的Color 为 FF0000,删除此Image
3、打开Game界面,设置为Full HD(1920*1080),可见红色图标
4、订单UI面板
(1) 以Canvas为父物体,Create Empty,命名为DeliveryManagerUI
(2) 设置面板:Alt+Stretch,Left、Top、Right、Bottom都为0。
此时,DeliveryManagerUI填满整个Canvas
五、订单内容
1、订单UI文本
(1) 以DeliveryManagerUI为父物体,UI-Text(TMP),重命名为TitleText。
(2) 文本为“RECIPES WAITING...”,字体设置为加粗(Bold)
(3) 将锚点设置到左上角,Width和Hight都为0,Wrapping设为Disabled,
(4) 移动到合适位置,posX = 33,PosY = -16
2、单个订单背景:
(1) 以DeliveryManagerUI 为父物体,Create Empty,命名为Container
(2) 锚点左上角,调整位置 Pos.x = 29,Pos.y=-91,Width和Hight都为0
(3) 给Container添加Vertical Layout Group组件,Padding下间距(Spacing)为30
3、单个订单图标模板
(1) Container下Create Empty,命名为RecipeTemplate,
(2) 选中RecipeTemplate,Shift +选择左上角图标,设置Width和Height分别为250,100
(3) 以RecipeTemplate为父物体,UI- Image,命名为Background,Alt + Stretch
(4) 颜色为黑色,透明度233
4、单个订单名称
(1) 以RecipeTemplate为父物体,UI- Text(TMP),命名为RecipeNameText
(2) 字号为20,加粗,Width、Hight都为0,Wrapping设为Disabled,文本内容为Recipe,白色
(3) 锚点左上,Pos.x = 10 ,Pos.y = -5
5、复制两个RecipeTemplate
6、编辑DeliveryManager.cs
public List<RecipeSO> GetKitchenObjectsSOList()
{return waitingRecipeSOList;
}
7、显示订单图标:给DeliveryManagerUI添加DeliveryManagerUI.cs组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DeliveryManagerUI : MonoBehaviour
{[SerializeField] private Transform container;[SerializeField] private Transform recipeTemplate;private void Awake(){recipeTemplate.gameObject.SetActive(false);}private void UpdateVisual(){// 清除UI界面上所有旧的订单图标foreach (Transform child in container){if(child == recipeTemplate) continue;//立即结束当前循环迭代Destroy(child.gameObject);}// 根据当前的订单列表创建并显示新的图标foreach (RecipeSO recipeSO in DeliveryManager.Instance.GetOrdersList()){Transform recipeTransform = Instantiate(recipeTemplate,container);recipeTransform.gameObject.SetActive(true);}}
}
8、重组代码
(1) 添加委托事件和触发事件程序:编辑 DeliveryManager.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DeliveryManager : MonoBehaviour
{public event EventHandler OnRecipeSpawned;public event EventHandler OnRecipeCompleted;public static DeliveryManager Instance { get; private set; }[SerializeField] private RecipeListSO recipeListSO;private List<RecipeSO> waitingRecipeSOList;private float spawnRecipeTimer;private float spawnRecipeTimeMax = 4f;private int waitingRecipesMax = 4;private void Awake(){Instance = this;waitingRecipeSOList = new List<RecipeSO>();}private void Update(){spawnRecipeTimer -= Time.deltaTime;if (spawnRecipeTimer <= 0f){spawnRecipeTimer = spawnRecipeTimeMax;if (waitingRecipeSOList.Count < waitingRecipesMax){RecipeSO waitingRecipeSO = recipeListSO.recipeSOList[UnityEngine.Random.Range(0, recipeListSO.recipeSOList.Count)];waitingRecipeSOList.Add(waitingRecipeSO);OnRecipeSpawned?.Invoke(this,EventArgs.Empty);}}}public void DeliverRecipe(PlateKitchenObject plateKitchenObject){for (int i = 0; i < waitingRecipeSOList.Count; i++){RecipeSO waitingRecipeSO = waitingRecipeSOList[i];if (waitingRecipeSO.kitchenObjectSOList.Count == plateKitchenObject.GetKitchenObjectsSOList().Count){// Has the same number of ingredientsbool plateContentsMatchesRecipe = true;foreach (KitchenObjectSO recipeKitchenObjectSO in waitingRecipeSO.kitchenObjectSOList){// Cycling through all ingredients in the Recipebool ingredientFound = false;foreach (KitchenObjectSO plateKitchenObjectSO in plateKitchenObject.GetKitchenObjectsSOList()){// Cycling through all ingredients in the plateif (plateKitchenObjectSO == recipeKitchenObjectSO){// Ingredients matches!ingredientFound = true;break;}}if (!ingredientFound){// This Recipe ingredient was not found on the plateplateContentsMatchesRecipe = false;}}if (plateContentsMatchesRecipe){// Player delivered the correct recipe!waitingRecipeSOList.RemoveAt(i);OnRecipeCompleted?.Invoke(this,EventArgs.Empty);return;}}}}public List<RecipeSO> GetWaitingRecipeSOList(){return waitingRecipeSOList;}
}
(2) 订阅事件和事件处理程序:编辑DeliveryManagerUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DeliveryManagerUI : MonoBehaviour
{[SerializeField] private Transform container;[SerializeField] private Transform recipeTemplate;private void Awake(){recipeTemplate.gameObject.SetActive(false);}private void Start(){DeliveryManager.Instance.OnRecipeSpawned += DeliveryManager_OnRecipeSpawned;DeliveryManager.Instance.OnRecipeCompleted += DeliveryManager_OnRecipeCompleted;UpdateVisul();}private void DeliveryManager_OnRecipeCompleted(object sender, System.EventArgs e){UpdateVisul();}private void DeliveryManager_OnRecipeSpawned(object sender, System.EventArgs e){UpdateVisul();}private void UpdateVisul(){foreach (Transform child in container){if (child == recipeTemplate) continue;Destroy(child.gameObject);}foreach (RecipeSO recipeSO in DeliveryManager.Instance.GetWaitingRecipeSOList()){Transform recipeTransform = Instantiate (recipeTemplate,container);recipeTransform.gameObject.SetActive(true);}}
}
(3) 赋值
9、获取订单上的文本
(1) 给RecipeTemplate添加 DeliveryManagerSingleUI.cs组件
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;public class DeliveryManagerSingleUI : MonoBehaviour
{[SerializeField] private TextMeshProUGUI recipeNameText;public void SetRecipeSO(RecipeSO recipeSO){recipeNameText.text = recipeSO.recipeName;}
}
(2) 赋值
10、组织脚本文件夹:在Scripts文件夹下新建文件夹,命名为UI
11、显示订单内容:编辑DeliveryManagerUI.cs
private void UpdateVisul()
{foreach (Transform child in container){if (child == recipeTemplate) continue;Destroy(child.gameObject);}foreach (RecipeSO recipeSO in DeliveryManager.Instance.GetWaitingRecipeSOList()){Transform recipeTransform = Instantiate (recipeTemplate,container);recipeTransform.gameObject.SetActive(true);recipeTransform.GetComponent<DeliveryManagerSingleUI>().SetRecipeSO(recipeSO);}
}
12、显示订单图标
(1) 以RecipeTemplate为父物体,Create Empty,命名为IconContainer,Pos.x = -110,Width和Height都为0
(2) 以IconContainer为父物体,UI - Image,命名为IconTemplate,Width和Height都为40、
(3) Source Image 为Bread
(4) 给IconContainer添加Horizontal Layout Group组件,可复制几个IconTemplate查看效果
(5) 显示订单图标:编辑DeliveryManagerSingleUI.cs
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;public class DeliveryManagerSingleUI : MonoBehaviour
{[SerializeField] private TextMeshProUGUI recipeNameText;[SerializeField] private Transform iconContainer;[SerializeField] private Transform iconTemplate;private void Awake(){iconTemplate.gameObject.SetActive(false);}public void SetRecipeSO(RecipeSO recipeSO){recipeNameText.text = recipeSO.recipeName;foreach (Transform child in iconContainer){if (child == iconTemplate) continue;Destroy(child.gameObject);}foreach (KitchenObjectSO kitchenObjectSO in recipeSO.kitchenObjectSOList){Transform iconTransform = Instantiate(iconTemplate, iconContainer);iconTransform.gameObject.SetActive(true);iconTransform.GetComponent<Image>().sprite = kitchenObjectSO.sprite;}}
}
赋值
测试结果:按订单制作食物,送到配送台后,食物消失,订单消失
声音
一、背景音乐
1、Create Empy,命名为MusicManager,Reset Transform
2、为MusicManager添加Audio Source组件
设置Audio Source下的AudioClip为Music
勾选Play On Awake,勾选Loop(循环)
Priority为0,Volume为0.5
3、确保Main Camera上有Audio Listener组件
二、配送音效
1、音效对象
(1) 在Scripts/ScripatableObjects文件夹下新建AudioClipRefsSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class AudioClipRefsSO : ScriptableObject
{public AudioClip[] chop;public AudioClip[] deliveryFail;public AudioClip[] deliverySuccess;public AudioClip[] footstep;public AudioClip[] objectDrop;public AudioClip[] objectPickup;public AudioClip[] trash;public AudioClip[] warning;public AudioClip stoveSizzle;
}
(2) 在ScriptableObjects文件夹,制作 AudioClipRefsSO对象,命名为AudioClipRefsSO
(3) 添加对应属性
2、在DeliveryManager.cs中,声明两个EventHandler委托事件
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DeliveryManager : MonoBehaviour
{public event EventHandler OnRecipeSpawned;public event EventHandler OnRecipeCompleted;// 委托事件public event EventHandler OnRecipeSuccess;public event EventHandler OnRecipeFailed;public static DeliveryManager Instance { get; private set; }[SerializeField] private RecipeListSO recipeListSO;private List<RecipeSO> waitingRecipeSOList;private float spawnRecipeTimer;private float spawnRecipeTimeMax = 4f;private int waitingRecipesMax = 4;private void Awake(){Instance = this;waitingRecipeSOList = new List<RecipeSO>();}private void Update(){spawnRecipeTimer -= Time.deltaTime;if (spawnRecipeTimer <= 0f){spawnRecipeTimer = spawnRecipeTimeMax;if (waitingRecipeSOList.Count < waitingRecipesMax){RecipeSO waitingRecipeSO = recipeListSO.recipeSOList[UnityEngine.Random.Range(0, recipeListSO.recipeSOList.Count)];waitingRecipeSOList.Add(waitingRecipeSO);OnRecipeSpawned?.Invoke(this,EventArgs.Empty);}}}public void DeliverRecipe(PlateKitchenObject plateKitchenObject){for (int i = 0; i < waitingRecipeSOList.Count; i++){RecipeSO waitingRecipeSO = waitingRecipeSOList[i];if (waitingRecipeSO.kitchenObjectSOList.Count == plateKitchenObject.GetKitchenObjectsSOList().Count){// Has the same number of ingredientsbool plateContentsMatchesRecipe = true;foreach (KitchenObjectSO recipeKitchenObjectSO in waitingRecipeSO.kitchenObjectSOList){// Cycling through all ingredients in the Recipebool ingredientFound = false;foreach (KitchenObjectSO plateKitchenObjectSO in plateKitchenObject.GetKitchenObjectsSOList()){// Cycling through all ingredients in the plateif (plateKitchenObjectSO == recipeKitchenObjectSO){// Ingredients matches!ingredientFound = true;break;}}if (!ingredientFound){// This Recipe ingredient was not found on the plateplateContentsMatchesRecipe = false;}}if (plateContentsMatchesRecipe){// Player delivered the correct recipe!waitingRecipeSOList.RemoveAt(i);OnRecipeCompleted?.Invoke(this,EventArgs.Empty);// 触发成功事件OnRecipeSuccess?.Invoke(this,EventArgs.Empty);return;}}}// 触发失败事件OnRecipeFailed?.Invoke(this,EventArgs.Empty);}public List<RecipeSO> GetWaitingRecipeSOList(){return waitingRecipeSOList;}
}
3、Create Empty,命名为SoundManager,Reset Transform
4、为SoundManager添加SoundManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SoundManager : MonoBehaviour
{[SerializeField] private AudioClipRefsSO audioClipRefsSO;private void Start(){DeliveryManager.Instance.OnRecipeSuccess += DeliveryManager_OnRecipeSuccess;DeliveryManager.Instance.OnRecipeFailed += DeliveryManager_OnRecipeFailed;}private void DeliveryManager_OnRecipeFailed(object sender, System.EventArgs e){PlaySound(audioClipRefsSO.deliveryFail,Camera.main.transform.position);}private void DeliveryManager_OnRecipeSuccess(object sender, System.EventArgs e){PlaySound(audioClipRefsSO.deliverySuccess, Camera.main.transform.position);}private void PlaySound(AudioClip[] audioClipArray ,Vector3 position,float volume = 1f){PlaySound(audioClipArray[Random.Range(0,audioClipArray.Length)],position,volume);}private void PlaySound(AudioClip audioClip ,Vector3 position,float volume = 1f){AudioSource.PlayClipAtPoint(audioClip,position,volume);}
}
5、赋值
6、调节声音播放的位置:
(1) 编辑DeliveryCounter.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DeliveryCounter : BaseCounter
{public static DeliveryCounter Instance { get; private set; }private void Awake(){Instance = this;}public override void Interact(Player player){if (player.HasKitchenObject()){if (player.GetKitchenObject().TryGetPlate(out PlateKitchenObject plateKitchenObject)){// Only accepts PlatesDeliveryManager.Instance.DeliverRecipe(plateKitchenObject);player.GetKitchenObject().DestroySelf();}}}
}
(2) 编辑SoundManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SoundManager : MonoBehaviour
{[SerializeField] private AudioClipRefsSO audioClipRefsSO;private void Start(){DeliveryManager.Instance.OnRecipeSuccess += DeliveryManager_OnRecipeSuccess;DeliveryManager.Instance.OnRecipeFailed += DeliveryManager_OnRecipeFailed;}private void DeliveryManager_OnRecipeFailed(object sender, System.EventArgs e){DeliveryCounter deliveryCounter = DeliveryCounter.Instance;PlaySound(audioClipRefsSO.deliveryFail, deliveryCounter.transform.position);}private void DeliveryManager_OnRecipeSuccess(object sender, System.EventArgs e){DeliveryCounter deliveryCounter = DeliveryCounter.Instance;PlaySound(audioClipRefsSO.deliverySuccess, deliveryCounter.transform.position);}private void PlaySound(AudioClip[] audioClipArray ,Vector3 position,float volume = 1f){PlaySound(audioClipArray[Random.Range(0,audioClipArray.Length)],position,volume);}private void PlaySound(AudioClip audioClip ,Vector3 position,float volume = 1f){AudioSource.PlayClipAtPoint(audioClip,position,volume);}}
三、切菜音效
1、声明、触发委托(按F时播放音效):编辑CuttingCounter.cs
场景存在多个处理台,不能用配送台的单例模式。所以使用静态委托:
以便使多个 CuttingCounter
实例能够共享同一个事件响应逻辑(共享OnAnyCut事件)
public static event EventHandler OnAnyCut;
public override void InteractAlternate(Player player)
{if (HasKitchenObject() && HasRecipeWithInput(GetKitchenObject().GetKitchenObjectSO())){cuttingProgress++;Oncut?.Invoke(this, EventArgs.Empty);OnAnyCut?.Invoke(this, EventArgs.Empty);}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;public class CuttingCounter : BaseCounter, IHasProgress
{public static event EventHandler OnAnyCut;public event EventHandler<IHasProgress.OnprogressChangedEventArgs> OnProgressChanged;public event EventHandler Oncut;[SerializeField] private CuttingRecipeSO[] cuttingRecipeSOArray;private int cuttingProgress;public override void Interact(Player player){if (!HasKitchenObject()){// There is no KitchenObject hereif (player.HasKitchenObject()){// player is carrying somethingif (HasRecipeWithInput(player.GetKitchenObject().GetKitchenObjectSO())){// Player carrying something that can be cutplayer.GetKitchenObject().SetKitchenObjectParent(this);cuttingProgress = 0;// CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(GetKitchenObject().GetKitchenObjectSO());OnProgressChanged?.Invoke(this, new IHasProgress.OnprogressChangedEventArgs{progressNormalized = (float)cuttingProgress / cuttingRecipeSO.cuttingProgressMax});}}else{// Player not carrying anything}}else{// There is a KitchenObject hereif (player.HasKitchenObject()){// Player is carrying somethingif (player.GetKitchenObject().TryGetPlate(out PlateKitchenObject plateKitchenObject)){// Player is holding a Plateif (plateKitchenObject.TryAddIngredient(GetKitchenObject().GetKitchenObjectSO())){GetKitchenObject().DestroySelf();}}}else{// Player is not carrying anythingGetKitchenObject().SetKitchenObjectParent(player);}}}public override void InteractAlternate(Player player){if (HasKitchenObject() && HasRecipeWithInput(GetKitchenObject().GetKitchenObjectSO())){// There is a KitchenObject her and it can be cutcuttingProgress++;Oncut?.Invoke(this, EventArgs.Empty);OnAnyCut?.Invoke(this, EventArgs.Empty);CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(GetKitchenObject().GetKitchenObjectSO());OnProgressChanged?.Invoke(this, new IHasProgress.OnprogressChangedEventArgs
相关文章:
胡闹厨房练习(三)
ScriptableObject 一、初步了解 1、实质:是一种特殊类型的Unity对象, 2、作用:用于存储大量数据,而不必依附于游戏场景中的某个GameObject。 3、特点: 可以在不增加场景中对象数量的情况下,管理和存储复杂的数据结构、配置信息、游戏状态等。 4、适用:非常适合用来…...
Postman接口测试01|接口测试基础概念、http协议、RESTful风格、接口文档
目录 一、接口测试基础概念 1、什么是接口 2、接口的类型 3、什么是接口测试 4、为什么要做接口测试 5、接口测试的实现方式 6、什么是自动化接口测试? 二、接口返回的数据格式 1、三种格式 2、Json 三、接口协议 1、webservice协议 2、dubbo协议 3、…...
算法进阶:贪心算法
贪心算法是一种简单而直观的算法思想,它在每一步选择中都采取在当前状态下最优的选择,以期望最终得到全局最优解。贪心算法通常适用于一些具有最优子结构的问题,即问题的最优解可以通过一系列局部最优解的选择得到。 贪心算法的基本思路是&a…...
深度学习笔记(6)——循环神经网络RNN
循环神经网络 RNN 核心思想:RNN内部有一个“内部状态”,随着序列处理而更新 h t f W ( h t − 1 , x t ) h_tf_W(h_{t-1},x_t) htfW(ht−1,xt) 一般来说 h t t a n h ( W h h h t − 1 W x h x t ) h_ttanh(W_{hh}h_{t-1}W_{xh}x_t) httanh(Whhht−1Wxhxt…...
电商项目高级篇07-redisson分布式锁
redisson分布式锁 1、引入maven依赖2、config类3、可重入锁设计 1、引入maven依赖 <!--引入redisson--><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.0</version></depend…...
STM32中断详解
STM32中断详解 NVIC 中断系统中断向量表相关寄存器中断优先级中断配置 外部中断实验EXTI框图外部中断/事件线映射中断步骤初始化代码实现 定时器中断通用定时器相关功能标号1:时钟源标号 2:控制器标号 3:时基单元 代码实现 NVIC 中断系统 STM…...
KNN分类算法 HNUST【数据分析技术】(2025)
1.理论知识 KNN(K-Nearest Neighbor)算法是机器学习算法中最基础、最简单的算法之一。它既能用于分类,也能用于回归。KNN通过测量不同特征值之间的距离来进行分类。 KNN算法的思想: 对于任意n维输入向量,分别对应于特征…...
【Win11】安装 VMware17 和 Ubuntu
【Win11】安装 VMware17 和 Ubuntu 15 版本和 Win11 家庭版间的兼容应该有 BUG,请直接跳至【VMware 17】 安装【VMware 15】 本来是按如下资源链接安装的,但发现 15 版本和 Win11 家庭版间的兼容应该有 BUG,在安装并关闭 Hyper-Vÿ…...
Jmeter快速入门
目录 1.安装Jmeter 1.1.下载 1.2.解压 1.3.运行 2.快速入门 2.1.设置中文语言 2.2.基本用法 1.安装Jmeter Jmeter依赖于JDK,所以必须确保当前计算机上已经安装了JDK,并且配置了环境变量。 1.1.下载 可以Apache Jmeter官网下载,地址…...
Android Studio2024版本安装环境SDK、Gradle配置
一、软件版本,安装包附上 👉android-studio-2024.1.2.12-windows.exe👈 👉百度网盘Android Studio安装包👈 (若下载连链接失效可去百度网盘链接下载) 二、软件安装过程 三、准备运行…...
嵌入式单片机窗口看门狗控制与实现
窗口看门狗 注意:WWDG外设没有独立的时钟源,而是挂载在APB1总线下,APB1总线外设时钟为42MHZ。 了解WWDG外设的使用流程,可以参考stm32f4xx_wwdg.c的开头注释,具体流程如下图所示...
[单master节点k8s部署]43.全链路监控(二)
部署pinpoint服务端 这里安装的是pinpoint-docker,可以从GitHub - pinpoint-apm/pinpoint-docker: Officix 下载。通过readme可以看到,该项目提供的镜像: Pinpoint-Web ServerPinpoint-CollectorPinpoint-AgentPinpoint-FlinkPinpoint-Hba…...
Apache Doris 创始人:何为“现代化”的数据仓库?
在 12 月 14 日的 Doris Summit Asia 2024 上,Apache Doris 创始人 & PMC 成员马如悦在开场演讲中,围绕“现代化数据仓库”这一主题,指出 3.0 版本是 Apache Doris 研发路程中的重要里程碑,他将这一进展总结为“实时之路”、“…...
json的作用?
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,具有简洁、易读、易于解析和生成等特点,在前后端数据交互、配置文件、数据存储等多个领域发挥着重要作用,以下是具体介绍: JSON 的作用 数据…...
ChatGPT是如何生成长文的
说实话,现在我们对chatGPT的最深的印象就是他是一个各方面知识都比较全面的机器助手,我们的问题他都能生成答案,不过大家发现没有,它生成相对应的长文的时候,都是一个词一个词蹦出来的,有的时候是一个个词组…...
微服务——部署与运维
1、你是否有将 Java 微服务部署到容器(如 Docker)中的经验?请描述一下部署过程和相关注意事项。 部署过程: 编写 Dockerfile,定义基础镜像(如 openjdk)、应用 JAR 包路径和启动命令。构建镜像…...
json笔记
1、json的组成 JSON是一个标记符序列。这套标记符包括:构造字符、字符串、数字和字面值(false, true, null)。 1.1 构造字符 六个构造字符: 左方括号 [ 右方括号 ] 左大括号 { 右大括号 } 冒号 : 逗号 , 1.2 JSON值 json值包括:对象…...
从0入门自主空中机器人-1【课程介绍】
关于本课程: 本次课程是一套面向对自主空中机器人感兴趣的学生、爱好者、相关从业人员的免费课程,包含了从硬件组装、机载电脑环境设置、代码部署、实机实验等全套详细流程,带你从0开始,组装属于自己的自主无人机,并让…...
C++ 设计模式
代码示例 从封装变化角度分类 1.组件协作 现代软件专业分工之后的第一个结果“框架与应用程序的划分” ,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。 模板方法(Template Me…...
最新版Edge浏览器加载ActiveX控件技术——alWebPlugin中间件V2.0.28-迎春版发布
allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品,致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX控件直接嵌入浏览器,实现插件加载、界面显示、接口调用、事件回调等。支持Chrome、Firefo…...
HarmonyOS NEXT应用开发实战:免费练手的网络API接口分享
学习一项技能,最好也最快的办法就是直接动手实战。在实战中不断的总结经验和收获成就感。这里分享些好用且免费的网络API练手接口,这对于想要提升自己网络开发能力的开发者来说,无疑是极大的福音。今天,我将详细介绍一个API接口集…...
高级技巧-使用Mysql 实现根据条件过滤整个分组数据
博客-mysql exists实现过滤所属条件对应分组的全部数据 在数据查询中,有时需要根据某一条件来过滤整个分组的数据,尤其是当某条记录满足特定条件时,需要将该组内的所有记录排除。本文将介绍如何使用 MySQL 的 EXISTS 关键字来实现这种分组过滤…...
LeetCode 9. 回文数
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。 回文数 是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 例如,121 是回文,…...
园区网综合拓扑实验
一、实验要求 实验拓扑图如上图所示 1、按照图示的VLAN及IP地址需求,完成相关配置 2、要求SW1为VLAN 2/3的主根及主网关 SW2为vlan 20/30的主根及主网关 SW1和SW2互为备份 3、可以使用super vlan(本实验未使用) 4、上层…...
Flink状态编程
Flink处理机制的核心就是“有状态的流处理”,在某些情况下,一条数据的计算不仅要基于当前数据自身,还需要依赖数据流中的一些其他数据。这些在一个任务中,用来辅助计算的数据我们就称之为这个任务的状态。 一、按键分区状态&…...
Ajax数据爬取
有时我们用requests 抓取页面得到的结果,可能和在浏览器中看到的不一样:在浏览器中可以看到正常显示的页面数据,而使用requests 得到的结果中并没有这些数据。这是因为 requests 获取的都是原始 HTML 文档,而浏览器中的页面是JavaScript 处理…...
MVCC实现原理以及解决脏读、不可重复读、幻读问题
MVCC实现原理以及解决脏读、不可重复读、幻读问题 MVCC是什么?有什么作用?MVCC的实现原理行隐藏的字段undo log日志版本链Read View MVCC在RC下避免脏读MVCC在RC造成不可重复读、丢失修改MVCC在RR下解决不可重复读问题RR下仍然存在幻读的问题 MVCC是什么…...
PilotGo
title: 解锁 PilotGo:智能化运维的得力助手 date: ‘2024-12-29’ category: blog tags: PilotGo运维管理智能化工具技术应用 sig: ops archives: ‘2024-12’ author:way_back summary: PilotGo 作为一款创新的运维管理工具,凭借其智能化的特性和丰富的…...
【后端】LNMP环境搭建
长期更新各种好文,建议关注收藏! 本文近期更新完毕。 LNMPlinuxnginxmysqlphp 需要的资源 linux服务器 web服务软件nginx 对应的语言编译器代码文件 数据库mysql安装 tar.gz包或者命令行安装 进入root: sodu 或su mkdir path/{server,soft}…...
vue 本地自测iframe通讯
使用 postMessage API 来实现跨窗口(跨域)的消息传递。postMessage 允许你安全地发送消息到其他窗口,包括嵌套的 iframe,而不需要担心同源策略的问题。 发送消息(父应用) 1. 父应用:发送消息给…...
【人工智能】基于Python和OpenCV实现实时人脸识别系统:从基础到应用
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着人工智能和计算机视觉的快速发展,人脸识别技术已广泛应用于监控、安全、社交媒体、金融和医疗等领域。本文将介绍如何利用Python和Ope…...
vue2使用pdfjs-dist和jsPDF生成pdf文件
vue2使用pdfjs-dist和jsPDF生成pdf文件 1、安装依赖 npm install pdfjs-dist2.6.3472、引入依赖 import { jsPDF } from jspdf// 使用require方式导入pdfjs-dist v2.6.347,高版本报错(import导入会报错:GlobalWorkerOptions undefined&…...
深度学习算法选择
1. 卷积神经网络(Convolutional Neural Networks, CNNs) 简介 卷积神经网络擅长处理具有网格结构的数据,如图像。通过卷积层、池化层和全连接层,CNN能够自动提取和学习数据的空间特征。 应用场景 图像识别与分类:如…...
asp.net core系统记录当前在线人数
实时记录当前在线人数,登录后保持120秒在线状态,在线状态保存在缓存中,采用滑动过期,在120秒内请求了系统,自动续活120秒;超过时间则移除用户在线状态; 需要在登录过滤器标记用户在线状态需要排…...
【每日学点鸿蒙知识】Grid子项拖动、Swiper指示器、手势事件上报、指定使用发布版本API、打包签名失败报错109
1、HarmonyOS Grid组件子项拖动问题? Grid组件中有20个GridItem子组件,其中前4个GridItem是不能拖动的,其余GridItem可拖动排序。 关于可拖拽grid及gridItem可参考如下代码(注:其中 .draggable(parseInt(day) < 6…...
轻松实现向量搜索:探索 Elastic-Embedding-Searcher 项目
随着人工智能和机器学习技术的飞速发展,向量搜索已成为数据检索的重要方式。尤其是在处理大规模文本数据时,传统的基于关键词的检索方式已经难以满足需求。为了优化检索性能并提升搜索精度,向量搜索成为了更加高效的解决方案。而在这一领域&a…...
区块链平台安全属性解释
区块链平台安全属性解释 双向认证 解释:双向认证是指在通信过程中,**通信双方都需要对对方的身份进行验证,确保对方是合法的、可信任的实体。**只有双方身份都得到确认后,通信才会被允许进行,从而防止非法用户的接入和数据的窃取或篡改。举例:在基于区块链和联邦学习的数…...
matlab reshape permute
1.reshape 将向量按照顺序重新构建 矩阵,新矩阵 先排完第一列, 再第二列… 2.permute 将向量 维度变换...
《一文读懂卷积网络CNN:原理、模型与应用全解析》
《一文读懂卷积网络CNN:原理、模型与应用全解析》 一、CNN 基本原理大揭秘(一)从人类视觉到 CNN 灵感(二)核心组件详解 二、经典 CNN 模型巡礼(一)LeNet-5:开山鼻祖(二&a…...
深入理解 PyTorch 的 view() 函数:以多头注意力机制(Multi-Head Attention)为例 (中英双语)
深入理解 PyTorch 的 view() 函数:以多头注意力机制(Multi-Head Attention)为例 在深度学习模型的实现中,view() 是 PyTorch 中一个非常常用的张量操作函数,它能够改变张量的形状(shape)而不改…...
【每日学点鸿蒙知识】获取是否有网接口、获取udid报错、本地通知、Json转Map、Window10安装Hyper-v
1、有没有获取当前是否真实有网的接口? 比如当前链接的是wifi,但是当前wifi是不能访问网络的,有没有接口可以获取到这个真实的网络访问状态? 请参考说明链接:https://developer.huawei.com/consumer/cn/doc/harmonyo…...
《Vue3 四》Vue 的组件化
组件化:将一个页面拆分成一个个小的功能模块,每个功能模块完成自己部分的独立的功能。任何应用都可以被抽象成一棵组件树。 Vue 中的根组件: Vue.createApp() 中传入对象的本质上就是一个组件,称之为根组件(APP 组件…...
Linux:alias别名永久有效
一、背景 日常使用bash时候,有些常用的命令参数的组合命令太长,很难记,此时可以利用Linux提供的alias命令生成命令的别名(命令的隐射),但是我们会发现,当退出了终端后重新登录就失效了ÿ…...
MicroDiffusion——采用新的掩码方法和改进的 Transformer 架构,实现了低预算的扩散模型
介绍 论文地址:https://arxiv.org/abs/2407.15811 现代图像生成模型擅长创建自然、高质量的内容,每年生成的图像超过十亿幅。然而,从头开始训练这些模型极其昂贵和耗时。文本到图像(T2I)扩散模型降低了部分计算成本&a…...
网神SecFox FastJson反序列化RCE漏洞复现(附脚本)
0x01 产品描述: 网神SecFox是奇安信网神信息技术(北京)股份有限公司推出的一款运维安全管理与审计系统,集“身份认证、账户管理、权限控制、运维审计”于一体,提供统一运维身份认证、细粒度的权限控制、丰富的运维审计报告、多维度的预警…...
解决无法在 Ubuntu 24.04 上运行 AppImage 应用
在 Ubuntu 24.04 中运行 AppImage 应用的完整指南 在 Ubuntu 24.04 中,许多用户可能会遇到 AppImage 应用无法启动的问题。即使你已经设置了正确的文件权限,AppImage 仍然拒绝运行。这通常是由于缺少必要的库文件所致。 问题根源:缺少 FUSE…...
Pytorch | 利用PC-I-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击
Pytorch | 利用PC-I-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击 CIFAR数据集PC-I-FGSM介绍算法原理 PC-I-FGSM代码实现PC-I-FGSM算法实现攻击效果 代码汇总pcifgsm.pytrain.pyadvtest.py 之前已经针对CIFAR10训练了多种分类器: Pytorch | 从零构建AlexNet对CIFAR…...
前端往后端传递参数的方式有哪些?
文章目录 1. URL 参数1.1. 查询参数(Query Parameters)1.2. 路径参数(Path Parameters) 2. 请求体(Request Body)2.1. JSON 数据2.2. 表单数据2.3. 文件上传 3. 请求头(Headers)3.1. 自定义请求…...
对抗攻击VA-I-FGSM:Adversarial Examples with Virtual Step and Auxiliary Gradients
文章目录 摘要相关定义算法流程代码:文章链接: Improving Transferability of Adversarial Examples with Virtual Step and Auxiliary Gradients 摘要 深度神经网络已被证明容易受到对抗样本的攻击,这些对抗样本通过向良性样本中添加人类难以察觉的扰动来欺骗神经网络。目…...
【Java】IO流练习
IO流练习 题干: 根据指定要求,完成电话记录、 注册、登录 注册 题干: 完成【注册】功能: 要求: 用户输入用户名、密码存入users.txt文件中 若users.txt文件不存在,创建该文件若users.txt文件存在 输入…...