无论是单机游戏还是网络游戏,丰富剧情的背后都离不开NPC默默无闻的工作;有的吆喝着卖药卖武器,有的做为宠物常拌左右,更灵活的还可以如《暗黑破坏神》那样,作为随从协助杀敌。强大的游戏离不开高智能的AI,而AI的背后则隐藏着更为复杂的技术,脚本系统就是其核心。想深入了解游戏脚本的朋友,我推荐阅读脚本AI与脚本引擎这篇文章。简单的说,脚本,以独立于游戏主程序代码之外为特征,可以让游戏设计者而不是游戏程序员编写和精制大部分的游戏结构,玩家同样可以很轻易的溶入到脚本的编写中并创造出一个全新的游戏世界,这一切都展示了脚本简单神奇之美。

本节,我将在上一节创建的梦幻西游世界中添加一些精灵NPC,并为它们附加一些简单的脚本AI,目的是让游戏显得更为生气勃勃,乐趣无边。

在与嵌入式脚本语言Lua & JavaScript的交互这篇文章中,我已很详细的讲解了如何实现Silverlight程序与JavaScript脚本的交互。于是,我们首先同样的需要在梦幻西游Demo源码中添加一个JavaScript脚本文件,并在嵌有游戏Demo的Index.htm页面里对该脚本进行调用:

接着,我们还要在程序中加入作为脚本调用对象的一些精灵自身方法;这里,我添加了两种方法:连续随机说话及连续随意跑动:

/// <summary>

/// 连续随机说话

/// </summary>

/// <param name="content"></param>

[ScriptableMember]

public void RandomSay(string content) {

Dialog dialog = new Dialog() {

Duration = 5,

LocatedSpriteWidth = BodyWidth,

Top = BodyTop

};

this.Children.Add(dialog);

dialog.Completed += (s, e) => {

this.Children.Remove(s as Dialog);

HtmlPage.Window.Invoke("RandomSay", this, new int[] { TalkContentCode });

};

dialog.Show(content);

}

/// <summary>

/// 连续随机跑动

/// </summary>

/// <param name="startX">走动范围起点X</param>

/// <param name="startY">走动范围起点Y</param>

/// <param name="endX">走动范围终点X</param>

/// <param name="endY">走动范围终点Y</param>

[ScriptableMember]

public void RandomMoveTo(int startX, int startY, int endX, int endY) {

Random random = new Random();

int x = random.Next(startX, endX);

int y = random.Next(startY, endY);

Point start = this.Coordinate;

Point end = new Point(x, y);

this.destination = end;

double spendTime = Math.Sqrt(Math.Pow((end.X - start.X) / LocatedScene.GridSize, 2) + Math.Pow((end.Y - start.Y) / LocatedScene.GridSize, 2)) * Speed * LocatedScene.GridSize; //计算总的移动花费

PointAnimation pointAnimation = new PointAnimation() {

To = end,

Duration = new Duration(TimeSpan.FromMilliseconds(spendTime))

};

Storyboard.SetTarget(pointAnimation, this);

Storyboard.SetTargetProperty(pointAnimation, new PropertyPath("Coordinate"));

Move();

StopMovingAnimation();

moveingAnimation = new Storyboard();

moveingAnimation.Children.Add(pointAnimation);

moveingAnimation.Completed += (s, e) => {

RandomMoveTo(startX, startY, endX, endY);

};

moveingAnimation.Begin();

}

需要再次强调,为了能让JavaScript脚本所识别,方法体必须标记为[ScriptableMember]。然后是书写相关脚本,代码截图如下(详细的请查看源码):

最后,我们通过在场景Scene.xml配置文件中为精灵设置相应的脚本参数即可:

<Sprites>

<Sprite Code="1" Weapon="-1" Mount="-1" X="85" Y="82" Direction="" Scripts="RandomSay:0"/>

<Sprite Code="2" Weapon="-1" Mount="-1" X="87" Y="82" Direction="2" Scripts="RandomSay:1"/>

<Sprite Code="1" Weapon="-1" Mount="-1" X="86" Y="78" Direction="4" Scripts="RandomMove:84,75,88,80"/>

<Sprite Code="3" Weapon="-1" Mount="-1" X="86" Y="78" Direction="3" Scripts="RandomSay:2_RandomMove:88,79,91,82"/>

</Sprites>

当场景初始化后,程序将通过如下代码自动解析该场景中每个精灵所持有的脚本:

//调用脚本

