【Cocos2d-X游戏实战开发】捕鱼达人之单例对象的设计(二)
本系列学习教程使用的是cocos2d-x-2.1.4(最新版为cocos2d-x-2.1.5)
博主发现前两个系列的学习教程被严重抄袭,在这里呼吁大家请尊重开发者的劳动成果,
转载的时候请务必注明出处:http://blog.csdn.net/yangyu20121224/article/details/11180135
经过上一篇文章对整个框架进行简单的分析了之后,我们可以开始着手开发了。这里
采用增量开发的方式,每一次项目的更新都完成其中一部分功能,同时保持其可扩展
性。功能的复杂性逐轮递增。
一、创建项目
1、首先打开cocos2d-x-2.1.4目录下的“cocos2d-win32.vc2010.sln”文件,这里根据大家使用的VS版本而定。
2、打开之后,会看到左边的解决方案中有很多的项目文件,我们会在此基础上新建项目,这样的做的好处是我们不
需要在额外的关联cocos2d的类库,避免了编译项目时发生异常。大家也可以参考我之前总结过的一篇文章《解决
Cocos2d-X新建项目运行报错的问题》。
3、右击解决方案“Solution'cocos2d-win32.vc2010'”,点击“Add”->“New Project”。
4、新建项目名为“MyFishingJoy”,并且保存在“E:\Cocos2D-X Projects”文件夹下,这里视个人习惯而定。
5、点击"OK"按钮进入下一步,去掉Box2D前面默认的勾选项,因为我们在此项目中没有用到物理引擎。
6、点击“Finish”按钮,整个项目就新建好了。
写到这里,可能有些读者会觉得我很啰嗦,一个简单新建项目的过程就写了好几个步骤。但是博主写这个教程的时
候就考虑到要针对大部分的读者,当然也包括很多刚刚接触Cocos2D-X的朋友们,也有很多都是从Java直接转过来
的,以前都没有接触过C++,但是博主认为这不并不影响我们对于Cocos2D-X游戏引擎的学习以及对游戏开发的热情和
痴迷。虽然Cocos2D-X已经把这一切都封装的非常好了,但是开发的难度还是很大的,尤其是对于刚刚入门的初学者
来说。所以博主这里只想写的更详细一点,最好能详细到每一个步骤,每一个方法,让更多的人知道,其实开发游戏
也是soeasy!(不过对于这些简单的操作步骤,只会出现一次,在今后的教程中并不会重复出现)
二、创建数据
创建好项目之后,我们首先要做的不是急着写场景、写逻辑,而是为这些场景适配数据,所以我们先完成动态数
据FishingJoyData和静态数据StaticData两个类。根据上一篇文章的分析可以知道,这两个类在整个游戏过程中只可
能出现一个实例,并且贯穿着整个游戏过程。因为,我们不妨把它们设计为单例。
单例(singleton)是软件开发过程中常用的一个设计模式。它通过控制一个全局变量,以便拥有某些独占性资
源,并达到便于管理和访问的作用,但是其缺点也很致命。它在整个程序运行周期内几乎不会被释放。同时,滥用单
例会使代码耦合性增大,不易于扩展。因此,单例模式一直备受争议。
Cocos2D-X是一个很好地运用了单例的例子。在引擎内部,有许多单例,它们都以shared为开头出现,例如:
CCDirector *pDirector = CCDirector::sharedDirector();
CCTextureCache *sharedTextureCache = CCTextureCache::sharedTextureCache();
仿照Cocos2D-X的做法,我们也同样设计这两个数据类单例,即实现以下功能:
<1> 将类的构造函数、析构函数和初始化函数都设为私有,禁止外部创建的新的实例。
<2> 统一使用shared*接口访问唯一的实例,并在该接口中使用延迟初始化,在需要时才创建实例。
<3> 实现purge函数,在必要时调用,以清理不必要的内存。
最后别忘了,在Cocos2D-X中,所有的对象类都必须继承自CCObject或其子类。
三、项目编码
1、新建StaticData
<1> 首先我们要新建一个类,右击该项目,点击“Add”->“Class”。
<2>在弹出的对话框中,选择“C++ Class”。
<3>然后在接下来的对话框中,取名为“staticData”,继承自CCObject类。
<4>点击“Finish”按钮,在目录中可以看到刚刚新建好的“StaticData.h”和“StaticData.cpp”这两个文件。
2、对StaticData编码
<1> StaticData.h头文件。定义这个类的作用主要是通过它可以很方便的读取“.plist”数据文件,根据键值来调用
外部的静态存储数据,以后更改数据也会非常的方便,比如图片的存放路径,金币的初始数量等等
#ifndef __FishingJoy__StaticData__
#define __FishingJoy__StaticData__ #include "cocos2d.h" #define STATIC_DATA_STRING(key) StaticData::sharedStaticData()->stringFromKey(key)
#define STATIC_DATA_INT(key) StaticData::sharedStaticData()->intFromKey(key)
#define STATIC_DATA_FLOAT(key) StaticData::sharedStaticData()->floatFromKey(key)
#define STATIC_DATA_BOOLEAN(key) StaticData::sharedStaticData()->booleanFromKey(key)
#define STATIC_DATA_POINT(key) StaticData::sharedStaticData()->pointFromKey(key)
#define STATIC_DATA_RECT(key) StaticData::sharedStaticData()->rectFromKey(key)
#define STATIC_DATA_SIZE(key) StaticData::sharedStaticData()->sizeFromKey(key) class StaticData : public cocos2d::CCObject
{
public:
static StaticData* sharedStaticData(); /**
* @brief 外部访问接口
*
* @param key 对应的static_data.plist中的Key
* @return 对应的Value
*/
const char* stringFromKey(std::string key);
int intFromKey(std::string key);
float floatFromKey(std::string key);
bool booleanFromKey(std::string key);
cocos2d::CCPoint pointFromKey(std::string key);
cocos2d::CCRect rectFromKey(std::string key);
cocos2d::CCSize sizeFromKey(std::string key); /**
* @brief 内存不足时调用
*/
void purge(); CC_SYNTHESIZE_READONLY(std::string, _staticDataPath, StaticDataPath); protected:
cocos2d::CCDictionary* _dictionary;
private:
StaticData();
~StaticData();
bool init();
}; #endif
<2> StaticData.cpp文件。
#include "StaticData.h" USING_NS_CC; using namespace std; //定义文件的路径
#define STATIC_DATA_PATH "static_data.plist" //定义StaticData对象
static StaticData* _sharedStaticData = NULL; //得到StaticData单例对象
StaticData* StaticData::sharedStaticData()
{
if(_sharedStaticData == NULL){
_sharedStaticData = new StaticData();
_sharedStaticData->init();
}
return _sharedStaticData;
} StaticData::StaticData()
{
_staticDataPath = STATIC_DATA_PATH;
} StaticData::~StaticData()
{
CC_SAFE_RELEASE_NULL(_dictionary);
} //初始化
bool StaticData::init()
{
_dictionary = CCDictionary::createWithContentsOfFile(_staticDataPath.c_str());
CC_SAFE_RETAIN(_dictionary);
return true;
} //根据键值得到String类型数据
const char* StaticData::stringFromKey(string key)
{
return _dictionary->valueForKey(key)->getCString();
} //根据键值得到int类型数据
int StaticData::intFromKey(string key)
{
return _dictionary->valueForKey(key)->intValue();
} //根据键值得到float类型数据
float StaticData::floatFromKey(string key)
{
return _dictionary->valueForKey(key)->floatValue();
} //根据键值得到bool类型数据
bool StaticData::booleanFromKey(string key)
{
return _dictionary->valueForKey(key)->boolValue();
} //根据键值得到point类型数据
cocos2d::CCPoint StaticData::pointFromKey(string key)
{
return CCPointFromString(_dictionary->valueForKey(key)->getCString());
} //根据键值得到rect类型数据
cocos2d::CCRect StaticData::rectFromKey(string key)
{
return CCRectFromString(_dictionary->valueForKey(key)->getCString());
} //根据键值得到size类型数据
cocos2d::CCSize StaticData::sizeFromKey(string key)
{
return CCSizeFromString(_dictionary->valueForKey(key)->getCString());
} //当内存不足时调用
void StaticData::purge()
{
CC_SAFE_RELEASE_NULL(_sharedStaticData);
}
通过上面的代码,我们就可以很轻松地获得plist的数据,并转换成我们想要的数据类型。为了更快速地写代码,
我们还定义了一系列接口将重复性的操作进行封装,同时使用宏定义,使代码更加的简洁。
在其init方法中,调用CCDictionary接口,读取“static_data.plist”文件,保存在缓存中。通过把plist读取到
CCDictionary后,我们得到是一个键为string类型、值为CCObject对象的字典。具体来说,值类型有3种:CCString、
CCArray和CCDictionary。对于简单的键值对,我们使用CCString来保存数据(包括字符串类型与数值类型),而对
于数组与字典,则采用后两种类型来保存。
我们来看看static_data.plist中的数据:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>default_gold</key>
<string>200</string>
</dict>
</plist>
可以看到,里面的数据都是以键值对的方式存储的,所以读取出来非常的方便,也易于管理。如果需要添加新的数
据的话,直接打开static_data.plist文件,以key-string的方式添加即可。
3、新建FishingJoyData
<1> 照着上面的方法,我们在新建一个FishingJoyData类。
<2>点击“Finish”按钮,在目录中可以看到刚刚新建好的这两个文件。
4、对FishingJoyData编码
<1> FishingJoyData.h头文件。该类的作用主要是对玩家的数据进行持久化存储,其实就是对CCUserDefault相关操
作的封装。
#ifndef __FishingJoy__FishingJoyData__
#define __FishingJoy__FishingJoyData__ #include "cocos2d.h" class FishingJoyData : public cocos2d::CCObject
{
public:
static FishingJoyData* sharedFishingJoyData();
CC_SYNTHESIZE(int , _gold, Gold);
CC_SYNTHESIZE(bool, _isBeginner, IsBeginner);
CC_SYNTHESIZE(float, _soundVolume, SoundVolume);
CC_SYNTHESIZE(float, _musicVolume, MusicVolume); //内存不足时调用
void purge(); //将数据从内存保存到外部文件中
void flush(); //对Gold的操作
void alterGold(int delta); //重置游戏数据为默认数值
void reset(); protected:
FishingJoyData();
~FishingJoyData();
bool init();
}; #endif
<2> FishingJoyData.cpp文件。
#include "FishingJoyData.h"
#include "StaticData.h" USING_NS_CC; //定义FishingJoyData对象
static FishingJoyData* _sharedFishingJoyData = NULL; //得到FishingJoyData单例对象
FishingJoyData* FishingJoyData::sharedFishingJoyData()
{
if(_sharedFishingJoyData==NULL){
_sharedFishingJoyData = new FishingJoyData();
_sharedFishingJoyData->init();
}
return _sharedFishingJoyData;
} //当内存不足时调用
void FishingJoyData::purge()
{
CC_SAFE_RELEASE_NULL(_sharedFishingJoyData);
} FishingJoyData::FishingJoyData()
{ } FishingJoyData::~FishingJoyData()
{
this->flush();
} //初始化
bool FishingJoyData::init()
{
//判断该玩家是否是第一次登录
_isBeginner = CCUserDefault::sharedUserDefault()->getBoolForKey("beginner",true); if(_isBeginner == true){
this->reset();
this->flush();
this->setIsBeginner(false);
}else{
_isBeginner = CCUserDefault::sharedUserDefault()->getBoolForKey("beginner");
_soundVolume = CCUserDefault::sharedUserDefault()->getFloatForKey("sound");
_musicVolume = CCUserDefault::sharedUserDefault()->getFloatForKey("music");
_gold = CCUserDefault::sharedUserDefault()->getIntegerForKey("gold");
CCUserDefault::sharedUserDefault()->purgeSharedUserDefault();
}
return true;
} //重置游戏数据
void FishingJoyData::reset()
{
int gold = STATIC_DATA_INT("default_gold");
this->setGold(gold);
this->setIsBeginner(true);
this->setMusicVolume(1);
this->setSoundVolume(1);
this->flush();
} //更新金币的数量
void FishingJoyData::alterGold(int delta)
{
this->setGold(this->getGold()+delta);
} //存储数据
void FishingJoyData::flush()
{
CCUserDefault::sharedUserDefault()->setFloatForKey("sound", this->getSoundVolume());
CCUserDefault::sharedUserDefault()->setBoolForKey("beginner", this->getIsBeginner());
CCUserDefault::sharedUserDefault()->setIntegerForKey("gold", this->getGold());
CCUserDefault::sharedUserDefault()->setFloatForKey("music", this->getMusicVolume());
CCUserDefault::sharedUserDefault()->flush();
CCUserDefault::sharedUserDefault()->purgeSharedUserDefault();
}
让我们一起来分析上面的这段代码:
<1>大多数游戏对于初学者都会有相应的新手引导,因此,除了金币数量、音乐音效和音量大小外,我们还需要一个
布尔值,用于判断玩家是否为初学者。
<2> 在CCUserDefault的get*ForKey系列方法中,有两个参数:一个是字符串类型的键值,另一个是默认参数,它表
示找不到对应键值时返回的值。
<3> 在FishingJoyData类中,每次对CCUserDefault进行操作后,都会调用purgeSharedUserDefault()将其删除。这
是因为FishingJoyData和CCUserDefault中都保存着游戏的数据。因此,只在与外部文件交互时,我们才打开
CCUserDefault,保存数据,然后删除CCUserDefault,以此避免重复占用游戏数据,而对内存造成浪费。
<4> 最后,别忘了在程序进入后台时调用flush方法,及时保存游戏数据。
关于CCUserDefault的详细讲解可以参考:http://blog.csdn.net/yangyu20121224/article/details/10857813
完成了游戏数据的封装,大家是不是觉得有些枯燥无味?尽管数据操作并不复杂,但没有看得见的图片和效果,总
是让人兴奋不起来的。但是,有了数据的铺垫,那么接下来我们制作场景的过程会更加的得心应手。
【Cocos2d-X游戏实战开发】捕鱼达人之单例对象的设计(二)的更多相关文章
- 【全面解禁!真正的Expression Blend实战开发技巧】第九章 FluidMoveBehavior完全解析之二平滑运动的滚动条
原文:[全面解禁!真正的Expression Blend实战开发技巧]第九章 FluidMoveBehavior完全解析之二平滑运动的滚动条 这一章讲解FluidMoveBehavior的另一个应用, ...
- 010-Scala单例对象、伴生对象实战详解
010-Scala单例对象.伴生对象实战详解 Scala单例对象详解 函数的最后一行是返回值 子项目 Scala伴生对象代码实战 object对象的私有成员可以直接被class伴生类访问,但是不可以被 ...
- 原生Js贪吃蛇游戏实战开发笔记
前言 本课程是通过JavaScript结合WebAPI DOM实现的一版网页游戏---贪吃蛇的开发全过程,采用面向以象的思想设计开发.通过这个小游戏的开发, 不仅可以掌握JS的语法的应用,还可以学会D ...
- 【Cocos2d-X游戏实战开发】捕鱼达人之游戏场景的创建(六)
本系列学习教程使用的是cocos2d-x-2.1.4(最新版为cocos2d-x-2.1.5) 博主发现前两个系列的学习教程被严重抄袭,在这里呼吁大家请尊重开发者的劳动成果, 转载的时候请务必注 ...
- 【Cocos2d-X游戏实战开发】捕鱼达人之开发前准备工作(一)
本系列学习教程使用的是cocos2d-x-2.1.4(最新版为cocos2d-x-2.1.5) 博主发现前两个系列的学习教程被严重抄袭,在这里呼吁大家请尊重开发者的劳动成果, 转载的时候请务必注明出处 ...
- cocos2dx游戏开发——捕鱼达人mini版学习笔记(二)——MainMenu的搭建
一.创建文件~ MainMenuScene.h MainMenuScene.cpp MainMenuLayer.h MainMenuLayer.cpp 那个场景的搭建就不多说了,那个我的打 ...
- cocos2dx游戏开发——捕鱼达人mini版学习笔记(一)——FishAchor的搭建
一.创建文件· FishAchor.h还有FishAchor.cpp. 主要就是创建每种鱼的类,方便以后的取用~,很多是重复性的操作,然后我们是mini版,暂时也就加入大概6钟鱼就好= =,然后我们现 ...
- c#实战开发:以太坊钱包对接私链 (二)
上一篇讲了 以太坊私链搭建 首先下载Ethereum Wallet 钱包 可以直接百度 下载如果直接打开它会默认连接公链 所以我们要通过命令打开 "F:\Program Files\Ethe ...
- iOS开发中常用的单例
定义:一个类的对象,无论在何时创建.无论创建多少次,创建出来的对象都是同一个对象. 使用场景:当有一些数据需要共享给别的类的时候,就可以把这些数据保存在单例对象中. 关键代码: + (instan ...
随机推荐
- centos 推荐使用epel源
centos 推荐使用epel源 张映 发表于 2011-10-13 分类目录: linux 在dell r410上面装的是centos6,64的操作系统,用的163的源,我一直都是用163的源,比较 ...
- JavaScript学习笔记(四十四) 装饰器
装饰器模式(Decorator) 在装饰器模式中,可以在运行时给一个对象动态的添加额外的功能.当和静态类打交道的时候(static classes),这可能是一个挑战.但在JavaScript中,对象 ...
- struts2_4_为Action属性注入值
Struts2为Action中的属性提供了依赖注入功能,在struts2的配置文件里,能够为Action中的属性注入值,属性必须提供setter方法. 1)employeeAction类: publi ...
- PHP - session编码和解码
<?php //session编码 session_start(); $_SESSION['name'] = 'Jack'; $_SESSION['sex'] = 'men'; $envar = ...
- java项目打jar包的两种情况
链接地址:http://jingyan.baidu.com/article/6b97984d8a6ddc1ca2b0bfa0.html 本文介绍一下java项目打jar包时的两种情况各怎么操作 方 ...
- Java进阶04 RTTI
链接地址:http://www.cnblogs.com/vamei/archive/2013/04/14/3013985.html 作者:Vamei 出处:http://www.cnblogs.com ...
- 04-Foundation-NSSet、NSDictionary、block
目录: 一.NSSet集合 二.NSDictionary字典 三.block代码块 回到顶部 一.NSSet集合 1 NSSet是一个无序的,管理对个对象的集合类,最大特点是集合中不允许出现重复对象, ...
- 05-OC多态
目录: 一.继承的缺陷 二.为什么使用继承 三.组合和聚合 四.多态 回到顶部 一.继承的缺陷 1 提高了程序的复杂度,维护性和扩展性低 2 破坏了类的封装性 回到顶部 二.为什么使用继承 1 代码复 ...
- OpenProcessToken令牌函数使用方法
>GetCurrentProcessID 得到当前进程的ID OpenProcessToken得到进程的令牌句柄LookupPrivilegeValue 查询进程的权限AdjustTokenPr ...
- 最简单的基于FFMPEG+SDL的视频播放器 ver2 (採用SDL2.0)
===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...