﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-swo2006-文章分类-游戏设计</title><link>http://www.cppblog.com/swo2006/category/3206.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 03:06:05 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 03:06:05 GMT</pubDate><ttl>60</ttl><item><title>学习使用XML引擎XQEngine </title><link>http://www.cppblog.com/swo2006/articles/17388.html</link><dc:creator>swo</dc:creator><author>swo</author><pubDate>Sun, 07 Jan 2007 06:21:00 GMT</pubDate><guid>http://www.cppblog.com/swo2006/articles/17388.html</guid><wfw:comment>http://www.cppblog.com/swo2006/comments/17388.html</wfw:comment><comments>http://www.cppblog.com/swo2006/articles/17388.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/swo2006/comments/commentRss/17388.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/swo2006/services/trackbacks/17388.html</trackback:ping><description><![CDATA[<div align="center">
<span class="ccwheading02">学习使用XML引擎XQEngine</span>&nbsp;<span class="Text2"></span></div>
<iframe name="abc" src="http://www.knowsky.com/googlead.htm" frameborder="no" height="60" scrolling="no" width="468">
</iframe>
<hr size="1">
<div style="overflow: hidden; visibility: hidden; width: 1px; height: 1px; color: white;">转自：动态网站制作南
| www.knowsky.com</div>
<span class="t18">最近我一直在寻找XML搜索工具，我编写的应用程序需要定期的搜索一些有关联的XML文件，我本来的意思是为了看一看文件中是否有与我想要的数据匹配的
数据，但是有时候，我也想把找到的这些数据输出出来。一开始，我试用了一下XSLT和XPath，想通过把搜索的问题转化成使用XSLT能够解决的问题，
但是经过一段时间的试验，我发现，使用XSLT并没有真正解决我想要处理的搜索问题，因为我想要输出的数据是使用逗号隔开的数，而XSLT不能满足这个要
求，而且XLST也不能提供全文搜索功能。然后我想尝试一下使用XML查询语言(XQL)，来看看能不能解决，所以我仔细的着了一下XQL的各种版本的实
现，很巧，正好发现一个叫XQEngine的小工具能解决这个问题，所以，在本文中我想介绍一下如何使用XQEngine来在你的XML文件中搜寻你想要
找的字符串数据。<br><br>　　XQEngine可以在www.fatdog.com网站下找到，它是一个JavaBean，使用一个SAX解析器来索引一个或多个XML文档，然后你就可以在这些文档中进行复合式搜索了。它所使用的搜索语言是XQL的超集，与XPath有相似的语法。<br><br>
使用XQEngine的Java类必须实现一个result()方法，完成搜索后，引擎将调用这个方法把搜索结果传到result()方法中，可以使用
三种显示数据的格式来输出数据结果。使用命令行参数指明你所需要的搜索参数，比如说你可以指明一个文件假如含有stop这个词，就不会被索引；又如你可以
在参数中命令引擎忽略那些少于指定子数的词。<br><br>　　下面，我给出了一个使用XQEngine的例程，现在让我们来分析一下。首先，main
()方法实例化一个搜索引擎：XmlEngine engine = new
XmlEngine()，然后它从命令行中取得文件名、返回结果格式和搜索请求这三个参数，再使用各种配置方法来设置引擎，接着调用
setSaxParserName()方法来设置SAX解析器的全名，因为我们使用的是Xerces解析器，所以要用到
"org.apache.xerces.parsers.SAXParser"。然后我们就需要设置搜索参数，再本例中，我们将不索引数字或任何少于3个
字符的词。在你下载到的XQEngine的API文档当中会有详细的配置参数说明，所以在此我就不细说如何配置参数了，请大家自己参阅相关文档。最后，
setDocument()方法指定XQEngine将要索引或搜索的XML文件。当然，如果你想要索引多个文件的话，只需设置几个相应的
setDocument()方法就可以了。<br><br>　　从下面的代码中我们还可以看到，XQEngine引擎将用三种不同的格式返回搜索结果：
STANDARD、SUMMARY和CSV（使用逗号分开的数值)为了简单起见，我为每种返回结果类型定义了一个数字来代替（1，2，3），然后使用相应
的参数调用setListenerType()方法。我将在后面详细介绍每一种返回结果类型。还有个方法printSessionState()用来输出
索引和引擎的信息，但是我没有把它写进例程中，所以上面的程序只会输出搜索结果；下一步再调用addXQLResultListener()方法，并传递
Search的一个实例，用来实现XQLResultListener的接口；然后再把查询字符串作为一个参数来调用setQuery方法，引擎就会开始
执行查询任务。等到查询结束后，引擎调用Search类的result()方法，把查询结果传回，在我提供的例程中，result()方法只是简单的把结
果输出出来。<br><font color="#cc0000">代码：<br><br></font>
<table align="center" border="0" cellpadding="0" cellspacing="0" height="27" width="468">
    <tbody>
        <tr bgcolor="#cccccc">
            <td><strong>import java.io.*;<br>import com.fatdog.textEngine.XmlEngine;<br>import com.fatdog.textEngine.exceptions.*;<br>import com.fatdog.textEngine.query.XQLResultListener; </strong><br><strong>public class Search implements XQLResultListener<br>{<br>public static void main( String[] args )<br>{<br>XmlEngine engine = new XmlEngine();<br>String searchFile = args[0];<br>String searchType = args[1];<br>String query = args[2];<br>try { file://配置引擎<br>engine.setSaxParserName( "org.apache.xerces.parsers.SAXParser");<br>engine.setMinIndexableWordLength( 3 );<br>engine.setDoIndexNumbers( false );<br>engine.setDocument( searchFile );</strong><br><br><strong>if (searchType.equals("1")) {<br>engine.setListenerType(<br>XmlEngine.STANDARD_LISTENER);<br>}<br>else if (searchType.equals("2")) {<br>engine.setListenerType(<br>XmlEngine.SUMMARY_LISTENER);<br>}<br>else {<br>engine.setListenerType(<br>XmlEngine.CSV_LISTENER);<br>}<br>}<br>catch( MissingOrInvalidSaxParserException e ){<br>System.out.println( <br>"缺少或不可用的 SAX解析器" ); <br>return;<br>}<br>catch( FileNotFoundException e ) {<br>System.out.println( <br>"不能找到 XML 文件: "); <br>return;<br>}<br>catch( CantParseDocumentException e ) {<br>System.out.println( <br>"不能解析 XML 文件: "); <br>return;<br>}<br>// engine.printSessionStats();<br>engine.addXQLResultListener( new Search() );<br>try { <br>engine.setQuery( query );<br>}<br>catch( InvalidQueryException e ) {<br>System.out.println( <br>"不可用的查询请求: " + e.getMessage() ); <br>return;<br>}<br>}<br>public void results( String xqlResults )<br>{<br>System.out.println( xqlResults );<br>}<br>}<br></strong><br></td>
        </tr>
    </tbody>
</table>
<br>　 <br>　　好，我们已经把一个使用XQEngine的程序编写出来了，那么就让我们来运行这段代码，在编译这段代码之前，我们需要下载到XQEngine和SAX解析器。我是从xml.apache.org上下载到Xerces解析器的。我使用的<font color="#000000"><u><a href="http://www.knowsky.com/system.asp">操作系统</a></u></font>是<a href="http://www.knowsky.com/article.asp?typeid=59">Windows 2000</a>
Professional，JDK为1.3版，好，搞定这些以后就跟我来设置CLASSPATH吧，在"环境变量"中修改CLASSPATH，添加"c:
\xql\XQEngine.jar;c:\xql\antlr.jar;
c:\xerces\xerces.jar"。现在就可以编译代码了，不过为了能够运行程序，我们还需要一个XML文件，我使用了Apache
Tomcat里的web.xml文件作为演示。前面我也介绍过了，我们使用1，2，3来分别代替三种返回查询结果格式：<br><br>　　1、使用
STANDARD_LISTENER
(数字1)和查询项"//welcome-file-list/welcome-file"，C:\xql\xql1＞java Search
web.xml 1 "//welcome-file-list/welcome-file" <br><br>Parser.installSaxParser: <br><br>
<table border="0" cellpadding="0" cellspacing="0" height="25" width="572">
    <tbody>
        <tr bgcolor="#cccccc">
            <td><strong>＜org.apache.xerces.parsers.SAXParser＞ <br>installed successfully <br>1: indexing web.xml <br>Query: ( // ( / welcome-file-list welcome-file ) ) <br>3 hit(s) for file://welcome-file-list/welcome-file <br>＜?xml version="1.0"?＞ <br>＜xql:result <br>query="//welcome-file-list/welcome-file" <br>hitCount="3" <br>elemCount="3" <br>docCount="1" <br>xmlns:xql="http://www.fatdog.com/ Standard_Listener.html"＞ <br>＜welcome-file＞ <br>index.jsp <br>＜/welcome-file＞ <br>＜welcome-file＞ <br>index.html <br>＜/welcome-file＞ <br>＜welcome-file＞ <br>index.htm <br>＜/welcome-file＞ <br>＜/xql:result＞<br></strong></td>
        </tr>
    </tbody>
</table>
<br>
上面的例子中，查询项要求找到任何"welcome-file-list"元素的所有的"welcome-file"子元素。请注意，搜索的结果基本上
是从原XML文档中摘录出来的，不能够建立搜索结果和原文档之间的关系。SUMMARY_LISTENER(2)返回类型则有些不同，它包括一个
"docID"号和一个"elemlx"号，这样就能够把结果和原文档联系起来了。<br><br>　　如下是返回结果的示例：<br><br>
<table align="center" border="0" cellpadding="0" cellspacing="0" height="17" width="587">
    <tbody>
        <tr bgcolor="#cccccc">
            <td><strong><br>　　C:\xql\xql1＞java Search web.xml 2<br>"//welcome-file-list/welcome-file"<br>Parser.installSaxParser: ＜org.apache.xerces.parsers.SAXParser＞<br>installed successfully<br><br>1: indexing web.xml<br><br>Query: ( // ( / welcome-file-list welcome-file ) )<br><br>3 hit(s) for file://welcome-file-list/welcome-file<br><br>＜?xml version="1.0"?＞<br>＜xql:result<br>query="//welcome-file-list/welcome-file"<br>hitCount="3"<br>elemCount="3"<br>docCount="1"<br>xmlns:xql="http://www.fatdog.com/<br>Summary_Listener.html"＞<br>＜welcome-file xql:docID="0" xql:elemIx="270"/＞<br>＜welcome-file xql:docID="0" xql:elemIx="271"/＞<br>＜welcome-file xql:docID="0" xql:elemIx="272"/＞<br>＜/xql:result＞<br></strong></td>
        </tr>
    </tbody>
</table>
<br>　　我前面也说过，对于我的应用程序来说，最重要的是返回使用逗号隔开的返回结果，所以CSV_LISTENER(3)就很有用了，它能够返回一个使用使用逗号隔开的结果，如下：<br><br>
<table align="center" border="0" cellpadding="0" cellspacing="0" height="22" width="531">
    <tbody>
        <tr bgcolor="#cccccc">
            <td><strong>C:\xql\xql1＞java Search web.xml 3 <br>"//welcome-file-list/welcome-file"<br>Parser.installSaxParser:<br>＜org.apache.xerces.parsers.SAXParser＞<br>installed successfully<br><br>1: indexing web.xml<br><br>Query: ( // ( / welcome-file-list welcome-file ) )<br><br>3 hit(s) for file://welcome-file-list/welcome-file<br><br>3,3,1,0<br>0,270,welcome-file<br>0,271,welcome-file<br>0,272,welcome-file<br></strong></td>
        </tr>
    </tbody>
</table>
<br>
当然，XQEngine还有很多很强大的功能，在此我不可能一一介绍，它所附带的文档中有丰富的源程序和使用方法，你可以对照着自己学习使用，当然，如
果你愿意的话你甚至还可以开发出一个GUI程序，文档中就自带了一个基于GUI的搜索程序：SwingQueryDemo，你可以看一看研究研究。</span><img src ="http://www.cppblog.com/swo2006/aggbug/17388.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/swo2006/" target="_blank">swo</a> 2007-01-07 14:21 <a href="http://www.cppblog.com/swo2006/articles/17388.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>RPG制作之路[转]</title><link>http://www.cppblog.com/swo2006/articles/11377.html</link><dc:creator>swo</dc:creator><author>swo</author><pubDate>Thu, 17 Aug 2006 13:59:00 GMT</pubDate><guid>http://www.cppblog.com/swo2006/articles/11377.html</guid><wfw:comment>http://www.cppblog.com/swo2006/comments/11377.html</wfw:comment><comments>http://www.cppblog.com/swo2006/articles/11377.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/swo2006/comments/commentRss/11377.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/swo2006/services/trackbacks/11377.html</trackback:ping><description><![CDATA[  RPG制作之路<br />                                     天眼<br /><br />一、绪论 <br />    RPG是角色扮演游戏的英文缩写，它是纸上冒险游戏和电脑结合的产物，自从诞生起就以独特的<br />冒险和故事性吸引了无数的玩家。我个人认为，RPG游戏是各类游戏中最能表述故事，体现情感的一<br />种游戏。RPG游戏不但自身在不断发展着，而且还不断吸取着其他游戏的精华，如加入冒险类的解密<br />情节（大多数RPG游戏都有），加入动作游戏的战斗方式（暗黑破坏神），引入战棋类游戏的策略战<br />斗部分（金庸群侠传），随着3D技术的发展，优秀的三维RPG游戏也在不断的涌现（如魔法门六）。<br />    然而这些优秀的游戏并不是凭空产生的，它同其他软件类产品一样，它也是由许多的程序人员编<br />制出来的，而且它的产生还离不开策划人员、美工和音乐制作人员协同的努力工作。RPG游戏的灵魂<br />是剧情，然而它却和其他类型游戏不同，它的特别强的故事性使得游戏的美工、音乐几乎同样重要。<br />然而却也不能因为重视它们而忽视了剧情，因为这三者互相失衡而导致失败的游戏的确不在少数。<br />    我非常喜欢编程序，看着计算机在自己的程序控制下说一是一，说二是二，让它向南它不敢往北<br />，哪怕是完成了一个非常小的程序，自己也会有一种成功的喜悦。当完成一个久未解决的难题时，真<br />想跳起来振臂高呼（我也真的这么干过，被人骂作"神经"）。相信真正沉浸其中的人都会有此感觉<br />！<br />    自从我刚刚接触电脑起，我就同样接触了游戏，虽然那只是在APPLE-II上的非常简单的游戏，我<br />就几乎沉迷了。沉迷的当然不是玩这些游戏，而是自己编这些游戏。那时我真的很惊讶，原来可以通<br />过自己的努力，让电脑给其他人提供这么吸引人的娱乐服务（那时我一直以为电脑是来进行科学计算<br />的）。虽然我那时在苹果机上编制的游戏简直没有娱乐性，但我还是很看重那一段时光，因为我在那<br />时不但认识到了电脑的超凡的功能和极端广泛的应用范围，而且还为我打下了程序设计的基础。<br />    这些年来，我自己编的游戏大大小小也有十来个，虽然真正的成品很少，但我还是努力尝试了游<br />戏的各种类型，包括射击、经营模拟、即时策略、战棋，当然最多的还是RPG游戏。    下面我就结<br />合自己的经验，讲解一下RPG游戏制作的具体方法和步骤，献给那些有程序设计经验且想自己制作游<br />戏的玩家。所有这些都是我个人的实践和思考的结果，可能会有许多不对的地方，欢迎大家批评指正<br />。<br />二、策划部分<br />1.  策划的必要性<br />    有句老话叫"未雨而绸缪"，是说应当为即将发生的事做好准备，在这里可以理解为即将开始的<br />游戏制作做好准备工作。因为即使是一个很小的游戏或者是软件，也有很多独立的方面需要完成。如<br />标题画面，操作界面，和系统有关的设置甚至还有安装等很多方面。如果事先不经过计划，在制作的<br />过程中必然会不断发现没有考虑到或者考虑不周全的地方，如果这时在进行改动，你的程序、美工和<br />音乐大概都得跟着改，有时甚至会导致整个工作重来。这样先前的许多工作都变成了白白的时间浪费<br />。我以前也走过这样一段弯路，总是一开始就坐在计算机前开始编程序，并且不断发现某些方面没法<br />实现，不得不修改以前的代码，有时还因为一个重要的方法技术上没有办法实现，被迫放弃制作。过<br />了一段时间后又突然解决了这个问题，再想拾起以前的工作继续下去，几乎已经不可能了。经过几番<br />挣扎，我终于认识到了策划的重要性，现在，无论是做什么东西，我总是"三思而后行"，有时我还<br />会提前编一些小程序验证某些方法的可行性，不会再盲目开始了。<br />    相信大部分有的程序设计经验的玩家都会同意我的看法，我之所以说这些话也是为了让大家少走<br />些弯路，更快的掌握制作游戏的方法。<br />2. 剧情的策划<br />    很多游戏制作者将详细的剧情策划放在第一步，我对此有不同的看法。<br />    剧情的策划固然重要，因为引人入胜的剧情往往是RPG游戏制作的关键，然而理想化的剧情策划<br />经常为程序设计制造难题，程序虽然可以不断完善，但出于技术和面向玩家阶层的考虑，程序并不是<br />万能的，策划者却往往不清楚程序描述剧情的能力达到到什么程度，当程序能力有所不及，就得重新<br />修改策划了！这是所有人都不愿看到的。<br />　　将剧情策划放在程序设计之后更是不可能的，因为程序设计者将无所指导，他不知道自己的程序<br />需要达到什么程度！<br />　　想到这里，我们不仅想到了在C语言程序中两个互相调用函数的情况，谁先谁后呢？那时，解决<br />的方法是函数原形，将相对基础的函数的函数名、输入输出参数以函数原形的方式写在所有调用它的<br />函数之前。同样我们可以将相对基础的"剧情"的大体框架和对程序的要求放在工作的第一步，在程<br />序设计完成以后在填充它的具体细节。在程序的基础上完成具体的剧情细节，这样就能成分发挥程序<br />的表述能力了！<br />3. 剧情框架<br />    应当主要描述剧情的时代背景，环境氛围，画面风格，事件的表述方法。至于具体的故事细节，<br />等到程序完成以后在进行设计也未尝不可。<br />    对于改编电影、小说的游戏，剧情已定，首先所需要的就是决定是否需要照搬原著的剧情？很重<br />要的一点是电影、小说有自己独到的表现方法，有些对于游戏是值得借用的，但并不表示要完全借用<br />。如果完全借用，那和电影、小说本身还有什么区别呢，我相信大多数人是不喜欢VCD般的游戏和电<br />子小说般的游戏的。<br />4. 决定程序设计的基础<br />     (1) 这一部分主要是指游戏的图形模式（分辨率、颜色深度），以及采用的特殊效果、音乐、<br />音效和动画的支持。由于本人对3D编程涉猎不多，所以主要以2D为主。<br />     图形模式由显示卡决定，由显示器支持，一般游戏常用的显示模式有<br />　　320 X 200 X 8 bits <br />    640 X 480 X 8 bits <br />    800 X 600 X 8 bits <br />   1024 X 768 X 8 bits <br />    640 X 480 X 16 bits <br />    800 X 480 X 16 bits <br />   1024 X 768 X 16 bits  （需显存2M）<br />    640 X 480 X 24 bits<br />    800 X 600 X 24 bits  （需显存2M）<br />   1024 X 768 X 24 bits  （需显存4M）<br />        未注明的显存1M即可  <br />    现在的显示器大都支持640X480、800X600、1024X768这三种分辨率,而显示卡对颜色深度的支持<br />与显存有关：<br />                                           颜色深度（bit） <br />   所须显存数量=横坐标分辨率X纵坐标分辨率X-------------<br />                                                8 <br />        颜色深度（bit）<br />      颜色数 = 2     <br />                              8<br />        如颜色深度8 bit即  2    =  256 色<br />     有些显示卡支持32bit或者更高的颜色深度，但24bit的颜色深度就已经使人眼分辨颜色的能力<br />达到极限，这些更高的颜色深度主要用于高质量的图象和视频设计。<br />       注：256色的调色机理和16bit、24bit的不同，现在的编程语言大都已经封装了对显示卡的控<br />制，制作者不用了解其中的原理就可以很方便的使用。<br />    分辨率越大、颜色数更多，图形的表现自然更好，但对显存、内存和数据传输的需求就越高，图<br />形数据文件也就越大。考虑到大多数电脑玩家的显卡都至少有1M显存，因此640x480x8bit、<br />800x600x8bit、640x480x16bit、800x600x16bit、640x480x24bit成了大多数游戏通用的分辨率，我<br />在制作游戏时也主要考虑这几种分辨率。需要较快的屏幕显示速度可以采用256色，需要光影等特殊<br />效果的最好采用16bit或24bit的颜色数，这些需要依具体情况定。至于分辨率，可以单独使用一种，<br />也可以同时支持几种，因为对于各种编程软件来说，这似乎并不是什么难题！<br />(2) 图块类型的选择<br />    大部分的RPG游戏的图形都是由数层图案逐层覆盖而成，比如最底的一层为地表图案如草地、石<br />子路等等；次一层为地物，包括其中行走的主角和NPC的图案；再高一层可以为覆盖于前一层的物体<br />之上的东西，如屋顶、树梢等等；如果需要的话，还可以再在上面加入表示天气一层，如白云、雨水<br />什么的。当这些图层重叠起来以后，上面一层镂空的地方露出下面的一层，这样就形成了"人在地上<br />走，云在人上飘"的层次高度效果。当然你也可以制定自己的图层，实现所需的特殊效果。<br />    游戏的每一个图层可以由多种方法实现：<br />     ——单张图片构成<br />         非常容易发挥美术效果，适合做地表，天气效果，但所需的内存和硬盘空间着实不小。<br />     ——多张不规则图片构成<br />         美术效果不错，适合做为地物层，但当需要构成遮挡效果时，计算较为麻烦 。<br />     ——多张规则图片组成<br />         最节省内存和硬盘空间，适合做任何层次，遮挡也非常容易处理，但对美工的要求比较高<br />。<br />     大多数的RPG游戏还是采用最后一种，因为技术上最为简单（用二维数组就可以描述），所以我<br />也比较倾向于这种，主要讲讲它。<br />     规则图案组成层次也有几种：<br />      ——矩形（出于对各个方向卷轴速度一致的考虑，多为正方型）图片拼接而成，本层内的图片<br />不存在互相遮挡的关系，只需从左到右，从上到下依次画出即可。只能创建出顶视或非常假的无遮挡<br />3D效果。（如《侠客英雄传》、早期的《勇者斗恶龙》系列）<br />      ——等边六角型图片拼接，就象蜂巢，本层内图片不存在遮挡关系。这种方式多用在战棋类游<br />戏中，或RPG游戏的战斗部分，在行走地图中不常用（因为得有六个行走方向，象是《英雄无敌》系<br />列）。<br />      ——矩形图片逐行覆盖而成，这种游戏的图片按着从左到右，从上到下依次画出，但每一行的<br />图片都覆盖前一行少许，但一个图片只覆盖本列的图片。这样的话，就可以理解为近处的物体遮挡了<br />远处东西，就是屏幕上部离玩家最远，下部离玩家最最近。产生一定的立体效果。（如《侠客英雄传3》、《新蜀山剑侠》和《剑侠情缘》）<br />      ——矩形图片交错覆盖，每一行不但覆盖前一行，而且横向偏移一段距离。这样就产生了视角<br />倾斜的效果，正方型的底面在这种视角中呈菱形。这种效果比上一种方法能产生更好的立体感觉，美<br />工好的话可以以假乱真。（如《仙剑奇侠传》、《金庸群侠传》和《暗黑破坏神》等）<br />     在这一部分很难用言语描述，我只讲各种类型，以后制作HTML版，再加入示意图片。大家只要<br />先有一个概念就行，<br />至于具体的数据描述和绘制方法我会在以后的程序部分讲解。<br />(3)  构成游戏的基本要素。<br />     单线还是多线剧情？<br />     单线剧情的和多线剧情对于程序员的工作来说，差别不大。但对于策划人员来说，却决不相同<br />。多线剧情相对于单线拥有数倍的工作量，而且大量的分支很容易搞乱。但多线剧情的可玩性十分诱<br />人，越来越多的制作者开始采用它。如果你决定使用这种方式，一定要做好策划，管好所有的资料数<br />据。（我的建议是专门编制一个管理多分支剧情的（信息、文本）编辑器，这并不时小题大做，因为<br />是你完全可以在编制下一个游戏时再次使用它。如果要求不太高的情况下，使用HTML语言是一种节省<br />效率的方法。）<br />     哪些人物属性？<br />     人物属性在RPG游戏中往往可以决定战斗、升级和剧情，是玩家非常在意的东西，因此对人物属<br />性的设定也要多化些工夫。拥有较多的属性可以产生更多的趣味性，你不必担心占用内存的问题，因<br />为这跟图象数据的内存需求比起来简直微不足道。但切记不要强硬的添加无用或是作用不大的属性。<br />     什么样的战斗方式？<br />     战斗是RPG游戏中的重头戏，目前RPG游戏的战斗方式有以下几种：<br />    ——敌我交替攻击的回合制：主要为早期的RPG游戏所采用，代表如《仙剑奇侠传》<br />    ——敌我同时攻击的回合制：为《剑侠情缘》中对前一种战斗方法的改进，意义不大。<br />    ——战棋式的回合制：最有代表的是《金庸群侠传》<br />    ——即时战斗：就在行走地图上展开的战斗，如《圣光岛》和《暗黑破坏神》<br />    ——格斗游戏方式：这是国内的晶合公司的在其游戏《毁灭天地》中的独创。（其实我也这么想<br />过，就是没有做过！）<br />    前两种战斗的实现方式较为简单，后三种则相当于融入了另外一种游戏的概念，所以制作起来比<br />较困难，不过也很有挑战性，很可能会吸引更多的玩家。<br />    什么样的升级系统？<br />    升级系统模仿了人类的成长，随着能力的提高去面对更加强大的敌人，也是RPG游戏所特有的魅<br />力，但近来也被不少其他类型的游戏（如即时策略和动作类）所吸收。作为传统的RPG当然更不能少<br />。目前各类RPG游戏的升级系统很单一，也是目前者最有有发挥潜力地方。在这里，我讲一些自己的<br />设想和看法。<br />       ——通过战斗获取经验值来升级，由等级数、资质之类的人物的属性来决定升级所需的经验<br />值，等级的提升又提高人物的某些属性，提高人物的战斗力。这也是最基本的升级规律，只需要制定<br />一些公式就可以实现，当然记着还要包含一定随机性（使用伪随机函数）。<br />       ——吸取MUD的优点，设置一些如"根骨"、"敏捷"、"悟性"等多项资质属性，对角色各<br />项属性的成长影响各自不同，通过剧情可以修改这些基本参数，创建出多种不同的角色成长方式。<br />       ——对于偏重于战斗的RPG游戏，设置较多"全局"角色，即在整个游戏过程都存在，他们拥<br />有同主角一致的升级系统，采用半随机（由人物参数、随机值和剧情共同决定）获取经验值。这样就<br />可以产生即时性的人物成长，当玩家游手好闲时，对手却在不断成长，给喜欢战斗的玩家以压力。（<br />如果只通过战斗来取得游戏的胜利未免有练功机器之嫌，应当提供多种成功的乐趣，不一定非要通过<br />战斗）<br />    需要特别注意得是：这些跟升级有关的公式和数据需要仔细记录，还需要便于后期修改。因为这<br />些东西直接决定着战斗和升级，和游戏的可玩性息息相关，加上很难以估算和决定，为了保证可玩性<br />，需要在后期花大量的时间修改完善。如果每改动一次都需要重新编译程序，那任何人都会受不了的<br />！（我通常的做法是使用文本格式的脚本描述语言）<br />    当你完全决定了所有上面这些时，就可以开始真正的程序工作了！当然你可能发现漏掉了一些细<br />节，但它们的作用不是很关键，我在程序部分再讲。<br />三、程序设计<br />    从这一章起，我开始讲ＲＰＧ游戏程序部分的具体实现，涉及代码的部分我主要采用自然描述语<br />言和类似Ｃ的描述语言。相信有一定程序设计基础的人都能看懂！<br />１．脚本描述语言<br />（１） 什么是脚本描述语言？为什么要用它？<br />    玩过很多ＲＰＧ游戏，打不过去的地方经常用ＰＣＴ改，有时候偶然发现游戏的某个文件是文本<br />文件，仔细阅读发现竟然象是某种语言程序，不是汇编，不是ＰＡＳＣＡＬ，也不是Ｃ，这究竟是什么呢？<br />    这其实是游戏制作者自己定义的一种脚本描述语言，制作者一般使用它来简化剧情的设计工作。<br />为什么它能简化剧情的设计呢，当我们了解了脚本描述语言本身后，再来说明这一点。<br />    脚本描述语言是一种解释语言，解释语言不同于编译语言之处就在于它在执行之前不需要编译成<br />机器代码，而是通解释机将语句解释成机器代码再执行。它的优点在于源程序可以方便快捷的修改，<br />因为不需要修改解释器，所以也不需要进行编译。脚本描述语言就是针对一种某特殊平台的描述再这<br />个平台上特定行为和动作的一种解释语言。游戏的脚本描述语言就是建立游戏本身这个平台上，描述<br />游戏中的情节事件的解释语言。<br />    为什么要用脚本描述语言呢？因为ＲＰＧ游戏的剧情往往是一个庞大而充满相互联系的故事群，<br />每个故事可能又由许多小事件组成，这复复杂杂的关系就如同我们设计的程序，函数就象事件，事件<br />可以包含事件，函数也可以包含函数，剧情中我们可以通过各种选择实现不同的分支情节，程序中我<br />们也可以通过条件分支来执行不同的程序段。这样的相似性必然会让我们想到用程序语言来描述剧情<br />。然而如果我们在直接在游戏程序中用"ＩＦ""ＴＨＥＮ"之类的语句来描述剧情，这样必然使剧<br />情依附于程序，剧情的改动也就意味着程序的改动，在ＲＰＧ游戏的制作中，这显然是不可行的。有<br />了上面对脚本描述语言的了解，我们不禁会这样想：如果用脚本描述语言呢？在游戏程序中加入解释<br />机，专门解释执行描述剧情的脚本语言。这样在改动脚本时，自然就不用改动程序了！如此一来，我<br />们修改剧情时，根本不用在意游戏程序本身，只需要一个简单的文本编辑器就行了，如此带来的工作<br />效率，相信不用我说大家也了解了！<br />（２）脚本描述语言的语法关键字和函数<br />    脚本描述语言最基本的单位是语句，它应当具备最基本语法，如表达式求值（包含各种常用的运<br />算），无条件转向，条件分支，循环，子函数等。变量有用户自定义数据也有游戏的全局数据；而描<br />述稍微复杂一些的功能可以采用全局函数，这些全局函数就象Ｃ语言的库函数或者是ＷＩＮＤＯＷＳ<br />的ＡＰＩ一样，和各种变量一起在表达式中引用或者作为其他函数的参数。<br />    下面是我制作的ＲＰＧ游戏的一个事件脚本文件。<br />// 示例事件１<br />say(1,165,"大家好！我是张斌，这是我做的第十七个游戏。")<br />say(11,30,"这是一个测试！用来检验我的游戏引擎。")<br />say(32,300,"你好！在我这里可以点播ＭＩＤＩ乐曲。")<br />choose_flag=choose(4,"请选择乐曲：","乐曲一?乐曲二?乐曲三?乐曲四")<br />midi(choose_flag)<br />say(36,30,"小子，你来找死！")<br />push<br />s0=fight(7)<br />if(s0==1) goto(WIN)<br />msg("你打输了了！")<br />gameover=1<br />:WIN<br />pop<br />msg("你打赢了！")<br />end<br />    这个事件的编号在地图装入时赋值给了一个ＮＰＣ，当主角接触到这个ＮＰＣ时，这个事件被触<br />发，于是这个文件被读入内存，开始解释执行。我们逐行解释这个文件：<br />第一行  前面的"//"同Ｃ＋＋语言一样表示注释一行。<br />第二行  是一个函数，名称是 SAY ，有三个参数，前两个是整数1和165，第三个是字符"大家好……<br />"。这个函数的意义是在屏幕的纵坐标165的位置上显示人物1（头像和姓名）的语言"大家好…"<br />第三行  同上，在屏幕纵坐标30的位置显示人物11的话"这是一个测试……"<br />第四行  同上，在屏幕纵坐标300的位置显示人物32的语言"你…"<br />第五行  choose是一个选择函数，在屏幕上出现"请选择乐曲"的信息和4项选择，分别是"乐曲１"，"乐曲２"，"乐曲３","乐曲４"，当玩家选择一个并按下回车后，<br /><br />玩家选择的选项号码（０表示第一个<br />选项，１表示第二个，依次类推）作为这个函数的返回值赋给变量choose_flag。<br />第六行  midi是一个播放MIDI音乐的函数，它的唯一参数就是乐曲号，我们可以看到它的参数是变量<br />choose_flag，这就表示根据choose_flag中的值来选取播放的乐曲，而choose_flag中恰恰就放的是<br />在前一语句中我们选择的号码，因此midi函数就会播放我们前面选择的乐曲。<br />第七行  仍然是人物语言显示<br />第八句  因为要进入战斗场景，所以用push函数将当前场景的参数保存。待战斗结束后可以再用pop<br />函数取出，借此恢复战斗前的场景。<br />第九句  fight是战斗事件函数，参数表示战斗事件的号码，这里表示第７号战斗事件。战斗的结果<br />（０表示输１表示赢）赋值给变量s0<br />第十句  if语句（也可以理解为函数）对装着战斗结果的标量s0进行判断，如果s0为1（战斗胜利）<br />，则执行后面的goto函数，跳转到标号为WIN的语句（就是":WIN"那一行），否则继续执行下面的语<br />句。<br />第十一句（s0==0，战斗失败）msg函数表示再屏幕上显示信息"你打输了！"<br />第十二句 给变量gameover赋值为1，处理这个脚本事件的解释器检测到此变量为１，就终止事件然后<br />结束游戏。<br />第十三句 为作为跳转语句目标行<br />第十四句 pop函数弹出战斗前的场景信息，恢复场景<br />第十五句 msg显示信息"你打赢了！"<br />第十六句 end函数表示事件结束<br />    事件结束后，脚本解释器会释放这段脚本占用的内存。<br />    脚本中的"gameover"对应这个游戏程序中的一个全局变量"gameover"，由于使用了指针，在脚本<br />中对它的引用就等同于对游戏程序中"gameover"的引用。同样对应于游戏程序中的其他全局变量，也<br />可以通过自己定义的脚本变量来引用。如地图大小"mapx"和"mapy"，当前主角位置"cx","cy"等等，<br />这样就可以直在脚本语言中引用它们。如if(cx==5&amp;&amp;cy==7)判断主角是否在地图(5,7)这个位置上<br />if(cx==mapx-1&amp;&amp;cy==mapy-1)   判断主角是否在地图的角上这段脚本中"say"，"msg"，"choose"，<br />"fight"，"midi"都是描述游戏中情节的函数，在游戏程序中都有相应的函数与之对应，它们有些有<br />返回值，有些没有。这些返回值还可以用来构成表达式，如：<br />midi(choose(3,"请选择乐曲：","乐曲一?乐曲二?乐曲三")+1)<br />    这个条语句的含义就成了选择"乐曲一"的时候，实际放乐曲二，选择"乐曲二"的时候放乐曲三，<br />选择"乐曲三"的时候放乐曲四。<br />    上面那段脚本中的"if"，"goto"可以被理解为控制语句也可以被理解成函数。所有的控制语句函<br />数化后，可以使脚本程序程序的格式更加统一，便于阅读。<br />    同样对数组的引用也可以函数化，如对地图(X,Y)位置的图案类型的赋值在游戏程序中为<br />map[x][y]=12，是一个标准的数组元素，而在脚本程序中的引用则成了map(x,y)=12，x,y成了函数<br />map的两个参数。虽然形式上是函数，但实际的使用中仍然等同于变量map[x][y]（因为程序内部使用<br />的是指针），因此可以进行赋值运算。<br />    下面再看一段脚本文件<br />//示例事件２<br />say(12,300,"公子，你要买我的宝剑吗？")<br />say(1,30,"这宝剑多少钱？")<br />say(12,300,"30两银子！")<br />say(1,30,"这也太贵了！")<br />say(12,300,"30两也嫌贵，这剑可是削豆腐如泥哦！")<br />say(1,30,"让我考虑一下！")<br />choose_flag=choose(2,"买吗？","买了 不买")<br />if(choose_flag==1) goto(NoBuy)<br />if(haveobj(1)&lt;30)  goto(NoMoney)<br />msg("你花３０两买下了这把破剑！")<br />say(12,300,"您走好！")<br />addobj(1,-30)<br />end<br />:NoBuy<br />say(12,300,"小气鬼，３０两也不肯出！")<br />end<br />:NoMoney<br />say(12,300,"真是个穷光蛋，快滚！")<br />end<br />第一句 仍然是注释<br />第二句 到第七句是主角(1)和卖剑的(12)人物对话<br />第八句 是选择"买"还是"不买"<br />第九句 如果选择了不买，跳转到:NoBuy<br />第十句 haveobj对应游戏程序中的函数haveobj，参数是物品的种类返回值是拥有这种物品的数量（<br />物品１表示银子，银子的数量就是两数）这一句是判断主角拥有银子的数量如果小于３０两，则跳转<br />到NoMoney<br />第十一句 显示买下剑的信息<br />第十二句 卖剑的招呼你走好<br />第十三句 addobj函数表示为主角增加物品，第一个参数为物品的种类（１为银子），第二个参数为<br />增加的数量（为负则是减少）<br />第十四句 事件结束<br />第十五句 不想买剑，跳转到这里<br />第十六句 卖剑的骂你小气鬼<br />第十七句 事件结束<br />第十八句 想卖剑但钱不够，跳转到这里<br />第十九句 卖剑的让你滚蛋<br />第二十句 事件结束<br />    通过上面这两段脚本语言文件，我们可以清楚的了解到脚本语言的的变量对应着游戏程序中的全<br />局变量、函数对应着游戏程序中的函数，通过脚本语言，我们可以轻易的引用游戏中的各项值（主角<br />属性，物品，地图等等），引用游戏中的表述方法（人物对话，播放音乐，旁白信息等等）。如此以<br />来我们构建剧情不就轻而易举了吗？<br />    然而，我们不能高兴的太早了，因为真正艰辛的，才刚刚开始！我们下一次将开始讲如何构件脚<br />本的解释机！<br />（３）解释机的编程<br />    在任何编程语言中，表达式计算都是最重要的，它在我们的脚本描述语言中同样存在。因此在脚<br />本解释机中，我们最先实现的就是表达式求值。我们在写源程序的时候进行计算非常简单：<br />    如  3*(3+2)-4<br />    然而在脚本描述语言中，解释机所得到的并不是如此简单的算式，而是一个从文本文件中提取的<br />一个字符串 "3*(3+2)-4"将这个字符串转化成一个数，可不是那么简单。如果你认真学习过算法，应<br />该能够从容的实现它。<br />    我们先看一个简单一点的算式  "32+41*50-2"<br />    我们把自己看成是计算机，从字符串的左边开始扫描，当扫描到'3'时，可以知道这是一个数的<br />开始，将它记如入一个空闲的字符串的第一位buf[0]，当我们扫描到'2'，它仍然是个数字，是我们<br />正在记录这个数的新的一位，我们将它放入buf[1]，当我们扫描到'+'时，它不是数字了，我们也就<br />知道第一个数读完了，在记录它的字符串的下一个位置buf[2]放入字符串结束标志'0'我们得到的这<br />个数放在buf中，是"32"，通过一些编程系统提供的字符串转整型数的函数，我们可以将这个字符串<br />转化为数值，即使你用的编程系统没有这个函数，根据数字字符的ＡＳＣＩＩ码值，我们自己也可以<br />很容易实现：<br />   '0',"1","2"...'9'的ＡＳＣＩＩ码值分别为 48～57,如果一个数字字符的ＡＳＣＩＩ码为n，则<br />它代表的数字值为n-48。如一个数char str[5]={"2341"}，它的值就可以写成<br />(str[0]-48)*1000+(str[1]-48)*100+(str[2]-48)*10+str[3]-48于是我们可以写出下面的将字符串<br />转变为整数的Ｃ语言函数（其他语言也类似）<br />int stoi(char *str)         //str是以0为结束的字符串<br />{<br /> int return_value=0,i=0;<br /> while（ｓｔｒ{ｉ}！＝０）           //字符串结束<br />   return_value=return_value*10+str[i++]-48;<br /> return(return_value);<br />}<br />    知道了这个"32"是32，我们将它记下（装入一个变量），再继续往下扫描，直到字符'4'，我们<br />知道我们得到了一个"+"，它是一个二元运算符号，我们接着向下扫描利用上面扫描32的方法，我们<br />得到了另一个数41，我们现在知道了32+41，考虑一下，我们在什么情况下能将它们相加：<br />    要么是在41后字符串结束，要么紧接着41的是一个比'+'优先级低或相等的运算符，如'-'。将它<br />们相加后得到73，我们就回到了刚刚得到31的那一步。<br />    如果41后面跟着的是一个更高优先级的运算符，如本例中的'*'，我们必须先进行这个乘法运算<br />，那只好先将31和'+'保存起来，接着向下扫描，我们又得到了50和其后的运算符'-'，判断的方法和<br />刚才一样，因为'*'比'-'的优先级高，所以我们可以放心的先将41*50算出，得到2050。这时我们现<br />在所扫描到的算式就成了32+2050-2，我们再次比较运算符'+'和'-'，优先级相同，我们就可以先算<br />32+2050了，得到2082，我们继续向后扫描，了解到在2082-2后字符串结束，我们可以继续计算最后<br />一步2082-2=2080，最后这个字符串表达式的结果就是2080。<br />    现在我们再来看看括号：如  3*(3==2+2*5)-4这个式子，在读完3*之后我们读到得不是一个数，<br />而是一个括号，这时我们需要先算括号内的式子，所以的先将3和*保存起来，比较==和+，得先计算<br />加法，先将3==保存，再来比较+和*，先计算2*5得到10，因为下面一个等到算完2*5得到10，因为后<br />面是括号，所以要取出先前保存的数和运算符进行计算，但一直要取到前一个括号，但我们顺序存了<br />3*、3==、和2+，怎么知道前一个括号在那里呢？方法是在遇到前括号时也保存括号的标记，这样的<br />话，我们算到这一步时所保存的顺序为：3*，(，3==和2+，我们遇到一个后括号，就取出以前保存的<br />数进行运算，先算2+10得12，再算3==12得0，这时取出了括号(，我们这才知道这个括号内得运算完<br />结，现在的算式剩下了3*0-4，再比较*和-，先算3*0得0，最后得结果就是0-4得-4。<br />    在上面的运算中，我们在遇到高优先级的运算时，需要将前面的数值和运算符保存，但我们并不<br />太清楚需要保存几次，如这两个算式：<br />    1=2==3+4*5    1+2+3+4+5<br />    它们在计算过程中需要保存的的数和运算符个数是不同的，前一个需要先算4*5结果为20，再算<br />3+20结果为23，再算2==23结果为0，再算1=0（在一般的语言中，象这样给常数赋值是禁止的，但在<br />我们脚本语言的运算中，为了保持一致性，我们允许这样的式子，但赋值被忽略），最多情况下需要<br />要保存三个数和运算符。而后一个式子一个也不用保存，从左到右依次运算就行了。<br />    我们发现这些数和运算符的使用都是"先存的后用，后存的先用"，这不是堆栈吗？对！我们就<br />用堆栈来保存它们。<br />    堆栈的实现很多软件书中都已经讲过，心中有数的读者自然可以跳过下面这一段。<br />    一般实现堆栈可以用链表或数组，我们犯不上用链表这么复杂的数据结构，用比较简单的数组就<br />可以了。对于使用Ｃ＋＋的朋友可以用类的形式来实现<br />class STACK             //整数堆栈<br />{<br />    int *stack;         //存放数据的首地址<br />    int p;              //堆栈位置指示（也可以用指针）<br />    int total;          //预先定义的堆栈大小<br />  public:<br />    STACK(int no);      //指定堆栈大小的构造函数<br />    ~STACK();           //析构函数<br />    int push(int n);    //压栈<br />    int pop(int *n);    //出栈<br />};<br />STACK::STACK(int no)<br />{<br />  total=no;<br />  stack=new int [total];<br />  p=0;<br />}<br />STACK::~STACK()<br />{<br />  delete[] stack;<br />}<br />int STACK::push(int n)   //压栈<br />{<br />  if(p&gt;total-1)<br />     return(0);<br />  else<br />    stack[p++]=n;<br />  return(1);<br />}<br />int STACK::pop(int *n)    //出栈<br />{<br />  if(p&lt;1)<br />    return(0);<br />  else<br />    *n=stack[--p];<br />  return(1);<br />}<br />    如果用Ｃ也是一样，使用initSTACK来声明一个堆栈，但要记着在用完之后调用freeSTACK释放内<br />存<br />typdef struct STACK<br />{<br />   int *stack;         //存放数据的首地址<br />   int p;              //堆栈位置指示（也可以用指针）<br />   int total;          //预先定义的堆栈大小<br />};<br />int initSTACK(struct STACK *stk,int no)<br />{<br />  stk=(struct STACK *)malloc(sizeof(STACK));<br />  stk-&gt;total=no;<br />  stk-&gt;p=0;<br />  stk-&gt;stack=new int [total];<br />//如果stack不为零表示分配成功，堆栈初始化也就成功<br />  if(stk-&gt;stack)<br />    return(1);<br />  free(stk);     //如果失败释放内存<br />  return(0);<br />}<br />void freeSTACK(struct STACK *stk)<br />{<br />  if(stk)<br />   {<br />    delete[] stk-&gt;stack;<br />    free(stk);<br />   }<br />}<br />int pushSTACK(struct STACK *stk,int n)   //压栈<br />{<br />  if(stk-&gt;p&gt;stk-&gt;total-1)<br />     return(0);<br />  else<br />    stk-&gt;stack[stk-&gt;p++]=n;<br />  return(1);<br />}<br />int popSTACK(struct STACK *stk,int *n)    //出栈<br />{<br />  if(stk-&gt;p&lt;1)<br />    return(0);<br />  else<br />    *n=stk-&gt;stack[--p];<br />  return(1);<br />}<br />    可以看出这种堆栈类在声明对象时要给出堆栈的大小，对于我们的表达式求值来说，１００个单<br />元足够了。但有人不禁会想到，上面这些都是整数堆栈，对于运符怎么存储呢？其实是一样的，我们<br />可以给运算符编上用整数序号来代表，这样就可以利用整数堆栈来保存了。给运算符编号的另一个好<br />处是可以利用运它的高位来代表运算符的优先级！如下面一个函数将字符串运算符转化成含优先级的<br />序号，只要比较这些序号高位值的大小就可以得出谁得优先级高了。（下面这个函数只对二元运算符<br />编号，没有处理一元和多元，因为它们都可以用二元运算表示。）<br />int convert_mark(char *str) <br />{<br />//优先级高<br />  if(strcmp(str,"*")==0) return(240);   //0xf0<br />  if(strcmp(str,"/")==0) return(241);   //0xf1<br />  if(strcmp(str,"%")==0) return(242);   //0xf2<br />  if(strcmp(str,"+")==0) return(224);   //0xe0<br />  if(strcmp(str,"-")==0) return(225);   //0xe1<br />  if(strcmp(str,"&lt;&lt;")==0) return(208);  //0xd0<br />  if(strcmp(str,"&gt;&gt;")==0) return(209);  //0xd1<br />  if(strcmp(str,"&lt;")==0) return(192);   //0xc0<br />  if(strcmp(str,"&lt;=")==0) return(193);  //0xc1<br />  if(strcmp(str,"&gt;")==0) return(194);   //0xc2<br />  if(strcmp(str,"&gt;=")==0) return(195);  //0xc3<br />  if(strcmp(str,"==")==0) return(176);  //0xb0<br />  if(strcmp(str,"!=")==0) return(177);  //0xb1<br />  if(strcmp(str,"&amp;")==0) return(160);   //0xa0<br />  if(strcmp(str,"^")==0) return(144);   //0x90<br />  if(strcmp(str,"|")==0) return(128);   //0x80<br />  if(strcmp(str,"&amp;&amp;")==0) return(112);  //0x70<br />  if(strcmp(str,"||")==0) return(96);   //0x60<br />  if(strcmp(str,"=")==0) return(80);    //0x50<br />  if(strcmp(str,"+=")==0) return(81);   //0x51<br />  if(strcmp(str,"-=")==0) return(82);   //0x52<br />  if(strcmp(str,"*=")==0) return(83);   //0x53<br />  if(strcmp(str,"/=")==0) return(84);   //0x54<br />  if(strcmp(str,"%=")==0) return(85);   //0x55<br />  if(strcmp(str,"&gt;&gt;=")==0) return(86);  //0x56<br />  if(strcmp(str,"&lt;&lt;=")==0) return(87);  //0x57<br />  if(strcmp(str,"&amp;=")==0) return(88);   //0x58<br />  if(strcmp(str,"^=")==0) return(89);   //0x59<br />  if(strcmp(str,"|=")==0) return(90);   //0x5a<br />//优先级低<br />}<br />    在ＲＰＧ得脚本描述语言中，我们基本用不上小数，因此我们在实际的二元运算中得到的将是三<br />个整数，其中两个是参与运算的数，另一个是运算符的序号，我们还得对此编出进行运算的函数。如<br />：<br />//运算求值 n1是第一个参加运算得数，n2是运算符号得序号<br />//n3是第二个参加运算的值<br />int quest(int n1,int n2,int n3)<br />{<br />  int ret=0;<br />  switch(n2)<br />  {<br />    case 240:ret=n1*n3;break;  // "*"   乘法<br />    case 241:ret=n1/n3;break;  // "/"   除法<br />    case 242:ret=n1%n3;break;  // "%"   求余数<br />    case 224:ret=n1+n3;break;  // "+"   加法<br />    case 225:ret=n1-n3;break;  // "-"   减法<br />    case 208:ret=n1&lt;&lt;n3;break; // "&lt;&lt;"  左移<br />    case 209:ret=n1&gt;&gt;n3;break; // "&gt;&gt;"  右移<br />    case 192:ret=n1&lt;n3;break;  // "&lt;"   小于<br />    case 193:ret=n1&lt;=n3;break; // "&lt;="  小于等于<br />    case 194:ret=n1&gt;n3;break;  // "&gt;"   大于<br />    case 195:ret=n1&gt;=n3;break; // "&gt;="  大于等于<br />    case 176:ret=n1==n3;break; // "=="  等于<br />    case 177:ret=n1!=n3;break; // "!="  不等于<br />    case 160:ret=n1&amp;n3;break;  // "&amp;"   与<br />    case 144:ret=n1^n3;break;  // "^"   异或<br />    case 128:ret=n1|n3;break;  // "|"   或<br />    case 112:ret=n1&amp;&amp;n3;break; // "&amp;&amp;"  逻辑与<br />    case 96:ret=n1||n3;break;  // "||"  逻辑或<br />    case 90:ret=n1|n3;break;   // "|="<br />    case 89:ret=n1^n3;break;   // "^="<br />    case 88:ret=n1&amp;n3;break;   // "&amp;="<br />    case 87:ret=n1&lt;&lt;n3;break;  // "&lt;&lt;="<br />    case 86:ret=n1&gt;&gt;n3;break;  // "&gt;&gt;="<br />    case 85:ret=n1%n3;break;   // "%="<br />    case 84:ret=n1/n3;break;   // "/="<br />    case 83:ret=n1*n3;break;   // "*="<br />    case 82:ret=n1-n3;break;   // "-="<br />    case 81:ret=n1+n3;break;   // "+="<br />    case 80:ret=n3;break;      // "="   赋值<br />    case -1:ret=n3;break;      // 用来表示前括号<br />    case  0:ret=n1;break;      // 空运算<br />  }<br />  return(ret);<br />}<br />    我们可以看到，在上面得有关赋值得运算中，我们实际上并没有进行赋值，因为我们还没有任何<br />变量来接受赋值，下一次里我们再来讲讲将游戏中的数据作为变量进行运算和赋值，这可是最激动人<br />心的哦！<br />注意：解释机并不是独立的软件程序，它是游戏源程序的一部<br />      分，只有这样脚本解释语言它才可能通过它引用到游戏<br />      中的变量和函数。<br />    为了达到引用游戏中变量和函数的目的，我们专门定制一个函数，用来将字符串转变成整数（假<br />如起名为val，则它的函数原型就是int val(char *str)）假若输入字符串是一个数字串，我们就可<br />以调用前面一讲讲过的将数字字符串转变为整数的函数将它转化为数值；如果输入字符串的第一个字<br />符是英文字母或者下划线，我们就根据这个字串返回它所代表的游戏中的变量。<br />    例如，我们在游戏程序中定义了主角当前的位置是放在int cur_x,cur_y 当中，我们可以约定在<br />当在脚本语言中也用cur_x和cur_y来代表这两个变量（只所以用同形的字串，是为了便于记忆，当然<br />你也可以给用另外的字串代替），假若我们的这个函数得到的输入字串是"cur_x"，我们就让val函数<br />返回它变量cur_x中的值。如果是"cur_y"，我们就返回变量cur_y 的值。同样象人物属性、物品等等<br />，都可以约定的字符串形式引用。但对于数组元素呢？我们在Ｃ语言中都是采用跟在变量后的方括号<br />内写入数组索引值，采用方括号的目的是在编译时区别数组和函数。但在解释语言中步存在区别的问<br />题，所以象BASIC 都采用和函数相同的圆括号。所以我们在处理数组和函数时也基本相同如：<br />       addobj(12,100)<br />       map(23,32)<br />    前一个是函数，表示给主角加100个物品12（12是物品代号）后一个是二维数组，表示地图上某<br />一点的物体，相当于map[23][32]。<br />    假若输入的字串不是数字串，我们就可以将它拆分处理：如将"addobj(12,100)"分为"addobj"、<br />"12"、"100"，共三项。对于cur_x就只得到一项"cur_x"，根据它们的第一项，我们可以知道它们代<br />表的是那个变量或函数，拆分出的其他项就是数组的索引或函数的参数，因此我们可以很容易的指定<br />val的返回值（对于函数就是函数的返回值，对变量就是变量值）。<br />    但如果圆括号内不仅仅是常数，而且有变量或者函数，或者是由函数变量组成的表达式，如：<br />       addobj(map(23-cur_x,32)+1,100)<br />这样又怎么办呢？解决方法就是交叉的递归调用。<br />我们现在所做的一切最终就是为了将一个字符串表达式转变成一个整数，写成函数的形式就是（假设<br />函数名为cal）<br />     int cal (char *str);<br />     如果输入参数为1+addobj(map(23-cur_x,32)+1,100)+1，<br />     它需要调用val 函数来求参加运算的一个数值<br />      addobj(map(23-cur_x,32)+1,100)<br />    而在val 函数中，对于addobj(map(23-cur_x,32)+1,100)会拆分出"map(23-cur_x,32)+1"这样的<br />项，它也是一个表达式，我们只有以它为参数再次调用cal求值。cal又会调用val求值<br />val("map(23-cur_x,32)")，val拆分得到"23-cur_x",，再次调用cal("23-cur_x")，cal再调用<br />val("cur_x")得到cur_x的值，再返回给前一个调用它的cal，如此逐曾返回，最终由cal求得真正的<br />结果。<br />    cal 调用 val，val又去调用cal，这就形成了交叉的递归调用，第一次调用<br />cal("1+addobj(map(23-cur_x,32)+1,100)")，第二次调用cal为cal("map(23-cur_x,32)+1")时，第<br />三次调用为cal("23-cur_x")，递归调用一个函数时，系统会自动的为它开辟内存，生成一个副本，<br />这三次调用就同三个不同的函数一般。<br />    如此一来，在复杂的表达式我们也能求出起结果了，但需要注意的一点是递归调用太多时耗费系<br />统资源太多，也容易出错，我们应当尽量避免。如在val中，如果对输入串拆分得到的项是数字串自<br />然不用去调用cal，如果拆分得到单独的变量或函数，如"map(cur_x,32)"经拆分得到的"cur_x"这一<br />项，因为没有运算，则直接递归调用自己val("cur_x")就可以了，而不用再调用cal了，这样就可以<br />减少递归调用的层次，减少内存消耗和发生错误的可能性。<br />    我们可以发现，到目前为止，我们可以引用游戏中的变量和函数了，但我们还不能给游戏中的变<br />量赋值，也就是说我们在脚本语言中能够知道主角位置、主角的属性、所处的位置、拥有的物品等等<br />，但却不能改变它们，因为我们的赋值运算并没有真正对游戏中的这些变量赋值，这将使脚本描述语<br />言大失光彩，没关系，我们将在下一讲解。<br />    首先我们分析我们以前进行的工作，<br />    STACK（类或结构）用来暂存运算式中的数字和运算符代号<br />    convert_mark 用来给运算符编含有优先级的代号<br />    quest        用来计算两个整数运算的结果<br />    val          用来将字符串（整数，变量，函数）转化成<br />                 整数结果<br />    cal          将字符串表达式转化成整数<br />    如果你看过以前的几讲，应该很容易搞清楚这些函数的基本调用关系。<br />         +--&gt; STACK<br />         |<br />         +--&gt; convert_mark<br />    cal--|<br />         +--&gt; quest<br />     |   |<br />     |   +--&gt; val ---+<br />     |               |<br />     +---------+-----+<br />    我们现在再来考虑一下对游戏中的变量赋值，最先想到的自然是再进行实际运算的函数quest 中<br />实现，但quest 函数的输入参数只是三个整数，即使我们知道现在进行的是赋值运算，知道了运算的<br />结果，但却无法知道应该将结果赋值给那个变量。我们又会想到只在val 函数中才有可能拆分出代表<br />变量的字符串，才知道是那个变量，但在这个函数中我们并不知道这个变量参加的是何种运算，也不<br />知道运算的结果，所以也没有办法进行赋值。所以我们只有将希望放在调用它们两个的函数cal 上了<br />，因为是cal 调用的val，所以cal能得到运算的类型和结果，至于参加赋值运算的那个具体的变量，<br />因为val 函数返回的是这个变量的值，因此我们还不能确定进行运算的是那个变量，但如果将val 的<br />返回值改为指向这个变量的指针，我们不是既能引用又能赋值了！我们根据参加运算变量指针所指向<br />的值调用quest 函数就可以得到运算结果，我们再根据这个运算是否赋值运算再决定是否将这个结果<br />写入这个变量的指针。<br />    需要注意的是，在val 函数中，如果判断出是变量，我们就返回它的指针就行，但如果是整数或<br />者是函数，它们并没有指向其值的指针，但我们可以定义一个静态(static)变量，用来存放这个整数<br />或者函数的结果，然后返回指向这个静态变量的指针就行了。（注意是静态变量，因为它在函数结束<br />后不释放内存。如果是一般的动态变量，在函数结束后就会释放，我们返回的指针指向的就是一个不<br />确定的值了！）当然你也可以采用全局变量。（因为它在整个程序执行期间都不释放内存）。<br />    完成这几个函数，我们的字符串表达式求值部分就完成了，但我们的解释机并没有完成，我们还<br />需要无条件转移、判断、条件转移、循环一些控制语句，这些我们会在下一讲中完成！首先为了快速的解释执行，我们一般都将整个脚本文件读入内存，将脚本语<br /><br />句一行的一行储存。因为移动内存指针<br />可比移动文件指针方便快速多了。<br />首先我们来看语句注释，我们可以采用";" "//","*"等注释一行，也可以用成对的"/*"和"*/注释一<br />整段。它的实现很简单，我们只要在将脚本文件读入内存时将这些行忽略就行了。（遇到这种注释一<br />行的标志，就读到此行末尾，但不在内存中保存。遇到是注释一段的起始标志，就一直读到注释一段<br />的结束标志，这其中读入的并不在内存中保存！）<br />    首先我们来看看语句跳转，很自然的，可以通过指定语句在脚本文件中的行号来进行跳转（注意<br />不是ＢＡＳＩＣ中语句前的行号），但这样做法实现很简单，但对于脚本文件的编制和修改就麻烦大<br />了，因为你必许自己数出想要跳转到的那一行的行号，而这其中又要排除忽略掉的注释行，而当你每<br />次修改时删除或者增加一行，那么相关的跳转又要重新数行号。这恐怕会使人丧失编制脚本文件的耐<br />心。<br />    解决这个问题较佳的办法是，在想要跳转的那一行前加一个标号行，跳转时指定哪个标号就行了<br />（结构化ＢＡＳＩＣ语言、汇编、Ｃ语言都是如此），在将这些脚本读入内存时忽略这些标号行，但<br />记录下它的标号名称和行号，再根据每个跳转中指定的标号名称，将它们的参数转化成行号。如：<br />假设脚本文件是这样的：<br />xxxxx            //行0<br />xxxxxxx          //行1<br />//此行是注释，读入内存时忽略<br />xxxxx            //行2<br />:label           //标号行读入内存时忽略<br />xxxxxxx          //行3<br />xxxxxx           //行4<br />xxxxxx           //行5<br />xxxxxx           //行6<br />xxxxxx           //行7<br />xxxxxx           //行8<br />goto(label)      //行9<br />xxxxxxx          //行10<br />xxxxxx           //行11<br />/*<br />这其中的内容都是注释，读入内存时忽略<br />xxxxx<br />xxxx<br />*/<br />xxxxxxx          //行12<br />xxxxxx           //行13<br />xxxxxxx          //行14<br />end              //行15<br />读入内存并修改跳转参数后变为<br />xxxxx<br />xxxxxxx<br />xxxxx<br />xxxxxxx<br />xxxxxx<br />xxxxxx<br />xxxxxx<br />xxxxxx<br />xxxxxx<br />goto(3)<br />xxxxxxx<br />xxxxxx<br />xxxxxxx<br />xxxxxx<br />xxxxxxx<br />end<br />注意到其中goto的参数变成了想要跳转到的那一行在内存中的行号，而注释和标号行都被忽略了。<br />    我们现在再讲讲如何具体实现：<br />    首先将脚本文件中的每一行读入内存（当然要忽略注释），当读入的是标号行时（此行第一个字<br />符是':' ），将所有的标号名称和下一行的行号保存起来（规定标号名的长度和数量，比如规定变量<br />名少于32的字符，每个脚本文件中的标号不超过100个，标号名称可以顺序保存在一个二维字符数组<br />中，如<br />      char label[100][32]<br />行号也顺序放入一个整数数组，如<br />      int labelno[100]<br />    当脚本文件读入内存后，再次扫描这些内存，如果遇到goto就比较goto后的参数和label中的内<br />容依次比较，如果相同，就将goto后的内容改变成labelno内相应的行号。<br />    如此一来，我们就得到了最终的内存结果。<br />    当内存中的这些脚本解释时，我们会用一个变量来放当前即将执行的行号，如果执行完一行，就<br />将这个变量加１，然后下次就选取这个变量指示的那一行语句执行。在进行跳转时，只要把这个变量<br />改变为goto后的行号即可。<br />    当然，goto(xx)的形式我们也可以把它当作函数处理，在我们前面讲过的val 函数中，遇到goto<br />时将当前的命令行号变为xx即可。<br />    这次主要讲解释机中对注释语句和转向语句的实现方法，下一次我们在来讲条件分支、循环等等<br />。<br />    条件分支我们可以采用类似汇编语言的方法，在解释机内设置一个判断专用的标志变量（<br />if_flag），根据if(...)括号内的表达式设置这个变量。然后then(....)再根据这个变量的值决定是<br />否转向括号内指定的标号行（这些都是在前面讲过的函数 val里实现），如：<br />    if(cur_x&lt;10)   //条件成立设置判断标志为１，反之为０<br />    xxxxxx<br />    xxxxxx<br />    then(label1)   //判断标志位为１则转向label1否则继续<br />    xxxxxx<br />    xxxx<br />    :label1<br />    xxxxxx<br />    我们在读入脚本进内存时时，同goto一样也要将then括号中的标号转变为相应的行号。<br />    这样我们就可以和汇编语言一样，结合其他变量实现循环。<br />    s1=0           //给循环记数器设置初值<br />    :label1<br />    xxxxxxx        //需要循环执行的语句<br />    xxxxxx         //需要循环执行的语句<br />    s2=s1*10       //需要循环执行的语句<br />    xxxxxx         //需要循环执行的语句<br />    s1+=1          //循环记数器自动增加<br />    if(s1&lt;10)      //判断循环是否结束<br />    then(label1)   //如果没有结束跳转到label1<br />    xxxxx          //如果结束了继续执行下面这些行<br />    另外还要说明的一点是，在ＲＰＧ游戏时我们经常会遇到弹出有多项选择。比如说在买东西的时<br />候，会列出多个物品让你选择。我们把这多项选择也做成函数：<br />    int choose(char *str,int n,char *item,int must)<br />    就拿前面买东西来说，你在一个武器店的地图中放置一个店老板的ＮＰＣ，他对应的脚本如下：<br />    say(12,30,"您好，欢迎光临本店！")<br />    :ask       //询问您要什么<br />    s1=choose("您要？",3,"买东西 卖东西 不要了",1)<br />    if(s1==0)<br />    then(buy)  //跳转到买东西<br />    if(s1==1)<br />    then(sell) //跳转到卖东西<br />    //不买也不卖<br />    say(12,30,"不要了？您慢走！")<br />    end<br />    :buy            //买物品<br />    s1=choose("买什么？",5,"匕首 竹剑 钢剑 梭镖 铜锤",0)<br />    xxxxxxxxxx      //根据选择的选项s1<br />    xxxxxxxxxxx     //给玩家增加物品，减少金钱等等<br />    xxxxxxxxx       //<br />    xxxxxxx         //<br />    xxxxxxxxxx      //<br />    if(s1==-1)      //如果选择"买什么"时点了ESC退出<br />    then(buy)       //放弃买物品跳转到"您要什么"的选择<br />    goto(ask)       //买了一件物品后继续选择要买的物品<br />    :sell           //卖物品<br />    s1=chooseobj()  //从自己有的物品中选择一样<br />    if(s1&gt;100)      //判断物品的种类<br />    then(nosell)    //决定是否跳转到nosell<br />    xxxxxxxx        //根据玩家选择的物品s1<br />    xxxxxxxxx       //减物品，加金钱等等<br />    xxxxxx          //<br />    if(s1==-1)      //选择要卖的物品时点了ＥＳＣ键退出<br />    then(ask)       //放弃卖物品跳转到"您要什么"的选择<br />    goto(sell)      //卖完一件，选择还要卖的<br />    :nosell         //不收这种物品<br />    say(12,30,"抱歉，这种东西我们不收！")<br />    goto(sell)      //继续选择要卖的物品<br />这其中的choose函数是我们在游戏程序中实现的一个多项选择的函数，以s1=choose("您要？",3,"买<br />东西 卖东西 不要了",1)为例<br />s1内放置的选项号码（第一个是０，第二个是１，依次类推）<br />    "您要？"是多项选择时的提示<br />    3是选项的个数<br />    "买东西 卖东西 不要了"是三个供玩家选项，中间以' '分隔<br />    1是强制玩家必须从这些选项中选择一个，不能按ＥＳＣ键放弃选择（此时返回-1给s1)，如果是<br />0则可以按ＥＳＣ键放弃选择。<br />    另外 chooseobj()也是我们在游戏中实现的一个函数。从玩家的物品中选择一样，返回它在玩家<br />物品匣中的位置，它在地图行走、战斗中的物品使用都可以使用。<br />    前面的八篇讲了有关RPG游戏脚本解释机的实现，从这篇起，我们就开始从一个更高的位置对游<br />戏做统筹！<br />一、首先我们来看看一般RPG游戏的大体包括的模块。 <br />   1 系统初始化/结束：(system_init/system_exit)<br />    在游戏进行之前，我们必须进行一些系统初始化的工作，象什么读取图片、音乐等数据，对屏幕<br />、声卡等硬件做初始化工作等等；同样，在游戏结束后，我们还需要进行一些整理工作，象是关闭设<br />备驱动、释放内存等等。<br />   2 标题画面：(logo)<br />    功能为显示游戏标题，选择菜单"新的游戏\读取进度\退出游戏"。并根据不同的选项分别调用<br />不同的模块。<br />    有时，我们不需要另外做开始新游戏的模块，我们只要专门做一个游戏开始状态的进度，象读取<br />进度一样读取它，就可以完成这个功能。譬如：游戏最多能保存5个进度，从game1.sav到game5.sav,<br />我们事先做好一个进度为game0.sav,保存游戏初始的状态，玩家在读取进度时可以用通过<br />load_game(int n)调用(n=1,2,3,4,5)。当开始新游戏时则通过load_game(0)调用game0.sav。<br />   3 读取/保存进度：（load_game/save_game)<br />    读取就是从文件中读出一些数据装入游戏的变量中，保存就是将游戏中的变量保存到文件中。当<br />然你也可以指定一个算法在在数据读入和写入之前进行变换，从而起到对进度加密的效果。一般需要<br />保存的数据有：主角状态、主角所在地图、 npc状态、其他数据，这些可以根据游戏具体情况进行取<br />舍。另外进度的保存在游戏中进行，读取则在标题画面或者游戏进行中都行。（当然使用剧情脚本的<br />话，你甚至可以通过和某个npc交谈或者使用某件物品来保存进度。）<br />   4 游戏进程： (run_game)<br />    一般是一个较大的循环，在循环中处理玩家的按键、控制npc的运动、处理其他实时的事件、显<br />示地图等等。<br />二、模块的运行关系<br />    游戏运行时，首先进行系统设置system_init()，然后调用标题画面i=logo()，如果i==0即玩者<br />选择"新的游戏"，那么开始新游戏load_game(0)，然后进行游戏run_game()；如果i==1即选择"旧<br />的进度"则选择进度号l=choose_progress(),如果l==0返回标题画面。如果1&lt;=n&lt;=5则读取进度<br />load_game(l),然后再进行游戏run_game()；如果i==2即玩者选择"退出游戏"，则调用结束模块<br />system_exit()，然后结束程序。<br />    当然在游戏进行过程中run_game()中，也可以读取进度load_game(l)和保存进度save_game(l);<br />三、其它<br />    这些模块中，除了游戏进程模块run_game外，都比较容易实现，所以我们就略过不讲，今后着重<br />讲有关run_game的部分。 <br />    世界是在不停运动改变着的，我们用游戏所创造的虚拟世界也是这样，这种不断的运动在我们的<br />程序中就体现为循环。程序实际上是一种在计算机和用户之间进行交互的工具，为了响应用户的控制<br />，程序需要了解用户的输入信息，并把通过对计算机的控制做出相应的响应。但程序怎样了解用户的<br />输入信息呢？那就需要我们的程序主动的对用户用来输入信息的硬件（如键盘、鼠标、游戏杆等等）<br />进行检测。<br />    用户可能在任何时候输入信息，因此程序也必须随时准备接收这种输入，在程序中接受输入的两<br />种方法有两种：一种是程序完全停止下来准备接收，直到接收到数据才开始继续运行；另一种是程序<br />以一定的频率在不断的循环，如果发现有输入信息，就对输入进行处理，如果没有输入信息时，程序<br />就继续循环并处理其他的事情。（就向tc里的bioskey(0)和bioskey(1),或者是windows编程中<br />GetMessage和PeekMessage)<br />    注意：上面这两种方法的划分，是完全从编程的角度来看的，<br />          即从某个函数或者方法来看的。实际上在硬件或者更<br />          低级的机器语言中，输入的接收是完全采用循环检测<br />          实现的。<br />    显而易见，第一种方法有它的局限性，它是一种单向不可逆的交互过程，在需要用户一步步输入<br />信息的简单程序中比较适用，但在需要双向交互的实时程序中却难以适应。试想在动作或者射击类游<br />戏中，等待玩家每次按键后才运动、攻击的敌人是多么的愚蠢可笑呀！（我原来就在APPLE-II上做过<br />这样的游戏）<br />    因此第二种方法才是游戏运行中关键的输入接收方法。也就是说，当玩家不进行输入操作时，程<br />序的循环就会去执行其他的事情，如控制敌人运动、攻击等等，而不是停下来等你输入。<br />    当然，我们在游戏中也需要程序停下来等待输入的时候，比如"请按任意键 press any key... <br />"就是我们经常使用它的地方。<br />    上面讲的这些并不是废话，因为在游戏中确需要区分这两种输入方法，正确的使用它们才能达到<br />你预期的效果。<br />    比如：在 RPG游戏中，人物对话显示一屏后，就会等待玩家按键看下一屏对话。这时我们就可以<br />采用第一种方法，将程序完全停下来等待按键，也可以在玩家没有按键的时候在人物对话框的下方闪<br />烁着显示一个按键的图形，提示玩家按键。这时就需要采用上面提到的第二种方法。在游戏中这样的<br />细节很多，你完全可以自己决定采用什么的方法以达到什么样的效果。<br />    我们的游戏主体，实际上就是在不断地处理着这样的用户输入、并对它做出响应的一个大的循环<br />体。有了这一概念，我们在对它进行设计时，就容易多了。<br />    循环结构开始<br />      --处理NPC<br />      --检测用户按键<br />      --如果是方向键，进行主角移动处理。如果触发事件，进<br />        行事件处理。<br />      --如果是Esc键，弹出游戏菜单，根据玩家选择处理。<br />      --刷新屏幕（可以设定独立的定时事件完成）<br />    循环结构结束  <br />        注意，其中的屏幕刷新，是由屏幕刷新率决定的，可以设置独立的定时事件来完成，也可以<br />放在这个主循环内进行。具体的实现方法，我们下次再讲。<img src ="http://www.cppblog.com/swo2006/aggbug/11377.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/swo2006/" target="_blank">swo</a> 2006-08-17 21:59 <a href="http://www.cppblog.com/swo2006/articles/11377.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏开发相关地址</title><link>http://www.cppblog.com/swo2006/articles/11376.html</link><dc:creator>swo</dc:creator><author>swo</author><pubDate>Thu, 17 Aug 2006 13:46:00 GMT</pubDate><guid>http://www.cppblog.com/swo2006/articles/11376.html</guid><wfw:comment>http://www.cppblog.com/swo2006/comments/11376.html</wfw:comment><comments>http://www.cppblog.com/swo2006/articles/11376.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/swo2006/comments/commentRss/11376.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/swo2006/services/trackbacks/11376.html</trackback:ping><description><![CDATA[
		<font style="font-size: 14pt;" color="#295200">
				<b>游戏开发相关地址 </b>
		</font>作　　者： silodiq (梦里踏雪:兄弟会) <br />Game Developer Magazine <br /><a href="http://www.gdmag.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.gdmag.com/ </font></a><br /><br /><br />3D Programming - Game Development <br /><a href="http://www.3dlinks.com/3dprogramming_gamedev.cfm" target="_blank"><font color="#000066" face="Tahoma">http://www.3dlinks.com/3dprogramming_gamedev.cfm </font></a><br /><br /><br />Open Directory: Game Design - Development Tools and Software <br /><a href="http://dmoz.org/Games/Game_Design/Development_Tools_and_Software/" target="_blank"><font color="#000066" face="Tahoma">http://dmoz.org/Games/Game_Design/Development_Tools_and_Software/ </font></a><br /><br /><br />Google Web Directory: Game Design - Development Tools and Software <br /><a href="http://directory.google.com/Top/Games/Game_Design/Development_Tools_and_Software/" target="_blank"><font color="#000066" face="Tahoma">http://directory.google.com/Top/Games/Game_Design/Development_Tools_and_Software/ </font></a><br /><br /><br />Game Design and Development Index <br /><a href="http://kuoi.asui.uidaho.edu/%7Ekamikaze/GameDesign/" target="_blank"><font color="#000066" face="Tahoma">http://kuoi.asui.uidaho.edu/~kamikaze/GameDesign/ </font></a><br /><br /><br />Game Development Course List <br /><a href="http://www.artschool.com/html/programs/courses/game.html" target="_blank"><font color="#000066" face="Tahoma">http://www.artschool.com/html/programs/courses/game.html </font></a><br /><br /><br />3D-Character Animation and Design Tool <br /><a href="http://www.digitalgamedeveloper.com/Htm/Tools/2000/July00/curiouslabs_rereleases_poser.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.digitalgamedeveloper.com/Htm/Tools/2000/July00/curiouslabs_rereleases_poser.htm </font></a><br /><br /><br />WPI Game Development Club <br /><a href="http://gdc.wpi.edu/events/workshop_021402.html" target="_blank"><font color="#000066" face="Tahoma">http://gdc.wpi.edu/events/workshop_021402.html </font></a><br /><br /><br />Managing Digital Assets In Game Development <br /><a href="http://www.gamedev.net/reference/business/features/mda/" target="_blank"><font color="#000066" face="Tahoma">http://www.gamedev.net/reference/business/features/mda/ </font></a><br /><br /><br />Digital Coast Insider <br /><a href="http://www.digitalcoastinsider.org/" target="_blank"><font color="#000000" face="Tahoma">http://www.digitalcoastinsider.org/ </font></a><br /><br /><br />Tools and The Games They Play <br /><a href="http://www.digitalmedianet.com/HTM/RESEARCH/Meloni/marketoutlook/Toolsandthegamestheyplay.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.digitalmedianet.com/HTM/RESEARCH/Meloni/marketoutlook/Toolsandthegamestheyplay.htm </font></a><br /><br /><br />Hong Kong Digital Entertainment Association (HKDEA) <br /><a href="http://www.hkdea.org/article.php?sid=73" target="_blank"><font color="#000066" face="Tahoma">http://www.hkdea.org/article.php?sid=73 </font></a><br /><br /><br />First Open Source Video Game Developer Support Network &amp; Resources Center <br /><a href="http://www.collab.net/news/press/2000/indrema_release.html" target="_blank"><font color="#000066" face="Tahoma">http://www.collab.net/news/press/2000/indrema_release.html </font></a><br /><br /><br />Computer Games and Digital Cultures Conference <br /><a href="http://www.gamesconference.org/programme.html" target="_blank"><font color="#000066" face="Tahoma">http://www.gamesconference.org/programme.html </font></a><br /><br /><br />Kama Digital Entertainment Game Development <br /><a href="http://www.kama.co.kr/english/development/gamedevelop.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.kama.co.kr/english/development/gamedevelop.htm </font></a><br /><br /><br />IDG Digital Media &amp; Entertainment <br /><a href="http://www.idg.net/content/channel_content/dig_theme.html" target="_blank"><font color="#000066" face="Tahoma">http://www.idg.net/content/channel_content/dig_theme.html </font></a><br /><br /><br />Digital Hollywood <br /><a href="http://www.digitalhollywood.com/SanJoseThursdayTwelve.html" target="_blank"><font color="#000066" face="Tahoma">http://www.digitalhollywood.com/SanJoseThursdayTwelve.html </font></a><br /><br /><br />Digital Media Net Directory <br /><a href="http://www.industrybrains.com/directories/dmn/" target="_blank"><font color="#000066" face="Tahoma">http://www.industrybrains.com/directories/dmn/ </font></a><br /><br /><br />Digital Game-Based Learning (Book) 2000 <br /><a href="http://search.barnesandnoble.com/booksearch/isbnInquiry.asp?isbn=0071363440" target="_blank"><font color="#000066" face="Tahoma">http://search.barnesandnoble.com/booksearch/isbnInquiry.asp?isbn=0071363440 </font></a><br /><br /><br />Game Development Search Engine <br /><a href="http://www.game-developer.com/" target="_blank"><font color="#000000" face="Tahoma">http://www.game-developer.com/ </font></a><br /><br /><br />Evaluating Java For Game Development <br /><a href="http://www.rolemaker.dk/articles/evaljava/" target="_blank"><font color="#000066" face="Tahoma">http://www.rolemaker.dk/articles/evaljava/ </font></a><br /><br /><br />Portal For Mac Game Developers <br /><a href="http://www.idevgames.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.idevgames.com/ </font></a><br /><br /><br />Digital Media World 2002: DMW Conference <br /><a href="http://www.digmedia.co.uk/scripts/publish/information.asp?code=3_DMW" target="_blank"><font color="#000066" face="Tahoma">http://www.digmedia.co.uk/scripts/publish/information.asp?code=3_DMW </font></a><br /><br /><br />Computer Games Magazines <br /><a href="http://dir.yahoo.com/Recreation/Games/Computer_Games/Magazines/" target="_blank"><font color="#000066" face="Tahoma">http://dir.yahoo.com/Recreation/Games/Computer_Games/Magazines/ </font></a><br /><br /><br />Game Developers Links <br /><a href="http://www.alawar.com/links.html" target="_blank"><font color="#000066" face="Tahoma">http://www.alawar.com/links.html </font></a><br /><br /><br />3D Hardware - Motion Capture <br /><a href="http://www.3dlinks.com/hardware_capture.cfm" target="_blank"><font color="#000066" face="Tahoma">http://www.3dlinks.com/hardware_capture.cfm </font></a><br /><br /><br />Game Research: The Arts, Business and Science of Computer Games <br /><a href="http://www.game-research.com/business.asp" target="_blank"><font color="#000066" face="Tahoma">http://www.game-research.com/business.asp </font></a><br /><br /><br />Free Download Games Links <br /><a href="http://www.freedownload-games.com/links.php" target="_blank"><font color="#000000" face="Tahoma">http://www.freedownload-games.com/links.php </font></a><br /><br /><br />Game Network Resources for Game Development <br /><a href="http://www.gamanetwork.com/advertise.html" target="_blank"><font color="#000066" face="Tahoma">http://www.gamanetwork.com/advertise.html </font></a><br /><br /><br />Game Programming Gems 3 With CDROM (Book) 2002 <br />AI Game Programming Wisdom (Book) 2002 <br />Animation Master 2002: A Complete Guide (Book) 2002 <br />Game Programming Gems 2 (Book) 2001 <br />Game Programming Gems (Book) 2000 <br />Mathematics For 3D Game Programming and Computer Graphics (Book) 2002 <br /><a href="http://search.barnesandnoble.com/booksearch/isbninquiry.asp?endeca=1&amp;userid=687W0JISIT&amp;ean=9781584500773" target="_blank"><font color="#000066" face="Tahoma">http://search.barnesandnoble.com/booksearch/isbninquiry.asp?endeca=1&amp;userid=687W0JISIT&amp;ean=9781584500773 </font></a><br /><br /><br />Game Companies Listing <br /><a href="http://www.patches-scrolls.de/companies.html" target="_blank"><font color="#000066" face="Tahoma">http://www.patches-scrolls.de/companies.html </font></a><br /><br /><br />Video Games News (VGN) <br /><a href="http://www.videogamenews.com/publish.html" target="_blank"><font color="#000066" face="Tahoma">http://www.videogamenews.com/publish.html </font></a><br /><br /><br />Windows 95/NT Games Development Page <br /><a href="http://www.blitwise.com/Scorched_Reality/" target="_blank"><font color="#000066" face="Tahoma">http://www.blitwise.com/Scorched_Reality/ </font></a><p><br /></p><table style="table-layout: fixed;" border="0" cellpadding="0" cellspacing="0" width="90%"><tbody><tr valign="top"><td bgcolor="#ffffff" width="*">Game Programming Resources and Links <br /><a href="http://www.ziron.com/links/links.html" target="_blank"><font color="#000066" face="Tahoma">http://www.ziron.com/links/links.html </font></a><br /><br /><br />Entertainment Computing: Using Technology and Innovation To Simulate Daily Life <br /><a href="http://www.computer.org/computer/articles/April/entertainment_400.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.computer.org/computer/articles/April/entertainment_400.htm </font></a><br /><br /><br />International Workshop On Entertainment Computing <br /><a href="http://www.zzz.pe.u-tokyo.ac.jp/ML/ae-info/200204/msg00038.html" target="_blank"><font color="#000066" face="Tahoma">http://www.zzz.pe.u-tokyo.ac.jp/ML/ae-info/200204/msg00038.html </font></a><br /><br /><br />Entertainment Computing: Computer Video Games <br /><a href="http://www.jungleshop.com/gamepro/gamepro.shtml" target="_blank"><font color="#000066" face="Tahoma">http://www.jungleshop.com/gamepro/gamepro.shtml </font></a><br /><br /><br />Web3D Graphics &amp; Virtual Reality <br /><a href="http://web3d.about.com/?once=true&amp;" target="_blank"><font color="#000066" face="Tahoma">http://web3d.about.com/?once=true&amp; </font></a><br /><br /><br />Entertainment and Computing <br /><a href="http://www.nanyo.net/e/nanyo/ent.html" target="_blank"><font color="#000066" face="Tahoma">http://www.nanyo.net/e/nanyo/ent.html </font></a><br /><br /><br />Web Search Guide: Arts &amp; Entertainment <br /><a href="http://websearch.about.com/cs/artsentertainment/" target="_blank"><font color="#000066" face="Tahoma">http://websearch.about.com/cs/artsentertainment/ </font></a><br /><br /><br />Video/Console Gaming <br /><a href="http://207.153.251.233/products/ce/videogaming.shtml" target="_blank"><font color="#000066" face="Tahoma">http://207.153.251.233/products/ce/videogaming.shtml </font></a><br /><br /><br />Omnibus Entertainment Systems <br /><a href="http://www.csua.berkeley.edu/%7Edhuang/com.html" target="_blank"><font color="#000066" face="Tahoma">http://www.csua.berkeley.edu/~dhuang/com.html </font></a><br /><br /><br />Best Of Windows Entertainment <br /><a href="http://www.computing.net/windows31/wwwboard/forum/7385.html" target="_blank"><font color="#000066" face="Tahoma">http://www.computing.net/windows31/wwwboard/forum/7385.html </font></a><br /><br /><br />SUN Entertainment &amp; Media User Group (SEMUG) <br /><a href="http://www.semug.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.semug.com/ </font></a><br /><br /><br />Handheld Computing: Pocket Express Entertainment Pack <br /><a href="http://www.hhcmag.com/backissues/225.html" target="_blank"><font color="#000066" face="Tahoma">http://www.hhcmag.com/backissues/225.html </font></a><br /><br /><br />Hot and Current Topics In The Mobile Computing Industry <br /><a href="http://www.mobileinfo.com/Hot_Topics/" target="_blank"><font color="#000066" face="Tahoma">http://www.mobileinfo.com/Hot_Topics/ </font></a><br /><br /><br />Handheld Computing Solutions <br /><a href="http://www.palm.com/europe/dirload.html" target="_blank"><font color="#000066" face="Tahoma">http://www.palm.com/europe/dirload.html </font></a><br /><br /><br />Digital Entertainment <br /><a href="http://computing-technology.derby.ac.uk/index.php?node=668" target="_blank"><font color="#000066" face="Tahoma">http://computing-technology.derby.ac.uk/index.php?node=668 </font></a><br /><br /><br />Convergence of Computing and Audio/Video Entertainment Has Arrived <br /><a href="http://www.computingsa.co.za/2000/11/13/Hardware/har01.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.computingsa.co.za/2000/11/13/Hardware/har01.htm </font></a><br /><br /><br />Top Family-Friendly Sites On The Web <br /><a href="http://www.edutainingkids.com/topinternetlinks.html" target="_blank"><font color="#000066" face="Tahoma">http://www.edutainingkids.com/topinternetlinks.html </font></a><br /><br /><br />Microsoft Brings In-Car Computing Technology To Consumers At CES <br /><a href="http://www.microsoft.com/PressPass/press/2001/Jan01/01-06InCarPR.asp" target="_blank"><font color="#000066" face="Tahoma">http://www.microsoft.com/PressPass/press/2001/Jan01/01-06InCarPR.asp </font></a><br /><br /><br />Ultimate Digital Entertainment and Home Computing <br /><a href="http://www.sony.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.sony.com/ </font></a><br /><br /><br />Entertainment <br /><a href="http://www.computingdreams.com/menu/enter.shtml" target="_blank"><font color="#000066" face="Tahoma">http://www.computingdreams.com/menu/enter.shtml </font></a><br /><br /><br />ACM Symposium On Applied Computing: Virtual Reality, Digital Media, and Computer Games <br /><a href="http://vitalstatistix.nicve.salford.ac.uk/sac2002/" target="_blank"><font color="#000066" face="Tahoma">http://vitalstatistix.nicve.salford.ac.uk/sac2002/ </font></a><br /><br /><br />3D Web Animation <br /><a href="http://www.brilliantdigital.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.brilliantdigital.com/ </font></a><br /><br /><br />Kronos Digital Entertainment <br /><a href="http://www.kronosdigital.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.kronosdigital.com/ </font></a><br /><br /><br />Broadway Digital Entertainment <br /><a href="http://www.broadwaydigitalentertainment.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.broadwaydigitalentertainment.com/ </font></a><br /><br /><br />Tribe Digital Entertainment <br /><a href="http://home.earthlink.net/%7Etribedigital/" target="_blank"><font color="#000066" face="Tahoma">http://home.earthlink.net/~tribedigital/ </font></a><br /><br /><br />Special Report: Digital Entertainment Rights and Responsibities <br /><a href="http://news.glass.mpr.org/programs/futuretense/docs/digital_piracy.shtml" target="_blank"><font color="#000066" face="Tahoma">http://news.glass.mpr.org/programs/futuretense/docs/digital_piracy.shtml </font></a><br /><br /><br />Brilliant Digital Entertainment's Annual Report <br /><a href="http://news.com.com/2009-1023-873905.html" target="_blank"><font color="#000066" face="Tahoma">http://news.com.com/2009-1023-873905.html </font></a><br /><br /><br />Digital Dreams Entertainment <br /><a href="http://www.dd-ent.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.dd-ent.com/ </font></a><br /><br /><br />Fusion Digital Entertainment <br /><a href="http://www.fusiondigital.co.uk/" target="_blank"><font color="#000066" face="Tahoma">http://www.fusiondigital.co.uk/ </font></a><br /><br /><br />Digital Hit Entertainment <br /><a href="http://www.digitalhit.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.digitalhit.com/ </font></a><br /><br /><br />InterTech Digital Entertainment <br /><a href="http://www.intertechdigital.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.intertechdigital.com/ </font></a><br /><br /><br />Hollywood, High-Tech Battle Over Digital Content <br /><a href="http://www.usatoday.com/life/cyber/tech/2002/06/25/bonus-cover.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.usatoday.com/life/cyber/tech/2002/06/25/bonus-cover.htm </font></a><br /><br /><br />HP Digital Entertainment <br /><a href="http://www.hp.com/hpinfo/newsroom/press/20jun01a.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.hp.com/hpinfo/newsroom/press/20jun01a.htm </font></a><br /><br /><br />HP Hopes To Reach The Living Room <br /><a href="http://www.internetnews.com/bus-news/article.php/9_787791" target="_blank"><font color="#000066" face="Tahoma">http://www.internetnews.com/bus-news/article.php/9_787791 </font></a><br /><br /><br />Slomedia The Agent of The Digital Entertainment Revolution <br /><a href="http://www.slomedia.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.slomedia.com/ </font></a><br /><br /><br />Persistence of Vision Digital Entertainment <br /><a href="http://www.povde.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.povde.com/ </font></a><br /><br /><br />Digital Theatre Systems (DTS) <br /><a href="http://www.dtsonline.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.dtsonline.com/ </font></a><br /><br /><br />Anderson Digital Entertainment <br /><a href="http://www.dtsonline.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.dtsonline.com/ </font></a><br /><br /><br />Digital Cinema Entertainment Film Links <br /><a href="http://www.digitalcinemaentertainment.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.digitalcinemaentertainment.com/ </font></a><br /><br /><br />Technology Meet Other Digital Entertainment Networks <br /><a href="http://www.nypost.com/technology/50455.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.nypost.com/technology/50455.htm </font></a><br /><br /><br />Extreme Digital Entertainment Media Appliance Concept <br /><a href="http://www.edgereview.com/ataglance.cfm?category=Video&amp;ID=72" target="_blank"><font color="#000066" face="Tahoma">http://www.edgereview.com/ataglance.cfm?category=Video&amp;ID=72 </font></a><br /><br /><br />Enabling Entertainment's Digital Future <br /><a href="http://www.artesia.com/entertainment/" target="_blank"><font color="#000066" face="Tahoma">http://www.artesia.com/entertainment/ </font></a><br /><br /><br />Digital Entertainment Network <br /><a href="http://www.microsoft.com/msft/invest/digital_entertain_network.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.microsoft.com/msft/invest/digital_entertain_network.htm </font></a><br /><br /><br />The Digital Television Group <br /><a href="http://www.dtg.org.uk/" target="_blank"><font color="#000066" face="Tahoma">http://www.dtg.org.uk/ </font></a><br /><br /><br />Home Video/DVD Resources <br /><a href="http://homevideo.about.com/mbody.htm?iam=excite_1&amp;terms=digital+entertainment" target="_blank"><font color="#000066" face="Tahoma">http://homevideo.about.com/mbody.htm?iam=excite_1&amp;terms=digital+entertainment </font></a><br /><br /><br />A Digital Archive of Architecture <br /><a href="http://www.bc.edu/bc_org/avp/cas/fnart/arch/" target="_blank"><font color="#000066" face="Tahoma">http://www.bc.edu/bc_org/avp/cas/fnart/arch/ </font></a><br /><br /><br />Digital Film Archive <br /><a href="http://www.digitalfilmarchive.net/" target="_blank"><font color="#000066" face="Tahoma">http://www.digitalfilmarchive.net/ </font></a><br /><br /><br />Digital Music Archives <br /><a href="http://www.digital-music-archives.com/cgi-bin/dmaweb/store.cgi?" target="_blank"><font color="#000066" face="Tahoma">http://www.digital-music-archives.com/cgi-bin/dmaweb/store.cgi? </font></a><br /><br /><br />Powerful Software Solutions For Film and TV Post-Production <br /><a href="http://www.slanecon.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.slanecon.com/</font></a><br /></td></tr><tr valign="top"><td bgcolor="#ffffff" width="*"><br />中国游戏开发者<br /><a href="http://mays.soage.com/" target="_blank"><font color="#000066" face="Tahoma">http://mays.soage.com</font></a><br />最近不能访问了,不过相信很快会好的<br />论坛还正常运行<br /><a href="http://cgd.pages.com.cn/cvbb/" target="_blank"><font color="#000066" face="Tahoma">http://cgd.pages.com.cn/cvbb/</font></a><br />中国游戏开发者的临时域名<br /><a href="http://cgd.pages.com.cn/cgd/" target="_blank"><font color="#000066" face="Tahoma">http://cgd.pages.com.cn/cgd/</font></a><p><a href="http://cgd.pages.com.cn/cgd/develop/3D/200109/3DProg.0.htm" target="_blank"><font color="#000066" face="Tahoma">http://cgd.pages.com.cn/cgd/develop/3D/200109/3DProg.0.htm</font></a></p><p><a href="http://www.hardcore3d.net/Res/link.htm" target="_blank"><font color="#000066" face="Tahoma">http://www.hardcore3d.net/Res/link.htm</font></a></p><p><a href="http://www.3dstate.com/" target="_blank"><font color="#000066" face="Tahoma">http://www.3dstate.com</font></a></p><p>中国游戏开发资源网<br /><a href="http://www.gameres.com/" target="_blank"><font color="#000000" face="Tahoma">http://www.gameres.com</font></a><br /></p></td></tr></tbody></table><img src ="http://www.cppblog.com/swo2006/aggbug/11376.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/swo2006/" target="_blank">swo</a> 2006-08-17 21:46 <a href="http://www.cppblog.com/swo2006/articles/11376.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>小谈网络游戏同步</title><link>http://www.cppblog.com/swo2006/articles/11371.html</link><dc:creator>swo</dc:creator><author>swo</author><pubDate>Thu, 17 Aug 2006 12:30:00 GMT</pubDate><guid>http://www.cppblog.com/swo2006/articles/11371.html</guid><wfw:comment>http://www.cppblog.com/swo2006/comments/11371.html</wfw:comment><comments>http://www.cppblog.com/swo2006/articles/11371.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/swo2006/comments/commentRss/11371.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/swo2006/services/trackbacks/11371.html</trackback:ping><description><![CDATA[
		<font style="font-size: 14pt;" color="#295200">
				<b>小谈网络游戏同步[转]</b>
		</font>
		<table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td>
										<div style="margin: 15px;" id="art">
												<font face="Times New Roman" size="2">        同步在网络游戏中是非常重要的，它保证了每个玩家在屏幕上看到的东
西大体是一样的。其实呢，解决同步问题的最简单的方法就是把每个玩家的动作都向其他玩家广播一遍，这里其实就存在两个问题：1，向哪些玩家广播，广播哪些
消息。2，如果网络延迟怎么办。事实上呢，第一个问题是个非常简单的问题，不过之所以我提出这个问题来，是提醒大家在设计自己的消息结构的时候，需要把这
个因素考虑进去。而对于第二个问题，则是一个挺麻烦的问题，大家可以来看这么个例子：　</font>
												<p>
														<font face="Times New Roman" size="2"> 　比如有一个玩家A向服务器发了条指令，说我现在在P1点，要去
P2点。指令发出的时间是T0，服务器收到指令的时间是T1，然后向周围的玩家广播这条消息，消息的内容是“玩家A从P1到P2”有一个在A附近的玩家
B，收到服务器的这则广播的消息的时间是T2，然后开始在客户端上画图，A从P1到P2点。这个时候就存在一个不同步的问题，玩家A和玩家B的屏幕上显示
的画面相差了T2-T1的时间。这个时候怎么办呢？ </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　有个解决方案，我给它取名叫
预测拉扯，虽然有些怪异了点，不过基本上大家也能从字面上来理解它的意思。要解决这个问题，首先要定义一个值叫：预测误差。然后需要在服务器端每个玩家连
接的类里面加一项属性，叫TimeModified，然后在玩家登陆的时候，对客户端的时间和服务器的时间进行比较，得出来的差值保存在
TimeModified里面。还是上面的那个例子，服务器广播消息的时候，就根据要广播对象的TimeModified，计算出一个客户端的
CurrentTime，然后在消息头里面包含这个CurrentTime，然后再进行广播。并且同时在玩家A的客户端本地建立一个队列，保存该条消息，
只到获得服务器验证就从未被验证的消息队列里面将该消息删除，如果验证失败，则会被拉扯回P1点。然后当玩家B收到了服务器发过来的消息“玩家A从P1到
P2”这个时候就检查消息里面服务器发出的时间和本地时间做比较，如果大于定义的预测误差，就算出在T2这个时间，玩家A的屏幕上走到的地点P3，然后把
玩家B屏幕上的玩家A直接拉扯到P3，再继续走下去，这样就能保证同步。更进一步，为了保证客户端运行起来更加smooth，我并不推荐直接把玩家拉扯过
去，而是算出P3偏后的一点P4，然后用(P4-P1)/T(P4-P3)来算出一个很快的速度S，然后让玩家A用速度S快速移动到P4，这样的处理方法
是比较合理的，这种解决方案的原形在国际上被称为（Full
plesiochronous），当然，该原形被我篡改了很多来适应网络游戏的同步，所以而变成所谓的：预测拉扯。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　另外一个解决方案，我给它取名叫
验证同步，听名字也知道，大体的意思就是每条指令在经过服务器验证通过了以后再执行动作。具体的思路如下：首先也需要在每个玩家连接类型里面定义一个
TimeModified，然后在客户端响应玩家鼠标行走的同时，客户端并不会先行走动，而是发一条走路的指令给服务器，然后等待服务器的验证。服务器接
受到这条消息以后，进行逻辑层的验证，然后计算出需要广播的范围，包括玩家A在内，根据各个客户端不同的TimeModified生成不同的消息头，开始
广播，这个时候这个玩家的走路信息就是完全同步的了。这个方法的优点是能保证各个客户端之间绝对的同步，缺点是当网络延迟比较大的时候，玩家的客户端的行
为会变得比较不流畅，给玩家带来很不爽的感觉。该种解决方案的原形在国际上被称为（Hierarchical master-slave
synchronization），80年代以后被广泛应用于网络的各个领域。 　　</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">           
最后一种解决方案是一种理想化的解决方案，在国际上被称为Mutual
synchronization，是一种对未来网络的前景的良好预测出来的解决方案。这里之所以要提这个方案，并不是说我们已经完全的实现了这种方案，而
只是在网络游戏领域的某些方面应用到这种方案的某些思想。我对该种方案取名为：半服务器同步。大体的设计思路如下：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　首先客户端需要在登陆世界的时候建立很多张广播列表，这些列
表在客户端后台和服务器要进行不及时同步，之所以要建立多张列表，是因为要广播的类型是不止一种的，比如说有local
message,有remote message,还有global message
等等，这些列表都需要在客户端登陆的时候根据服务器发过来的消息建立好。在建立列表的同时，还需要获得每个列表中广播对象的TimeModified，并
且要维护一张完整的用户状态列表在后台，也是不及时的和服务器进行同步，根据本地的用户状态表，可以做到一部分决策由客户端自己来决定，当客户端发送这部
分决策的时候，则直接将最终决策发送到各个广播列表里面的客户端，并对其时间进行校对，保证每个客户端在收到的消息的时间是和根据本地时间进行校对过的。
那么再采用预测拉扯中提到过的计算提前量，提高速度行走过去的方法，将会使同步变得非常的smooth。该方案的优点是不通过服务器，客户端自己之间进行
同步，大大的降低了由于网络延迟而带来的误差，并且由于大部分决策都可以由客户端来做，也大大的降低了服务器的资源。由此带来的弊端就是由于消息和决策权
都放在客户端本地，所以给外挂提供了很大的可乘之机。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　综合以上三种关于网络同步派系的优缺点，综合出一套关于网络游戏传输同步的较完整的解决方案，我称它为综合同步法（colligate synchronization）。大体设计思路如下：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　首先将服务器需要同步的所有消息从划分一个优先等级，然后按照3/4的比例划分出重要消息和非重要消息，对于非重要消息，把决策权放在客户端，在客户端逻辑上建立相关的决策机构和各种消息缓存区，以及相关的消息缓存区管理机构，如下图所示：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> <img src="http://dev.gameres.com/Program/Abstract/DeadReckoning1.gif" alt="" border="0" height="247" width="508" /></font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　上图简单说明了对于非重要消息，客户端的大体处理流程，其中有
一个客户端被动行为值得大家注意，其中包括对服务器发过来的某些验证代码做返回，来确保消息缓存中的消息和服务器端是一致的，从而有效的防止外挂来篡改本
地消息缓存。其中的消息来源是包括本地的客户端响应玩家的消息以及远程服务器传递过来的消息。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　对于重要消息，比如说战斗或者是某些牵扯到玩家一些比较敏感
数据的操作，则采用另外一套方案，该方案首先需要在服务器和客户端之间建立一套Ping
System，然后服务器保存和用户的及时的ping值，当ping比较小的时候，响应玩家消息的同时先不进行动作，而是先把该消息反馈给服务器，并且阻
塞，服务器收到该消息，进行逻辑验证之后向所有该详细广播的有效对象进行广播（包括消息发起者），然后客户端收到该消息的验证，才开始执行动作。而当
ping比较大的时候，客户端响应玩家消息的同时立刻进行动作，并且同时把该消息反馈给服务器，值得注意的是这个时候还需要在本地建立一个无验证消息的队
列，把该消息入队，执行动作的同时等待服务器的验证，还需要保存当前状态。服务器收到客户端的请求后，进行逻辑验证，并把消息反馈到各个客户端，带上各个
客户端校对过的本地时间。如果验证通过不过，则通知消息发起者，该消息验证失败，然后客户端自动把已经在进行中的动作取消，恢复原来状态。如果验证通过，
则广播到的各个客户端根据从服务器获得校对时间进行对其进行拉扯，保证在该行为完成之前完成同步。 　　</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">
																<img src="http://dev.gameres.com/Program/Abstract/DeadReckoning2.gif" alt="" border="0" height="396" width="587" />
														</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">         至此，一个比较成熟的网络游戏的同步机制已经初步建立起来了，接下来的逻辑代码就根据各自不同的游戏风格以及侧重点来写了。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　同步是网络游戏最重要的问题，如何同步也牵扯到各个方面的问
题，比如说游戏的规模，游戏的类型以及各种各样的方面，对于规模比较大的游戏，在同步方面可以下很多的工夫，把消息分得十分的细腻，对于不同的消息采用不
同的同步机制，而对于规模比较小的游戏，则可以采用大体上一样的同步机制，究竟怎么样同步，没有个定式，是需要根据自己的不同情况来做出不同的同步决策的
网游同步算法之导航推测（Dead Reckoning）算法：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　在了解该算法前，我们先来谈谈该算法的一些背景资料。大家都
知道，在网络传输的时候，延迟现象是很普遍的，而在基于Server/Client结构下的网络游戏的同步也就成了很头疼的问题，在保证客户端响应用户本
地指令流畅的情况下，没法有效的保证的同步的及时性。同样，在军方也有类似的事情发生，即使是同一LAN里面的机器，也会因为传输的延迟，导致一些运算的
失误，介于此，美国国防部投入了大量的资金用于研究一种比较的好的方案来解决分布式系统中的延迟问题，特别是一个叫分布式模拟运动
（Distributed Interactive Simulation）的系统，这套系统呢，其中就提出了一套号称是Latency Hiding
&amp; Bandwidth Reduction的方案，命名为Dead
Reckoning。呵呵，来头很大吧，恩，那么我们下面就来看看这套系统的一些观点，以及我们如何把它运用到我们的网络游戏的同步中。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　首先，这套同步方案是基于我那篇《网络游戏的同步》一文中的Mutual Synchronization同步方案的，也就是说，它并不是Server/Client结构的，而是基于客户端之间的同步的。下面我们先来说一些本文中将用到的名词概念：　　</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">网状网络：客户端之间构成的网络　　</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">节点：网状网络中的每个客户端　　</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">极限误差：进行同步的时候可能产生的误差的极值 　　</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">       
恩，在探讨其原理的之前，我们先来看看我们需要一个什么样的环境。首先，需要一个网状网络，网状网络如何构成呢？当有新节点进入的时候，通知该网络里面的
所有节点，各节点为该客户端在本地创建一个副本，登出的时候，则通知所有节点销毁本地关于该节点的副本。然后每个节点该保存一些什么数据呢？首先有一个很
重要的包需要保存，叫做协议数据包（PDU Protocol Data
Unit），PDU包含节点的一些相关的运动信息，比如当前位置，速度，运动方向，或者还有加速度等一些信息。除PDU之外，还有其他信息需要保存，比如
说节点客户端人物的HP，MP之类的。然后，保证每个节点在最少8秒之内要向其它节点广播一次PDU信息。最后，设置一个极限误差值。到此，其环境就算搭
建完成了。下面，我们就来看看相关的具体算法： 　　</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">        
假设在节点A有一个小人（路人甲），开始跑路了，这个时候，就像所有的节点广播一次他的PDU信息，包括：速度（S），方向（O），加速度（A）。那么所
有的节点就开始模拟路人甲的运动轨迹和路线，包括节点A本身（这点很重要），同时，路人甲在某某玩家的控制下，会不时的改变一下方向，让其跑路的路线变得
不是那么正规。在跑路的过程中，节点A有一个值在不停的记录着其真实坐标和在后台模拟运动的坐标的差值，当差值大于极限误差的时候，则计算出当前的速度
S，方向O和速度A（算法将在后面介绍），并广播给网络中其他所有节点。其他节点在收到这条消息之后呢，就可以用一些很平滑的移动把路人甲拉扯过去，然后
重新调整模拟跑路的数据，让其继续在后台模拟跑路。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　很显然，如果极限误差定义得大了，其他节点看到的偏差就会过
大，如果极限偏差定义得小了，网络带宽就会增大。如果定义这个极限误差，就该根据各种数据的重要性来设计了。如果是回合制的网络游戏，那么在走路上把极限
误差定义得大些无所谓，可以减少带宽。但是如果是及时打斗的网络游戏，那么就得把极限误差定义得小一些，否则会出现某人看到某人老远把自己给砍死的情况。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　Dead Reckoning的主要算法有9种，但是只有两种是解决主要问题的，其他的基本上只是针对不同的坐标系的一些不同的算法，这里就不一一介绍了。好，那么我们下面来看传说中的最主要的两种算法：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　　第一：目标点 = 原点 + 速度 * 时间差</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　　第二：目标点 = 原点 + 速度 * 时间差 + 1/2 * 加速度 * 时间差呵呵，传说中的算法都是很经典的，虽然我们早在初中物理的时候就学过。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　该算法的好处呢，正如它开始所说的，Latency
Hiding &amp; Bandwidth
Reduction，从原则上解决了网络延迟导致的不同步的问题，并且有效的减少了带宽，不好的地方就是该算法基本上只能使用于移动中的同步，当然，移动
的同步是网络游戏中同步的最大的问题。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　该方法结合我在《网络游戏的同步》一文中提出的综合同步法的构架可以基本上解决掉网络游戏中走路同步的问题。相关问题欢迎大家一起讨论。 有关导航推测算法（Dead Reckoning）中的平滑处理：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　根据我上篇文章所介绍的，在节点A收到节点B新的PDU包
时，如果和A本地的关于B的模拟运动的坐标不一致时，怎么样在A的屏幕上把B拽到新的PDU包所描叙的点上面去呢，上文中只提了用“很平滑的移动”把B
“拉扯”过去，那么实际中应该怎么操作呢？这里介绍四种方法。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　第一种方法，我取名叫直接拉扯法，大家听名字也知道，就是直接把B硬生生的拽到新的PDU包所描叙的坐标上去，该方法的好处是：简单。坏处是：看了以下三种方法之后你就不会用这种方法了。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　第二种方法，叫直线行走（Linear），即让B从它的当前坐标走直线到新的PDU包所描叙的坐标，行走速度用上文中所介绍的经典算法：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　　目标点 = 原点 + 速度 * 时间差 + 1/2 * 加速度 * 时间差算出：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　首先算出从当前坐标到PDU包中描叙的坐标所需要的时间：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　　T = Dest( TargetB – OriginB ) / Speed </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　然后根据新PDU包中所描叙的坐标信息模拟计算出在时间T之后，按照新的PDU包中的运动信息所应该达到的位置：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　　_TargetB = NewPDU.Speed * T </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　然后根据当前模拟行动中的B和_TargetB的距离配合时间T算出一个修正过的速度_S</font>
														<font face="Times New Roman" size="2">：</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　　_S = Dest( _TargetB – OriginB ) / T </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　然后在画面上让B以速度_S走直线到Target_B，并且在走到之后调整其速度，方向，加速度等信息为新的PDU包中所描叙的。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　这种方法呢，非常的土，会让物体在画面上移动起来变得非常的不现实，经常会出现很生硬的拐角，而且对于经常要修改的速度_S，在玩家A的画面上，玩家B的行动会变得非常的诡异。其好处是：比第一种方法要好。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　第三种方法，叫二次方程行走（Quadratic），该方法
的原理呢，就是在直线行走的过程中，加入二次方程来计算一条曲线路径，让Dest( _TargetB – OriginB
)的过程是一条曲线，而不是一条直线，恩，具体的实现方法，就是在Linear方法的计算中，设定一个二次方程，在Dest函数计算距离的时候根据设定的
二次方程来计算，这样一来，可以使B在玩家A屏幕上的移动变得比较的有人性化一些。但是该方法的考虑也是不周全的，仅仅只考虑了TargetB到
_TargetB的方向，而没有考虑新的PDU包中的方向描叙，那么从_TargetB开始模拟行走的时候，仍然是会出现比较生硬的拐角，那么下面提出的
最终解决方案，将彻底解决这个问题。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　最后一种方法叫：立方体抖动（Cubic
Splines），这个东东比较复杂，它需要四个坐标信息作为它的参数来进行运算，第一个参数Pos1是OriginB，第二个参数Pos2是
OriginB在模拟运行一秒以后的位置，第三个参数Pos3是到达_TargetB前一秒的位置，第四个参数pos4是_TargetB的位置。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> Struct pos { Coordinate X; Coordinate Y; } </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　Pos1 = OriginB </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　Pos2 = OriginB + V </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　Pos3 = _TargetB – V</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　　Pos4 = _TargetB </font>
														<font face="Times New Roman" size="2">运动轨迹中(x, y)的坐标。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　x = At^3 + Bt^2 + Ct + D </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　y = Et^3 + Ft^2 + Gt + H </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">（其中时间t的取值范围为0-1，在Pos1的时候为0，在Pos4的时候为1） x(0-3)代表Pos1-Pos4中x的值，y(0-3)代表Pos1-Pos4中y的值</font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　A = x3 – 3 * x2 +3 * x1 – x0 </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　B = 3 * x2 – 6 * x1 + 3 * x0</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　　C = 3 * x1 – 3 * x0 </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　D = x0 </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　E = y3 – 3 * y2 +3 * y1 – y0</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 　　　F = 3 * y2 – 6 * y1 + 3 * y0 </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　G = 3 * y1 – 3 * y0 </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　　H = y0 </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">　　上面是公式，那么下面我们来看看如何获得Pos1-Pos4：
首先，Pos1和
Pos2的取值会比较容易获得，根据OriginB配合当前的速度和方向可以获得，然而Pos3和Pos4呢，怎么获得呢？如果在从Pos1到Pos4的
过程中有新的PDU到达，那么我们定义它为NewPackage。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> Pos3 = NewPackage.X + NewPackage.Y * t + 1/2 * NewPackage.a * t^2 </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">Pos4 = Pos3 – (NewPackage.V + NewPackage.a * t) </font>
												</p>
												<p>
														<font face="Times New Roman" size="2">如果没有NewPackage的情况下,则Pos3和Pos4按照开始所规定的方法获得。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 至此，关于导航推测的算法大致介绍完毕。</font>
												</p>
												<p>
														<font face="Times New Roman" size="2"> 欢迎讨论，联系作者：QQ 181194 MSN: <a href="mailto:xiataiyi@hotmail.com">xiataiyi@hotmail.com</a></font>
												</p>
												<p>
														<font face="Times New Roman" size="2">参考文献《Defeating Lag with Cubic Splines》</font>
												</p>
										</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/swo2006/aggbug/11371.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/swo2006/" target="_blank">swo</a> 2006-08-17 20:30 <a href="http://www.cppblog.com/swo2006/articles/11371.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>