Могу ли я создать указатель для опроса поплавка из любого источника? - PullRequest
0 голосов
/ 28 мая 2018

Есть ли способ получить доступ ко всем различным типам объектов, унаследованных от одного и того же класса, используя одну и ту же ссылку в объекте без жесткого его кодирования?

Я работаю над единицей и хочу добавитьмодуль в моей игре, который может наблюдать за любым конкретным плавающим элементом в GameObject, а затем изменять другой плавающий элемент в другом GameObject, как только он достигает определенного значения.

Например: объект / модуль «Триггер», который устанавливает значениеголода = 1 в мозговом объекте, когда в объекте желудка достигается значение полноты <0,5. </p>

Поскольку у меня будет большое количество возможных комбинаций, я не хочу жестко кодировать его, создаваядочь класса триггеров для каждого из них.

Моя первоначальная идея состояла в том, чтобы использовать указатели, которые были бы направлены на хорошие поплавки для просмотра / изменения при инициализации.Но, очевидно, мы не можем использовать небезопасный код внутри итераторов (IEnumerator), поэтому я не уверен, как опрашивать значение Fullness.

Чтобы привести пример того, что я хотел бы:

public Class Trigger{
    private float* ToPoll;
    private float* ToChange;

    public void SetTrigger(float* poll, float* change){
        ToPoll = poll;
        ToChange = change;

        // the loop would be a IEnumerator, not a litteral loop
        while(*ToPoll < 0.5f){
            sleep(0.1)
        }
        *ToChange = 1f
    }
}

void Main(){
    Trigger trigger1, trigger2;
    trigger1.SetTrigger(&Stomach.fullness, &Brain.hunger)
    trigger2.SetTrigger(&Sun.activityLevel, &Earth.radiationLevel)
    // ^ Literally any float from any object
}

Есть ли у вас какие-либо идеи, как это сделать или как лучше их реализовать?

Ответы [ 2 ]

0 голосов
/ 29 мая 2018

Расширяя ответ от @kara, следующий код реализует независимые Stomach и Brain объекты и использует Being для их соединения.

Что Being знает о Stomach:

  • имеет NeedsFoodEvent

Что Being знает о Brain

  • есть OnRaiseIsHungryEvent (т. Е. "Голодный" сигнал - кого волнует, откуда он)
  • имеет IsHungryEvent

Имейте в виду, что в реальной реализации могут быть другие объекты, прослушивающие эти события.Например, может быть, у вас есть система эмоций, которая переключается на «голодный», и искусственный интеллект, который переключается в режим поиска пищи.Ни одна из систем не должна знать о другой, но обе могут реагировать на сигналы от Brain.В этой тривиальной реализации Being реагирует на сигнал Stomach и уведомляет и реагирует на Brain.

Важным выводом из этого является не конкретный метод вызова и реагирования на события.(в этом случае механизм .Net по умолчанию), но тот факт, что ни один объект не знает ничего о внутренностях другого (см. различные реализации HumanStomach и ZombieStomach), и вместо этого соединение подключено на более подходящем уровне(Being в этом случае).Также обратите внимание на зависимость от интерфейсов, что позволяет нам делать такие вещи, как создание гибридных существ (т.е. соединение ZombieBrain с HumanStomach).

Код был написан / протестирован с .Net Core CLI в качестве консолиприложение, но оно должно быть совместимо с большинством любых версий .Net> 3.5.

using System;
using System.Linq;
using System.Threading;

namespace so_example
{
    public class Program
    {
        static void Main(string[] args)
        {
            var person1 = new Being("Human 1", new HumanBrain(), new HumanStomach());
            var zombie1 = new Being("Zombie 1", new ZombieBrain(), new ZombieStomach());
            var hybrid1 = new Being("Hybrid 1", new ZombieBrain(), new HumanStomach());
            var hybrid2 = new Being("Hybrid 2", new HumanBrain(), new ZombieStomach());

            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }

    public class HungryEventArgs : EventArgs
    {
        public string Message { get; set; }
    }

    public interface IStomach
    {
        event EventHandler<HungryEventArgs> NeedsFoodEvent;
    }

    public class Stomach : IStomach
    {
        public event EventHandler<HungryEventArgs> NeedsFoodEvent;

        protected virtual void OnRaiseNeedsFoodEvent(HungryEventArgs e)
        {
            EventHandler<HungryEventArgs> handler = NeedsFoodEvent;

            if (handler != null)
            {
                handler(this, e);
            }
        }
    }

    public class HumanStomach : Stomach
    {
        private Timer _hungerTimer;

        public HumanStomach()
        {
            _hungerTimer = new Timer(o =>
            {
                // only trigger if breakfast, lunch or dinner (24h notation)
                if (new [] { 8, 13, 19 }.Any(t => t == DateTime.Now.Hour))
                {
                    OnRaiseNeedsFoodEvent(new HungryEventArgs { Message = "I'm empty!" });
                }
                else
                {
                    Console.WriteLine("It's not mealtime");
                }
            }, null, 1000, 1000);
        }
    }

    public class ZombieStomach : Stomach
    {
        private Timer _hungerTimer;

        public ZombieStomach()
        {
            _hungerTimer = new Timer(o =>
            {
                OnRaiseNeedsFoodEvent(new HungryEventArgs { Message = "Need brains in stomach!" });
            }, null, 1000, 1000);
        }
    }

    public interface IBrain
    {
        event EventHandler<HungryEventArgs> IsHungryEvent;
        void OnRaiseIsHungryEvent();
    }

    public class Brain : IBrain
    {
        public event EventHandler<HungryEventArgs> IsHungryEvent;
        protected string _hungryMessage;

        public void OnRaiseIsHungryEvent()
        {
            EventHandler<HungryEventArgs> handler = IsHungryEvent;

            if (handler != null)
            {
                var e = new HungryEventArgs
                {
                Message = _hungryMessage
                };

                handler(this, e);
            }
        }
    }

    public class HumanBrain : Brain
    {
        public HumanBrain()
        {
            _hungryMessage = "Need food!";
        }
    }

    public class ZombieBrain : Brain
    {
        public ZombieBrain()
        {
            _hungryMessage = "Braaaaaains!";
        }
    }

    public class Being
    {
        protected readonly IBrain _brain;
        protected readonly IStomach _stomach;
        private readonly string _name;

        public Being(string name, IBrain brain, IStomach stomach)
        {
            _stomach = stomach;
            _brain = brain;
            _name = name;

            _stomach.NeedsFoodEvent += (s, e) =>
            {
                Console.WriteLine($"{_name}: {e.Message}");
                _brain.OnRaiseIsHungryEvent();
            };

            _brain.IsHungryEvent += (s, e) =>
            {
                Console.WriteLine($"{_name}: {e.Message}");
            };
        }
    }
}

Некоторые заметки

Чтобы обеспечить вывод, я подделал вещи в реализациях 2 IStomach.HumanStomach создает таймер обратного вызова в конструкторе, который срабатывает каждую 1 секунду и проверяет, является ли текущий час временем приема пищи.Если это так, то поднимается NeedsFoodEvent.ZombieStomach также использует обратный вызов каждую 1 секунду, но он просто запускает NeedsFoodEvent каждый раз.В реальной реализации Unity вы, скорее всего, инициируете четность на основе какого-либо события из Unity - действия, предпринятого игроком, через некоторое заданное время и т. Д.

0 голосов
/ 28 мая 2018

Я не совсем уверен, что вы хотите сделать, но похоже, что вы хотите добавить триггеры к своим объектам.В моем понимании триггер должен быть делегатом в этом случае.

Вот пример того, как определить тип делегата и добавить список триггеров в ваш класс мозга.

Каждый мозг можеттеперь есть разные триггеры.Я настроил два производных мозга, чтобы показать вам, как с ним работать:

public class TestBrain
{
    private static int NextId = 1;
    public TestBrain(List<MyTrigger> triggers)
    {
        this.Triggers = triggers;
        this.Id = NextId++;
    }

    public int Id { get; private set; }
    public int Hunger { get; set; }
    public int StomachFullness { get; set; }
    public List<MyTrigger> Triggers { get; private set; }

    public void FireTriggers()
    {
        foreach (MyTrigger t in this.Triggers)
        {
            t.Invoke(this);
            this.StomachFullness = 100;
        }
    }

    public delegate void MyTrigger(TestBrain b);
}

public class HumanBrain : TestBrain
{
    static readonly List<MyTrigger> defaultHumanTriggers = new List<MyTrigger>()
    {
        b => { if (b.StomachFullness < 50) { b.Hunger = 1; Console.WriteLine("{0} is hungry..", b.Id); } }
    };

    public HumanBrain() : base(defaultHumanTriggers)
    {

    }
}

public class RobotBrain : TestBrain
{
    static readonly List<MyTrigger> defaultRobotTriggers = new List<MyTrigger>()
    {
        b => { if (b.StomachFullness < 50) { Console.WriteLine("{0} ignores hunger only want's some oil..", b.Id); } }
    };

    public RobotBrain() : base(defaultRobotTriggers)
    {

    }
}

static void Main()
{
    // Create some test-data
    List<TestBrain> brains = new List<TestBrain>()
    {
        new HumanBrain(),
        new HumanBrain(),
        new RobotBrain(),
        new HumanBrain(),
    };

    Console.WriteLine(" - - - Output our Testdata - - -");
    foreach (TestBrain b in brains)
    {
        Console.WriteLine("Status Brain {0} - Stomachfulness: {1} Hunger: {2}", b.Id, b.StomachFullness, b.Hunger);
    }

    Console.WriteLine(" - - - Empty stomachs - - -");
    foreach (TestBrain b in brains)
    {
        b.StomachFullness = 0;
    }

    Console.WriteLine(" - - - Fire triggers - - -");
    foreach (TestBrain b in brains)
    {
        b.FireTriggers();
    }

    Console.WriteLine(" - - - Output our Testdata - - -");
    foreach (TestBrain b in brains)
    {
        Console.WriteLine("Status Brain {0} - Stomachfulness: {1} Hunger: {2}", b.Id, b.StomachFullness, b.Hunger);
    }

}
...