sprite.Loaded += (s, e) => {

string str = xSprite.Attribute("Scripts").Value;

if (str != "") {

string[] scripts = str.Split('_');

for (int n = 0; n < scripts.Count(); n++) {

string[] values = scripts[n].Split(':');

string[] args = values[1].Split(',');

HtmlPage.Window.Invoke(values[0], sprite, args);

……

}

}

};

脚本引擎的好处显而易见,通过在主程序中事先封装好对脚本的解析逻辑,配合上独立于主程序之外的xml数据存储文件加上JavaScript脚本文件即可分层实现。我们不仅可以单独的为某个精灵赋予某个脚本,也可以为其同时赋予多个脚本;更有价值的是,由于脚本文件的独立性与外置性(均可作为独立的配置文件存放于服务器上),修改时无需重新编译主程序而通过记事本即可轻松应对。当然了,有朋友或许会提出质疑,上面的随机说话脚本似乎还算凑合,但是随意移动脚本感觉牵强了些吧?主程序通过类似HtmlPage.Window.Invoke("RandomMove", sprite, new int[] { x1,y1,x2,y2 });的方法调用JavaScript的RandomMove方法,而该RandomMove方法又反回调用Silverlight中的RandomMoveTo方法,这不是典型的吃饱了撑着-没事找事。其实,本节我是为了向大家演示脚本的动态性与随意组合特性而特意制作的该两个简易的脚本。实际游戏开发中,脚本会更为复杂且多变,最直接的例子就是类似《征途》中的跑商任务。假设在游戏中,某个NPC与主角对话后,将骑上代号为5的坐骑,并启动A*寻路移动到特定的目的地坐标。于是我的做法是赋予该精灵代号为的脚本,该脚本的内容如下:

//0号脚本

function Script0(sprite, args) {

sprite.Mount = 5;

sprite.AStarMoveTo(args[0], args[1], args[2], args[3]);

}

意外情况1出现了,游戏测试时项目经理觉得5号坐骑不够绚,要求换上8号坐骑,并且让该NPC处于无敌状态,我们该怎么办?很简单,用记事本修改Script0:sprite.Mount = 8; sprite.State = States.Invincible; 就OK了。

意外情况2出现了,公司老总觉得该精灵在移动过程中应该不停的随机说话,话的内容为宣传本公司的产品及相关业务,我们该怎么办?很简单,用记事本打开Script.js,为talkContent增加一行,写上公司需要宣传的内容,并在Script0中添加: Rand omSay(sprite,3);

意外情况3出现了,NPC移动到目的地后应该立即消失,而程序员们漏掉了这点,该怎么办呢?同样很简单,首先在Sprite.cs中将精灵移动完成事件及销毁方法均标记为[ScriptableMember]:

[ScriptableMember]

public event EventHandler MovingCompleted;

[ScriptableMember]

public void Dispose(){…}

接着用记事本修改Script0添加:sprite.MovingCompleted = function(sender,e){sender.Dispose();}即可。

是否简单到不行?嘿嘿,这就是脚本系统的优越性。如果不嫌麻烦,你完全可以为每个NPC精灵设定不同的脚本,而同一个脚本又可以随时随地的进行内部结构逻辑更改;不仅如此,脚本还可更广泛的作用于场景、魔法、甚至主角身上(外挂?^ ^ HOHO),真正实现“心随我动,想怎么动就怎么动”。

说到此,不由又让我想到了Silverlight4中一个似乎不太起眼却又意义非凡的新特性:新增对9种脚本语言的支持,包括大名鼎鼎的Ruby和Python等。这条讯息如同Silverlight3中的HLSL渲染一样,必将为Silverlight的开发提供更大的便捷与灵活性,同样也将吸引更多的开发爱好者投身其中,因为你总能找到一款对得上你口味的Coding方式;没错,这就是Silverlight!

到此,Silverlight脚本引擎入门就讲解完了。随便提示一下,脚本毕竟不同于游戏主程序语言,它们无需编译带来的是如同反射般性能上的问题,过度频繁的调用(解析)将会使浏览器消耗持续占用大量的CPU资源,最典型的表现就是浏览器假死;因此作为辅助,建议大家还是酌情使用,不要颠倒了主次。

下面让我们运行程序,并将主角移动的宽敞的地方(放出的精灵一旦碰到障碍物会自动停止,当然,逻辑是可以在主程序中随意修改的),测试一下成果吧:

最后,我还想补充一些关于游戏素材的设定问题,因为这会直接影响到游戏的各方面性能。

