cocos2d x 设计模式-x中的委托模式是什么,能给个例子吗

深入了解Cocos2d-x中几个代表性的类 - 博客频道 - CSDN.NET
蒲公英的晴空
分类:Cocos2dx
本人前些日子看到一篇关于cocos2d-x的典型类的介绍的文章,感觉甚好,今日特整理转来,希望能帮助到一些初学者~
本文对Cocos2d-x引擎中最具代表性,最能体现框架结构的几个类做了简单的介绍,包括Director,Application, Renderer, EventDispatcher, Scheduler。对于这些类,也只对关系主要流程的方法做了介绍,略过了容错代码和其它细节。主要目的是让大家快速的对Cocos2d-x引擎有一个全面笼统的认识,也方便快速定位问题。
Cocos2d-x对openGL的封装。不同平台下,openGL有一些差别。
一段简单的例子
以下内容引用自。需要更具体的介绍也可参考这个链接。
#include&&whateverYouNeed.h&
&&&InitializeAWindowPlease();
&&&glClearColor&(0.0,&0.0,&0.0,&0.0);
&&&glClear&(GL_COLOR_BUFFER_BIT);
&&&glColor3f&(1.0,&1.0,&1.0);
&&&glOrtho(0.0,&1.0,&0.0,&1.0,&-1.0,&1.0);
&&&glBegin(GL_POLYGON);
&&&&&&glVertex3f&(0.25,&0.25,&0.0);
&&&&&&glVertex3f&(0.75,&0.25,&0.0);
&&&&&&glVertex3f&(0.75,&0.75,&0.0);
&&&&&&glVertex3f&(0.25,&0.75,&0.0);
&&&glEnd();
&&&glFlush();
&&&UpdateTheWindowAndCheckForEvents();
OpenGL指令语法
* OpenGL使用前缀gl加每个单词的首字母组成一个指令名称
* 一些看似无关的字母附加在指令名称中(如,glColor3f()和glVertex3f()中的3f)
OpenGL是一个状态机
OpenGL是一个状态机,它保持自身的状态,除非用户输入一条命令让它改变状态。
Application
virtual&const&char&*&getCurrentLanguage();
virtual&Platform&getTargetPlatform();
virtual&void&setAnimationInterval(double&interval);
int&run();
int&Application::run()
&&&&while(!glview-&windowShouldClose())
&&&&&&&&QueryPerformanceCounter(&nNow);
&&&&&&&&if&(nNow.QuadPart&-&nLast.QuadPart&&&_animationInterval.QuadPart)
&&&&&&&&&&&&nLast.QuadPart&=&nNow.QuadP
&&&&&&&&&&&&director-&mainLoop();&&&&&&&
&&&&&&&&&&&&glview-&pollEvents();&&&&&&&
&&&&&&&&else
&&&&&&&&&&&&Sleep(0);
&&&&return&true;
主要函数预览
&&&&void&pushMatrix(MATRIX_STACK_TYPE&type);
&&&&void&popMatrix(MATRIX_STACK_TYPE&type);
&&&&void&loadIdentityMatrix(MATRIX_STACK_TYPE&type);
&&&&void&loadMatrix(MATRIX_STACK_TYPE&type,&const&Mat4&&mat);
&&&&void&multiplyMatrix(MATRIX_STACK_TYPE&type,&const&Mat4&&mat);
&&&&Mat4&getMatrix(MATRIX_STACK_TYPE&type);
&&&&void&resetMatrixStack();
&&&&inline&double&getAnimationInterval();
&&&&inline&bool&isDisplayStats();
&&&&inline&GLView*&getOpenGLView();
&&&&inline&Projection&getProjection();
&&&&Size&getVisibleSize()&const;
&&&&Vec2&getVisibleOrigin()&const;
&&&&Vec2&convertToGL(const&Vec2&&point);
&&&&Vec2&convertToUI(const&Vec2&&point);
&&&&float&getZEye()&const;
&&&&inline&Scene*&getRunningScene();
&&&&void&runWithScene(Scene&*scene);
&&&&void&pushScene(Scene&*scene);
&&&&void&end();
&&&&void&pause();
&&&&void&resume();
&&&&void&drawScene();
&&&&Scheduler*&getScheduler()&const&{&return&_&}
&&&&void&setScheduler(Scheduler*&scheduler);
&&&&ActionManager*&getActionManager()&const&{&return&_actionM&}
&&&&void&setActionManager(ActionManager*&actionManager);
&&&&EventDispatcher*&getEventDispatcher()&const&{&return&_eventD&}
&&&&void&setEventDispatcher(EventDispatcher*&dispatcher);
&&&&Renderer*&getRenderer()&const&{&return&_&}
drawScene(): 主要绘制函数
void&Director::drawScene()
&&&&if&(!&_paused)
&&&&&&&&_scheduler-&update(_deltaTime);&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&_eventDispatcher-&dispatchEvent(_eventAfterUpdate);&&&&&
&&&&glClear(GL_COLOR_BUFFER_BIT&|&GL_DEPTH_BUFFER_BIT);&&&&&&&&&
&&&&if&(_nextScene)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&setNextScene();
&&&&pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);&&&&&&
&&&&if&(_runningScene)
&&&&&&&&_runningScene-&visit(_renderer,&Mat4::IDENTITY,&false);
&&&&&&&&_eventDispatcher-&dispatchEvent(_eventAfterVisit);
&&&&_renderer-&render();&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&_eventDispatcher-&dispatchEvent(_eventAfterDraw);
&&&&popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);&&&&&&&
&&&&if&(_openGLView)
&&&&&&&&_openGLView-&swapBuffers();&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
Node::visit() 函数
Node::visit() 的主要功能就是:
1. 调用所有孩子的visit函数
2. 调用self-&draw()函数
void&Node::visit(Renderer*&renderer,&const&Mat4&&parentTransform,&uint32_t&parentFlags)
&&&&if&(!_visible)
&&&&&&&&return;
&&&&uint32_t&flags&=&processParentFlags(parentTransform,&parentFlags);
&&&&Director*&director&=&Director::getInstance();
&&&&director-&pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
&&&&director-&loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW,&_modelViewTransform);
&&&&int&i&=&0;
&&&&if(!_children.empty())
&&&&&&&&sortAllChildren();
&&&&&&&&for(&;&i&&&_children.size();&i++&)
&&&&&&&&&&&&auto&node&=&_children.at(i);
&&&&&&&&&&&&if&(&node&&&&node-&_localZOrder&&&0&)
&&&&&&&&&&&&&&&&node-&visit(renderer,&_modelViewTransform,&flags);
&&&&&&&&&&&&else
&&&&&&&&&&&&&&&&break;
&&&&&&&&this-&draw(renderer,&_modelViewTransform,&flags);
&&&&&&&&for(auto&it=_children.cbegin()+i;&it&!=&_children.cend();&++it)
&&&&&&&&&&&&(*it)-&visit(renderer,&_modelViewTransform,&flags);
&&&&&&&&this-&draw(renderer,&_modelViewTransform,&flags);
&&&&director-&popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Node::draw()
因为Node是所有可显示对象的父类, 没有任何显示内容, 所以draw函数为空。
这里我们以Sprite::draw函数为例简单介绍下draw的作用。
void&Sprite::draw(Renderer&*renderer,&const&Mat4&&transform,&uint32_t&flags)
&&&&_insideBounds&=&(flags&&&FLAGS_TRANSFORM_DIRTY)&?&renderer-&checkVisibility(transform,&_contentSize)&:&_insideB
&&&&if(_insideBounds)
&&&&&&&&_quadCommand.init(_globalZOrder,&_texture-&getName(),&getGLProgramState(),&_blendFunc,&&_quad,&1,&transform);
&&&&&&&&renderer-&addCommand(&_quadCommand);
我们看到, Sprite::draw函数主要实现了[添加一个QuadCommand到Render中去]的功能。
再看看Label的绘制函数。
Label::draw
void&Label::draw(Renderer&*renderer,&const&Mat4&&transform,&uint32_t&flags)
&&&&_insideBounds&=&(flags&&&FLAGS_TRANSFORM_DIRTY)&?&renderer-&checkVisibility(transform,&_contentSize)&:&_insideB
&&&&if(_insideBounds)&{
&&&&&&&&_customCommand.init(_globalZOrder);
&&&&&&&&_customCommand.func&=&CC_CALLBACK_0(Label::onDraw,&this,&transform,&flags);
&&&&&&&&renderer-&addCommand(&_customCommand);
其实, 跟Sprite::draw也差不多. 关键在于这个RenderCommand怎么构造和执行的。
Renderer 渲染器
主要函数预览
void&initGLView();
void&addCommand(RenderCommand*&command);
void&addCommand(RenderCommand*&command,&int&renderQueue);
void&pushGroup(int&renderQueueID);
void&popGroup();
int&createRenderQueue();
void&render();
可见它主要由两个功能:
1. 对ReanderCommand进行排序和分类管理
2. 进行渲染:render()
渲染函数Renderer::render()
void&Renderer::render()
&&&&if&(_glViewAssigned)
&&&&&&&...
&&&&&&&&for&(auto&&renderqueue&:&_renderGroups)
&&&&&&&&&&&&renderqueue.sort();
&&&&&&&&}&
&&&&&&&&visitRenderQueue(_renderGroups[0]);
&&&&&&&&...
Renderer::visitRenderQueue
按照顺序执行所有的 RenderCommand
void&Renderer::visitRenderQueue(const&RenderQueue&&queue)
&&&&ssize_t&size&=&queue.size();
&&&&for&(ssize_t&index&=&0;&index&&&&++index)
&&&&&&&&auto&command&=&queue[index];
&&&&&&&&auto&commandType&=&command-&getType();
&&&&&&&&if(RenderCommand::Type::QUAD_COMMAND&==&commandType)
&&&&&&&&&&&&auto&cmd&=&static_cast&QuadCommand*&(command);
&&&&&&&&&&&&
&&&&&&&&&&&&if(_numQuads&+&cmd-&getQuadCount()&&&VBO_SIZE)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&drawBatchedQuads();
&&&&&&&&&&&&}
&&&&&&&&&&&&_batchedQuadCommands.push_back(cmd);
&&&&&&&&&&&&memcpy(_quads&+&_numQuads,&cmd-&getQuads(),&sizeof(V3F_C4B_T2F_Quad)&*&cmd-&getQuadCount());
&&&&&&&&&&&&convertToWorldCoordinates(_quads&+&_numQuads,&cmd-&getQuadCount(),&cmd-&getModelView());
&&&&&&&&&&&&_numQuads&+=&cmd-&getQuadCount();
&&&&&&&&else&if(RenderCommand::Type::GROUP_COMMAND&==&commandType)
&&&&&&&&&&&&flush();
&&&&&&&&&&&&int&renderQueueID&=&((GroupCommand*)&command)-&getRenderQueueID();
&&&&&&&&&&&&visitRenderQueue(_renderGroups[renderQueueID]);
&&&&&&&&else&if(RenderCommand::Type::CUSTOM_COMMAND&==&commandType)
&&&&&&&&&&&&...
&&&&&&&&...
Schelduler介绍
Schelduler是Cocos2d-x中实现延迟调用,定时调用时最重要的功能。类似于其他语言中的Timer。
他最核心的函数就是:
void&schedule(const&ccSchedulerFunc&&callback,&void&*target,&float&interval,&unsigned&int&repeat,&float&delay,&bool&paused,&const&std::string&&key);
用来启动一个定时操作: 在延迟delay时间后, 每隔repeat时间, 调用一次callback。target用来标记这个操作属于谁, 方便管理, 比如在析构的时候调用void unschedule(void *target)即可移除当前对象的所有定时操作。
Schelduler的其它大部分方法, 要么是它的衍生, 为了减少调用参数; 要么是对定时操作的控制, 比如暂停, 恢复, 移除等. 如果只对想对框架的各个模块有大概的了解, 可以不做深入。
EventDispatcher
EventDispatcher,EventListener,Event之间的关系
1. EventDispatcher: 事件分发器, 相当于所有事件的中控中心。管理着EventListener,当一个Event到来的时候决定CallBack的调用顺序。
2. Event ( EventTouch, EventKeyboard 等), 具体的事件数据。
3. EventListener ( EventListenerTouch, EventListenerKeyboard 等 ): 建立了Event到CallBack的映射关系, EventDispatcher 根据这种映射关系调用对应的 CallBack。
Event有以下几种类型:
enum&class&Type
&&&&&&&&TOUCH,
&&&&&&&&KEYBOARD,
&&&&&&&&ACCELERATION,
&&&&&&&&MOUSE,
&&&&&&&&FOCUS,
&&&&&&&&CUSTOM
Event最重要的属性就是type, 标识了它是那种类型的事件, 也决定了由哪个EventListner来处理它。
EventListener
EventListner有以下几种类型:
enum&class&Type
&&&&&&&&UNKNOWN,
&&&&&&&&TOUCH_ONE_BY_ONE,
&&&&&&&&TOUCH_ALL_AT_ONCE,
&&&&&&&&KEYBOARD,
&&&&&&&&MOUSE,
&&&&&&&&ACCELERATION,
&&&&&&&&FOCUS,
&&&&&&&&CUSTOM
除了UNKNOWN, 跟Event::Type相比,Event::Type::TOUCH会同时被两种类型的EventListener处理: TOUCH_ONE_BY_ONE和TOUCH_ALL_AT_ONCE. 这两种EventListener分别处理单点触摸事件和多点触摸事件。多说几句: 假如一个TouchEvent事件中有多个触摸点, 那么类型为 EventListener::Type::TOUCH_ONE_BY_ONE 的 EventListener 会把这个事件分解成若干个单点触摸事件来处理。而类型为
EventListener::Type::TOUCH_ALL_AT_ONCE 的 EventListener 就是来处理多点触摸的, 会一次处理它。
其它几种类型都是一一对应的, 即一种Event::Type的Event会被对应类型的EventListener接受。
存放 EventListener 的地方
在EventDispatcher中, 它把以上7种 EventListener::Type 类型的 EventListner 放到7个队列中。也就是在这样一个字段中:
std::unordered_map&EventListener::ListenerID,&EventListenerVector*&&_listenerM
* EventListener::ListenerID : 每一种EventListener::Type有唯一的 EventListener::ListenerID. 其实通过这段代码 typedef std::string ListenerID; 可知: EventListener::ListenerID 就是简单 string, 就是一个名称而已。
* EventListenerVector: 顾名思义, 就是一个 EventListener 的向量容器. 相对于普通的向量容器, 它增加了priority管理功能。
EventListener的fixedPriority
简单来说, 每个 EventListener 有自己的 fixedPriority 属性, 它是一个整数。
EventListener的遍历顺序
EventDispatcher 在抛发事件的时候, 会先处理 Event 的时候, 会优先遍历 fixedPriority 低的 EventListener, 调用它的 CallBack。在某些条件下, 一个 Event 被一个 EventListener 处理之后, 会停止遍历其它的 EventListener。反映到实战中就是: 你监听了某种事件, 这种事件也出发了, 但是对应的回调函数并没有被调用, 也就是被优先级更高的 EventListener 截获了。
如果 fixedPriority 一样呢? 按照什么顺序?
1. fixedPriority 为0, 这个值是专门为 Scene Object 预留的。即, 默认情况下, 绝大多数继承自 Node 的对象添加的普通事件监听器, 其 fixedPriority 都为0。此时, Node 的 globalZOrder决定了优先级, 值越大, 越先被遍历到, 即在显示层中层级越高, 越先接受事件。 这在UI响应逻辑中也是合理的。
2. fixedPriority 不为0, 那就按添加顺序。
Event在什么条件下会被优先级更高的EventListener截获?
对于 EventListenerTouchOneByOne, 它有一个字段: _needSwallow, 当它为 true 的时候, 如果它接受了某个 Event, 优先级更低的 EventListener 就接受不到了. 可以用 EventListenerTouchOneByOne::setSwallowTouches(bool needSwallow) 来改变它。
对于其它类型的 EventLIstener, 只有在显示调用了 Event::stopPropagation()的时候, 才会中断遍历。
核心函数: EventDispatcher::dispatchEvent()
下面我们看看EventDispatcher最核心的函数:&
void EventDispatcher::dispatchEvent(Event* event): 当有响应的事件到来的时候, 都会调用这个函数来通知监听了此事件的对象。
其实, 上面的介绍, 已经把这个函数里绝大部分逻辑都描述了,这里做一个最后的总结。
事件抛发的简要流程如下:
1. 检查 _listenerMap 中所有的EventListnerVector, 如果哪个容器的 EventListener 优先级顺序需要更新, 则重新排序。
2. 对于类型为 Event::Type::TOUCH 的事件, 则按照EventListener的遍历顺序遍历所有的 EventListener. 只有接受了 EventTouch::EventCode::BEGAN 事件的 EventListener, 才会收到其他类型的 EventTouch 事件。
3. 对于其他类型的事件, 也按照EventListener的遍历顺序的顺序遍历对应的EventListener。
Eventdispatcher 中的其它函数, 主要功能都是 添加EventListener, 删除EventListener等, 不做详细介绍。
总的来说, Eventdispatcher 是一个中转器:
1. 事件的产生模块儿, 只关心自己构造正确的 Event, 调用 EventDispatcher::dispatchEvent(Event* event) 交给 EventDispatcher。
2. 需要监听事件的模块儿, 只需调用 EventDispatcher::addEventListener(EventListener* listener) (或者它的其它变种)来注册自己作为监听者。
3.&而 EventDispatcher 的作用是:&
& &1)把特定类型的 Event 送给对应类型的 EventListener。
& &2)对于同一种 Event, 规定了事件送达的优先级。
来源网址:
排名:千里之外
(14)(2)(1)(2)(2)(0)(0)(1)(9)(0)由Cocos2d-x工程入口窥见代理模式
关于设计模式(Design Pattern),自从&四人帮&第一次在《Design Patterns: Elements of Reusable Object-Oriented Software》中将其上升到理论高度,发展到今天已经成为众所周知的代码设计经验的总结。然而,关于设计模式的具体使用,大多数人却望而生畏,具体原因在于:书上提及的理论往往过于晦涩,读者只见其结果,却不明白这样设计的动机与过程;即,缺乏大型项目实践的支撑,或者说,没有经历一个数十万行项目的迭代、开发、重构,确实难以理解设计模式的智慧。
  自然,笔者也不敢说有多懂设计模式,只是从一些开源的项目中看见些许设计模式的影子,本文打算不做过多的理论讲解,而是直接从Cocos2d-x工程入口的代码部分尝试与大家分享其中体现的代理模式。注:笔者使用的Cocos2d-x版本为2.2.6,不能保证3.X版本一样适用。
  先建立一个最简单的Hello World项目(具体过程不做阐述,网上可以查看教程),我们找到main函数,代码如下:
