【发布时间】:2022-08-03 22:10:36
【问题描述】:
我正在制作一个 2D 游戏,我想让用户结合集合中的两种草药来制作某种药水我不知道如何实现这一点,尽管在我制作场景和产卵脚本后我被困在这一步。知道如何在我将例如 2 种草药组合放入锅中以混合它们并得到我分配给它们的某种药水后如何生成药水吗?
-
你想如何检测这个所谓的“组合在一起”?
-
例如,我想从库存中挑选它们,然后他们去锅炉并给我分配给他们的某种药水。我知道这听起来很烦人,但我不知道这是否可能
标签: unity3d
我正在制作一个 2D 游戏,我想让用户结合集合中的两种草药来制作某种药水我不知道如何实现这一点,尽管在我制作场景和产卵脚本后我被困在这一步。知道如何在我将例如 2 种草药组合放入锅中以混合它们并得到我分配给它们的某种药水后如何生成药水吗?
标签: unity3d
好的,请耐心等待,这将是一个沉重的问题,我将首先解释一下我的解决方案中的一些想法,然后你会找到我的代码以及它如何工作的演示。
我采用了“拖放”风格,将成分表示为可以拖到大锅上的游戏对象。
当一种成分接触到大锅时,它就会被添加到其中。为了简单起见,我使用对撞机进行了此操作。
至于魔药的制作,逻辑分为两类:
食谱可能需要不同数量的相同成分,如果我们尝试制作药水,而大锅中的成分与任何食谱都不匹配,则会丢失成分
出于复杂性和时间的考虑,我制作了一个无法通过检查器编辑的手工制作药水配方的静态“存储库”,这个解决方案对于一个大项目来说是不可行的,但它有助于说明我的解决方案是如何工作的。
谈完这些,这里有一些代码
(为了使代码更易于使用,我删除了所有命名空间,并将除 MonoBehaviour 之外的所有内容放在一个大块中)
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
public enum Ingredient
{
Garlic,
Aloe,
Lichen
}
public class PotionRecipe
{
public string Name { get; }
public uint NumberOfIngredients { get; }
// A dictionary containing the quantity of
// each ingredient needed to make this potion
private Dictionary<Ingredient, uint> Recipe { get; }
// The Potion GameObject can contain the prefab
// corresponding to this potion and its components
private readonly GameObject _potionPrefab;
public PotionRecipe(string name, Dictionary<Ingredient, uint> recipe, GameObject potionPrefab = null)
{
Name = name;
Recipe = recipe;
NumberOfIngredients = (uint)Recipe.Values.Sum(u => u);
_potionPrefab = potionPrefab;
}
// Check if the recipe is made with the given ingredient and if the amount of it is correct
public bool IsMadeWith(Ingredient ingredient, uint quantity)
{
if (!Recipe.ContainsKey(ingredient)) return false;
return Recipe[ingredient] == quantity;
}
public GameObject CreatePotion()
{
// Instantiate the potion prefab or create a new empty object for demonstration
return _potionPrefab ? Object.Instantiate(_potionPrefab) : new GameObject(Name);
}
}
public class PotionBrewer
{
private readonly HashSet<PotionRecipe> _potionRecipes;
public PotionBrewer()
{
// Get the list of recipes from the repository
_potionRecipes = PotionRecipeRepository.Recipes;
}
public GameObject MakePotion(Queue<KeyValuePair<Ingredient, uint>> ingredients, uint numberOfIngredients)
{
if (ingredients.Count == 0) return null;
// Only test recipes that have the same number of ingredients in them
foreach (var recipe in _potionRecipes.Where(recipe => recipe.NumberOfIngredients == numberOfIngredients))
{
// Make a copy of the ingredient queue for each loop
var ingredientsCopy = ingredients;
Ingredient ingredient;
uint quantity;
// Iterate over the queue as long as the ingredients are matching
do
{
// If the ingredient Queue is empty, we matched all the ingredients
if (ingredientsCopy.Count == 0)
{
// Return the potion associated with this recipe
return recipe.CreatePotion();
}
(ingredient, quantity) = ingredientsCopy.Dequeue();
} while (recipe.IsMadeWith(ingredient, quantity));
}
// Otherwise we failed to make a potion out of this recipe
return null;
}
}
// This is a static repository made for this example
// It would be probably best to replace is by something configurable in the editor
static class PotionRecipeRepository
{
public static HashSet<PotionRecipe> Recipes { get; } = new();
static PotionRecipeRepository()
{
var healingPotion = new Dictionary<Ingredient, uint>()
{
[Ingredient.Garlic] = 2,
[Ingredient.Aloe] = 1
};
Recipes.Add(new PotionRecipe("Healing Potion", healingPotion));
var sicknessPotion = new Dictionary<Ingredient, uint>()
{
[Ingredient.Lichen] = 1,
[Ingredient.Garlic] = 1
};
Recipes.Add(new PotionRecipe("Sickness Potion", sicknessPotion));
}
}
要连接到大锅游戏对象,它使用 SphereCollider
public interface IBrewingCauldron
{
public void AddIngredient(Ingredient ingredient);
public GameObject BrewPotion();
}
[RequireComponent(typeof(SphereCollider))]
[RequireComponent(typeof(Rigidbody))]
public class Cauldron : MonoBehaviour, IBrewingCauldron
{
public Dictionary<Ingredient, uint> Ingredients { get; private set; } = new();
[SerializeField] private SphereCollider cauldronCollider;
private readonly PotionBrewer _potionBrewer = new();
private uint _numberOfIngredients;
private void Awake()
{
cauldronCollider ??= GetComponent<SphereCollider>();
// Set the collider as trigger to interact with ingredients GameObject
cauldronCollider.isTrigger = true;
}
public void AddIngredient(Ingredient ingredient)
{
// Keep track of the number of ingredients added
_numberOfIngredients++;
if (!Ingredients.ContainsKey(ingredient))
{
Ingredients[ingredient] = 1;
}
else
{
Ingredients[ingredient]++ ;
}
}
public GameObject BrewPotion()
{
var ingredientQueue = new Queue<KeyValuePair<Ingredient, uint>>(Ingredients);
var potionObject = _potionBrewer.MakePotion(ingredientQueue, _numberOfIngredients);
if (potionObject is not null)
{
Debug.Log($"We made a {potionObject.name} !");
potionObject.transform.position = transform.position;
}
else
{
Debug.Log("We failed to make any potion !!!");
}
Ingredients = new Dictionary<Ingredient, uint>();
_numberOfIngredients = 0;
return potionObject;
}
}
要附加到每个成分 GameObject,它们需要 Cauldron 的 GameObject 才能运行,如果它们没有它或者如果 GameObject 不包含 Cauldron 的脚本,它们将禁用自己。
public class PotionIngredient: MonoBehaviour
{
[SerializeField] private GameObject cauldronGameObject;
[SerializeField] private Ingredient ingredient;
private SphereCollider _cauldronCollider;
private IBrewingCauldron _cauldron;
private void Awake()
{
if (cauldronGameObject is not null)
{
_cauldron = cauldronGameObject.GetComponent<IBrewingCauldron>();
if (_cauldron is not null) return;
}
var ingredientObject = gameObject;
ingredientObject.name += " [IN ERROR]";
ingredientObject.SetActive(false);
throw new MissingComponentException($"{ingredientObject.name} is missing the cauldron gameobject");
}
private void Start()
{
_cauldronCollider = cauldronGameObject.GetComponent<SphereCollider>();
gameObject.name = ingredient.ToString();
}
private void OnTriggerEnter(Collider other)
{
if (other != _cauldronCollider) return;
_cauldron.AddIngredient(ingredient);
Destroy(gameObject);
}
}
[CustomEditor(typeof(Cauldron))]
public class CauldronEditor : UnityEditor.Editor
{
private Cauldron _cauldron;
private void OnEnable()
{
_cauldron = (Cauldron) target;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
EditorGUILayout.Space();
var ingredients = _cauldron.Ingredients;
if (ingredients.Any())
{
GUILayout.Label("Ingredients :");
EditorGUILayout.Space();
foreach (var (ingredient, quantity) in ingredients)
{
GUILayout.Label($"{ingredient} : {quantity}");
}
}
EditorGUILayout.Space();
if (GUILayout.Button("BrewPotion"))
{
_cauldron.BrewPotion();
}
}
}
这是一个小 gif,说明了我的解决方案如何在编辑器中工作。
有相当多的代码,所以如果你发现自己在使用它时遇到麻烦,我也可以分享一个工作项目的 github 链接。
希望这可以帮助。
【讨论】: