在 C# 中使用带有事件的动态代码?

发布于 2024-10-08 04:11:18 字数 529 浏览 1 评论 0原文

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

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

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

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

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

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

谢谢
——迈克尔

I'm currently implementing a script engine in a game I wrote using the C# "dynamic" feature.

How the system should work is when a script is called, it should register the events it listens for, then return control to the application. Then when an event that the script is listening for is fired the script should execute. One thing I'd really like to implement in the script engine is to have methods with certain names automatically bind to events. For example, the onTurnStart() listener should automatically bind to the turnStart event.

The scripts will mostly need to execute existing methods and change variable values in classes; stuff like player.takeDamage() and player.HP = somevalue. Most scripts will need to wait for the start of the players' turn and the end of the players' turn before being unloaded.

The complicated part is that these scripts need to be able to be changed without making any code changes to the game. (Security aside) the current plan is to have all the script changes automatically download when the game starts up to ensure all the players are using the same version of the scripts.

However I have three questions:
1) How do I register and unregister the script event listeners?
2) Can dynamic code listen for events?
3) (How) can I register events dynamically?

This is my first time using C#'s dynamic feature, so any help will be appreciated.

Thanks
--Michael

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风启觞 2024-10-15 04:11:18

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

如果您想为游戏编写“脚本”,您可能需要考虑集成 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 的要求。您的游戏启动器可以像下载一组脚本一样轻松地下载充满行为的类库! (如果没记错的话,《我的世界》的启动器就是这么做的)

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

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

一些入门链接:-

I'm not sure you've got the right end of the stick with the dynamic keyword. It doesn't by itself let you interpret new code at runtime. All it does it let you bypass static type checking by delaying the resolution of operations until runtime.

If you're looking to "script" your game, you probably want to take a look at integrating Lua, IronPython, or one of the other DLR languages:-

Otherwise, the usual thing to do is have something along the lines of:-

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

The question is, do you want to be able to add entirely new behaviours after compile-time? If so you'll need to expose some or all of your game-state to a scripting language.

Or is it sufficient to be able to compose existing behaviours at runtime?


After your edit

I'm still unsure, to be honest, about your requirement for the dynamic keyword and the DLR. Your game's launcher can download a class library full of behaviours just as easily as it can pull down a set of scripts! (That's what Minecraft's launcher does if memory serves)

If you absolutely must use the DLR then take a look at the links I posted. You'll have to expose as much of your game state as necessary to one of the DLR languages. Events get exposed as first-order-function properties. You shouldn't even need the "dynamic" keyword for basic stuff.

Quick example in IronPython:-

def DoSomethingWhenDamageTaken(*args):
  #do whatever...

player.DamageTaken += DoSomethingWhenDamageTaken

The player class:-

public class Player
{
  public event EventHandler DamageTaken;

  // ...
}

You set it up like:-

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

Some links to get you started:-

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文