cocos2d-x 委托模式的巧妙运用——附源码(一)

先来说一下委托模式是什么,下面的内容摘要自维基百科:

委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。委托模式使得我们可以用聚合来替代继承。

简单的Java例子

在此例中,类模拟打印机Printer拥有针式打印机RealPrinter的实例,Printer拥有的方法print()将处理转交给RealPrinter的print()方法。

 class RealPrinter { // the "delegate"
     void print() {
       System.out.print("something");
     }
 }

 class Printer { // the "delegator"
     RealPrinter p = new RealPrinter(); // create the delegate
     void print() {
       p.print(); // delegation
     }
 }

 public class Main {
     // to the outside world it looks like Printer actually prints.
     public static void main(String[] args) {
         Printer printer = new Printer();
         printer.print();
     }
 }

复杂的Java例子

通过使用接口,委托可以做到类型安全并且更加灵活。在这个例子中,类C可以委托类A或者类B,类C拥有方法使自己可以在类A或者类B间选择。因为类A或者类B必须实现接口I规定的方法,所以在这里委托是类型安全的。这个例子显示出委托的缺点就是需要更多的代码。

interface I {
     void f();
     void g();
 }

 class A implements I {
     public void f() { System.out.println("A: doing f()"); }
     public void g() { System.out.println("A: doing g()"); }
 }

 class B implements I {
     public void f() { System.out.println("B: doing f()"); }
     public void g() { System.out.println("B: doing g()"); }
 }

 class C implements I {
     // delegation
     I i = new A();

     public void f() { i.f(); }
     public void g() { i.g(); }

     // normal attributes
     public void toA() { i = new A(); }
     public void toB() { i = new B(); }
 }

 public class Main {
     public static void main(String[] args) {
         C c = new C();
         c.f();     // output: A: doing f()
         c.g();     // output: A: doing g()
         c.toB();
         c.f();     // output: B: doing f()
         c.g();     // output: B: doing g()
     }
 }

介绍了委托模式,根本上讲就是我是一个对象,我需要做的事我委托另一个对象来做,这样就减少了我这个对象所要做的事情,我只需把需要的东西都委托给另一个对象,它能自行处理我的需求。这样一来我这个对象所需要实现的代码就减少了,而且委托的对象可以重复利用,不光我这个对象,凡是有这个需求的都可以委托这个对象来处理同样的事,减少了开发中的重复代码。

对于cocos2d-x中的委托设计模式,在这里我不讲GUI方面的委托模式。例如Menu的响应事件,Button的响应事件,我主要将一下在游戏控制中,判断游戏开始、运行、结束时用到的委托模式,这样把这三个逻辑都交给一个委托类来实现,思路较清晰,而且可以重复利用,减少开发周期。

游戏中的主要逻辑都集中在GameLayer层中,在GameScene场景中添加所需要的Layer层,然后在各个Layer层中实现相应的逻辑。

对于大多数的开发中,我都是在一个GameLayer层中完成游戏中的所有逻辑判断,后来感觉这种方法使GameLayer层的代码太臃肿,太不清晰,自己看着都想重构一下代码。

接下来以一个小demo来介绍下cocos2d-x中的委托模式的巧妙运用,使游戏开发更清晰,更快捷。

先来看一下委托类,游戏中的开始、运行、结束的逻辑判断都在这个委托类中实现。

下面是委托类的实现代码,StatusLayer.h的具体代码

#include "cocos2d.h"
#include "GameLayer.h"

USING_NS_CC;

const int SPRITE_TITLE_TAG = 1000;
/**
 * StatusDelegate 是委托类的父类,在GameLayer中实现三个虚函数
 * 具体代码如下
 * class StatusDelegate {
 *	public:
 *		virtual void onGameStart() = 0;
 *		virtual void onGamePlaying() = 0;
 *		virtual void onGameEnd() = 0;
 *	};
 */
class StatusLayer : public Layer ,public StatusDelegate{
public:
	StatusLayer(void);
	~StatusLayer(void);

	virtual bool init();
	CREATE_FUNC(StatusLayer);

	//实现父类StatusDelegate的三个虚函数
	void onGameStart();//游戏开始逻辑判断函数
	void onGamePlaying();//游戏运行逻辑判断函数
	void onGameEnd();//游戏结束逻辑判断函数

private:

