存档

2013年1月 的存档

Cocos2d-x 2.0.4 在iPad iOS 4.3中JPG图片Alpha显示错误的解决办法

2013年1月29日 没有评论

今天安装一个iOS 4.3的iPad的时候,突然发现游戏中所有的JPG图变得非常黑,实际上就是JPG变成了半透明的。之前都是在5.1以上系统上运行,所以没发现该问题。


看了下代码,发现是CCImage对JPG格式图片设置Alpha的时候错误了。


解决办法:

找到/libs/cocos2dx/platform/ios/CCImage.mm 文件


打开后,找到这段代码:

pImageinfo->hasAlpha = (info == kCGImageAlphaPremultipliedLast)
                            || (info == kCGImageAlphaPremultipliedFirst) 
                            || (info == kCGImageAlphaLast) 
                            || (info == kCGImageAlphaFirst);

替换成下面的:

pImageinfo->hasAlpha = ((info == kCGImageAlphaNoneSkipLast) ||
                             (info == kCGImageAlphaPremultipliedLast) ||
                             (info == kCGImageAlphaPremultipliedFirst) ||
                             (info == kCGImageAlphaLast) ||
                             (info == kCGImageAlphaFirst) ? YES : NO);

在运行下,看看是不是好了。


一般使用JPG主要是为了减少容量,所以大尺寸的背景图都会用JPG格式。


但是有一点要知道加载JPG的速度比加载PNG的速度要慢,所以什么地方用JPG,要权衡权衡。

分类: 未分类 标签:

Cocos2d-x2.0 粒子系统深入分析三部曲(一)

2013年1月24日 没有评论

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ2群:44208467加群写:Cocos2d-x
红孩儿Cocos2d-X学习园地QQ群:249941957加群写:Cocos2d-x

Cocos2d-x2.0 粒子系统深入分析三部曲(一)

另:本章所用Cocos2d-x版本为:

cocos2d-2.0-x-2.0.2@ Aug 30 2012

http://cn.cocos2d-x.org/download


             大家好,今天我们来学习一下Cocos2d-x 2.0 的粒子系统,所谓粒子系统,即:“具有相同运动物理特性的一定数量级的有生命周期的个体,通过对其的控制来表现一些特定的现象,如火、爆炸、烟、水流、火花、落叶、云、雾、雪、尘、流星尾迹或者象发光轨迹这样的抽象视觉效果等等”。


Cocos2d-x2.0 粒子系统深入分析三部曲(一)


 

          在几乎所有的游戏引擎中,都有专属的类来进行生成粒子系统,其中主要有两个功能类:

(1)。粒子:具有大量属性(如生命,方向,速度,加速度等)的结构体或类。表现为一个粒子个体。

(2)。粒子发射器:用来进行生成,控制,回收粒子的管理器类。


          在进行粒子系统的创建时,首先把粒子发射器放到某个位置,然后设定要生成的粒子的数量及粒子起始属性(往往设定生命值为随机),然后在开始创建出粒子并不断的更新粒子粒子在更新运动状态的同时生命值会被不断的消耗直至死亡。死亡后的粒子被发射器记录回收,为了保证同一时间内有固定数量的粒子在存活中,发射器会在合适的时间重新初始化并运行回收的粒子

        

         回到Cocos2d-x,打开ParticleTest.h:

//演示粒子系统的场景
class ParticleTestScene : public TestScene
{
public:
    virtual void runThisTest();
};
//由纯色层派生的显示粒子系统的层,做为后面多种形式粒子系统演示所在层的基类。
class ParticleDemo : public CCLayerColor
{
protected:
	//粒子发射器
    CCParticleSystem*    m_emitter;
	//背景图精灵
    CCSprite*            m_background;

public:
	//析构
    ~ParticleDemo(void);
	//当前层加载时调用的函数。
    virtual void onEnter(void);
	//取得当前层的标题。
    virtual std::string title();
	//取得当前层的副标题。
    virtual std::string subtitle();
	//响应菜单按钮的回调函数。
	//重新启动当前演示。
    void restartCallback(CCObject* pSender);
	//启动下一个演示。
    void nextCallback(CCObject* pSender);
	//启动上一个演示。
    void backCallback(CCObject* pSender);
	//点击文字选项时切换粒子运动模式的响应函数。
    void toggleCallback(CCObject* pSender);
	//注册相应的触屏消息处理。
    virtual void registerWithTouchDispatcher();
	//触屏消息处理
	//触屏(按下时调用)
    virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
	//触屏(按下并移动时调用)
    virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
	//触屏(松开时调用)
    virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
	//更新
    virtual void update(float dt);
	//设置粒子发射器的位置。
    void setEmitterPosition();
};

//模拟火的粒子系统演示。
class DemoFirework : public ParticleDemo
{
public:
    virtual void onEnter();
    virtual std::string title();
};

后面是更多的粒子系统演示,不再一一重复列出。

 

上面定义了一些比较常见的粒子系统的演示,我们来看一下它们是怎么实现的,打开CPP文件:

//加载当前演示Layer时调用的函数。
void DemoFirework::onEnter()
{
	//先调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个Fireworks类型的粒子发射器。
m_emitter = CCParticleFireworks::create();
//对其引用计数器加一。
m_emitter->retain();
//将发射器放入背景图精灵。
    m_background->addChild(m_emitter, 10);
//设置发射器的纹理。    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
    //设置发射器的位置。
    setEmitterPosition();
}
//取得当前演示的标题。
std::string DemoFirework::title()
{
    return "ParticleFireworks";
}

后面也是一大堆相似的代码,也不一一列出了。

    重点是:所有的演示都需要用到一个粒子系统对象实例,所有的粒子系统都是由CCParticleSystem派生出来的。我们只需要好好的掌握它和学会派生出更多需要的粒子系统类就可以了。

    在学习粒子系统基类之前,我们要先了解一下CCParticleBatchNode。这个类名称中有“Batch”这个词,说明它与粒子的批次优化有一定关系。在“Cocos2d-x中图字原理之深入分析”一文中,我有讲解过CCSpriteBatchNode这个类。如果您是跟随本博一起学习Cocos2d-x的话,那么会很容易看懂CCParticleBatchNode这个类。

CCParticleBatchNode是为了将使用相同纹理的粒子并合到一个渲染批次中,可以大大的提升渲染的效率。

CCParticleBatchNode.h:

#ifndef __CCPARTICLEBATCHNODE_H__
#define __CCPARTICLEBATCHNODE_H__

#include "base_nodes/CCNode.h"
#include "CCProtocols.h"
//使用Cocos2d命名空间
NS_CC_BEGIN
//使用相应的类。
class CCTexture2D;
class CCTextureAtlas;
class CCParticleSystem;

//默认粒子系统中粒子的容器的容量。
#define kCCParticleDefaultCapacity 500
//粒子的批次结点。
class CC_DLL CCParticleBatchNode : public CCNode, public CCTextureProtocol
{
public:
	//构造函数。
CCParticleBatchNode();
//析构函数。
    virtual ~CCParticleBatchNode();

    //创建粒子系统的批次结点,参数一为对应的纹理,参数二为创建批次结点所能容纳的最大粒子数量。内部调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCParticleBatchNode* batchNodeWithTexture(CCTexture2D *tex, unsigned int capacity = kCCParticleDefaultCapacity);

    //创建粒子系统的批次结点,参数一为图片文件名称,参数二为创建批次结点所能容纳的最大粒子数量。内部调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCParticleBatchNode* batchNodeWithFile(const char* fileImage, unsigned int capacity = kCCParticleDefaultCapacity);

    //第一个创建函数的create实现。
    static CCParticleBatchNode* createWithTexture(CCTexture2D *tex, unsigned int capacity = kCCParticleDefaultCapacity);

    //第二个创建函数的create实现。
    static CCParticleBatchNode* create(const char* fileImage, unsigned int capacity = kCCParticleDefaultCapacity);

    //初始化粒子系统。
    bool initWithTexture(CCTexture2D *tex, unsigned int capacity);
    bool initWithFile(const char* fileImage, unsigned int capacity);

    //将一个粒子系统做为子结点加入批次管理结点。
    virtual void addChild(CCNode * child);
    virtual void addChild(CCNode * child, int zOrder);
    virtual void addChild(CCNode * child, int zOrder, int tag);

     //将一个粒子系统做为子结点插入批次管理结点的相应位置。
    void insertChild(CCParticleSystem* pSystem, unsigned int index);

    //将一个粒子系统子结点从批次管理结点中移除。
    virtual void removeChild(CCNode* child, bool cleanup);
	//重新排序粒子系统子结点.
    virtual void reorderChild(CCNode * child, int zOrder);
    //将一个指定索引位置的粒子系统子结点从批次管理结点中移除。
    void removeChildAtIndex(unsigned int index, bool doCleanup);
    //将所有粒子系统子结点从批次管理结点中移除。
    void removeAllChildrenWithCleanup(bool doCleanup);
    //设置某个粒子失效不显示。
    void disableParticle(unsigned int particleIndex);
	//绘制当前管理的所有粒子系统。
    virtual void draw(void);
    // 返回所用的纹理。
    virtual CCTexture2D* getTexture(void);
    // 设置所用的纹理。
    virtual void setTexture(CCTexture2D *texture);
	//设置ALPHA混合方案。
    virtual void setBlendFunc(ccBlendFunc blendFunc);
    //取得ALPHA混合方案。
    virtual ccBlendFunc getBlendFunc(void);
	//结点树遍历当前结点时调用的函数。
    void visit();
private:
	//更新所有粒子的矩形顶点缓冲信息块的索引。.
    void updateAllAtlasIndexes();
	//扩大矩形顶点缓冲信息块的数组的容量。
    void increaseAtlasCapacityTo(unsigned int quantity);
	//通过Z值取得矩形顶点缓冲信息块的索引。
    unsigned int searchNewPositionInChildrenForZ(int z);
	//取得子结点在设置Z排序中z值前的位置和设置后的位置
    void getCurrentIndex(unsigned int* oldIndex, unsigned int* newIndex, CCNode* child, int z);
	//将一个粒子系统做为子结点加入当前批次结点。
    unsigned int addChildHelper(CCParticleSystem* child, int z, int aTag);
	//更新ALPHA混合方案。
    void updateBlendFunc(void);
    // CCTextureAtlas类是用来对当前粒子的批次结点中所有粒子所使用的顶点缓冲区进行管理的类,它以数组的方式保存了显示所有粒子所需的四边形的顶点结构信息。在“Cocos2d-x中图字原理之深入分析”一文中有详细源码分析。我们可称之为“同纹理的顶点缓冲区管理器”.
    CC_SYNTHESIZE(CCTextureAtlas*, m_pTextureAtlas, TextureAtlas);
private:
    //ALPHA混合方案。
    ccBlendFunc m_tBlendFunc;
};

NS_CC_END

对应的CPP:


#include "CCParticleBatchNode.h"
#include "textures/CCTextureCache.h"
#include "textures/CCTextureAtlas.h"
#include "ccConfig.h"
#include "ccMacros.h"
#include "effects/CCGrid.h"
#include "support/CCPointExtension.h"
#include "CCParticleSystem.h"
#include "shaders/CCShaderCache.h"
#include "shaders/CCGLProgram.h"
#include "shaders/ccGLStateCache.h"
#include "support/base64.h"
#include "support/zip_support/ZipUtils.h"
#include "platform/CCFileUtils.h"
#include "kazmath/GL/matrix.h"
//Cocos2d命名空间
NS_CC_BEGIN
//构造
CCParticleBatchNode::CCParticleBatchNode()
: m_pTextureAtlas(NULL)
{

}
//析构
CCParticleBatchNode::~CCParticleBatchNode()
{
	//释放“同纹理的顶点缓冲区管理器”。
    CC_SAFE_RELEASE(m_pTextureAtlas);
}
//创建粒子系统的批次结点,参数一为对应的纹理,参数二为创建批次结点所能容纳的最大粒子数量。内部调用create实现。
CCParticleBatchNode* CCParticleBatchNode::batchNodeWithTexture(CCTexture2D *tex, unsigned int capacity/* = kCCParticleDefaultCapacity*/)
{
    return CCParticleBatchNode::createWithTexture(tex, capacity);
}
//上面的创建函数的create实现。
CCParticleBatchNode* CCParticleBatchNode::createWithTexture(CCTexture2D *tex, unsigned int capacity/* = kCCParticleDefaultCapacity*/)
{
	//先new出实例对象,然后调用初始化函数,之后交由内存管理器进行引用计数器的管理。
    CCParticleBatchNode * p = new CCParticleBatchNode();
    if( p && p->initWithTexture(tex, capacity))
    {
        p->autorelease();
        return p;
    }
	//如果失败,释放并置空。
    CC_SAFE_DELETE(p);
    return NULL;
}

//创建粒子系统的批次结点,参数一为图片文件名称,参数二为创建批次结点所能容纳的最大粒子数量。内部调用create实现。
CCParticleBatchNode* CCParticleBatchNode::batchNodeWithFile(const char* imageFile, unsigned int capacity/* = kCCParticleDefaultCapacity*/)
{
    return CCParticleBatchNode::create(imageFile, capacity);
}
//上面的创建函数的create实现。
CCParticleBatchNode* CCParticleBatchNode::create(const char* imageFile, unsigned int capacity/* = kCCParticleDefaultCapacity*/)
{
	//先new出实例对象,然后调用初始化函数,之后交由内存管理器进行引用计数器的管理。
    CCParticleBatchNode * p = new CCParticleBatchNode();
    if( p && p->initWithFile(imageFile, capacity))
    {
        p->autorelease();
        return p;
    }
	//如果失败,释放并置空。
    CC_SAFE_DELETE(p);
    return NULL;
}

//初始化函数。
bool CCParticleBatchNode::initWithTexture(CCTexture2D *tex, unsigned int capacity)
{
	//实例化一个“同纹理的顶点缓冲区管理器”。
    m_pTextureAtlas = new CCTextureAtlas();
	//使用纹理对象和容量大小初始化这个管理器。
    m_pTextureAtlas->initWithTexture(tex, capacity);

    // 创建一个CCArray,初始化容量大小,用于存放所有的粒子结点。
    m_pChildren = new CCArray();
    m_pChildren->initWithCapacity(capacity);
	// 初始化ALPHA混合方案。
    m_tBlendFunc.src = CC_BLEND_SRC;
    m_tBlendFunc.dst = CC_BLEND_DST;

    //设置所用的Shader代码片段 setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
    
    return true;
}

//与上面不同参数的初始化函数。
bool CCParticleBatchNode::initWithFile(const char* fileImage, unsigned int capacity)
{
	//先由图片名称创建出对应的纹理对象,并调用初始化函数。
    CCTexture2D *tex = CCTextureCache::sharedTextureCache()->addImage(fileImage);
    return initWithTexture(tex, capacity);
}

//系统在遍历结点时调用的函数。
void CCParticleBatchNode::visit()
{
    //如果不显示,直接返回。
    if (!m_bIsVisible)
    {
        return;
    }
	//将当前程序所用的矩阵先压栈保存。
    kmGLPushMatrix();
	//如果m_pGrid有值被激活则开启渲染到纹理(此节可参看本博“Cocos2d-x 2.0 网格动画深入分析”一文。
    if ( m_pGrid && m_pGrid->isActive())
    {
        m_pGrid->beforeDraw();
        transformAncestors();
    }
	//进行矩阵的变换。
    transform();
	//绘制。
    draw();
	//如果m_pGrid有值并被激活,则关闭渲染到纹理。这样当前结点上所有绘制的图像都被输出到m_pGrid对应的纹理中了。
    if ( m_pGrid && m_pGrid->isActive())
    {
        m_pGrid->afterDraw(this);
    }
	//将之前压栈的矩阵恢复成当前所用的矩阵。
    kmGLPopMatrix();
}

//重载基类的相应函数。
//加入一个粒子系统子结点。
void CCParticleBatchNode::addChild(CCNode * child)
{
    CCNode::addChild(child);
}
//加入一个粒子系统子结点。
void CCParticleBatchNode::addChild(CCNode * child, int zOrder)
{
    CCNode::addChild(child, zOrder);
}
//加入一个粒子系统子结点。
void CCParticleBatchNode::addChild(CCNode * child, int zOrder, int tag)
{
	//一大堆有效性判断。
    CCAssert( child != NULL, "Argument must be non-NULL");
    CCAssert( dynamic_cast<CCParticleSystem*>(child) != NULL, "CCParticleBatchNode only supports CCQuadParticleSystems as children");
    CCParticleSystem* pChild = (CCParticleSystem*)child;
    CCAssert( pChild->getTexture()->getName() == m_pTextureAtlas->getTexture()->getName(), "CCParticleSystem is not using the same texture id");
    // 如果当前粒子的批次优化结点中存储的粒子数量为0,则设置相应的ALPHA混合方案。
    if( m_pChildren->count() == 0 ) 
    {
        setBlendFunc(pChild->getBlendFunc());
    }
	//有效性判断。
    CCAssert( m_tBlendFunc.src  == pChild->getBlendFunc().src && m_tBlendFunc.dst  == pChild->getBlendFunc().dst, "Can't add a PaticleSystem that uses a differnt blending function");

    //将粒子系统做为当前批次结点的相应索引位置的子结点。
    unsigned int pos = addChildHelper(pChild,zOrder,tag);

    //定义一个临时变量存储当前批次结点中“同纹理的顶点缓冲区管理器”中对应矩形顶点缓冲块的索引。
    unsigned int atlasIndex = 0;
	//如果不是第一个。
    if (pos != 0) 
    {
		//取得对应的粒子系统。
        CCParticleSystem* p = (CCParticleSystem*)m_pChildren->objectAtIndex(pos-1);
		//取得当前粒子系统的“同纹理的顶点缓冲区管理器”中对应矩形顶点缓冲块的索引加上粒子的总数量存入atlasIndex。
        atlasIndex = p->getAtlasIndex() + p->getTotalParticles();
    }
    else
    {
	    //如果是第一个,就直接设置atlasIndex为0。
        atlasIndex = 0;
    }
	//将粒子系统与atlasIndex做为Z排序值插入相应的容器。
    insertChild(pChild, atlasIndex);

    // 设置粒子系统使用当前的批次结点。
    pChild->setBatchNode(this);
}
//将一个粒子系统做为当前批次结点的子结点。
unsigned int CCParticleBatchNode::addChildHelper(CCParticleSystem* child, int z, int aTag)
{
	//有效性判断。
    CCAssert( child != NULL, "Argument must be non-nil");
    CCAssert( child->getParent() == NULL, "child already added. It can't be added again");
	//如果存储子结点的容器指针为空,则使用new创建出对应的容器,并初始化容量为4。
    if( ! m_pChildren ) 
    {
        m_pChildren = new CCArray();
        m_pChildren->initWithCapacity(4);
    }
    //取出子结点容器中z位置在Z排序中的索引位置。
    unsigned int pos = searchNewPositionInChildrenForZ(z);
	//将子结点放入容器的相应位置。
    m_pChildren->insertObject(child, pos);
	//设置子结点的tag值。
    child->setTag(aTag);
	//设置Z排序值。
    child->_setZOrder(z);
	//设置父结点为当前批次结点。
    child->setParent(this);
	//如果在运行中,调用相应的初始化函数。
    if( m_bIsRunning ) 
    {
        child->onEnter();
        child->onEnterTransitionDidFinish();
    }
    return pos;
}

//对指定的粒子系统重新排序。
void CCParticleBatchNode::reorderChild(CCNode * child, int zOrder)
{
	//有效性判断。
    CCAssert( child != NULL, "Child must be non-NULL");
    CCAssert( dynamic_cast<CCParticleSystem*>(child) != NULL, "CCParticleBatchNode only supports CCQuadParticleSystems as children");
    CCAssert( m_pChildren->containsObject(child), "Child doesn't belong to batch" );
	//转换为粒子系统。
    CCParticleSystem* pChild = (CCParticleSystem*)(child);
	//如果本身顺序就与指定排序索引相同,直接返回。
    if( zOrder == child->getZOrder() ) 
    {
        return;
    }

    // 如果只有一个子结点,就不用排序了。
    if( m_pChildren->count() > 1)
    {
	   //定义临时变量,用于存储指定结点插入前的索引和插入后的索引。
        unsigned int newIndex = 0, oldIndex = 0;
	   //取得这两个索引值。
        getCurrentIndex(&oldIndex, &newIndex, pChild, zOrder);
	   //如果索引有改动。
        if( oldIndex != newIndex )
        {
            //子结点的引用计数器加一。
            pChild->retain();
		   //将旧的索引位置的结点从容器中删除。
            m_pChildren->removeObjectAtIndex(oldIndex);
		   //将子结点以新的索引位置放入容器中。
            m_pChildren->insertObject(pChild, newIndex);
		   //子结点的引用计数器减一。
            pChild->release();

            // 将粒子系统的“同纹理的顶点缓冲区管理器”中对应矩形顶点缓冲块的索引保存到oldAtlasIndex中。
            unsigned int oldAtlasIndex = pChild->getAtlasIndex();

            // 更新所有的索引位置。
            updateAllAtlasIndexes();

            //定义临时变量用于取出新的相应粒子系统的“同纹理的顶点缓冲区管理器”中对应矩形顶点缓冲块的索引。
            unsigned int newAtlasIndex = 0;
            for( unsigned int i=0;i < m_pChildren->count();i++)
            {
                CCParticleSystem* pNode = (CCParticleSystem*)m_pChildren->objectAtIndex(i);
                if( pNode == pChild ) 
                {
                    newAtlasIndex = pChild->getAtlasIndex();
                    break;
                }
            }

            //将oldAtlasIndex位置的数据拷到newAtlasIndex位置保持数组中信息连续有效。
            m_pTextureAtlas->moveQuadsFromIndex(oldAtlasIndex, pChild->getTotalParticles(), newAtlasIndex);
		   //更新一下粒子系统。
            pChild->updateWithNoTime();
        }
    }
    //重新设置Z排序值。
    pChild->_setZOrder(zOrder);
}
//取得粒子系统子结点在Z排序的结点数组中的位置和设置排序值z后的位置
void CCParticleBatchNode::getCurrentIndex(unsigned int* oldIndex, unsigned int* newIndex, CCNode* child, int z)
{
    bool foundCurrentIdx = false;
    bool foundNewIdx = false;

    int  minusOne = 0;
    unsigned int count = m_pChildren->count();
	//遍历所有的子结点。
    for( unsigned int i=0; i < count; i++ ) 
    {
		//取出相应的结点。
        CCNode* pNode = (CCNode *)m_pChildren->objectAtIndex(i);

        // 如果Z值大于z了,就记录索引返回给新的索引参数。
        if( pNode->getZOrder() > z &&  ! foundNewIdx ) 
        {
            *newIndex = i;
            foundNewIdx = true;
			//新索引和旧索引都找到时break。
            if( foundCurrentIdx && foundNewIdx )
            {
                break;
            }
        }

        //如果结点相同。就记录索引返回给旧的索引参数。
        if( child == pNode ) 
        {
            *oldIndex = i;
            foundCurrentIdx = true;
		   //如果未找到新的索引,则将minuseOne值由0改为-1。
            if( ! foundNewIdx )
            {
                minusOne = -1;
            }
		    //新索引和旧索引都找到时break。
            if( foundCurrentIdx && foundNewIdx )
            {	
                break;
            }
        }

    }
	//如果未找到新的索引,则将新的索引参数的值填为最大值。
    if( ! foundNewIdx )
    {
        *newIndex = count;
    }
	//
    *newIndex += minusOne;
}
//通过z值来取得结点在Z排序的结点数组中的索引。
unsigned int CCParticleBatchNode::searchNewPositionInChildrenForZ(int z)
{
	//取得子结点的数量。
    unsigned int count = m_pChildren->count();
	//遍历子结点,找到Z结点在Z排序的结点数组中大于z值的第一个结点的索引。
    for( unsigned int i=0; i < count; i++ ) 
    {
        CCNode *child = (CCNode *)m_pChildren->objectAtIndex(i);
        if (child->getZOrder() > z)
        {
            return i;
        }
    }
	//如果找不到,返回最大值。
    return count;
}

//将粒子系统子结点从批次结点中移除。
void  CCParticleBatchNode::removeChild(CCNode* child, bool cleanup)
{
    // 无效返回.
    if (child == NULL)
    {
        return;
    }
    //有效性判断.
    CCAssert( dynamic_cast<CCParticleSystem*>(child) != NULL, "CCParticleBatchNode only supports CCQuadParticleSystems as children");
    CCAssert(m_pChildren->containsObject(child), "CCParticleBatchNode doesn't contain the sprite. Can't remove it");
	//将对应的子结点指针转化为粒子系统指针.
    CCParticleSystem* pChild = (CCParticleSystem*)child;
	//移除对应的子结点.
    CCNode::removeChild(pChild, cleanup);

    // 从“同纹理的顶点缓冲区管理器”中找到相应的矩形顶点缓冲块的索引位置然后移除相应粒子数量的矩形顶点缓冲信息块.并使用memmove将后面的数据拷到删除位置保持矩形顶点缓冲信息块的数组信息连续有效。
    m_pTextureAtlas->removeQuadsAtIndex(pChild->getAtlasIndex(), pChild->getTotalParticles());

    //在“同纹理的顶点缓冲区管理器”中从参数一指定位置后相应粒子数量的矩形顶点缓冲信息块都置零。这里即将上面数组中更新的矩形顶点缓冲信息块数量之后的内存置零。
    m_pTextureAtlas->fillWithEmptyQuadsFromIndex(m_pTextureAtlas->getTotalQuads(), pChild->getTotalParticles());

    // 设置被移除的粒子系统不使用批次结点.
    pChild->setBatchNode(NULL);
	//重建“同纹理的顶点缓冲区管理器”中所有的矩形顶点缓冲信息块的索引.
    updateAllAtlasIndexes();
}
//通过索引移除相应的粒子系统。
void CCParticleBatchNode::removeChildAtIndex(unsigned int index, bool doCleanup)
{
    removeChild((CCParticleSystem *)m_pChildren->objectAtIndex(index),doCleanup);
}
//清空所有使用当前批次结点的粒子系统。
void CCParticleBatchNode::removeAllChildrenWithCleanup(bool doCleanup)
{
	//遍历所有的粒子系统调用setBatchNode函数,参数为NULL。即将所有使用当前批次结点的粒子系统都设置不使用批次结点。
    arrayMakeObjectsPerformSelectorWithObject(m_pChildren, setBatchNode, NULL, CCParticleSystem*);
	//清空所有的子结点。
    CCNode::removeAllChildrenWithCleanup(doCleanup);
	//清空“同纹理的顶点缓冲区管理器”中所有的矩形顶点缓冲信息块。
    m_pTextureAtlas->removeAllQuads();
}
//绘制批次结点。
void CCParticleBatchNode::draw(void)
{
    CC_PROFILER_STOP("CCParticleBatchNode - draw");
	//如果矩形顶点缓冲信息块的数量为0直接返回。
    if( m_pTextureAtlas->getTotalQuads() == 0 )
    {
        return;
    }
	//开始使用Shader进行绘制结点。
    CC_NODE_DRAW_SETUP();
	//设置Opengl采用相应的ALPHA混合方案。
    ccGLBlendFunc( m_tBlendFunc.src, m_tBlendFunc.dst );
	//调用drawQuads来绘制所有矩形顶点缓冲构成的图形。
    m_pTextureAtlas->drawQuads();
    CC_PROFILER_STOP("CCParticleBatchNode - draw");
}
//扩增矩形顶点缓冲信息块组数的容量。
void CCParticleBatchNode::increaseAtlasCapacityTo(unsigned int quantity)
{
	//打印日志。
    CCLOG("cocos2d: CCParticleBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].",
          (long)m_pTextureAtlas->getCapacity(),
          (long)quantity);
	//重新调整矩形顶点缓冲信息块数组的大小。
    if( ! m_pTextureAtlas->resizeCapacity(quantity) ) {
        // serious problems
        CCLOGWARN("cocos2d: WARNING: Not enough memory to resize the atlas");
        CCAssert(false,"XXX: CCParticleBatchNode #increaseAtlasCapacity SHALL handle this assert");
    }
}