1 int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
lpCmdLine,
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// create the application instance
CCEGLView* eglView = CCEGLView::sharedOpenGLView();
eglView-&setViewName(&Hello World&);
eglView-&setFrameSize(480, 320);
return CCApplication::sharedApplication()-&run();
  从 AppD 我们看出这是一个代理模式,查看其定义我们看到 class AppDelegate : private cocos2d::CCApplication ,即继承自CCApplication类。我们先放在一边,继续往下看。
  显然,想要从main函数跳到工程的入口是从 CCApplication::sharedApplication()-&run() 这句代码实现的。其中sharedApplication()是一个单例模式,其内部有一个静态指针,指针为空则创建对象,不为空则跳过,如此设定以保证多次调用仍然只返回唯一一个单例。当然本文不是讲解单例模式,简单提及一下。下面我们转入CCApplication的定义,找到如下代码:
// Initialize instance and cocos2d.
if (!applicationDidFinishLaunching())
  显然,这里便是整个游戏工程的入口。我们考虑,该函数在何处定义?如果 applicationDidFinishLaunching() 是CCApplication类中的成员函数,我们便可以直接调用而无需顾虑。而事实是这样吗?我们转入其定义。看到 bool AppDelegate::applicationDidFinishLaunching() 这样的代码。即,真正的实现是在AppDelegate中完成的。然而,我们发现,在CCApplication类中既无定义,也无声明,那为什么可以使用?我们看CCApplication类,看到 class CC_DLL CCApplication : public CCApplicationProtocol 这句话,即它是继承子CCApplicationProtocol。再次跳转到该函数的定义,我们看到 virtual bool applicationDidFinishLaunching() = 0; ,这是一个纯虚函数。何为纯虚函数?纯虚函数是一种特殊的虚函数,在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。在派生类中,若未对该接口进行复写(OverRide),该派生类依然为纯虚基类。显然,在CCApplication类中并没有进行复写,却可以直接调用该接口。
  回到最上面,我们知道AppDelegate继承自CCApplication类,在AppDelegate中给出了该函数的定义 virtual bool applicationDidFinishLaunching(); ,这不是一个纯虚函数,即,可以对其进行实现。
  回顾一下逻辑,整理如下:
8 CCApplicationProtocol //Interface
virtual bool applicationDidFinishLaunching() = 0; //定义一个纯虚函数的接口
11 //各个平台不同的逻辑
12 CCApplication: public CCApplicationProtocol
applicationDidFinishLaunching(); //调用该接口
18 AppDelegate: private CCApplication
applicationDidFinishLaunching() //实现接口
真正的入口;
virtual bool applicationDidFinishLaunching();
virtual void applicationDidEnterBackground();
virtual void applicationWillEnterForeground();
  关于代理模式的优点:
职责清晰,真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了的作用和保护了目标对象的作用。
  好了,本文到此就要结束了,通过一个具体的工程案例,希望大家对代理模式能学到一些新的内容。关于理论部分就不多做阐述,大家可以去看看《设计模式》这本书。
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'cocos2d-x 委托模式的巧妙运用——附源码(一)_cocos2dx吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:8,167贴子:
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 {
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();
// output: A: doing f()
// output: A: doing g()
// output: B: doing f()
// 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 {
* virtual void onGameStart() = 0;
* virtual void onGamePlaying() = 0;
* virtual void onGameEnd() = 0;
class StatusLayer : public Layer ,public StatusDelegate{
StatusLayer(void);
~StatusLayer(void);
virtual bool init();
CREATE_FUNC(StatusLayer);
//实现父类StatusDelegate的三个虚函数
void onGameStart();//游戏开始逻辑判断函数
void onGamePlaying();//游戏运行逻辑判断函数
void onGameEnd();//游戏结束逻辑判断函数
void moveFinished(Ref* pSender);//title精灵移动结束后回调此函数
void showRestartMenu(Ref* pSender);//显示重新开始按钮
void showOverSprite();//显示GameOver精灵函数
void menuRestartCallback(cocos2d::Ref* pSender);//点击开始按钮后回调此函数
void menuShareCallback(cocos2d::Ref* pSender);//分享按钮回调按钮
Size visibleS
Sprite* gameOverS
代码中不做太多解释,在该注释的地方都有详细注释。
下面来看委托类StatusLayer的具体实现。
Statuslayer.cpp
* StatusLayer.cpp
Created on: 日
Author: tubuntu
#include &StatusLayer.h&
#include &GameScene.h&
USING_NS_CC;
StatusLayer::StatusLayer() {
StatusLayer::~StatusLayer() {
bool StatusLayer::init() {
if(!Layer::init()) {
//获取屏幕大小和原点坐标
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);
* title移动结束后调用此函数
void StatusLayer::moveFinished(Ref* pSender) {
* 委托类的方法,此方法会在GameLayer中被调用,用于游戏的开始
void StatusLayer::onGameStart(){
this-&getChildByTag(SPRITE_TITLE_TAG)-&runAction(FadeOut::create(0.4f));
* 委托类的方法,此方法会在GameLayer中被调用,用于游戏的运行中的逻辑实现
void StatusLayer::onGamePlaying(){
* 委托类的方法,此方法会在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){
上面的函数都做了详细的注释,后面会给出具体的源码。
委托类中的实现主要是实现了父类StatusDelegate类中的三个虚函数onGameStart(),onGamePlaying(),onGameEnd(),这三个函数不必在GameLayer中实现,可以在GameLayer中调用委托类中的这三个函数,来实现相应的逻辑判断,也起到了GameLayer层和StatusLayer层通信的目的。
好了,这篇介绍了具体的委托类的实现,下一篇介绍这个委托类的具体使用。
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或

我要回帖

更多关于 cocos2d js 单例模式 的文章

 

随机推荐