	void moveFinished(Ref* pSender);//title精灵移动结束后回调此函数
	void showRestartMenu(Ref* pSender);//显示重新开始按钮
	void showOverSprite();//显示GameOver精灵函数
	void menuRestartCallback(cocos2d::Ref* pSender);//点击开始按钮后回调此函数
	void menuShareCallback(cocos2d::Ref* pSender);//分享按钮回调按钮

private:
	Size visibleSize;
	Point origin;
	Sprite* gameOverSprite;
};

代码中不做太多解释,在该注释的地方都有详细注释。

下面来看委托类StatusLayer的具体实现。

Statuslayer.cpp

/*
 * StatusLayer.cpp
 *
 *  Created on: 2014年7月29日
 *      Author: tubuntu
 */
#include "StatusLayer.h"
#include "GameScene.h"

USING_NS_CC;

StatusLayer::StatusLayer() {
}

StatusLayer::~StatusLayer() {
}

bool StatusLayer::init() {
	if(!Layer::init()) {
		return false;
	}
	//获取屏幕大小和原点坐标
	visibleSize = Director::getInstance()->getVisibleSize();
	origin = Director::getInstance()->getVisibleOrigin();
	//添加游戏中的背景
	Sprite* background = Sprite::createWithSpriteFrameName("flappyrec_welcome_bg.png");
	background->setPosition(Point::ZERO);
	background->setAnchorPoint(Point::ZERO);
	this->addChild(background);

	//添加游戏中的标题
	Sprite* title = Sprite::createWithSpriteFrameName("flappyrec_welcome_title.png");
	title->setPosition(Vec2(0-title->getContentSize().width,visibleSize.height*4/5));
	title->setTag(SPRITE_TITLE_TAG);//设置tag值
	this->addChild(title);

	auto move = MoveTo::create(1.0f,Vec2(visibleSize.width/2-50,title->getPositionY()));
	//移动结束后回调此函数
	auto moveDone = CallFuncN::create(CC_CALLBACK_1(StatusLayer::moveFinished,this));
	//先加速后减速的动画特效
	EaseExponentialOut* sineIn = EaseExponentialOut::create(move);
	//序列动画
	auto sequence = Sequence::createWithTwoActions(sineIn,moveDone);
	title->runAction(sequence);

	return true;
}

/**
 * title移动结束后调用此函数
 */
void StatusLayer::moveFinished(Ref* pSender) {
	//TODO
}

/**
 * 委托类的方法,此方法会在GameLayer中被调用,用于游戏的开始
 */
void StatusLayer::onGameStart(){
	this->getChildByTag(SPRITE_TITLE_TAG)->runAction(FadeOut::create(0.4f));
}

/**
 * 委托类的方法,此方法会在GameLayer中被调用,用于游戏的运行中的逻辑实现
 */
void StatusLayer::onGamePlaying(){
	//TODO
}

/**
 * 委托类的方法,此方法会在GameLayer中被调用,用于游戏的结束逻辑的实现
 */
void StatusLayer::onGameEnd(){
	this->showOverSprite();

}
/**
 * gameOverSprite精灵的添加,并添加从下到上出现的动作,
 * 动作结束后调用显示按钮的函数
 */
void StatusLayer::showOverSprite() {

	gameOverSprite = Sprite::createWithSpriteFrameName("flappyrec_welcome_rec.png");
	gameOverSprite->setPosition(Vec2(visibleSize.width / 2,0-gameOverSprite->getContentSize().height));
	gameOverSprite->setScale(0.5f);
	this->addChild(gameOverSprite);

	auto move = MoveTo::create(0.8f ,Vec2(visibleSize.width/2,visibleSize.height/2 + 60));
	auto moveDone = CallFuncN::create(CC_CALLBACK_1(StatusLayer::showRestartMenu,this));
	EaseExponentialOut* sineIn = EaseExponentialOut::create(move);
	Sequence* sequence = Sequence::createWithTwoActions(sineIn,moveDone);

	gameOverSprite->runAction(sequence);
}
/**
 * 按钮显示的回调函数,显示开始和分享按钮
 * 并为按钮设置回调函数
 */