一直有朋友反映梦幻西游的Demo似乎有些卡,问题到底出在哪?是Silverlight性能问题吗?非也,其实病根可回溯到精灵、地图背景、音乐等方面。首先,主角精灵所用的素材单位图片尺寸为200*200,且由身体、武器、坐骑3部分组成,也就是说同时有3张200*200的图片一直在动。这确实由于我个人业余时间有限而无法对每张图片设定偏移量所致的。并且,每个精灵的素材为整图,实际运行时还做了时时的切割处理,类似如下代码:

if (currentFrame > endFrame) {

currentFrame = startFrame;

}

double translateX = BodyWidth * currentFrame,

translateY = BodyHeight * (int)Direction;

body.Clip = new RectangleGeometry() {

Rect = new Rect() {

X = translateX,

Y = translateY,

Width = BodyWidth,

Height = BodyHeight,

}

};

body.RenderTransform = new TranslateTransform() { X = -translateX, Y = -translateY };

虽然便于简单的编写按需下载模块(Downloader),但是却不利于鼠标悬停时的Effect效果;因为如果此时对精灵进行渲染,就意味着Silverlight实际上渲染了整个大图,性能消耗可想而知是相当巨大的,大家可以从三国策Demo中体会到。解决办法也并非没有,我们可以通过对精灵名字进行高亮突出或在精灵脚底添加一个光环来标记玩家拾取了该精灵;其次,在地图方面我采用2.8D双背景地图,为了简单起见,前景地图切片均使用的是PNG格式,带A通道的性能消耗自然更高些。大家其实可以通过预先后台处理的方式对JPG进行去背,在不打断UI线程的前提下同样可以得到完美的呈现;再者,通过MediaElement去连续播放脚步声似乎有些杀鸡用牛刀的感觉,外加这个音效是从网上获取的,容量大了些,因此在配置较低的电脑上测试你会发现脚步声经常性的发不出,可见MediaElement进行连续操作时性能消耗也很大。而游戏中除了脚步声外,还有背景音乐等等。其实,游戏只需背景音乐就好了,而脚步声这个华而不实的东西大家还需思量再三,毕竟性能是第一位嘛;最后,由于所有代码均在场景编辑器的基础上修改而成,如果需要进行商业话使用,必须去掉至少一半的无关代码,比如3D场景变化等,并使用更多的Const、ReadOnly以及Static来替换掉那些公式中的动态参数。综上所述,如能从以上各角度出发并做出正确处理后,我保证游戏的最终整体性能至少提升50%以上。当然了,精致的游戏需要更为细腻的游戏设计思维及技巧,中游在线的《WOWO世界》就是一款杰出的代表,不远了,MMORPG大作即将粉墨登场,到时让世界一同来见证Silverlight给我们带来的奇迹!

一口气又写了这么多,总结:脚本系统作为游戏的重要辅助,对于提升游戏的灵活性及拓展性有着巨大的作用,不仅仅作为AI系统的一部分,同样它还能实现更丰富的功能,比如通过正则表达式屏蔽脏字眼以及代替XML进行数据存储等等。朋友们,Silverlight的游戏世界无比丰富,赶快加入到我们的行列中吧,未来或许会因你而改变!

在线演示地址:http://silverfuture.cn

源码请到目录中下载。

作者: 深蓝色右手
出处: http://alamiye010.cnblogs.com/
教程目录及源码下载: 点击进入( 欢迎加入WPF/Silverlight小组 WPF/Silverlight博客团队)
本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面显著位置给出原文连接,否则保留追究法律责任的权利。

原文链接: http://www.cnblogs.com/alamiye010/archive/2010/04/25/1719680.html

转载于:https://my.oschina.net/chen106106/blog/43695

Silverlight游戏设计(Game Design):(十一)梦幻西游(Demo) 之 “天人合一”②相关推荐

  1. Silverlight游戏设计(Game Design):目录

    孩提时那无数个难眠的夜晚,时常在思考着如何干掉BOSS的方案时已不知不觉的入睡.爱游戏,痴迷到难以割舍的状态.不断的在这个虚拟的轮回世界中扮演着一个又一个的主角而无法自拔-感受着日本人给我们重温自家的 ...

  2. Silverlight游戏设计(Game Design):(十五)如果还有梦(完)

    Silverlight游戏设计系列到此就全部结束了,原先计划至少为大家献上5个最有影响力的Demo,由于近期生活及工作的原因让我感到实在疲惫不堪因而未能如愿,还望大家见谅. Silverlight在游 ...

  3. Silverlight游戏设计(Game Design):(十)梦幻西游(Demo) 之 “天人合一”①

    与当年盛大通过代理<传奇>一举成名,九城代理<奇迹>一夜发迹完全不同,金山.网易凭借他们自主的研发团队,数年时间倾力打造了<剑侠>及<西游>等系列非常优 ...

  4. Silverlight游戏设计(Game Design):(七)创建基于场景编辑器的新游戏Demo

    场景编辑器的功能强大且灵活,从设计之初我已毫不惭愧的将其定位到"让Silverlight游戏场景架设更简单.更快捷"这样一个高度.源码公布后,很多朋友均迫切想知道如何将其运用到实际 ...

  5. Silverlight游戏设计(Game Design):(八)三国策(Demo) 之 “江山一统”①

    教程中无数次提到<三国>系列,那段荡气回肠的过去一直深刻烙印于心.我深爱中国的历史,因此我从不去公开评论政治,因为它是我的母亲:我执着于策略游戏,闲暇时爱不离手的依旧是NDS中的<三 ...

  6. Silverlight游戏设计(Game Design):(五)面向对象的思想塑造游戏对象

    传说,面向对象的开发模式最初是因为程序员偷懒而不小心诞生的.发展至今,人们从最初的热忠于讨论某某语言是否足够面向对象到现在开始更广泛的关注面向对象的思想而不是具体内容.面向对象的思想其实并不深奥,它存 ...

  7. Silverlight游戏设计(Game Design):(二)场景编辑器让游戏开发更美好

    如果哪天光荣告诉我:<三国志>系列将终结,我会义无返顾的用余下那点青春继续诠释这部中国历史经典题材游戏,已无法细数它占据了我多少童年的回忆,就好比曾有那么一群满腔热血的<梦幻模拟战& ...

  8. Silverlight游戏设计(Game Design):(一)游戏中斜视角的原理与分析

    "暗黑破坏神3"将采用45度斜视角设置这篇数月前貌似毫不起眼的新闻一直让我记忆深刻,不断的尝试去理解大师级的暗黑团队为何放弃目前主流的纯3D路线而回归到类2D,B大的一句话道破天机 ...

  9. Silverlight游戏设计(Game Design):(十二)帝国时代II(Demo) 之 “战争艺术”①

    即时战略类型游戏因其精确的微操,宏大的场面以及丰富的策略元素广受玩家的爱戴,<沙丘魔堡II>开创了真正意义上的即时战略游戏形态,之后Westwood创生了<命令与征服>系列加之 ...

最新文章

  1. 硬件安全模块如何启用AUTOSAR
  2. rn 滑动验证_继卷轴屏之后,OPPO又展示一款“滑动”概念手机
  3. nginx负载均衡集群
  4. web页面事件无响应,元素点击不到
  5. ubuntu文件、目录操作基本命令
  6. android 适合mvp模式,Android中的MVP:如何使Presenter层系统化?
  7. mysql left join 结果怎么这么慢
  8. 恒企自考_致自考生:想自考的人千千万万,遇到的困难却千篇一律
  9. JS实现下一天的显示
  10. ubuntu14.04 LTS Visual Studio Code 编辑器推荐
  11. 不好意思昨天断更了,今天聊聊创业
  12. php notice错误是什么意思,PHP中Notice错误常见解决方法
  13. U盘装系统中bios怎么设置USB启动(图文教程)
  14. CSS 绘制太阳系行星运行轨迹
  15. Python——数字排列组合
  16. Redis-设置过期时间及淘汰策略
  17. Exiftool not found metadata operations disabled 索尼相机查如何查快门教程
  18. java中flist cannot_关于usr/bin/ld: cannot find -lxxx问题总结
  19. .git文件泄露的一次渗透darkhole2
  20. h5微信本地调试 vue_VUE开发微信H5页面总结

热门文章

  1. js面试--ajax与性能优化
  2. Deepin+Docker+Redis5.0 安装 Redis集群
  3. matlab mha,ITK 实现多张图像转成单个nii.gz或mha文件案例
  4. linux proc 自动清理,Linux下清理内存和Cache方法 /proc/sys/vm/drop_caches
  5. 【黑科技】腾讯的 IOCanary 监控系统原理分析
  6. 我去蔚来试车了。。。
  7. JavaScript练手题 原生制作论坛发帖工具
  8. 在项目中没有发现问题,但是报错,sql set parameter exceptional
  9. 练手项目一:手机安全卫士
  10. 十年经典书籍下载地点