RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1418007
Accepted
Максим Фисман
Максим Фисман
Asked:2022-08-08 04:14:43 +0000 UTC2022-08-08 04:14:43 +0000 UTC 2022-08-08 04:14:43 +0000 UTC

如何在 ScriptableObject 和 Enum 之间进行匹配

  • 772

对于玩家可以收集的物品,我有一个 ScriptableObject。它可以是硬币、棍子、石头、椰子等等。这样的对象具有名称、精灵和类型:

public enum FancyItemType
{
    Coin, Stick, Stone, Coconut
}

[CreateAssetMenu(fileName = "New Fancy Item", menuName = "New item", order = 51)]
public class FancyItem : ScriptableObject
{
    public FancyItemType Type;
    public Sprite Sprite;
    public string Name;
}

我在单元中创建并配置了这些对象。我想通过枚举 Enum 来接收这样的 FancyItem。例如,我想给玩家 10 根棍子:

void ReceiveBonus (FancyItemType type, int amount) {
    // Эта функция принимает ТИП предмета, который игроку надо выдать
    // Функция должна каким-то образом узнать спрайт и название предмета по типу
}
...
ReceiveBonus(FancyItemType.Stick, 10); // Передаем тип предмета -- палка, 10 штук

如何从枚举中获取包含所有数据的 FancyItem:精灵和名称?

我有这个想法:创建一个类,我将在检查器中向其中抛出对 ScriptableObject 的引用,它将通过 Enum 发出必要的引用:

public class FancyItemHelper : MonoBehaviour {
    [SerializeField] private FancyItem Coin, Stick, Stone, Coconut;

    public FancyItem GetItem (FancyItemType type) {
        switch (type) {
            case FancyItemType.Coin:
            return Coin;
            case FancyItemType.Stick:
            return Stick;
            case FancyItemType.Stone:
            return Stone;
            case FancyItemType.Coconut:
            return Coconut;
        }
    }
}

但是,这种方法对我来说太复杂了。我应该如何实现这个?

c# unity3d
  • 2 2 个回答
  • 35 Views

2 个回答

  • Voted
  1. Best Answer
    KOTlK
    2022-08-08T18:40:20Z2022-08-08T18:40:20Z

    有两把椅子。

    1. 使用字典
    2. 不要同时使用字典和 enams。

    第一个是最简单的 - 与 switch 中的操作相同,仅使用字典:

    using System.Collections.Generic;
    using UnityEngine;
    
    namespace ScriptableHell
    {
        public interface IConverter<in TIn, out TOut>
        {
            TOut Convert(TIn from);
        }
        
        public class EnumToItem : MonoBehaviour, IConverter<FancyItemType, FancyItem>
        {
            [SerializeField] private FancyItem _coin, _stick, _kamenbYaNeDam, _coconut;
    
            private Dictionary<FancyItemType, FancyItem> _items;
    
            private void Awake()
            {
                _items = new Dictionary<FancyItemType, FancyItem>()
                {
                    {FancyItemType.Coin, _coin},
                    {FancyItemType.Coconut, _coconut},
                    {FancyItemType.Stone, _kamenbYaNeDam},
                    {FancyItemType.Stick, _stick}
                };
            }
    
    
            public FancyItem Convert(FancyItemType from) => _items[from];
        }
    }
    

    第二个稍微难一些。重构代码并删除 enam。

    闪烁界面:

    using UnityEngine;
    
    namespace ScriptableHell
    {
        public interface IItem
        {
            Sprite Sprite { get; }
            string Name { get; }
        }
    }
    

    在 SO 继任者中实现它:

    using UnityEngine;
    
    namespace ScriptableHell
    {
        [CreateAssetMenu(menuName = "New/Item", fileName = "NewItem")]
        public class Item : ScriptableObject, IItem
        {
            [field: SerializeField] public Sprite Sprite { get; private set; }
            [field: SerializeField] public string Name { get; private set; }
        }
    }
    

    然后只需通过装饰器扩展这些项目。例如,可以捡起和扔掉的物品。再次接口:

    using UnityEngine;
    
    namespace ScriptableHell
    {
        public interface IPickable
        {
            IItem Pick();
        }
        
        public interface IThrowable
        {
            void Throw(Vector3 direction, float distance);
        }
    }
    

    及其实施:

    using System.Collections;
    using UnityEngine;
    
    namespace ScriptableHell
    {
        [RequireComponent(typeof(SpriteRenderer), typeof(Collider2D))]
        public class PickableItem : MonoBehaviour, IItem, IPickable, IThrowable
        {
            [SerializeField] private Item _item;
            
            private IItem _origin;
    
            private const float ThrowingTime = 3f;
    
            public Sprite Sprite => _origin.Sprite;
            public string Name => _origin.Name;
            
            public void Init(IItem origin)
            {
                if (_origin != null) return;
                
                _origin = origin;
                GetComponent<SpriteRenderer>().sprite = _origin.Sprite;
            }
            
            public IItem Pick()
            {
                Destroy(gameObject);
                return _origin;
            }
            
            public void Throw(Vector3 direction, float distance)
            {
                StartCoroutine(Throwing(direction, distance));
            }
    
            private void OnValidate()
            {
                if (_item == null)
                {
                    GetComponent<SpriteRenderer>().sprite = null;
                    return;
                }
    
                GetComponent<SpriteRenderer>().sprite = _item.Sprite;
            }
    
            private void Awake()
            {
                if (_item == null) return;
                _origin = _item;
            }
    
    
            private IEnumerator Throwing(Vector3 direction, float distance)
            {
                var startPosition = transform.position;
                var targetPosition = startPosition + direction * distance;
                var elapsedTime = 0f;
    
                while (elapsedTime < ThrowingTime)
                {
                    var progress = elapsedTime / ThrowingTime;
                    elapsedTime += Time.deltaTime;
    
                    transform.position = Vector3.Lerp(startPosition, targetPosition, progress);
                    yield return null;
                }
    
            }
            
        }
    }
    

    PickableItem 是穷人的装饰器,因为 MonoBehaviour 没有普通的构造函数,你必须这样做。_item 只是为了能够将这个可提升的物体推到舞台周围并在检查器中将其扔到所需的项目中。也是一个包含多个相同类型物品的箱子:容器:

    using System;
    using UnityEngine;
    
    namespace ScriptableHell
    {
        [Serializable]
        public class ItemContainer : IItem
        {
            [field: SerializeField] public Item Item { get; private set; }
            [field: SerializeField] public int Amount { get; private set; }
            
            
            public Sprite Sprite => Item.Sprite;
            public string Name => Item.Name;
        }
    }
    

    盒子:

    using UnityEngine;
    
    namespace ScriptableHell
    {
        public class BonusChest : MonoBehaviour, IPickable
        {
            [SerializeField] private ItemContainer _itemContainer;
            
            public IItem Pick()
            {
                Destroy(gameObject);
                return _itemContainer;
            }
        }
    }
    

    将引发一切的组件挂在播放器上:

    using UnityEngine;
    
    namespace ScriptableHell
    {
        [RequireComponent(typeof(Collider2D))]
        public class PickArea : MonoBehaviour
        {
            [SerializeField] private Player _player;
    
            private void OnTriggerEnter2D(Collider2D other)
            {
                if (other.TryGetComponent(out IPickable pickable))
                {
                    Pick(pickable.Pick());
                }
            }
    
            private void Pick(ItemContainer container)
            {
                for (var i = 0; i < container.Amount; i++)
                {
                    _player.GiveItem(container.Item);
                }
            }
            
            private void Pick(IItem item)
            {
                if (item is ItemContainer container)
                {
                    Pick(container);
                    return;
                }
                
                _player.GiveItem(item);
            }
        }
    }
    

    玩家本人:

    using System.Collections.Generic;
    using UnityEngine;
    
    namespace ScriptableHell
    {
        public class Player : MonoBehaviour
        {
            [SerializeField] private Transform _parent;
            [SerializeField] private ItemUIPresentation _prefab;
            [SerializeField] private PickableItem _throwablePrefab;
            [SerializeField] private float _speed = 5f;
            
            private readonly List<IItem> _items = new List<IItem>();
    
            private readonly List<ItemUIPresentation> _spawnedItems = new List<ItemUIPresentation>();
            
            public void GiveItem(IItem item)
            {
                _items.Add(item);
                Draw(_items);
            }
    
            private void Update()
            {
                var x = Input.GetAxis("Horizontal");
                var y = Input.GetAxis("Vertical");
    
                transform.position += new Vector3(x, y) * Time.deltaTime * _speed;
                
                if (Input.GetKeyDown(KeyCode.Space))
                {
                    Throw();
                }
            }
    
            private void Throw()
            {
                if (_items.Count == 0) return;
    
                var item = _items[0];
                var pickable = Instantiate(_throwablePrefab, transform.position + transform.up * 2f, Quaternion.identity);
                pickable.Init(item);
                pickable.Throw(transform.up, 5f);
    
                _items.Remove(item);
    
                Draw(_items);
            }
    
            private void Draw(List<IItem> items)
            {
                Clear();
    
                if (items.Count == 0) return;
                
                foreach (var item in items)
                {
                    var spawned = Instantiate(_prefab, _parent);
                    spawned.Init(item);
                    _spawnedItems.Add(spawned);
                }
            }
    
            private void Clear()
            {
                if (_spawnedItems.Count == 0) return;
                
                foreach (var item in _spawnedItems)
                {
                    Destroy(item.gameObject);
                }
                _spawnedItems.Clear();
            }
        }
    }
    

    我的玩家自己也画了库存,所以他需要一个库存单元的预制件,这也是一个穷人的装饰器:

    using UnityEngine;
    using UnityEngine.UI;
    
    namespace ScriptableHell
    {
        [RequireComponent(typeof(RectTransform), typeof(Image))]
        public class ItemUIPresentation : MonoBehaviour, IItem
        {
            private IItem _origin;
    
            public Sprite Sprite => _origin.Sprite;
            public string Name => _origin.Name;
    
            public void Init(IItem item)
            {
                _origin = item;
                GetComponent<Image>().sprite = _origin.Sprite;
            }
        }
    }
    

    不需要任何单例/字典/返回 null 等。

    我不知道为什么,但这 就是它的样子

    • 2
  2. Alemkhan Utepkaliev
    2022-08-08T05:06:02Z2022-08-08T05:06:02Z

    有一个在舞台上创建对象的选项,可能使用单例从任何地方访问(由您自行决定),脚本将存储一个包含您需要的元素的列表,在检查器中填充和更改

    
    public class GameItems : MonoBehaviour 
    {
        [SerializeField] private List<FancyItem> _fancyItems;
        // Сюда также можно добавить таким же способом любой другой список предметов
    
        public static Instance { get; private set;}
    
        private void Awake()
        {
           if (Instance == null)
           {
               Instance = this;
               DontDestroyOnLoad(this.gameObject);
    
               return;
           }
    
           Destroy(this.gameObject);
        }
    
        // Вернёт null если не смог найти по типу, можно обработать как угодно
        public FancyItem GetFancyItemByType(FancyItemType fancyItemType)
        {
           foreach (FancyItem fancyItem in _fancyItems)
           {
              if (fancyItem.Type == fancyItemType)
              {
                  return fancyItem;
              }
           }
    
           return null;
        }
    }
    

    也许这就是您需要的解决方案!

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5