与AS3/GML类似的C#语句

本文关键字:语句 AS3 GML | 更新日期: 2023-09-27 18:20:00

我正在使用Monogame制作一个游戏,我一直在尝试如何实现一个与AS3和GML的语句类似的函数。

到目前为止,我有一个可以工作的系统,但并不是完全按照我想要的方式。我将我的游戏对象存储在列表词典中。这样我就可以找到我想要访问的特定类型的对象,而不必遍历所有对象的列表。使用的键是类型的名称。

public static Dictionary<string, List<GameObject>> All = 
    new Dictionary<string, List<GameObject>>();

我使用AllOf访问所有特定类型的对象。如果字典中存在包含该类型的列表,则返回该列表,否则返回空列表。

public static List<GameObject> AllOf(Type type)
{
    string key = type.Name;
    if(All.ContainsKey(key))
    {
        return All[key];
    }
    return new List<GameObject>();
}

如何实现的示例

public override void Update(GameTime gameTime)
{
    List<GameObject> list = Instance.AllOf(typeof(Dummy));
    for(int i = 0; i < list.Count; i++)
    {
        list[i].Update(gameTime);
        list[i].foo += bar;
    }
}

但我宁愿使用类似于AS3/GML with语句的东西,它也允许执行其他非成员代码。

with(typeof(Dummy))
{
    Update(gameTime);
    foo += bar;
    int fooBar = 2;
    someObject.someMemberFunction(fooBar);
}

有办法做到这一点吗?我的最终目标只是让我的代码看起来更干净,并且更容易进行大量更改,而不必每次都键入for循环。

与AS3/GML类似的C#语句

C#中不存在这样的语法,但可以访问for中与集合无关的方法:

public override void Update(GameTime gameTime)
{
    List<GameObject> list = Instance.AllOf(typeof(Dummy));
    for(int i = 0; i < list.Count; i++)
    {
        list[i].Update(gameTime);
        list[i].foo += bar;
        int fooBar = 2;
        someObject.someMemberFunction(fooBar);
    }
}

请注意,如果您不需要索引器,也可以使用foreach,这是一种清洁器:

foreach(var item in list)
{
    item.Update(gameTime);
    item.foo += bar;
    int fooBar = 2;
    someObject.someMemberFunction(fooBar);
}

尝试

using(Object myObject = new Object()){
}

我想这可能是你想要用的?

我为这个用例提供了一个小的解决方案。这可能有点像死柱,但这是一个非常巧妙的解决方案。此外,我认为在提出这个问题时,所有需要的C#功能都已经存在了。

通过使用某种形式的委托作为静态方法的参数,并传递lambda作为该参数,可以执行与GMLwith(x){}非常类似的操作。该函数甚至可以被泛型化,并且可以通过using static语句在没有类名的情况下调用它。您需要显式提供类型化/命名参数,但可能的。你需要将其连接到你自己的类型,但总体思路是:

namespace NiftyStuff {
    public static class With {
        public static void with<T>(Action<T> proc) where T : GameObj {
            var typeName = typeof(T).Name;
            foreach (var item in GameObj.AllOf(typeName)) { proc((T)item); }
        }
    }
    public class GameObj {
        private static Dictionary<string, List<GameObj>> All = new Dictionary<string, List<GameObj>>();
        public static List<GameObj> AllOf(string name) {
            return All.ContainsKey(name) ? All[name] : null;
        }
        public static void Add(GameObj foo) {
            string typeName = foo.GetType().Name;
            List<GameObj> foos = All.ContainsKey(typeName) ? All[typeName] : (All[typeName] = new List<GameObj>());
            foos.Add(foo);
        }
        public float x, y, angle;
        public GameObj() { x = y = angle = 0; }
        public void Destroy() { AllOf(GetType().Name)?.Remove(this); }
    }
    public class Enemy : GameObj {
        public float maxHealth, curHealth;
        public Enemy() : base() { maxHealth = curHealth = 300; }
        public Enemy(float health) : base() { maxHealth = curHealth = health; }
        public bool Damage(float amt) {
            if (curHealth > 0) {
                curHealth -= amt;
                return curHealth <= 0;
            }
            return false;
        }
    }
    public class Pumpkin : GameObj {
        public bool exists = false;
        public Pumpkin() : base() { exists = true; }
        public bool LookAt() { return (exists = !exists); }
    }
}

实际使用上述代码的工作方式如下:

using NiftyStuff;
using static NiftyStuff.With;
//...
    with ((Enemy e) => {
        if (e.Damage(50)) {
            Log("Made a kill!"); // Whatever log function you have...
        }
    });
    with ((Pumpkin p) => {
        if (p.LookAt()) {
            Log("You see the pumpkin");
        } else {
            Log("You no longer see the pumpkin");
        }
    });

虽然与GML的with语句不完全一样,但它至少可以让您针对某些类型的所有注册对象运行代码。

一个重要的注意事项是,不能用这种方式破坏集合内部的对象(因为在迭代集合时同时修改集合)。你需要收集所有要销毁的对象,然后在"全部"中将它们从列表中删除,通常在游戏循环中,这是在一帧结束时完成的。

希望这能有所帮助,尽管已经过时2年了。