在前一章中,我们谈到游戏的场景滚动主要包括3种类型:纵向、横向、纵横向。无论何种画面滚动方式,都需要实现主角在地图中的游历。在游历的过程中需要判断:
1) 是否遇到障碍物。
2) 是否被敌方炮弹击中。
以上两种判断都涉及到游戏中一个十分重要的概念:碰撞探测(Collision detection)。本章将在前一章的基础之上,讲解主教精灵如何在地图中漫游,如何实现碰撞探测,如何通过火炮击中敌人。并且给出简单的敌方AI模拟。总之,完成本章学习之后,读者已经可以开始编写类似于“坦克大战”等基本简单游戏了。
下图就是我们示例ZYG007的游戏画面:
游戏的架构
游戏的编程模型
在正式详述示例之前,我们首先就游戏的整体编程模型进行一个简单的概述。每一个游戏都是所谓的现实模拟系统:按照预先规定的频率,将虚拟世界的状态不断的输出到目标屏幕上(每秒多少帧本质上就是每秒重画画面多少次),实现虚拟世界的模拟展示。用户的输入、内部定时器触发的各种程序逻辑通过修改内存变量进而修改虚拟世界的状态实现虚拟世界的运动、场景变化。如下图:
如上图所示,图像引擎按照每秒30次的频率不断将内存数据所描绘的虚拟世界画到iPhone的屏幕上,这就是所谓的30帧/秒。
通常情况下由以下3类独立的程序逻辑组成了游戏程序的主要编程模型。他们的共同点就是在不断修改内存数据:
1)用户输入:玩家通过“触摸”iPhone屏幕,向游戏中的主角对象下达各种指令:向上、下、左、右移动,开炮等。这些指令直接导致游戏中的主角精灵发生状态改变。
2)AI引擎指令:由机器控制的敌方精灵、环境精灵、网络游戏中来自服务器的指令、网络游戏中敌对玩家控制敌对精灵的指令等。这些指令都是针对非玩家控制精灵的状态改变指令。
3)各类定时逻辑。前两类指令直接修改游戏精灵的状态,各种定时检查逻辑则根据各种精灵的相互位置信息判断可能触发的精灵或者环境状态改变:
i. 炮弹击中地方坦克,导致地方坦克爆炸后消失,或者是炮弹击中砖墙,导致砖墙被击碎消失,道路可以通过。再有的就是游戏提示信息更新。
ii. 定期统计一下还有剩余多少敌人,玩家还剩余多少条“命”。
iii. 物理引擎:按照受力分析来显示精灵对象之间的互相作用效果。
iv. Cocos2D-iPhone内置的各种动作,画面变更效果。
以上为游戏的主要内部架构,是我们理解游戏编程的基础。这与我们通常的面向功能的软件编程有很大的区别。与此对应的是游戏程序的调试很难按照通常的单步执行来找Bug的,通常要通过对游戏的运行日志分析来发现问题。
Cocos2d-iphone的编程模型
Cocos2d-iPhone游戏引擎也是基于上述理念设计的,我们在此就这个图像引擎做一个整体性的概述。
1) 内存数据。
CocosNode是最基础的数据单元,通过AddChild函数实现的互相联系起来的CocosNode派生类的实例组成了整个游戏的整体内存数据集合。还记得教程2中的下图么?
每一个场景就代表了当前画面虚拟的游戏世界,不同的场景通过Director对象切换完成整个游戏的各个关卡变化。每一个场景中的所有内容都是由精灵对象都是CocosNode的派生类的实例。
2) 更新引擎。
我们进一步细化之前的那个图:
那个按照预定频率不断更新画面的引擎就是Director对象,Director对象实现该引擎功能包括以下两个核心内容:
a) 如何调用mainLoop函数
从0.8.2开始,cocos2d-iphone开始支持4种形式的Director工作模式,这4重模式的核心不同点就是如何调用mainLoop函数:
l CCDirectorTypeNSTimer:通过Cocoa的NSTimer来定时调用mainLoop。因此保持了与UIKit的友好兼容性,但执行效率最慢。每秒帧数上限可设置。
l CCDirectorTypeMainLoop:这是一个通过While循环来不断调用执行mainLoop的方法,无法与UIKit整合,执行效率很高,每秒帧数上限不可以设置。
l CCDirectorTypeThreadMainLoop:与CCDirectorTypeMainLoop处理和特点都很类似,但让mainLoop运行在主线程中。
l CCDirectorTypeDisplayLink:利用iPhoneOS 3.1新特性,提供高于NSTimer的执行效率,保持与UIKit的兼容性。
无论以上那种工作模式,mainLoop被按照一定的间隔不断被调用这一基本模式是不变的。CCDirectorTypeNSTimer为默认工作模式,考虑到手持设备的电池问题,在游戏对实时性要求不是很高的情况下,建议大家维持使用默认方式,本章示例就是采用的默认方式。
Cocos2d-iPhone在0.8.2之前仅提供CCDirectorTypeNSTimer和CCDirectorTypeMainLoop方式。
b) mainLoop函数执行内容分析
mainLoop函数主要做了以下两件事:
l 触发定时逻辑
关键的调用语句:[[CCScheduler sharedScheduler] tick: dt];
对Cocos2d-iPhone源代码的分下表明,凡是通过类似以下语句来实现动作效果的定时处理逻辑,这里是整个机制的调用点:
[self schedule:@selector(KeepDoing) interval: 1/30];
Cocos2d-iPhone内置的各类动作的执行者 ActionManager就是使用该机制实现动画的。
l 展示当前场景
关键的调用语句:[runningScene_ visit];
该函数将导致,所有的CocosNode派生类实例对象的draw函数将按照父子层级关系被逐一调用,这样就实现了全部游戏画面的展示。
3) 玩家指令
就是我们在第5章中讲的“触摸”事件处理机制。
4) 定时器
都是通过每一个CocosNode的schedule方法来实现的,而该方法内置的单例sharedScheduler就是前面讲的[[Scheduler sharedScheduler] tick: dt]的调用对象。至此,我们可以看出无论读者在Cocos2d中设置多少定时回调逻辑,其实并没有增加系统整体开销。所有的定时调用逻辑,无论是系统的Action还是游戏开发的逻辑最终都是在统一的调用中实现的:Scheduler 类的tick方法。
通过以上分析,我希望读者对Cocos2d-iPhone游戏引擎的编程架构有一个清晰地整体性了解:
1) Cocos2d-iPhone提供不同的更新实现机制:NSTimer和While循环。
2) mainLoop函数确保:
a) 状态更新在屏幕绘制之前被执行。
b) 各种定时器被线序调用。
因此,读取内存数据绘制画面与更新内存数据程序之间,各种不同的基于定时器(schedule)的更新内存数据的程序之间都是线性被调用执行的,不存在内存冲突。而且在每一个定时逻辑的具体处理时间点上,完全可以按照大家都是静止的来处理,也就是说不存在同时变化的任何内存对象。对于内存对象状态的修改都是线续排队执行的。
3) 我们可以放心的使用Cocos2d-iPhone提供的各种动作和效果,他们完全可以与我们的特定逻辑程序友好共处,因为大家的共同基础都是一样的。不要直接使用NSTimer和自己的定时器等。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/110432.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...