【问题标题】:Using Dynamic Code in C# with Events?在 C# 中使用带有事件的动态代码?
【发布时间】:2011-05-28 08:11:18
【问题描述】:

我目前正在使用 C#“动态”功能编写的游戏中实现脚本引擎。

系统的工作方式是当调用脚本时,它应该注册它所侦听的事件,然后将控制权返回给应用程序。然后,当触发脚本正在侦听的事件时,脚本应该执行。我真正想在脚本引擎中实现的一件事是让具有特定名称的方法自动绑定到事件。例如,onTurnStart() 监听器应该自动绑定到 turnStart 事件。

脚本主要需要执行现有方法并更改类中的变量值;像 player.takeDamage() 和 player.HP = somevalue 这样的东西。大多数脚本需要等待玩家回合开始和玩家回合结束后才能被卸载。

复杂的部分是这些脚本需要能够在不对游戏进行任何代码更改的情况下进行更改。 (抛开安全问题不谈)目前的计划是在游戏启动时自动下载所有脚本更改,以确保所有玩家都使用相同版本的脚本。

但是我有三个问题:
1) 如何注册和注销脚本事件监听器?
2) 动态代码可以监听事件吗?
3)(如何)我可以动态注册事件吗?

这是我第一次使用 C# 的动态功能,因此将不胜感激。

谢谢
——迈克尔

【问题讨论】:

    标签: c# events scripting dynamic


    【解决方案1】:

    我不确定您是否正确地使用了动态关键字。它本身并不能让您在运行时解释新代码。它所做的一切就是让您通过将操作的解析延迟到运行时来绕过静态类型检查。

    如果您希望为您的游戏“编写脚本”,您可能想看看集成 Lua、IronPython 或其他 DLR 语言之一:-

    否则,通常的做法是:-

    interface IBehavior
    {
      // Binds whatever events this behaviour needs, and optionally adds
      // itself to a collection of behaviours on the entity.
      void Register(Entity entity);
    }
    
    // Just an example
    public abstract class TurnEndingDoSomethingBehavior
    {
      public void Register(Entity entity)
      {
        entity.TurnEnding += (s, e) => DoSomething();
      }
    
      private abstract void DoSomething();
    }
    

    问题是,您是否希望能够在编译时添加全新的行为?如果是这样,您需要将部分或全部游戏状态暴露给脚本语言。

    或者能够在运行时组合现有行为就足够了?


    修改后

    老实说,我仍然不确定您对动态关键字和 DLR 的要求。您的游戏启动器可以像下载一组脚本一样轻松地下载一个充满行为的类库! (如果有记忆,这就是 Minecraft 的启动器所做的)

    如果您绝对必须使用 DLR,请查看我发布的链接。您必须根据需要向其中一种 DLR 语言公开尽可能多的游戏状态。事件作为一阶函数属性公开。您甚至不需要为基本内容添加“动态”关键字。

    IronPython 中的快速示例:-

    def DoSomethingWhenDamageTaken(*args):
      #do whatever...
    
    player.DamageTaken += DoSomethingWhenDamageTaken
    

    播放器类:-

    public class Player
    {
      public event EventHandler DamageTaken;
    
      // ...
    }
    

    你可以这样设置:-

    ScriptEngine engine = Python.CreateEngine();
    ScriptRuntime runtime = engine.Runtime;
    ScriptScope scope = runtime.CreateScope();
    
    // In an actual application you might even be
    // parsing the script from user input.
    ScriptSource source = engine.CreateScriptSourceFromFile(...); 
    
    Player p = new Player();
    scope.SetVariable("player", p);
    source.Execute(scope);
    

    一些帮助您入门的链接:-

    【讨论】:

    • 我已经编辑了原始帖子以使大多数帖子更加清晰。感谢您迄今为止的反馈。
    • 这篇文章充满了代码和示例,我希望我能为您提供所有帮助。
    猜你喜欢
    • 2022-09-25
    • 2014-10-12
    • 1970-01-01
    • 1970-01-01
    • 2015-03-30
    • 2011-06-10
    • 2020-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多