//设置对应索引的粒子无效。
void CCParticleBatchNode::disableParticle(unsigned int particleIndex)
{
	//取出对应索引的矩形顶点缓冲信息块并将大小置0.
    ccV3F_C4B_T2F_Quad* quad = &((m_pTextureAtlas->getQuads())[particleIndex]);
    quad->br.vertices.x = quad->br.vertices.y = quad->tr.vertices.x = quad->tr.vertices.y = quad->tl.vertices.x = quad->tl.vertices.y = quad->bl.vertices.x = quad->bl.vertices.y = 0.0f;
}

// 将一个粒子系统按照指定的索引位置做为子结点加入批次结点。
void CCParticleBatchNode::insertChild(CCParticleSystem* pSystem, unsigned int index)
{
	//用index做为粒子系统使用的矩形顶点缓冲信息块索引。
    pSystem->setAtlasIndex(index);
	//如果“同纹理的顶点缓冲区管理器”中所有的矩形顶点缓冲信息块的数量在直接加入粒子系统的相应数量之后超过了容量,需要扩增容量。
    if(m_pTextureAtlas->getTotalQuads() + pSystem->getTotalParticles() > m_pTextureAtlas->getCapacity())
    {
        //扩增一下“同纹理的顶点缓冲区管理器”中所有的矩形顶点缓冲信息块的数组容量。
        increaseAtlasCapacityTo(m_pTextureAtlas->getTotalQuads() + pSystem->getTotalParticles());

        //将数组中新增的尾部粒子数量大小的矩形顶点缓冲信息块内存数据清零。
        m_pTextureAtlas->fillWithEmptyQuadsFromIndex(m_pTextureAtlas->getCapacity() - pSystem->getTotalParticles(), pSystem->getTotalParticles());
    }

    // 粒子系统不是最后一个结点。则需要将index位置粒子数量的矩形顶点缓冲信息移到插入新的信息之后的位置,这样对应位置的矩形顶点缓冲信息块就空出来了。
    if (pSystem->getAtlasIndex() + pSystem->getTotalParticles() != m_pTextureAtlas->getTotalQuads())
    {
        m_pTextureAtlas->moveQuadsFromIndex(index, index+pSystem->getTotalParticles());
    }

    //按照粒子系统的总的粒子数量增加矩形顶点缓冲信息块的数量记数值。
    m_pTextureAtlas->increaseTotalQuadsWith(pSystem->getTotalParticles());
	//重建所有的粒子的“同纹理的顶点缓冲区管理器”的矩形顶点缓冲信息块的索引。
    updateAllAtlasIndexes();
}

//重建所有的粒子的“同纹理的顶点缓冲区管理器”的矩形顶点缓冲信息块的索引。
void CCParticleBatchNode::updateAllAtlasIndexes()
{
	//定义临时变量用于循环取值.
    CCObject *pObj = NULL;
    unsigned int index = 0;
	//遍历所有的子结点容器,取出每个元素存入到pObj中.
    CCARRAY_FOREACH(m_pChildren,pObj)
    {
		//取出每个元素,转换成为粒子系统指针.
        CCParticleSystem* child = (CCParticleSystem*)pObj;
	    //设置对应的粒子系统所对应的“同纹理的顶点缓冲区管理器”的矩形顶点缓冲信息块的索引.
        child->setAtlasIndex(index);
	   //索引递增相应粒子数量.
        index += child->getTotalParticles();
    }
}

// 更新ALPHA混合状态方案.
void CCParticleBatchNode::updateBlendFunc(void)
{
	//如果纹理有ALPHA通道,设置其相应的ALPHA混合方案.
    if( ! m_pTextureAtlas->getTexture()->hasPremultipliedAlpha()) {
        m_tBlendFunc.src = GL_SRC_ALPHA;
        m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
    }
}
//设置粒子的批次结点所使用的纹理对象。
void CCParticleBatchNode::setTexture(CCTexture2D* texture)
{
	//设置当前“同纹理的顶点缓冲区管理器”的对应纹理图。
    m_pTextureAtlas->setTexture(texture);

    //如果纹理没有ALPHA通道并且ALPHA混合方案与指定方案相同,则重新设置相应的ALPHA混合方案。
    if( texture && ! texture->hasPremultipliedAlpha() && ( m_tBlendFunc.src == CC_BLEND_SRC && m_tBlendFunc.dst == CC_BLEND_DST ) )
    {
            m_tBlendFunc.src = GL_SRC_ALPHA;
            m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
    }
}

//取得当前粒子的批次结点所用的纹理对象指针。
CCTexture2D* CCParticleBatchNode::getTexture(void)
{
    return m_pTextureAtlas->getTexture();
}
//设置ALPHA混合方案。
void CCParticleBatchNode::setBlendFunc(ccBlendFunc blendFunc)
{
    m_tBlendFunc = blendFunc;
}
//取得ALPHA混合方案。
ccBlendFunc CCParticleBatchNode::getBlendFunc(void)
{
    return m_tBlendFunc;
}

NS_CC_END

粒子的批次结点讲完了,我们来总结一下:

         批次结点通过m_pTextureAtlas来对使用相同纹理的多个粒子系统中的粒子进行矩形顶点缓冲区的管理,这些粒子系统以子结点的形式按照Z排序存放在m_pChildren中。批次结点可以根据设置的纹理是否有ALPHA通道来设置透明度混合和高亮两种混合方案,并在渲染时使用。渲染时通过m_pTextureAtlas来进行绘制处理,一个批次内将使用相同纹理的所有粒子系统的所有粒子渲染出来。大大优化了渲染状态和纹理的切换开销,提升了渲染效率。


       但是,如何指定粒子系统放在相应的批次结点中呢?这个还需要后面我们继续分析研究。

 

        下面我们继续学习粒子系统,打开CCParicleSystem.h:

#ifndef __CCPARTICLE_SYSTEM_H__
#define __CCPARTICLE_SYSTEM_H__

#include "CCProtocols.h"
#include "base_nodes/CCNode.h"
#include "cocoa/CCDictionary.h"
#include "cocoa/CCString.h"

//使用Cocos2d命名空间
NS_CC_BEGIN

//.h中要用到CCParticleBatchNode类。

class CCParticleBatchNode;

//一些发射器的状态类型设置枚举。
enum {
    //发射器永远激活
    kCCParticleDurationInfinity = -1,

    //开始与结束时粒子数量相等
    kCCParticleStartSizeEqualToEndSize = -1,

    //开始与结束时粒子的旋转角度相等。
    kCCParticleStartRadiusEqualToEndRadius = -1,

    //上面枚举的一些别名
    kParticleStartSizeEqualToEndSize = kCCParticleStartSizeEqualToEndSize,
    kParticleDurationInfinity = kCCParticleDurationInfinity,
};

//粒子发射器的两种模式
enum {
    //重力模式 [让粒子向一个中心点移动或离开一个中心点,比如火,烟]
    kCCParticleModeGravity,

    //环型模式[让粒子沿着一个圆形旋转,比如螺旋]
    kCCParticleModeRadius,    
};


//粒子的两种运动方式
typedef enum {
    //自由方式:粒子的运动是自由的无拘无束的,移动不受任何结点的影响。
    kCCPositionTypeFree,

    //相对方式:粒子的运动是相对于父结点的坐标系。比如可以将粒子关联到一个精灵上,让粒子跟随精力一起移动。
    kCCPositionTypeRelative,

    //集群方式:这种方式粒子跟随发射器移动。
    kCCPositionTypeGrouped,
}tCCPositionType;

// 兼容之前的版本的对应枚举值。
enum {
    kPositionTypeFree = kCCPositionTypeFree,
    kPositionTypeGrouped = kCCPositionTypeGrouped,
}; 

//这里就是包含粒子信息的粒子结构体了。我们来深入的看一下它的属性。
typedef struct sCCParticle {

    CCPoint     pos;//位置
    CCPoint     startPos;//起始位置

    ccColor4F    color;//颜色
    ccColor4F    deltaColor;//颜色变化量

    float        size;//大小
    float        deltaSize;//大小变化量

    float        rotation;//旋转角度
    float        deltaRotation;//旋转角度变化量。

    float        timeToLive;//生命值。

    unsigned int    atlasIndex;//对应批次结点中的矩形顶点缓冲块的索引。

    //描述变化方式的成员结构 (重力加速度模式)
    struct {
        CCPoint      dir;				//运动方向
        float        radialAccel;		//旋转加速度
        float        tangentialAccel;	//切线加速度
    } modeA;

    //描述变化方式的成员结构(环型模式)
    struct {
        float        angle;				//起始旋转角度
        float        degreesPerSecond;// 每秒的旋转弧度
        float        radius;			// 起始旋转角度
        float        deltaRadius;	    // 每次更新时旋转角度变化量。
    } modeB;

}tCCParticle;

需要用到纹理对象,这里声明一下。

class CCTexture2D;

//粒子系统类的定义,一个粒子系统是由结点类和纹理接口类派生。
class CC_DLL CCParticleSystem : public CCNode, public CCTextureProtocol
{    
protected:
	//对应的PLIST文件
    std::string m_sPlistFile;
    //开始到当前的运行秒数
    float m_fElapsed;

    // 不同变化模式的信息结构
    //重力加速度模式
    struct {
        //重力加速度
        CCPoint gravity;
        //速度值
        float speed;
        //速度值的变化量
        float speedVar;
        //相关加速值
        float tangentialAccel;
        //相关加速值变化量
        float tangentialAccelVar;
        //旋转加速值
        float radialAccel;
        //旋转加速值变化量
        float radialAccelVar;
    } modeA;

    //环型移动模式
    struct {
        //起始角度值
        float startRadius;
        //起始角度值变化量
        float startRadiusVar;
        //结束角度值
        float endRadius;
        //结束角度值变化量
        float endRadiusVar;            
        //每秒旋转角度值
        float rotatePerSecond;
        //每秒旋转角度值变化量
        float rotatePerSecondVar;
    } modeB;

    //粒子的动态数组
    tCCParticle *m_pParticles;

    //色彩调整
    //    BOOL colorModulate;

    //每秒的粒子发射数
    float m_fEmitCounter;

    //!  particle idx
    unsigned int m_uParticleIdx;

    // 优化选项
    //CC_UPDATE_PARTICLE_IMP    updateParticleImp;
    //SEL                        updateParticleSel;

    //渲染精灵所用的粒子批次结点动态数组
    CC_PROPERTY(CCParticleBatchNode*, m_pBatchNode, BatchNode);

    // 当前系统在批次结点数组中的索引
    CC_SYNTHESIZE(unsigned int, m_uAtlasIndex, AtlasIndex);

    //是否缩放和旋转。
    bool m_bTransformSystemDirty;
    //已经创建的粒子数量
    unsigned int m_uAllocatedParticles;

    //发射器是否被激活。
    bool m_bIsActive;
    //当前已经发射的粒子数量。
    CC_PROPERTY_READONLY(unsigned int, m_uParticleCount, ParticleCount)
    //发射器将运行的时间。
    CC_PROPERTY(float, m_fDuration, Duration)
    //发射器的原位置点。
    CC_PROPERTY_PASS_BY_REF(CCPoint, m_tSourcePosition, SourcePosition)
    //当前发射器的位置变量。
    CC_PROPERTY_PASS_BY_REF(CCPoint, m_tPosVar, PosVar)
    //初始时的粒子生命值。
    CC_PROPERTY(float, m_fLife, Life)
    //粒子生命值的变化量。
    CC_PROPERTY(float, m_fLifeVar, LifeVar)
    //粒子的初始角度。
    CC_PROPERTY(float, m_fAngle, Angle)
    //粒子的角度变化量。
    CC_PROPERTY(float, m_fAngleVar, AngleVar)

//相关公共函数。
public:
    //重力加速度模式的变量值的存取函数。
    virtual const CCPoint& getGravity();
    virtual void setGravity(const CCPoint& g);
    virtual float getSpeed();
    virtual void setSpeed(float speed);
    virtual float getSpeedVar();
    virtual void setSpeedVar(float speed);
    virtual float getTangentialAccel();
    virtual void setTangentialAccel(float t);
    virtual float getTangentialAccelVar();
    virtual void setTangentialAccelVar(float t);
    virtual float getRadialAccel();
    virtual void setRadialAccel(float t);
    virtual float getRadialAccelVar();
    virtual void setRadialAccelVar(float t);
    //环型移动模式的变量值的存取函数。
    virtual float getStartRadius();
    virtual void setStartRadius(float startRadius);
    virtual float getStartRadiusVar();
    virtual void setStartRadiusVar(float startRadiusVar);
    virtual float getEndRadius();
    virtual void setEndRadius(float endRadius);
    virtual float getEndRadiusVar();
    virtual void setEndRadiusVar(float endRadiusVar);
    virtual float getRotatePerSecond();
    virtual void setRotatePerSecond(float degrees);
    virtual float getRotatePerSecondVar();
    virtual void setRotatePerSecondVar(float degrees);

	//设置缩放
    virtual void setScale(float s);
	//设置旋转
    virtual void setRotation(float newRotation);
	//设置单方向轴的缩放
    virtual void setScaleX(float newScaleX);
    virtual void setScaleY(float newScaleY);
    //是否被激活
    virtual bool isActive();
	//混合状态是否为亮模式
    virtual bool isBlendAdditive();
	//设置是否高亮模式
    virtual void setBlendAdditive(bool value);
    //粒子的开始大小的属性和存取访问接口
    CC_PROPERTY(float, m_fStartSize, StartSize)
    //粒子的开始大小的变化值属性和存取访问接口
    CC_PROPERTY(float, m_fStartSizeVar, StartSizeVar)
    //粒子的结束大小的属性和存取访问接口
    CC_PROPERTY(float, m_fEndSize, EndSize)
    //粒子的结束大小的变化值属性和存取访问接口
    CC_PROPERTY(float, m_fEndSizeVar, EndSizeVar)
    //粒子的起始颜色的属性和存取访问接口
    CC_PROPERTY_PASS_BY_REF(ccColor4F, m_tStartColor, StartColor)
    //粒子的起始颜色的变化值属性和存取访问接口
    CC_PROPERTY_PASS_BY_REF(ccColor4F, m_tStartColorVar, StartColorVar)
    //粒子的结束颜色的属性和存取访问接口
    CC_PROPERTY_PASS_BY_REF(ccColor4F, m_tEndColor, EndColor)
    //粒子的结束颜色的变化值属性和存取访问接口
    CC_PROPERTY_PASS_BY_REF(ccColor4F, m_tEndColorVar, EndColorVar)
    //粒子的初始化角度值属性和存取访问接口
    CC_PROPERTY(float, m_fStartSpin, StartSpin)
    //粒子的初始化角度变化值属性和存取访问接口
    CC_PROPERTY(float, m_fStartSpinVar, StartSpinVar)
    //粒子的结束角度值属性和存取访问接口
    CC_PROPERTY(float, m_fEndSpin, EndSpin)
    //粒子的结束角度变化值属性和存取访问接口
    CC_PROPERTY(float, m_fEndSpinVar, EndSpinVar)
    //发射器的发射速度值属性和存取访问接口
    CC_PROPERTY(float, m_fEmissionRate, EmissionRate)
    //最大粒子数量值属性和存取访问接口
    CC_PROPERTY(unsigned int, m_uTotalParticles, TotalParticles)
    //粒子所用的纹理对象实例指针和存取访问接口
    CC_PROPERTY(CCTexture2D*, m_pTexture, Texture)
    //粒子渲染所用的混合状态方案和存取访问接口
    CC_PROPERTY(ccBlendFunc, m_tBlendFunc, BlendFunc)
    //是否使用透晨度来影响修改颜色值和存取访问接口
    CC_PROPERTY(bool, m_bOpacityModifyRGB, OpacityModifyRGB)

	//混合状态是否是使用源混合状态 = GL_SRC_ALPHA,目标混合状态 = GL_ONE 的混合叠加方案
    bool m_bIsBlendAdditive;
    //粒子的移动方式:1,自由方式   2,集群方式
    CC_PROPERTY(tCCPositionType, m_ePositionType, PositionType)
protected:
	//在运行结束后是否被自动移除。
    bool m_bIsAutoRemoveOnFinish;
public:
	//取得运行结束后是否被自动移除。
    virtual bool isAutoRemoveOnFinish();
	//设置运行结束后是否被自动移除。
    virtual void setAutoRemoveOnFinish(bool var);

	//发射器模式:1,重力加速度模式 2 环型模式
    CC_PROPERTY(int, m_nEmitterMode, EmitterMode)

public:
	//构造函数。
    CCParticleSystem();
	//析构函数。
    virtual ~CCParticleSystem();
    //静态的由PLIST文件创建粒子系统的函数,参数为PLIST文件名,内部调用create来进行实现。
    CC_DEPRECATED_ATTRIBUTE static CCParticleSystem * particleWithFile(const char *plistFile);

    //上面的create实现。
    static CCParticleSystem * create(const char *plistFile);

    //粒子系统的初始化。
    bool init();
    //从一个PLIST文件中初始化粒子系统。
    bool initWithFile(const char *plistFile);

    //从一个词典中初始化粒子系统。
    bool initWithDictionary(CCDictionary *dictionary);

    //初始化粒子总数量。
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    //为发射器增加一个粒子。
    bool addParticle();
    //初始化一个粒子。
    void initParticle(tCCParticle* particle);
    //暂停当前粒子系统。
    void stopSystem();
    //重置当前粒子系统
    void resetSystem();
    //当前粒子系统是否已经将所有粒子发射。
    bool isFull();

    //用于被派生类重载的接口。
    virtual void updateQuadWithParticle(tCCParticle* particle, const CCPoint& newPosition);
    //用于被派生类重载的接口。
    virtual void postStep();

	//更新粒子系统。
    virtual void update(float dt);
	//不需要时间流逝的更新粒子系统。
    virtual void updateWithNoTime(void);

protected:
	//更新混合状态方案
    virtual void updateBlendFunc();
};

CPP文件:

#include "CCParticleSystem.h"
#include "CCParticleBatchNode.h"
#include "ccTypes.h"
#include "textures/CCTextureCache.h"
#include "textures/CCTextureAtlas.h"
#include "support/base64.h"
#include "support/CCPointExtension.h"
#include "platform/CCFileUtils.h"
#include "platform/CCImage.h"
#include "platform/platform.h"
#include "support/zip_support/ZipUtils.h"
#include "CCDirector.h"
#include "support/CCProfiling.h"
// opengl
#include "CCGL.h"

//使用Cocos2d-x命名空间。
NS_CC_BEGIN

