暴米花 发表于 2006-12-8 11:43:00

底层脚本、高层脚本和微线程

<strong><br/><br/></strong>&nbsp;&nbsp;&nbsp;&nbsp;脚本在游戏中占据的位置是不可质疑的。不仅仅是由于其解释语言的特性(修改不需要对源代码进行编译);而且游戏的逻辑采用脚本语言更加容易描述。对于底层脚本和高层脚本,至今尚未有一个明确的定义。本系列文章试图从技术的角度,在概念上对底层和高层脚本进行一个划分,并对微线程在游戏引擎的引入的必要性进行分析。<br/>以下面网上对于脚本与游戏主循环的同步作为一个引子,展开本文研究的话题。<br/>&nbsp;&nbsp;&nbsp;&nbsp;“基于命令的脚本语言可以用来控制游戏中的non-player&nbsp;character,可以让他们"自主"的转悠、动作,使游戏更生动。当然,这些NPCs的动作也可以使用Hard-code的方式跟游戏引擎写在同一处,显而易见的是,这种方式将游戏的底层(Gameengine)和游戏的高层(Logic)混在一起,对于游戏的扩展、游戏的调试。。。。没有可取之处。<br/>&nbsp;&nbsp;&nbsp;&nbsp;下面的这些脚本片断用来控制一个NPC,每个命令的含义很直观。<br/>//&nbsp;RPG&nbsp;NPC&nbsp;Script<br/>//&nbsp;A&nbsp;Command-Based&nbsp;Language&nbsp;Demo<br/>//&nbsp;----&nbsp;Walking&nbsp;in&nbsp;a&nbsp;square&nbsp;pattern<br/>ShowTextBox&nbsp;"THAT&nbsp;WAS&nbsp;SIMPLE&nbsp;ENOUGH."<br/>Pause&nbsp;2400<br/>ShowTextBox&nbsp;"NOW&nbsp;LET'S&nbsp;WALK&nbsp;IN&nbsp;A&nbsp;SQUARE&nbsp;PATTERN."<br/>Pause&nbsp;2400<br/>HideTextBox<br/>Pause&nbsp;800<br/>SetCharDir&nbsp;"Right"<br/>MoveChar&nbsp;40&nbsp;0<br/>MoveChar&nbsp;8&nbsp;8<br/>SetCharDir&nbsp;"Down"<br/>MoveChar&nbsp;0&nbsp;80<br/>MoveChar&nbsp;-8&nbsp;8<br/>&nbsp;&nbsp;&nbsp;&nbsp;要让这个脚本控制一个NPC,我们必须编写一个"脚本解释器",从脚本文件内读入每行命令、解释、并执行。需要注意的是,如何与游戏的主循环同步呢?也就是说,如果我们顺序执行这个脚本序列(在一个循环中),则整个程序的其他部分得不到响应,那就会出现这样的情况:在NPC运动过程中,游戏主角将不会响应用户的控制。如何解决这个问题呢?多线程(Multi-Thread)?这是个自找麻烦的做法。<br/>&nbsp;&nbsp;&nbsp;&nbsp;实际上,为了解决面临的同步问题,我们可以把脚本的作用限制在"改变程序的状态"这个范围内,例如:NPC要从A处移动到B处,我们不会在解释这个命令脚本的时候做这样的事情:把代表NPC的图片从A处以一个微小的增量移动,直到它到到B处,这样就导致了"不同步",我们要做的是,设置这个NPC的状态为正在移动,并记录目标点的坐标,然后,在游戏的主循环中,我们检测到这个NPC的状态,如果它没有到目标点,我们就继续移动它。<br/>同时需要注意的是,如果这个命令没有执行完,那么我们应该不允许下一个命令的开始,也就是说,我们在游戏的主循环中,依次解释每一条命令,并且设置相应的状态值,但是遇到类似"移动"这样的命令,如果没有执行完这条命令,程序将不会解释执行下一条命令,也就是说,移动到某处不是一蹴而就的,是需要过程的。” <p></p><p>&nbsp;&nbsp;&nbsp;&nbsp;本文将针对六个方面的问题展开讨论:高层脚本和底层脚本;微线程;微线程与主循环同步;脚本组织与对象脚本;脚本与任务系统;网游客户端脚本与游戏的交互性。”<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;本文内容仅代表星河工作室针对此类问题的观点。相关的技术在星河平台2.1版本全部得到支持(SRPV2.1),可访问相关网站:<a href="http://www.srplab.com/" target="_blank"><font color="#9c0000">http://www.srplab.com</font></a>。</p><p>一:高层脚本和底层脚本</p><p>&nbsp;&nbsp;&nbsp;&nbsp;高层脚本更适于描述逻辑,上述的例子是一个高层脚本,下面的一段描述也是高层脚本:<br/>走到(死水沼泽,56,99)<br/>等待(500)毫秒<br/>走到(死水沼泽,56,42)<br/>找到(首饰店掌柜)(死水沼泽【7】,52,31)<br/>与首饰店掌柜对话<br/>选择【出售首饰】<br/>自动卖掉【手镯】类物品<br/>自动卖掉【戒指】类物品<br/>自动卖掉【项链】类物品<br/>选择【返回】<br/>结束对话<br/>找到【服装店掌柜】(死水沼泽【7】,49,37)<br/>与【服装店掌柜】对话<br/>选择【出售衣服】<br/>自动卖掉【衣服】类别物品<br/>自动卖掉【头盔】类别物品<br/>选择【返回】<br/>结束对话<br/>&nbsp;&nbsp;&nbsp;&nbsp;对于底层脚本,更加类似于函数,如下一段代码(摘自:Game&nbsp;Programming&nbsp;With&nbsp;Python,lua&nbsp;and&nbsp;Ruby)是底层脚本:<br/>function&nbsp;render_frame(screen,&nbsp;background)<br/>--&nbsp;When&nbsp;called&nbsp;renders&nbsp;a&nbsp;new&nbsp;frame.<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;First&nbsp;clears&nbsp;the&nbsp;screen<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SDL.SDL_FillRect(screen,&nbsp;NULL,&nbsp;background);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;re-draws&nbsp;each&nbsp;actor&nbsp;in&nbsp;gamestate.actors<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;i&nbsp;=&nbsp;1,&nbsp;getn(gamestate.actors)&nbsp;do<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gamestate.actors:render(screen)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;updates<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SDL.SDL_UpdateRect(screen,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0)<br/>end<br/>function&nbsp;engine_init(argv)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local&nbsp;width,&nbsp;height;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local&nbsp;video_bpp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local&nbsp;videoflags;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;videoflags&nbsp;=&nbsp;SDL.bit_or(SDL.SDL_HWSURFACE,&nbsp;SDL.SDL_ANYFORMAT)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width&nbsp;=&nbsp;800<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height&nbsp;=&nbsp;600<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;video_bpp&nbsp;=&nbsp;16<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;Set&nbsp;video&nbsp;mode<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gamestate.screen&nbsp;=&nbsp;SDL.SDL_SetVideoMode(width,&nbsp;height,&nbsp;video_bpp,&nbsp;videoflags);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gamestate.background&nbsp;=&nbsp;SDL.SDL_MapRGB(gamestate.screen.format,&nbsp;0,&nbsp;0,&nbsp;0);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SDL.SDL_ShowCursor(0)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;initialize&nbsp;the&nbsp;timer/ticks<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gamestate.begin_time&nbsp;=&nbsp;SDL.SDL_GetTicks();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gamestate.last_update_ticks&nbsp;=&nbsp;gamestate.begin_time;<br/>end</p><p>&nbsp;&nbsp;&nbsp;&nbsp;那么什么是高层脚本,什么是底层脚本,两者之间的区别是什么?<br/>&nbsp;&nbsp;&nbsp;&nbsp;如果脚本不能够顺序执行完毕,则该脚本是一个高层脚本。高层脚本更加适合于描述游戏逻辑。如果脚本能够顺序执行完毕,则该脚本是一个底层脚本,底层脚本使游戏在更新局部功能的时候不需要重新编译。<br/>对于高层脚本描述游戏逻辑,有时一条脚本命令需要一个执行过程,比如:走到(死水沼泽,56,99),该脚本命令执行后,会触发一个移动的动作,需要几帧甚至更多的游戏循环,才能够执行完毕。类似命令“MoveChar&nbsp;0&nbsp;80”同样。因此在命令执行过程中,需要让出CPU,给游戏其它部分运行,直到达到目标状态。<br/>对于底层脚本,不存在类似的问题,执行过程都是顺序的,中间不中断。<br/>高层脚本执行如何进行,采用类似前面,通过引入状态的方法进行控制,该方法有哪些缺陷?有没有更好的方案,答案是有,哪就是采用微线程。<br/>我们在第二部分讨论微线程,并与一个通过引入状态进行控制的代码进行比较。<br/><i>abc</i><br/></p>
页: [1]
查看完整版本: 底层脚本、高层脚本和微线程