void StatusLayer::showRestartMenu(Ref* pSender) {

	Node* tmpNode = Node::create();//两个按钮的父节点

	Sprite* restartBtn = Sprite::createWithSpriteFrameName("play.png");
	Sprite* restartBtnActive = Sprite::createWithSpriteFrameName("play.png");
	restartBtn->setScale(0.6f);//缩放
	restartBtnActive->setScale(0.6f);//缩放
	restartBtnActive->setPositionY(-4);//先下移动四个单位

	auto menuRestartItem = MenuItemSprite::create(restartBtn,restartBtnActive,NULL,
			CC_CALLBACK_1(StatusLayer::menuRestartCallback,this));//设置按钮回调函数
	auto menuRestart = Menu::create(menuRestartItem,NULL);
	menuRestart->setPosition(Vec2(this->visibleSize.width/2 - 35,
			this->visibleSize.height/2 - gameOverSprite->getContentSize().height/3 + 60.0f));
	tmpNode->addChild(menuRestart);//将按钮添加到父节点中

	Sprite* shareBtn = Sprite::createWithSpriteFrameName("share.png");
	Sprite* shareBtnActive = Sprite::createWithSpriteFrameName("share.png");
	shareBtn->setScale(0.6f);
	shareBtnActive->setScale(0.6f);
	shareBtnActive->setPositionY(-4);

	auto menuShareItem = MenuItemSprite::create(shareBtn,shareBtnActive,NULL,
			CC_CALLBACK_1(StatusLayer::menuShareCallback,this));//分享按钮的会点函数
	auto menuShare = Menu::create(menuShareItem,NULL);
	menuShare->setPosition(Point(this->visibleSize.width/2 + 65,
			this->visibleSize.height/2 - gameOverSprite->getContentSize().height/3 + 60.0f));
	tmpNode->addChild(menuShare);

	this->addChild(tmpNode);//添加父节点
	auto fadeIn = FadeIn::create(0.1f);//0.1s后显示出现
	tmpNode->runAction(fadeIn);//父节点执行动作
}

/**
 * 重新开始按钮的回调函数,再次开始游戏
 */
void StatusLayer::menuRestartCallback(cocos2d::Ref* pSender){
    auto scene = GameScene::create();
    TransitionScene *transition = TransitionFade::create(1, scene);
    Director::getInstance()->replaceScene(transition);
}
/**
 * 分享按钮的回调函数
 */
void StatusLayer::menuShareCallback(cocos2d::Ref* pSender){
	//TODO
}

上面的函数都做了详细的注释,后面会给出具体的源码。

委托类中的实现主要是实现了父类StatusDelegate类中的三个虚函数onGameStart(),onGamePlaying(),onGameEnd(),这三个函数不必在GameLayer中实现,可以在GameLayer中调用委托类中的这三个函数,来实现相应的逻辑判断,也起到了GameLayer层和StatusLayer层通信的目的。

好了,这篇介绍了具体的委托类的实现,下一篇介绍这个委托类的具体使用。

cocos2d-x 委托模式的巧妙运用——附源码(一)

时间: 2024-07-29 10:22:36

cocos2d-x 委托模式的巧妙运用——附源码(一)的相关文章

cocos2d-x 委托模式的巧妙运用——附源码(二)

转载请注明出处:http://blog.csdn.net/hust_superman/article/details/38292265,谢谢. 继上一篇将了委托类的具体实现后,这篇来将一下如何在游戏中使用实现的委托类.也就是如何在游戏中来调用委托类来完成一些功能.具体的应用场景和应用层会在下面介绍. 先来看一看游戏demo实现的具体图片,demo比较简单,但是资源齐全,拿到源码后可以在源码的基础上继续完善demo做出一款真正的游戏.好了,老规矩,先上图再说: 游戏中点击播放按钮后会进入游戏主界面

Java设计模式-代理模式之动态代理(附源码分析)