//粒子系统的构造函数。
CCParticleSystem::CCParticleSystem()
    :m_sPlistFile("")
    ,m_fElapsed(0)
    ,m_pParticles(NULL)
    ,m_fEmitCounter(0)
    ,m_uParticleIdx(0)
    ,m_bIsActive(true)
    ,m_uParticleCount(0)
    ,m_fDuration(0)
    ,m_tSourcePosition(CCPointZero)
    ,m_tPosVar(CCPointZero)
    ,m_fLife(0)
    ,m_fLifeVar(0)
    ,m_fAngle(0)
    ,m_fAngleVar(0)
    ,m_fStartSize(0)
    ,m_fStartSizeVar(0)
    ,m_fEndSize(0)
    ,m_fEndSizeVar(0)
    ,m_fStartSpin(0)
    ,m_fStartSpinVar(0)
    ,m_fEndSpin(0)
    ,m_fEndSpinVar(0)
    ,m_fEmissionRate(0)
    ,m_uTotalParticles(0)
    ,m_pTexture(NULL)
    ,m_bOpacityModifyRGB(false)
    ,m_bIsBlendAdditive(false)
    ,m_ePositionType(kCCPositionTypeFree)
    ,m_bIsAutoRemoveOnFinish(false)
    ,m_nEmitterMode(kCCParticleModeGravity)
    ,m_pBatchNode(NULL)
    ,m_uAtlasIndex(0)
    ,m_bTransformSystemDirty(false)
    ,m_uAllocatedParticles(0)
{
	//大量的数据初始化工作。
    modeA.gravity = CCPointZero;
    modeA.speed = 0;
    modeA.speedVar = 0;
    modeA.tangentialAccel = 0;
    modeA.tangentialAccelVar = 0;
    modeA.radialAccel = 0;
    modeA.radialAccelVar = 0;
    modeB.startRadius = 0;
    modeB.startRadiusVar = 0;
    modeB.endRadius = 0;
    modeB.endRadiusVar = 0;            
    modeB.rotatePerSecond = 0;
    modeB.rotatePerSecondVar = 0;
    m_tBlendFunc.src = CC_BLEND_SRC;
    m_tBlendFunc.dst = CC_BLEND_DST;
}
// 从PLIST文件中创建粒子系统。内部调用create进行实现。
CCParticleSystem * CCParticleSystem::particleWithFile(const char *plistFile)
{
    return CCParticleSystem::create(plistFile);
}
//从PLIST文件中创建粒子系统的具体实现。
CCParticleSystem * CCParticleSystem::create(const char *plistFile)
{
	//创建一个粒子系统,由PLIST文件进行初始化并交由内存管理器进行引用计数器的管理。
    CCParticleSystem *pRet = new CCParticleSystem();
    if (pRet && pRet->initWithFile(plistFile))
    {
        pRet->autorelease();
        return pRet;
    }
	//如果失败,释放并置空。
    CC_SAFE_DELETE(pRet);
    return pRet;
}
//粒子系统的初始化。
bool CCParticleSystem::init()
{
	//初始化粒子的总数为150个。
    return initWithTotalParticles(150);
}
//从PLIST文件中初始化粒子系统。
bool CCParticleSystem::initWithFile(const char *plistFile)
{
    bool bRet = false;
	//取得PLIST的全路径。
    m_sPlistFile = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(plistFile);
	//从PLIST文件中创建出数据项与数据值对应关系表的词典。
    CCDictionary *dict = CCDictionary::createWithContentsOfFileThreadSafe(m_sPlistFile.c_str());
	//有效性判断。
    CCAssert( dict != NULL, "Particles: file not found");
	//由词典中的所有数据来进行初始化当前粒子系统。
    bRet = this->initWithDictionary(dict);
	//释放词典。
    dict->release();

    return bRet;
}
//由词典来初始化当前的粒子系统。
bool CCParticleSystem::initWithDictionary(CCDictionary *dictionary)
{
	//定义临时变量。
    bool bRet = false;
    unsigned char *buffer = NULL;
    unsigned char *deflated = NULL;
    CCImage *image = NULL;
	//
    do 
    {
		//从词典中取得粒子的最大数量值。
        int maxParticles = dictionary->valueForKey("maxParticles")->intValue();
        //初始化粒子容量。
        if(this->initWithTotalParticles(maxParticles))
        {
            // 从词典中取得角度及角度变化量的值。
            m_fAngle = dictionary->valueForKey("angle")->floatValue();
            m_fAngleVar = dictionary->valueForKey("angleVariance")->floatValue();

            //从词典中取得动画时长的值。
            m_fDuration = dictionary->valueForKey("duration")->floatValue();

            //从词典中取得混合方案
            m_tBlendFunc.src = dictionary->valueForKey("blendFuncSource")->intValue();
            m_tBlendFunc.dst = dictionary->valueForKey("blendFuncDestination")->intValue();

            //从词典中取得起始颜色
            m_tStartColor.r = dictionary->valueForKey("startColorRed")->floatValue();
            m_tStartColor.g = dictionary->valueForKey("startColorGreen")->floatValue();
            m_tStartColor.b = dictionary->valueForKey("startColorBlue")->floatValue();
            m_tStartColor.a = dictionary->valueForKey("startColorAlpha")->floatValue();
			//从词典中取得起始颜色变化量
            m_tStartColorVar.r = dictionary->valueForKey("startColorVarianceRed")->floatValue();
            m_tStartColorVar.g = dictionary->valueForKey("startColorVarianceGreen")->floatValue();
            m_tStartColorVar.b = dictionary->valueForKey("startColorVarianceBlue")->floatValue();
            m_tStartColorVar.a = dictionary->valueForKey("startColorVarianceAlpha")->floatValue();
			//从词典中取得结束的颜色值。
            m_tEndColor.r = dictionary->valueForKey("finishColorRed")->floatValue();
            m_tEndColor.g = dictionary->valueForKey("finishColorGreen")->floatValue();
            m_tEndColor.b = dictionary->valueForKey("finishColorBlue")->floatValue();
            m_tEndColor.a = dictionary->valueForKey("finishColorAlpha")->floatValue();
			//从词典中取得结束的颜色变化量值。
            m_tEndColorVar.r = dictionary->valueForKey("finishColorVarianceRed")->floatValue();
            m_tEndColorVar.g = dictionary->valueForKey("finishColorVarianceGreen")->floatValue();
            m_tEndColorVar.b = dictionary->valueForKey("finishColorVarianceBlue")->floatValue();
            m_tEndColorVar.a = dictionary->valueForKey("finishColorVarianceAlpha")->floatValue();

            //从词典中取得粒子的起始大小。
            m_fStartSize = dictionary->valueForKey("startParticleSize")->floatValue();
            m_fStartSizeVar = dictionary->valueForKey("startParticleSizeVariance")->floatValue();

            //从词典中取得粒子的终止大小。
            m_fEndSize = dictionary->valueForKey("finishParticleSize")->floatValue();
            m_fEndSizeVar = dictionary->valueForKey("finishParticleSizeVariance")->floatValue();

            //从词典中取得源位置。
            float x = dictionary->valueForKey("sourcePositionx")->floatValue();
            float y = dictionary->valueForKey("sourcePositiony")->floatValue();
            this->setPosition( ccp(x,y) );
            //从词典中取得源位置变化量。            
            m_tPosVar.x = dictionary->valueForKey("sourcePositionVariancex")->floatValue();
            m_tPosVar.y = dictionary->valueForKey("sourcePositionVariancey")->floatValue();

            //从词典中取得粒子的起始旋转角度。
            m_fStartSpin = dictionary->valueForKey("rotationStart")->floatValue();
			 //从词典中取得粒子的起始旋转角度变化值。
            m_fStartSpinVar = dictionary->valueForKey("rotationStartVariance")->floatValue();
            //从词典中取得粒子的终止旋转角度。
            m_fEndSpin= dictionary->valueForKey("rotationEnd")->floatValue();
            //从词典中取得粒子的终止旋转角度变化值。
            m_fEndSpinVar= dictionary->valueForKey("rotationEndVariance")->floatValue();
            //从词典中取得粒子的发射器类型。
            m_nEmitterMode = dictionary->valueForKey("emitterType")->intValue();

            //重力加速度模式
            if( m_nEmitterMode == kCCParticleModeGravity ) 
            {
                //从词典中取得粒子的重力加速度。
                modeA.gravity.x = dictionary->valueForKey("gravityx")->floatValue();
                modeA.gravity.y = dictionary->valueForKey("gravityy")->floatValue();

                //从词典中取得粒子的速度及变化量。
                modeA.speed = dictionary->valueForKey("speed")->floatValue();
                modeA.speedVar = dictionary->valueForKey("speedVariance")->floatValue();

                //从词典中取得粒子的旋转加速度及变化量。
                modeA.radialAccel = dictionary->valueForKey("radialAcceleration")->floatValue();
                modeA.radialAccelVar = dictionary->valueForKey("radialAccelVariance")->floatValue();

                //从词典中取得粒子的切线加速度及变化量。
                modeA.tangentialAccel = dictionary->valueForKey("tangentialAcceleration")->floatValue();
                modeA.tangentialAccelVar = dictionary->valueForKey("tangentialAccelVariance")->floatValue();
            }

            // 环型模式
            else if( m_nEmitterMode == kCCParticleModeRadius ) 
            {
				  //从词典中取得粒子的最大角度。
                modeB.startRadius = dictionary->valueForKey("maxRadius")->floatValue();
				 //从词典中取得粒子的最大角度变化值。
                modeB.startRadiusVar = dictionary->valueForKey("maxRadiusVariance")->floatValue();
				 //从词典中取得粒子的最小角度。
                modeB.endRadius = dictionary->valueForKey("minRadius")->floatValue();
				 //竟然没有从词典中取得粒子的最小角度变化值而是直接设为0。好吧!
                modeB.endRadiusVar = 0.0f;
				 //从词典中取得粒子的每秒旋转圈数。

                modeB.rotatePerSecond = dictionary->valueForKey("rotatePerSecond")->floatValue();
				//从词典中取得粒子的每秒旋转圈数变化量。
                modeB.rotatePerSecondVar = dictionary->valueForKey("rotatePerSecondVariance")->floatValue();

            } else {//类型无效的错误提示。
                CCAssert( false, "Invalid emitterType in config file");
                CC_BREAK_IF(true);
            }

            // 从词典中取得粒子的生命值及变化量。
            m_fLife = dictionary->valueForKey("particleLifespan")->floatValue();
            m_fLifeVar = dictionary->valueForKey("particleLifespanVariance")->floatValue();

            //计数出发射器的发射速度。
            m_fEmissionRate = m_uTotalParticles / m_fLife;

			 //如果一个批次结点被使用着,则不能直接取得内部的纹理对象。
            if (!m_pBatchNode)
            {
				  // 设置修改透明度不影响RGB值。
                m_bOpacityModifyRGB = false;

                // 从词典中取得所用的纹理图片名称。
                const char* textureName = dictionary->valueForKey("textureFileName")->getCString();
				  // 通过文件管理器查找到相应的全路径字符串。
                std::string fullpath = CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(textureName, m_sPlistFile.c_str());
                //定义纹理对象指针并置空。
                CCTexture2D *tex = NULL;
                //如果纹理名称有效。
                if (strlen(textureName) > 0)
                {
                    // 先取得当前文件管理器是否进行相关提示。
                    bool bNotify = CCFileUtils::sharedFileUtils()->isPopupNotify();
                    // 在这里指定不需要弹出文件读取出错的提示对话框。为什么呢?
CCFileUtils::sharedFileUtils()->setPopupNotify(false);
					  // 将文件加载到纹理管理器中并返回纹理对象指针。
                    tex = CCTextureCache::sharedTextureCache()->addImage(fullpath.c_str());
                    
                    //重置文件读取出错的提示对话框。
CCFileUtils::sharedFileUtils()->setPopupNotify(bNotify);
                }
                //如果纹理对象指针有效,设置为当前粒子系统所用的纹理贴图。
                if (tex)
                {
                    setTexture(tex);
                }
                else
                {   
					 //如果无效,则不是从文件中加载,而是直接从数据中创建纹理。
					  //从词典中取得所用的纹理图片数据返回给textureData。
                    const char *textureData = dictionary->valueForKey("textureImageData")->getCString();
					  //数据的有效性判断。
                    CCAssert(textureData, "");
                    //取得数据的长度。
                    int dataLen = strlen(textureData);
                    if(dataLen != 0)
                    {
                        //注意base64Decode提供了Base64解码过程。返回解码后的数据到buffer,返回长度值decodeLen。
                        int decodeLen = base64Decode((unsigned char*)textureData, (unsigned int)dataLen, &buffer);
							//有效性判断。
                        CCAssert( buffer != NULL, "CCParticleSystem: error decoding textureImageData");
                        CC_BREAK_IF(!buffer);
                        // ZipUtils::ccInflateMemory 提供了gzip解压的过程。返回解压后的数据到deflated,返回长度值decodeLen。
                        int deflatedLen = ZipUtils::ccInflateMemory(buffer, decodeLen, &deflated);
							//有效性判断。
                        CCAssert( deflated != NULL, "CCParticleSystem: error ungzipping textureImageData");
                        CC_BREAK_IF(!deflated);
                        
                        //创建一个新的CCImage,返回其实例对象指针给image。
                        image = new CCImage();
						   //通过解压后的数据来填充CCImage的图像数据。
                        bool isOK = image->initWithImageData(deflated, deflatedLen);
							//判断是否成功。
                        CCAssert(isOK, "CCParticleSystem: error init image with Data");
                        CC_BREAK_IF(!isOK);
                        
                        //将这个image加入到纹理管理器中,并设置给当前粒子系统使用。
setTexture(CCTextureCache::sharedTextureCache()->addUIImage(image, fullpath.c_str()));
							//image的释放处理。
                        image->release();
                    }
                }
				  //纹理的有效性判断。
                CCAssert( this->m_pTexture != NULL, "CCParticleSystem: error loading the texture");
            }
            bRet = true;
        }
    } while (0);
	//释放buffer和deflated。
    CC_SAFE_DELETE_ARRAY(buffer);
    CC_SAFE_DELETE_ARRAY(deflated);
    return bRet;
}
//通过粒子的最大数量来初始化粒子系统。
bool CCParticleSystem::initWithTotalParticles(unsigned int numberOfParticles)
{
	//记录最大数量。
    m_uTotalParticles = numberOfParticles;
	//先释放原来的粒子数组信息。
    CC_SAFE_FREE(m_pParticles);
    //重新申请粒子数组对应的相应内存。
    m_pParticles = (tCCParticle*)calloc(m_uTotalParticles, sizeof(tCCParticle));
	//如果申请内存失败,提示出错,释放并返回false。
    if( ! m_pParticles )
    {
        CCLOG("Particle system: not enough memory");
        this->release();
        return false;
    }
	//记录当前数组中的粒子的数量。
    m_uAllocatedParticles = numberOfParticles;
	//如果对应的粒子的批次结点有效。
    if (m_pBatchNode)
    {
		//遍历并填充相应粒子对应的批次结点中矩形顶点缓冲信息块的索引。
        for (unsigned int i = 0; i < m_uTotalParticles; i++)
        {
            m_pParticles[i].atlasIndex=i;
        }
    }
    // 默认激活。
    m_bIsActive = true;

    // 默认的ALPHA混合方案。
    m_tBlendFunc.src = CC_BLEND_SRC;
    m_tBlendFunc.dst = CC_BLEND_DST;

    // 默认的移动类型。
    m_ePositionType = kCCPositionTypeFree;

    // 默认的运动模式。
    m_nEmitterMode = kCCParticleModeGravity;

    //默认在结束后不自动释放当前粒子系统。
    m_bIsAutoRemoveOnFinish = false;

    //设置不使用矩阵转换,更新更快一些,
    m_bTransformSystemDirty = false;
    // 在动画作束后更新。
    this->scheduleUpdateWithPriority(1);

    return true;
}
//析构函数。
CCParticleSystem::~CCParticleSystem()
{
    unscheduleUpdate();
	//释放粒子数组。
    CC_SAFE_FREE(m_pParticles);
	//释放纹理。
    CC_SAFE_RELEASE(m_pTexture);
}
//增加一个新的粒子。
bool CCParticleSystem::addParticle()
{
	//如果当前粒子系统已经达到最大量则返回。
    if (this->isFull())
    {
        return false;
    }
	//否则获取数组中当前数量对应的索引位置取出来相应的粒子并初始化。
    tCCParticle * particle = &m_pParticles[ m_uParticleCount ];
    this->initParticle(particle);
    ++m_uParticleCount;

    return true;
}

//初始化粒子。
void CCParticleSystem::initParticle(tCCParticle* particle)
{
    // 初始化生命时间。CCRANDOM_MINUS1_1()是取一个(-1,1)之间的随机值。
    particle->timeToLive = m_fLife + m_fLifeVar * CCRANDOM_MINUS1_1();
	// 将随机值限定大于0。
    particle->timeToLive = MAX(0, particle->timeToLive);

    // 初始化一个以m_tSourcePosition位置为中心的随机位置,最大变化范围为m_tPosVar。
    particle->pos.x = m_tSourcePosition.x + m_tPosVar.x * CCRANDOM_MINUS1_1();
    particle->pos.y = m_tSourcePosition.y + m_tPosVar.y * CCRANDOM_MINUS1_1();


    // 初始化一个以m_tStartColor为基本色的随机色,最大变化范围为m_ tStartColorVar。
    ccColor4F start;
    start.r = clampf(m_tStartColor.r + m_tStartColorVar.r * CCRANDOM_MINUS1_1(), 0, 1);
    start.g = clampf(m_tStartColor.g + m_tStartColorVar.g * CCRANDOM_MINUS1_1(), 0, 1);
    start.b = clampf(m_tStartColor.b + m_tStartColorVar.b * CCRANDOM_MINUS1_1(), 0, 1);
start.a = clampf(m_tStartColor.a + m_tStartColorVar.a * CCRANDOM_MINUS1_1(), 0, 1);

    // 初始化一个以m_tEndColor为基本色的随机色,最大范围为m_ tEndColorVar。
    ccColor4F end;
    end.r = clampf(m_tEndColor.r + m_tEndColorVar.r * CCRANDOM_MINUS1_1(), 0, 1);
    end.g = clampf(m_tEndColor.g + m_tEndColorVar.g * CCRANDOM_MINUS1_1(), 0, 1);
    end.b = clampf(m_tEndColor.b + m_tEndColorVar.b * CCRANDOM_MINUS1_1(), 0, 1);
    end.a = clampf(m_tEndColor.a + m_tEndColorVar.a * CCRANDOM_MINUS1_1(), 0, 1);

    // 初始化粒子的颜色和每次更新时的变化量。
    particle->color = start;
    particle->deltaColor.r = (end.r - start.r) / particle->timeToLive;
    particle->deltaColor.g = (end.g - start.g) / particle->timeToLive;
    particle->deltaColor.b = (end.b - start.b) / particle->timeToLive;
    particle->deltaColor.a = (end.a - start.a) / particle->timeToLive;

// 初始化一个以m_fStartSize为基本大小的随机大小值,最大变化范围为m_fStartSizeVar。
    float startS = m_fStartSize + m_fStartSizeVar * CCRANDOM_MINUS1_1();
	//限定大小。
    startS = MAX(0, startS); 
	//设置大小。
    particle->size = startS;
	//如果终止大小为设定的值-1,即代表大小不随更新变化。
    if( m_fEndSize == kCCParticleStartSizeEqualToEndSize )
    {
        particle->deltaSize = 0;
    }
    else
    {
		//否则计算出大小每次更新时的变化量。
        float endS = m_fEndSize + m_fEndSizeVar * CCRANDOM_MINUS1_1();
        endS = MAX(0, endS); // No negative values
        particle->deltaSize = (endS - startS) / particle->timeToLive;
    }

    // 随机的旋转起始和终止角度。
    float startA = m_fStartSpin + m_fStartSpinVar * CCRANDOM_MINUS1_1();
    float endA = m_fEndSpin + m_fEndSpinVar * CCRANDOM_MINUS1_1();
	//保存起始角度,并计算每次更新的旋转角度。
    particle->rotation = startA;
    particle->deltaRotation = (endA - startA) / particle->timeToLive;

    //如果是自由方式的位置设置方式,初始化起点为0,0点。
    if( m_ePositionType == kCCPositionTypeFree )
    {	
		//将当前坐标系中零零点转换为世界坐标系中的位置。
        currentPosition = this->convertToWorldSpace(CCPointZero);
        particle->startPos = this->convertToWorldSpace(CCPointZero);
    }
    else if ( m_ePositionType == kCCPositionTypeRelative )
    {
		//如果是相对方式的位置设置方式,初始化起点位置为当前粒子系统坐标系的位置。
        particle->startPos = m_tPosition;
    }

    // 设置方向值,是一个弧度,由以m_fAngle为基础的随机范围为m_fAngleVar所生成的角度转化而成。
    float a = CC_DEGREES_TO_RADIANS( m_fAngle + m_fAngleVar * CCRANDOM_MINUS1_1() );    

    // 如果是重力加速度模式。
    if (m_nEmitterMode == kCCParticleModeGravity) 
    {
		 //由方向值计算出X,Y的分量的角度。
        CCPoint v(cosf( a ), sinf( a ));
		// s 为此模式下的速度,是以modeA.speed为基础的随机范围为modeA.speedVar的随机值。
        float s = modeA.speed + modeA.speedVar * CCRANDOM_MINUS1_1();

        // 将上面的信息相乘的结果做为方向保存到粒子的modeA的方向值中。
        particle->modeA.dir = ccpMult( v, s );

        // 旋转加速度的值为modeA.radialAccel为基础的随机范围为modeA.radialAccelVar的值。
        particle->modeA.radialAccel = modeA.radialAccel + modeA.radialAccelVar * CCRANDOM_MINUS1_1();
 

        // 切线加速度的值为modeA.tangentialAccel为基础的随机范围为modeA.tangentialAccelVar的值。
        particle->modeA.tangentialAccel = modeA.tangentialAccel + modeA.tangentialAccelVar * CCRANDOM_MINUS1_1();

    }

    // 如果是环形模式。
    else 
    {
        // 设置起始弧度和结束弧度。也是以相应值为基础进行随机计算。
        float startRadius = modeB.startRadius + modeB.startRadiusVar * CCRANDOM_MINUS1_1();
        float endRadius = modeB.endRadius + modeB.endRadiusVar * CCRANDOM_MINUS1_1();

		 //填充到粒子的数据值中。
        particle->modeB.radius = startRadius;
		 //如果结束弧度值为指定的值,则在更新过程中不进行旋转变化。
        if(modeB.endRadius == kCCParticleStartRadiusEqualToEndRadius)
        {
            particle->modeB.deltaRadius = 0;
        }
        else
        {
			  //否则计算每次更新的弧度变化量。
            particle->modeB.deltaRadius = (endRadius - startRadius) / particle->timeToLive;
        }
		 //保存方向值弧度。并计算每秒的旋转弧度。
        particle->modeB.angle = a;
        particle->modeB.degreesPerSecond = CC_DEGREES_TO_RADIANS(modeB.rotatePerSecond + modeB.rotatePerSecondVar * CCRANDOM_MINUS1_1());
    }    
}
//停止粒子系统。
void CCParticleSystem::stopSystem()
{
    m_bIsActive = false;
    m_fElapsed = m_fDuration;
    m_fEmitCounter = 0;
}
//重新启动粒子系统。
void CCParticleSystem::resetSystem()
{
    m_bIsActive = true;
    m_fElapsed = 0;
	//遍历每个粒子,设置生命值为0。
    for (m_uParticleIdx = 0; m_uParticleIdx < m_uParticleCount; ++m_uParticleIdx)
    {
        tCCParticle *p = &m_pParticles[m_uParticleIdx];
        p->timeToLive = 0;
    }
}
//粒子系统是否已经填满。
bool CCParticleSystem::isFull()
{
    return (m_uParticleCount == m_uTotalParticles);
}

// 粒子系统的更新函数。
void CCParticleSystem::update(float dt)
{
	//
    CC_PROFILER_START_CATEGORY(kCCProfilerCategoryParticles , "CCParticleSystem - update");
	//如果是激活状态,并且发射速度大于0
    if (m_bIsActive && m_fEmissionRate)
    {
		//计算出产生每个粒子的平均时间间隔。
        float rate = 1.0f / m_fEmissionRate;
		//如果当前粒子数量仍小于最大数量。对发射器的时间计数器加上当前帧的时间间隔值dt。
        if (m_uParticleCount < m_uTotalParticles)
        {
            m_fEmitCounter += dt;
        }
        //Whle循环,看当前积累的时间间隔积足够创建多少粒子就创建多少粒子。
        while (m_uParticleCount < m_uTotalParticles && m_fEmitCounter > rate) 
        {
            this->addParticle();
            m_fEmitCounter -= rate;
        }
		//记录时间间隔。
        m_fElapsed += dt;
		//如时总的动画时长为-1并且
        if (m_fDuration != -1 && m_fDuration < m_fElapsed)
        {
            this->stopSystem();
        }
    }
	//设置粒子的索引值。
    m_uParticleIdx = 0;
	//定义一个临时结构变量存储当前位置,初始化为零零点。
    CCPoint currentPosition = CCPointZero;
	//如果自由模式
    if (m_ePositionType == kCCPositionTypeFree)
    {
		//将当前坐标系中零零点转换为世界坐标系中的位置。
        currentPosition = this->convertToWorldSpace(CCPointZero);
    }
    else if (m_ePositionType == kCCPositionTypeRelative)
    {
		//如果是相对模式,保存当前坐标系的位置。
        currentPosition = m_tPosition;
    }
	//如果显示标记为true
    if (m_bIsVisible)
    {
		//遍历每一个粒子,进行更新.
        while (m_uParticleIdx < m_uParticleCount)
        {
			 //取得对应的粒子.
            tCCParticle *p = &m_pParticles[m_uParticleIdx];

            // 生命值的减少.
            p->timeToLive -= dt;

			//如果生命值大于0.则进行更新.
            if (p->timeToLive > 0) 
            {
                //如果是重力加速度模式
                if (m_nEmitterMode == kCCParticleModeGravity) 
                {
					   //定义临时变量存储位置,旋转角度,切角
                    CCPoint tmp, radial, tangential;
					  //初始化角度值为零零点.
                    radial = CCPointZero;
                    // 旋转角度的计算.
                    if (p->pos.x || p->pos.y)
                    {
						   //向量归一化.
                        radial = ccpNormalize(p->pos);
                    }
					  //设置切角度为当前角度。
                    tangential = radial;
					  // 旋转角度的乘以速度值.
                    radial = ccpMult(radial, p->modeA.radialAccel);

                    // 切角度变化计算.
                    float newy = tangential.x;
                    tangential.x = -tangential.y;
                    tangential.y = newy;
					 //
                    tangential = ccpMult(tangential, p->modeA.tangentialAccel);

                    // 位置的计算.
                    tmp = ccpAdd( ccpAdd( radial, tangential), modeA.gravity);
                    tmp = ccpMult( tmp, dt);
                    p->modeA.dir = ccpAdd( p->modeA.dir, tmp);
                    tmp = ccpMult(p->modeA.dir, dt);
                    p->pos = ccpAdd( p->pos, tmp );
                }

                //
                else 
                {
                    // 如果是环形模式。
                    // 更新粒子的角度。
                    p->modeB.angle += p->modeB.degreesPerSecond * dt;
                    p->modeB.radius += p->modeB.deltaRadius * dt;
                    // 计算粒子的位置。
                    p->pos.x = - cosf(p->modeB.angle) * p->modeB.radius;
                    p->pos.y = - sinf(p->modeB.angle) * p->modeB.radius;
                }

                // 颜色的变化计算。
                p->color.r += (p->deltaColor.r * dt);
                p->color.g += (p->deltaColor.g * dt);
                p->color.b += (p->deltaColor.b * dt);
                p->color.a += (p->deltaColor.a * dt);

                // 大小的变化计算。
                p->size += (p->deltaSize * dt);
                p->size = MAX( 0, p->size );

                // 角度。
                p->rotation += (p->deltaRotation * dt);

				  //
                CCPoint    newPos;
				  //如果是自由模式或者相对模式
                if (m_ePositionType == kCCPositionTypeFree || m_ePositionType == kCCPositionTypeRelative) 
                {
					   //
                    CCPoint diff = ccpSub( currentPosition, p->startPos );
                    newPos = ccpSub(p->pos, diff);
                } 
                else
                {
                    newPos = p->pos;
                }
                //如果使用批次结点,加上当前粒子系统坐标系的位置。为什么呢?因为矩阵转换不会影响批次结点。
                if (m_pBatchNode)
                {
                    newPos.x+=m_tPosition.x;
                    newPos.y+=m_tPosition.y;
                }
				  //更新粒子对应批次结点中的矩形顶点缓冲信息。
                updateQuadWithParticle(p, newPos);
                
                // 更新粒子计数器。
                ++m_uParticleIdx;
            } 
            else 
            {
                // 如果生命值小于0.
				  // 
                int currentIndex = p->atlasIndex;
                if( m_uParticleIdx != m_uParticleCount-1 )
                {
                    m_pParticles[m_uParticleIdx] = m_pParticles[m_uParticleCount-1];
                }
				  //如果使用批次结点,通过设置对应的矩形顶点缓冲中大小为0达到不显示指定粒子的目的。
                if (m_pBatchNode)
                {
                    //disable the switched particle
                    m_pBatchNode->disableParticle(m_uAtlasIndex+currentIndex);

                    //将最后一个粒子信息中的
                    m_pParticles[m_uParticleCount-1].atlasIndex = currentIndex;
                }
				  //粒子数量减一。
                --m_uParticleCount;
				  //如果粒子数量减为0并且设置自动删除粒子系统。
                if( m_uParticleCount == 0 && m_bIsAutoRemoveOnFinish )
                {
					   //停止更新并从父结点中删除自已。
                    this->unscheduleUpdate();
                    m_pParent->removeChild(this, true);
                    return;
                }
            }
        } 
	     //
        m_bTransformSystemDirty = false;
    }
	//如果不使用批次结点进行优化,则调用用户重载的虚函数postStep进行更新处理。
    if (! m_pBatchNode)
    {
        postStep();
    }

    CC_PROFILER_STOP_CATEGORY(kCCProfilerCategoryParticles , "CCParticleSystem - update");
}

//不增加时间变化的更新
void CCParticleSystem::updateWithNoTime(void)
{
    this->update(0.0f);
}

//需要被重载的更新粒子的对应矩形顶点缓冲信息块的虚函数。
void CCParticleSystem::updateQuadWithParticle(tCCParticle* particle, const CCPoint& newPosition)
{
    CC_UNUSED_PARAM(particle);
    CC_UNUSED_PARAM(newPosition);
    // should be overriden
}
//需要被重载的粒子更新处理函数。
void CCParticleSystem::postStep()
{
    // should be overriden
}

//设置当前粒子系统所用的纹理对象。
void CCParticleSystem::setTexture(CCTexture2D* var)
{
	//如果纹理对象不同,则释放旧的纹理对象,设置为新的纹理对象,并更新ALPHA混合方案。
    if (m_pTexture != var)
    {
        CC_SAFE_RETAIN(var);
        CC_SAFE_RELEASE(m_pTexture);
        m_pTexture = var;
        updateBlendFunc();
    }
}

//更新ALPHA混合方案。为了方便理解,先了解一下常用的混合状态值的意义:
GL_ZERO: 表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
GL_ONE: 表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。 除此以外,还有GL_SRC_COLOR(把源颜色的四个分量分别作为因子的四个分量)、GL_ONE_MINUS_SRC_COLOR、 GL_DST_COLOR、GL_ONE_MINUS_DST_COLOR等,前两个在OpenGL旧版本中只能用于设置目标因子,后两个在OpenGL 旧版本中只能用于设置源因子。新版本的OpenGL则没有这个限制,并且支持新的GL_CONST_COLOR(设定一种常数颜色,将其四个分量分别作为 因子的四个分量)、GL_ONE_MINUS_CONST_COLOR、GL_CONST_ALPHA、 GL_ONE_MINUS_CONST_ALPHA。另外还有GL_SRC_ALPHA_SATURATE。新版本的OpenGL还允许颜色的alpha 值和RGB值采用不同的混合因子。

void CCParticleSystem::updateBlendFunc()
{
	//有效性判断。
    CCAssert(! m_pBatchNode, "Can't change blending functions when the particle is being batched");
	//如果纹理对象指针有效,取出其是否有ALPHA通道,并将有ALPHA通道的设为ALPHA混合方案,
    if(m_pTexture)
    {
        bool premultiplied = m_pTexture->hasPremultipliedAlpha();
        
        m_bOpacityModifyRGB = false;
        
        if( m_pTexture && ( m_tBlendFunc.src == CC_BLEND_SRC && m_tBlendFunc.dst == CC_BLEND_DST ) )
        {
			  //如果有ALPHA通道,设置标记m_bOpacityModifyRGB为true.
            if( premultiplied )
            {
                m_bOpacityModifyRGB = true;
            }
            else
            {
               //如果没有ALPHA通道,使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减 小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
                m_tBlendFunc.src = GL_SRC_ALPHA;
                m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
            }
        }
    }
}
//取得当前粒子系统的纹理对象.
CCTexture2D * CCParticleSystem::getTexture()
{
    return m_pTexture;
}

// 设置粒子的混合方案是否采用高亮模式
void CCParticleSystem::setBlendAdditive(bool additive)
{
	//如果是高亮模式
    if( additive )
    {
		 //设置混合方案的源和目标状态值
        m_tBlendFunc.src = GL_SRC_ALPHA;
        m_tBlendFunc.dst = GL_ONE;
    }
    else
    {
		//如果不是高亮模式,则根据是否有ALPHA通道来进行相应的方案设置.
        if( m_pTexture && ! m_pTexture->hasPremultipliedAlpha() )
        {
            m_tBlendFunc.src = GL_SRC_ALPHA;
            m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
        } 
        else 
        {
			 //下面的两个渲染状态值为别是对应GL_ONE和GL_ONE_MINUS_SRC_ALPHA.
            m_tBlendFunc.src = CC_BLEND_SRC;
            m_tBlendFunc.dst = CC_BLEND_DST;
        }
    }
}
//取得是否采用了高亮方式.
bool CCParticleSystem::isBlendAdditive()
{
    return( m_tBlendFunc.src == GL_SRC_ALPHA && m_tBlendFunc.dst == GL_ONE);
}

// 后面是一大堆属性设置函数.不贴了,大家都看得懂.

 总结一下:

    粒子系统提供了对于粒子信息结构体tCCParticle的数组控制管理,通过tCCParticle中有限的数据属性来摸拟自然界粒子的存在与运动规律。从其中需要重载的函数我们知道,Cocos2d-x的一些粒子系统的具体演示都是由这个基础的粒子系统类进行派生和相应函数重载实现的。所以重点是理解属性的意义和掌握将来我们如何进行扩展。

在粒子系统中有函数setBatchNode来设置所用的批次结点。我们将在二部曲中再具体分析。


课后作业

(1)理解批次优化的概念,提高游戏研发的思维习惯。

(2)参考DX或Opengl的一般粒子系统的实现,实做一个小的粒子系统。

(3)找到不同的ALPHA混合方案并进行测试和理解,或者找到相应的博客资源进行学习。

 

       这篇博客时间跨度较大,近期红孩儿除了写博之外,也在每天深夜坚持写新的工具-“红孩儿游戏工具箱”,一个巨无霸的游戏开发工具箱。包括切图,拼图,帧动画与骨骼动画,粒子系统编辑,界面编辑,场景编辑,字体编辑等多项功能。以立图将游戏开发形成一套形之有效的工具化流程。


#分享视频# http://url.cn/BUYo0v “红孩儿游戏工具箱”之切图编辑

 Cocos2d-x2.0 粒子系统深入分析三部曲(一)


#分享视频# http://url.cn/A4hkpf 红孩儿游戏工具箱之图片合并编辑

 Cocos2d-x2.0 粒子系统深入分析三部曲(一)


#分享视频# http://url.cn/CXe3w3 新做的“红孩儿游戏工具箱”之关键帧动画演示

Cocos2d-x2.0 粒子系统深入分析三部曲(一)


#分享视频# http://url.cn/9fUJt9 新做的“红孩儿游戏工具箱”之融合动画演示
Cocos2d-x2.0 粒子系统深入分析三部曲(一)


#分享视频# http://url.cn/DngRC8 红孩儿游戏工具箱-骨骼动画编辑演示

Cocos2d-x2.0 粒子系统深入分析三部曲(一)


 #分享视频# http://url.cn/E36O0O 红孩儿工具箱之:骨骼动画树做为结点的挂接编辑
Cocos2d-x2.0 粒子系统深入分析三部曲(一)


#分享视频# http://url.cn/Bc0iq6 红孩儿工具箱之基础层场景编辑
Cocos2d-x2.0 粒子系统深入分析三部曲(一)

#分享视频# http://url.cn/BX9uAk 红孩儿游戏工具箱-场景阻挡格编辑:不是对某个格子编辑,而是对一个图块进行阻挡编辑。

Cocos2d-x2.0 粒子系统深入分析三部曲(一)

#分享视频# http://url.cn/B1DwtD 红孩儿游戏工具箱-界面编辑器之面板与按钮编辑

Cocos2d-x2.0 粒子系统深入分析三部曲(一)



欢迎大家到我的微博上关注我随时更新发布的视频。再次感谢大家的支撑!


新浪微博: http://weibo.com/u/1834515945

腾讯微博:http://t.qq.com/honghaier_2005

分类: cocos2d, cocos2d-x 标签:

No value was provided for the parameter ‘appIdName’ when provisioning

2013年1月16日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!
原文地址: http://www.cocos2dev.com/?p=373


Xcode 自动提交certificate requests的时候,需要寻找一个“Wildcard” 的app Id,如果你丢了这个(一般是自动创建的),那么xcode的自动请求就会失败。解决起来也很简单。

 

1、登陆到Developer。


2、选中certificates,把你创建的certificates撤销了(Revoke)


3、选中App IDs,创建一个名为Wildcard的app id,suffix敲入*,记住只有一个 * ,不带其他的。

 

4、重新在xcode上再来一次生成Provisioning。


(不撤销certificates,好像也可以,我第二次做的时候好像没有撤销,呵呵)

分类: 未分类 标签:

How to do 2D animation in Unity

2013年1月15日 没有评论

So I’m doing a 3D game for kids for Android and iOS in Unity, but i’m new in game developing and it’s been really difficult to plan the assets.

We need to create 2D animations (paper like characters) and the characters have to be really detailed with great animations.

We have been thinking of several options:

We could create frame by frame animations but our designer says there has to be at least 24 images per second (because of 24 fps per second) with this the app will be very big.

Other option is to create 2D models in Blender and animate them there, but it’s a lot of work and could take a lot of time.

The last option is to have the pieces of the model an animate it throughout code but it’s a lot of work and I believe the quality of the animations would be low.

What’s the better way to create 2D animations in Unity?.

Thank you!

Have you explored the 2D sprite engines that are available in Unity? Whoever said “Unity isn’t really an engine designed to work with 2D stuff” is talking guff. I have just started working on a hobby 2D game and am using a Unity plugin called Orthello (see WyrmTale website for info). It handles sprite sheets, animations, collision detection and more without you having to write loads of code to do this. The learning curve is a bit steep and the examples on their website aren’t the best but I found replicating the sample solutions that come with the download the best way to get something working.

There’s also a similar tool called Sprite Manager 2 but you have to pay for that (I think). Check out the asset store for more information.

I would be really interested to hear if Orthello is what you’re looking for and how you find working with it – please let me know via http://markp3rry.wordpress.com if you can.

Just because the app runs at 24fps doesn’t mean you can’t just display the animations for more than one frame of the main loop. It might not be smooth, but then again looking at the sprite sheets of games like super street fighter it doesn’t look like they’re at anywhere close to 24fps (the sprite sheet for Dhalism in SF3 Alpha is a 210kb .gif file on my computer, and there’s less than 252 frames of animation on it. Likewise, the total storage space take up by every character sprite in Dustforce takes up a mere 7mb, though those sprites are just 192×192, maybe too low-res for you. They do look like paper though). I doubt that anything involving blender would take longer than hand animating — Blender does key frames for you.

分类: stackoverflow精选, unity3d 标签:

Cocos2d-x2.0 RotateWorldTest深入分析

2013年1月14日 没有评论

 

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ2群:44208467加群写:Cocos2d-x 
红孩儿Cocos2d-X学习园地QQ群:249941957 [暂满]加群写:Cocos2d-x

本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!

另请转载者注明转载地址及作者。

Cocos2d-x2.0 RotateWorldTest深入分析

另:本章所用Cocos2d-x版本为:

cocos2d-2.0-x-2.0.2@ Aug 30
2012

http://cn.cocos2d-x.org/download

              我们今天来学习一下RotateWorldTest这个实例,这个实例讲了点什么呢?顾名思义,“旋转世界”。嗯,让我想起潘帅的“反转地球”了。我们一起来转起来吧~

运行截图:

Cocos2d-x2.0 RotateWorldTest深入分析

   打开RotateWorldTest.h:

//没什么新鲜,演示当前实例的场景和CCLayer们:
class RotateWorldTestScene : public TestScene
{
public:
    virtual void runThisTest();
};

class SpriteLayer : public CCLayer
{
public:
    virtual void onEnter();
    CREATE_FUNC(SpriteLayer)
};

class TestLayer : public CCLayer
{
public:
    virtual void onEnter();

    CREATE_FUNC(TestLayer)
};

class RotateWorldMainLayer : public CCLayer
{
public:
    virtual void onEnter();

    CREATE_FUNC(RotateWorldMainLayer)
};

直接看RotateWorldTest.cpp吧:

//加载TestLayer时调用的处理:
void TestLayer::onEnter()
{
	//调用基类的相应函数。
    CCLayer::onEnter();
	//定义临时变量x,y保存窗口大小。
    float x,y;
    //取得窗口大小。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    x = size.width;
    y = size.height;

    //创建一个文字标签
    CCLabelTTF* label = CCLabelTTF::create("cocos2d", "Tahoma", 64);
	//设置其位置
    label->setPosition( CCPointMake(x/2,y/2) );
    //放到当前CCLayer中。
    addChild(label);
}

//加载SpriteLayer时调用的处理:
void SpriteLayer::onEnter()
{
	//调用基类的相应函数。
    CCLayer::onEnter();
	//定义临时变量x,y保存窗口大小。
    float x,y;
    //取得窗口大小。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    x = size.width;
    y = size.height;
    //创建演员们,熟悉的男一号,女一号,女二号,都上台了。
    CCSprite* sprite = CCSprite::create(s_pPathGrossini);
    CCSprite* spriteSister1 = CCSprite::create(s_pPathSister1);
    CCSprite* spriteSister2 = CCSprite::create(s_pPathSister2);
    //设置各自的缩放值 。
    sprite->setScale(1.5f);
    spriteSister1->setScale(1.5f);
    spriteSister2->setScale(1.5f);
    //设置各自的位置。
    sprite->setPosition(CCPointMake(x/2,y/2));
    spriteSister1->setPosition(CCPointMake(40,y/2));
    spriteSister2->setPosition(CCPointMake(x-40,y/2));
	//创建一个16秒内反向旋转3600度的动画。
    CCAction *rot = CCRotateBy::create(16, -3600);
    //将演员们都放到场景。
    addChild(sprite);
    addChild(spriteSister1);
    addChild(spriteSister2);
    //男一号运行上面的旋转动画。
    sprite->runAction(rot);
	//创建一个跳跃动画,4秒内跳跃4次。
    CCActionInterval *jump1 = CCJumpBy::create(4, CCPointMake(-400,0), 100, 4);
	//创建上面跳跃动画的反向动画。
    CCActionInterval *jump2 = jump1->reverse();
    //创建一个旋转动画,4秒内旋转720度。
    CCActionInterval *rot1 = CCRotateBy::create(4, 360*2);
	//创建上面旋转动画的反向动画。
    CCActionInterval *rot2 = rot1->reverse();
    
    //女一号循环运行5次一个动画序列,动画序列就是交替运行跳跃动画和其反向动画,不过是先运行反向动画。
spriteSister1->runAction(CCRepeat::create( CCSequence::create(jump2, jump1, NULL), 5 ));
    //女二号无限循环运行5次一个动画序列,动画序列也是交替运行跳跃动画和其反向动画。
spriteSister2->runAction(CCRepeat::create( CCSequence::create((CCFiniteTimeAction *)(jump1->copy()->autorelease()), (CCFiniteTimeAction *)(jump2->copy()->autorelease()), NULL), 5 ));
    
    //女一号循环运行5次一个动画序列,动画序列交替运行旋转动画和其反向动画。
spriteSister1->runAction(CCRepeat::create( CCSequence::create(rot1, rot2, NULL), 5 ));
     //女二号循环运行5次一个动画序列,动画序列交替运行旋转动画和其反向动画,不过是先运行反向动画。
spriteSister2->runAction(CCRepeat::create( CCSequence::create((CCFiniteTimeAction *)(rot2->copy()->autorelease()), (CCFiniteTimeAction *)(rot1->copy()->autorelease()), NULL), 5 ));
}

//加载RotateWorldMainLayer时调用的处理:
void RotateWorldMainLayer::onEnter()
{
	//先调用基类的相应函数。
    CCLayer::onEnter();
	//定义临时变量x,y保存窗口大小。
    float x,y;
    //取得窗口大小。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    x = size.width;
    y = size.height;
    //创建分别代表Blue,Red,Green,White四种纯色的纯色层。
    CCNode* blue =  CCLayerColor::create(ccc4(0,0,255,255));
    CCNode* red =   CCLayerColor::create(ccc4(255,0,0,255));
    CCNode* green = CCLayerColor::create(ccc4(0,255,0,255));
    CCNode* white = CCLayerColor::create(ccc4(255,255,255,255));
	//设置其各自的缩放为屏幕大小的一半。
	//蓝色,设置缩放后,设置其位置初始化时向左下移动屏幕大小四分之一。
    blue->setScale(0.5f);
    blue->setPosition(CCPointMake(-x/4,-y/4));
	//创建SpriteLayer实例放入blue层中。
    blue->addChild( SpriteLayer::create() );
    //红色,设置缩放后,设置其位置初始化时向右下移动屏幕大小四分之一。
    red->setScale(0.5f);
    red->setPosition(CCPointMake(x/4,-y/4));
	//绿色,设置缩放后,设置其位置初始化时向左上移动屏幕大小四分之一。
    green->setScale(0.5f);
    green->setPosition(CCPointMake(-x/4,y/4));
	//创建TestLayer实例放入green层中。
    green->addChild(TestLayer::create());
	//白色。,设置缩放后,设置其位置初始化时向右下移动屏幕大小四分之一。
    white->setScale(0.5f);
    white->setPosition(ccp(x/4,y/4));
    white->ignoreAnchorPointForPosition(false);
    white->setPosition(ccp(x/4*3,y/4*3));
	//将各纯色层加入当前场景。
    addChild(blue, -1);
    addChild(white);
    addChild(green);
    addChild(red);
	
	//创建一个旋转动画。8秒内从当前状态开始旋转720度。
    CCAction* rot = CCRotateBy::create(8, 720);
    //各色纯色层都运行这个动画。
    blue->runAction(rot);
    red->runAction((CCAction *)(rot->copy()->autorelease()));
    green->runAction((CCAction *)(rot->copy()->autorelease()) );
    white->runAction((CCAction *)(rot->copy()->autorelease()) );
}

//加载场景时调用的处理。
void RotateWorldTestScene::runThisTest()
{
	//创建一个RotateWorldMainLayer的CCLayer实例,并放入当前场景。
    CCLayer* pLayer = RotateWorldMainLayer::create();

    addChild(pLayer);
	//让当前场景运行在4秒内反向旋转360度的动画。
    runAction( CCRotateBy::create(4, -360) );
	//运行这个场景。
    CCDirector::sharedDirector()->replaceScene(this);

}

              现在总结一下世界是怎么转动的吧。在开始时,场景创建了四个纯色层,分别通过缩小为屏幕的一半后偏移来填充到窗口的左上,右上,左下,右下四个部分,并让它们开始绕着中心点旋转,其中蓝色层和绿色层中又各自放了一个子结点层,分别为SpriteLayerTestLayer,其中SpriteLayer中表现的是三个演员的跳跃和旋转动画,TestLayer中有一个文字标签。就这么回事。这里的关键是旋转动画的对象要弄清楚。既有精灵,也有层,也有场景,它们都属于CCNode的派生类,因为旋转动画操纵的是CCNode的变化,所以我们可以对所有基于CCNode的派生类来进行相应的动画。

分类: cocos2d, cocos2d-x 标签:

Cocos2d-x2.0 之 ClickAndMoveTest “谈不上深入”的分析.

2013年1月14日 没有评论

 

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地QQ2群:44208467加群写:Cocos2d-x 
红孩儿Cocos2d-X学习园地QQ群:249941957 [暂满]加群写:Cocos2d-x

本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!

另请转载者注明转载地址及作者。

Cocos2d-x2.0 之 ClickAndMoveTest “谈不上深入”的分析.

另:本章所用Cocos2d-x版本为:

cocos2d-2.0-x-2.0.2@ Aug 30
2012

http://cn.cocos2d-x.org/download

        大家好,经过前一阶段复杂的,长篇大论的动画部分的讲解,我们对于Cocos2d-x的基本结构有了一个大概的认识,也更加增强了我们继续将Cocos2d-x的示例学习完的信心,我们今天轻松一下,来学习一下ClickAndMoveTest这个例子。因为这个示例代码较短,谈不上深入,所以之后会再讲解一篇RotateWorldTest,以满足大家的胃口。

 

打开工程中TestCpp下的ClickAndMoveTest目录,可以看到两个文件:ClickAndMove.h和cpp。

打开ClickAndMove.h:

与之前一样,建立了一个场景,并在场景中创建所需要表现的CCLayer。

class ClickAndMoveTestScene : public TestScene
{
public:
    virtual void runThisTest();
};

class MainLayer : public CCLayer
{
public:
MainLayer();
    virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);
};

     可以看到,这里面最关键的是在MainLayer中重载了基类的相应接口函数:

virtualvoid ccTouchesEnded(CCSet*pTouches, CCEvent *pEvent);

         CCLayer层本身由CCTouchDelegate派生,CCTouchDelegate是触点消息响应接口类,它指定了CCLayer在响应触点事件时所触发的函数。

class CC_DLL CCTouchDelegate
{
public:
	//构造
    CCTouchDelegate() {}
	//析构
    virtual ~CCTouchDelegate()
    {
    }
	//单点触屏事件响应
	//按下
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent); return false;};
//移动
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);}
//停止移动
    virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);}
	//离开
    virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);}

    //多点触屏事件响应,事件与上面一致。
     virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);}
     virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);}
     virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);}
     virtual void ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent) {CC_UNUSED_PARAM(pTouches); CC_UNUSED_PARAM(pEvent);}

};

    看一下这些接口,如果我们需要增加相应的触屏事件处理,只需要在CCLayer的派生类中重载这些接口函数就OK了。

而MainLayer重载的virtualvoid ccTouchesEnded(CCSet *pTouches, CCEvent*pEvent);

即为多点触屏事件中的停止移动时触发的回调函数。

我们继续分析CPP文件:

//定义一个枚举值为做精灵的tag。
enum
{
    kTagSprite = 1,
};
//场景运行时调用的函数。
void ClickAndMoveTestScene::runThisTest()
{
	//实例化一个MainLayer并设置由内存管理器进行引用计数管理。
    CCLayer* pLayer = new MainLayer();
    pLayer->autorelease();
	//将其放入当前场景中并运行这个场景。
    addChild(pLayer);
    CCDirector::sharedDirector()->replaceScene(this);
}
//MainLayer的构造函数。
MainLayer::MainLayer()
{
	//这个是打开当前CCLayer的触屏事件响应。
    setTouchEnabled(true);
    //创建一个男一号精灵。
    CCSprite* sprite = CCSprite::create(s_pPathGrossini);
    //创建一个纯色CCLayer,设置其初值色。
    CCLayer* layer = CCLayerColor::create(ccc4(255,255,0,255));
	//降低一层放在当前CCLayer中。
    addChild(layer, -1);
    //将精灵放在上面的纯色CCLayer之上。
    addChild(sprite, 0, kTagSprite);
	//设置精灵的初始位置。
    sprite->setPosition( CCPointMake(20,150) );
    //精灵运行一个4秒内跳跃4次到指定位置的动画。
    sprite->runAction( CCJumpTo::create(4, CCPointMake(300,48), 100, 4) );
    //纯色层运行一个无限循环的动画序列,这个动画序列中是两个动画,分别为渐亮和渐暗动画。
    layer->runAction( CCRepeatForever::create( 
                                                        (CCActionInterval*)( CCSequence::create(    
                                                                            CCFadeIn::create(1),
                                                                            CCFadeOut::create(1),
                                                                            NULL) )
                                                        ) ); 
}
//多点触屏事件中的停止移动时触发的回调函数。
void MainLayer::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
{
	//取得多点中第一个触点。
    CCSetIterator it = pTouches->begin();
    CCTouch* touch = (CCTouch*)(*it);
    
	//取得其位置。
    CCPoint location = touch->getLocation();
	//通过TAG值取得相应的精灵。
    CCNode* s = getChildByTag(kTagSprite);
	//让精灵停止所有动作。
    s->stopAllActions();
	//让精灵运行一个新的移动动画,在1秒内移动一触点位置。
    s->runAction( CCMoveTo::create(1, CCPointMake(location.x, location.y) ) );
	//取得触点位置与精灵的位置的方向。
    float o = location.x - s->getPosition().x;
    float a = location.y - s->getPosition().y;
	//通过斜率计算出角度。
    float at = (float) CC_RADIANS_TO_DEGREES( atanf( o/a) );
    //让人物头的方向始终朝着终点。
    if( a < 0 ) 
    {
        if(  o < 0 )
            at = 180 + fabs(at);
        else
            at = 180 - fabs(at);    
    }
    //让精灵运行一个1秒内旋转相应角度的动画。
    s->runAction( CCRotateTo::create(1, at) );
}

这个示例比较简单,大家也可以参考《Cocos2d-x 2.0 百例精讲:如何让一个精灵跟随触点移动》一文进行学习。

分类: cocos2d, cocos2d-x 标签:

What is the alternative method for the “self.isTouchEnabled ” in Cocos2d 2.0?

2013年1月13日 没有评论

When I use the

self.isTouchEnabled = YES

in Cocos2d v2.0 for the layer could be touched,the Xcode give me a tip:

setIsTouchEnabled: is deprecated

Now, I just want to know alternative method for isTouchEnabled.

Here is new code:

    self.touchEnabled = true; //In Cocos2d 2.0

    self.userInteractionEnabled = true; //In Cocos2d 3.0

Just wondering why genius people(Cocos2d team) doing this kind of silly change!

But this one is the right call to setup touch in Cocos2d 2.x and above !!!

It seems the setIsTouchEnabled is located in “CCDeprecated.h” now.

The following method is in the current “CCLayer.h”:

[self setTouchEnabled:YES]
分类: cocos2d, stackoverflow精选 标签:

Admob banner integration in Cocos2d 2.0 / Admob banner in iphone games

2013年1月11日 没有评论

Does anybody know how to make work admob in cocos 2d v2, all the documentation is based in a view root controller and cocos2d 2 go just in another way.

The only documentation I found was this: Working-with-admob-and-cocos2d but its a little poor for a newbie like me. If anyone can help me I appreciate too much!!

Here is my working admob cocos2d code: Copy createAdmobAds, showBannerView, hideBannerView and dismissAdView to your class.

Here is Cocos2d 3.0 Admob Sample , for Cocos2d 2.0 check below

#import "GADBannerView.h"

typedef enum _bannerType
{
    kBanner_Portrait_Top,
    kBanner_Portrait_Bottom,
    kBanner_Landscape_Top,
    kBanner_Landscape_Bottom,
}CocosBannerType;

#define BANNER_TYPE  kBanner_Landscape_Bottom //change this on need basis

@interface MyMainMenu : CCLayer
{
    GADBannerView *mBannerView;
    CocosBannerType mBannerType;
    float on_x, on_y, off_x, off_y;
}

@implementation MyMainMenu


-(void)onEnter
{
    [super onEnter];
    [self createAdmobAds];
}

-(void)onExit 
{
    [self dismissAdView];
    [super onExit];
}

-(void)createAdmobAds
 {
    mBannerType = BANNER_TYPE;

    AppController *app =  (AppController*)[[UIApplication sharedApplication] delegate];
    // Create a view of the standard size at the bottom of the screen.
    // Available AdSize constants are explained in GADAdSize.h.

    if(mBannerType <= kBanner_Portrait_Bottom)
        mBannerView = [[GADBannerView alloc] initWithAdSize:kGADAdSizeSmartBannerPortrait];
    else
        mBannerView = [[GADBannerView alloc] initWithAdSize:kGADAdSizeSmartBannerLandscape];

    // Specify the ad's "unit identifier." This is your AdMob Publisher ID.
    mBannerView.adUnitID = MY_BANNER_UNIT_ID;

    // Let the runtime know which UIViewController to restore after taking
    // the user wherever the ad goes and add it to the view hierarchy.

    mBannerView.rootViewController = app.navController;
    [app.navController.view addSubview:mBannerView];

    // Initiate a generic request to load it with an ad.
    [mBannerView loadRequest:[GADRequest request]];

    CGSize s = [[CCDirector sharedDirector] winSize];

    CGRect frame = mBannerView.frame;

    off_x = 0.0f;
    on_x = 0.0f;

    switch (mBannerType)
    {
        case kBanner_Portrait_Top:
        {
            off_y = -frame.size.height;
            on_y = 0.0f;
        }
            break;
        case kBanner_Portrait_Bottom:
        {
            off_y = s.height;
            on_y = s.height-frame.size.height;
        }
            break;
        case kBanner_Landscape_Top:
        {
            off_y = -frame.size.height;
            on_y = 0.0f;
        }
            break;
        case kBanner_Landscape_Bottom:
        {
            off_y = s.height;
            on_y = s.height-frame.size.height;
        }
            break;

        default:
            break;
    }

    frame.origin.y = off_y;
    frame.origin.x = off_x;

    mBannerView.frame = frame;

    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.5];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

    frame = mBannerView.frame;
    frame.origin.x = on_x;
    frame.origin.y = on_y;


    mBannerView.frame = frame;
    [UIView commitAnimations];
}


