将脚本function添加到.NET应用程序
我有一个用C#编写的小游戏。 它使用数据库作为后端。 这是一个交易卡游戏 ,我想实现卡的function作为一个脚本。
我的意思是我基本上有一个卡类实现的接口ICard
( public class Card056 : ICard
),它包含了被游戏调用的函数。
现在,为了使事物可维护/可修改,我希望将每张卡片的类作为数据库中的源代码,并在首次使用时进行基本编译。 所以,当我不得不添加/更换一张卡片时,我只是把它添加到数据库中,并告诉我的应用程序刷新,而不需要任何程序集的部署(特别是因为我们会谈论每张卡片1个程序集,这意味着数百个程序集) 。
那可能吗? 从源文件注册一个类,然后实例化它等
ICard Cards[current] = new MyGame.CardLibrary.Card056(); Cards[current].OnEnterPlay(ref currentGameState);
这个语言是C#,但是如果可以用任何.NET语言编写脚本,那么额外的奖励也是可以的。
Oleg Shilo的C#脚本解决scheme(在代码项目中 )真的是在应用程序中提供脚本function的一个很好的介绍。
另一种方法是考虑专门为脚本编写的语言,如IronRuby , IronPython或Lua 。
IronPython和IronRuby现在都可用。
有关embeddedIronPython的指南,请参阅如何通过10个简单步骤在现有应用程序中embeddedIronPython脚本支持 。
Lua是游戏中常用的脚本语言。 有一个.NET的Lua编译器,可以从CodePlex – http://www.codeplex.com/Nua获得;
如果您想了解如何在.NET中构build编译器,那么该代码库是一个很好的阅读。
完全不同的angular度是尝试PowerShell 。 有很多将PowerShellembedded到应用程序中的例子 – 下面是关于PowerShell隧道的一个彻底的项目
您可能可以使用IronRuby。
否则,我build议你有一个目录放置预编译程序集。 然后你可以在数据库中引用程序集和类,并使用reflection在运行时加载正确的程序集。
如果你真的想在运行时编译,你可以使用CodeDOM,那么你可以使用reflection来加载dynamic程序集。 MSDN文章可能会帮助 。
如果你不想使用DLR,你可以使用Boo(它有一个解释器),或者你可以考虑CodePlex上的Script.NET(S#)项目 。 使用Boo解决scheme,您可以select编译后的脚本或使用解释器,Boo通过其开放式编译器体系结构提供了一种很好的脚本语言,具有灵活的语法和可扩展的语言。 尽pipeScript.NET看起来不错,但您可以轻松地扩展该语言以及其开源项目,并使用非常友好的编译器生成器( Irony.net )。
您可以使用任何DLR语言,这提供了一种真正轻松托pipe自己的脚本平台的方法。 但是,您不必为此使用脚本语言。 您可以使用C#并使用C#代码提供程序进行编译。 只要您将其加载到自己的AppDomain中,就可以将其加载和卸载到您的心中。
我build议使用LuaInterface,因为它完全实现了Lua,看起来Nua并不完整,并且可能不会实现一些非常有用的function(协程等)。
如果你想使用一些外部预装的Lua模块,我build议使用1.5.x的版本,而不是build立完全托pipe代码的2.x系列,而不能公开必要的C API。
我正在使用LuaInterface1.3 + Lua 5.0 NET 1.1应用程序。
Boo的问题在于,每当您在运行时parsing/编译/评估您的代码时,都会创build一组boo类,这样您将会发生内存泄漏。
另一方面,Lua并没有这样做,所以它非常稳定,并且工作得非常好(我可以将C#中的对象传递给Lua并向后传递)。
到目前为止,我还没有把它放在PROD,但似乎非常有前途。
我使用LuaInterface + Lua 5.0在PROD中遇到了内存泄漏问题 ,因此我使用了Lua 5.2,并直接使用DllImport链接到C#中。 内存泄漏在LuaInterface库中。
一旦我这样做,我所有的内存泄漏都消失了,应用程序非常稳定。
我司销售的主要应用程序做了非常相似的事情来提供客户定制(这意味着我不能发布任何来源)。 我们有一个加载dynamicVB.NET脚本的C#应用程序(尽pipe可以很容易地支持任何.NET语言 – 因为定制团队来自ASP背景,所以selectVB)。
使用.NET的CodeDom,我们使用VB CodeDomProvider
编译数据库中的脚本(烦人的是它默认为.NET 2,如果你想支持3.5特性,你需要传递一个带有“CompilerVersion”=“v3.5”的字典到它的构造函数)。 使用CodeDomProvider.CompileAssemblyFromSource
方法编译它(您可以传递设置来强制它在内存中编译。
这会导致内存中有数百个程序集,但是您可以将所有dynamic类的代码放在一个程序集中,并在发生任何更改时重新编译整个程序集。 这样做的好处是可以在testing时添加一个用PDB在磁盘上编译的标志,允许您通过dynamic代码进行debugging。
是的,我考虑过这个问题,但是很快我发现另一个特定领域语言(DSL)会有点太过分了。
从本质上讲,他们需要以不可预知的方式与我的游戏状态进行交互。 例如,一张牌可以有一个规则:“当这张牌进场时,除敌人被祝福之外,你所有的不死生物都会对飞行的敌人造成+3的攻击。 由于交易卡游戏是基于回合的,所以GameState Manager将会触发OnStageX事件并让卡片以卡片所需的任何方式修改其他卡片或GameState。
如果我尝试创build一个DSL,我必须实现一个相当大的function集,并且可能不断更新它,这将维护工作转移到另一个部分而不实际删除它。
这就是为什么我想留在一个“真正的”.NET语言基本上能够发射事件,让卡片以任何方式(在代码访问安全性的范围内)操纵游戏状态的原因。
.NET(5.0?)的下一个版本已经有很多关于打开“编译器即服务”的讨论,这将使直接脚本评估成为可能。