📄 g9.htm
字号:
击命令之后,再选择了一群敌人,那敌人就是命令客体。 <br>
如果所有的命令都这样下达,那编起程序就简单了,玩游戏的人也会累死了。我们常用 <br>
的玩法是选择了命令的主体后直接用鼠标右键点击命令客体。而命令的产生就由命令主体 <br>
和命令客体的类别来确定了。 <br>
这才是需要我们考虑的东西。 <br>
首先是选择命令主体。当我们在战场上鼠标一点,或圈了一个框,然后一松手,这时侯 <br>
程序就开始算了。这里我要介绍一下<赤壁>的单元类型。我们把单元分成士兵,将领和建 <br>
筑。士兵又有已经组建成部队的和尚未组建成部队的。士兵的种类可以分成一般士兵,工 <br>
人和船。我们的选择规则是:组队的士兵优先于建筑。将领优先于组队的士兵。未组队的 <br>
一般士兵优先于将领,未组队的船优先于一般士兵,未组队的工人优先于船。另外还有特 <br>
殊处理。按下Ctrl键则选中该组队士兵的将领,按下Shift键则把刚选中的士兵添加到已 <br>
选中的士兵中。 <br>
其次,是选择命令的客体。命令客体可分为地点和单元。地点有普通地点和资源地点。 <br>
单元又分为敌人的和我方的。单元的种类有士兵,将领和建筑。士兵分为未组队的和组队 <br>
的。士兵的种类有工人和一般士兵。建筑又分为一般建筑和资源建筑。我们需要根据命令 <br>
主体和客体共同来判断命令是什么。不同的组合所产生的命令可能可能是不同的。比如, <br>
如果我选择了组队的工人作为命令主体,又选择了一个建筑作为命令客体。如果这个建筑 <br>
是铁矿,那么命令就是采矿。如果这个建筑是我方的建筑,则这个命令是修理。如果这个 <br>
建筑是敌人的建筑,那这个命令就是攻击了。 <br>
有关内容请详见CBCtrl.cpp。 <br>
<br>
眼睛函数 <br>
在游戏策划编写人工智能的时侯,总喜欢写成这样:“当匪兵甲发现敌人就在不远处时 <br>
就去攻击。”而要把这句话变成程序,还需要不少的路要走。比如匪兵甲是如何发现敌人 <br>
就在附近呢?首先必须要知道匪兵甲自己在什么地方,其次匪兵甲的视野有多大,也就是 <br>
说多远算作附近,第三敌人在哪里。这三样东西匪兵甲是如何知道的呢?依靠的就是眼睛 <br>
函数。 <br>
一般我们在编写人工智能时都会把它作为一个比较独立的模块。因为它的主要功能就是 <br>
判断。它的出口同游戏者的鼠标操作一样,发布命令。如果它的入口也很明确和简单则这 <br>
部分内容的难度就非常小了。可是人工智能需要得到大量的信息作为判断的依据,而我们 <br>
的主体数据结构又可能写得不很好,这时候就需要我们另外制作一套程序把游戏内核与人 <br>
工智能分开。这就是眼睛函数的用处。 <br>
编写人工智能的程序员只需要写出他想知道什么,然后交给游戏核心程序员,由游戏核 <br>
心程序员从游戏核心数据中找到相应的信息,传递给人工智能。这样编写人工智能的程序 <br>
员就可以根本不知道游戏核心数据是什么样子就可以开始工作了。但是这也是临时的办法 <br>
, <br>
如果我们可以把核心数据结构写得风雨不透那也不必如此劳神了。 <br>
有关内容请详见CBEyes.cpp。 <br>
<br>
命令队列与网络 <br>
本以为网络编程很简单,一写下来才知道头大。虽然这部分代码不是我写的,但是我也 <br>
知道跟踪一个10兆大的Log文件是什么滋味。网络上的游戏通讯一般有两个办法:交换所 <br>
有信息和交换重要信息。第一种方法是随时把自己的所有数据都传递到另一端,另一端根 <br>
据这些数据进行显示和操作。它的优点是不需要同步,对网络状况要求低,计算也非常简 <br>
单。缺点则是数据量太大,只传递少量数据还可以接受。所以这种方法多用于RPG游戏并 <br>
多在Internet上使用。第二种方法则只传递关键的数据,计算机接到这些数据后对其进行 <br>
计算,因为计算的结果是唯一的,所以可以保证网络两端一致。这种方法适用于那些数据 <br>
量大的游戏,但它的编写和调试就比较复杂,而且对同步的要求较高。我们的<赤壁>就是 <br>
采用的这种方法(<西游记>用的是第一种方法)。 <br>
在这种网络的联接方式中,最重要的问题一个是同步,一个是结果唯一。只有同步我们 <br>
才能保证在每一轮的计算中所有计算机上的初始状况是统一的,只有结果唯一我们才能保 <br>
证在执行了一段时间后所有计算机上的状态是统一的。这样才能保证网络游戏的正确性。 <br>
<br>
除了初始化等必要得操作以外,我们把游戏中的命令作为关键信息在网络上传递。游戏 <br>
者对战场上部队的任何操作,下达的任何命令都会被传递给其它的计算机。如果游戏者没 <br>
有操作,则我们制3在每一轮循环中传递同步信号。在这里我们遇到了一个DirectPlay中 <br>
的问题,那就是信号并不是每次都会正确传递到对方那里的,尽管我们在DirectPlay中使 <br>
用的参数要求系统必须在确认对方收到后才返回,可是它返回后对方依然没有收到。关于 <br>
这个问题我没有查看有关文档,也许有更好的解决办法。所以我们自己制作了一套校验确 <br>
认的方法。 <br>
但是我们在本机上发布的命令传递到另一台计算机上时一定会有延迟,这就不能保证初 <br>
始状态的一致。所以我们采用了轮回制。任何人发布的命令并不会马上被执行,而是先存 <br>
储到一个命令队列中。在每次循环中对曾经下达的所有命令统一进行发送,侦听。当所有 <br>
计算机都收到了所有计算机本轮要执行的命令后,所有的命令开始被顺序执行。这样在任 <br>
何一台计算机上本轮中所有命令的内容和顺序都是相同的,执行时才能保证其结果的一致 <br>
。 <br>
有关内容请详见CBCtrl.cpp, CBRun.cpp。 <br>
<br>
兵种的颜色 <br>
这大概是即时战略游戏里最隐蔽的一个问题了。因为不做的人不知道,而做的人都心照 <br>
不宣。在我们的游戏里,不同的君主用不同的颜色代表,属于该君主的士兵则在它的衣服 <br>
上赋予相应的颜色。这样虽然兵种相同但是他们的颜色不同,这样就可以区分敌我了。但 <br>
是如果我为每一个兵种都制作一套图素,那么就显得有些浪费了,这会占用过多的内存( <br>
我们这里所有兵种的图素量约有10MB,如果有四种颜色就需要40MB的图素)。谁都希望找 <br>
到一个好的办法节省这些内容。 <br>
我们的方法很简单,抽色。我们在调色板里的留出固定的位置,需要改变的颜色就按照 <br>
顺序存储在这里。在显示时,我们会根据传递进来的君主号为每张图素的颜色进行判断, <br>
如果颜色在这些区域里就根据规则偏移。这样各种颜色的士兵就会显示出来,而只需要一 <br>
份图素。 <br>
这种方法的缺点就是速度慢,毕竟需要对每个点进行判断和处理。所以这部分也是我们 <br>
针对MMX优化的主要部分之一。 <br>
怎么样?奇怪么?有关内容请详见CBDraw.cpp。 <br>
<br>
遮挡 <br>
我在设计游戏开始时就考虑过单元的遮挡问题,因为这曾经是<官渡>遇到的问题之一。 <br>
游戏单元在屏幕上的位置会经常有所变动,但我们必须保证一点,即下面的物体总要把上 <br>
面的物体遮住。但是我发现这个问题实际上在<赤壁>里几乎不存在。因为在每一帧都是重 <br>
新显示一屏内的所有信息,只要我每次从上向下画图就可以了。当然这是个极为极端的例 <br>
子。我们仍然需要考虑遮挡的问题。比如,人被山挡住的问题。我们通常为了在显示函数 <br>
里显得结构化一些,都是先显示地形,再显示人物。那么当人物走到了山的上部时,就需 <br>
要山峰挡住人,可是这时侯山都已经显示完了,我们必须再次显示山。而显示山的时侯又 <br>
不能够显示整个的山,我们必须划分出显示的区域,让它可以显示又不会遮挡别的地形。 <br>
<br>
调整这部分内容可能占据了我制作显示部分的绝大部分的时间。好在最后实现了,但我 <br>
仍然在想是否有更好的办法解决这个问题。 <br>
有关内容请详见CBDraw.cpp。 </td>
</tr>
</table>
<p align="center"> </p>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -