高中英语有什么语法:[转]游戏开发新手入门之游戏的结构

来源:百度文库 编辑:偶看新闻 时间:2024/04/26 16:09:02
简介

  上一章的结尾,我曾经承诺将在本章教你写一个小的游戏引擎。但在结束上一章,然后我在考虑怎样更好的完成这个系列教程时,我感到这并不是一个好主意。让我告诉你我的真实想法。

  有几次,我曾经间接或直接的提到你不能象在DOS模式下一样编写你的Windows程序,你必须组织好每一件事。因为全部的主循环在每一帧至少要被执行一次,所以你千万不能漏掉对Windows发给你的每一个请求的跟踪。

  现在,你可能对我以前给你的Demo代码了解了一些,但你可能还没有把它扩充为一个大个儿的游戏程序,或者你还没有开始根据本教程系列编写自己的游戏,太好了!?现在,在我教你具体怎样编程前,我得出了一个结论,就是——现在,我们最后拿出一点儿时间来学习一下游戏的结构,看看一个游戏最终是怎样由各个部分组成的。

  本章不同于以往,你将很少看到程序代码,所以,哈哈,如果你对前几章关于DirectDraw的部分还不是很明白,或者还有一些其它的事不是很清楚,不要紧,因为我们要先暂停一下前面的部分,开始一个新的话题。你只需要了解一些Windows编程的知识,所以看过或了解第一、二章的内容就可以了。

  还有一件事情我要说一下:对于本章以及后面的一些章节,我都将以Terran(地球人)这个小游戏为蓝本讲解,所以你最好下载这个Demo来更好的理解我所要讲的。目前的版本一,可能不能满足所有的显示卡,版本二出来后可能会好些(已经完成)。废话不说了,让我们开始吧

  概览

  在开始编程前,你应该对你的游戏逻辑和具体操作方式有一个详细的方案。这将保证你在实施代码编辑时,不会出大的过错。否则,随着代码的编写,你本来清晰的思路会被意料外的问题和更新的创意搞得乱七八糟,最后使你自己都迷失在自己的代码中了。相信我,我是有惨痛教训的。

  你应该从WinMain()函数着手,它是程序开始的地方。这一点你可能认为理所应当,但是很多人从编写退出程序,或者从编写冲突检测函数,或者一些其它的细节处入手,而不是从WinMain()处开始,这是一种倒序的编程方法。对于初学者,理想的顺序还是从头到尾的正序方式。你首先应该从主函数开始,然后在主函数里调用每一个子函数,以及处理一些其它的事情。这样做,至少有两个理由:

  第一, 每一件事情都可以立竿见影。如果你从细节函数开始编写,你很难检测和实时演示这些函数的功能,因为你的主环境还没有构造好(编写好)。另一方面,如果你按照从头到尾的正序编写程序,你可以随时随地检测你编写的结果。先有主干,再一步一步的添枝加叶,完善程序,你的游戏就会从开始走向完成。

  第二, 有步骤的完善你的游戏。如果你先从细节函数着手,很有可能在稍后你有了新的想法或又想添加一点儿功能,于是,你又写了另一个细节函数,最终,乱糟糟的一堆函数,你都分不清应该先调用哪个,后调用哪个。所以,最好的办法是,从简单到复杂一步一步的添加,条理清晰。我想你已经理解我的意思了,就不多说了。

  我在Terran中所做的是先把主程序分成五个部分,然后每一个部分按照从头到尾的方式设计。其中四个是实际的游戏内容:卷轴引擎,脚本引擎,战斗引擎和菜单系统。第五部分就是利用Windows和DirectX使它们按照我的想法运转起来,这包括初始化、退出和从全屏模式变为Windows模式。游戏的四个部分的每一个部分都由多种函数完成,我将稍后详细介绍它们,所以你可以清楚的看到每一部分是怎样有机的组合的。希望真正的给你启迪,并且我将尽量说得清楚详细,使你更好的理解。

  实例:Terran

  从Initialization(初始化)开始,也就是WinMain()中首先要调用的部分。当然,现在表中的描述很简单,实际上有好多函数会在初始化阶段被调用。我没有详细的列出所有的函数,原因是那样做后,你将根本无法看明白。^_^ 你当然已经很熟悉这些DirectX和Windows的函数——它们建立窗口,还要用到DirectX的接口。还有很多接口我们还没有学习到,但是这对于本章并不重要,我们将随用随学。这个Load Game Data(读取游戏数据)部分将从外部文件读取所有的有用信息,包括:游戏中可利用的分类数据,脚本引擎要操作的数据,图形系统数据,等等。

  初始化部分的最后一件事情,调用一个游戏真正开始的脚本。Terran中,几乎每一部分都是由脚本引擎操控的。我将稍后详细介绍它。我将会在本系列教程安排一章来专心介绍怎样为你的游戏创建一个基础的,十分有用的脚本引擎。

  初始化完成后,程序进入主循环(Main Loop)。主循环将在你玩儿完游戏或你退出游戏后停止。在表中,你可以看到每一次循环都经历4个基本步骤。

  第一步,启动输入设备驱动程序。在Terran中,有两种输入设备(也就是操纵游戏的工具),键盘和游戏棒。它们都具有同样的两种功能,一个是检测当前的移动状态,一个是检测是否有即时的按键按下。第一种是持续的按键,例如人物在地图上的移动,第二种检测是否有突发的事件,例如进入了菜单选项。

  第二步,主循环装载音乐平台。基本上,载入音乐平台所做的就是在游戏进程没有结束前,播放音乐,然后根据不同的场景,选择相应的音乐。不用担心具体的细节——我们将会对音乐部分有一个专题讲解。

  第三步是最重要的一步:主循环在此被分成了五个部分,地图部分、菜单系统、战斗系统、脚本和结束部分。字面上你可以大概的了解每一部分的功能。在图标中你可以看出,只有结束部分(Shutdown)是单独存在的,没有其它的子系统,一旦它被调用,ok,一切都将结束。其它的四个部分还需要费一点儿口舌。

  这四个部分都有自己的子部分,在图上你一眼就能看出来。每一部分【例如:WorldMapMain()】都先完成该部分的通用功能,然后就进入分支子部分,完成具体的功能。有些时候,这些子功能通过if或switch就可以实现,但有些时候必须把子功能做成相应的函数来调用。
  地图部分(World Map):地图部分很简单。首先,它被相应的脚本调用;然后,基于子功能,或者相应当前的用户输入,或者忽略;最后,就是屏幕上的人物更新,地图被显示,再根据用户的输入,地图不断的得到更新。就这些!

  菜单系统(Menu System):这部分有一点儿复杂,因为它要掌控每一件事情,包括加载游戏、创建角色,设置硬件等等。主函数根据菜单的不同功能,把菜单设置成不同的颜色,然后子菜单根据需要再有自己的子菜单。菜单通常都通过指针数组来实现。游戏中的子系统通常都是一个整数标识,从而方便数组的索引。就像下面这样: (*lpfnMenuSubfunctions[substate.current])();

  如果你对上面这一行感到困惑,那么你有必要复习一下C语言的指针函数部分,此时,结合实际的情况,你可能对指针的理解更深刻一些。每一个子菜单都操控着一个细节。当玩家选择不同的菜单条,就要有程序实现其相对应的功能,并保证在当前的帧得到显示。当然,如果有必要显示的话。做到这些,主菜单函数需要运行当前的脚本,申请(调用)相应的动作,从而显示相应的画面。如显示当前的金块儿数量,或者是游戏角色的状态等等。

  战斗系统(Battle System):战斗系统和菜单系统紧密的联合在一起。战斗系统是一个半自动系统(semi-active),就像《最终幻想》系列(如果你还不知道《最终幻想》这个游戏,我、我、我倒),任何时候都是弹出菜单系统,来半控制角色进行打斗(因为具体的打斗动作,是由脚本控制的),而战斗系统也同时控制菜单系统,因此,战斗系统和菜单系统相辅相成。战斗的逻辑如下:

  首先,判断所有的游戏角色是否通敌人的距离足够近。如果是敌人主动靠近游戏角色,则敌人的AI(人工智能)接管,并为敌人做出一个选择;如果是游戏角色主动靠近敌人,并且当前的菜单系统不是战斗模式,那么,战斗系统菜单启动,然后由玩家进一步操控游戏角色。如果此时菜单系统被另一个游戏角色操控,那么把当前要调用的菜单放入队列,等待调用。一旦前一个菜单调用完成,计算机(也就是你的程序)要检查该队列,如果不为空,就启动相应的菜单。

  以上的任何一种情况,即敌人走近游戏角色或游戏角色走近敌人,一旦成立,则进入下一个环节——调用动作队列。每一次战斗逻辑(可能来自战斗系统调用,也可能来自菜单系统的调用)需要判断两件事:如果有战斗动作正在进行中,则由脚本控制完成;如果没有战斗动作发生,则检测战斗动作队列,如果队列中有下一个动作,就交给脚本控制完成。

  与普通敌人的战斗(Regular Fight)和与Boss(关头人物)的战斗(Boss Fight)不同之处在于,与Boss战斗时,游戏角色不能逃跑——你必须血战到底(很残酷哦!),用一个简单的if结构就完成了这个设定。第三个子菜单是胜利(Victory),它有一点儿不同,它报告战斗的成果,不用有什么逻辑判断,通常都是由脚本控制这一步。

  脚本(Scripts Only):这是游戏主要部分的最后一个部分了。它很简单:就是运行被调用的脚本,仅此而已!它通常都不被玩家所觉察,除了在游戏初始化的时候,有的游戏打出一些初始化序列的字幕。不过,无论是在游戏过程中正常出现无显示画面的时候,或者是计算机处理后台作业的时候,它始终是工作的。(无名英雄)

  游戏结束(Shutdown):顾名思义,就是结束游戏:它使程序退出主循环,释放所有在游戏中创建的DirectX的对象,释放所有的游戏占用的内存,然后结束游戏。

  主循环的最后一步是显示当前的帧,就是简单的把后缓冲区的内容拷贝到屏幕。如果是在Windows模式下,还得使用双缓冲区;如果游戏是在全屏模式下,还得需要页面切换。