Java设计模式-代理模式之动态代理(附源码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的区别就是:动态代理是在运行时刻动态的创建出代理类及其对象.上篇中的静态代理是在编译的时候就确定了代理类具体类型,如果有多个类需要代理,那么就得创建多个.还有一点,如果Subject中新增了一个方法,那么对应的实现接口的类中也要相应的实习该方法,不符合设计模式原则. 动态代理的做法:在运行时刻,可以动态创建出一个实现了多个接口的代理类.每个代理类的对象都会关联一个表示内部处理

C#版无人驾驶汽车(附源码)

一,简单问题复杂化: 100公里/1小时的速度,在日常生活中是比较常见的速度,把它转换为其它单位: 100公里/1小时 ≈ 28米/1秒 100公里/1小时 ≈ 2800厘米/秒 如果想要无人驾驶汽车达到厘米级的位移监测.探测器扫描路况时,每秒上传2800次数据给PC机.若一辆汽车有10个探测器,就意味着每秒的并发量为2.8W次/秒. 2.8W次/秒的并发量,在网站上肯定会采用分布式,缓存,读写分离,集群技术,关键还有这个数据的存储,到底用二维数据库,还是用NOSQL.这些问题是不是让你很头痛?

Android学习笔记(十四)——在运行时添加碎片(附源码)

在运行时添加碎片 点击获取源码 将UI分割为多个可配置的部分是碎片的优势之一,但其真正强大之处在于可在运行时动态地把它们添加到活动中. 1.使用上一篇创建的Fragments项目,在main.xml文件中注释掉两个<fragment>元素: 2.在FragmentActivity.java中添加下面的代码: FragmentManager fragmentManager = getSupportFragmentManager();//向活动添加碎片 FragmentTransaction fr

Android学习笔记(十五)——碎片的生命周期(附源码)

碎片的生命周期 点击下载源码 与活动类似,碎片具有自己的生命周期.理解了碎片的生命周期后,我们可以在碎片被销毁时正确地保存其实例,在碎片被重建时将其还原到前一个状态. 1.使用上一篇的项目Fragments,在Fragment1.java文件中添加如下代码: package net.zenail.Fragments; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragm

ANDROID自定义视图——仿瀑布布局(附源码)

简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3.绘制--onDraw():如何绘制这个View. 第3步的onDraw系统已经封装的很好了,基本不用我们来操心,只需要专注到1,2两个步骤就中好了. 第一步的测量,可以参考:(ANDROID自定义视图--onMeasure,MeasureSpec源码 流程 思路详解) 第二步的布局,可以参考:(AN

Android应用经典主界面框架之一:仿QQ (使用Fragment, 附源码)

最近反复研究日常经典必用的几个android app,从主界面带来的交互方式入手进行分析,我将其大致分为三类.今天记录第一种方式,即主界面下面有几个tab页,最上端是标题栏,tab页和tab页之间不是通过滑动切换的,而是通过点击切换tab页.早期这种架构一直是使用tabhost+activitygroup来使用,随着fragment的出现及google官方也大力推荐使用fragment,后者大有代替前者之势.本文也使用fragment进行搭建,标题中的"经典"指这种交互经典,非本文的代

C#版的无人驾驶汽车(附源码)

一,简单问题复杂化: 100公里/1小时的速度,在日常生活中是比较常见的速度,把它转换为其它单位: 100公里/1小时 ≈ 28米/1秒 100公里/1小时 ≈ 2800厘米/秒 如果想要无人驾驶汽车达到厘米级的位移监测.探测器扫描路况时,每秒上传2800次数据给PC机.若一辆汽车有10个探测器,就意味着每秒的并发量为2.8W次/秒. 2.8W次/秒的并发量,在网站上肯定会采用分布式,缓存,读写分离,集群技术,关键还有这个数据的存储,到底用二维数据库,还是用NOSQL.这些问题是不是让你很头痛?

java多线程核心技术梳理(附源码)

java多线程核心技术梳理(附源码) java多线程核心技术梳理附源码 写在前面 java多线程 对象及变量的并发访问 线程间通信 Lock的使用 定时器 单例模式与多线程 拾遗补增 参考资料 本文对多线程基础知识进行梳理,主要包括多线程的基本使用,对象及变量的并发访问,线程间通信,lock的使用,定时器,单例模式,以及线程状态与线程组. 写在前面 花了一周时间阅读<java多线程编程核心技术>(高洪岩 著),本文算是此书的整理归纳,书中几乎所有示例,我都亲手敲了一遍,并上传到了我的githu