-(void)showBannerView
{
    if (mBannerView)
    {
        [UIView animateWithDuration:0.5
                              delay:0.1
                            options: UIViewAnimationCurveEaseOut
                         animations:^
         {
             CGRect frame = mBannerView.frame;
             frame.origin.y = on_y;
             frame.origin.x = on_x;

             mBannerView.frame = frame;
         }
                         completion:^(BOOL finished)
         {
         }];
    }

}


-(void)hideBannerView
 {
    if (mBannerView)
    {
        [UIView animateWithDuration:0.5
                              delay:0.1
                            options: UIViewAnimationCurveEaseOut
                         animations:^
         {
             CGRect frame = mBannerView.frame;
             frame.origin.y = off_y;
             frame.origin.x = off_x;
         }
                         completion:^(BOOL finished)
         {
         }];
    }

}

-(void)dismissAdView
 {
    if (mBannerView) 
    {
        [UIView animateWithDuration:0.5
                              delay:0.1
                            options: UIViewAnimationCurveEaseOut
                         animations:^
         { 
             CGRect frame = mBannerView.frame;
             frame.origin.y = off_y;
             frame.origin.x = off_x;
             mBannerView.frame = frame;
         } 
                         completion:^(BOOL finished)
         {
             [mBannerView setDelegate:nil];
             [mBannerView removeFromSuperview];
             mBannerView = nil;

         }];
    }
}
分类: cocos2d, stackoverflow精选 标签:

Android 水果机游戏实例解析

2013年1月9日 没有评论

 [Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http://blog.csdn.net/honghaier]  

       Android 水果机游戏实例解析

         近段时间在手游方面的学习主要集中在Cocos2d-x上。有引擎的确是好哇!不用再像过去费劲的使用Android上底效的SurfaceView或者用OpenGL ES一点一点去建立底层框架。今天以我在此前学Android时自已所策划编写的一个小工程来记念一下,告别那段时光。

      这个游戏名叫“水果机”,80后不少同好都在街厅中玩过吧。还是喜欢下注“苹果”。在移植到Android上后,我增加了一些内容并提供了双人对战以丰富它的玩法。下面我来介绍一下:

      玩家运行程序后。首先是主界面:

Android 水果机游戏实例解析

为了吸引人气,用了一张美眉(网上找的图自已PS的),宅男福利,我运行调试时也总要多看两眼。QQ群已经不在了。不必再加入。

主界面上有菜单四项:

“开始游戏”点击后进入游戏。一会儿讲。

“游戏设置”点击后进入设置界面。这里可以选择单人还是双人玩,在单人玩法中是否弹出打气球的随机奖励以及音效和音乐的设置。“打气球”是我在此基础上的一种创新吧,就是单人玩时每次轮盘经过最下面中间位置时会随机飞出来一些气球,可以选择相应水果位置飞出一些飞镖来击中它爆出彩蛋奖励!

Android 水果机游戏实例解析

“游戏帮助”: 点击后进入帮助界面。

Android 水果机游戏实例解析

“退出游戏”点击后关闭程序退出。

咱们直接选“开始游戏”,即进入游戏界面:

单人玩法:

Android 水果机游戏实例解析

这是一个单人游戏的游戏界面,最上方是筹码显示,初时都是10个。中间是轮盘,下方是下注区。现在玩家可以下注了,在下方水果下注区域(黄或绿区)中触屏,可以在相应水果档里下想要的注数。点击中间的手形按钮可以开始转动轮盘。

在轮盘每次经过最下方中间位置,会飞出一个气球,玩家可以点击下注区的水果以控制轮盘的相应水果位置向中间射出飞镖,击中有随机奖励:

Android 水果机游戏实例解析

双人对战玩法:

Android 水果机游戏实例解析

这是一个双人对战的游戏界面,最上方是两个人的筹码,初时都是10个。中间是轮盘,下方是下注区。现在1号玩家可以下注了,在下方水果下注区域(黄或绿区)中触屏,可以在相应水果档里下想要的注数。点击中间的手形按钮可以让二号玩家开始下注,都下完注后点击中间的手会开始转动轮盘。

中奖后会有提示继续。

Android 水果机游戏实例解析

      好的,游戏介绍完了,咱们现在来学习一下如何进行开发:

工程共有11个源文件组成:

Android 水果机游戏实例解析

介绍一下:

WinnerActivity.java:主程序框架。

WinnerRecord.java:筹码计数器。

Apple.java:主游戏逻辑框架

AppThread.java:多线程更新逻辑。

AppleView.java:游戏视图。

DrawThread.java:多线程更新

FeiBiao.java:飞镖类

FeiBiaoThread.java:多线程更新飞镖逻辑。

QiQiu.java:气球类

QiQiuThread.java: 多线程更新气球逻辑。

Tile.java:轮盘的格子类。

咱们就按这个清单来讲解代码:

一. WinnerActivity.java:主程序框架。

package win.gam;

import win.gam.AppleView;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class WinnerActivity extends Activity {
    /** Called when the activity is first created. */
	AppleView appleview;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        requestWindowFeature(Window.FEATURE_NO_TITLE);		//设置不显示标题
        getWindow().setFlags(									//设置为全屏模式
        		WindowManager.LayoutParams.FLAG_FULLSCREEN, 
        		WindowManager.LayoutParams.FLAG_FULLSCREEN);
        appleview = new AppleView(this);	//创建BallView对象
        setContentView(appleview);			//将屏幕设置为BallView对象
    }
  }

二.WinnerRecord.java:筹码计数器。

package win.gam;

import android.graphics.Color;

public class WinnerRecord {
	
	int		m_WinnerID;				//ID
	int		m_IconSize;				//我的金币数量
	int		m_AppleRecord[];		//对应的水果下注数量
	int		m_Color;				//色彩
	//构造函数
	public	WinnerRecord(int vWinnerID)
	{
		m_WinnerID = vWinnerID ;
		switch(m_WinnerID)
		{
		case 0:
			m_Color = Color.YELLOW ;
			break;
		case 1:
			m_Color = Color.GREEN;
			break;
		case 2:
			m_Color = Color.BLUE;
			break;
		case 3:
			m_Color = Color.WHITE;
			break;
		}
		m_AppleRecord = new int[8];
		for(int i = 0 ; i < 8 ; i++)
		{
			m_AppleRecord[i] = 0;
		}
		m_IconSize = 0;
	}
	//取得色彩
	public	int		GetColor()
	{
		return m_Color;
	}
	//设置金币数量
	public	void	SetIconSize(int vIconSize)
	{
		m_IconSize = vIconSize;
	}
	//取得金币数量
	public	int		GetIconSize()
	{
		return	m_IconSize;
	}
	//增加金币数量
	public	void	AddIcon(int vAddIconSize)
	{
		m_IconSize += vAddIconSize;
	}
	//减少金币数量
	public	void	DecIcon()
	{
		if(m_IconSize>0)
		{
			m_IconSize--;
		}
	}
	//清空金币
	public	void	CleanIcon()
	{
		m_IconSize = 0;
	}
	//设置对应的水果的数量
	public	void	SetRecord(int vAppleIndex,int vRecord)
	{
		m_AppleRecord[vAppleIndex] = vRecord;
	}
	public	int		GetRecord(int vAppleIndex)
	{
		return m_AppleRecord[vAppleIndex];
	}
	//增加对应的水果的数量
	public	void	AddRecord(int vAppleIndex)
	{
		if(m_IconSize > 0)
		{
			m_AppleRecord[vAppleIndex]++;
			m_IconSize--;
		}
	}
	
	//清空对应的水果数量
	public	void	CleanRecord(int vAppleIndex )
	{
		if(vAppleIndex >= 0)
		{
			m_AppleRecord[vAppleIndex] = 0;
		}
		else
		{
			for(int i = 0 ; i < 8 ; i++)
			{
				m_AppleRecord[i] = 0;
			}
		}
	}
}

三.Apple.java:主游戏逻辑框架

package win.gam;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import win.gam.WinnerActivity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.os.Handler;
import android.os.Message;

public class Apple 
{
	private static final Bitmap Bitmap = null;
	
	final	int		INITSTATE		= 0;						//初始状态
	final	int		SETTINGSTATE	= 1;						//游戏设置
	final	int		HELPSTATE		= 2;						//游戏帮助
	final	int		GAMESTATE		= 3;						//游戏中
	final	int		GAMEEXIT		= 4;						//游戏退出
	
	Bitmap			m_WelcomeUI		= null;						//欢迎界面
	Bitmap			m_BitMapArray[] = null;						//位置	
	final	int		m_AppleBitMapSize 	= 17;					//数量
	int				m_GameState		= INITSTATE;				//初始状态
	
	float			m_HelpTextMinTop = 70;						//帮助状态时的最小文字位置
	float			m_HelpTextTop	 = m_HelpTextMinTop;		//帮助状态时的文字Y位置
	
	int				m_Winsize[];								//大
	WinnerActivity  winnerActivity;								//视窗
	Tile			appleTileArray[];							//水果格子
	float			m_ScreenWidth;								//屏幕宽度
	float			m_ScreenHeight;								//屏幕高度
	int 			m_TileCows = 8; 							//列数
	int 			m_TileRows  = 7;							//行数,不算头尾
	int				m_TileW = 30;								//格子宽
	int 			m_TileH = 30;								//格子高
	
	int				m_Left	=  0;								//左上角X
	int				m_Top	=  30;								//左上角Y
	
	int				m_TileCount = 0;							//格子总数
	int				m_AppleImageArray[];						//水果图ID数组
	WinnerRecord	m_WinnerRecord[];							//玩家
	Paint 			m_WinnerPaint[];							//创建画笔对象
	int				m_WinnerSize    = 1;						//玩家数量
	final	int		m_MaxWinnerSize = 2;						//最大玩家数量
	int				m_CurrWinnerID  = 0;						//当前玩家ID
	int				m_InitIconSize  = 10;						//初始金币数量
	int				m_ShowIconHeight= 60;						//显示玩家下注的高度
	int				m_ShowIconTop	= 0;						//显示玩家下注时的位置
	int				m_ShowStartBtnX = 0;						//显示GO按钮的位置X
	int				m_ShowStartBtnY = 0;						//显示GO按钮的位置Y
	
	double			m_GameExitTime  = 0;						//获取当前时间,单位为纳秒
	double  		m_currenttime 	= 0;						//记录当前时间
	double			m_starttime 	= 0;						//开始的时间
	double			m_TimeLength 	= 0;						//间隔时间
	int				m_currentpathid = 0;  						//路径ID
	
	double			m_Timer			= 1000;						//每隔多久前进一次
	double			m_DecTimer		= 10;						//加速度
	double			m_MinTimer		= 20;						//最小时间间隔
	
	boolean			m_bStartRun		= false;					//开始运动
	Matrix			m_BackGroundMatrix  = new Matrix();
	Matrix			m_CenterMatrix  = new Matrix();
	float			m_ScaleW			= 1.0f;
	float			m_ScaleH			= 1.0f;
	
	int				m_HandIndex_apple	= -1;					//手的显示索引_对应水果
	int				m_HandIndex_winner  = -1;					//手的显示索引_对应玩家
	AppleThread		m_appleThread  		= null;					//水果线程
	MediaPlayer 	m_BackMusic;								//背景音乐
	SoundPool	 	m_SoundPool;								//音效控制器
	HashMap<Integer,Integer>	m_SoundPoolMap;					//音效容器
	
	boolean			m_SoundOpen			= true;					//音效开关
	boolean			m_MusicOpen			= true;					//声音天并
	boolean			m_QiQiuOpen			= true;					//是否弹出气球
	
	int				m_RunNum			= 0;					//运行次数
	public	QiQiu	m_QiQiu				= null;					//气球
	QiQiuThread		m_QiQiuThread		= null;					//气球线程
	public	boolean	m_QiQiuIsLive		= false;				//气球是否可用
	public	ArrayList<FeiBiao> m_FeiBiaoArray = new ArrayList<FeiBiao>();	//飞镖对象数组
	FeiBiaoThread	m_FeiBiaoThread		= null;					//飞镖线程
	
	int				m_ShowBaoWu			= -1;					//宝物
	int				m_WinnerAppleID		= -1;					//胜利者

	boolean			m_ShowWinner		= true;					//显示胜利者
	Random			random  			= new Random();			//随机数
	
	int				m_WinAllApple		= 0;					//0没意义,1所有水果都中奖,2中奖水果X2倍
	final	int		m_WinAllAppleID		= 777;					//都中奖
	
	Paint 			m_UITitle			= new Paint();			//创建UI的标题画笔对象
	int				m_UITitleHeight		= 0;					//创建UI时的标题字体大小
	Paint 			m_UIText			= new Paint();			//创建UI的文字画笔对象t		
	int				m_UITextHeight 		= 0;					//创建UI的文字画笔字体大小
	//Paint			m_UIText2			= new Paint();			
	//用来更新多线程的消息
	Handler myHandler = new Handler()
	{
        public void handleMessage(Message msg) 
        {
        	if(msg.what == 1)
        	{	//收到放气球的消息
        		PutOutQiQiu();	
        	} 
        	if(msg.what == 2)
        	{	//返回主界面
        		m_GameState	= INITSTATE ;
        	} 
        }
	}; 
	//构造函数
	Apple(WinnerActivity 	Activity,int vScreenWidth,int vScreenHeight)
	{
		winnerActivity 	= Activity;
		m_ScreenWidth  	= vScreenWidth;									//屏幕宽度
		m_ScreenHeight 	= vScreenHeight;		
		//屏幕高度
		m_ScaleW		= m_ScreenWidth/240.0f ;
		m_ScaleH		= m_ScreenHeight/400.0f ;
		m_TileW 		= (int) (m_ScaleW * 30);						//格子宽
		m_TileH 		= (int) (m_ScaleH * 32);						//格子高
		m_Left			=  (int) (0 * m_ScaleW);						//左上角X
		m_Top			=  (int) (30* m_ScaleH);						//左上角Y
		m_HelpTextMinTop = 70* m_ScaleH;								//帮助状态时的最小文字位置
		m_HelpTextTop	 = m_HelpTextMinTop;							//帮助状态时的文字Y位置	
		InitApple(m_InitIconSize);
	}

	//初始化
	public void  InitApple(int vIconSize)
	{      
		m_BackMusic = MediaPlayer.create(winnerActivity, R.raw.back);//背景音乐
		//m_IconSound = MediaPlayer.create(winnerActivity, R.raw.icon);//放金币的音乐
		m_SoundPool = new SoundPool(4,AudioManager.STREAM_MUSIC,100);
		m_SoundPoolMap = new HashMap<Integer,Integer>();
		m_SoundPoolMap.put(1, m_SoundPool.load(winnerActivity, R.raw.icon,1));	//加金币
		m_SoundPoolMap.put(2, m_SoundPool.load(winnerActivity, R.raw.path,1));  //路径
		m_SoundPoolMap.put(3, m_SoundPool.load(winnerActivity, R.raw.fly,1));   //飞镖 
		m_SoundPoolMap.put(4, m_SoundPool.load(winnerActivity, R.raw.bomb,1));  //打破气球
		m_SoundOpen = true;
		m_MusicOpen = true;
		m_QiQiuOpen = true;
		//开始播放背景音乐
		if(true == m_MusicOpen)
		{
			m_BackMusic.start();
		}
		//玩家管理器
		m_WinnerRecord = new WinnerRecord[m_MaxWinnerSize];
		m_WinnerPaint  = new Paint[m_MaxWinnerSize];
		for(int i = 0 ; i < m_MaxWinnerSize ; i++)
		{
			//玩家
			m_WinnerRecord[i] = new WinnerRecord(i);
			m_WinnerRecord[i].SetIconSize(vIconSize);
			
			//字体
			m_WinnerPaint[i] = new Paint();
			String familyName_Player = "宋体";
			Typeface font_Player = Typeface.create(familyName_Player, Typeface.BOLD);
			m_WinnerPaint[i].setTypeface(font_Player);
			m_WinnerPaint[i].setColor(m_WinnerRecord[i].GetColor());				//为画笔设置颜色
			m_WinnerPaint[i].setTextSize(16* m_ScaleH);					//为画笔设置字体大小
			m_WinnerPaint[i].setAntiAlias(true);				//设置抗锯齿 
		}

		m_WelcomeUI   = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.ui);//欢迎界面
		
		int tBitMapSize = 50;
		m_BitMapArray = new Bitmap[tBitMapSize];

		m_BitMapArray[0] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.a1);//苹果0
		m_BitMapArray[1] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.a2);//桔子1
		m_BitMapArray[2] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.a3);//橄榄2
		m_BitMapArray[3] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.a4);//铃铛3
		m_BitMapArray[4] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.a5);//西瓜4
		m_BitMapArray[5] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.a6);//双星5
		m_BitMapArray[6] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.a7);//双7 6
		m_BitMapArray[7] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.a8);//黑板 7
		
		m_BitMapArray[8] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.b1);//苹果 8
		m_BitMapArray[9] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.b2);//桔子 9
		m_BitMapArray[10] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.b3);//橄榄 10
		m_BitMapArray[11] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.b4);//铃铛 11
		m_BitMapArray[12] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.b5);//西瓜 12
		m_BitMapArray[13] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.b6);//双星 13
		m_BitMapArray[14] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.b7);//双7 14
		m_BitMapArray[15] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.b8);//黑板  15
		
		m_BitMapArray[16] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.a0);//招财进宝 16
		
		m_BitMapArray[17] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.gp);//绿点 17
		m_BitMapArray[18] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.rp);//红点 18
		
		m_BitMapArray[19] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.c1);//苹果 19
		m_BitMapArray[20] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.c2);//桔子 20
		m_BitMapArray[21] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.c3);//橄榄 21
		m_BitMapArray[22] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.c4);//铃铛22
		m_BitMapArray[23] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.c5);//西瓜 23
		m_BitMapArray[24] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.c6);//双星 24
		m_BitMapArray[25] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.c7);//双7 25
		m_BitMapArray[26] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.c8);//黑板26
		
		//手的类型
		m_BitMapArray[27] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.hand1);//黄手
		m_BitMapArray[28] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.hand2);//绿手
		m_BitMapArray[29] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.hand3);//蓝手
		m_BitMapArray[30] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.hand4);//白手
		
		//背景图
		m_BitMapArray[31] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.background1);//背景
		//GO按钮
		m_BitMapArray[32] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.go);//开始
		//金币
		m_BitMapArray[33] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.icon);//金币
		
		//气球
		m_BitMapArray[34] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.qq1);//气球1
		m_BitMapArray[35] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.qq2);//气球2
		m_BitMapArray[36] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.qq3);//气球3
		m_BitMapArray[37] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.qq4);//气球4
		m_BitMapArray[38] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.qq5);//气球5
		m_BitMapArray[39] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.qq6);//气球6
		
		//箭头
		m_BitMapArray[40] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.jt1);//箭头1右
		m_BitMapArray[41] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.jt1_2);//箭头1左
		m_BitMapArray[42] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.jt2);//箭头2右
		m_BitMapArray[43] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.jt2_2);//箭头2左
		
		//开始按钮
		m_BitMapArray[44] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.start);//开始按钮
		//设置按钮
		m_BitMapArray[45] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.setting);//设置按钮
		//帮助按钮
		m_BitMapArray[46] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.help);//帮助按钮
		//退出按钮
		m_BitMapArray[47] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.exit);//退出按钮
		//菜单按钮
		m_BitMapArray[48] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.menu);//菜单按钮
		//中奖钱袋
		m_BitMapArray[49] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.money);//中奖钱袋
		//胜数
		m_Winsize = new int[m_AppleBitMapSize];
		m_Winsize[0] = 5;
		m_Winsize[1] = 10;
		m_Winsize[2] = 15;
		m_Winsize[3] = 20;
		m_Winsize[4] = 20;
		m_Winsize[5] = 30;
		m_Winsize[6] = 40;
		m_Winsize[7] = 100;
		
		m_Winsize[8] = 2;
		m_Winsize[9] = 2;
		m_Winsize[10] = 2;
		m_Winsize[11] = 2;
		m_Winsize[12] = 2;
		m_Winsize[13] = 2;
		m_Winsize[14] = 2;
		m_Winsize[15] = 50;
		
		m_Winsize[16] = 0;
		
		m_bStartRun     = false;
		
		//设置缩放
		m_BackGroundMatrix.reset();
		float	bW1 = m_WelcomeUI.getWidth() ;
		float	bH1 = m_WelcomeUI.getHeight() ;
		float	bW2 = m_ScreenWidth ;
		float	bH2 = m_ScreenHeight;
		float	tScaleW = bW2/bW1;
		float	tScaleH = bH2/bH1;
		m_BackGroundMatrix.postScale(tScaleW, tScaleH);
		
		String familyName = "宋体";
		Typeface font = Typeface.create(familyName, Typeface.BOLD);
		m_UITitle.setTypeface(font);
		m_UITitle.setColor(Color.BLACK);			//为画笔设置颜色
		m_UITitleHeight = (int) (20 * m_ScaleH);
		m_UITitle.setTextSize(m_UITitleHeight);		//为画笔设置字体大小
		m_UITitle.setAntiAlias(true);				//设置抗锯齿 
		
		
		String familyName2 = "宋体";
		Typeface font2 = Typeface.create(familyName2, Typeface.BOLD);
		m_UIText.setTypeface(font2);
		m_UIText.setColor(Color.BLACK);				//为画笔设置颜色
		m_UITextHeight = (int) (16 * m_ScaleH);
		m_UIText.setTextSize(m_UITextHeight);		//为画笔设置字体大小
		m_UIText.setAntiAlias(true);				//设置抗锯齿 
		
		//创建一个气球
		m_QiQiu = new QiQiu();
		//创建气球线程
		m_QiQiuThread = new QiQiuThread(this);
		//创建飞标线程
		m_FeiBiaoThread = new FeiBiaoThread(this);
		//创建线程
		m_appleThread = new AppleThread(this);
		//启动线程
		StartThread();
	}
	//初始化游戏界面
	public void  InitGameUI()
	{
		if( 1 == m_WinnerSize )
		{
			m_ShowIconHeight = (int) (60 * m_ScaleH) ;
		}
		else
		{
			m_ShowIconHeight = (int) (80 * m_ScaleH) ;
		}
		m_ShowIconTop = (int) (m_ScreenHeight - m_ShowIconHeight);	//显示玩家下注的位置高度
		
		m_CurrWinnerID = 0;
		m_InitIconSize = 10;										//初始时10个金币
		m_RunNum = 0;

		m_CenterMatrix.reset();
		float bW1 = m_BitMapArray[31].getWidth() ;
		float bH1 = m_BitMapArray[31].getHeight() ;
		float bW2 = m_ScreenWidth ;
		float bH2 = m_ScreenHeight - m_ShowIconHeight;
		float tScaleW = bW2/bW1;
		float tScaleH = bH2/bH1;
		m_CenterMatrix.postScale(tScaleW, tScaleH);
		
		//计算格子行数与列数
		m_TileCows = (int) (m_ScreenWidth / m_TileW);
		m_TileRows = (int) ((m_ScreenHeight - m_Top - m_ShowIconHeight) / m_TileH - 2); //上下两行的要去掉
				
		//格子数量
		m_TileCount = m_TileCows * 2 + m_TileRows * 2;
		m_AppleImageArray = new int[m_TileCount];
				
		
		//先随机生成格子盘面
		for(int i = 0 ; i < m_TileCount ; i++)
		{
			m_AppleImageArray[i] = random.nextInt(m_AppleBitMapSize);	//起始位置
			
			if(m_AppleImageArray[i] == 7 || m_AppleImageArray[i] == 15)
			{
				m_AppleImageArray[i] = 0;
				continue;
			}
			else if(m_AppleImageArray[i] == 6||m_AppleImageArray[i] == 14)
			{
				m_AppleImageArray[i] = 0;
				continue;
			}
			else if(m_AppleImageArray[i] == 5||m_AppleImageArray[i] == 13)
			{
				m_AppleImageArray[i] = 0;
				continue;
			}
			else if(m_AppleImageArray[i] == 4||m_AppleImageArray[i] == 12)
			{
				m_AppleImageArray[i] = 0;
				continue;
			}			
		}
				
		//两边中央必须是招财进宝
		int	tTheTileIndex = m_TileCows + m_TileRows / 2;
		m_AppleImageArray[tTheTileIndex] = 16 ;
				
		tTheTileIndex = 2 * (m_TileCows + m_TileRows) - m_TileRows / 2;
		m_AppleImageArray[tTheTileIndex] = 16 ;
				
		//上边中央必须是大小黑板
		tTheTileIndex =  m_TileCows / 2;
		m_AppleImageArray[tTheTileIndex-1] = 15 ;
		m_AppleImageArray[tTheTileIndex] = 7 ;
				
		//下边中央必须是大小七
		tTheTileIndex =  2 * m_TileCows + m_TileRows - m_TileCows / 2;
		m_AppleImageArray[tTheTileIndex+1] = 14 ;
		m_AppleImageArray[tTheTileIndex] = 6 ;
				
				
		//右上角下边必须是大小西瓜
		tTheTileIndex =  m_TileCows ;
		m_AppleImageArray[tTheTileIndex] =  4;
		m_AppleImageArray[tTheTileIndex+1] = 12 ;
				
		//左下角上边必须是大小星
		tTheTileIndex =  2 * m_TileCows + m_TileRows ;
		m_AppleImageArray[tTheTileIndex] =  5;
		m_AppleImageArray[tTheTileIndex+1] = 13 ;
				
		//左上角是苹果
		m_AppleImageArray[0] = 0 ;
		//左1是小苹果
		m_AppleImageArray[1] = 8 ;
				
		//右上角是桔子
		m_AppleImageArray[m_TileCows-1] = 1 ;
		m_AppleImageArray[m_TileCows-2] = 9 ;
		
		//左下角是橄榄
		m_AppleImageArray[2 * m_TileCows + m_TileRows-1] = 2 ;
		m_AppleImageArray[2 * m_TileCows + m_TileRows-2] = 10 ;
				
		//右下角是铃铛
		m_AppleImageArray[m_TileCows + m_TileRows] = 3 ;
		m_AppleImageArray[m_TileCows + m_TileRows+1] = 11 ;
				
		//生成格子
		appleTileArray = new Tile[m_TileCount];
		for(int i = 0 ; i < m_TileCount ; i++)
		{
			appleTileArray[i] =  new Tile();
			int	tImageIndex = m_AppleImageArray[i];
			appleTileArray[i].SetImage(tImageIndex,m_BitMapArray[tImageIndex]);
		}

		//上行
		int tCount = 0;
		for(int i = 0 ; i < m_TileCows ; i++)
		{
			appleTileArray[tCount].SetTile(i*m_TileW+m_Left,m_Top);
			tCount++;
		}
		//右列
		for(int i = 0 ; i < m_TileRows ; i++)
		{
			appleTileArray[tCount].SetTile((m_TileCows-1)*m_TileW+m_Left,(i+1)*m_TileH+m_Top);
			tCount++;
		}
		//下行
		for(int i = m_TileCows-1 ; i >= 0  ; i--)
		{
			appleTileArray[tCount].SetTile(i*m_TileW+m_Left,(m_TileRows+1)*m_TileH+m_Top);
			tCount++;
		}
		//左列
		for(int i = m_TileRows-1 ; i >= 0  ; i--)
		{
			appleTileArray[tCount].SetTile(m_Left,(i+1)*m_TileH+m_Top);
			tCount++;
		}
				
		m_starttime = System.nanoTime();//获取当前时间,单位为纳秒
		m_Timer			= 500;				//每隔多久前进一次
		m_DecTimer		= 40;				//加速度
		m_MinTimer		= 0;				//最小时间间隔
		m_currentpathid = random.nextInt(m_TileCount);	//起始位置

	}
	//退出释放
	public void	 Release()
	{
		m_BackMusic.stop();
		m_BackMusic = null;
		m_SoundPool.stop(m_SoundPoolMap.get(1));
		m_SoundPool.stop(m_SoundPoolMap.get(2));
		m_SoundPool.stop(m_SoundPoolMap.get(3));
		m_SoundPool.stop(m_SoundPoolMap.get(4));
		m_SoundPool = null;
		m_SoundOpen = false;
		m_MusicOpen = false;
	}
	//取得屏幕宽度
	public float	GetScreenWidth()
	{
		return m_ScreenWidth ;
	}
	//取得屏幕高度
	public float	GetScreenHeight()
	{
		return m_ScreenHeight;
	}
	
	//播放音效
	public void	PlaySound(int sound,int loop)
	{     
		if(true == m_SoundOpen)
		{
			AudioManager mgr = (AudioManager)winnerActivity.getSystemService(Context.AUDIO_SERVICE);
			float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
			float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
			float volume = streamVolumeCurrent / streamVolumeMax;
			//播放音效
			m_SoundPool.play(m_SoundPoolMap.get(sound), volume, volume, 1, loop, 1f);
		}
	}
	//设置玩家人数
	public void  SetWinnerSize(int vWinnerSize)
	{
		m_WinnerSize = vWinnerSize;
	}
	//打开气球
	public void  EnableQiQiu(boolean vQiQiuOpen)
	{
		m_QiQiuOpen = vQiQiuOpen ;
	}
	//打开音效
	public void  EnableSound(boolean vSoundOpen)
	{
		m_SoundOpen = vSoundOpen;
	}
	//打开音乐
	public void  EnableMusic(boolean vMusicOpen)
	{
		m_MusicOpen = vMusicOpen;
		if(true == m_MusicOpen)
		{
			m_BackMusic.start();
		}
		else
		{
			m_BackMusic.pause();
		}
	}
	//播放金币声音
	public void  PlayIconSound()
	{
        PlaySound(1,0);
	}
	//播放路径声音
	public void  PlayPathSound()
	{
        PlaySound(2,0);
	}
	//播放飞镖声音
	public void  PlayFeiBiaoSound()
	{
        PlaySound(3,0);
	}
	//播放打破气球声音
	public void  PlayBombSound()
	{
        PlaySound(4,0);
	}
	//改变窗口大小
	public void  ChangeWindowSize(int vScreenWidth,int vScreenHeight)
	{
		m_ScreenWidth  = vScreenWidth;		//屏幕宽度
		m_ScreenHeight = vScreenHeight;		//屏幕高度
		//屏幕高度
		m_ScaleW		= m_ScreenWidth/240.0f ;
		m_ScaleH		= m_ScreenHeight/400.0f ;
		m_TileW 		= (int) (m_ScaleW * 30);						//格子宽
		m_TileH 		= (int) (m_ScaleH * 32);						//格子高
		

		m_HelpTextMinTop = (int) (70* m_ScaleH);								//帮助状态时的最小文字位置
		m_HelpTextTop	 = m_HelpTextMinTop;							//帮助状态时的文字Y位置	
		m_BackGroundMatrix.reset();
		float	bW1 = m_WelcomeUI.getWidth() ;
		float	bH1 = m_WelcomeUI.getHeight() ;
		float	bW2 = m_ScreenWidth ;
		float	bH2 = m_ScreenHeight;
		float	tScaleW = bW2/bW1;
		float 	tScaleH = bH2/bH1;
		m_BackGroundMatrix.postScale(tScaleW, tScaleH);
		
		m_CenterMatrix.reset();
		bW1 = m_BitMapArray[31].getWidth() ;
		bH1 = m_BitMapArray[31].getHeight() ;
		bW2 = m_ScreenWidth ;
		bH2 = m_ScreenHeight - m_ShowIconHeight;
		tScaleW = bW2/bW1;
		tScaleH = bH2/bH1;
		m_CenterMatrix.postScale(tScaleW, tScaleH);
	}
	//开始线程
	public void  StartThread()
	{
		if(!m_appleThread.isAlive())
		{
			m_appleThread.start();
		}
	}
	//退出线程
	public void  ExitThread()
	{
		m_appleThread.Exit();
		m_appleThread = null;
		//释放
		Release();
	}
	//更新
	public void  Update()
	{	

		if(GAMEEXIT == m_GameState)
		{
			m_currenttime = System.nanoTime();//获取当前时间,单位为纳秒
			m_TimeLength = (double)((m_currenttime - m_GameExitTime)/1000/1000);//毫秒
			if(m_TimeLength > 3000)
			{
				winnerActivity.finish();
			}
		}
		//游戏帮助
		else if(HELPSTATE == m_GameState)
		{
			m_HelpTextTop  -= 0.5f;
		}
		else if(GAMESTATE == m_GameState)
		{
			//游戏开始状态
			if(true == m_bStartRun)
			{
				m_currenttime = System.nanoTime();//获取当前时间,单位为纳秒
				m_TimeLength = (double)((m_currenttime - m_starttime)/1000/1000);//毫秒
				
				//加速变化
				if( m_TimeLength > m_Timer )
				{
					m_Timer -= m_DecTimer ;
					//加速到终点
					if(m_Timer < m_MinTimer)
					{
						m_DecTimer = -m_DecTimer;
					}
					else if(m_Timer >= 1000)
					{
						//减到到终点
						m_DecTimer = -m_DecTimer;
						m_Timer = 1000;
						m_starttime = m_currenttime ;
						m_currentpathid++;
						m_currentpathid = (m_currentpathid)%m_TileCount;
						//播放放金币声音
						PlayPathSound();
						//计算胜利结果
						CalculateWinner();
						m_bStartRun = false;
						m_ShowBaoWu = -1;
						return ;
						
					}
					
					m_starttime = m_currenttime ;
					m_currentpathid++;
					m_currentpathid = (m_currentpathid)%m_TileCount;
					
					//如果是招财进宝,则放气球
					int		tCurrentImageId = appleTileArray[m_currentpathid].GetImageID() ;
					if( tCurrentImageId == 16)
					{
						myHandler.sendEmptyMessage(1);//发送放气球的消息
					}
				}
			}
			else
			{
				if(m_WinnerAppleID >= 0)
				{
					m_currenttime = System.nanoTime();//获取当前时间,单位为纳秒
					m_TimeLength = (double)((m_currenttime - m_starttime)/1000/1000);//毫秒
					
					if( m_TimeLength > 500 )
					{
						m_ShowWinner = !m_ShowWinner;
						m_starttime = m_currenttime ;
					}
				}
			}
		}
		
	}
	//渲染
	public void  Draw(Canvas canvas,Paint paint)
	{
		//退出状态
		if(GAMEEXIT == m_GameState)
		{
			int		centerBitmapW = m_BitMapArray[31].getWidth();
			int		centerBitmapH = m_BitMapArray[31].getHeight();
			Bitmap	centerBitmap = Bitmap.createBitmap(m_BitMapArray[31],0,0,centerBitmapW,centerBitmapH,m_BackGroundMatrix,true);
			canvas.save();
			canvas.drawBitmap(centerBitmap, 0 , 0 , paint );
			canvas.restore();
			
			//=================="游戏人数设置====================================
			String text = "正退出游戏。。。";
			canvas.drawText(text, m_ScreenWidth /2 - 50*m_ScaleW, 40 * m_ScaleH, m_UITitle) ;
			
			return ;
		}
		//初始状态
		if(INITSTATE == m_GameState)
		{	
			int		centerBitmapW = m_WelcomeUI.getWidth();
			int		centerBitmapH = m_WelcomeUI.getHeight();
			Bitmap	centerBitmap = Bitmap.createBitmap(m_WelcomeUI,0,0,centerBitmapW,centerBitmapH,m_BackGroundMatrix,true);
			canvas.save();
			canvas.drawBitmap(centerBitmap, 0 , 0 , paint );
			canvas.restore();
			
			//按钮
			int		tBtnLeft = 20;
			int		tBtnHeight = (int) (m_BitMapArray[ 44 ].getHeight()+3 * m_ScaleH);
			int		tBtnTop  = (int) (m_ScreenHeight - 4*(tBtnHeight+2)) ;
			
			for(int i = 0 ; i < 4 ; i++)
			{
				canvas.drawBitmap(m_BitMapArray[ 44 + i ], tBtnLeft + i * 40 * m_ScaleW , tBtnTop + i * tBtnHeight , paint );
			}
			
			return ;
		}
		//SETTINGSTATE	= 1;				//游戏设置
		if(SETTINGSTATE == m_GameState)
		{
			int		centerBitmapW = m_BitMapArray[31].getWidth();
			int		centerBitmapH = m_BitMapArray[31].getHeight();
			Bitmap	centerBitmap = Bitmap.createBitmap(m_BitMapArray[31],0,0,centerBitmapW,centerBitmapH,m_BackGroundMatrix,true);
			canvas.save();
			canvas.drawBitmap(centerBitmap, 0 , 0 , paint );
			canvas.restore();
			
			//=================="游戏人数设置====================================
			String text = "游 戏 设 置";
			canvas.drawText(text, m_ScreenWidth /2 - 50*m_ScaleW, 40 * m_ScaleH, m_UITitle) ;
			
			m_UIText.setColor(Color.BLACK);
			text = "游戏人数设置:";
			canvas.drawText(text, 20*m_ScaleW, 70 * m_ScaleH, m_UITitle) ;
			int	tShowConfig1 = (int) (100 * m_ScaleH);
			int tHalfUITextHeight = m_UITextHeight / 2 ;
			if( 1 ==  m_WinnerSize)
			{
				canvas.drawBitmap(m_BitMapArray[18], 40*m_ScaleW , tShowConfig1 , paint );
				text = "单人";
				canvas.drawText(text, 70*m_ScaleW, tShowConfig1 + tHalfUITextHeight , m_UIText) ;
				canvas.drawBitmap(m_BitMapArray[17], 120*m_ScaleW , tShowConfig1 , paint );
				text = "双人";
				canvas.drawText(text, 150*m_ScaleW, tShowConfig1 + tHalfUITextHeight, m_UIText) ;
			}
			else
			{
				canvas.drawBitmap(m_BitMapArray[17], 40*m_ScaleW , tShowConfig1 , paint );
				text = "单人";
				canvas.drawText(text, 70*m_ScaleW, tShowConfig1 + tHalfUITextHeight, m_UIText) ;
				canvas.drawBitmap(m_BitMapArray[18], 120*m_ScaleW , tShowConfig1 , paint );
				text = "双人";
				canvas.drawText(text, 150*m_ScaleW, tShowConfig1 + tHalfUITextHeight, m_UIText) ;
			}
			//================气球飞镖设置======================================
			text = "弹出气球设置:";
			canvas.drawText(text, 20*m_ScaleW, 140 * m_ScaleH, m_UITitle) ;
			int	tShowConfig2 = (int) (170 * m_ScaleH);
			if( true ==  m_QiQiuOpen)
			{
				canvas.drawBitmap(m_BitMapArray[18], 40*m_ScaleW , tShowConfig2 , paint );
				text = "开";
				canvas.drawText(text, 70*m_ScaleW, tShowConfig2 + tHalfUITextHeight, m_UIText) ;
				canvas.drawBitmap(m_BitMapArray[17], 120*m_ScaleW , tShowConfig2 , paint );
				text = "关";
				canvas.drawText(text, 150*m_ScaleW, tShowConfig2 + tHalfUITextHeight, m_UIText) ;
			}
			else
			{
				canvas.drawBitmap(m_BitMapArray[17], 40*m_ScaleW , tShowConfig2 , paint );
				text = "开";
				canvas.drawText(text, 70*m_ScaleW, tShowConfig2 + tHalfUITextHeight, m_UIText) ;
				canvas.drawBitmap(m_BitMapArray[18], 120*m_ScaleW , tShowConfig2 , paint );
				text = "关";
				canvas.drawText(text, 150*m_ScaleW, tShowConfig2 + tHalfUITextHeight, m_UIText) ;
			}
			//=================游戏音效设置=====================================
			text = "游戏音效设置:";
			canvas.drawText(text, 20*m_ScaleW, 210 * m_ScaleH, m_UITitle) ;
			int	tShowConfig3 = (int) (240 * m_ScaleH);
			if( true ==  m_SoundOpen)
			{
				canvas.drawBitmap(m_BitMapArray[18], 40*m_ScaleW , tShowConfig3 , paint );
				text = "开";
				canvas.drawText(text, 70*m_ScaleW, tShowConfig3 + tHalfUITextHeight, m_UIText) ;
				canvas.drawBitmap(m_BitMapArray[17], 120*m_ScaleW , tShowConfig3 , paint );
				text = "关";
				canvas.drawText(text, 150*m_ScaleW, tShowConfig3 + tHalfUITextHeight, m_UIText) ;
			}
			else
			{
				canvas.drawBitmap(m_BitMapArray[17], 40*m_ScaleW , tShowConfig3 , paint );
				text = "开";
				canvas.drawText(text, 70*m_ScaleW, tShowConfig3 + tHalfUITextHeight, m_UIText) ;
				canvas.drawBitmap(m_BitMapArray[18], 120*m_ScaleW , tShowConfig3 , paint );
				text = "关";
				canvas.drawText(text, 150*m_ScaleW, tShowConfig3 + tHalfUITextHeight, m_UIText) ;
			}
			
			//================背景音乐设置======================================
			text = "背景音乐设置:";
			canvas.drawText(text, 20*m_ScaleW, 280 * m_ScaleH, m_UITitle) ;
			int	tShowConfig4 = (int) (310 * m_ScaleH);
			if( true ==  m_MusicOpen)
			{
				canvas.drawBitmap(m_BitMapArray[18], 40*m_ScaleW , tShowConfig4 , paint );
				text = "开";
				canvas.drawText(text, 70*m_ScaleW, tShowConfig4 + tHalfUITextHeight, m_UIText) ;
				canvas.drawBitmap(m_BitMapArray[17], 120*m_ScaleW , tShowConfig4 , paint );
				text = "关";
				canvas.drawText(text, 150*m_ScaleW, tShowConfig4 + tHalfUITextHeight, m_UIText) ;
			}
			else
			{
				canvas.drawBitmap(m_BitMapArray[17], 40*m_ScaleW , tShowConfig4 , paint );
				text = "开";
				canvas.drawText(text, 70*m_ScaleW, tShowConfig4 + tHalfUITextHeight, m_UIText) ;
				canvas.drawBitmap(m_BitMapArray[18], 120*m_ScaleW , tShowConfig4 , paint );
				text = "关";
				canvas.drawText(text, 150*m_ScaleW, tShowConfig4 + tHalfUITextHeight, m_UIText) ;
			}
			
			int	tShowConfig5 = (int) (m_ScreenHeight - 30);
			text = "返回主界面";
			canvas.drawText(text, m_ScreenWidth /2 - 54*m_ScaleW, tShowConfig5, m_UITitle) ;
			return ;
		}
		//游戏帮助
		if(HELPSTATE == m_GameState)
		{
			int		centerBitmapW = m_BitMapArray[31].getWidth();
			int		centerBitmapH = m_BitMapArray[31].getHeight();
			Bitmap	centerBitmap = Bitmap.createBitmap(m_BitMapArray[31],0,0,centerBitmapW,centerBitmapH,m_BackGroundMatrix,true);
			canvas.save();
			canvas.drawBitmap(centerBitmap, 0 , 0 , paint );
			canvas.restore();
			

			
			String text = "游  戏  帮  助";
			canvas.drawText(text, m_ScreenWidth /2 - 50*m_ScaleW, 40*m_ScaleH, m_UITitle) ;	
			
			m_UIText.setColor(Color.BLACK);			//为画笔设置颜色
			int			tAreaHeight = (int) ( m_ScreenHeight - m_HelpTextMinTop + 80);
			float		tHeight = m_HelpTextTop - (tAreaHeight - 20);
			tHeight += 90 * m_ScaleH ;
			{
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "进入游戏后开始下注, ";
					canvas.drawText(text, 70*m_ScaleW, tRealPosY, m_UIText) ;
				}
			}
			tHeight += 30* m_ScaleH;
			{
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "在最下方水果上点击进行下注,";
					canvas.drawText(text, 5*m_ScaleW, tRealPosY, m_UIText) ;
				}
			}
			tHeight += 30* m_ScaleH;
			{
				
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "点击";
					canvas.drawText(text, 5*m_ScaleW, tRealPosY, m_UIText) ;
					text = " 按钮可以开始游戏转动,";
					canvas.drawText(text, 72*m_ScaleW, tRealPosY, m_UIText) ;
					
					canvas.drawBitmap(m_BitMapArray[32], 42*m_ScaleW, tRealPosY - 18* m_ScaleH ,paint );
				}
			}
			tHeight += 30* m_ScaleH;
			{
				
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "金币用完后,点击";
					canvas.drawText(text, 5*m_ScaleW, tRealPosY, m_UIText) ;
					text = " 可以投入";
					canvas.drawText(text, 165*m_ScaleW, tRealPosY, m_UIText) ;
					canvas.drawBitmap(m_BitMapArray[33], 140*m_ScaleW, tRealPosY - 15* m_ScaleH ,paint );
				}
			}
			tHeight += 30* m_ScaleH;
			{
				
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "一个金币继续游戏,转动时经过";
					canvas.drawText(text, 5*m_ScaleW, tRealPosY, m_UIText) ;
				}
			}
			tHeight += 30* m_ScaleH;
			{
				
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "图块时会从底部放出气球";
					canvas.drawText(text, 40*m_ScaleW, tRealPosY, m_UIText) ;
					canvas.drawBitmap(m_BitMapArray[16], 10*m_ScaleW, tRealPosY - 15* m_ScaleH ,paint );
				}
			}
			tHeight += 30* m_ScaleH;
			{
				
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = ",点击下注水果图片可以使左右两";
					canvas.drawText(text, 5*m_ScaleW, tRealPosY, m_UIText) ;
				}
			}
			tHeight += 30* m_ScaleH;
			{
				
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "排相应水果释放 ";
					canvas.drawText(text, 5*m_ScaleW, tRealPosY, m_UIText) ;
					text = " ,击中";
					canvas.drawText(text, 150*m_ScaleW, tRealPosY, m_UIText) ;
					canvas.drawBitmap(m_BitMapArray[40], 120*m_ScaleW, tRealPosY - 15* m_ScaleH ,paint );
					canvas.drawBitmap(m_BitMapArray[34], 205*m_ScaleW, tRealPosY - 15* m_ScaleH ,paint );
				}
			}
			tHeight += 30* m_ScaleH;
			//if(tHeight > m_HelpTextMinTop)
			{
				
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "会有意想不到的奖励哦。";
					canvas.drawText(text, 5*m_ScaleW, tRealPosY, m_UIText) ;
				}
			}
			
			tHeight += 30* m_ScaleH;
			m_UIText.setColor(Color.BLUE);			//为画笔设置颜色
			//if(tHeight > m_HelpTextMinTop)
			{
				
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "开发者:红孩儿安卓游戏乐园";
					canvas.drawText(text, 30*m_ScaleW, tRealPosY, m_UIText) ;
				}
			}
			tHeight += 30* m_ScaleH;
			//if(tHeight > m_HelpTextMinTop)
			{
				/*
				float   tRealPosY = (m_ScreenHeight-90* m_ScaleH)+(int)(tHeight)%tAreaHeight;
				if(tRealPosY > m_HelpTextMinTop && tRealPosY < (m_ScreenHeight - 90* m_ScaleH))
				{
					text = "QQ群:205100149";
					canvas.drawText(text, 30*m_ScaleW, tRealPosY, m_UIText) ;
				}
				*/
			}
			
			int	tShowConfig4 = (int) (m_ScreenHeight - 30*m_ScaleH);
			text = "返回主界面";
			canvas.drawText(text, m_ScreenWidth /2 - 54*m_ScaleW, tShowConfig4, m_UITitle) ;
			
			m_UIText.setColor(Color.BLACK);
			return ;
		}
		
		
		//=========================游戏主界面显示===========================================
		//背景图
		//===========================================================
		int		centerBitmapW = m_BitMapArray[31].getWidth();
		int		centerBitmapH = m_BitMapArray[31].getHeight();
		Bitmap	centerBitmap = Bitmap.createBitmap(m_BitMapArray[ 31 ],0,0,centerBitmapW,centerBitmapH,m_CenterMatrix,true);
		canvas.save();
		canvas.drawBitmap(centerBitmap, 0 , 0 , paint );
		canvas.restore();
		//===========================================================
		//canvas.drawBitmap(m_BitMapArray[31], 0, 0 ,paint );
		
		//显示玩家金币情况
		for(int i = 0 ; i < m_WinnerSize ; i++)
		{
			canvas.drawBitmap(m_BitMapArray[33], 8 + i * 100, 5 ,paint );
			int     tIconSize = m_WinnerRecord[i].GetIconSize();
			String	text = ""+tIconSize;
			canvas.drawText(text,( 8  + i * 100   + 30) * m_ScaleW , 20 * m_ScaleH, m_WinnerPaint[i]) ;
		}
		//显示返回菜单
		int		menuBitMapW = m_BitMapArray[48].getWidth();
		canvas.drawBitmap(m_BitMapArray[48],m_ScreenWidth - menuBitMapW - 5 , 2 * m_ScaleH ,paint );
		
		
		//显示水果种类与胜利数量
		Paint TextPaint = new Paint();				//创建画笔对象
		
		String familyName = "宋体";
		Typeface font = Typeface.create(familyName, Typeface.BOLD);
		TextPaint.setTypeface(font);
		TextPaint.setColor(Color.RED);				//为画笔设置颜色
		TextPaint.setTextSize(16* m_ScaleH);					//为画笔设置字体大小
		TextPaint.setAntiAlias(true);				//设置抗锯齿 
		
		for(int i = 0 ; i < m_TileCows ; i++)
		{
			int index = m_TileCows - 1 - i;
			String text = ""+ m_Winsize[index];
			if(i == m_TileCows - 1 )
			{
				canvas.drawText(text, i * m_TileW + 5 * m_ScaleW + m_Left, m_ScreenHeight - m_ShowIconHeight + 10* m_ScaleH, TextPaint) ;
			}
			else
			{
				canvas.drawText(text, i * m_TileW + m_Left, m_ScreenHeight - m_ShowIconHeight + 10* m_ScaleH, TextPaint) ;
			}
			canvas.drawBitmap(m_BitMapArray[index], i * m_TileW +m_Left, m_ScreenHeight - m_ShowIconHeight + 10* m_ScaleH ,paint );
		}
		

		//开始按钮
		if(false == m_bStartRun)
		{
			int tBtnW = m_BitMapArray[32].getWidth();
			int tBtnH = m_BitMapArray[32].getHeight();
			
			m_ShowStartBtnX = (int) ((m_ScreenWidth - tBtnW)/2);								//显示GO按钮的位置X
			m_ShowStartBtnY = (int) ((m_ScreenHeight - tBtnH)/2 + 20 * m_ScaleH);				//显示GO按钮的位置Y		
			
			canvas.drawBitmap(m_BitMapArray[32], m_ShowStartBtnX, m_ShowStartBtnY ,paint );
			
			String familyName2 = "黑体";
			Typeface font2 = Typeface.create(familyName2, Typeface.BOLD);
			Paint TextPaint2 = new Paint();				//创建画笔对象
			TextPaint2.setTypeface(font2);
			TextPaint2.setColor(Color.BLACK);			//为画笔设置颜色
			TextPaint2.setTextSize(16* m_ScaleH);					//为画笔设置字体大小
			TextPaint2.setAntiAlias(true);				//设置抗锯齿 
			
			if(m_WinnerSize == 1)
			{
				String text = "开始下注";
				canvas.drawText(text, m_ShowStartBtnX - 26* m_ScaleW , m_ShowStartBtnY + 50* m_ScaleH, TextPaint2) ;
			}
			else
			{
				if( m_CurrWinnerID >= 0 )
				{
					String text = "" + (m_CurrWinnerID + 1) + "号玩家下注";
					canvas.drawText(text, m_ShowStartBtnX - 36* m_ScaleW , m_ShowStartBtnY + 60* m_ScaleH, TextPaint2) ;
				}
			}
			
		}
		//中间显示
		//当前的图像ID
		int		tCurrentImageId = appleTileArray[m_currentpathid].GetImageID() ;
		if( tCurrentImageId == 16)
		{
			int tShowCurrentImageW = m_BitMapArray[tCurrentImageId].getWidth();
			int tShowCurrentImageH = m_BitMapArray[tCurrentImageId].getHeight();
			int	tShowCurrentImageX = (int) (m_ShowStartBtnX - tShowCurrentImageW); 
			int tShowCurrentImageY = (int) (m_ShowStartBtnY - tShowCurrentImageH - 10 * m_ScaleH);
			canvas.drawBitmap(m_BitMapArray[tCurrentImageId], tShowCurrentImageX , tShowCurrentImageY, paint );
			
			if( false == m_bStartRun )
			{
				Paint TextPaint2 = new Paint();				//创建画笔对象
				String familyName2 = "黑体";
				Typeface font2 = Typeface.create(familyName2, Typeface.BOLD);
				TextPaint2.setTypeface(font2);
				TextPaint2.setColor(Color.RED);				//为画笔设置颜色
				TextPaint2.setTextSize(32* m_ScaleH);					//为画笔设置字体大小
				TextPaint2.setAntiAlias(true);				//设置抗锯齿 
					
				String text = "X"+ m_Winsize[tCurrentImageId];
				canvas.drawText(text, tShowCurrentImageX + tShowCurrentImageW + 10 * m_ScaleW  , tShowCurrentImageY + tShowCurrentImageH, TextPaint2) ;
			}
		}
		else
		{
			int tShowCurrentImageW = m_BitMapArray[tCurrentImageId].getWidth();
			int tShowCurrentImageH = m_BitMapArray[tCurrentImageId].getHeight();
			int	tShowCurrentImageX = (int) (m_ShowStartBtnX - tShowCurrentImageW); 
			int tShowCurrentImageY = (int) (m_ShowStartBtnY - tShowCurrentImageH - 10 * m_ScaleH);
			int	tGetIconSize = m_Winsize[tCurrentImageId] ;
			tCurrentImageId = tCurrentImageId % 8 ;
			canvas.drawBitmap(m_BitMapArray[tCurrentImageId], tShowCurrentImageX , tShowCurrentImageY , paint );
			
			//普通图像
			if( false == m_bStartRun )
			{
				Paint TextPaint2 = new Paint();				//创建画笔对象
				String familyName2 = "黑体";
				Typeface font2 = Typeface.create(familyName2, Typeface.BOLD);
				TextPaint2.setTypeface(font2);
				TextPaint2.setColor(Color.RED);				//为画笔设置颜色
				TextPaint2.setTextSize(32* m_ScaleH);					//为画笔设置字体大小
				TextPaint2.setAntiAlias(true);				//设置抗锯齿 
					
				String text = "X"+ tGetIconSize;
				canvas.drawText(text,  tShowCurrentImageX + tShowCurrentImageW + 10 * m_ScaleW , tShowCurrentImageY + tShowCurrentImageH, TextPaint2) ;
			}
		}
		
		//显示奖励
		if(m_ShowBaoWu >= 0)
		{
			Paint TextPaint2 = new Paint();				//创建画笔对象
			String familyName2 = "黑体";
			Typeface font2 = Typeface.create(familyName2, Typeface.BOLD);
			TextPaint2.setTypeface(font2);
			TextPaint2.setColor(Color.RED);				//为画笔设置颜色
			TextPaint2.setTextSize(20* m_ScaleH);					//为画笔设置字体大小
			TextPaint2.setAntiAlias(true);				//设置抗锯齿 
				
			int	tShowText_X = (int) (m_ScreenWidth / 2 );
			int tShowText_Y = (int) (m_ScreenHeight/ 2 + 30 * m_ScaleH);
			
			switch(m_ShowBaoWu)
			{
			case 0://增加一个金币
				{
					String text = "增加一个金币";
					tShowText_X -= 70;
					canvas.drawText(text, tShowText_X , tShowText_Y, TextPaint2) ;
				}
				break;
			case 1://扣除一个金币
				{
					String text = "扣除一个金币";
					tShowText_X -= 70;
					canvas.drawText(text, tShowText_X , tShowText_Y, TextPaint2) ;
				}
				break;
			case 2://增加二个金币
				{
					String text = "增加二个金币";
					tShowText_X -= 70;
					canvas.drawText(text, tShowText_X , tShowText_Y, TextPaint2) ;
				}
				break;
			case 3://扣除二个金币
				{
					String text = "扣除二个金币";
					tShowText_X -= 70;
					canvas.drawText(text, tShowText_X , tShowText_Y, TextPaint2) ;
				}
				break;
			case 4://再持续一圈
				{
					String text = "再发力转动";
					tShowText_X -= 60;

					canvas.drawText(text, tShowText_X , tShowText_Y, TextPaint2) ;
				}
				break;
			case 5://停下来
				{
					String text = "停下来";
					tShowText_X -= 40;
					canvas.drawText(text, tShowText_X , tShowText_Y, TextPaint2) ;
				}
				break;
			case 6://所有水果都中奖
				{
					String text = "所有水果都中奖";
					tShowText_X -= 75;
					canvas.drawText(text, tShowText_X , tShowText_Y, TextPaint2) ;
				}
				break;
			case 7://中奖水果X2倍奖金
				{
					String text = "中奖水果X2倍奖金";
					tShowText_X -= 100;
					canvas.drawText(text, tShowText_X , tShowText_Y, TextPaint2) ;
				}
				break;
			}
			
		}
		
		//显示当前位置格子背景
		appleTileArray[m_currentpathid].DrawBack(canvas,m_RunNum%4);
		
		//格子元素显示
		for(int i = 0 ; i < m_TileCount ; i++)
		{
			appleTileArray[i].OnDraw(canvas,paint);
		}

		
		/*
		// ===============================小珠显示============================================
		//左上角
		canvas.drawBitmap(m_BitMapArray[17], 25+m_Left,25+m_Top, paint);
		//右上角
		canvas.drawBitmap(m_BitMapArray[17], (m_TileCows-1)*m_TileW-10+m_Left,25+m_Top, paint);
		//左下角
		canvas.drawBitmap(m_BitMapArray[17], 25+m_Left,(m_TileRows+1)*m_TileH - 12+m_Top, paint);
		//右下角
		canvas.drawBitmap(m_BitMapArray[17], (m_TileCows-1)*m_TileW-10+m_Left,(m_TileRows+1)*m_TileH - 12+m_Top, paint);
		//其它
		for(int i = 1 ; i < m_TileCows-1 ; i++)
		{
			//上行
			canvas.drawBitmap(m_BitMapArray[17], i*m_TileW+5+m_Left,25+m_Top, paint);
			//下行
			canvas.drawBitmap(m_BitMapArray[17], i*m_TileW+5+m_Left,(m_TileRows+1)*m_TileH - 12+m_Top  , paint);
		}
		for(int i = 0 ; i < m_TileRows ; i++)
		{
			//右列
			canvas.drawBitmap(m_BitMapArray[17],(m_TileCows-1) * m_TileW-5+m_Left,(i + 1)* m_TileH + 5+m_Top, paint);
			//左行
			canvas.drawBitmap(m_BitMapArray[17], 20+m_Left,(i + 1) * m_TileH + 5+m_Top, paint);
		}	
		
		//红珠
		int     tPathIndex = m_currentpathid;
		
		if(0 == tPathIndex)
		{
			//左上角
			canvas.drawBitmap(m_BitMapArray[18], 25+m_Left,25+m_Top, paint);	
		}
		else if(7 == tPathIndex)
		{
			//左上角
			canvas.drawBitmap(m_BitMapArray[18], (m_TileCows-1)*m_TileW-10+m_Left,25+m_Top, paint);
		}
		else if(15 == tPathIndex)
		{
			//右下角	
			canvas.drawBitmap(m_BitMapArray[18], (m_TileCows-1)*m_TileW-10+m_Left,(m_TileRows+1)*m_TileH - 12+m_Top, paint);
		}
		else if(22 == tPathIndex)
		{
			//左下角	
			canvas.drawBitmap(m_BitMapArray[18], 25+m_Left,(m_TileRows+1)*m_TileH - 12+m_Top, paint);
		}
		else
		{
			if(0 < tPathIndex && tPathIndex < 7)
			{
				//上行
				canvas.drawBitmap(m_BitMapArray[18], tPathIndex*m_TileW+5+m_Left,25+m_Top, paint);	
			}
			else if(7 < tPathIndex && tPathIndex < 15)
			{
				//右列
				int tIndex = tPathIndex - 8 ;
				canvas.drawBitmap(m_BitMapArray[18],(m_TileCows-1) * m_TileW-5+m_Left,(tIndex + 1)* m_TileH + 5+m_Top, paint);
			}
			else if(15 < tPathIndex && tPathIndex < 22)
			{
				//下行
				int tIndex = (22 - tPathIndex);
				canvas.drawBitmap(m_BitMapArray[18], tIndex*m_TileW+5+m_Left,(m_TileRows+1)*m_TileH - 12+m_Top  , paint);
			}
			else 
			{
				//左行
				int tIndex = (29 - tPathIndex) ;
				canvas.drawBitmap(m_BitMapArray[18], 20+m_Left,(tIndex + 1) * m_TileH + 5+m_Top, paint);
			}
		}
		// ===========================================================================
		*/
		
		if(true == m_QiQiuOpen)
		{
			//显示气球
			if(true == m_QiQiuIsLive)
			{
				//气球显示
				m_QiQiu.OnDraw(canvas,TextPaint);
			}
			
			//遍历飞镖列表,绘制每个飞镖对象
			for( FeiBiao fb:m_FeiBiaoArray)
			{	
				fb.OnDraw(canvas,TextPaint);
			}
		}
		//显示玩家下注情况
		for(int i = 0 ; i < m_TileCows ; i++)
		{	
			for(int j = 0 ; j < m_WinnerSize ; j++)
			{
				int tLeft = (int) (i * m_TileW + 1* m_ScaleW);
				int tTop = (int) (m_ShowIconTop + 40* m_ScaleH + j * 20* m_ScaleH) ;
				int tRight = (int) (tLeft + m_TileW - 1* m_ScaleW);
				int tBottom = (int) (tTop + 20* m_ScaleH - 1);
				//背景
				canvas.drawRect(tLeft,tTop,tRight,tBottom, m_WinnerPaint[j]);
				
				int	tappleIndex = m_TileCows-1-i;
				
				//取得是否有下注
				int tRecordSize = m_WinnerRecord[j].GetRecord(tappleIndex);
				
				if( tRecordSize > 0 )
				{
					if( (m_WinnerAppleID == tappleIndex || m_WinnerAppleID == m_WinAllAppleID )&& false == m_ShowWinner)
					{
	
					}
					else
					{
						//数字
						String text = " " + tRecordSize;
						canvas.drawText(text, tLeft+4* m_ScaleW, tBottom-3* m_ScaleH, TextPaint) ;
					}
				}
				else
				{
					//数字
					String text = " " + tRecordSize;
					canvas.drawText(text, tLeft+4* m_ScaleW , tBottom-3* m_ScaleH, TextPaint) ;
				}
			}
		}
		
		//显示钱袋
		if( m_WinnerAppleID >= 0)
		{
			if(true == m_ShowWinner)
			{
				int tShowMoneW = m_BitMapArray[49].getWidth();
				int tShowMoneH = m_BitMapArray[49].getHeight();
				
				int	tShowMoneyX = (int) ((m_ScreenWidth - tShowMoneW)/2);			//显示钱袋的位置X
				int	tShowMoneyY = (int) ((m_ScreenHeight - tShowMoneH)/2);				//显示钱袋的位置Y		
				
				canvas.drawBitmap(m_BitMapArray[49], tShowMoneyX, tShowMoneyY - 35* m_ScaleH ,paint );
			}
		}
		/*
		//显示手
		if(m_HandIndex_apple >= 0)
		{
			int tLeft = m_HandIndex_apple * m_TileW ;
			int tTop = m_ShowIconTop + m_HandIndex_winner * 20 + 40;
			canvas.drawBitmap(m_BitMapArray[27+m_HandIndex_winner], tLeft,tTop, paint);
		}
		*/
		
	}
	//设置游戏
	public	boolean  GameConfig(float mouseX,float mouseY)
	{
		//如果是游戏设置状态
		if(SETTINGSTATE == m_GameState)
		{
			if(mouseY < 120*m_ScaleH)
			{
				//单双人
				if(mouseX < 120*m_ScaleW)
				{
					SetWinnerSize(1);
				}
				else
				{
					SetWinnerSize(2);
				}
			}
			else if(mouseY < 190*m_ScaleH)
			{
				//气球
				if(mouseX < 120*m_ScaleW)
				{
					EnableQiQiu(true);
				}
				else
				{
					EnableQiQiu(false);
				}
			}
			else if(mouseY < 260*m_ScaleH)
			{
				//音效
				if(mouseX < 120*m_ScaleW)
				{
					EnableSound(true);
				}
				else
				{
					EnableSound(false);
				}
			}
			else if(mouseY < 330*m_ScaleH)
			{
				//音乐
				if(mouseX < 120*m_ScaleW)
				{
					EnableMusic(true);
				}
				else
				{
					EnableMusic(false);
				}
			}
			else
			{
				//返回游戏状态
				myHandler.sendEmptyMessage(2);//发送返回主界面的消息
			}
			return true;
		}
		else if(HELPSTATE == m_GameState)
		{
			//如果是帮助界面,返回游戏状态

			if( mouseY > (m_ScreenHeight - 70*m_ScaleH))
			{
				myHandler.sendEmptyMessage(2);//发送返回主界面的消息
			}
			return true;
		}
		return false;
	}
	//重新开始
	public  void  Restart(float mouseX,float mouseY)
	{
		//判断是否点击在"开始按钮"中
		int tBtnW = m_BitMapArray[32].getWidth();
		int tBtnH = m_BitMapArray[32].getHeight();
		
		int tLeft = m_ShowStartBtnX - 4;
		int tTop =  m_ShowStartBtnY - 4;
		
		int tRight = tLeft + tBtnW + 4;
		int tBottom = tTop + tBtnH + 4;
		
		if(mouseX < tLeft)return;
		if(mouseX > tRight)return;
		if(mouseY < tTop)return;
		if(mouseY > tBottom)return;
		
		if(true == IsReady())
		{
			m_starttime = System.nanoTime();					//获取当前时间,单位为纳秒
			m_Timer			= 500 + random.nextInt(500);		//每隔多久前进一次
			m_DecTimer		= 40  + random.nextInt(40);			//加速度
			m_MinTimer		= 0;								//最小时间间隔
			
			//运行次数换图
			int    tBackImageIndex = m_RunNum % 4 ;
			switch(tBackImageIndex)
			{
				case 0:
					m_BitMapArray[31] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.background1);//背景
					break;
				case 1:
					m_BitMapArray[31] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.background2);//背景
					break;
				case 2:
					m_BitMapArray[31] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.background3);//背景
					break;
				default:
					m_BitMapArray[31] = BitmapFactory.decodeResource(winnerActivity.getResources(), R.drawable.background4);//背景
					break;
			}
				
			m_RunNum++;
			m_bStartRun 	= true;								//开始
			m_CurrWinnerID  = 0;								//当前玩家
		}
		
	}
	//是否可以开始[检查是否有下注]
	public	boolean  IsReady()
	{
		boolean	tPutIcon = false;
		if(false == m_bStartRun)
		{
			//如果是双人对战
			if(m_WinnerSize == 2)
			{
				//查找是否有下注
				for(int i = 0 ; i < m_TileCows ; i++)
				{	
					for(int j = 0 ; j < m_WinnerSize ; j++)
					{
						if( m_WinnerRecord[m_CurrWinnerID].GetRecord(i) > 0 )
						{
							tPutIcon = true;
							break;
						}
					}
				}
				
				if( true == tPutIcon )
				{
					if(m_CurrWinnerID < 2)
					{
						m_CurrWinnerID++;
					}
				}
				
				if( m_CurrWinnerID == 2)
				{
					tPutIcon = true;
					m_CurrWinnerID = -1;
				}
				else
				{
					tPutIcon = false;
				}
			}
			else
			{
				for(int i = 0 ; i < m_TileCows ; i++)
				{	
					for(int j = 0 ; j < m_WinnerSize ; j++)
					{
						if( m_WinnerRecord[j].GetRecord(i) > 0 )
						{
								tPutIcon = true;
								break;
						}
					}
				}
			}
		}
		return tPutIcon;	
	}
	//加币
	public	boolean  AddIcon(float mouseX,float mouseY)
	{
		if(false == m_bStartRun)
		{	
			//显示返回菜单
			int		tMaxRight = (int) (m_ScreenWidth - m_BitMapArray[48].getWidth()- 5);
			//显示玩家金币情况
			for(int i = 0 ; i < m_WinnerSize ; i++)
			{
				int     tIconSize = m_WinnerRecord[i].GetIconSize();
				if( 0 ==  tIconSize)
				{
					int tLeft = i * 100 ;
					int tTop = 0 ;
					int tRight = tLeft + 100 ;
					if(tRight >= tMaxRight )
					{
						tRight = tMaxRight - 1;
					}
					int tBottom = m_Top;
					
					if(mouseX < tLeft)continue;
					if(mouseX > tRight)continue;
					if(mouseY < tTop)continue;
					if(mouseY > tBottom)continue;
					
					m_WinnerRecord[i].AddIcon(1);
				
					//播放放金币声音
					PlayIconSound();
					return true;
				}
			}
		}
		return false;
	}
	//投注
	public	boolean  DecIcon(float mouseX,float mouseY)
	{
		if(false == m_bStartRun)
		{
			if(m_WinnerAppleID >= 0)
			{
				//清空
				for(int j = 0 ; j < m_WinnerSize ; j++)
				{
					m_WinnerRecord[j].CleanRecord(-1);
				}
				m_WinnerAppleID = -1;
			}
			
			for(int i = 0 ; i < m_TileCows ; i++)
			{	
				//for(int j = 0 ; j < m_WinnerSize ; j++)
				//{
					int tLeft = i * m_TileW ;
					int tTop = m_ShowIconTop ;//+ j * 20 ;
					int tRight = tLeft + m_TileW;
					int tBottom = (int) m_ScreenHeight;
					
					if(mouseX < tLeft)continue;
					if(mouseX > tRight)continue;
					if(mouseY < tTop)continue;
					if(mouseY > tBottom)continue;
					
					m_WinnerRecord[m_CurrWinnerID].AddRecord(m_TileCows-1-i);
					return true;
				//}
			}
		}
		return false;
	}
	//开始游戏
	public  boolean StartGame(float mouseX,float mouseY)
	{		

		//如果是开始界面
		if(INITSTATE == m_GameState)
		{
			//按钮
			int		tBtnLeft = (int) (20 * m_ScaleW);
			int		tBtnWidth  = m_BitMapArray[ 44 ].getWidth();
			int		tBtnHeight = (int) (m_BitMapArray[ 44 ].getHeight()+3 * m_ScaleH);
			int		tBtnTop  = (int) (m_ScreenHeight - 4*(tBtnHeight+2)) ;	
			for(int i = 0 ; i < 4 ; i++)
			{
				int tLeft = (int) (tBtnLeft + i * 40 * m_ScaleW) ;
				int tTop =  (int) (tBtnTop + i * tBtnHeight ) ;
				int tRight = tLeft + tBtnWidth;
				int tBottom = tTop + tBtnHeight;
				
				if(mouseX < tLeft)continue;
				if(mouseX > tRight)continue;
				if(mouseY < tTop)continue;
				if(mouseY > tBottom)continue;

				if( 0 == i )
				{
					//进入游戏
					InitGameUI();
					m_GameState = GAMESTATE;
				}
				else if( 1 == i )
				{
					//游戏设置
					m_GameState = SETTINGSTATE ;
				}
				else if( 2 == i )
				{
					//游戏帮助
					m_GameState = HELPSTATE ;
					m_HelpTextTop = m_HelpTextMinTop;
				}
				else if( 3 == i )
				{
					//退出游戏
					m_GameState = GAMEEXIT ;
					m_GameExitTime = System.nanoTime();//获取当前时间,单位为纳秒 
					m_currenttime = System.nanoTime();//获取当前时间,单位为纳秒

					//winnerActivity.finish();
					//android.os.Process.killProcess(android.os.Process.myPid());
				}
				
			}

			return true;
		}
		return false;
	}
	//检查是否点击了返回菜单
	public	void	CheckEnterReturnMenu(float mouseX,float mouseY)
	{
		if( GAMESTATE == m_GameState )
		{
			//显示返回菜单
			int		menuBitMapW = m_BitMapArray[48].getWidth();
			int		menuBitMapH = m_BitMapArray[48].getHeight();
			
			int tLeft = (int) (m_ScreenWidth - menuBitMapW - 5 * m_ScaleW) ;
			int tTop = 0 ;
			int tRight = (int) (tLeft + m_TileW + 5 * m_ScaleW);
			int tBottom = (int) (tTop + menuBitMapH + 2 * m_ScaleH);
				
			if(mouseX < tLeft)return ;
			if(mouseX > tRight)return;
			if(mouseY < tTop)return;
			if(mouseY > tBottom)return;	
			
			//重设状态
			m_starttime = System.nanoTime();//获取当前时间,单位为纳秒
			m_Timer			= 500;				//每隔多久前进一次
			m_DecTimer		= 40;				//加速度
			m_MinTimer		= 0;				//最小时间间隔
			m_bStartRun     = false;
			m_currentpathid = random.nextInt(m_TileCount);		//起始位置
			m_CurrWinnerID = 0;
			m_InitIconSize = 10;								//初始时10个金币
			for(int i = 0 ; i < m_MaxWinnerSize ; i++)
			{
				//玩家
				m_WinnerRecord[i].SetIconSize(m_InitIconSize);
				m_WinnerRecord[i].CleanRecord(-1);
			}
			m_WinnerAppleID = -1;
			//返回菜单界面
			m_GameState = INITSTATE ;
			
		}
		
	}
	//打气球
	public  void    PutOutJianTou(float mouseX,float mouseY)
	{
		if( GAMESTATE == m_GameState )
		{
			if( true == m_bStartRun &&  true == m_QiQiuOpen)
			{
				//如果没有气球可用,直接返回
				if(false == m_QiQiuIsLive)return ;
				//取得点击的水果类型
				int vAppleType = -1;
				for(int i = 0 ; i < m_TileCows ; i++)
				{
					int tLeft = i * m_TileW ;
					int tTop = m_ShowIconTop ;
					int tRight = tLeft + m_TileW;
					int tBottom = (int) m_ScreenHeight;
						
					if(mouseX < tLeft)continue;
					if(mouseX > tRight)continue;
					if(mouseY < tTop)continue;
					if(mouseY > tBottom)continue;
					
					vAppleType = m_TileCows - 1 -i;
					break;
				}
				
				if(-1 == vAppleType )return ;
				
				//如果已经有飞镖了,不能再发射了
				if(m_FeiBiaoArray.size() > 0)return ;
				//清空飞镖
				m_FeiBiaoArray.clear();
				//左边
				int	tStartIndex = 2 * m_TileCows + m_TileRows ;
				for(int i = 0 ; i < m_TileRows ; i++)
				{
					int tImageID = appleTileArray[tStartIndex + i].GetImageID();
					if(tImageID < 16)
					{
						tImageID = tImageID % 8 ;
						if( vAppleType == tImageID)
						{
							FeiBiao	tFeiBiao = new FeiBiao();
							int		tPosX = appleTileArray[tStartIndex + i].GetTileX();
							int		tPosY = appleTileArray[tStartIndex + i].GetTileY();
							float	tSpeed = 0.5f;
							if(tImageID > 3)
							{
								tFeiBiao.Init(1, m_BitMapArray[42], 0, tPosX, tPosY, tSpeed);
							}
							else
							{
								tFeiBiao.Init(0, m_BitMapArray[40], 0, tPosX, tPosY, tSpeed);
							}
		
							m_FeiBiaoArray.add(tFeiBiao);
						}
					}
				}
				
				//右边
				tStartIndex = m_TileCows  ;
				for(int i = 0 ; i < m_TileRows ; i++)
				{
					int tImageID = appleTileArray[tStartIndex + i].GetImageID();
					if(tImageID < 16)
					{
						tImageID = tImageID % 8 ;
						if( vAppleType == tImageID)
						{
							FeiBiao	tFeiBiao = new FeiBiao();
							int		tPosX = appleTileArray[tStartIndex + i].GetTileX();
							int		tPosY = appleTileArray[tStartIndex + i].GetTileY();
							float	tSpeed = 0.5f;
							if(tImageID > 3)
							{
								tFeiBiao.Init(1, m_BitMapArray[43], 1, tPosX, tPosY, tSpeed);
							}
							else
							{
								tFeiBiao.Init(0, m_BitMapArray[41], 1, tPosX, tPosY, tSpeed);
							}
		
							m_FeiBiaoArray.add(tFeiBiao);
						}
					}
				}
				
				//播放飞镖声音
				if(m_FeiBiaoArray.size() > 0)
				{
					PlayFeiBiaoSound();
				}
			}
		}
	}
	//放一个气球
	public	void	PutOutQiQiu()
	{
		if( GAMESTATE == m_GameState )
		{
			if(false == m_QiQiuIsLive && true == m_QiQiuOpen)
			{
				int		vQiQiuID = random.nextInt(6);		//气球色彩
				int		vBaoWuID = random.nextInt(8);		//宝物类型 
				float 	vSpeed 	= random.nextFloat()* 0.5f + 0.1f;
				
				int		tImageW = m_BitMapArray[34 + vQiQiuID].getWidth();
				//int		tImageH = m_BitMapArray[34 + vQiQiuID].getHeight();
				m_QiQiu.Init(vQiQiuID, m_BitMapArray[34 + vQiQiuID], (m_ScreenWidth - tImageW)/2, m_ScreenHeight, -vSpeed);
				m_QiQiu.SetBaoWu(vBaoWuID);
				m_QiQiuIsLive = true;
			}
		}
	}
	//飞镖碰撞气球
	public void		CollQiQiu()
	{
		if( GAMESTATE == m_GameState )
		{
			if(true == m_QiQiu.IsLive() &&  true == m_QiQiuOpen)
			{
				int		tBaoWuID = m_QiQiu.GetBaoWu();
				m_QiQiu.Kill();
				//播放打破气球的声音
				PlayBombSound();
				//刷币
				RefreshBaoWu(tBaoWuID);
			}
		}
	}
	//处理刷宝
	public	void	RefreshBaoWu(int vBaoWuID)
	{
		//播放放金币声音
		PlayIconSound();
		
		switch(vBaoWuID)
		{
		case 0://增加一个金币
			m_WinnerRecord[0].AddIcon(1);
			break;
		case 1://扣除一个金币
			m_WinnerRecord[0].DecIcon();
			break;
		case 2://增加二个金币
			m_WinnerRecord[0].AddIcon(2);
			break;
		case 3://扣除二个金币
			m_WinnerRecord[0].DecIcon();
			m_WinnerRecord[0].DecIcon();
			break;
		case 4://再持续一圈
			m_Timer = 1000;
			break;
		case 5://停下来
			m_Timer = 2000;
			break;
		case 6://所有水果都中奖
			m_WinAllApple = 1;
			break;
		case 7://中奖水果X2倍奖金
			m_WinAllApple = 2;
			break;
		}
		m_ShowBaoWu = vBaoWuID ;
	}
	//鼠标
	public	void	ShowHand(float mouseX,float mouseY)
	{
		/*
		if(false == m_bStartRun)
		{
			for(int i = 0 ; i < m_TileCows ; i++)
			{	
				for(int j = 0 ; j < m_WinnerSize ; j++)
				{
					int tLeft = i * m_TileW ;
					int tTop = m_ShowIconTop + j * 20;
					int tRight = tLeft + m_TileW;
					int tBottom = tTop + 20;
					
					if(mouseX < tLeft)continue;
					if(mouseX > tRight)continue;
					if(mouseY < tTop)continue;
					if(mouseY > tBottom)continue;
					
					m_HandIndex_apple = i ;
					m_HandIndex_winner = j ;
					return ;
				}
			}
		}
		m_HandIndex_apple = -1 ;
		m_HandIndex_winner = -1;
		*/
	}
	//结算
	public	void	CalculateWinner()
	{
		m_WinnerAppleID = -1;
		
		//所有水果都中奖
		if( 1 == m_WinAllApple )
		{
			int		tCurrentImageId = appleTileArray[m_currentpathid].GetImageID() ;
			boolean tWin = false;
			if( tCurrentImageId < 16)
			{
				for(int i = 0 ; i < m_TileCows ; i++)
				{
					for(int j = 0 ; j < m_WinnerSize ; j++)
					{
						int tRecordSize = m_WinnerRecord[j].GetRecord(i);
						int tWinIconSisze = m_Winsize[i] * tRecordSize ;
						if( tWinIconSisze > 0)
						{
							m_WinnerRecord[j].AddIcon(tWinIconSisze) ;
							tWin = true;
						}
					}
				}
				m_WinnerAppleID = m_WinAllAppleID ;
			}

			if(false == tWin)
			{
				//清空
				for(int j = 0 ; j < m_WinnerSize ; j++)
				{
					m_WinnerRecord[j].CleanRecord(-1);
				}
			}

		}
		else
		{
			//计算结果
			int		tCurrentImageId = appleTileArray[m_currentpathid].GetImageID() ;
			boolean tWin = false;
			if( tCurrentImageId < 16)
			{
				int	tImageType = tCurrentImageId % 8  ;
					
				for(int j = 0 ; j < m_WinnerSize ; j++)
				{
					int tRecordSize = m_WinnerRecord[j].GetRecord(tImageType);
					//如果下注数大于0
					if(tRecordSize > 0)
					{
						int tWinIconSisze = m_Winsize[tCurrentImageId] * tRecordSize ;
						//中奖水果X2
						if( 2 == m_WinAllApple)
						{
							tWinIconSisze = tWinIconSisze * 2 ;
						}
						
						m_WinnerRecord[j].AddIcon(tWinIconSisze) ;
						m_WinnerAppleID = tImageType ;
						tWin = true;
					}
				}
			}
			if(false == tWin)
			{
				//清空
				for(int j = 0 ; j < m_WinnerSize ; j++)
				{
					m_WinnerRecord[j].CleanRecord(-1);
				}
			}		
		}
		
		//恢复
		m_WinAllApple = 0;
	}
}

