游戏开发的基本概念
若要进行游戏开发,首先要理解游戏的基本原理以及元素组成。在这一节中,我们将要介绍游戏的基本概念,包括游戏的流程控制,场景,层和精灵等。
场景与流程控制
游戏的主要流程
1.进入游戏,显示游戏主菜单
2.选择新游戏,开始教学任务或者第一个关卡
3.选择载入游戏,继续以前的游戏
4.选择设置,调整游戏的听觉或者视觉效果
通常,我们把内容相对不变的游戏元素集合称做场景,而把场景之间的切换过程叫做流程控制
在Cocos2d-X中,场景的实现是CCScene.
层
层是属于场景之下的游戏元素。通常,一个复杂场景会有很多个层,一个层会显示一部分视觉元素,空白部分为透明或者半透明,以显示多个层的重叠显示。层与层之间按照顺序叠加在一起,就组成了一个复杂的场景。也许,读者接触过Photoshop, 在这些编辑器中,也存在层的概念。在游戏设计中,层的概念与它们相似。
以捕鱼游戏为例子,场景大致可以分为四层。
菜单层:悬浮于最上方的各种菜单项
触摸层:处理在屏幕上的触摸点击时间
动作层:放置鱼,×××,网等,并处理碰撞
背景层:背景图片
精灵
层和场景是其他游戏的容器,如果没有向它们添加可见的游戏元素,他们看起来就一直是透明的。精灵则与层或场景不同,它隶属于层,是场景中可出现的可见
图形。玩家控制的主角、AI控制的NPC,以及地图上的宝箱、石块,甚至游戏主菜单的背景图片都是精灵。因此,可以这样认为,玩家看到的一切都是精灵组成。
精灵不一定是静态的。通常,一个精灵可以不断变化,变化的方式包括:移动,缩放,旋转,变形,显示消失,动画效果。精灵按照层次结合起来,并与玩家互动,
构成一个完整的游戏。
节点和渲染树
回顾之前的介绍,我们已经知道了精灵、层和场景如何组成一个游戏的框架。精灵属于层,层属于场景,玩家和精灵互动,并导致游戏画面在不同的场景中切换。
把每个环节拼接在一起,我们得到了完整的关系图。
为了绘制场景,必须绘制层,为了绘制层,必须绘制精灵。因此,关系图实质上安排了图元的绘图方式,关系图中的每一个元素称为节点,关系图则称为渲染树。
渲染场景的过程就是遍历渲染树的过程。
一旦建立起渲染树,组织复杂的场景就变得很简单。我们赋予每个节点一系列属性,包括节点相对于父节点的位置、旋转角度、缩放比例、变形参数等。渲染树的优势在于,我们只需要考虑节点相对于父节点的属性,就可以逐层创建复杂的对象或者动作。
动作与动画
动作作用于游戏元素,可以使得游戏元素动起来。常见的动作有移动、闪动、消失等。动作分为瞬时动作和延时动作,持续性动作在一段时间内完成,瞬时动作在
瞬间完成。在Cocos2d-X中,动作由CCAction类实现,由CCAction类派生出来的持续性动作类CCActionInterval和CCActionInstant。所有的动作都派生自这两个类
之一。
动画animation是一种持续性动作,它只能应用于精灵上,用于实现帧动画效果。如果电影胶片一样,一个帧动画由多张静止的图片不断地切换形成。静止的图片叫做
帧(frame),帧的序列代表一个动画效果。在Cocos2d-X中,我们可以使用多个帧创建帧动画序列CCAnimation,并用帧动画序列创建可以用于精灵的帧动画CCAnimate
Cocos2d-X的代码风格
命名空间与类名称
Cocos2d-X的类都放置于Cocos2d命名空间下。以引擎目录下的"actions/CCAction.h"为例子,我们可以看到文件的前面有两个宏: NS_CC_Begin和NS_CC_END
类的命名与Cocos2d-iPhone一致,由类库缩写加上类名称组成,其中类库缩写采用大写,类名采用驼峰法。Cocos2d的缩写是CC,所以Cocos2d-X的类都拥有CC
前缀,例如表示动作的类就叫做CCAction
构造函数与初始化
在Cocos2d-X中创建对象的方法和C++开发者的习惯迥乎不同.在C++中,我们只需要调用类的构造函数就可以创建一个对象,既可以直接创建一个栈上的值对象,
也可以使用new操作符创建一个指针,指向堆上的图像。而在Cocos2d-X中,无论是创建对象的类型,还是创建对象的方法都与C++不同.
Cocos2d-X不使用传统的值类型,所有的对象都创建在堆上,然后通过指针引用。创建Cocos2d-X对象通常有两种方法:第一种是首先使用new操作符创建一个未初始化
对象,然后调用init系列方法来初始化;第二种是使用静态的工厂方法直接创建一个对象。
在Object-C中并没有构造函数,创建一个对象首先需要为对象分配内存,然后调用初始化方法来初始化对象,这个过程等价于C++中的构造函数.Cocos2d-X中的构造函数
并没有参数,创建对象所需的参数通过init开头的一系列初始化方法传递给对象。
初始化方法带有一个bool值,表示是否成功初始化对象。比如一个从文件初始化精灵的例子
CCSprite* sprite = new CCSprite();
sprite->initWithFile("HelloWorld.png");
第二种方法则是使用类自带的工厂方法来创建对象。在Cocos2d-X中,许多类会自带一系列工厂方法,这些工厂方法是类提供的静态函数。只要提供必要的参数,就会返回
一个完成了初始化的对象。通常init方法都有对应的工厂方法,它们参数一致,都可以用于创建对象。工厂的方法名称统一为create,我们仍然以创建精灵为例子,
CCSprite * sprite2 = CCSprite::spriteWithFile("HelloWorld.png"); //旧版本方法
CCSprite * sprite3 = CCSprite::create("HelloWorld.png");
然而,在内存管理方面,还是有一点差异。使用构造函数创建的对象,所有权属于创建者;而使用静态工厂方法创建的对象并不属于调用者。因此,使用构造函数创建的
对象需要调用者负责释放,而工厂方法创建的对象则不需要。
在游戏中,我们需要不断地创建新的游戏元素,通常采用的方法是从Cocos2d-X提供的游戏元素类派生出新的类,并在初始化方法中建立好我们所需的游戏元素.
比如在HelloWorld类中,我们从CCLayer派生出HelloWorld类,并重载了HelloWorld类的init方法,在这个方法中为HelloWorld类添加内容,为了保证初始化
方法可以被子类重载,需要确保初始化方法声明为虚函数。
virtual bool init();
作为参数,我们提供一个典型的init方法框架
bool init()
{
if(CCLayer::init())
{
//此处添加初始化这个类所需的代码
return true;
}
return false;
}
选择器
在Object-C中,选择器Selector是类似于C++中的类函数指针的机制。由于Cocos2d-X继承了Cocos2d-iPhone的代码风格,因此也提供了一系列类似于Object-C的
创建选择器语法的宏,用来创建函数指针。这些宏只有一个参数SELECTOR,表示被指向的类方法。将这些宏列举如下
schedule_selector( SELECTOR )
callfuncN_selector( SELECTOR )
callfuncND_selector( SELECTOR )
callfunc_selector( SELECTOR )
menu_selector( SELECTOR )
event_selector( SELECTOR )
compare_selector( SELECTOR )
下面我们来看HelloWorld例子,在这个例子中,我们在HelloWorld类的init方法中添加了一个菜单,当用户点击菜单时候,就会触发此类中的menuCloseCallback()
方法。可以看到初始化菜单的后两个参数分别是被调用对象与Cocos2d-X选择器
CCMenuItemImage *pCloseItem = CCMenuItemImage:: create(
"CloseNormal.png", "CloseSelected.png", this, menu_selector(HelloWorld::menuCloseCallback));
属性
C++的类成员只有方法与字段,没有属性和事件,这给开发者带来不便。Cocos2d-X 使用get和set方法来模拟。
在Cocos2d-X中,CCNode包含大量属性,比如给节点做标记的Tag属性,它的访问其分别是getTag()和setTag()
其实现原理大致如下:
int tag;
void getTag() { return tag; }
void setTag(int aTag) { tag = aTag; }
给每一个属性编写一个或者多个访问器的方法是一项非常枯燥的任务,为了避免重复性的工作,Cocos2d-X提供了一系列宏来帮助我们方便地创建
属性,他们定义在 "platform / CCPlatformMacros.h" 中。
CC_PROPERTY 定义了一个属性及其访问器,没有实现。通常用于简单的值类型
CC_PROPERTY_READONLY 定义了一个属性,只包含get访问器,没有实现
CC_PROPERTY_PASS_BY_REF 定义了一个属性,访问器使用引用传递参数,没有实现,通常用于结构体类型
CC_PROPERTY_READONLY_PASS_BY_REF 定义了一个属性,只包含get访问器,且使用引用类型传递参数,没有实现
CC_SYNTHESIZE 同CC_PROPERTY, 实现了访问器方法
CC_SYNTHESIZE_READONLY 同CC_PROPERTY_READONLY, 实现了访问器方法
CC_SYNTHESIZE_PASS_BY_REF 同CC_PROPERTY_PASS_BY_REF,实现了访问器方法
CC_SYNTHESIZE_READONLY_PASS_BY_REF 同CC_PROPERTY_READONLY_PASS_BY_REF,实现了访问器方法
CC_SYNTHESIZE_RETAIN 同CC_PROPERTY, 实现了访问器方法, 由于派生自CCObject类型,访问器采取Cocos2d-X的内存管理机制自动维护对象的引用计数。
这些宏只要写在类定义中就可以了。每个宏有三个参数,分别是:
@ varType 属性类型,如果属性类型是对象,需要写成指针的形式
@ varName 属性的私有字段名称
@ funName 属性的访问器名称,也就是紧接在get或者set前缀后的部分
利用Cocos2d-X提供的宏,CC_SYNTHESIZE(int, tag, Tag)
就实现了
int tag;
int getTag( ) { return tag; }
void setTag( int aTag ) { tag = aTag; }
确实很方便
单例
单例模式保证了全局有且只有一个对象,保证自动初始化该对象,使得程序在任何时候都可以访问,获取该对象。比如CCDirector:
CCDirector:: sharedDirector()->replaceScene( newScene );
这条语句利用CCDirector:: sharedDirector( ) 获取CCDirector的唯一实例,然后调用replaceScene来切换场景
内存管理
暂时跳过
工厂方法
基类中只定义创建对象的接口,将实际的实现推迟到子类。在这里,我们对它稍加推广,泛指一切生成并返回一个对象的静态函数。
CCObject * factoryMethod( )
{
CCObject *ret = new CCObject( );
// 在这里对ret对象进行必要的初始化操作
ret->autorelease( );
return ret;
}
对象传值
将一个对象赋值给某一个指针作为引用的时候,为了遵循内存管理的原则,我们需要获得新对象的引用权,释放旧对象的引用权。此时,
release() 和 retain() 的顺序就非常重要。
看下面一段代码
void SomeClass::setObject(CCObject * other)
{
this->object->release();
other->retain();
this->object = other;
}
这里存在的隐患在于,当other和other实际指向同一对象的时候,第一个release()可能会触发该对象的回收,这显然不是我们想看到的局面,
所以应该先执行retain()来保证other对象有效,然后再释放旧对象。
void SomeClass::setObject(CCObject * other)
{
other->retain();
this->object->release();
this->object = other;
}
其他可行的方法也还有很多,比如使用autorelease()方法来代替release(),或者在赋值之前判断两个对象是否相等。在Object-C编程规范中,
推荐使用autorelease()方法代替release()方法。
release( ) 还是 autorelease( ) ?
容器
CCArray & CCDictionary Object-C 风格
Cocos2d-X 引擎为我们提供了CCArray, CCDictionary 等Object-C风格的容器。
使用这些容器的一个重要原因在于Cocos2d-X的内存管理。
class CC_DLL CCAray: public CCObject
{
public:
~CCArray();
bool initWithObjects(CCObject *pObject, ...);
}
我们应该尽量使用Cocos2d-X提供的容器类.
相关辅助宏
引用计数很奇妙也很方便,但是大部分处理过程涉及到指针,难免比较繁琐,也容易出错。针对这个问题,Cocos2d-X为我们准备了一系列辅助宏来简化代码,
这些宏的头文件包含在"CCPlatformMacro.h"中。下面列出了和内存管理相关的宏。
CC_SAFE_DELETE(p) delete p
CC_SAFE_DELETE_ARRAY(p) delete [] p
CC_SAFE_FREE(p) free p
CC_SAFE_RELEASE(p) release( )方法释放
CC_SAFE_RELEASE_NULL(p) release( ) + p = null
CC_SAFE_RETAIN(p) p->retain( ) 如果p为null, 则不操作
Cocos2d-X 的内存管理原则
程序段必须成对执行retain()和release()或者执行autorelease()来声明和结束对象的引用。
工厂方法返回前,应该通过autorelease()结束该对象的引用
对象传值时候,应该考虑新旧对象相同的特殊情况
尽量使用release()而不是autorelease()来释放对象引用,以确保性能最优秀
保存CCObject子对象时候,应该严格使用Cocos2d-X提供的容器,避免使用STL容器,对象必须以指针形式传入
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。