我可以创建一个指针来从任何来源轮询浮点数吗?

时间:2018-05-28 13:19:43

标签: c# pointers unity3d unsafe

有没有办法在对象中使用相同的引用来访问从同一个类继承的所有不同类型的对象而不进行硬编码?

我在团结方面发展,我想在我的游戏中添加一个模块,可以在GameObject中观察任何特定的浮动,然后在达到某个值时更改另一个GameObject中的另一个浮点数。

作为一个例子:A&#34; Trigger&#34;对象/模块,当在胃对象中达到丰满度<0.5的值时,在大脑对象中设置饥饿值= 1。

由于我有大量可能的组合,我不想通过为每个组合创建一个触发类的女儿来对其进行硬编码。

我最初的想法是使用指针指向好的浮点数,以便在初始化时观察/更改。但显然,我们不能在迭代器(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 :(得分:1)

我不太确定,你想做什么,但听起来你想为你的对象添加触发器。在我的理解中,触发器在这种情况下应该是委托。

这是一个如何定义委托类型并向Brain类添加触发器列表的示例。

现在每个大脑都有不同的触发器。我设置了两个派生的大脑来向你展示如何使用它:

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);
    }

}

答案 1 :(得分:1)

扩展@kara的答案,以下代码实现独立的StomachBrain个对象,并使用Being连接它们。

Being了解Stomach

  • 它有NeedsFoodEvent

Being了解Brain

的内容
  • 有一个OnRaiseIsHungryEvent(即#34;饥饿&#34;信号 - 关心它来自哪里)
  • 它有IsHungryEvent

请记住,在实际实现中,可能会有其他对象侦听这些事件。例如也许你有一个情感系统,可以切换到&#34; hangry&#34;以及基于目标的人工智能将转向寻求食物的模式。两个系统都不需要知道另一个系统,但两者都可以响应来自Brain的信号。在这个简单的实现中,Being响应Stomach信号,并通知并响应Brain

这一点的重要内容不是提升和响应事件的具体方法(在这种情况下是默认的.Net机制),而是事实上两个对象都不知道关于另一个内部的内容(参见不同的实现) HumanStomachZombieStomach),而连接是在更合适的级别连接(在这种情况下为Being)。还要注意对接口的依赖,这使我们能够创建混合生物(即将ZombieBrainHumanStomach配对)。

使用.Net Core CLI编写/测试代码作为控制台应用程序,但它应与.Net&gt;的大多数版本兼容。 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秒触发一次并检查当前小时是否为进餐时间。如果是,则会提升NeedsFoodEventZombieStomach每1秒也会使用一次回调,但每次都会触发NeedsFoodEvent。在真正的Unity实现中,您可能会根据Unity中的某些事件触发偶数 - 玩家在一段预设时间后执行的操作等等。