四.AppThread.java:多线程更新逻辑。

package win.gam;


public class AppleThread extends Thread
{	
	int 	sleepSpan 	= 20;		//休眠时间
	boolean flag 		= false;	//线程执行标志位
	Apple	apple 		= null;
	public AppleThread(Apple	apple)
	{
		this.apple = apple ;
		flag = true;		//设置线程执行的标志位为true
	}	
	
	public void Exit()
	{
		flag = false;
	}
	
	public void run()
	{
		while(flag)
		{
			apple.Update();
			
			try{
				Thread.sleep(sleepSpan);		//休眠一段时间				
			}
			catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}
   

五.AppleView.java:游戏视图。

package win.gam;

import win.gam.DrawThread;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class AppleView extends SurfaceView implements SurfaceHolder.Callback
{
	WinnerActivity	winnerActivity;
	DisplayMetrics	DMInfo;
	Apple			apple;
	DrawThread 		dt;	//后台屏幕绘制线程
	String 			fps="FPS:N/A";		//用于显示帧速率的字符串,调试使用	
	boolean			ButtonDown = false;
	public AppleView(Context activity)
	{
		super(activity);				//调用父类构造器		
		winnerActivity = (WinnerActivity)activity;
		getHolder().addCallback(this);
		init();
		dt = new DrawThread(this,getHolder());		//初始化重绘线程		
	}
	public void init()
	{
		DMInfo = new DisplayMetrics();
		winnerActivity.getWindowManager().getDefaultDisplay().getMetrics(DMInfo);
		apple = new Apple(winnerActivity,DMInfo.widthPixels,DMInfo.heightPixels);
	}

	public boolean onTouchEvent(MotionEvent event)
	{
	   if(event.getAction() == MotionEvent.ACTION_DOWN)
	   {
		   //如果是设置游戏
		   if(true == apple.GameConfig(event.getX(), event.getY()))return true;
		   //如果开始游戏
		   if(true == apple.StartGame(event.getX(), event.getY()))return true;
		   
		   //如果游戏中,看是否点击返回菜单
		   apple.CheckEnterReturnMenu(event.getX(), event.getY());
		   //如果游戏中,可以发射箭头
		   apple.PutOutJianTou(event.getX(), event.getY());
		   
		   ButtonDown = true;
	   
		   //加分
		   if(false == apple.AddIcon(event.getX(), event.getY()))
		   {
			   //苹果机开始
			   if(false == apple.DecIcon(event.getX(), event.getY()))
			   {
					apple.Restart(event.getX(), event.getY());
					invalidate();
			   }
		   }
		   else
		   {
			   invalidate();
		   }
	   }
	   if(event.getAction() == MotionEvent.ACTION_UP)
	   {
		   ButtonDown = false;
	   }

	   //显示手
	   apple.ShowHand(event.getX(), event.getY());

	   return true;	 
	}

	public void doDraw(Canvas canvas) 
	{
		 Paint BackGraoundP = new Paint();		//创建画笔对象
		 BackGraoundP.setColor(Color.BLACK);	//为画笔设置颜色
		 //刷背景色
		 RectF	tRect = new RectF(0,0, this.getWidth(),this.getHeight());
		 canvas.drawRect(tRect, BackGraoundP);
		 
	     //画水果机
         Paint paintApple = new Paint();
         apple.Draw(canvas, paintApple);
         
         
         /*
         //画FPS
 		 Paint p = new Paint();				//创建画笔对象
 		 p.setColor(Color.BLUE);			//为画笔设置颜色
 		 p.setTextSize(18);					//为画笔设置字体大小
 		 p.setAntiAlias(true);				//设置抗锯齿 
 		 canvas.drawText(fps, 30, 30, p);	//画出帧速率字符串
 		 */
	}
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) 
	{//重写surfaceChanged方法	
		apple.ChangeWindowSize(width,height);
	}
	@Override
	public void surfaceCreated(SurfaceHolder holder) 
	{//从写surfaceCreated方法
		if(!dt.isAlive())
		{	//如果DrawThread没有启动,就启动这个线程
			dt.start();
		}
	}
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) 
	{//重写surfaceDestroyed方法
		dt.flag = false;	//停止线程的执行
		dt = null;			//将dt指向的对象声明为垃圾
		
		apple.ExitThread();
	}	
}

