【问题标题】:how to make Items spawn when you combine two other items together如何在将其他两个项目组合在一起时生成项目
【发布时间】:2022-08-03 22:10:36
【问题描述】:

我正在制作一个 2D 游戏,我想让用户结合集合中的两种草药来制作某种药水我不知道如何实现这一点,尽管在我制作场景和产卵脚本后我被困在这一步。知道如何在我将例如 2 种草药组合放入锅中以混合它们并得到我分配给它们的某种药水后如何生成药水吗?

  • 你想如何检测这个所谓的“组合在一起”?
  • 例如,我想从库存中挑选它们,然后他们去锅炉并给我分配给他们的某种药水。我知道这听起来很烦人,但我不知道这是否可能

标签: unity3d


【解决方案1】:

好的,请耐心等待,这将是一个沉重的问题,我将首先解释一下我的解决方案中的一些想法,然后你会找到我的代码以及它如何工作的演示。

我采用了“拖放”风格,将成分表示为可以拖到大锅上的游戏对象。

当一种成分接触到大锅时,它就会被添加到其中。为了简单起见,我使用对撞机进行了此操作。

至于魔药的制作,逻辑分为两类:

  • 药水配方类将包含制作药水所需的成分组合,以及制作此配方时应生成的预制件。
  • PotionCrafter 类负责用炼药锅中的成分制作药水

食谱可能需要不同数量的相同成分,如果我们尝试制作药水,而大锅中的成分与任何食谱都不匹配,则会丢失成分

出于复杂性和时间的考虑,我制作了一个无法通过检查器编辑的手工制作药水配方的静态“存储库”,这个解决方案对于一个大项目来说是不可行的,但它有助于说明我的解决方案是如何工作的。

谈完这些,这里有一些代码

(为了使代码更易于使用,我删除了所有命名空间,并将除 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));
    }
}

Cauldron.cs 组件:

要连接到大锅游戏对象,它使用 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;
    }
}

PotionIngredient.cs 组件

要附加到每个成分 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 链接。

希望这可以帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-25
    • 2023-03-22
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多