六.DrawThread.java:多线程更新

   package win.gam;
   
   import win.gam.AppleView;
   import android.graphics.Canvas;
   import android.view.SurfaceHolder;
   
   public class DrawThread extends Thread
   {
   	AppleView bv;				//BallView对象引用
   	SurfaceHolder surfaceHolder;//SurfaceHolder对象引用
   	boolean flag=false;			//线程执行标志位
   	int sleepSpan = 30;			//休眠时间
   	long start = System.nanoTime();	//记录起始时间,该变量用于计算帧速率
   	int count=0;		//记录帧数,该变量用于计算帧速率
   	//构造器
   	public DrawThread(AppleView bv,SurfaceHolder surfaceHolder){
   		this.bv = bv;		//为BallView对象应用赋值
   		this.surfaceHolder = surfaceHolder;	//为SurfaceHolder对象应用赋值
   		this.flag = true;		//设置标志位
   	}
   	//方法:线程的执行方法,用于绘制屏幕和计算帧速率
   	public void run(){
   		Canvas canvas = null;//声明一个Canvas对象
   		while(flag){
   			try{
   				canvas = surfaceHolder.lockCanvas(null);//获取BallView的画布
   				synchronized(surfaceHolder){
   					bv.doDraw(canvas);		//调用BallView的doDraw方法进行绘制
   				}
   			}
   			catch(Exception e){
   				e.printStackTrace();			//捕获并打印异常
   			}
   			finally{
   				if(canvas != null){		//如果canvas不为空
   					surfaceHolder.unlockCanvasAndPost(canvas);//surfaceHolder解锁并将画布对象传回
   				}
   			}		
   			this.count++;
   			if(count == 20){	//如果计满20帧
   				count = 0;		//清空计数器
   				long tempStamp = System.nanoTime();//获取当前时间
   				long span = tempStamp - start;		//获取时间间隔
   				start = tempStamp;					//为start重新赋值
   				double fps = Math.round(100000000000.0/span*20)/100.0;//计算帧速率
   				bv.fps = "FPS:"+fps;//将计算出的帧速率设置到BallView的相应字符串对象中
   			}
   			try{
   				Thread.sleep(sleepSpan);		//线程休眠一段时间
   			}
   			catch(Exception e){					
   				e.printStackTrace();		//捕获并打印异常
   			}
   		}
   	}	
}

.FeiBiao.java:飞镖类

   package win.gam;
   
   import android.graphics.Bitmap;
   import android.graphics.Canvas;
   import android.graphics.Paint;
   
   public class FeiBiao
   {
   	int			m_FeiBiaoType = 0;				//飞镖类型
   	int			m_FangXiang = 0;				//方向 0右 1左
   	float		m_PosX		= 0;				//位置X
   	float		m_PosY		= 0;				//位置Y
   	float		m_Speed		= -0.01f;			//速度
   	boolean		m_IsLive	= false;			//是否存活
   	Bitmap		m_Image		= null;				//图像
   	
   	FeiBiao()
   	{
   		
   	}
   	
   	//设置气球
   	public	void	Init(int vFeiBiaoType,Bitmap	Image,int vFangXiang ,int vPosX,int vPosY,float vSpeed)
   	{
   		m_FeiBiaoType = vFeiBiaoType;
   		m_Image		= Image ;
   		m_FangXiang = vFangXiang ;
   		m_PosX = vPosX;
   		m_PosY = vPosY;
   		m_Speed = vSpeed;
   		m_IsLive = true;
   	}
   	//是否存活
   	public  boolean IsLive()
   	{
   		return m_IsLive;
   	}
   	//取得气球类型
   	public	int		GetQiQiuType()
   	{
   		return m_FeiBiaoType;
   	}
   	//取得气球位置
   	public	float	GetPosX()
   	{
   		return m_PosX;
   	}
   	public  float	GetPosY()
   	{
   		return m_PosY;
   	}
   	//取得气球的宽度
   	public float 	GetImageWidth()
   	{
   		return m_Image.getWidth();
   	}
   	//取得气球的高度
   	public float	GetImageHeight()
   	{
   		return m_Image.getHeight();
   	}
   	//更新
   	public void	Update(float vScreenWidth,float vScreenHeight)
   	{
   		if(m_IsLive)
   		{
   			int	tImageW = m_Image.getWidth();
   			//右
   			if(0 == m_FangXiang)
   			{
   				if( m_PosX > (vScreenWidth + tImageW))
   				{
   					m_IsLive = false;
   				}
   				else
   				{
   					m_PosX += m_Speed ;
   				}
   			}
   			else
   			{
   				//左
   				if( m_PosX < - tImageW)
   				{
   					m_IsLive = false;
   				}
   				else
   				{
   					m_PosX -= m_Speed ;
   				}
   			}
   
   		}
   	}
   	//绘制
   	public	void	OnDraw(Canvas canvas,Paint paint)
   	{
   		if(m_IsLive)
   		{
   			canvas.drawBitmap(m_Image, m_PosX ,m_PosY, paint);
   		}
   	}
}

.FeiBiaoThread.java:多线程更新飞镖逻辑。

package win.gam;

public class FeiBiaoThread extends Thread
{	
	int 	sleepSpan 	= 2;		//休眠时间
	boolean flag 		= false;	//线程执行标志位
	Apple	apple 	= null;
	public FeiBiaoThread(Apple	apple)
	{
		this.apple = apple ;
		flag = true;		//设置线程执行的标志位为true
		this.start();
	}	
	//运行
	public void run()
	{
		while(flag)
		{
			//更新飞镖
			if( apple.m_FeiBiaoArray.size() > 0 )
			{
				boolean	IsLive = false;
				//位置
				float	tLeft 	= apple.m_QiQiu.GetPosX();
				float	tTop  	= apple.m_QiQiu.GetPosY();
				float   tRight	= tLeft + apple.m_QiQiu.GetImageWidth();
				float   tBottom	= tTop + apple.m_QiQiu.GetImageHeight();
				
				//遍历飞镖列表,绘制每个飞镖对象
				for( FeiBiao fb:apple.m_FeiBiaoArray)
				{	
					fb.Update(apple.GetScreenWidth(),apple.GetScreenHeight());
					if(true == fb.IsLive())
					{
						IsLive = true;
						//位置
						float	tLeft2 		= fb.GetPosX();
						float	tTop2 		= fb.GetPosY();
						float   tRight2		= tLeft2 + fb.GetImageWidth();
						float   tBottom2	= tTop2 + fb.GetImageHeight();
						
						if(true == CheckColl(tLeft,tTop,tRight,tBottom,tLeft2,tTop2,tRight2,tBottom2))
						{
							apple.CollQiQiu();
						}
					}
				}
				//如果都结束了退出线程
				if(false == IsLive)
				{
					apple.m_FeiBiaoArray.clear();
				}
			}
			
			try{
				Thread.sleep(sleepSpan);		//休眠一段时间				
			}
			catch(Exception e){
				e.printStackTrace();
			}
		}
	}
	
	//检查是否碰撞
	public	boolean	CheckColl(float vLeft1,float vTop1,float vRight1,float vBottom1,float vLeft2,float vTop2,float vRight2,float vBottom2)
	{
		if(vLeft1 > vRight2 )return false;
		if(vTop1 > vBottom2 )return false;		
		
		if(vRight1 < vLeft2 )return false;
		if(vBottom1 < vTop2 )return false;		
		
		return true;
	}
}

.QiQiu.java:气球类

   package win.gam;
   
   import android.graphics.Bitmap;
   import android.graphics.Canvas;
   import android.graphics.Paint;
   
   public class QiQiu 
   {
   	int			m_QiQiuType = 0;				//气球类型
   	int			m_BaoWu		= 0;				//宝物
   	float		m_PosX		= 0;				//位置X
   	float		m_PosY		= 0;				//位置Y
   	float		m_Speed		= -0.1f;			//速度
   	boolean		m_IsLive	= false;			//是否存活
   	Bitmap		m_Image		= null;				//图像
   	QiQiu()
   	{
   		
   	}
   	
   	//设置气球
   	public	void	Init(int vQiQiuType,Bitmap	Image,float vPosX,float vPosY,float vSpeed)
   	{
   		m_QiQiuType = vQiQiuType;
   		m_Image		= Image ;
   		m_PosX = vPosX;
   		m_PosY = vPosY;
   		m_Speed = vSpeed;
   		m_IsLive = true;
   	}
   	//是否存活
   	public  boolean IsLive()
   	{
   		return m_IsLive;
   	}
   	//杀掉气球
   	public	void	Kill()
   	{
   		m_IsLive = false;
   	}
   	//取得气球类型
   	public	int		GetQiQiuType()
   	{
   		return m_QiQiuType;
   	}
   	//取得气球位置
   	public	float	GetPosX()
   	{
   		return m_PosX;
   	}
   	public  float	GetPosY()
   	{
   		return m_PosY;
   	}
   	//取得气球的宽度
   	public float 	GetImageWidth()
   	{
   		return m_Image.getWidth();
   	}
   	//取得气球的高度
   	public float	GetImageHeight()
   	{
   		return m_Image.getHeight();
   	}
   	//设置宝物
   	public	void	SetBaoWu(int vBaoWu)
   	{
   		m_BaoWu = vBaoWu;
   	}
   	//取得宝物
   	public	int	GetBaoWu()
   	{
   		return m_BaoWu;
   	}
   	//更新
   	public void	Update(float vScreenWidth,float vScreenHeight)
   	{
   		if(m_IsLive)
   		{
   			int	tImageH = m_Image.getHeight();
   			if( m_PosY < - tImageH)
   			{
   				m_IsLive = false;
   			}
   			else
   			{
   				m_PosY += m_Speed ;
   			}
   		}
   	}
   	//绘制
   	public	void	OnDraw(Canvas canvas,Paint paint)
   	{
   		if(m_IsLive)
   		{
   			canvas.drawBitmap(m_Image, m_PosX ,m_PosY, paint);
   		}
   	}
   
}

.QiQiuThread.java: 多线程更新气球逻辑。

package win.gam;

public class QiQiuThread extends Thread
{	
	int 	sleepSpan 	= 2;		//休眠时间
	boolean flag 		= false;	//线程执行标志位
	Apple	apple 	= null;
	
	public QiQiuThread(Apple	apple)
	{
		this.apple = apple ;
		flag = true;		//设置线程执行的标志位为true
		this.start();
	}	
	//运行
	public void run()
	{
		while(flag)
		{
			if(true == apple.m_QiQiu.IsLive())
			{
				//更新气球
				apple.m_QiQiu.Update(apple.GetScreenWidth(),apple.GetScreenHeight());
				
				//如果气球挂了,则退出线程
				if(false == apple.m_QiQiu.IsLive())
				{
					apple.m_QiQiuIsLive = false;
				}
			}
			else 
			{
				apple.m_QiQiuIsLive = false;
			}
			
			try{
				Thread.sleep(sleepSpan);		//休眠一段时间				
			}
			catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}

十一.Tile.java:轮盘的格子类。

package win.gam;
   
   import android.graphics.Bitmap;
   import android.graphics.Canvas;
   import android.graphics.Color;
   import android.graphics.Paint;
   
   public class Tile {
   	/*
   	static	final	int		FX_RIGHTTOP  	= 0;
   	static  final	int		FX_RIGHT	 	= 1;
   	static  final	int		FX_RIGHTBOTTOM 	= 2;
   	static	final	int		FX_BOTTOM		= 3;
   	static	final	int		FX_LEFTBOTTOM	= 4;
   	static	final	int		FX_LEFT			= 5;
   	static	final	int		FX_LEFTTOP		= 6;
   	static	final	int		FX_TOP			= 7;
   	*/
   	
   	
   	int		TileX = 0;	//格子位置
   	int		TileY = 0;
   	//int		FangXiang = FX_RIGHTTOP;	//方向	//
   	int		ImageID = 0;	//图像ID
   	Bitmap	Image;//图像
   	
   	Tile()
   	{
   		
   	}
   	//设置位置
   	public	void	SetTile(int vTileX,int vTileY)//,int vFangXiang)
   	{
   		TileX = vTileX;
   		TileY = vTileY;
   		//FangXiang = vFangXiang ;
   	}
   	//取得位置X
   	public	int		GetTileX()
   	{
   		return TileX;
   	}
   	//取得位置Y
   	public	int		GetTileY()
   	{
   		return TileY;
   	}
   	//设置图像ID
   	public  void	SetImage(int vBitMapID,Bitmap	vBitMap)
   	{
   		ImageID = vBitMapID ;
   		Image = vBitMap;
   	}
   	//取得图像ID
   	public int		GetImageID()
   	{
   		return ImageID;
   	}
   	//绘制背景
   	public	void	DrawBack(Canvas canvas,int  vBackIndex)
   	{
   		
   		int		tWidth = Image.getWidth();
   		int 	tHeight = Image.getHeight();
   		
   		int		tBorderSize = 4 ;
   		
   		Paint 	tBackPaint = new Paint();				//创建画笔对象
   		switch(vBackIndex)
   		{
   		case 0:
   			tBackPaint.setColor(Color.BLUE);					//为画笔设置颜色
   			break;
   		case 1:
   			tBackPaint.setColor(Color.YELLOW);					//为画笔设置颜色
   			break;
   		case 2:
   			tBackPaint.setColor(Color.CYAN);					//为画笔设置颜色
   			break;
   		case 3:
   			tBackPaint.setColor(Color.RED);						//为画笔设置颜色
   			break;
   		}
   		
   		//绘制背景图
   		canvas.drawRect(TileX-tBorderSize,TileY-tBorderSize, TileX + tWidth+tBorderSize,TileY + tHeight+tBorderSize,tBackPaint);
   
   	}
   	//绘制
   	public	void	OnDraw(Canvas canvas,Paint paint)
   	{
   		canvas.drawBitmap(Image, TileX,TileY, paint);
   	}
}

代码讲解完了。原理应该很清楚了。但因为是使用SurfaceView,所以效率不太好,我正打算改成cocos2d-x版本,等改好后再教给大家如何使用Cocos2d-x来制做。好了,下课吧~

 

本游戏程序压缩包下载位置:

http://download.csdn.net/detail/honghaier/4976678


分类: cocos2d 标签:

How to manually make a dynamic body(ball) to pass into the box in box2d iPhone

2013年1月8日 没有评论

As I am newbie to the cocos2d and I am struggling alot.Can anyone suggest me how to work on this.

  1. I am having 3 boxes(which are kinematic bodies)

  2. Also having multiple balls(which are dynamic bodies) which is having a tag value(box number) for each ball.

  3. I am having some obstacles(kinematic bodies) in between from where the ball is shooten and the boxes.

The scenario is I will get box number randomly ex: 2(second box) and is set as tha tag value to the ball. And the ball should pass into the second box exactly even it hits the obstacles.And if I got the box number for the next ball as 3 the ball should pass into the third box.

Can anyone please suggest me,

Thank you,
Monish

Here is a simple helper class to create balls and return. You could use similar approach to make boxes.

//
//  Ball.h
//  BouncingBall
//
//  Created by Omar Hussain on 8/12/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"

class Ball : public CCSprite {
public:
    CCSprite *sprite;
    b2BodyDef ballBodyDef;
    b2Body *ballBody;
    b2FixtureDef ballShapeDef;
    b2Fixture *fixture;
    Ball(b2World *world);
    b2Vec2 disp;
    CCSprite *shadowBall;
    b2World *world;
    bool isAnimating;
    void SetBallOverlap(bool willOverlap, int scale);
    void Destroy();
};


#import "Ball.h"

#define Density 1.0f
#define Friction 1.0f
#define Restitution 0.8f
#define PTM_RATIO 32
#define SpriteRadius 40.5


Ball::Ball(b2World *world){
    this->world = world;
    this->sprite = [CCSprite spriteWithFile:@"5.png"];
    [sprite setPosition:ccp(160,460)];  
    this->isAnimating = false;
    //Set up sprite
    ballBodyDef.type = b2_dynamicBody;
    ballBodyDef.position.Set(160/PTM_RATIO, 460/PTM_RATIO);
    ballBodyDef.userData = sprite;
    this->ballBody = world->CreateBody(&ballBodyDef);

    b2CircleShape circle;
    circle.m_radius = SpriteRadius/PTM_RATIO;

    ballShapeDef.shape = &circle;
    ballShapeDef.density = Density;
    ballShapeDef.friction =Friction;
    ballShapeDef.restitution = Restitution;
    this->fixture = this->ballBody->CreateFixture(&ballShapeDef);

    //this->shadowBall = [TSprite spriteWithFile:@"ball_shadow.png"];


}
void Ball::SetBallSize(int scale){
    this->ballBody->DestroyFixture(fixture);
    this->world->DestroyBody(ballBody);

    ballBodyDef.type = b2_dynamicBody;
    ballBodyDef.position.Set(160/PTM_RATIO, 460/PTM_RATIO);
    ballBodyDef.userData = sprite;
    this->ballBody = world->CreateBody(&ballBodyDef);
    float  correctedScale = scale/9.0;
    b2CircleShape circle;
    circle.m_radius = (correctedScale *SpriteRadius)/PTM_RATIO;

    ballShapeDef.shape = &circle;
    ballShapeDef.density = Density;
    ballShapeDef.friction = Friction;
    ballShapeDef.restitution = Restitution;
    this->fixture = this->ballBody->CreateFixture(&ballShapeDef);
    //this->shadowBall.scale = scale;
    this->sprite.scale = correctedScale;
}
void Ball::SetBallOverlap(bool willOverlap, int scale){
    this->world->DestroyBody(ballBody);

    ballBodyDef.type = b2_dynamicBody;
    ballBodyDef.position.Set(160/PTM_RATIO, 460/PTM_RATIO);
    ballBodyDef.userData = sprite;
    this->ballBody = world->CreateBody(&ballBodyDef);
    float  correctedScale = scale/9.0;
    b2CircleShape circle;
    circle.m_radius = (correctedScale *SpriteRadius)/PTM_RATIO;

    ballShapeDef.shape = &circle;
    ballShapeDef.density = Density;
    ballShapeDef.friction = Friction;
    ballShapeDef.restitution = Restitution;
    if(willOverlap)
        ballShapeDef.filter.groupIndex = 1;
    else 
        ballShapeDef.filter.groupIndex = -1;
    this->ballBody->CreateFixture(&ballShapeDef);
}
void Ball::Destroy(){
    this->ballBody->DestroyFixture(fixture);
    this->world->DestroyBody(ballBody);
}
分类: cocos2d, stackoverflow精选 标签: