存档

2013年2月 的存档

C++麻将游戏算法深入解析

2013年2月23日 没有评论

以此文奠我那些年中无意删除而删除的代码工程

               C++麻将游戏算法深入解析


 

      这两天为了工具箱的完善,整理了这些年引擎开发的一些资料,无意中发现06年写的一个麻将算法,编译运行了一下,还是有点意思的,拿出来整理一下分享给大家。

 

       麻将是一种大家最喜爱的娱乐活动之一,相信所有人都有接触过。我写的这版算法,是可以吃,碰,杠,还有把牌摸完没有人胡时的皇庄和包听。是用控制台方式来表现的,什么?控制台?

       对,因为是算法的设计,所以用控制台来表现当然最简单了。

C++麻将游戏算法深入解析

      当然,在交互时要用文字输入会有少许不便,不过这种形式的游戏可是图形游戏的鼻祖哦~

 

     好,废话不多说了,来说一下设计思路:

 

     对于麻将的一个玩家,都有一个牌墙的管理,这里封装了一副牌墙的各种算法,这个类我命名为CMJ。

     另外还有一个洗牌类,负责洗牌和发牌。这个类为CMJManage。

 

     我们先来看一下CMJ类。

CMJ.h:

#ifndef _CMJ_H
#define _CMJ_H
//============================================
//Author:Honghaier
//Date:2006-12-20
//QQ:285421210
//============================================
#include <windows.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;


#define	MJPAI_ZFB				0	//中,发,白
#define MJPAI_FENG				1	//东西南北风
#define MJPAI_WAN				2	//万
#define MJPAI_TIAO				3	//条
#define MJPAI_BING				4	//饼
#define MJPAI_HUA				5	//花

#define MJPAI_GETPAI			true	//起牌
#define MJPAI_PUTPAI			false	//打牌
//节点信息
struct stPAI
{
	int		m_Type;				//牌类型
	int		m_Value;			//牌字

}
;

//吃牌顺
struct stCHI					
{
	int		m_Type;				//牌类型
	int		m_Value1;			//牌字
	int		m_Value2;			//牌字
	int		m_Value3;			//牌字
}
;
//	m_Type		m_Value
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-//
//	0		|	中	1	发2	白											  
//			|
//	1		|	东 1	西2	南	  北									 
//			|
//	2		|	一万  二万	……	九万
//			|
//	3		|	一条	二条	……	九条					
//			|
//	4		|	一饼	二饼	……	九饼
//			|
//	5		|	春		夏		秋		东		竹		兰		梅		菊
//			|
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-//



//胡牌信息
struct stGoodInfo
{
	char	m_GoodName[100];			//胡牌术语
	int		m_GoodValue;				//胡牌番数
}
;
//牌
class CMJ
{
	vector<	int >		m_MyPAIVec[6];		//起的种牌型
	vector<	int >		m_ChiPAIVec[6];		//吃的种牌型
	vector<	int >		m_PengPAIVec[6];	//碰的种牌型
	vector<	int >		m_GangPAIVec[6];	//杠的种牌型

	stPAI				m_LastPAI;			//最后起的牌
	stGoodInfo			m_GoodInfo;			//胡牌信息

	bool				m_9LBD;				//是否听连宝灯牌型
	bool				m_13Y;				//是否听十三幺
	int					m_MKNum;			//明刻数
	int					m_AKNum;			//暗刻数
	bool				m_4AK;				//是否是听四暗刻

	vector<	stCHI >		m_TempChiPAIVec;	//吃的可选组合
	vector<	stPAI >		m_TempPengPAIVec;	//碰的可选组合
	vector<	stPAI >		m_TempGangPAIVec;	//杠的可选组合

public:

	//构造
	CMJ();
	//析构
	~CMJ();
	//初始化
	void			Init();
	//起牌
	bool			AddPai(int p_Type,int p_Value);
	//取得对应的牌在牌墙的索引
	int				GetPaiIndex(int p_Type,int p_Value);
	//打牌(参数为对应的牌在牌墙的索引)
	bool			DelPai(int PaiIndex);
	//删除牌
	bool			DelPai(int p_Type,int p_Value);
	//清空牌
	void			CleanUp();
	//取得胡牌信息
	stGoodInfo		*GetInfo();
	//检测是否胡牌
	bool			CheckAllPai(bool GetOrPut);
	//对所有的牌进行输出
	void			PrintAllPai();
	//对一张牌进行输出
	void			PrintPai(int p_Type,int p_Value);
	//吃牌
	bool			CheckChiPai(int p_Type,int p_Value);
	//吃牌
	bool			DoChiPai(int p_iIndex,int p_Type,int p_Value);
	//碰牌
	bool			CheckPengPai(int p_Type,int p_Value);
	//碰牌
	bool			DoPengPai(int p_Type,int p_Value);
	//杠牌
	bool			CheckGangPai(int p_Type,int p_Value);
	//杠牌
	bool			DoGangPai(int p_Type,int p_Value);
	//对可吃的组合进行输出
	void			PrintChiChosePai();
	//对可碰的组合进行输出
	void			PrintPengChosePai();
	//对可杠的组合进行输出
	void			PrintGangChosePai();
	//取得吃牌组合数
	UINT			GetChiChoseNum();

private:

	//检测是否胡牌(张)
	bool	CheckAAPai(int iValue1,int iValue2);
	//检测是否三连张
	bool	CheckABCPai(int iValue1,int iValue2,int iValu3);
	//检测是否三重张
	bool	CheckAAAPai(int iValue1,int iValue2,int iValu3);
	//检测是否四重张
	bool	CheckAAAAPai(int iValue1,int iValue2,int iValu3,int iValue4);
	//检测是否三连对
	bool	CheckAABBCCPai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6);
	//检测是否三连高压
	bool	CheckAAABBBCCCPai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9);
	//检测是否三连刻
	bool	CheckAAAABBBBCCCCPai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11,int iValue12);
	//检测是否六连对
	bool	CheckAABBCCDDEEFFPai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11,int iValue12);
	//带将牌检测=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	
	//检测是否胡牌(张)
	bool	Check5Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5);
	//检测是否胡牌(张)
	bool	Check8Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8);
	//检测是否胡牌(张)
	bool	Check11Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11);
	//检测是否胡牌(张)
	bool	Check14Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11,int iValue12,int iValue13,int iValue14);

	//不带将牌检测-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	
	//检测是否胡牌(张)
	bool	Check3Pai(int iValue1,int iValue2,int iValue3);
	//检测是否胡牌(张)
	bool	Check6Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6);
	//检测是否胡牌(张)
	bool	Check9Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9);
	//检测是否胡牌(张)
	bool	Check12Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11,int iValue12);
	

private:	
	//胡牌判断

	//检测是否胡大四喜
	bool	CheckD4X_HU();
	//检则是否胡大三元
	bool	CheckD3Y_HU();
	//检测是否胡绿一色
	bool	CheckL1S_HU();
	//检测是否胡九莲宝灯
	bool	Check9LBD_HU();
	//检测是否胡四杠
	bool	Check4Gang_HU();
	//检测是否胡连七对
	bool	CheckL7D_HU();
	//检测是否胡十三幺
	bool	Chekc13Y_HU();
	//检测是否胡清幺九
	bool	CheckQY9_HU();
	//检测是否胡小四喜
	bool	CheckX4X_HU();
	//检测是否胡小三元
	bool	CheckX3Y_HU();
	//检测是否胡字一色
	bool	CheckZ1S_HU();
	//检测是否四暗刻
	bool	Check4AK_HU();
	//检测是否一色双龙会
	bool	Check1S2LH_HU();
	//检测是否一色四同顺
	bool	Check1S4TS_HU();
	//检测是否一色四节高?
	bool	Check1S4JG_HU();
	//检测是否一色四步高?
	bool	Check1S4BG_HU();
	//检测是否三杠
	bool	Check3Gang_HU();
	//检测是否混幺九
	bool	CheckHY9_HU();
	//检测是否七对
	bool	Check7D_HU();
	//检测是否七星不靠
	bool	Check7XBK_HU();
	//检测是否全双刻?
	bool	CheckQSK_HU();
	//清一色
	bool	CheckQ1S_HU();
	//检测是否一色三同顺
	bool	Check1S3TS_HU();
	//检测是否一色三节高
	bool	Check1S3JG_HU();
	//检测是否全大
	bool	CheckQD_HU();
	//检测是否全中
	bool	CheckQZ_HU();
	//检测是否全小
	bool	CheckQX_HU();
	//检测是否青龙
	bool	CheckQL_HU();
	//检测是否三色双龙会
	bool	Check3S2LH_HU();
	//检测是否一色三步高
	bool	Check1S3BG_HU();
	//全带五
	bool	CheckQD5_HU();
	//三同刻
	bool	Check3TK_HU();
	//三暗刻
	bool	Check3AK_HU();
	//单钓将
	bool	CheckDDJ_HU();
	//检测胡
	bool	CheckHU();
private:
	//听牌判断

	//检测是否听九莲宝灯
	bool	Check9LBD_TING();
	//检测是否听十三幺
	bool	Check13Y_TING();
	//检测是否听四暗刻
	bool	Check4AK_TING();
	//检测是否听牌
	bool	CheckTING();

}
;

#endif

其对应的CPP :

#include "CMJ.h"
//构造
CMJ::CMJ()
{
	m_9LBD  = false;
	m_13Y   = false;
	m_4AK	= false;
	m_AKNum = 0;
	m_MKNum = 0;
}

//析构
CMJ::~CMJ()
{

}
//初始化
void   CMJ::Init()
{
	m_9LBD  = false;
	m_13Y   = false;
	m_4AK	= false;
	m_AKNum = 0;
	m_MKNum = 0;
}
//加入新牌,并排序
bool	CMJ::AddPai(int p_Type,int p_Value)
{
	int iSize = m_MyPAIVec[p_Type].size();
	bool t_Find = false;
	vector<	int >::iterator Iter;
	for(Iter = m_MyPAIVec[p_Type].begin();Iter !=m_MyPAIVec[p_Type].end(); Iter++)
	{
		if((*Iter)>p_Value)
		{
			m_MyPAIVec[p_Type].insert(Iter,p_Value);
			t_Find = true;
			break;
		}
		
	}
	
	if(t_Find==false)
	{
		m_MyPAIVec[p_Type].push_back(p_Value);
	}
	m_LastPAI.m_Type	= p_Type;
	m_LastPAI.m_Value	= p_Value;

	return true;
}

//取得对应的牌在牌墙的索引
int	CMJ::GetPaiIndex(int p_Type,int p_Value)
{
	int count = 0;
	for(UINT i = 0 ; i < 6 ; i++ )
	{
		vector<	int >::iterator Iter;
		for(Iter = m_MyPAIVec[i].begin();Iter !=m_MyPAIVec[i].end(); Iter++)
		{
			if(p_Type==i&&(*Iter)==p_Value)
			{
				return count;
			}
			count++;
		}
	}
	return -1;
}
//打牌
bool	CMJ::DelPai(int PaiIndex)
{
	int count = 0;
	for(UINT i = 0 ; i < 6 ; i++ )
	{
		vector<	int >::iterator Iter;
		for(Iter = m_MyPAIVec[i].begin();Iter !=m_MyPAIVec[i].end(); Iter++)
		{
			if(count==PaiIndex)
			{
				m_MyPAIVec[i].erase(Iter);
				return true;
			}
			count++;
		}
	}
	return false;
}
//删除牌
bool	CMJ::DelPai(int p_Type,int p_Value)
{
	vector<	int >::iterator Iter;
	for(Iter = m_MyPAIVec[p_Type].begin();Iter !=m_MyPAIVec[p_Type].end(); Iter++)
	{
		if((*Iter)==p_Value)
		{
			m_MyPAIVec[p_Type].erase(Iter);
			return true;
		}
	}
	return false;
}
//清空牌
void	CMJ::CleanUp()
{
	for(UINT i = 0 ; i < 6 ; i++ )
	{
		m_MyPAIVec[i].clear();
		m_ChiPAIVec[i].clear();
		m_PengPAIVec[i].clear();
		m_GangPAIVec[i].clear();
	}
}
//取得胡牌信息
stGoodInfo		*CMJ::GetInfo()
{
	return &m_GoodInfo;
}

//对所有的牌进行函数调用
void	CMJ::PrintAllPai()
{
	cout<<" ";
	for(UINT i = 0 ; i < 13 ; i++ )
	{
		cout<<i<<"  - ";
	}
	cout<<endl;
	int icount = 0;
	//箭牌
	if(m_MyPAIVec[0].empty()==false)
	{
		vector<	int >::iterator Iter;
		for(Iter = m_MyPAIVec[0].begin();Iter !=m_MyPAIVec[0].end(); Iter++)
		{
			switch(*Iter)
			{
			case 1:
				cout<<"[ 中]";
				break;
			case 2:
				cout<<"[ 发]";
				break;
			case 3:
				cout<<"[ 白]";
				break;

			}
			icount++;
		}

	}
	cout<<endl;
	for(UINT i =0 ; i < icount; i++ )
	{
		cout<<"     ";
	}
	//风牌
	if(m_MyPAIVec[1].empty()==false)
	{
		vector<	int >::iterator Iter;
		for(Iter = m_MyPAIVec[1].begin();Iter !=m_MyPAIVec[1].end(); Iter++)
		{
			switch(*Iter)
			{
			case 1:
				cout<<"[ 东]";
				break;
			case 2:
				cout<<"[ 南]";
				break;
			case 3:
				cout<<"[ 西]";
				break;
			case 4:
				cout<<"[ 北]";
				break;
			}
			icount++;
		}
	}
	cout<<endl;
	for(UINT i =0 ; i < icount; i++ )
	{
		cout<<"     ";
	}
	//万
	if(m_MyPAIVec[2].empty()==false)
	{
		vector<	int >::iterator Iter;
		for(Iter = m_MyPAIVec[2].begin();Iter !=m_MyPAIVec[2].end(); Iter++)
		{
			cout<<"["<<(*Iter)<<"万]";
			icount++;
		}
	}
	cout<<endl;
	for(UINT i =0 ; i < icount; i++ )
	{
		cout<<"     ";
	}
	//条
	if(m_MyPAIVec[3].empty()==false)
	{
		vector<	int >::iterator Iter;
		for(Iter = m_MyPAIVec[3].begin();Iter !=m_MyPAIVec[3].end(); Iter++)
		{
			cout<<"["<<(*Iter)<<"条]";
			icount++;
		}
	}
	cout<<endl;
	for(UINT i =0 ; i < icount; i++ )
	{
		cout<<"     ";
	}
	//饼
	if(m_MyPAIVec[4].empty()==false)
	{
		vector<	int >::iterator Iter;
		for(Iter = m_MyPAIVec[4].begin();Iter !=m_MyPAIVec[4].end(); Iter++)
		{
			cout<<"["<<(*Iter)<<"饼]";
			icount++;
		}
	}
	cout<<endl;
	for(UINT i =0 ; i < icount; i++ )
	{
		cout<<"     ";
	}
}
//对一张牌进行输出
void CMJ::PrintPai(int p_Type,int p_Value)
{
//箭牌
	if(p_Type==0)
	{
		switch(p_Value)
		{
		case 1:
			cout<<"[中]";
			break;
		case 2:
			cout<<"[发]";
			break;
		case 3:
			cout<<"[白]";
			break;
		}
	}
	//风牌
	if(p_Type==1)
	{
		switch(p_Value)
		{
		case 1:
			cout<<"[东]";
			break;
		case 2:
			cout<<"[南]";
			break;
		case 3:
			cout<<"[西]";
			break;
		case 4:
			cout<<"[北]";
			break;
		}
	}
	//万
	if(p_Type==2)
	{
		cout<<"["<<p_Value<<"万]";
	}
	//条
	if(p_Type==3)
	{
		cout<<"["<<p_Value<<"条]";
	}
	//饼
	if(p_Type==4)
	{
		cout<<"["<<p_Value<<"饼]";
	}
}

//吃牌
bool	CMJ::CheckChiPai(int p_Type,int p_Value)
{
	m_TempChiPAIVec.clear();
	//饼
	if(m_MyPAIVec[p_Type].empty()==false)
	{
		int iSize = m_MyPAIVec[p_Type].size();
		if( iSize >= 2)
		{
			for(UINT i = 0 ; i < iSize-1 ;  i++ )
			{
				if((m_MyPAIVec[p_Type][i]==(p_Value-2))&&(m_MyPAIVec[p_Type][i+1]==(p_Value-1)))
				{
					stCHI t_Chi;
					t_Chi.m_Type = p_Type;
					t_Chi.m_Value1 = p_Value-2;
					t_Chi.m_Value2 = p_Value-1;
					t_Chi.m_Value3 = p_Value;
					m_TempChiPAIVec.push_back(t_Chi);

				}
				if((m_MyPAIVec[p_Type][i]==(p_Value-1))&&(m_MyPAIVec[p_Type][i+1]==(p_Value+1)))
				{
					stCHI t_Chi;
					t_Chi.m_Type = p_Type;
					t_Chi.m_Value1 = p_Value-1;
					t_Chi.m_Value2 = p_Value;
					t_Chi.m_Value3 = p_Value+1;
					m_TempChiPAIVec.push_back(t_Chi);

				}
				if((m_MyPAIVec[p_Type][i]==(p_Value+1))&&(m_MyPAIVec[p_Type][i+1]==(p_Value+2)))
				{
					stCHI t_Chi;
					t_Chi.m_Type = p_Type;
					t_Chi.m_Value1 = p_Value;
					t_Chi.m_Value2 = p_Value+1;
					t_Chi.m_Value3 = p_Value+2;
					m_TempChiPAIVec.push_back(t_Chi);
				}
			}

		}
		//假设吃B,已有ABC
		if( iSize >= 3)
		{
			for(UINT i = 1 ; i < iSize-1 ;  i++ )
			{
				if((m_MyPAIVec[p_Type][i-1]==(p_Value-1))&&(m_MyPAIVec[p_Type][i]==p_Value)&&(m_MyPAIVec[p_Type][i+1]==(p_Value+1)))
				{
					stCHI t_Chi;
					t_Chi.m_Type = p_Type;
					t_Chi.m_Value1 = p_Value-1;
					t_Chi.m_Value2 = p_Value;
					t_Chi.m_Value3 = p_Value+1;
					m_TempChiPAIVec.push_back(t_Chi);
				}
			}
		}
		//假设吃B,已有ABBC
		if( iSize >= 4)
		{
			for(UINT i = 1 ; i < iSize-2 ;  i++ )
			{
				if((m_MyPAIVec[p_Type][i-1]==(p_Value-1))&&(m_MyPAIVec[p_Type][i]==p_Value)&&(m_MyPAIVec[p_Type][i+2]==(p_Value+1)))
				{
					stCHI t_Chi;
					t_Chi.m_Type = p_Type;
					t_Chi.m_Value1 = p_Value-1;
					t_Chi.m_Value2 = p_Value;
					t_Chi.m_Value3 = p_Value+1;
					m_TempChiPAIVec.push_back(t_Chi);
				}
			}
		}
		//假设吃B,已有ABBBC
		if( iSize >= 5)
		{
			for(UINT i = 1 ; i < iSize-3 ;  i++ )
			{
				if((m_MyPAIVec[p_Type][i-1]==(p_Value-1))&&(m_MyPAIVec[p_Type][i]==p_Value)&&(m_MyPAIVec[p_Type][i+3]==(p_Value+1)))
				{
					stCHI t_Chi;
					t_Chi.m_Type = p_Type;
					t_Chi.m_Value1 = p_Value-1;
					t_Chi.m_Value2 = p_Value;
					t_Chi.m_Value3 = p_Value+1;
					m_TempChiPAIVec.push_back(t_Chi);
				}
			}
		}
		//假设吃B,已有ABBBBC
		if( iSize >= 6)
		{
			for(UINT i = 1 ; i < iSize-4 ;  i++ )
			{
				if((m_MyPAIVec[p_Type][i-1]==(p_Value-1))&&(m_MyPAIVec[p_Type][i]==p_Value)&&(m_MyPAIVec[p_Type][i+4]==(p_Value+1)))
				{
					stCHI t_Chi;
					t_Chi.m_Type = p_Type;
					t_Chi.m_Value1 = p_Value-1;
					t_Chi.m_Value2 = p_Value;
					t_Chi.m_Value3 = p_Value+1;
					m_TempChiPAIVec.push_back(t_Chi);
				}
			}
		}
		if(m_TempChiPAIVec.size() > 0)
		{
			return	true;
		}
	}
	return false;
}
//吃牌
bool CMJ::DoChiPai(int p_iIndex,int p_Type,int p_Value)
{ 
	AddPai(p_Type,p_Value);
	vector<stCHI>::iterator Iter;
	int icount = 0;
	for(Iter = m_TempChiPAIVec.begin(); Iter != m_TempChiPAIVec.end(); Iter++ )
	{
		if(icount == p_iIndex)
		{
			DelPai((*Iter).m_Type,(*Iter).m_Value1);
			DelPai((*Iter).m_Type,(*Iter).m_Value2);
			DelPai((*Iter).m_Type,(*Iter).m_Value3);

			m_ChiPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value1);
			m_ChiPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value2);
			m_ChiPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value3);

			return true;
		}
		icount++;
	}
	return false;
}
//对可吃的组合进行输出
void CMJ::PrintChiChosePai()
{
	cout<<"================吃牌组合======================="<<endl;
	vector<stCHI>::iterator Iter;
	for(Iter = m_TempChiPAIVec.begin(); Iter != m_TempChiPAIVec.end();Iter++)
	{
		//万
		if((*Iter).m_Type==2)
		{
			cout<<"["<<(*Iter).m_Value1<<"万";
			cout<<""<<(*Iter).m_Value2<<"万";
			cout<<""<<(*Iter).m_Value3<<"万]";
		}
		//条
		if((*Iter).m_Type==3)
		{
			cout<<"["<<(*Iter).m_Value1<<"条";
			cout<<""<<(*Iter).m_Value2<<"条";
			cout<<""<<(*Iter).m_Value3<<"条]";
		}
		//饼
		if((*Iter).m_Type==4)
		{
			cout<<"["<<(*Iter).m_Value1<<"饼";
			cout<<""<<(*Iter).m_Value2<<"饼";
			cout<<""<<(*Iter).m_Value3<<"饼]";
		}
	}
	cout<<endl<<"========================================="<<endl;
}
//对可碰的组合进行输出
void CMJ::PrintPengChosePai()
{
	cout<<"=====================碰牌=================="<<endl;
	vector<stPAI>::iterator Iter;
	for(Iter = m_TempPengPAIVec.begin(); Iter != m_TempPengPAIVec.end();Iter++)
	{
		//万
		if((*Iter).m_Type==2)
		{
			cout<<"["<<(*Iter).m_Value<<"万";
			cout<<""<<(*Iter).m_Value<<"万";
			cout<<""<<(*Iter).m_Value<<"万]";
		}
		//条
		if((*Iter).m_Type==3)
		{
			cout<<"["<<(*Iter).m_Value<<"条";
			cout<<""<<(*Iter).m_Value<<"条";
			cout<<""<<(*Iter).m_Value<<"条]";
		}
		//饼
		if((*Iter).m_Type==4)
		{
			cout<<"["<<(*Iter).m_Value<<"饼";
			cout<<""<<(*Iter).m_Value<<"饼";
			cout<<""<<(*Iter).m_Value<<"饼]";
		}
	}
	cout<<endl<<"========================================="<<endl;
}
//对可杠的组合进行输出
void CMJ::PrintGangChosePai()
{
	cout<<"====================杠牌==================="<<endl;
	vector<stPAI>::iterator Iter;
	for(Iter = m_TempGangPAIVec.begin(); Iter != m_TempGangPAIVec.end();Iter++)
	{
		//万
		if((*Iter).m_Type==2)
		{
			cout<<"["<<(*Iter).m_Value<<"万";
			cout<<""<<(*Iter).m_Value<<"万";
			cout<<""<<(*Iter).m_Value<<"万";
			cout<<""<<(*Iter).m_Value<<"万]";
		}
		//条
		if((*Iter).m_Type==3)
		{
			cout<<"["<<(*Iter).m_Value<<"条";
			cout<<""<<(*Iter).m_Value<<"条";
			cout<<""<<(*Iter).m_Value<<"条";
			cout<<""<<(*Iter).m_Value<<"条]";
		}
		//饼
		if((*Iter).m_Type==4)
		{
			cout<<"["<<(*Iter).m_Value<<"饼";
			cout<<""<<(*Iter).m_Value<<"饼";
			cout<<""<<(*Iter).m_Value<<"饼";
			cout<<""<<(*Iter).m_Value<<"饼]";
		}
	}
	cout<<endl<<"========================================="<<endl;
}
//取得吃牌组合数
UINT CMJ::GetChiChoseNum()
{
	return m_TempChiPAIVec.size();
}
//碰牌
bool	CMJ::CheckPengPai(int p_Type,int p_Value)
{
	m_TempPengPAIVec.clear();
	//饼
	if(m_MyPAIVec[p_Type].empty()==false)
	{
		int iSize = m_MyPAIVec[p_Type].size();
		if( iSize >= 2)
		{
			for(UINT i = 0 ; i < iSize-1 ;  i++ )
			{
				if((m_MyPAIVec[p_Type][i]==p_Value)&&(m_MyPAIVec[p_Type][i+1]==p_Value))
				{
					stPAI t_Peng;
					t_Peng.m_Type	= p_Type;
					t_Peng.m_Value	= p_Value;
					m_TempPengPAIVec.push_back(t_Peng);
					break;
				}
			}
		}
		if(m_TempPengPAIVec.size() > 0)
		{
			return true;
		}
	}
	return false;
}
//碰牌
bool	CMJ::DoPengPai(int p_Type,int p_Value)
{
	AddPai(p_Type,p_Value);
	vector<stPAI>::iterator Iter;
	for(Iter = m_TempPengPAIVec.begin(); Iter != m_TempPengPAIVec.end(); Iter++ )
	{
		DelPai((*Iter).m_Type,(*Iter).m_Value);
		DelPai((*Iter).m_Type,(*Iter).m_Value);
		DelPai((*Iter).m_Type,(*Iter).m_Value);

		m_PengPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value);
		m_PengPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value);
		m_PengPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value);
		return true;
	}
	return false;
}
//杠牌
bool	CMJ::CheckGangPai(int p_Type,int p_Value)
{	
	m_TempGangPAIVec.clear();
	//饼
	if(m_MyPAIVec[p_Type].empty()==false)
	{
		int iSize = m_MyPAIVec[p_Type].size();
		if( iSize >= 3)
		{
			for(UINT i = 0 ; i < iSize-2 ;  i++ )
			{
				if((m_MyPAIVec[p_Type][i]==p_Value)&&(m_MyPAIVec[p_Type][i+1]==p_Value)&&(m_MyPAIVec[p_Type][i+2]==p_Value))
				{
					stPAI t_Gang;
					t_Gang.m_Type	= p_Type;
					t_Gang.m_Value	= p_Value;
					m_TempGangPAIVec.push_back(t_Gang);
					break;
				}
			}
		}
		if(m_TempGangPAIVec.size() > 0)
		{
			return true;
		}
	}
	return false;
}
//杠牌
bool	CMJ::DoGangPai(int p_Type,int p_Value)
{
	AddPai(p_Type,p_Value);
	vector<stPAI>::iterator Iter;
	for(Iter = m_TempGangPAIVec.begin(); Iter != m_TempGangPAIVec.end(); Iter++ )
	{
		DelPai((*Iter).m_Type,(*Iter).m_Value);
		DelPai((*Iter).m_Type,(*Iter).m_Value);
		DelPai((*Iter).m_Type,(*Iter).m_Value);
		DelPai((*Iter).m_Type,(*Iter).m_Value);

		//排序放入
		if(m_GangPAIVec[(*Iter).m_Type].empty())
		{
			m_GangPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value);
			m_GangPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value);
			m_GangPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value);
			m_GangPAIVec[(*Iter).m_Type].push_back((*Iter).m_Value);
		}
		else
		{
				vector<int>::iterator Iter2;
				for(Iter2 = m_GangPAIVec[(*Iter).m_Type].begin(); Iter2 != m_GangPAIVec[(*Iter).m_Type].end(); Iter2++ )
				{
					if((*Iter2)>(*Iter).m_Value)
					{
						m_GangPAIVec[(*Iter).m_Type].insert(Iter2,(*Iter).m_Value);
						m_GangPAIVec[(*Iter).m_Type].insert(Iter2,(*Iter).m_Value);
						m_GangPAIVec[(*Iter).m_Type].insert(Iter2,(*Iter).m_Value);
						m_GangPAIVec[(*Iter).m_Type].insert(Iter2,(*Iter).m_Value);
						break;
					}
				}
		}
		return true;
	}
	return false;
}
//检测是否胡牌
bool	CMJ::CheckAllPai(bool GetOrPut)
{
	if(GetOrPut == MJPAI_GETPAI)
	{
		//检查大四喜
		if(CheckD4X_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"大四喜");
			m_GoodInfo.m_GoodValue = 88;
			return true;
		}
		//检查大三元
		if(CheckD3Y_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"大三元");
			m_GoodInfo.m_GoodValue = 88;
			return true;
		}
		//检查绿一色
		if(CheckL1S_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"绿一色");
			m_GoodInfo.m_GoodValue = 88;
			return true;
		}
		//检查九莲宝灯
		if(Check9LBD_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"九莲宝灯");
			m_GoodInfo.m_GoodValue = 88;
			return true;
		}
		//检查四杠
		if(Check4Gang_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"四杠");
			m_GoodInfo.m_GoodValue = 88;
			return true;
		}
		//检查连七对
		if(CheckL7D_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"连七对");
			m_GoodInfo.m_GoodValue = 88;
			return true;
		}
		//检查十三幺
		if(Chekc13Y_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"十三幺");
			m_GoodInfo.m_GoodValue = 88;
			return true;
		}
		//检查清幺九
		if(CheckQY9_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"清幺九");
			m_GoodInfo.m_GoodValue = 64;
			return true;
		}
		//检查小四喜
		if(CheckX4X_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"小四喜");
			m_GoodInfo.m_GoodValue = 64;
			return true;
		}
		//检查小三元
		if(CheckX3Y_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"小三元");
			m_GoodInfo.m_GoodValue = 64;
			return true;
		}
		//检测是否四暗刻
		if(Check4AK_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"四暗刻");
			m_GoodInfo.m_GoodValue = 64;
			return true;
		}
		//检测是否一色双龙会
		if(Check1S2LH_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"一色双龙会");
			m_GoodInfo.m_GoodValue = 64;
			return true;
		}
		//检测是否一色四同顺
		if(Check1S4TS_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"一色四同顺");
			m_GoodInfo.m_GoodValue = 48;
			return true;
		}
		//检测是否一色四节高
		if(Check1S4JG_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"一色四节高");
			m_GoodInfo.m_GoodValue = 48;
			return true;
		}
		//检测是否一色四步高
		if(Check1S4BG_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"一色四步高");
			m_GoodInfo.m_GoodValue = 32;
			return true;
		}
		//检测是否三杠
		if(Check3Gang_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"三杠");
			m_GoodInfo.m_GoodValue = 32;
			return true;
		}
		
		//检测是否七对
		if(Check7D_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"七对");
			m_GoodInfo.m_GoodValue = 24;
			return true;
		}
		//检测是否七星不靠
		if(Check7XBK_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"七星不靠");
			m_GoodInfo.m_GoodValue = 24;
			return true;
		}
		//检测是否全双刻
		if(CheckQSK_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"全双刻");
			m_GoodInfo.m_GoodValue = 24;
			return true;
		}
		//检测是否清一色
		if(CheckQ1S_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"清一色");
			m_GoodInfo.m_GoodValue = 24;
			return true;
		}
		//检测是否一色三同顺
		if(Check1S3TS_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"一色三同顺");
			m_GoodInfo.m_GoodValue = 24;
			return true;
		}
		//检测是否一色三节高
		if(Check1S3JG_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"一色三节高");
			m_GoodInfo.m_GoodValue = 24;
			return true;
		}
		//检测是否全大
		if(CheckQD_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"全大");
			m_GoodInfo.m_GoodValue = 24;
			return true;
		}
		//检测是否全中
		if(CheckQZ_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"全中");
			m_GoodInfo.m_GoodValue = 24;
			return true;
		}
		//检测是否全小
		if(CheckQX_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"全小");
			m_GoodInfo.m_GoodValue = 24;
			return true;
		}
		//检测是否青龙
		if(CheckQL_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"青龙");
			m_GoodInfo.m_GoodValue = 16;
			return true;
		}
		//检测是否三色双龙会
		if(Check3S2LH_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"三色双龙会");
			m_GoodInfo.m_GoodValue = 16;
			return true;
		}
		//检测是否一色三步高
		if(Check1S3BG_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"一色三步高");
			m_GoodInfo.m_GoodValue = 16;
			return true;
		}
	
		//检测是否单调将
		if(CheckDDJ_HU())
		{
			strcpy(m_GoodInfo.m_GoodName,"单调将");
			m_GoodInfo.m_GoodValue = 1;
			return true;
		}

		//检测是否平胡
		if(CheckHU())
		{
			strcpy(m_GoodInfo.m_GoodName,"平胡");
			m_GoodInfo.m_GoodValue = 1;
			return true;
		}

	}		
	else
	{
		//判断是否听连宝灯
		m_9LBD = Check9LBD_TING(); 
		if(m_9LBD)return true;
		//判断是否听幺
		m_13Y  = Check13Y_TING();
		if(m_13Y)return true;
		//判断是否四暗刻
		m_4AK  = Check4AK_TING();
		if(m_4AK)return true;
		//检测是否听头
		return CheckTING();
	}
	return false;
}

//检测是否胡牌(张)
inline bool	CMJ::CheckAAPai(int iValue1,int iValue2)
{
	if(iValue1 == iValue2)return true;
	return false;
}

//检测是否三连张
inline bool	CMJ::CheckABCPai(int iValue1,int iValue2,int iValue3)
{
	if(iValue1 == (iValue2-1)&&iValue2 == (iValue3-1))return true;
	return false;
}

//检测是否三重张
inline bool	CMJ::CheckAAAPai(int iValue1,int iValue2,int iValue3)
{
	if(iValue1 == iValue2&&iValue2 == iValue3)return true;
	return false;
}

//检测是否四重张
inline bool	CMJ::CheckAAAAPai(int iValue1,int iValue2,int iValue3,int iValue4)
{
	if(iValue1 == iValue2&&iValue2 == iValue3&&iValue3 == iValue4)return true;
	return false;
}
//检测是否三连对
inline bool	CMJ::CheckAABBCCPai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6)
{
	if(iValue1 == iValue2&&iValue3 == iValue4&&iValue5 == iValue6)
	{
		if((iValue1 == iValue3-1)&&(iValue3 == iValue5-1))
		{
			return true;
		}
	}
	return false;	
}
//检测是否三连高压
inline bool	CMJ::CheckAAABBBCCCPai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9)
{
	if((iValue1 == iValue2&&iValue2 == iValue3)&&(iValue4 == iValue5&&iValue5 == iValue6)&&(iValue7 == iValue8&&iValue8 == iValue9))
	{
		if((iValue1 == iValue4-1)&&(iValue4 == iValue7-1))
		{
			return true;
		}
	}
	return false;
}
//检测是否三连刻
inline bool	CMJ::CheckAAAABBBBCCCCPai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11,int iValue12)
{
	if((iValue1 == iValue2&&iValue2 == iValue3&&iValue3 == iValue4)&&(iValue5 == iValue6&&iValue6 == iValue7&&iValue7 == iValue8)&&(iValue9 == iValue10&&iValue10 == iValue11&&iValue11 == iValue12))
	{
		if((iValue1 == iValue5-1)&&(iValue5 == iValue9-1))
		{
			return true;
		}
	}
	return false;
}
//检测是否六连对
inline bool	CMJ::CheckAABBCCDDEEFFPai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11,int iValue12)
{
	if(iValue1 == iValue2&&iValue3 == iValue4&&iValue5 == iValue6&&iValue7 == iValue8&&iValue9 == iValue10&&iValue11 == iValue12)
	{
		if((iValue1 == iValue3-1)&&(iValue3 == iValue5-1)&&(iValue5 == iValue7-1)&&(iValue7 == iValue9-1)&&(iValue9 == iValue11-1))
		{
			return true;
		}
	}
	return false;
}

//检测是否胡牌(张)
bool	CMJ::Check5Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5)
{
	//如果是左边两个为将,右边为三重张或三连张
	if(CheckAAPai(iValue1,iValue2))
	{
		if(Check3Pai(iValue3,iValue4,iValue5))return true;

	}
	//如果中间两个为将
	if(CheckAAAPai(iValue2,iValue3,iValue4))
	{
		if(CheckABCPai(iValue1,iValue4,iValue5))return true;
	}
	//如果是左边两个为将,右边为三重张或三连张
	if(CheckAAPai(iValue4,iValue5))
	{
		if(Check3Pai(iValue1,iValue2,iValue3))return true;
	}

	return false;
}
//检测是否胡牌(张)
bool	CMJ::Check8Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8)
{
	//如果是左边两个为将,右边为三重张或三连张
	if(CheckAAPai(iValue1,iValue2))
	{
		return Check6Pai(iValue3,iValue4,iValue5,iValue6,iValue7,iValue8);
	}

	//如果是中间两个为将,左右边为三重张或三连张
	if(CheckAAPai(iValue4,iValue5))
	{
		if(Check3Pai(iValue1,iValue2,iValue3)&&Check3Pai(iValue6,iValue7,iValue8))return true;
	}

	//如果是右边两个为将,左边为三重张或三连张
	if(CheckAAPai(iValue7,iValue8))
	{
		return Check6Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6);
	}

	return false;
}
//检测是否胡牌(张)
bool	CMJ::Check11Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11)
{
	//如果是左边两个为将
	if(CheckAAPai(iValue1,iValue2))
	{
		return Check9Pai(iValue3,iValue4,iValue5,iValue6,iValue7,iValue8,iValue9,iValue10,iValue11);	
	}

	//如果是中间两个为将
	if(CheckAAPai(iValue4,iValue5))
	{
		//无AAA,全ABC
		if(Check3Pai(iValue1,iValue2,iValue3)&&Check6Pai(iValue4,iValue5,iValue6,iValue7,iValue8,iValue9))return true;	
	}

	//如果是右边两个为将
	if(CheckAAPai(iValue7,iValue8))
	{
		//无AAA,全ABC
		if(Check3Pai(iValue9,iValue10,iValue11)&&Check6Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6))return true;
	}

	//如果是右边两个为将
	if(CheckAAPai(iValue10,iValue11))
	{
		return Check9Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6,iValue7,iValue8,iValue9);
	}
	return false;
}
//检测是否胡牌(张)
bool	CMJ::Check14Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11,int iValue12,int iValue13,int iValue14)
{
	//如果是左边两个为将,右边为三重张或三连张
	if(CheckAAPai(iValue1,iValue2))
	{
		//无AAA,全ABC
		if(Check12Pai(iValue3,iValue4,iValue5,iValue6,iValue7,iValue8,iValue9,iValue10,iValue11,iValue12,iValue13,iValue14))return true;
		return false;
	}

	//如果是中间两个为将,左右边为三重张或三连张
	if(CheckAAPai(iValue4,iValue5))
	{
		//无AAA,全ABC
		if(Check3Pai(iValue1,iValue2,iValue3)&&Check9Pai(iValue6,iValue7,iValue8,iValue9,iValue10,iValue11,iValue12,iValue13,iValue14))return true;
		return false;
	}

	//如果是中间两个为将,左右边为三重张或三连张
	if(CheckAAPai(iValue7,iValue8))
	{
		//无AAA,全ABC
		if(Check6Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6)&&Check6Pai(iValue9,iValue10,iValue11,iValue12,iValue13,iValue14))return true;
		return false;
	}

	//如果是中间两个为将,左右边为三重张或三连张
	if(CheckAAPai(iValue10,iValue11))
	{
		//无AAA,全ABC
		if(Check3Pai(iValue12,iValue13,iValue14)&&Check9Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6,iValue7,iValue8,iValue9))return true;
		return false;
	}

	//如果是右边两个为将,左右边为三重张或三连张
	if(CheckAAPai(iValue13,iValue14))
	{
		//无AAA,全ABC
		if(Check12Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6,iValue7,iValue8,iValue9,iValue10,iValue11,iValue12))return true;
	}
	return false;
}
//检测是否胡牌(张)
bool	CMJ::Check3Pai(int iValue1,int iValue2,int iValue3)
{
	if(CheckABCPai(iValue1,iValue2,iValue3))return true;
	if(CheckAAAPai(iValue1,iValue2,iValue3))return true;
	return false;
}
//检测是否胡牌(张)
bool	CMJ::Check6Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6)
{
	if(Check3Pai(iValue1,iValue2,iValue3)&&Check3Pai(iValue4,iValue5,iValue6))return true;
	if(Check3Pai(iValue1,iValue2,iValue3)&&Check3Pai(iValue4,iValue5,iValue6))return true;
	//三连对
	if(CheckAABBCCPai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6))return true;
	//第一张牌四连张
	if(CheckAAAAPai(iValue2,iValue3,iValue4,iValue5))
	{
		if(CheckABCPai(iValue1,iValue2,iValue6))return true;
	}
	return false;
}

//检测是否胡牌(张)
bool	CMJ::Check9Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9)
{
	if(CheckABCPai(iValue1,iValue2,iValue3)&&Check6Pai(iValue4,iValue5,iValue6,iValue7,iValue8,iValue9))return true;
	if(CheckAAAPai(iValue1,iValue2,iValue3)&&Check6Pai(iValue4,iValue5,iValue6,iValue7,iValue8,iValue9))return true;
	if(CheckABCPai(iValue7,iValue8,iValue9)&&Check6Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6))return true;
	if(CheckAAAPai(iValue7,iValue8,iValue9)&&Check6Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6))return true;

	return false;
}

//检测是否胡牌(张)
bool	CMJ::Check12Pai(int iValue1,int iValue2,int iValue3,int iValue4,int iValue5,int iValue6,int iValue7,int iValue8,int iValue9,int iValue10,int iValue11,int iValue12)
{
	if(CheckABCPai(iValue1,iValue2,iValue3)&&Check9Pai(iValue4,iValue5,iValue6,iValue7,iValue8,iValue9,iValue10,iValue11,iValue12))return true;
	if(CheckAAAPai(iValue1,iValue2,iValue3)&&Check9Pai(iValue4,iValue5,iValue6,iValue7,iValue8,iValue9,iValue10,iValue11,iValue12))return true;
	if(CheckABCPai(iValue10,iValue11,iValue12)&&Check9Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6,iValue7,iValue8,iValue9))return true;
	if(CheckAAAPai(iValue10,iValue11,iValue12)&&Check9Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6,iValue7,iValue8,iValue9))return true;
	if(Check6Pai(iValue1,iValue2,iValue3,iValue4,iValue5,iValue6)&&Check6Pai(iValue7,iValue8,iValue9,iValue10,iValue11,iValue12))return true;

	return false;
}
//检测是否是大四喜
bool	CMJ::CheckD4X_HU()
{
	//东西南北四杠
	if(m_GangPAIVec[1].size()==16)
	{
		//将牌
		for(int i = 0 ; i < 6 ; i++ )
		{
			if(m_MyPAIVec[i].size()==2)
			{
				//如果是将
				if(m_MyPAIVec[i][0] == m_MyPAIVec[i][1])
				{
					return true;
				}
			}
		}
	}
	return false;

}
	
//检则是否是大三元
bool	CMJ::CheckD3Y_HU()
{
	//中发白三杠
	if(m_GangPAIVec[0].size()==12)
	{
		//将牌
		for(int i = 0 ; i < 6 ; i++ )
		{
			if(m_MyPAIVec[i].size()==2)
			{
				//如果是将
				if(m_MyPAIVec[i][0] == m_MyPAIVec[i][1])
				{
					return true;
				}
			}
		}
	}
	return false;
}

//检测是否绿一色
bool	CMJ::CheckL1S_HU()
{
	//只准有发财和条
	if(m_MyPAIVec[1].size()>0)return false;
	if(m_MyPAIVec[2].size()>0)return false;
	if(m_MyPAIVec[4].size()>0)return false;
	if(m_MyPAIVec[5].size()>0)return false;
	if(m_ChiPAIVec[1].size()>0)return false;
	if(m_ChiPAIVec[2].size()>0)return false;
	if(m_ChiPAIVec[4].size()>0)return false;
	if(m_ChiPAIVec[5].size()>0)return false;
	if(m_PengPAIVec[1].size()>0)return false;
	if(m_PengPAIVec[2].size()>0)return false;
	if(m_PengPAIVec[4].size()>0)return false;
	if(m_PengPAIVec[5].size()>0)return false;
	if(m_GangPAIVec[1].size()>0)return false;
	if(m_GangPAIVec[2].size()>0)return false;
	if(m_GangPAIVec[4].size()>0)return false;
	if(m_GangPAIVec[5].size()>0)return false;
	//对发财
	if(m_MyPAIVec[0].size() ==2)
	{
		if(m_MyPAIVec[0][0]==2&&m_MyPAIVec[0][1]==2)
		{
			for(int i = 1 ;i < 6 ; i++)
			{
				if(i==3)continue;
				if(m_MyPAIVec[i].size()>0)return false;
				if(m_ChiPAIVec[i].size()>0)return false;
				if(m_PengPAIVec[i].size()>0)return false;
				if(m_GangPAIVec[i].size()>0)return false;
			}
			//吃
			int iSize = m_ChiPAIVec[3].size();
			if(iSize>0)
			{
				vector<	int >::iterator Iter;
				for(Iter = m_ChiPAIVec[3].begin();Iter != m_ChiPAIVec[3].end();Iter++ )
				{
					if((*Iter)==1)return false;
					if((*Iter)==5)return false;
					if((*Iter)==7)return false;
					if((*Iter)==9)return false;
				}
			}
			//碰
			iSize = m_PengPAIVec[3].size();
			if(iSize>0)
			{
				vector<	int >::iterator Iter;
				for(Iter = m_PengPAIVec[3].begin();Iter != m_PengPAIVec[3].end();Iter++ )
				{
					if((*Iter)==1)return false;
					if((*Iter)==5)return false;
					if((*Iter)==7)return false;
					if((*Iter)==9)return false;
				}
			}
			//杠
			iSize = m_GangPAIVec[3].size();
			if(iSize>0)
			{
				vector<	int >::iterator Iter;
				for(Iter = m_GangPAIVec[3].begin();Iter != m_GangPAIVec[3].end();Iter++ )
				{
					if((*Iter)==1)return false;
					if((*Iter)==5)return false;
					if((*Iter)==7)return false;
					if((*Iter)==9)return false;
				}
			}
			//起
			iSize = m_MyPAIVec[3].size();
			if(iSize>0)
			{
				vector<	int >::iterator Iter;
				for(Iter = m_MyPAIVec[3].begin();Iter != m_MyPAIVec[3].end();Iter++ )
				{
					if((*Iter)==1)return false;
					if((*Iter)==5)return false;
					if((*Iter)==7)return false;
					if((*Iter)==9)return false;
				}

			}
		}
	}
	else
	{
		return false;
	}
	//如果有三张
	if(m_MyPAIVec[3].size() == 3)
	{
		if(Check3Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2]))return true;
	}
	//如果有六张
	if(m_MyPAIVec[3].size() == 6)
	{
		if(Check6Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4],m_MyPAIVec[3][5]))return true;
	}
	//九张
	if(m_MyPAIVec[3].size() == 9)
	{
		if(Check9Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4],m_MyPAIVec[3][5],m_MyPAIVec[3][6],m_MyPAIVec[3][7],m_MyPAIVec[3][8]))return true;
	}
	//十二张
	if(m_MyPAIVec[3].size() == 12)
	{
		if(Check12Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4],m_MyPAIVec[3][5],m_MyPAIVec[3][6],m_MyPAIVec[3][7],m_MyPAIVec[3][8],m_MyPAIVec[3][9],m_MyPAIVec[3][10],m_MyPAIVec[3][11]))return true;
	}
	return	false;
}

//检测是否九莲宝灯(胡)
bool	CMJ::Check9LBD_HU()
{
	if(m_9LBD)//如果已经成九连宝灯牌型
	{
		if(m_MyPAIVec[2].size()==14)return true;
		if(m_MyPAIVec[3].size()==14)return true;
		if(m_MyPAIVec[4].size()==14)return true;
	}
	return false;
}
//检测是否九莲宝灯牌型(听)
bool	CMJ::Check9LBD_TING()
{
	for(UINT i = 2 ; i < 5 ; i++ )
	{
		if(m_MyPAIVec[i].size()==13)
		{
			if(m_MyPAIVec[i][0]==1&&m_MyPAIVec[i][1]==1&&m_MyPAIVec[i][2]==1)
			{
				if(m_MyPAIVec[i][3]==2&&m_MyPAIVec[i][4]==3&&m_MyPAIVec[i][5]==4&&m_MyPAIVec[i][6]==5&&m_MyPAIVec[i][7]==6&&m_MyPAIVec[i][8]==7&&m_MyPAIVec[i][9]==8)
				{
					if(m_MyPAIVec[i][10]==9&&m_MyPAIVec[i][11]==9&&m_MyPAIVec[i][12]==9)
					{
						return true;
					}
				}
			}
		}
	}

	return false;
}
//检测是否是四杠
bool	CMJ::Check4Gang_HU()
{
	int iSize = 0;
	for(UINT i = 0 ; i < 6 ; i++ )
	{
		iSize = m_GangPAIVec[i].size();
	}

	if(iSize == 16)
	{
		//将牌
		for(int i = 0 ; i < 6 ; i++ )
		{
			//如果是将
			if(CheckAAPai(m_MyPAIVec[i][0],m_MyPAIVec[i][1]))
			{
				return true;
			}
		}
		
	}
	return false;
}

//检测是否连七对
bool	CMJ::CheckL7D_HU()
{	
	for(UINT i = 2 ; i < 5 ; i++ )
	{
		if(m_MyPAIVec[i].size()==14)
		{
			if(m_MyPAIVec[i][0]==1&&m_MyPAIVec[i][1]==1&&m_MyPAIVec[i][2]==2&&m_MyPAIVec[i][3]==2&&m_MyPAIVec[i][4]==3&&m_MyPAIVec[i][5]==3&&m_MyPAIVec[i][6]==4&&m_MyPAIVec[i][7]==4&&m_MyPAIVec[i][8]==5&&m_MyPAIVec[i][9]==5&&m_MyPAIVec[i][10]==6&&m_MyPAIVec[i][11]==6&&m_MyPAIVec[i][12]==7&&m_MyPAIVec[i][13]==7)
			{
				return true;
			}
			if(m_MyPAIVec[i][0]==2&&m_MyPAIVec[i][1]==2&&m_MyPAIVec[i][2]==3&&m_MyPAIVec[i][3]==3&&m_MyPAIVec[i][4]==4&&m_MyPAIVec[i][5]==4&&m_MyPAIVec[i][6]==5&&m_MyPAIVec[i][7]==5&&m_MyPAIVec[i][8]==6&&m_MyPAIVec[i][9]==6&&m_MyPAIVec[i][10]==7&&m_MyPAIVec[i][11]==7&&m_MyPAIVec[i][12]==8&&m_MyPAIVec[i][13]==8)
			{
				return true;
			}
			if(m_MyPAIVec[i][0]==3&&m_MyPAIVec[i][1]==3&&m_MyPAIVec[i][2]==4&&m_MyPAIVec[i][3]==4&&m_MyPAIVec[i][4]==5&&m_MyPAIVec[i][5]==5&&m_MyPAIVec[i][6]==6&&m_MyPAIVec[i][7]==6&&m_MyPAIVec[i][8]==7&&m_MyPAIVec[i][9]==7&&m_MyPAIVec[i][10]==8&&m_MyPAIVec[i][11]==8&&m_MyPAIVec[i][12]==9&&m_MyPAIVec[i][13]==9)
			{
				return true;
			}
		}
	}

	return false;
}

//检测是否胡十三幺
bool	CMJ::Chekc13Y_HU()
{
	if(m_13Y)
	{
		bool		i13YSize[13] ;
		for(UINT i = 0 ; i < 13 ; i++ )
		{
			i13YSize[i]=false;
		}
		//中发白
		vector<int>::iterator Iter;
		for(Iter = m_MyPAIVec[0].begin();Iter != m_MyPAIVec[0].end(); Iter++ )
		{
			if((*Iter)==1)
			{
				i13YSize[0]=true;
			}
			if((*Iter)==2)
			{
				i13YSize[1]=true;
			}
			if((*Iter)==3)
			{
				i13YSize[2]=true;
			}
		}
		//东南西北风
		for(Iter = m_MyPAIVec[1].begin();Iter != m_MyPAIVec[1].end(); Iter++ )
		{
			if((*Iter)==1)
			{
				i13YSize[3]=true;
			}
			if((*Iter)==2)
			{
				i13YSize[4]=true;
			}
			if((*Iter)==3)
			{
				i13YSize[5]=true;
			}
			if((*Iter)==4)
			{
				i13YSize[6]=true;
			}
		}
		//一九万
		for(Iter = m_MyPAIVec[2].begin();Iter != m_MyPAIVec[2].end(); Iter++ )
		{
			if((*Iter)==1)
			{
				i13YSize[7]=true;
			}
			if((*Iter)==9)
			{
				i13YSize[8]=true;
			}
		}

		//一九条
		for(Iter = m_MyPAIVec[3].begin();Iter != m_MyPAIVec[3].end(); Iter++ )
		{
			if((*Iter)==1)
			{
				i13YSize[9]=true;
			}
			if((*Iter)==9)
			{
				i13YSize[10]=true;
			}
		}

		//一九饼
		for(Iter = m_MyPAIVec[4].begin();Iter != m_MyPAIVec[4].end(); Iter++ )
		{
			if((*Iter)==1)
			{
				i13YSize[11]=true;
			}
			if((*Iter)==9)
			{
				i13YSize[12]=true;
			}
		}
		int icount = 0;
		for(UINT i = 0 ; i < 13 ; i++ )
		{
			if(i13YSize[i]==true)
			{
				icount++;
			}
		}
		if(icount == 13)return true;

	}
	return false;
}
//检测是否清幺九
bool	CMJ::CheckQY9_HU()
{	
	int iSize = 0;
	int iCount = 0;
	for(UINT i = 2 ; i < 5 ; i++ )
	{
		iSize = m_GangPAIVec[i].size();
		iCount += iSize;
		for(UINT j = 0 ; j < iSize ; j++ )
		{
			if(m_GangPAIVec[i][j]!=1||m_GangPAIVec[i][j]!=9)return false;
		}
	}

	if(iCount == 12)
	{
		if(m_MyPAIVec[2].size()==2)
		{
			if(m_MyPAIVec[2][0]==1&&m_MyPAIVec[2][1]==1)return true;
			if(m_MyPAIVec[2][0]==9&&m_MyPAIVec[2][1]==9)return true;
		}

		if(m_MyPAIVec[3].size()==3)
		{
			if(m_MyPAIVec[3][0]==1&&m_MyPAIVec[3][1]==1)return true;
			if(m_MyPAIVec[3][0]==9&&m_MyPAIVec[3][1]==9)return true;
		}

		if(m_MyPAIVec[4].size()==4)
		{
			if(m_MyPAIVec[4][0]==1&&m_MyPAIVec[4][1]==1)return true;
			if(m_MyPAIVec[4][0]==9&&m_MyPAIVec[4][1]==9)return true;
		}
	}

	return false;

}
//检测是否胡小四喜
bool	CMJ::CheckX4X_HU()
{
	//东西南北四杠
	if(m_GangPAIVec[1].size()==12)
	{
		//将牌的位置
		int iJiangPos = -1;
		//将牌
		for(int i = 0 ; i < 6 ; i++ )
		{
			if(m_MyPAIVec[i].size()==5)
			{
				
				if(Check5Pai(m_MyPAIVec[i][0],m_MyPAIVec[i][1],m_MyPAIVec[i][2],m_MyPAIVec[i][3],m_MyPAIVec[i][4]))
				{
					return true;
				}
			}
			if(m_MyPAIVec[i].size()==2)
			{
				//如果是将
				if(CheckAAPai(m_MyPAIVec[i][0],m_MyPAIVec[i][1]))
				{
					iJiangPos = i;
					break;
				}
			}
		}
		//
		if(iJiangPos>0)
		{
			for(int i = 0 ; i < 6 ; i++ )
			{
				if(i!=iJiangPos)
				{
					if((m_MyPAIVec[i].size()==3))
					{
						if(Check3Pai(m_MyPAIVec[i][0],m_MyPAIVec[i][1],m_MyPAIVec[i][2]))return true;
					}
				}
			}
		}
	}
	return false;
}
//检测是否胡小三元
bool	CMJ::CheckX3Y_HU()
{
	//东西南北四杠
	if(m_GangPAIVec[0].size()==8)
	{	
		if(m_MyPAIVec[0].size()==5)
		{
			if(Check5Pai(m_MyPAIVec[0][0],m_MyPAIVec[0][1],m_MyPAIVec[0][2],m_MyPAIVec[0][3],m_MyPAIVec[0][4]))
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		else if(m_MyPAIVec[0].size()==2)
		{
			//如果是将
			if(CheckAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1])==false)
			{
				return false;
			}
		}
		else
		{
			return false;
		}
		return CheckHU();

	}
	return false;
}
//检测是否胡字一色
bool	CMJ::CheckZ1S_HU()
{	
	//只准有字
	if(m_MyPAIVec[2].size()>0)return false;
	if(m_MyPAIVec[3].size()>0)return false;
	if(m_MyPAIVec[4].size()>0)return false;
	if(m_MyPAIVec[5].size()>0)return false;
	if(m_ChiPAIVec[2].size()>0)return false;
	if(m_ChiPAIVec[3].size()>0)return false;
	if(m_ChiPAIVec[4].size()>0)return false;
	if(m_ChiPAIVec[5].size()>0)return false;
	if(m_PengPAIVec[2].size()>0)return false;
	if(m_PengPAIVec[3].size()>0)return false;
	if(m_PengPAIVec[4].size()>0)return false;
	if(m_PengPAIVec[5].size()>0)return false;
	if(m_GangPAIVec[2].size()>0)return false;
	if(m_GangPAIVec[3].size()>0)return false;
	if(m_GangPAIVec[4].size()>0)return false;
	if(m_GangPAIVec[5].size()>0)return false;
	int iSize = m_MyPAIVec[0].size();
	if(iSize > 0)
	{
		if(iSize == 2)
		{
			if(m_MyPAIVec[0][0]==m_MyPAIVec[0][1])
			{
				iSize = m_MyPAIVec[1].size();
				if(iSize == 0)return true;
				if(iSize == 3)
				{
					if(CheckAAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1],m_MyPAIVec[1][2]))return true;
				}
			}
		}

	}
	return false;
}
//检测是否四暗刻
bool	CMJ::Check4AK_HU()
{
	if(m_4AK)
	{
		//将牌
		for(int i = 0 ; i < 6 ; i++ )
		{
			if(m_MyPAIVec[i].size()==2)
			{
				//如果是将
				if(m_MyPAIVec[i][0] == m_MyPAIVec[i][1])
				{
					return true;
				}
			}
		}
	}
	return false;
}	
//检测是否一色双龙会
bool	CMJ::Check1S2LH_HU()
{
	//万,条,饼
	for(UINT i = 0 ; i <= 4; i++ )
	{
		int iType = i;
		if(m_MyPAIVec[iType].size()==14)
		{

			if(m_MyPAIVec[iType][0]==1&&m_MyPAIVec[iType][1]==1)
			{

			}
			else
			{
				return false;
			}
			if(m_MyPAIVec[iType][2]==2&&m_MyPAIVec[iType][3]==2)
			{

			}
			else
			{
				return false;
			}
			if(m_MyPAIVec[iType][4]==3&&m_MyPAIVec[iType][5]==3)
			{

			}
			else
			{
				return false;
			}
			if(m_MyPAIVec[iType][6]==5&&m_MyPAIVec[iType][7]==5)
			{

			}
			else
			{
				return false;
			}
			if(m_MyPAIVec[iType][8]==7&&m_MyPAIVec[iType][9]==7)
			{

			}
			else
			{
				return false;
			}
			if(m_MyPAIVec[iType][10]==8&&m_MyPAIVec[iType][11]==8)
			{

			}
			else
			{
				return false;
			}
			if(m_MyPAIVec[iType][12]==9&&m_MyPAIVec[iType][13]==9)
			{

			}
			else
			{
				return false;
			}

			return true;
		}
	}
	return false;
}

//检测是否一色四同顺
bool	CMJ::Check1S4TS_HU()
{
	//万,条,饼
	for(UINT i = 0 ; i <= 4; i++ )
	{
		int iType = i;
		//吃过的顺
		int iSize1  =  m_ChiPAIVec[iType].size();
		//剩余牌墙中的顺
		int iSize2  =  m_MyPAIVec[iType].size();
		//万
		if(iSize1 + iSize2 >= 12)
		{
			//无吃的顺
			if(iSize1==0)
			{
				if(iSize2==12)
				{
					//三连暗杠成顺
					if(CheckAAAABBBBCCCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10],m_MyPAIVec[iType][11]))return CheckHU();
					return false;
				}
				if(iSize2==14)
				{
					//三连暗杠成顺
					if((m_MyPAIVec[iType][12]==(m_MyPAIVec[iType][13]))&&CheckAAAABBBBCCCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10],m_MyPAIVec[iType][11]))return true;
					//三连暗杠成顺
					if((m_MyPAIVec[iType][0]==(m_MyPAIVec[iType][1]))&&CheckAAAABBBBCCCCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10],m_MyPAIVec[iType][11],m_MyPAIVec[iType][12],m_MyPAIVec[iType][13]))return true;
					return false;
				}
			}
			//吃到一个顺
			if(iSize1==3)
			{
				if(iSize2==9)
				{
					//三连高压
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][6])
						{
							return CheckHU();
						}
					}
					return false;
				}
				if(iSize2==11)
				{

					//三连高压
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8]))
					{
						if(m_MyPAIVec[iType][9]==m_MyPAIVec[iType][10]&&m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][6])
						{
							return true;
						}
					}
					//三连高压
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10]))
					{
						if(m_MyPAIVec[iType][0]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][5]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][8])
						{
							return true;
						}
					}

					return false;
				}
			}
			//吃到二个顺
			if(iSize1==6)
			{
				if(iSize2==6)
				{
					//三连对
					if(CheckAABBCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][4])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][4])
							{
								return CheckHU();
							}
						}
					}
					return false;
				}
				if(iSize2==8)
				{
					//三连对
					if(CheckAABBCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5]))
					{
						if(m_MyPAIVec[iType][6]==m_MyPAIVec[iType][7]&&m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][4])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][4])
							{
								return true;
							}

						}
					}

					//三连对
					if(CheckAABBCCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7]))
					{
						if(m_MyPAIVec[iType][0]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][4]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][6])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][4]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][6])
							{
								return true;
							}
						}
					}

					return false;
				}
			}
			//吃到三个顺
			if(iSize1==9)
			{
				if(iSize2==3)
				{
					//顺子
					if(CheckABCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][2])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][2])
							{
								if(m_ChiPAIVec[iType][6]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][7]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][8]==m_MyPAIVec[iType][2])
								{				
									return CheckHU();
								}
							}
						}
					}
					return false;
				}
				if(iSize2==5)
				{
					//顺子
					if(CheckABCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2]))
					{
						if(m_MyPAIVec[iType][3]==m_MyPAIVec[iType][4]&&m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][2])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][2])
							{
								if(m_ChiPAIVec[iType][6]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][7]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][8]==m_MyPAIVec[iType][2])
								{	
									return true;
								}
							}
						}
					}

					//顺子
					if(CheckABCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4]))
					{
						if(m_MyPAIVec[iType][0]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][4])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][4])
							{
								if(m_ChiPAIVec[iType][6]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][7]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][8]==m_MyPAIVec[iType][4])
								{	
									return true;
								}

							}
						}
					}
					return false;
				}
			}

			//吃到三连顺
			if(iSize1==12)
			{
				if((m_ChiPAIVec[iType][0]==m_ChiPAIVec[iType][3])&&(m_ChiPAIVec[iType][3]==m_ChiPAIVec[iType][6])&&(m_ChiPAIVec[iType][6]==m_ChiPAIVec[iType][9]))
				{
					return CheckHU();
				}
				return false;
			}
		}

	}
	return false;
}

//检测是否一色四节高
bool	CMJ::Check1S4JG_HU()
{
	//万,条,饼
	for(UINT i = 2 ; i <= 4 ; i++)
	{
		int iType = i;
		if(m_GangPAIVec[iType].size()==16)
		{
			if((m_GangPAIVec[iType][0]==m_GangPAIVec[iType][4]-1)&&(m_GangPAIVec[iType][4]==m_GangPAIVec[iType][8]-1)&&(m_GangPAIVec[iType][8]==m_GangPAIVec[iType][12]-1))
			{
				return CheckHU();
			}
		}
	}
	return false;
}
//检测是否一色四步高
bool	CMJ::Check1S4BG_HU()
{
	/*//万
	if(m_GangPAIVec[2].size()==16)
	{
		if((m_GangPAIVec[2][0]==m_GangPAIVec[2][4]-1)&&(m_GangPAIVec[2][4]==m_GangPAIVec[2][8]-1)&&(m_GangPAIVec[2][8]==m_GangPAIVec[2][12]-1))
		{
			return CheckHU();
		}
	}*/
	return false;
}
//检测是否三杠
bool	CMJ::Check3Gang_HU()
{
	int iSize = 0;
	for(UINT i = 0 ; i < 6 ; i++ )
	{
		iSize = m_GangPAIVec[i].size();
	}

	if(iSize == 12)
	{
		//将牌
		return CheckHU();
		
	}
	return false;
}
//检测是否混幺九
bool	CMJ::CheckHY9_HU()
{
	return false;
}
//检测是否七对
bool	CMJ::Check7D_HU()
{
	int iDoubleNum = 0 ;
	for(UINT i = 0 ; i < 6 ; i++ )
	{
		UINT iSize = m_MyPAIVec[i].size();
		if(iSize%2 ==1||iSize==0)return false;//如果是奇数肯定不是对
		for(UINT j = 0 ; j < iSize-1 ; j++)
		{
			if(m_MyPAIVec[i][j]==m_MyPAIVec[i][j+1])
			{
				iDoubleNum++;
				j++;
			}
		}
	}
	if(iDoubleNum==7)return true;
	return false;
}
//检测是否七星不靠
bool	CMJ::Check7XBK_HU()
{
	bool		bIs7XBK[14] ;
	for(UINT i = 0 ; i < 14 ; i++ )
	{
		bIs7XBK[i]=false;
	}
	//中发白
	vector<int>::iterator Iter;
	if(m_MyPAIVec[0].size()!=3)return false;
	for(Iter = m_MyPAIVec[0].begin();Iter != m_MyPAIVec[0].end(); Iter++ )
	{
		if((*Iter)==1)
		{
			bIs7XBK[7]=true;
		}
		if((*Iter)==2)
		{
			bIs7XBK[8]=true;
		}
		if((*Iter)==3)
		{
			bIs7XBK[9]=true;
		}
	}
	//东南西北风
	if(m_MyPAIVec[1].size()!=4)return false;
	for(Iter = m_MyPAIVec[1].begin();Iter != m_MyPAIVec[1].end(); Iter++ )
	{
		if((*Iter)==1)
		{
			bIs7XBK[10]=true;
		}
		if((*Iter)==2)
		{
			bIs7XBK[11]=true;
		}
		if((*Iter)==3)
		{
			bIs7XBK[12]=true;
		}
		if((*Iter)==4)
		{
			bIs7XBK[13]=true;
		}
	}
	//万,条,饼
	for(UINT i = 2 ; i <= 4 ; i++)
	{
		if(m_MyPAIVec[i].size()==3)
		{
			if(m_MyPAIVec[i][0]==1&&m_MyPAIVec[i][1]==4&&m_MyPAIVec[i][2]==7)
			{
				bIs7XBK[0]=true;
				bIs7XBK[3]=true;
				bIs7XBK[6]=true;
			}
		}
		else if(m_MyPAIVec[2].size()==2)
		{
			if(m_MyPAIVec[i][0]==2&&m_MyPAIVec[i][1]==5)
			{
				bIs7XBK[1]=true;
				bIs7XBK[4]=true;
			}
			else if(m_MyPAIVec[i][0]==3&&m_MyPAIVec[i][1]==6)
			{
				bIs7XBK[2]=true;
				bIs7XBK[5]=true;
			}
		}
		else
		{
			return false;
		}
	}
	bool t_Result = true;
	for(UINT i = 0 ; i < 14 ; i++ )
	{
		if(bIs7XBK[i]==false)t_Result=false;
	}
	if(t_Result)return true;

	for(UINT i = 2 ; i <= 4 ; i++)
	{
		if(m_MyPAIVec[i].size()==3)
		{
			if(m_MyPAIVec[i][0]==2&&m_MyPAIVec[i][1]==5&&m_MyPAIVec[i][2]==8)
			{
				bIs7XBK[0]=true;
				bIs7XBK[3]=true;
				bIs7XBK[6]=true;
			}
		}
		else if(m_MyPAIVec[2].size()==2)
		{
			if(m_MyPAIVec[i][0]==3&&m_MyPAIVec[i][1]==6)
			{
				bIs7XBK[1]=true;
				bIs7XBK[4]=true;
			}
			else if(m_MyPAIVec[i][0]==4&&m_MyPAIVec[i][1]==7)
			{
				bIs7XBK[2]=true;
				bIs7XBK[5]=true;
			}
		}
		else
		{
			return false;
		}
	}
	t_Result = true;
	for(UINT i = 0 ; i < 14 ; i++ )
	{
		if(bIs7XBK[i]==false)t_Result=false;
	}
	if(t_Result)return true;

	for(UINT i = 2 ; i <= 4 ; i++)
	{
		if(m_MyPAIVec[i].size()==3)
		{
			if(m_MyPAIVec[i][0]==3&&m_MyPAIVec[i][1]==6&&m_MyPAIVec[i][2]==9)
			{
				bIs7XBK[0]=true;
				bIs7XBK[3]=true;
				bIs7XBK[6]=true;
			}
		}
		else if(m_MyPAIVec[2].size()==2)
		{
			if(m_MyPAIVec[i][0]==4&&m_MyPAIVec[i][1]==7)
			{
				bIs7XBK[1]=true;
				bIs7XBK[4]=true;
			}
			else if(m_MyPAIVec[i][0]==5&&m_MyPAIVec[i][1]==8)
			{
				bIs7XBK[2]=true;
				bIs7XBK[5]=true;
			}
		}
		else
		{
			return false;
		}
	}

	t_Result = true;
	for(UINT i = 0 ; i < 14 ; i++ )
	{
		if(bIs7XBK[i]==false)t_Result=false;
	}
	if(t_Result)return true;

	return false;
}
//检测是否全双刻
bool	CMJ::CheckQSK_HU()
{
	//万,条,饼
	for(UINT i = 2 ; i <= 4 ; i++)
	{
		int iType = i;
		if(m_GangPAIVec[iType].size()==16)
		{
			if(m_GangPAIVec[iType][0]==2&&m_GangPAIVec[iType][4]==4&&m_GangPAIVec[iType][8]==6&&m_GangPAIVec[iType][12]==8)
			{
				return CheckHU();
			}
		}
	}
	return false;
}
//清一色
bool	CMJ::CheckQ1S_HU()
{

	if(m_MyPAIVec[0].empty()==false)return false;
	if(m_MyPAIVec[1].empty()==false)return false;
	if(m_ChiPAIVec[0].empty()==false)return false;
	if(m_ChiPAIVec[1].empty()==false)return false;
	if(m_PengPAIVec[0].empty()==false)return false;
	if(m_PengPAIVec[1].empty()==false)return false;
	if(m_GangPAIVec[0].empty()==false)return false;
	if(m_GangPAIVec[1].empty()==false)return false;
	//清万
	if(m_MyPAIVec[2].empty()==false)
	{
		if(m_MyPAIVec[3].empty()==false)return false;
		if(m_MyPAIVec[4].empty()==false)return false;
		if(m_ChiPAIVec[3].empty()==false)return false;
		if(m_ChiPAIVec[4].empty()==false)return false;
		if(m_PengPAIVec[3].empty()==false)return false;
		if(m_PengPAIVec[4].empty()==false)return false;
		if(m_GangPAIVec[3].empty()==false)return false;
		if(m_GangPAIVec[4].empty()==false)return false;
		return CheckHU();
	}
	//清条
	if(m_MyPAIVec[3].empty()==false)
	{
		if(m_MyPAIVec[2].empty()==false)return false;
		if(m_MyPAIVec[4].empty()==false)return false;
		if(m_ChiPAIVec[2].empty()==false)return false;
		if(m_ChiPAIVec[4].empty()==false)return false;
		if(m_PengPAIVec[2].empty()==false)return false;
		if(m_PengPAIVec[4].empty()==false)return false;
		if(m_GangPAIVec[2].empty()==false)return false;
		if(m_GangPAIVec[4].empty()==false)return false;
		return CheckHU();
	}
	//清饼
	if(m_MyPAIVec[4].empty()==false)
	{
		if(m_MyPAIVec[2].empty()==false)return false;
		if(m_MyPAIVec[3].empty()==false)return false;
		if(m_ChiPAIVec[2].empty()==false)return false;
		if(m_ChiPAIVec[3].empty()==false)return false;
		if(m_PengPAIVec[2].empty()==false)return false;
		if(m_PengPAIVec[3].empty()==false)return false;
		if(m_GangPAIVec[2].empty()==false)return false;
		if(m_GangPAIVec[3].empty()==false)return false;
		return CheckHU();
	}
	return false;
}
//检测是否一色三同顺
bool	CMJ::Check1S3TS_HU()
{
	//万条饼
	for(UINT i = 2 ; i <= 4 ; i++ )
	{
		int iType = i;
		//吃过的顺
		int iSize1  =  m_ChiPAIVec[iType].size();
		//剩余牌墙中的顺
		int iSize2  =  m_MyPAIVec[iType].size();
		if(iSize1 + iSize2 >= 9)
		{
			//无吃的顺
			if(iSize1==0)
			{
				if(iSize2==9)
				{
					//三连高压
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8]))
					{
						return CheckHU();
					}
					return false;
				}
				if(iSize2==11)
				{
					//三连高压
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8])&&CheckAAPai(m_MyPAIVec[iType][9],m_MyPAIVec[iType][10]))
					{
						return true;
					}
					//三连高压
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10])&&CheckAAPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1]))
					{
						return true;
					}
					return false;
				}
				if(iSize2==12)
				{
					//三连高压
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8])&&Check3Pai(m_MyPAIVec[iType][9],m_MyPAIVec[iType][10],m_MyPAIVec[iType][11]))
					{
						return CheckHU();
					}
					//三连高压
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10],m_MyPAIVec[iType][11])&&Check3Pai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2]))
					{
						return CheckHU();
					}	
					return false;
				}
				if(iSize2==14)
				{
					//三连顺(前)
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8])&&Check5Pai(m_MyPAIVec[iType][9],m_MyPAIVec[iType][10],m_MyPAIVec[iType][11],m_MyPAIVec[iType][12],m_MyPAIVec[iType][13]))
					{
						return true;
					}
					//三连顺(中)
					if(CheckAAPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1])&&CheckAAABBBCCCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10])&&Check3Pai(m_MyPAIVec[iType][11],m_MyPAIVec[iType][12],m_MyPAIVec[iType][13]))
					{
						return true;
					}	
					//三连顺(中)
					if(Check3Pai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2])&&CheckAAABBBCCCPai(m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10],m_MyPAIVec[iType][11])&&CheckAAPai(m_MyPAIVec[iType][12],m_MyPAIVec[iType][13]))
					{
						return true;
					}	
					//三连顺(后)
					if(CheckAAABBBCCCPai(m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10],m_MyPAIVec[iType][11],m_MyPAIVec[iType][12],m_MyPAIVec[iType][13])&&Check5Pai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4]))
					{
						return true;
					}
					return false;
				}
			}
			//吃到一个顺
			if(iSize1==3)
			{
				if(iSize2==6)
				{
					//三连对
					if(CheckAABBCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][2])
						{
							return CheckHU();
						}
					}
				}
				if(iSize2==8)
				{
					//三连对(前)
					if(CheckAABBCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5])&&CheckAAPai(m_MyPAIVec[iType][6],m_MyPAIVec[iType][7]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][4])
						{
							return true;
						}
					}
					//三连对(后)
					if(CheckAABBCCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7])&&CheckAAPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][4]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][6])
						{
							return true;
						}
					}
				}
				if(iSize2==9)
				{
					//三连对(前)
					if(CheckAABBCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5])&&Check3Pai(m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][4])
						{
							return CheckHU();
						}
					}
					//三连对(后)
					if(CheckAABBCCPai(m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8])&&Check3Pai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][5]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][7])
						{
							return CheckHU();
						}
					}
				}
				if(iSize2==11)
				{
					//三连对(前)
					if(CheckAABBCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5])&&Check5Pai(m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][4])
						{
							return true;
						}
					}
					//三连对(中)
					if(CheckAAPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1])&&CheckAABBCCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7])&&Check3Pai(m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][4]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][6])
						{
							return true;
						}
					}
					//三连对(中)
					if(Check3Pai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2])&&CheckAABBCCPai(m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8])&&CheckAAPai(m_MyPAIVec[iType][9],m_MyPAIVec[iType][10]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][5]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][7])
						{
							return true;
						}
					}
					//三连对(前)
					if(CheckAABBCCPai(m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7],m_MyPAIVec[iType][8],m_MyPAIVec[iType][9],m_MyPAIVec[iType][10])&&Check5Pai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][5]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][7]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][9])
						{
							return true;
						}
					}
				}
			}
			//吃到二个顺
			if(iSize1==6)
			{
				if(iSize2==3)
				{
					//顺子
					if(CheckABCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][2])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][2])
							{
								return CheckHU();
							}
						}
					}
				}
				if(iSize2==5)
				{
					//顺子(前)
					if(CheckABCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2])&&CheckAAPai(m_MyPAIVec[iType][3],m_MyPAIVec[iType][4]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][2])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][2])
							{
								return true;
							}
						}
					}
					//顺子(后)
					if(CheckABCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4])&&CheckAAPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][4])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][4])
							{
								return true;
							}
						}
					}
				}
				if(iSize2==6)
				{
					//三连对
					if(CheckAABBCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][4])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][5])
							{
								return CheckHU();
							}
						}
					}
				}
				if(iSize2==8)
				{
					//三连对(前)
					if(CheckAABBCCPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1],m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5])&&CheckAAPai(m_MyPAIVec[iType][6],m_MyPAIVec[iType][7]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][0]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][4])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][1]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][5])
							{
								return true;
							}
						}
					}
					//三连对(后)
					if(CheckAABBCCPai(m_MyPAIVec[iType][2],m_MyPAIVec[iType][3],m_MyPAIVec[iType][4],m_MyPAIVec[iType][5],m_MyPAIVec[iType][6],m_MyPAIVec[iType][7])&&CheckAAPai(m_MyPAIVec[iType][0],m_MyPAIVec[iType][1]))
					{
						if(m_ChiPAIVec[iType][0]==m_MyPAIVec[iType][2]&&m_ChiPAIVec[iType][1]==m_MyPAIVec[iType][4]&&m_ChiPAIVec[iType][2]==m_MyPAIVec[iType][6])
						{
							if(m_ChiPAIVec[iType][3]==m_MyPAIVec[iType][3]&&m_ChiPAIVec[iType][4]==m_MyPAIVec[iType][5]&&m_ChiPAIVec[iType][5]==m_MyPAIVec[iType][7])
							{
								return true;
							}
						}
					}
				}
			}
			//吃到三个顺
			if(iSize1==9)
			{
				if((m_ChiPAIVec[2][0]==m_ChiPAIVec[iType][3])&&(m_ChiPAIVec[iType][3]==m_ChiPAIVec[iType][6]))
				{
					return CheckHU();
				}
			}
		}
	}
	return false;
}
//检测是否一色三节高
bool	CMJ::Check1S3JG_HU()
{
	//万,条,饼
	for(UINT i = 2 ; i <= 4 ; i++)
	{
		int iType = i;
		if(m_GangPAIVec[iType].size()==12)
		{
			if((m_GangPAIVec[iType][0]==m_GangPAIVec[iType][4]-1)&&(m_GangPAIVec[iType][4]==m_GangPAIVec[iType][8]-1))
			{
				return CheckHU();
			}
		}
	}
	return false;
}
//检测是否全大
bool	CMJ::CheckQD_HU()
{
	//剑牌,风牌
	if(m_MyPAIVec[0].empty()==false)return false;
	if(m_MyPAIVec[1].empty()==false)return false;
	if(m_ChiPAIVec[0].empty()==false)return false;
	if(m_ChiPAIVec[1].empty()==false)return false;
	if(m_PengPAIVec[0].empty()==false)return false;
	if(m_PengPAIVec[1].empty()==false)return false;
	if(m_GangPAIVec[0].empty()==false)return false;
	if(m_GangPAIVec[1].empty()==false)return false;
	
	//万,条,饼
	for(UINT i = 2 ; i <= 4 ; i++ )
	{
		if(m_MyPAIVec[i].empty()==false)
		{
			//剩余牌墙
			int iSize = m_MyPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_MyPAIVec[i][j]<7)return false;
			}
			//吃
			iSize = m_ChiPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_ChiPAIVec[i][j]<7)return false;
			}
			//碰
			iSize = m_PengPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_PengPAIVec[i][j]<7)return false;
			}
			//杠
			iSize = m_GangPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_GangPAIVec[i][j]<7)return false;
			}
		}
	}
	return CheckHU();
}

//检测是否全中
bool	CMJ::CheckQZ_HU()
{
//剑牌,风牌
	if(m_MyPAIVec[0].empty()==false)return false;
	if(m_MyPAIVec[1].empty()==false)return false;
	if(m_ChiPAIVec[0].empty()==false)return false;
	if(m_ChiPAIVec[1].empty()==false)return false;
	if(m_PengPAIVec[0].empty()==false)return false;
	if(m_PengPAIVec[1].empty()==false)return false;
	if(m_GangPAIVec[0].empty()==false)return false;
	if(m_GangPAIVec[1].empty()==false)return false;
	
	//万,条,饼
	for(UINT i = 2 ; i <= 4 ; i++ )
	{
		if(m_MyPAIVec[i].empty()==false)
		{
			//剩余牌墙
			int iSize = m_MyPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_MyPAIVec[i][j]<4)return false;
				if(m_MyPAIVec[i][j]>6)return false;
			}
			//吃
			iSize = m_ChiPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_ChiPAIVec[i][j]<4)return false;
				if(m_ChiPAIVec[i][j]>6)return false;
			}
			//碰
			iSize = m_PengPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_PengPAIVec[i][j]<4)return false;
				if(m_PengPAIVec[i][j]>6)return false;
			}
			//杠
			iSize = m_GangPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_GangPAIVec[i][j]<4)return false;
				if(m_GangPAIVec[i][j]>6)return false;
			}
		}
	}
	return CheckHU();
}

//检测是否全小
bool	CMJ::CheckQX_HU()
{
	//剑牌,风牌
	if(m_MyPAIVec[0].empty()==false)return false;
	if(m_MyPAIVec[1].empty()==false)return false;
	if(m_ChiPAIVec[0].empty()==false)return false;
	if(m_ChiPAIVec[1].empty()==false)return false;
	if(m_PengPAIVec[0].empty()==false)return false;
	if(m_PengPAIVec[1].empty()==false)return false;
	if(m_GangPAIVec[0].empty()==false)return false;
	if(m_GangPAIVec[1].empty()==false)return false;
	
	//万,条,饼
	for(UINT i = 2 ; i <= 4 ; i++ )
	{
		if(m_MyPAIVec[i].empty()==false)
		{
			//剩余牌墙
			int iSize = m_MyPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_MyPAIVec[i][j]>3)return false;
			}
			//吃
			iSize = m_ChiPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_ChiPAIVec[i][j]>3)return false;
			}
			//碰
			iSize = m_PengPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_PengPAIVec[i][j]>3)return false;
			}
			//杠
			iSize = m_GangPAIVec[i].size();
			for( UINT j = 0 ; j < iSize ; j++ )
			{
				if(m_GangPAIVec[i][j]>3)return false;
			}
		}
	}
	return CheckHU();
}
//检测是否青龙
bool	CMJ::CheckQL_HU()
{
	//花色
	int iColorNum = 0;
	//万,条,饼
	for(UINT i = 2 ; i <= 4 ; i++ )
	{
		if(m_MyPAIVec[i].empty()==false)
		{
			iColorNum++;

			if(m_MyPAIVec[i].size()==9)
			{
				for(UINT j = 0 ;j < 9 ;j++)
				{
					if(m_MyPAIVec[i][j] != j)return false;
				}
			}
		}
	}
	if(iColorNum==1)return CheckHU();
	return false;
}
//检测是否三色双龙会
bool	CMJ::Check3S2LH_HU()
{
	//五万为双龙
	if(m_MyPAIVec[2].size()==2)
	{
		//双龙
		if(m_MyPAIVec[2][0]==5&&m_MyPAIVec[2][1]==5)
		{
			//老少副
			if(m_MyPAIVec[3].size()==6&&m_MyPAIVec[4].size()==6)
			{
				if(m_MyPAIVec[3][0]==1&&m_MyPAIVec[3][1]==2&&m_MyPAIVec[3][2]==3&&m_MyPAIVec[3][3]==7&&m_MyPAIVec[3][4]==8&&m_MyPAIVec[3][5]==9)
				{
					if(m_MyPAIVec[4][0]==1&&m_MyPAIVec[4][1]==2&&m_MyPAIVec[4][2]==3&&m_MyPAIVec[4][3]==7&&m_MyPAIVec[4][4]==8&&m_MyPAIVec[4][5]==9)
					{
						return true;
					}
				}
			}
		}
	}

	//五条为双龙
	if(m_MyPAIVec[3].size()==2)
	{
		//双龙
		if(m_MyPAIVec[3][0]==5&&m_MyPAIVec[3][1]==5)
		{
			//老少副
			if(m_MyPAIVec[2].size()==6&&m_MyPAIVec[4].size()==6)
			{
				if(m_MyPAIVec[2][0]==1&&m_MyPAIVec[2][1]==2&&m_MyPAIVec[2][2]==3&&m_MyPAIVec[2][3]==7&&m_MyPAIVec[2][4]==8&&m_MyPAIVec[2][5]==9)
				{
					if(m_MyPAIVec[4][0]==1&&m_MyPAIVec[4][1]==2&&m_MyPAIVec[4][2]==3&&m_MyPAIVec[4][3]==7&&m_MyPAIVec[4][4]==8&&m_MyPAIVec[4][5]==9)
					{
						return true;
					}
				}
			}
		}
	}

	//五饼为双龙
	if(m_MyPAIVec[4].size()==2)
	{
		//双龙
		if(m_MyPAIVec[4][0]==5&&m_MyPAIVec[4][1]==5)
		{
			//老少副
			if(m_MyPAIVec[2].size()==6&&m_MyPAIVec[3].size()==6)
			{
				if(m_MyPAIVec[2][0]==1&&m_MyPAIVec[2][1]==2&&m_MyPAIVec[2][2]==3&&m_MyPAIVec[2][3]==7&&m_MyPAIVec[2][4]==8&&m_MyPAIVec[2][5]==9)
				{
					if(m_MyPAIVec[3][0]==1&&m_MyPAIVec[3][1]==2&&m_MyPAIVec[3][2]==3&&m_MyPAIVec[3][3]==7&&m_MyPAIVec[3][4]==8&&m_MyPAIVec[3][5]==9)
					{
						return true;
					}
				}
			}
		}
	}
	return false;
}

//检测是否一色三步高
bool	CMJ::Check1S3BG_HU()
{
	return false;
}
//全带五
bool	CMJ::CheckQD5_HU()
{
	//剑牌,风牌
	if(m_MyPAIVec[0].empty()==false)return false;
	if(m_MyPAIVec[1].empty()==false)return false;
	if(m_ChiPAIVec[0].empty()==false)return false;
	if(m_ChiPAIVec[1].empty()==false)return false;
	if(m_PengPAIVec[0].empty()==false)return false;
	if(m_PengPAIVec[1].empty()==false)return false;
	if(m_GangPAIVec[0].empty()==false)return false;
	if(m_GangPAIVec[1].empty()==false)return false;
	return false;
}
//三同刻
bool	CMJ::Check3TK_HU()
{
	/*if(m_GangPAIVec[2].size()==4)
	{
		//万,条,饼
		for(UINT i = 3 ; i <= 4 ; i++ )
		{
			if(m_GangPAIVec[i].size()==4)
			{
				if(m_GangPAIVec[2][0]!=m_GangPAIVec[i][0])
				{
					return false;
				}
			}
		}		
	}*/
	return false;

}
//三暗刻
bool	CMJ::Check3AK_HU()
{
	return false;
}
//单钓将
bool	CMJ::CheckDDJ_HU()
{
	int count = 0;
	for(UINT i = 0 ; i < 6 ; i++ )
	{
		count += m_MyPAIVec[i].size();
	}
	if(count==2)
	{
		if(m_MyPAIVec[m_LastPAI.m_Type].size()==2)
		{
			if(m_MyPAIVec[m_LastPAI.m_Type][0]==m_MyPAIVec[m_LastPAI.m_Type][1])return true;
		}
	}
	return false;
}
//检测是否听十三幺
bool	CMJ::Check13Y_TING()
{
	bool		i13YSize[13] ;
	for(UINT i = 0 ; i < 13 ; i++ )
	{
		i13YSize[i]=false;
	}
	//中发白
	vector<int>::iterator Iter;
	for(Iter = m_MyPAIVec[0].begin();Iter != m_MyPAIVec[0].end(); Iter++ )
	{
		if((*Iter)==1)
		{
			i13YSize[0]=true;
		}
		if((*Iter)==2)
		{
			i13YSize[1]=true;
		}
		if((*Iter)==3)
		{
			i13YSize[2]=true;
		}
	}
	//东南西北风
	for(Iter = m_MyPAIVec[1].begin();Iter != m_MyPAIVec[1].end(); Iter++ )
	{
		if((*Iter)==1)
		{
			i13YSize[3]=true;
		}
		if((*Iter)==2)
		{
			i13YSize[4]=true;
		}
		if((*Iter)==3)
		{
			i13YSize[5]=true;
		}
		if((*Iter)==4)
		{
			i13YSize[6]=true;
		}
	}
	//一九万
	for(Iter = m_MyPAIVec[2].begin();Iter != m_MyPAIVec[2].end(); Iter++ )
	{
		if((*Iter)==1)
		{
			i13YSize[7]=true;
		}
		if((*Iter)==9)
		{
			i13YSize[8]=true;
		}
	}

	//一九条
	for(Iter = m_MyPAIVec[3].begin();Iter != m_MyPAIVec[3].end(); Iter++ )
	{
		if((*Iter)==1)
		{
			i13YSize[9]=true;
		}
		if((*Iter)==9)
		{
			i13YSize[10]=true;
		}
	}
	
	//一九饼
	for(Iter = m_MyPAIVec[4].begin();Iter != m_MyPAIVec[4].end(); Iter++ )
	{
		if((*Iter)==1)
		{
			i13YSize[11]=true;
		}
		if((*Iter)==9)
		{
			i13YSize[12]=true;
		}
	}
	int icount = 0;
	for(UINT i = 0 ; i < 13 ; i++ )
	{
		if(i13YSize[i]==true)
		{
			icount++;
		}
	}
	if(icount >=12)return true;
	return	false;
}
//检测是否听四暗刻
bool	CMJ::Check4AK_TING()
{
	if(m_AKNum==4)return true;
	return false;
}
//检测胡
bool	CMJ::CheckHU()
{
	bool t_Ok = false;
	int iJiangNum = 0;

	int iSize = m_MyPAIVec[0].size();
	if(iSize>0)
	{
		//中发白
		if(iSize==2)
		{
			if(!CheckAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==3)
		{
			if(!CheckAAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1],m_MyPAIVec[0][2]))
			{
				return false;
			}
		}
		else if(iSize==5)
		{
			if(CheckAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1])&&CheckAAAPai(m_MyPAIVec[0][2],m_MyPAIVec[0][3],m_MyPAIVec[0][4]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1],m_MyPAIVec[0][2])&&CheckAAPai(m_MyPAIVec[0][3],m_MyPAIVec[0][4]))
			{
				iJiangNum++ ;
			}
			else
			{
				return false;
			}
		}
		else if(iSize==8)
		{
			if(CheckAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1])&&CheckAAAPai(m_MyPAIVec[0][2],m_MyPAIVec[0][3],m_MyPAIVec[0][4])&&CheckAAAPai(m_MyPAIVec[0][5],m_MyPAIVec[0][6],m_MyPAIVec[0][7]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1],m_MyPAIVec[0][2])&&CheckAAPai(m_MyPAIVec[0][3],m_MyPAIVec[0][4])&&CheckAAAPai(m_MyPAIVec[0][5],m_MyPAIVec[0][6],m_MyPAIVec[0][7]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1],m_MyPAIVec[0][2])&&CheckAAAPai(m_MyPAIVec[0][3],m_MyPAIVec[0][4],m_MyPAIVec[0][5])&&CheckAAPai(m_MyPAIVec[0][6],m_MyPAIVec[0][7]))
			{
				iJiangNum++ ;
			}
			else
			{
				return false;
			}
		}
		else if(iSize==11)
		{
			if(CheckAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1])&&CheckAAAPai(m_MyPAIVec[0][2],m_MyPAIVec[0][3],m_MyPAIVec[0][4])&&CheckAAAPai(m_MyPAIVec[0][5],m_MyPAIVec[0][6],m_MyPAIVec[0][7])&&CheckAAAPai(m_MyPAIVec[0][8],m_MyPAIVec[0][9],m_MyPAIVec[0][10]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1],m_MyPAIVec[0][2])&&CheckAAPai(m_MyPAIVec[0][3],m_MyPAIVec[0][4])&&CheckAAAPai(m_MyPAIVec[0][5],m_MyPAIVec[0][6],m_MyPAIVec[0][7])&&CheckAAAPai(m_MyPAIVec[0][8],m_MyPAIVec[0][9],m_MyPAIVec[0][10]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1],m_MyPAIVec[0][2])&&CheckAAAPai(m_MyPAIVec[0][3],m_MyPAIVec[0][4],m_MyPAIVec[0][5])&&CheckAAPai(m_MyPAIVec[0][6],m_MyPAIVec[0][7])&&CheckAAAPai(m_MyPAIVec[0][8],m_MyPAIVec[0][9],m_MyPAIVec[0][10]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[0][0],m_MyPAIVec[0][1],m_MyPAIVec[0][2])&&CheckAAAPai(m_MyPAIVec[0][3],m_MyPAIVec[0][4],m_MyPAIVec[0][5])&&CheckAAAPai(m_MyPAIVec[0][6],m_MyPAIVec[0][7],m_MyPAIVec[0][8])&&CheckAAPai(m_MyPAIVec[0][9],m_MyPAIVec[0][10]))
			{
				iJiangNum++ ;
			}
			else
			{
				return false;
			}
		}
		else
		{
			return false;
		}

	}
	//东南西北
	iSize = m_MyPAIVec[1].size();
	if(iSize>0)
	{
		if(iSize==2)
		{
			if(!CheckAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==3)
		{
			if(!CheckAAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1],m_MyPAIVec[1][2]))
			{
				return false;
			}	
		}
		else if(iSize==5)
		{
			if(CheckAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1])&&CheckAAAPai(m_MyPAIVec[1][2],m_MyPAIVec[1][3],m_MyPAIVec[1][4]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1],m_MyPAIVec[1][2])&&CheckAAPai(m_MyPAIVec[1][3],m_MyPAIVec[1][4]))
			{
				iJiangNum++ ;
			}
			else
			{
				return false;
			}
		}
		else if(iSize==8)
		{
			if(CheckAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1])&&CheckAAAPai(m_MyPAIVec[1][2],m_MyPAIVec[1][3],m_MyPAIVec[1][4])&&CheckAAAPai(m_MyPAIVec[1][5],m_MyPAIVec[1][6],m_MyPAIVec[1][7]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1],m_MyPAIVec[1][2])&&CheckAAPai(m_MyPAIVec[1][3],m_MyPAIVec[1][4])&&CheckAAAPai(m_MyPAIVec[1][5],m_MyPAIVec[1][6],m_MyPAIVec[1][7]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1],m_MyPAIVec[1][2])&&CheckAAAPai(m_MyPAIVec[1][3],m_MyPAIVec[1][4],m_MyPAIVec[1][5])&&CheckAAPai(m_MyPAIVec[1][6],m_MyPAIVec[1][7]))
			{
				iJiangNum++ ;
			}
			else
			{
				return false;
			}
		}
		else if(iSize==11)
		{
			if(CheckAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1])&&CheckAAAPai(m_MyPAIVec[1][2],m_MyPAIVec[1][3],m_MyPAIVec[1][4])&&CheckAAAPai(m_MyPAIVec[1][5],m_MyPAIVec[1][6],m_MyPAIVec[1][7])&&CheckAAAPai(m_MyPAIVec[1][8],m_MyPAIVec[1][9],m_MyPAIVec[1][10]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1],m_MyPAIVec[1][2])&&CheckAAPai(m_MyPAIVec[1][3],m_MyPAIVec[1][4])&&CheckAAAPai(m_MyPAIVec[1][5],m_MyPAIVec[1][6],m_MyPAIVec[1][7])&&CheckAAAPai(m_MyPAIVec[1][8],m_MyPAIVec[1][9],m_MyPAIVec[1][10]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1],m_MyPAIVec[1][2])&&CheckAAAPai(m_MyPAIVec[1][3],m_MyPAIVec[1][4],m_MyPAIVec[1][5])&&CheckAAPai(m_MyPAIVec[1][6],m_MyPAIVec[1][7])&&CheckAAAPai(m_MyPAIVec[1][8],m_MyPAIVec[1][9],m_MyPAIVec[1][10]))
			{
				iJiangNum++ ;
			}
			else if(CheckAAAPai(m_MyPAIVec[1][0],m_MyPAIVec[1][1],m_MyPAIVec[1][2])&&CheckAAAPai(m_MyPAIVec[1][3],m_MyPAIVec[1][4],m_MyPAIVec[1][5])&&CheckAAAPai(m_MyPAIVec[1][6],m_MyPAIVec[1][7],m_MyPAIVec[1][8])&&CheckAAPai(m_MyPAIVec[1][9],m_MyPAIVec[1][10]))
			{
				iJiangNum++ ;
			}
			else
			{
				return false;
			}
		}
		else
		{
			return false;
		}
	}
	//万
	iSize = m_MyPAIVec[2].size();
	if(iSize>0)
	{
		if(iSize==2)
		{
			if(!CheckAAPai(m_MyPAIVec[2][0],m_MyPAIVec[2][1]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==3)
		{
			if(!CheckAAAPai(m_MyPAIVec[2][0],m_MyPAIVec[2][1],m_MyPAIVec[2][2]))
			{
				if(!CheckABCPai(m_MyPAIVec[2][0],m_MyPAIVec[2][1],m_MyPAIVec[2][2]))
				{
					return false;
				}
			}
		}
		else if(iSize==5)
		{
			if(!Check5Pai(m_MyPAIVec[2][0],m_MyPAIVec[2][1],m_MyPAIVec[2][2],m_MyPAIVec[2][3],m_MyPAIVec[2][4]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==6)
		{
			if(!Check6Pai(m_MyPAIVec[2][0],m_MyPAIVec[2][1],m_MyPAIVec[2][2],m_MyPAIVec[2][3],m_MyPAIVec[2][4],m_MyPAIVec[2][5]))
			{
				return false;
			}
		}
		else if(iSize==8)
		{
			if(!Check8Pai(m_MyPAIVec[2][0],m_MyPAIVec[2][1],m_MyPAIVec[2][2],m_MyPAIVec[2][3],m_MyPAIVec[2][4],m_MyPAIVec[2][5],m_MyPAIVec[2][6],m_MyPAIVec[2][7]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==9)
		{
			if(!Check9Pai(m_MyPAIVec[2][0],m_MyPAIVec[2][1],m_MyPAIVec[2][2],m_MyPAIVec[2][3],m_MyPAIVec[2][4],m_MyPAIVec[2][5],m_MyPAIVec[2][6],m_MyPAIVec[2][7],m_MyPAIVec[2][8]))
			{
				return false;
			}
		}
		else if(iSize==11)
		{
			if(!Check11Pai(m_MyPAIVec[2][0],m_MyPAIVec[2][1],m_MyPAIVec[2][2],m_MyPAIVec[2][3],m_MyPAIVec[2][4],m_MyPAIVec[2][5],m_MyPAIVec[2][6],m_MyPAIVec[2][7],m_MyPAIVec[2][8],m_MyPAIVec[2][9],m_MyPAIVec[2][10]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==12)
		{
			if(!Check12Pai(m_MyPAIVec[2][0],m_MyPAIVec[2][1],m_MyPAIVec[2][2],m_MyPAIVec[2][3],m_MyPAIVec[2][4],m_MyPAIVec[2][5],m_MyPAIVec[2][6],m_MyPAIVec[2][7],m_MyPAIVec[2][8],m_MyPAIVec[2][9],m_MyPAIVec[2][10],m_MyPAIVec[2][11]))
			{
				return false;
			}
		}
		else if(iSize==14)
		{
			if(!Check14Pai(m_MyPAIVec[2][0],m_MyPAIVec[2][1],m_MyPAIVec[2][2],m_MyPAIVec[2][3],m_MyPAIVec[2][4],m_MyPAIVec[2][5],m_MyPAIVec[2][6],m_MyPAIVec[2][7],m_MyPAIVec[2][8],m_MyPAIVec[2][9],m_MyPAIVec[2][10],m_MyPAIVec[2][11],m_MyPAIVec[2][12],m_MyPAIVec[2][13]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else
		{
			return false;
		}
	}

	//条
	iSize = m_MyPAIVec[3].size();
	if(iSize>0)
	{
		if(iSize==2)
		{
			if(!CheckAAPai(m_MyPAIVec[3][0],m_MyPAIVec[3][1]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==3)
		{
			if(!CheckAAAPai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2]))
			{
				if(!CheckABCPai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2]))
				{
					return false;
				}
			}
		}
		else if(iSize==5)
		{
			if(!Check5Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==6)
		{
			if(!Check6Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4],m_MyPAIVec[3][5]))
			{
				return false;
			}
		}
		else if(iSize==8)
		{
			if(!Check8Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4],m_MyPAIVec[3][5],m_MyPAIVec[3][6],m_MyPAIVec[3][7]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==9)
		{
			if(!Check9Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4],m_MyPAIVec[3][5],m_MyPAIVec[3][6],m_MyPAIVec[3][7],m_MyPAIVec[3][8]))
			{
				return false;
			}
		}
		else if(iSize==11)
		{
			if(!Check11Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4],m_MyPAIVec[3][5],m_MyPAIVec[3][6],m_MyPAIVec[3][7],m_MyPAIVec[3][8],m_MyPAIVec[3][9],m_MyPAIVec[3][10]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==12)
		{
			if(!Check12Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4],m_MyPAIVec[3][5],m_MyPAIVec[3][6],m_MyPAIVec[3][7],m_MyPAIVec[3][8],m_MyPAIVec[3][9],m_MyPAIVec[3][10],m_MyPAIVec[3][11]))
			{
				return false;
			}
		}
		else if(iSize==14)
		{
			if(!Check14Pai(m_MyPAIVec[3][0],m_MyPAIVec[3][1],m_MyPAIVec[3][2],m_MyPAIVec[3][3],m_MyPAIVec[3][4],m_MyPAIVec[3][5],m_MyPAIVec[3][6],m_MyPAIVec[3][7],m_MyPAIVec[3][8],m_MyPAIVec[3][9],m_MyPAIVec[3][10],m_MyPAIVec[3][11],m_MyPAIVec[3][12],m_MyPAIVec[3][13]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else
		{
			return false;
		}

	}

	//饼
	iSize = m_MyPAIVec[4].size();
	if(iSize>0)
	{
		if(iSize==2)
		{
			if(!CheckAAPai(m_MyPAIVec[4][0],m_MyPAIVec[4][1]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==3)
		{
			if(!CheckAAAPai(m_MyPAIVec[4][0],m_MyPAIVec[4][1],m_MyPAIVec[4][2]))
			{
				if(!CheckABCPai(m_MyPAIVec[4][0],m_MyPAIVec[4][1],m_MyPAIVec[4][2]))
				{
					return false;
				}
			}
		}
		else if(iSize==5)
		{
			if(!Check5Pai(m_MyPAIVec[4][0],m_MyPAIVec[4][1],m_MyPAIVec[4][2],m_MyPAIVec[4][3],m_MyPAIVec[4][4]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==6)
		{
			if(!Check6Pai(m_MyPAIVec[4][0],m_MyPAIVec[4][1],m_MyPAIVec[4][2],m_MyPAIVec[4][3],m_MyPAIVec[4][4],m_MyPAIVec[4][5]))
			{
				return false;
			}
		}
		else if(iSize==8)
		{
			if(!Check8Pai(m_MyPAIVec[4][0],m_MyPAIVec[4][1],m_MyPAIVec[4][2],m_MyPAIVec[4][3],m_MyPAIVec[4][4],m_MyPAIVec[4][5],m_MyPAIVec[4][6],m_MyPAIVec[4][7]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==9)
		{
			if(!Check9Pai(m_MyPAIVec[4][0],m_MyPAIVec[4][1],m_MyPAIVec[4][2],m_MyPAIVec[4][3],m_MyPAIVec[4][4],m_MyPAIVec[4][5],m_MyPAIVec[4][6],m_MyPAIVec[4][7],m_MyPAIVec[4][8]))
			{
				return false;
			}
		}
		else if(iSize==11)
		{
			if(!Check11Pai(m_MyPAIVec[4][0],m_MyPAIVec[4][1],m_MyPAIVec[4][2],m_MyPAIVec[4][3],m_MyPAIVec[4][4],m_MyPAIVec[4][5],m_MyPAIVec[4][6],m_MyPAIVec[4][7],m_MyPAIVec[4][8],m_MyPAIVec[4][9],m_MyPAIVec[4][10]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else if(iSize==12)
		{
			if(!Check12Pai(m_MyPAIVec[4][0],m_MyPAIVec[4][1],m_MyPAIVec[4][2],m_MyPAIVec[4][3],m_MyPAIVec[4][4],m_MyPAIVec[4][5],m_MyPAIVec[4][6],m_MyPAIVec[4][7],m_MyPAIVec[4][8],m_MyPAIVec[4][9],m_MyPAIVec[4][10],m_MyPAIVec[4][11]))
			{
				return false;
			}
		}
		else if(iSize==14)
		{
			if(!Check14Pai(m_MyPAIVec[4][0],m_MyPAIVec[4][1],m_MyPAIVec[4][2],m_MyPAIVec[4][3],m_MyPAIVec[4][4],m_MyPAIVec[4][5],m_MyPAIVec[4][6],m_MyPAIVec[4][7],m_MyPAIVec[4][8],m_MyPAIVec[4][9],m_MyPAIVec[4][10],m_MyPAIVec[4][11],m_MyPAIVec[4][12],m_MyPAIVec[4][13]))
			{
				return false;
			}
			else
			{
				iJiangNum++ ;
			}
		}
		else
		{
			return false;
		}
	}

	if(iJiangNum==1)return true;
	return false;
}
//检测听
bool	CMJ::CheckTING()
{
	//剑牌
	for(UINT j = 0 ; j < 9 ;j++ )
	{
		//起牌
		AddPai(0,j+1);
		if(CheckAllPai(MJPAI_GETPAI))
		{
			int iPaiIndex = GetPaiIndex(0,j+1);
			DelPai(iPaiIndex);
			return true;
		}
		else
		{
			int iPaiIndex = GetPaiIndex(0,j+1);
			DelPai(iPaiIndex);
		}
	}
	//风牌
	for(UINT j = 0 ; j < 9 ;j++ )
	{
		//起牌
		AddPai(1,j+1);
		if(CheckAllPai(MJPAI_GETPAI))
		{
			int iPaiIndex = GetPaiIndex(1,j+1);
		    DelPai(iPaiIndex);
			return true;
		}
		else
		{
			int iPaiIndex = GetPaiIndex(1,j+1);
			DelPai(iPaiIndex);
		}
	}
	for(UINT i = 2 ; i < 5 ;i++ )
	{
		for(UINT j = 0 ; j < 9 ;j++ )
		{
			//起牌
			AddPai(i,j+1);
			if(CheckAllPai(MJPAI_GETPAI))
			{
				int iPaiIndex = GetPaiIndex(i,j+1);
				DelPai(iPaiIndex);
				return true;
			}
			else
			{
				int iPaiIndex = GetPaiIndex(i,j+1);
				DelPai(iPaiIndex);
			}
		}
	}
	return false;
}

 

    怎么样,代码写的还算清晰吧。喝口水,休息一下,再看一下CMJManage类的实现:

#ifndef _CMJMANAGE_H
#define _CMJMANAGE_H
#include "CMJ.h"
//剩余牌墙信息
//扩展
struct stPAIEx
{
	stPAI	m_NewPai;						//起的新牌
	int		m_PaiNum;						//剩余牌数
	bool	m_IsHZ;							//是否黄庄
}
;

//麻将管理器
class CMJManage
{
	vector<stPAI>	m_MJVec;				//麻将数据VEC
	int				m_HZPaiNum;				//黄庄的牌数
public:

	//构造函数
	CMJManage();
	//析构函数
	~CMJManage();
	//初始化牌
	void	InitPai(int p_HZPaiNum = 0);
	//起牌
	stPAIEx	GetAPai();
private:
	//洗牌
	void	XiPai();
}
;

#endif

对应CPP:

#include "CMJManage.h"
	
//构造函数
CMJManage::CMJManage()
{
	m_HZPaiNum = 0;
}
//析构函数
CMJManage::~CMJManage()
{
	
}

//初始化牌
void	CMJManage::InitPai(int p_HZPaiNum)
{
	m_HZPaiNum = p_HZPaiNum;
	m_MJVec.clear();
	//中发白
	for(UINT i = 1 ; i <= 3 ; i++)
	{
		stPAI t_Pai;
		t_Pai.m_Type = 0;
		t_Pai.m_Value = i;
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
	}
	//东南西北
	for(UINT i = 1 ; i <= 4 ; i++)
	{
		stPAI t_Pai;
		t_Pai.m_Type = 1;
		t_Pai.m_Value = i;
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
	}
	//万
	for(UINT i = 1 ; i <= 9 ; i++)
	{
		stPAI t_Pai;
		t_Pai.m_Type = 2;
		t_Pai.m_Value = i;
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
	}
	//条
	for(UINT i = 1 ; i <= 9 ; i++)
	{
		stPAI t_Pai;
		t_Pai.m_Type = 3;
		t_Pai.m_Value = i;
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
	}
	//饼
	for(UINT i = 1 ; i <= 9 ; i++)
	{
		stPAI t_Pai;
		t_Pai.m_Type = 4;
		t_Pai.m_Value = i;
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
		m_MJVec.push_back(t_Pai);
	}
	XiPai();
}

//洗牌
void	CMJManage::XiPai()
{
	srand( GetTickCount() );
	random_shuffle(m_MJVec.begin(),m_MJVec.end());
}
	
//起牌
stPAIEx	CMJManage::GetAPai()
{
	//如果所有牌都起完了
	
	stPAIEx	t_Pai;
	t_Pai.m_NewPai.m_Type  = m_MJVec.back().m_Type;
	t_Pai.m_NewPai.m_Value = m_MJVec.back().m_Value;
	t_Pai.m_PaiNum = m_MJVec.size()-1;
	if(t_Pai.m_PaiNum ==m_HZPaiNum)
	{
		t_Pai.m_IsHZ = true;
	}
	else
	{
		t_Pai.m_IsHZ = false;
	}
	//扔去一个
	m_MJVec.pop_back();
	return t_Pai;
}

 

    看,洗牌发牌类也搞定了,下面我们来将其在控制台上运行起来,打开控制台的main函数。

#include "AI//CMJ.h"
#include "AI//CMJManage.h"

#include <windows.h>
#include <iostream>
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
	//其它三个玩家
	CMJ				t_OtherPlayer[3];
	//我
	CMJ				t_MyPlayer;
	//洗牌器
	CMJManage		t_MJManage;
	//分数
	int				t_Score = 0;
GameStart:

	//初始化及洗牌
	t_MJManage.InitPai();//初始化
	t_MyPlayer.CleanUp();
	for(UINT i = 0 ; i < 3; i++ )
	{
		t_OtherPlayer[i].CleanUp();
	}
	cout<<"洗牌完成"<<endl;
	cout<<"起牌:========================================================"<<endl;
	for(UINT i = 0 ; i < 13 ; i++)
	{

		stPAIEx t_Pai = t_MJManage.GetAPai();
		t_MyPlayer.AddPai(t_Pai.m_NewPai.m_Type,t_Pai.m_NewPai.m_Value);

		for(UINT j = 0 ; j < 3; j++ )
		{
			stPAIEx t_Pai2 = t_MJManage.GetAPai();
			t_OtherPlayer[j].AddPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
		}
	}
	t_MyPlayer.Init();
	for(UINT j = 0 ; j < 3; j++ )
	{
		t_OtherPlayer[j].Init();
	}
	//打牌循环
	bool t_Finish	= false;
	bool t_Ting		= false;
	while(t_Finish == false)
	{

		t_MyPlayer.PrintAllPai();
		cout<<endl;
		cout<<"起牌:========================================================"<<endl;
		stPAIEx t_Pai = t_MJManage.GetAPai();	
	
		//刷新我方牌墙
		t_MyPlayer.PrintPai(t_Pai.m_NewPai.m_Type,t_Pai.m_NewPai.m_Value);
		cout<<endl;
		//如果没有听头
		if(t_Ting == false)
		{
			cout<<"要还是打?Y/N";
			char t_Result;
			cin>>t_Result;
			if(t_Result =='Y'||t_Result=='y')
			{
				//起牌
				t_MyPlayer.AddPai(t_Pai.m_NewPai.m_Type,t_Pai.m_NewPai.m_Value);
				//起牌后胡牌判断
				t_Finish = t_MyPlayer.CheckAllPai(MJPAI_GETPAI);
				if(t_Finish)
				{
					printf("胡啦!!!:%s-%d",t_MyPlayer.GetInfo()->m_GoodName,t_MyPlayer.GetInfo()->m_GoodValue);
					::_sleep(1000);
				}
				else
				{
					if(t_Pai.m_NewPai.m_Type == -1)//如果起牌数已达到上限
					{
						cout<<endl;
						cout<<"黄庄了!!!!!!!!!!!!!"<<endl;
						break;
					}

					t_MyPlayer.PrintAllPai();
					cout<<endl;
OUTPai:
					cout<<"请打牌(输入牌序号)";
					int PaiIndex;
					cin>>PaiIndex;
					if(t_MyPlayer.DelPai(PaiIndex)==false)
					{
						cout<<"没有此牌"<<endl;
						goto OUTPai;
					}
					//==============================牌面刷新================================================
					cout<<"牌面刷新============================"<<endl;
					t_MyPlayer.PrintAllPai();
					cout<<endl;
					//==============================================================================

					//======================包听========================================================
					if(t_MyPlayer.CheckAllPai(MJPAI_PUTPAI))
					{

						char t_BTing;
						cout<<"要包听吗?:(Y/N)";
						cin>>t_BTing;
						if(t_BTing=='y'||t_BTing=='Y')
						{
							t_Ting = true;
						}
					}
					//==============================================================================

				}
			}
			else
			{
					//======================包听========================================================
					if(t_MyPlayer.CheckAllPai(MJPAI_PUTPAI))
					{

						char t_BTing;
						cout<<"要包听吗?:(Y/N)";
						cin>>t_BTing;
						if(t_BTing=='y'||t_BTing=='Y')
						{
							t_Ting = true;
						}
					}
					//==============================================================================
			}
		}
		else
		{
				
				t_MyPlayer.AddPai(t_Pai.m_NewPai.m_Type,t_Pai.m_NewPai.m_Value);
				//起牌
				int iPaiIndex = t_MyPlayer.GetPaiIndex(t_Pai.m_NewPai.m_Type,t_Pai.m_NewPai.m_Value);
				if(iPaiIndex>=0)
				{
					//起牌后胡牌判断
					t_Finish = t_MyPlayer.CheckAllPai(MJPAI_GETPAI);
					if(t_Finish)
					{
						cout<<endl;
						printf("胡啦!!!:%s-合计%d番",t_MyPlayer.GetInfo()->m_GoodName,t_MyPlayer.GetInfo()->m_GoodValue);
						t_Score += t_MyPlayer.GetInfo()->m_GoodValue;
						::_sleep(1000);
						break;
					}
					else
					{
						t_MyPlayer.DelPai(iPaiIndex);
						cout<<"打牌";
						t_MyPlayer.PrintPai(t_Pai.m_NewPai.m_Type,t_Pai.m_NewPai.m_Value);
						cout<<endl;
						::_sleep(1000);
					}
				}
				else
				{
					cout<<"程序出错!"<<endl;
				}

		}
		cout<<endl;
		//其它玩家起牌出牌
		for(UINT j = 0 ; j < 3; j++ )
		{
			stPAIEx t_Pai2 = t_MJManage.GetAPai();
			if(j==0)
			{
				cout<<"南家起牌出牌:";
				t_MyPlayer.PrintPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
				cout<<endl;
				::_sleep(1000);
			}
			if(j==1)
			{
				cout<<"西家起牌出牌:";
				t_MyPlayer.PrintPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
				cout<<endl;
				::_sleep(1000);
			}
			if(j==2)
			{
				cout<<"北家起牌出牌:";
				t_MyPlayer.PrintPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
				cout<<endl;
				::_sleep(1000);
			}
			
			char t_Result;
			if(t_Ting == false)
			{
				if(t_Pai2.m_IsHZ)//如果起牌数已达到上限
				{
					cout<<endl;
					cout<<"黄庄了!!!!!!!!!!!!!"<<endl;
					t_Finish = true;
					break;
				}

				bool t_Check = false;
				//检查吃牌
				if(t_MyPlayer.CheckChiPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value))
				{
					if(t_Check==false)
					{
						cout<<"请选择:";
					}
					cout<<"(吃)";
					t_Check = true;
				}
				//检查碰牌
				if(t_MyPlayer.CheckPengPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value))
				{
					if(t_Check==false)
					{
						cout<<"请选择:";
					}
					cout<<"(碰)";
					t_Check = true;
				}
				//检查杠牌
				if(t_MyPlayer.CheckGangPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value))
				{
					if(t_Check==false)
					{
						cout<<"请选择:";
					}
					cout<<"(杠)";
					t_Check = true;
				}
				//起牌
				t_MyPlayer.AddPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
				//起牌后胡牌判断
				if(t_MyPlayer.CheckAllPai(MJPAI_GETPAI))
				{
					if(t_Check==false)
					{
						cout<<"请选择:";
					}
					cout<<"(胡)";
					t_Check = true;
				}	
				int iPaiIndex = t_MyPlayer.GetPaiIndex(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
				t_MyPlayer.DelPai(iPaiIndex);
				//如果查到
				if(t_Check)
				{
					cout<<endl;
					cin>>t_Result;
				}
				else
				{
					//返回循环
					continue;
				}
			}
			else
			{
				t_Result = '4';
			}
			//吃牌
			if(t_Result =='1')
			{
				t_MyPlayer.PrintChiChosePai();

				int index = 0;
				//如果吃牌组合大于
				if(t_MyPlayer.GetChiChoseNum()>1)
				{
					cout<<"请输入组合号:"<<endl;
					cin>>index;
				}
				t_MyPlayer.DoChiPai(index,t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);

				//==============================================================================
				cout<<"牌面刷新============================"<<endl;
				t_MyPlayer.PrintAllPai();
				cout<<endl;
				//==============================================================================

OUTPai2:
				cout<<"请打牌(输入牌序号)";
				int PaiIndex;
				cin>>PaiIndex;
				if(t_MyPlayer.DelPai(PaiIndex)==false)
				{
					cout<<"没有此牌"<<endl;
					goto OUTPai2;
				}

				//=================================牌面刷新=============================================
				cout<<"牌面刷新============================"<<endl;
				t_MyPlayer.PrintAllPai();
				cout<<endl;
				//==============================================================================
				//======================包听========================================================
				if(t_MyPlayer.CheckAllPai(MJPAI_PUTPAI))
				{

					char t_BTing;
					cout<<"要包听吗?:(Y/N)";
					cin>>t_BTing;
					if(t_BTing=='y'||t_BTing=='Y')
					{
						t_Ting = true;
					}
				}
				//==============================================================================
				//该我下家
				j = -1;
		
			}
			else if(t_Result =='2')//碰牌
			{

				t_MyPlayer.PrintPengChosePai();
				t_MyPlayer.DoPengPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
				//==============================================================================
				cout<<"牌面刷新============================"<<endl;
				t_MyPlayer.PrintAllPai();
				cout<<endl;
				//==============================================================================
OUTPai3:
				cout<<"请打牌(输入牌序号)";
				int PaiIndex;
				cin>>PaiIndex;
				if(t_MyPlayer.DelPai(PaiIndex)==false)
				{
					cout<<"没有此牌"<<endl;
					goto OUTPai3;
				}
				//==========================牌面刷新====================================================
				cout<<"牌面刷新============================"<<endl;
				t_MyPlayer.PrintAllPai();
				cout<<endl;
				//==============================================================================
				//======================包听========================================================
				if(t_MyPlayer.CheckAllPai(MJPAI_PUTPAI))
				{

					char t_BTing;
					cout<<"要包听吗?:(Y/N)";
					cin>>t_BTing;
					if(t_BTing=='y'||t_BTing=='Y')
					{
						t_Ting = true;
					}
				}
				//==============================================================================
				j = -1;
	
			}
			else if(t_Result =='3')//杠牌
			{

				t_MyPlayer.PrintGangChosePai();
				t_MyPlayer.DoGangPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
				cout<<"起杠底牌"<<endl;
				t_MyPlayer.AddPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
				//==============================================================================
				cout<<"牌面刷新============================"<<endl;
				t_MyPlayer.PrintAllPai();
				cout<<endl;
				//==============================================================================
				stPAIEx t_Pai2 = t_MJManage.GetAPai();
				
				//起牌后胡牌判断
				t_Finish = t_MyPlayer.CheckAllPai(MJPAI_GETPAI);
				if(t_Finish)
				{
					cout<<"杠底花吗?(Y/N)"<<endl;
					char t_Result;
					cin>>t_Result;
					if(t_Result =='Y'||t_Result=='y')
					{
						cout<<endl;
						printf("胡啦!!!:%s-%d",t_MyPlayer.GetInfo()->m_GoodName,t_MyPlayer.GetInfo()->m_GoodValue);
						t_Score += t_MyPlayer.GetInfo()->m_GoodValue;
						::_sleep(1000);
						break;
					}
				}

				if(t_Pai2.m_IsHZ)//如果起牌数已达到上限
				{
					cout<<"黄庄了!!!!!!!!!!!!!"<<endl;
					t_Finish = true;
					break;
				}

OUTPai4:
				cout<<"请打牌(输入牌序号)";
				int PaiIndex;
				cin>>PaiIndex;
				if(t_MyPlayer.DelPai(PaiIndex)==false)
				{
					cout<<"没有此牌"<<endl;
					goto OUTPai4;
				}
				//===========================牌面刷新===================================================
				cout<<"牌面刷新============================"<<endl;
				t_MyPlayer.PrintAllPai();
				cout<<endl;
				//==============================================================================
				//======================包听========================================================
				if(t_MyPlayer.CheckAllPai(MJPAI_PUTPAI))
				{

					char t_BTing;
					cout<<"要包听吗?:(Y/N)";
					cin>>t_BTing;
					if(t_BTing=='y'||t_BTing=='Y')
					{
						t_Ting = true;
					}
				}
				//==============================================================================
				//该我下家
				j = -1;

			}
			else if(t_Result =='4')//胡牌
			{
				//起牌
				t_MyPlayer.AddPai(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
				//起牌后胡牌判断
				t_Finish = t_MyPlayer.CheckAllPai(MJPAI_GETPAI);
				if(t_Finish)
				{
					printf("胡啦!!!:%s-合计%d番",t_MyPlayer.GetInfo()->m_GoodName,t_MyPlayer.GetInfo()->m_GoodValue);
					t_Score += t_MyPlayer.GetInfo()->m_GoodValue;
					::_sleep(1000);
					break;
				}
				else
				{
					if(t_Pai2.m_IsHZ)//如果起牌数已达到上限
					{
						cout<<"黄庄了!!!!!!!!!!!!!"<<endl;
						t_Finish = true;
						break;
					}
					//起牌
					int iPaiIndex = t_MyPlayer.GetPaiIndex(t_Pai2.m_NewPai.m_Type,t_Pai2.m_NewPai.m_Value);
					cout<<endl;
					t_MyPlayer.DelPai(iPaiIndex);
				}
			}
			
		}


	}
	cout<<"我的分数:"<<t_Score<<endl;
	::_sleep(3000);	
	goto GameStart;//重新开始一局

	return 0;
}

 

    OK,麻将就这样实现了,你可以将其任意修改一下变形成你喜欢的麻将游戏,我很开心这个代码能再次焕发青春,在此谢谢你。

分类: 未分类 标签:

Cocos2d-x 2.0 拖尾效果深入分析

2013年2月22日 没有评论

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

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

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


      Cocos2d-x 2.0 拖尾效果深入分析

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

cocos2d-2.0-x-2.0.2@ Aug 30
2012

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

 

         今天我们来学习一下Cocos2d-x中的拖尾效果。在游戏中,拖尾效果常常用来做刀光,火球法术等一些运动物体的效果。如图:

                                       Cocos2d-x 2.0 拖尾效果深入分析

        在Cocos2d-x中,拖尾效果有一个专门的类CCMotionStreak来实现。下面我们打开CCMotionStreak.h来看一下:

#ifndef __CCMOTION_STREAK_H__
#define __CCMOTION_STREAK_H__

#include "CCProtocols.h"
#include "textures/CCTexture2D.h"
#include "ccTypes.h"
#include "base_nodes/CCNode.h"
//使用Cocos2d命名空间
NS_CC_BEGIN

//条带由三个基类共同派生
class CC_DLL CCMotionStreak : public CCNode, public CCTextureProtocol, public CCRGBAProtocol
{
public:
	//构造
    CCMotionStreak();
	//析构
    virtual ~CCMotionStreak();
    //静态函数,创建一个拖尾效果,内部调用create实现,参一为消隐动画时长,参二为拖尾条带相邻顶点间的最小距离,参三为拖尾条带的宽度,参四为顶点颜色值,参五为所使用的纹理图片。
    CC_DEPRECATED_ATTRIBUTE static CCMotionStreak* streakWithFade(float fade, float minSeg, float stroke, ccColor3B color, const char* path);
    //静态函数,创建一个拖尾效果,内部调用create实现,参一为消隐动画时长,参二为拖尾条带相邻顶点间的最小距离,参三为拖尾条带的宽度,参四为顶点颜色值,参五为所使用的纹理对象指针。
    CC_DEPRECATED_ATTRIBUTE static CCMotionStreak* streakWithFade(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture);

    //上面第一个创建函数的create实现。
    static CCMotionStreak* create(float fade, float minSeg, float stroke, ccColor3B color, const char* path);
    //上面第二个创建函数的create实现。
    static CCMotionStreak* create(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture);

    //初始化拖尾效果
    bool initWithFade(float fade, float minSeg, float stroke, ccColor3B color, const char* path);
    //初始化拖尾效果
    bool initWithFade(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture);

    //
    void tintWithColor(ccColor3B colors);

    //重置,删除所有的条带段
    void reset();

    //设置位置
    virtual void setPosition(const CCPoint& position);
	//绘制
    virtual void draw();
	//更新
    virtual void update(float delta);

    //取得纹理。
    virtual CCTexture2D* getTexture(void);
	//设置纹理。
    virtual void setTexture(CCTexture2D *texture);
	//设置
    virtual void setBlendFunc(ccBlendFunc blendFunc);
    virtual ccBlendFunc getBlendFunc(void);
	//设置颜色
    virtual void setColor(const ccColor3B& color);
    virtual const ccColor3B& getColor(void);
	//设置透明度
    virtual GLubyte getOpacity(void);
    virtual void setOpacity(GLubyte opacity);
	//设置修改A同时影响RGB
    virtual void setOpacityModifyRGB(bool bValue);
    virtual bool isOpacityModifyRGB(void);

    //是否是快速模式
    inline bool isFastMode() { return m_bFastMode; }
    inline void setFastMode(bool bFastMode) { m_bFastMode = bFastMode; }
	//起点是否初始化
    inline bool isStartingPositionInitialized() { return m_bStartingPositionInitialized; }
    inline void setStartingPositionInitialized(bool bStartingPositionInitialized) 
    { 
        m_bStartingPositionInitialized = bStartingPositionInitialized; 
    }
protected:
	//是否是快速模式
    bool m_bFastMode;
	//起点是否初始化
    bool m_bStartingPositionInitialized;
private:
    //所用的纹理对象指针
    CCTexture2D* m_pTexture;
	//ALPHA混合方案
    ccBlendFunc m_tBlendFunc;
	//当前拖尾起点位置
    CCPoint m_tPositionR;
	//颜色值
    ccColor3B m_tColor;
	//拖尾线条的宽度,越大当前越粗
    float m_fStroke;
	//每秒条带渐隐的alpha值减少量
    float m_fFadeDelta;
	//拖尾中用于划分条带的顶点的最小距离。
    float m_fMinSeg;
	//顶点最大数量
    unsigned int m_uMaxPoints;
	//当前的顶点数量
    unsigned int m_uNuPoints;
	//上次的顶点数量
    unsigned int m_uPreviousNuPoints;

    //顶点位置数组
    CCPoint* m_pPointVertexes;
	//顶点的状态值数组,这个状态值取值为0~1.0间,代表了消隐程度,其实就是alpha值。
    float* m_pPointState;

    // OPENGL所用的顶点各类数据绘冲
	//位置
    ccVertex2F* m_pVertices;
	//颜色
    GLubyte* m_pColorPointer;
	//纹理UV
    ccTex2F* m_pTexCoords;
};

// end of misc_nodes group
/// @}

NS_CC_END

#endif //__CCMOTION_STREAK_H__

对应的CPP:

#include "CCMotionStreak.h"
#include "textures/CCTextureCache.h"
#include "shaders/ccGLStateCache.h"
#include "shaders/CCGLProgram.h"
#include "shaders/CCShaderCache.h"
#include "ccMacros.h"

#include "support/CCVertex.h"
#include "support/CCPointExtension.h"
//使用Cocos2d命名空间
NS_CC_BEGIN
//构造
CCMotionStreak::CCMotionStreak()
: m_bFastMode(false)
, m_pTexture(NULL)
, m_tPositionR(CCPointZero)
, m_tColor(ccc3(0,0,0))
, m_fStroke(0.0f)
, m_fFadeDelta(0.0f)
, m_fMinSeg(0.0f)
, m_uMaxPoints(0)
, m_uNuPoints(0)
, m_uPreviousNuPoints(0)
, m_pPointVertexes(NULL)
, m_pPointState(NULL)
, m_pVertices(NULL)
, m_pColorPointer(NULL)
, m_pTexCoords(NULL)
, m_bStartingPositionInitialized(false)
{
    m_tBlendFunc.src = GL_SRC_ALPHA;
    m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
}
//析构
CCMotionStreak::~CCMotionStreak()
{
	//释放纹理对象和申请的内存缓冲区
    CC_SAFE_RELEASE(m_pTexture);
    CC_SAFE_FREE(m_pPointState);
    CC_SAFE_FREE(m_pPointVertexes);
    CC_SAFE_FREE(m_pVertices);
    CC_SAFE_FREE(m_pColorPointer);
    CC_SAFE_FREE(m_pTexCoords);
}
//静态函数,创建一个拖尾效果,内部调用create实现,参一为消隐动画时长,参二为拖尾条带相邻顶点间的最小距离,参三为拖尾条带的宽度,参四为顶点颜色值,参五为所使用的纹理图片。
CCMotionStreak* CCMotionStreak::streakWithFade(float fade, float minSeg, float stroke, ccColor3B color, const char* path)
{
    return CCMotionStreak::create(fade, minSeg, stroke, color, path);
}
//上面的create实现。 
CCMotionStreak* CCMotionStreak::create(float fade, float minSeg, float stroke, ccColor3B color, const char* path)
{
	//创建一个CCMotionStreak的实例对象并初始化,如果成功交由内存管理器进行引用计数器的管理。
    CCMotionStreak *pRet = new CCMotionStreak();
    if (pRet && pRet->initWithFade(fade, minSeg, stroke, color, path))
    {
        pRet->autorelease();
        return pRet;
    }
	//如果创建或初始化失败,删除实例对象指针并置空,返回NULL。
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//静态函数,创建一个拖尾效果,内部调用create实现,参一为消隐动画时长,参二为拖尾条带相邻顶点间的最小距离,参三为拖尾条带的宽度,参四为顶点颜色值,参五为所使用的纹理对象。
CCMotionStreak* CCMotionStreak::streakWithFade(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture)
{
    return CCMotionStreak::create(fade, minSeg, stroke, color, texture);
}
//上面创建函数的create实现。
CCMotionStreak* CCMotionStreak::create(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture)
{
	//创建一个CCMotionStreak的实例对象并初始化,如果成功交由内存管理器进行引用计数器的管理。
    CCMotionStreak *pRet = new CCMotionStreak();
    if (pRet && pRet->initWithFade(fade, minSeg, stroke, color, texture))
    {
        pRet->autorelease();
        return pRet;
    }
	//如果创建或初始化失败,删除实例对象指针并置空,返回NULL。
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//初始化条带
bool CCMotionStreak::initWithFade(float fade, float minSeg, float stroke, ccColor3B color, const char* path)
{
	//有效性判断。
    CCAssert(path != NULL, "Invalid filename");
	//加载图片生成纹理,使用纹理指针调用相应的初始化函数。
    CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage(path);
    return initWithFade(fade, minSeg, stroke, color, texture);
}
//初始化条带
bool CCMotionStreak::initWithFade(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture)
{
	//将结点位置为零零点。
    CCNode::setPosition(CCPointZero);
	//设置锚点位置为零零点。
    setAnchorPoint(CCPointZero);
	//
    ignoreAnchorPointForPosition(true);
	//
    m_bStartingPositionInitialized = false;
	//初始化起点为零零点。
    m_tPositionR = CCPointZero;
	//使用快速模式
    m_bFastMode = true;
	//如果minSeg值设-1,则成员变量m_fMinSeg取默认值为宽度/5.0f,否则按照参数取值。
    m_fMinSeg = (minSeg == -1.0f) ? stroke/5.0f : minSeg;
	//为什么要乘积呢?因为后面判断距离时提高效率,两点间距离公式不是要开平方嘛?这样做就省掉开平方了。
    m_fMinSeg *= m_fMinSeg;
	//保存拖尾效果条带的宽度。
    m_fStroke = stroke;
	//计算出每秒条带渐隐的alpha值减少量
    m_fFadeDelta = 1.0f/fade;
	//顶点最大数量这里是按消隐动画时长来计算的,每秒60个顶点,再加上首尾两个顶点。
    m_uMaxPoints = (int)(fade*60.0f)+2;
	//当前顶点数量为0。
    m_uNuPoints = 0;
	//创建相应顶点数量的状态值数组和顶点位置数组。
    m_pPointState = (float *)malloc(sizeof(float) * m_uMaxPoints);
    m_pPointVertexes = (CCPoint*)malloc(sizeof(CCPoint) * m_uMaxPoints);
	//创建相应顶点缓冲所用的内存。
    m_pVertices = (ccVertex2F*)malloc(sizeof(ccVertex2F) * m_uMaxPoints * 2);
    m_pTexCoords = (ccTex2F*)malloc(sizeof(ccTex2F) * m_uMaxPoints * 2);
    m_pColorPointer =  (GLubyte*)malloc(sizeof(GLubyte) * m_uMaxPoints * 2 * 4);

    // 设置ALPHA混合方案
    m_tBlendFunc.src = GL_SRC_ALPHA;
    m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;

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

	//设置所用的纹理
    setTexture(texture);
	//设置所用的颜色
    setColor(color);
	//
    scheduleUpdate();

    return true;
}
//设置位置
void CCMotionStreak::setPosition(const CCPoint& position)
{
    m_bStartingPositionInitialized = true;
    m_tPositionR = position;
}
//设置颜色
void CCMotionStreak::tintWithColor(ccColor3B colors)
{
	//
    setColor(colors);

    // Fast assignation
    for(unsigned int i = 0; i<m_uNuPoints*2; i++) 
    {
        *((ccColor3B*) (m_pColorPointer+i*4)) = colors;
    }
}
//取得纹理
CCTexture2D* CCMotionStreak::getTexture(void)
{
    return m_pTexture;
}
//设置纹理
void CCMotionStreak::setTexture(CCTexture2D *texture)
{
    if (m_pTexture != texture)
    {
        CC_SAFE_RETAIN(texture);
        CC_SAFE_RELEASE(m_pTexture);
        m_pTexture = texture;
    }
}
//设置ALPHA混合方案
void CCMotionStreak::setBlendFunc(ccBlendFunc blendFunc)
{
    m_tBlendFunc = blendFunc;
}
//取得ALPHA混合方案。
ccBlendFunc CCMotionStreak::getBlendFunc(void)
{
    return m_tBlendFunc;
}
//设置颜色
void CCMotionStreak::setColor(const ccColor3B& color)
{
    m_tColor = color;
}
//取得颜色
const ccColor3B& CCMotionStreak::getColor(void)
{
    return m_tColor;
}
//设置透明度
void CCMotionStreak::setOpacity(GLubyte opacity)
{
    CCAssert(false, "Set opacity no supported");
}
//取得透明度
GLubyte CCMotionStreak::getOpacity(void)
{
    CCAssert(false, "Opacity no supported");
    return 0;
}
//设置修改透明度是否影响RGB值。
void CCMotionStreak::setOpacityModifyRGB(bool bValue)
{
    CC_UNUSED_PARAM(bValue);
}

bool CCMotionStreak::isOpacityModifyRGB(void)
{
    return false;
}
//更新处理
void CCMotionStreak::update(float delta)
{
	//如果未初始化直接返回。
    if (!m_bStartingPositionInitialized)
    {
        return;
    }
	//通过时间音隔x每秒消隐值,取得当前时间片内要消隐的值。
    delta *= m_fFadeDelta;
	//定义临时变量
    unsigned int newIdx, newIdx2, i, i2;
	//定义临时变量保存消隐的顶点个数。
    unsigned int mov = 0;

    // 遍历更新当前所有的顶点。
    for(i = 0; i<m_uNuPoints; i++)
    {
		//状态数组对应值减去消隐值。
        m_pPointState[i]-=delta;
		//如果小于0,即完全消隐,mov++。
        if(m_pPointState[i] <= 0)
            mov++;
        else
        {
			//这里是通过mov计算出当前顶点数组在更新后移除完全消隐的顶点之后的当前顶点实际位置保存到变量newIdx。
            newIdx = i-mov;
			//如果mov大于0,即有完全消隐的顶点。
            if(mov>0)
            {
                // 将当前顶点原来的状态值保存到数组新位置中,这是一个前移操作。
                m_pPointState[newIdx] = m_pPointState[i];

                //将当前顶点原来的位置值保存到数组新位置中。
                m_pPointVertexes[newIdx] = m_pPointVertexes[i];

                //将当前顶点原来的位置顶点缓冲值保存到顶点缓冲新位置中。
                i2 = i*2;
                newIdx2 = newIdx*2;
                m_pVertices[newIdx2] = m_pVertices[i2];
                m_pVertices[newIdx2+1] = m_pVertices[i2+1];

                //将当前顶点原来的颜色顶点缓冲值保存到顶点缓冲新位置中。这里要注意,因为是GLubyte格式,所以A,R,G,B分别占一个数组元素。
                i2 *= 4;
                newIdx2 *= 4;
                m_pColorPointer[newIdx2+0] = m_pColorPointer[i2+0];
                m_pColorPointer[newIdx2+1] = m_pColorPointer[i2+1];
                m_pColorPointer[newIdx2+2] = m_pColorPointer[i2+2];
                m_pColorPointer[newIdx2+4] = m_pColorPointer[i2+4];
                m_pColorPointer[newIdx2+5] = m_pColorPointer[i2+5];
                m_pColorPointer[newIdx2+6] = m_pColorPointer[i2+6];
            }else
                newIdx2 = newIdx*8;//如果mov等于0,则R,G,B值不用变,只修改A值即可。
			//将当前顶点原来的颜色顶点缓冲中Alpha值保存到顶点缓冲新位置Alpha值中。
            const GLubyte op = (GLubyte)(m_pPointState[newIdx] * 255.0f);
            m_pColorPointer[newIdx2+3] = op;
            m_pColorPointer[newIdx2+7] = op;
        }
    }
	//当前的顶点数量减去完全消隐的顶点数量。
    m_uNuPoints-=mov;

    // 定义临时变量判断是否需要增加顶点。
    bool appendNewPoint = true;
	//如果当前顶点数量大于最大顶点数量,不能再新增顶点了。
    if(m_uNuPoints >= m_uMaxPoints)
    {
        appendNewPoint = false;
    }
	//如果当前顶点数量大于0,需要做一些计算来判断是否增加新顶点。
    else if(m_uNuPoints>0)
    {
		//取当前拖尾起点位置顶点与后面一个顶点的距离的平方,看是否小于m_fMinSeg。
        bool a1 = ccpDistanceSQ(m_pPointVertexes[m_uNuPoints-1], m_tPositionR) < m_fMinSeg;
		//取当前拖尾起点位置顶点与后面第二个顶点的距离的平方,看是否小于m_fMinSeg*2。
        bool a2 = (m_uNuPoints == 1) ? false : (ccpDistanceSQ(m_pPointVertexes[m_uNuPoints-2], m_tPositionR) < (m_fMinSeg * 2.0f));
		//如果在距离范围内,就不再增加顶点,这一段代码是为了避免拖尾间顶点过密过多,约定只有相邻顶点间距离达到一定范围以外才增加新的顶点。
        if(a1 || a2)
        {
            appendNewPoint = false;
        }
    }
	//如果需要增加新的顶点。
    if(appendNewPoint)
    {
		//将当前起点位置保存到相应数组元素中。
        m_pPointVertexes[m_uNuPoints] = m_tPositionR;
		//将相应状态数组元素值初始化为1.0。
        m_pPointState[m_uNuPoints] = 1.0f;

        // 颜色赋值
        const unsigned int offset = m_uNuPoints*8;
        *((ccColor3B*)(m_pColorPointer + offset)) = m_tColor;
        *((ccColor3B*)(m_pColorPointer + offset+4)) = m_tColor;

        // 透明度赋值
        m_pColorPointer[offset+3] = 255;
        m_pColorPointer[offset+7] = 255;

        //生成相应的四边形条带
        if(m_uNuPoints > 0 && m_bFastMode )
        {
            if(m_uNuPoints > 1)
            {//如果当前顶点数量大于1,则由上一个顶点到当前顶点位置生成相四边形存入到m_pVertices。
                ccVertexLineToPolygon(m_pPointVertexes, m_fStroke, m_pVertices, m_uNuPoints, 1);
            }
            else
            {//否则由起始位置到当前顶点位置生成相四边形存入到m_pVertices。
                ccVertexLineToPolygon(m_pPointVertexes, m_fStroke, m_pVertices, 0, 2);
            }
        }
		//顶点数量加一。
        m_uNuPoints ++;
    }
	//如果不是快速模式,则从起始位置到当前顶点位置生成相应四边形条带存入到m_pVertices。可见,快速模式比非快速模式要生成更少的次数。
    if( ! m_bFastMode )
    {
        ccVertexLineToPolygon(m_pPointVertexes, m_fStroke, m_pVertices, 0, m_uNuPoints);
    }

    // 更新纹理坐标
    if( m_uNuPoints  && m_uPreviousNuPoints != m_uNuPoints ) {
        float texDelta = 1.0f / m_uNuPoints;
        for( i=0; i < m_uNuPoints; i++ ) {
			//左右两边各一个顶点,故纹理U值分别为¬0,1
            m_pTexCoords[i*2] = tex2(0, texDelta*i);
            m_pTexCoords[i*2+1] = tex2(1, texDelta*i);
        }
		//记录当前粒子数量。
        m_uPreviousNuPoints = m_uNuPoints;
    }
}
//重置
void CCMotionStreak::reset()
{
    m_uNuPoints = 0;
}
//绘制
void CCMotionStreak::draw()
{
	//如果粒子数量<=1,不能形成条带,就不需要显示。
    if(m_uNuPoints <= 1)
        return;
	//设置使用shader,并设置相关矩阵。
    CC_NODE_DRAW_SETUP();
	//设置使用的顶点格式和ALPHA混合方案。
    ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex );
    ccGLBlendFunc( m_tBlendFunc.src, m_tBlendFunc.dst );
	//设置使用的纹理。
    ccGLBindTexture2D( m_pTexture->getName() );
	//设置顶点缓冲区。
    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, m_pVertices);
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, m_pTexCoords);
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, m_pColorPointer);
	//渲染三角形条带
    glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)m_uNuPoints*2);
	//更新渲染调用次数统计
    CC_INCREMENT_GL_DRAWS(1);
}

NS_CC_END

拖尾效果类的实现原理了解了,现在我们看一下Cocos2d-x中的相关演示。

MotionStreakTest.h:

#ifndef _MOTION_STREAK_TEST_H_
#define _MOTION_STREAK_TEST_H_

#include "../testBasic.h"
//使用Cocos2d命名空间
//USING_NS_CC;
//用于演示的基类。
class MotionStreakTest : public CCLayer
{
public:
	//构造
    MotionStreakTest(void);
	//析构
    ~MotionStreakTest(void);
	//取得标题。
    virtual std::string title();
	//取得副标题。
    virtual std::string subtitle();
	//当前层被加载时的处理。
    virtual void onEnter();
	//响应菜单按钮项重启当前演示。
    void restartCallback(CCObject* pSender);
	//响应菜单按钮项进行下一个演示。
    void nextCallback(CCObject* pSender);
	//响应菜单按钮项进行上一个演示。
    void backCallback(CCObject* pSender);
	//响应菜单文字项进行模式切换。
    void modeCallback(CCObject* pSender);
protected:
	//拖尾对象指针。
    CCMotionStreak *streak;
};

对应CPP:

//枚举所用到的精灵的TAG值
enum {
	kTagLabel = 1,
	kTagSprite1 = 2,
	kTagSprite2 = 3,
};

//下一个效果演示
CCLayer* nextMotionAction();
//上一个效果演示
CCLayer* backMotionAction();
//重新演示当前的效果
CCLayer* restartMotionAction();
//场景索引
static int sceneIdx = -1; 
//最大层数
#define MAX_LAYER    3
//根据效果创建不同的拖尾效果
CCLayer* createMotionLayer(int nIndex)
{
    switch(nIndex)
    {
        case 0: return new MotionStreakTest1();
        case 1: return new MotionStreakTest2();
        case 2: return new Issue1358();
    }

    return NULL;
}
//下一个效果
CCLayer* nextMotionAction()
{
    sceneIdx++;
    sceneIdx = sceneIdx % MAX_LAYER;

    CCLayer* pLayer = createMotionLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//上一个效果演示
CCLayer* backMotionAction()
{
    sceneIdx--;
    int total = MAX_LAYER;
    if( sceneIdx < 0 )
        sceneIdx += total;    
    
    CCLayer* pLayer = createMotionLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//重新演示当前的效果
CCLayer* restartMotionAction()
{
    CCLayer* pLayer = createMotionLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
} 

//构造
MotionStreakTest::MotionStreakTest(void)
{
}
//析构
MotionStreakTest::~MotionStreakTest(void)
{
}

//标题
std::string MotionStreakTest::title()
{
    return "No title";
}

//副标题。
std::string MotionStreakTest::subtitle()
{
    return "";
}

//当前层加载时的处理
void MotionStreakTest::onEnter()
{
	//调用基类的相应处理。
    CCLayer::onEnter();
	//获取屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//创建标题文字标签
    CCLabelTTF* label = CCLabelTTF::create(title().c_str(), "Arial", 32);
	//将文字标签加入当前层中
    addChild(label, 0, kTagLabel);
	//设置文字标签的位置 
    label->setPosition(CCPointMake(s.width/2, s.height-50));
    //取得副标题
    string subTitle = this->subtitle();
	//创建副标题文字标签
    if (subTitle.size() > 0)
    {
        CCLabelTTF *l = CCLabelTTF::create(subTitle.c_str(), "Thonburi", 16);
        addChild(l, 1);
        l->setPosition(ccp(s.width/2, s.height-80));
    }
	//创建菜单项
    CCMenuItemImage *item1 = CCMenuItemImage::create(s_pPathB1, s_pPathB2, this, menu_selector(MotionStreakTest::backCallback) );
    CCMenuItemImage *item2 = CCMenuItemImage::create(s_pPathR1, s_pPathR2, this, menu_selector(MotionStreakTest::restartCallback) );
    CCMenuItemImage *item3 = CCMenuItemImage::create(s_pPathF1, s_pPathF2, this, menu_selector(MotionStreakTest::nextCallback) );
	//创建菜单
    CCMenu *menu = CCMenu::create(item1, item2, item3, NULL);
	//设置菜单与各菜单项的位置
    menu->setPosition(CCPointZero);
    item1->setPosition(CCPointMake(s.width/2 - item2->getContentSize().width*2, item2->getContentSize().height/2));
    item2->setPosition(CCPointMake(s.width/2, item2->getContentSize().height/2));
    item3->setPosition(CCPointMake(s.width/2 + item2->getContentSize().width*2, item2->getContentSize().height/2));
	//将菜单加入当前层
    addChild(menu, 1);    
	//创建一个菜单切换文字项,其实就是有复选框效果的文字标签。这里创建的切换项为“质量优先模式”和“效率优先模式”,响应函数为modeCallback。
    CCMenuItemToggle *itemMode = CCMenuItemToggle::createWithTarget(this, menu_selector(MotionStreakTest::modeCallback),
        CCMenuItemFont::create("Use High Quality Mode"),
        CCMenuItemFont::create("Use Fast Mode"),
        NULL);
	//创建第二个菜单并加入当前层
    CCMenu *menuMode = CCMenu::create(itemMode, NULL);
    addChild(menuMode);
	//设置菜单位置
    menuMode->setPosition(ccp(s.width/2, s.height/4));
}
//切换文字项的响应函数。
void MotionStreakTest::modeCallback(CCObject *pSender)
{
	//来回切换模式
    bool fastMode = streak->isFastMode();
    streak->setFastMode(! fastMode);
}
//重新演示当前效果。
void MotionStreakTest::restartCallback(CCObject* pSender)
{
	//创建一个演示场景。
    CCScene* s = new MotionStreakTestScene();//CCScene::create();
    s->addChild(restartMotionAction()); 
	//使用当前场景。
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
}

//下一个效果的演示
void MotionStreakTest::nextCallback(CCObject* pSender)
{
	//创建一个演示场景。
    CCScene* s = new MotionStreakTestScene();//CCScene::create();
	//将下一个演示层加入场景,并运行这个场景
    s->addChild( nextMotionAction() );
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
}

//上一个效果的演示
void MotionStreakTest::backCallback(CCObject* pSender)
{	//创建一个演示场景。
    CCScene* s = new MotionStreakTestScene;//CCScene::create();
	//将上一个演示层加入场景,并运行这个场景
    s->addChild( backMotionAction() );
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
} 

然后是派生的第一个拖尾效果演示类:

class MotionStreakTest1 : public MotionStreakTest
{
protected:
	//根结点
    CCNode*        m_root;
	//带拖尾的目标结点
    CCNode*        m_target;

public:
	//加载当前层时的处理
    virtual void onEnter();
	//更新函数
    void onUpdate(float delta);
	//取得标题
    virtual std::string title();
};

对应CPP:

//加载当前层时的处理
void MotionStreakTest1::onEnter()
{
	//调用基类的相应函数。
    MotionStreakTest::onEnter();
	//取和屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();
  
    // 创建根精灵结点,放入当前层中的屏幕中心位置。
    m_root = CCSprite::create(s_pPathR1);
    addChild(m_root, 1);
    m_root->setPosition(ccp(s.width/2, s.height/2));
  
    //创建目标精灵结点,放入根结点下的右边1/4屏幕宽度位置。
    m_target = CCSprite::create(s_pPathR1);
    m_root->addChild(m_target);
    m_target->setPosition(ccp(s.width/4, 0));

    // 创建拖尾效果并放入到当前层下。
    streak = CCMotionStreak::create(2, 3, 32, ccGREEN, s_streak);
    addChild(streak);
    //每帧调用onUpdate函数。
    schedule(schedule_selector(MotionStreakTest1::onUpdate));
  	//创建一个旋转动画,2秒内自转360度。
    CCActionInterval* a1 = CCRotateBy::create(2, 360);
	//创建一个al1的无限循环动画。
    CCAction* action1 = CCRepeatForever::create(a1);
	//创建一个平移动画,2秒内向右移动100像素。
    CCActionInterval* motion = CCMoveBy::create(2, CCPointMake(100,0) );
//根结点运行一个无限循环的动画序列,动画序列为平移动画及其反向动画。 	m_root->runAction( CCRepeatForever::create((CCActionInterval*)(CCSequence::create(motion, motion->reverse(), NULL)) ) );
//同时也运行无限循环的自转动画。
    m_root->runAction( action1 );
	//创建一个无限循环的动画序列,动画序列为7个变色动画,哪个结点使用它就会不断的变色。
    CCActionInterval *colorAction = CCRepeatForever::create((CCActionInterval *)CCSequence::create(
        CCTintTo::create(0.2f, 255, 0, 0),
        CCTintTo::create(0.2f, 0, 255, 0),
        CCTintTo::create(0.2f, 0, 0, 255),
        CCTintTo::create(0.2f, 0, 255, 255),
        CCTintTo::create(0.2f, 255, 255, 0),
        CCTintTo::create(0.2f, 255, 0, 255),
        CCTintTo::create(0.2f, 255, 255, 255),
        NULL));
	//让拖尾运行这个变色动画序列。
    streak->runAction(colorAction);
}
//实时更新函数。
void MotionStreakTest1::onUpdate(float delta)
{
	//更新拖尾的位置,设置为目标精灵结点的位置。
    streak->setPosition( m_target->convertToWorldSpace(CCPointZero) );
}
//取得标题。
std::string MotionStreakTest1::title()
{
    return "MotionStreak test 1";
}

演示如图:

Cocos2d-x 2.0 拖尾效果深入分析


派生的第二个拖尾效果演示类:

class MotionStreakTest2 : public MotionStreakTest
{
	//根结点
    CCNode*        m_root;
	//带拖尾的目标结点
    CCNode*        m_target;
public:
	//加载当前层时的处理
    virtual void onEnter();
	//触屏并移动时响应处理
    void ccTouchesMoved(CCSet* touches, CCEvent* event);
	//取得标题
    virtual std::string title();
};

 
对应的CPP:

//加载当前层时的处理
void MotionStreakTest2::onEnter()
{
	//调用基类的相应函数。
    MotionStreakTest::onEnter();
	//这里设置打开触屏响应
    setTouchEnabled(true);
	//取得屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();
        
    // 创建拖尾效果并放入到当前层下。
    streak = CCMotionStreak::create(3, 3, 64, ccWHITE, s_streak );
    addChild(streak);
    //设置拖尾的初始位置为屏幕中小。
    streak->setPosition( CCPointMake(s.width/2, s.height/2) ); 
}
//触屏并移动时响应处理
void MotionStreakTest2::ccTouchesMoved(CCSet* touches, CCEvent* event)
{
	//取得触点位置。
    CCSetIterator it = touches->begin();
    CCTouch* touch = (CCTouch*)(*it);

    CCPoint touchLocation = touch->getLocation();    
    //设置为拖层的位置。
    streak->setPosition( touchLocation );
}
//取得标题
std::string MotionStreakTest2::title()
{
    return "MotionStreak test";
}

效果如图:

Cocos2d-x 2.0 拖尾效果深入分析


派生的第三个拖尾效果演示类:

class Issue1358 : public MotionStreakTest
{
public:
	//取得标题
    virtual std::string title();
	//取得副标题
    virtual std::string subtitle();
	//加载当前层时的处理
    virtual void onEnter();
	//更新函数
    virtual void update(float dt);
private:
	//中心位置
    CCPoint m_center;
	//半径
    float m_fRadius;
	//角度
    float m_fAngle;
};

对应CPP:

//加载当前层时的处理
void Issue1358::onEnter()
{
	//调用基类的相应函数。
    MotionStreakTest::onEnter();
    
    //取得屏幕位置
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    // 创建拖尾效果并放入到当前层下。
    streak = CCMotionStreak::create(2.0f, 1.0f, 50.0f, ccc3(255, 255, 0), "Images/Icon.png");
    addChild(streak);
	//初始化中心位置,半径和角度。
    m_center  = ccp(size.width/2, size.height/2);
    m_fRadius = size.width/3;
    m_fAngle = 0.0f;
	//设置每帧的回调更新函数。
    schedule(schedule_selector(Issue1358::update), 0);
}
//每帧的回调更新函数。
void Issue1358::update(float dt)
{
	//角度增加1.0f
    m_fAngle += 1.0f;
	//计算拖尾的位置
    streak->setPosition(ccp(m_center.x + cosf(m_fAngle/180 * M_PI)*m_fRadius,
                            m_center.y + sinf(m_fAngle/ 180 * M_PI)*m_fRadius));
}
//取得标题。
std::string Issue1358::title()
{
    return "Issue 1358";
}
//取得副标题。
std::string Issue1358::subtitle()
{
    return "The tail should use the texture";
}


效果如图:

Cocos2d-x 2.0 拖尾效果深入分析

然后是演示用的场景:

class MotionStreakTestScene : public TestScene
{
public:
	//运行场景的处理
    virtual void runThisTest();
};


//运行场景的处理
void MotionStreakTestScene::runThisTest()
{
	//创建下一个要演示的效果并放入当前场景中。
    CCLayer* pLayer = nextMotionAction();
    addChild(pLayer);
	//使用当前场景。
    CCDirector::sharedDirector()->replaceScene(this);
}

      

总结一下:

    拖尾效果的原理就是在相应距离内动态生成条带,然后逐渐消隐,在Cocos2d-x中可以指定这个最小距离,以及消隐的速度,还有条带的粗细,相应的纹理对象,都是通过CCMotionStreak 类来实现的。

     又一篇结束了,看看时间,一点半了,为了今年的目标,我只能坚持下去。工作,写作,工具箱,一个都不能少!努力吧,骚年!

分类: cocos2d, cocos2d-x 标签:

error: expected specifier-qualifier-list before 'cv'

2013年2月21日 没有评论

I am using OpenCV framework for image processing, i am identifying edge in photo for
normal iOS project, now i am converting this code in cocos2d project, i have imported all
required headers but while compiling i am getting this error:expected specifier-qualifier-list before ‘cv’. here is my code

     #import "cocos2d.h"
     #import "CameraController.h"
     #import "Globals.h"

     @interface BotoxEffectController : CCLayer 
     { cv::VideoCapture *_videoCapture; cv::Mat _lastFrame;}//this is line where i am
       getting error

     @property (nonatomic, retain) CCSprite *sprite2D;
     +(CCScene *) scene;
     @end

     here is code in .pch file.

     #import <Availability.h>

     #ifndef __IPHONE_3_0
     #warning "This project uses features only available in iPhone SDK 3.0 and later."
     #endif

     #ifdef __cplusplus
     #import <OpenCV/opencv2/opencv.hpp>
     #endif

     #ifdef __OBJC__
     #import <UIKit/UIKit.h>
     #import <Foundation/Foundation.h>
     #endif

Please refer below screen shot BotoxEffectController class interface where error is & implementation file has extension .mm.
error: expected specifier-qualifier-list before &#39;cv&#39;

here is .pch file where i am importing opencv.hpp

error: expected specifier-qualifier-list before &#39;cv&#39;

I think I know what’s going on here. Try adding the OpenCV header to your BotoxEffectController header file:

#import <OpenCV/opencv2/opencv.hpp>

If this fixes the compile errors, the problem is with the prefix.pch. For some reason it doesn’t parse the #if __cplusplus part. You can verify it by adding a warning to it, see if it triggers:

#ifdef __cplusplus
#warning "ok so it DID import the OpenCV header"
#import <OpenCV/opencv2/opencv.hpp>
#endif

If that is the issue, I can’t tell you why the opencv header in the prefix.pch won’t work. I only remember having had this issue once or twice before, but can’t remember how I fixed it. One thing you should check: the BotoxEffectController.h should have its File Type set to “Default – C Header” and not C++ header.

分类: stackoverflow精选, unity3d 标签:

Applying color to single vertices in a quad in opengl

2013年2月20日 没有评论

I’m trying to color single vertices of quads that are drawn through glDrawElements, I’m working with cocos2d libray, so I’ve been able to scavenge the source code to understand exactly what is happening, code is the following:

glBindVertexArray( VAOname_ );
glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );
glBindVertexArray(0);

So vertex array objects are used. I’m trying to modify single vertices color of the objects that are passed and it seems to work but with a glitch which is described by the following image:

Applying color to single vertices in a quad in opengl

Here I tried to change the color of the lower left and right vertex. The result is different, I guess this is because the quad is rendered as a couple of triangles with shared hypotenuse which resides on the diagonal which goes from lower left vertex to higher right vertex. So this could cause the different result.

Now I would like to have the second result also for the first case. Is there a way to obtain it?

Your guess is right. The OpenGL driver tesselates your quad into two triangles, in which the vertex colours are interpolated barycentrically, which results in what you see.

The usual approach to solve this, is by performing the interpolation “manually” in a fragment shader, that takes into account the target topology, in your case a quad. Or in short you have to perform barycentric interpolation not based on a triangle but on a quad. You might also want to apply perspective correction.

I don’t have ready to read resources at hand right now, but I’ll update this answer as soon as I have (might actually mean, I’ll have to write it myself).

Update

First we must understand the problem: Most OpenGL implementations break down higher primitives into triangles and render them localized, i.e. without further knowledge about the rest of the primitive, e.g. a quad. So we have to do this ourself.

This is how I’d do it.

#version 330 // vertex shader

Of course we also need the usual uniforms

uniform mat4x4 MV;
uniform mat4x4 P;

First we need the position of the vertex processed by this shader execution instance

layout (location=0) in vec3 pos;

Next we need some vertex attributes which we use to describe the quad itself. This means its corner positions

layout (location=1) in vec3 qp0;
layout (location=2) in vec3 qp1;
layout (location=3) in vec3 qp2;
layout (location=4) in vec3 qp3;

and colors

layout (location=5) in vec3 qc0;
layout (location=6) in vec3 qc1;
layout (location=7) in vec3 qc2;
layout (location=8) in vec3 qc3;

We put those into varyings for the fragment shader to process.

out vec3 position;
out vec3 qpos[4];
out vec3 qcolor[4];

void main()
{
    qpos[0] = qp0;
    qpos[1] = qp1;
    qpos[2] = qp2;
    qpos[3] = qp3;

    qcolor[0] = qc0;
    qcolor[1] = qc1;
    qcolor[2] = qc2;
    qcolor[3] = qc3;

    gl_Position = P * MV * position;
}

In the fragment shader we use this to implement a distance weighting for the color components:

#version 330 // fragment shader

in vec3 position;
in vec3 qpos[4];
in vec3 qcolor[4];

void main()
{
    vec3 color = vec3(0);

The following can be simplified combinatorical, but for sake of clarity I write it out:
For each corner point of the vertex mix with the colors of all corner points with the projection of the position on the edge between them as mix factor.

    for(int i=0; i < 4; i++) {
        vec3 p = position - qpos[i];
        for(int j=0; j < 4; j++) {
            vec3 edge = qpos[i] - qpos[j];
            float edge_length = length(edge);
            edge = normalize(edge);
            float tau = dot(edge_length, p) / edge_length;

            color += mix(qcolor[i], qcolor[j], tau);
        }
    }

Since we looked at each corner point 4 times, scale down by 1/4

    color *= 0.25;

    gl_FragColor = color; // and maybe other things.
}

We’re almost done. On the client side we need to pass the additional information. Of course we don’t want to duplicate data. For this we use glVertexBindingDivisor so that a vertex attribute advances only every 4 vertices (i.e. a quad), on the qp… and qc… locations, i.e. location 1 to 8

typedef float vec3[3];
extern vec3 *quad_position;
extern vec3 *quad_color;

glVertexAttribute(0, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[0]);

glVertexBindingDivisor(1, 4);
glVertexAttribute     (1, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[0]);

glVertexBindingDivisor(2, 4);
glVertexAttribute     (2, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[1]);

glVertexBindingDivisor(3, 4);
glVertexAttribute     (3, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[2]);

glVertexBindingDivisor(4, 4);
glVertexAttribute     (4, 3, GL_FLOAT, GL_FALSE, 0, &quad_position[3]);

glVertexBindingDivisor(5, 4);
glVertexAttribute     (5, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[0]);

glVertexBindingDivisor(6, 4);
glVertexAttribute     (6, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[1]);

glVertexBindingDivisor(7, 4);
glVertexAttribute     (7, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[2]);

glVertexBindingDivisor(8, 4);
glVertexAttribute     (8, 3, GL_FLOAT, GL_FALSE, 0, &quad_color[3]);

It makes sense to put the above into a Vertex Array Object. Also using a VBO would make sense, but then you must calculate the offset sizes manually; due to the typedef float vec3 the compiler does the math for us ATM.

With all this being set you can finally tesselation independently draw your quad.

分类: cocos2d, stackoverflow精选 标签:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

2013年2月14日 没有评论

[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中粒子系统的的基类,了解了粒子系统的构成与原理,本节我们来看一下在Cocos2d-x中如何具体展示粒子系统。

         我们打开libcocos2d工程下的particle_nodes目录,可以看到:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

这个目录是粒子系统相关的一些源码的列表,很明显,我们还有一个CCParticleExample.h/cpp没有学习。那么,这一节,我们将会从这两个文件开始。

         CCParticleSystem.h:

#ifndef __CCPARTICLE_EXAMPLE_H__
#define __CCPARTICLE_EXAMPLE_H__
//包含粒子系统基类头文件
#include "CCParticleSystemQuad.h"
//使用Cocos2d命名空间
NS_CC_BEGIN


//由粒子系统基类派生出CCParticleFire,表现的是火焰
class CC_DLL CCParticleFire : public CCParticleSystemQuad
{
public:
	//构造
    CCParticleFire(){}
	//析构
    virtual ~CCParticleFire(){}
	//初始化,创建250个粒子。
    bool init(){ return initWithTotalParticles(250); }
	//创建相应数量的粒子。
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
	//创建一个当前实例对象,内部调用create实现。
    static CCParticleFire * node()
    {
        return create();
    }
	//上面的create实现。
    static CCParticleFire * create()
    {
		//创建一个新的实例,初始化后交由内存管理器进行管理,返回实例指针。
        CCParticleFire *pRet = new CCParticleFire();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
		//如果失败释放置空并返回NULL。
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};

对应的CPP实现:

//创建相应数量的粒子。
bool CCParticleFire::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的初始化函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        this->m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。
        this->modeA.gravity = ccp(0,0);

        // 设置半径变化值及其用于随机初始化的范围值。
        this->modeA.radialAccel = 0;
        this->modeA.radialAccelVar = 0;

        // 设置速度及其用于随机初始化的范围值。
        this->modeA.speed = 60;
        this->modeA.speedVar = 20;        

        // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 10;

        // 设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, 60));
        this->m_tPosVar = ccp(40, 20);

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 3;
        m_fLifeVar = 0.25f;

        //起始大小及其用于随机初始化的范围值。终止大小为枚举值,在粒子系统基类的update里有处理,即大小在更新时不变化。
        m_fStartSize = 54.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

        // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.76f;
        m_tStartColor.g = 0.25f;
        m_tStartColor.b = 0.12f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

        // 设置为加亮模式。
        this->setBlendAdditive(true);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleFire,表现的是焰火
class CC_DLL CCParticleFireworks : public CCParticleSystemQuad
{
public:
	//构造
    CCParticleFireworks(){}
	//析构
    virtual ~CCParticleFireworks(){}
	//初始化,创建1500个粒子。
    bool init(){ return initWithTotalParticles(1500); }
	//创建相应数量的粒子。
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
	//创建一个当前实例对象,内部调用create实现。
    static CCParticleFireworks * node()
    {
        return create();
    }
	//上面的create实现。
    static CCParticleFireworks * create()
    {
		//创建一个新的实例,初始化后交由内存管理器进行管理,返回实例指针。
        CCParticleFireworks *pRet = new CCParticleFireworks();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
		//如果失败释放置空并返回NULL。
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP实现:
//创建相应数量的粒子。
bool CCParticleFireworks::initWithTotalParticles(unsigned int numberOfParticles)
{
    //先调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        this->m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度值。
        this->modeA.gravity = ccp(0,-90);

        // 设置半径变化值及其用于随机初始化的范围值。
        this->modeA.radialAccel = 0;
        this->modeA.radialAccelVar = 0;

         // 设置速度及其用于随机初始化的范围值。
        this->modeA.speed = 180;
        this->modeA.speedVar = 50;

        //设置发射器的位置。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));

         //设置起始角度及其用于随机初始化的范围值。
        this->m_fAngle= 90;
        this->m_fAngleVar = 20;

        // 粒子的生命值及其用于随机初始化的范围值。
        this->m_fLife = 3.5f;
        this->m_fLifeVar = 1;

        // 发射器的发射速率。
        this->m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.5f;
        m_tStartColor.g = 0.5f;
        m_tStartColor.b = 0.5f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.5f;
        m_tStartColorVar.g = 0.5f;
        m_tStartColorVar.b = 0.5f;
        m_tStartColorVar.a = 0.1f;
        m_tEndColor.r = 0.1f;
        m_tEndColor.g = 0.1f;
        m_tEndColor.b = 0.1f;
        m_tEndColor.a = 0.2f;
        m_tEndColorVar.r = 0.1f;
        m_tEndColorVar.g = 0.1f;
        m_tEndColorVar.b = 0.1f;
        m_tEndColorVar.a = 0.2f;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 8.0f;
        m_fStartSizeVar = 2.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

        //不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleSun,表现的是太阳
class CC_DLL CCParticleSun : public CCParticleSystemQuad
{
public:
	//构造
    CCParticleSun(){}
	//析构
    virtual ~CCParticleSun(){}
	//初始化,创建350个粒子。
    bool init(){ return initWithTotalParticles(350); }
	//创建相应数量的粒子。
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
	//创建一个当前实例,内部调用create实现。
    static CCParticleSun * node()
    {
        return create();
    }
	//上面的create实现。
    static CCParticleSun * create()
    {
        CCParticleSun *pRet = new CCParticleSun();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应的CPP实现:
//创建相应数量的粒子。
bool CCParticleSun::initWithTotalParticles(unsigned int numberOfParticles)
{
    //先调用基类的相应函数。if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置使用加亮模式。
        this->setBlendAdditive(true);

        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度值。
        modeA.gravity = ccp(0,0);

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 0;

        // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 20;
        modeA.speedVar = 5;


        //设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

       //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 1;
        m_fLifeVar = 0.5f;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 30.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

        // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.76f;
        m_tStartColor.g = 0.25f;
        m_tStartColor.b = 0.12f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleGalaxy,表现的是银河或一大堆星星。
class CC_DLL CCParticleGalaxy : public CCParticleSystemQuad
{
public:
	//构造与析构
    CCParticleGalaxy(){}
virtual ~CCParticleGalaxy(){}
//初始化,创建200个粒子。
bool init(){ return initWithTotalParticles(200); }
//创建相应数量的例子。
virtual bool initWithTotalParticles(unsigned int numberOfParticles);
//创建当前类的实例,内部调用create实现。
    static CCParticleGalaxy * node()
    {
        return create();
    }
	//上面的create实现。
    static CCParticleGalaxy * create()
    {
        CCParticleGalaxy *pRet = new CCParticleGalaxy();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
其对应的CPP:
bool CCParticleGalaxy::initWithTotalParticles(unsigned int numberOfParticles)
{
//调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度值。
        modeA.gravity = ccp(0,0);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 60;
        modeA.speedVar = 10;

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = -80;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 80;
        modeA.tangentialAccelVar = 0;

        //设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

       //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 4;
        m_fLifeVar = 1;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 37.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

        //发射器的发射速率
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.12f;
        m_tStartColor.g = 0.25f;
        m_tStartColor.b = 0.76f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

        // 设置为加亮模式。
        this->setBlendAdditive(true);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleFlower,表现的是花瓣洒落。
class CC_DLL CCParticleFlower : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleFlower(){}
    virtual ~CCParticleFlower(){}
    bool init(){ return initWithTotalParticles(250); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleFlower * node()
    {
        return create();
    }

    static CCParticleFlower * create()
    {
        CCParticleFlower *pRet = new CCParticleFlower();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应的CPP:
bool CCParticleFlower::initWithTotalParticles(unsigned int numberOfParticles)
{
    //先调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。
        modeA.gravity = ccp(0,0);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 80;
        modeA.speedVar = 10;

         // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = -60;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 15;
        modeA.tangentialAccelVar = 0;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 4;
        m_fLifeVar = 1;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 30.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.50f;
        m_tStartColor.g = 0.50f;
        m_tStartColor.b = 0.50f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.5f;
        m_tStartColorVar.g = 0.5f;
        m_tStartColorVar.b = 0.5f;
        m_tStartColorVar.a = 0.5f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置为加亮模式。
        this->setBlendAdditive(true);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleMeteor,表现的是流星
class CC_DLL CCParticleMeteor : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleMeteor(){}
    virtual ~CCParticleMeteor(){}
    bool init(){ return initWithTotalParticles(150); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleMeteor * node()
    {
        return create();
    }
    static CCParticleMeteor * create()
    {
        CCParticleMeteor *pRet = new CCParticleMeteor();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
bool CCParticleMeteor::initWithTotalParticles(unsigned int numberOfParticles)
{
    if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

         // 设置重力加速度的值。

        modeA.gravity = ccp(-200,200);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 15;
        modeA.speedVar = 5;

         // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 0;
        modeA.tangentialAccelVar = 0;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 2;
        m_fLifeVar = 1;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 60.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.2f;
        m_tStartColor.g = 0.4f;
        m_tStartColor.b = 0.7f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.2f;
        m_tStartColorVar.a = 0.1f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置为加亮模式。
        this->setBlendAdditive(true);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleSpiral,表现的是螺旋线
class CC_DLL CCParticleSpiral : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleSpiral(){}
    virtual ~CCParticleSpiral(){}
    bool init(){ return initWithTotalParticles(500); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleSpiral * node()
    {
        return create();
    }
    static CCParticleSpiral * create()
    {
        CCParticleSpiral *pRet = new CCParticleSpiral();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleSpiral::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) ) 
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

         // 设置重力加速度的值。

        modeA.gravity = ccp(0,0);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 150;
        modeA.speedVar = 0;

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = -380;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 45;
        modeA.tangentialAccelVar = 0;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 0;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 12;
        m_fLifeVar = 0;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 20.0f;
        m_fStartSizeVar = 0.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.5f;
        m_tStartColor.g = 0.5f;
        m_tStartColor.b = 0.5f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.5f;
        m_tStartColorVar.g = 0.5f;
        m_tStartColorVar.b = 0.5f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.5f;
        m_tEndColor.g = 0.5f;
        m_tEndColor.b = 0.5f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.5f;
        m_tEndColorVar.g = 0.5f;
        m_tEndColorVar.b = 0.5f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleExplosion,表现的是爆炸
class CC_DLL CCParticleExplosion : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleExplosion(){}
    virtual ~CCParticleExplosion(){}
    bool init(){ return initWithTotalParticles(700); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleExplosion * node()
    {
        return create();
    }
    static CCParticleExplosion * create()
    {
        CCParticleExplosion *pRet = new CCParticleExplosion();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleExplosion::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) ) 
    {
        // 设置为一直不断的发射。
        m_fDuration = 0.1f;

		// 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

         // 设置重力加速度的值。

        modeA.gravity = ccp(0,0);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 70;
        modeA.speedVar = 40;

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 0;
        modeA.tangentialAccelVar = 0;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 5.0f;
        m_fLifeVar = 2;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 15.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fDuration;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.7f;
        m_tStartColor.g = 0.1f;
        m_tStartColor.b = 0.2f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.5f;
        m_tStartColorVar.g = 0.5f;
        m_tStartColorVar.b = 0.5f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.5f;
        m_tEndColor.g = 0.5f;
        m_tEndColor.b = 0.5f;
        m_tEndColor.a = 0.0f;
        m_tEndColorVar.r = 0.5f;
        m_tEndColorVar.g = 0.5f;
        m_tEndColorVar.b = 0.5f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleSmoke,表现的是烟雾
class CC_DLL CCParticleSmoke : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleSmoke(){}
    virtual ~CCParticleSmoke(){}
    bool init(){ return initWithTotalParticles(200); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleSmoke * node()
    {
        return create();
    }
    static CCParticleSmoke * create()
    {
        CCParticleSmoke *pRet = new CCParticleSmoke();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleSmoke::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。
        modeA.gravity = ccp(0,0);

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 0;

         // 设置速度及其用于随机初始化的范围值。

        modeA.speed = 25;
        modeA.speedVar = 10;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 5;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, 0));
        m_tPosVar = ccp(20, 0);

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 4;
        m_fLifeVar = 1;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 60.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

       // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.8f;
        m_tStartColor.g = 0.8f;
        m_tStartColor.b = 0.8f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.02f;
        m_tStartColorVar.g = 0.02f;
        m_tStartColorVar.b = 0.02f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleSnow,表现的是雪
class CC_DLL CCParticleSnow : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleSnow(){}
    virtual ~CCParticleSnow(){}
    bool init(){ return initWithTotalParticles(700); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleSnow * node()
    {
        return create();
    }

    static CCParticleSnow * create()
    {
        CCParticleSnow *pRet = new CCParticleSnow();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleSnow::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) ) 
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。

        modeA.gravity = ccp(0,-1);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 5;
        modeA.speedVar = 1;

         // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 1;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 0;
        modeA.tangentialAccelVar = 1;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height + 10));
        m_tPosVar = ccp( winSize.width/2, 0 );

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = -90;
        m_fAngleVar = 5;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 45;
        m_fLifeVar = 15;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 10.0f;
        m_fStartSizeVar = 5.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = 10;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 1.0f;
        m_tStartColor.g = 1.0f;
        m_tStartColor.b = 1.0f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 1.0f;
        m_tEndColor.g = 1.0f;
        m_tEndColor.b = 1.0f;
        m_tEndColor.a = 0.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleRain,表现的是雨
class CC_DLL CCParticleRain : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleRain(){}
    virtual ~CCParticleRain(){}
    bool init(){ return initWithTotalParticles(1000); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleRain * node()
    {
        return create();
    }
    static CCParticleRain * create()
    {
        CCParticleRain *pRet = new CCParticleRain();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleRain::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;
		// 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。

        modeA.gravity = ccp(10,-10);

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 1;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 0;
        modeA.tangentialAccelVar = 1;

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 130;
        modeA.speedVar = 30;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = -90;
        m_fAngleVar = 5;


        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height));
        m_tPosVar = ccp( winSize.width/2, 0 );

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 4.5f;
        m_fLifeVar = 0;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 4.0f;
        m_fStartSizeVar = 2.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = 20;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.7f;
        m_tStartColor.g = 0.8f;
        m_tStartColor.b = 1.0f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.7f;
        m_tEndColor.g = 0.8f;
        m_tEndColor.b = 1.0f;
        m_tEndColor.a = 0.5f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

    CCParticleExample.h中都是典型的粒子系统的表现类。我们可以清楚的看到,粒子系统的基类已经将这些典型的粒子系统所需要的属性一一包含,想要使用粒子系统摸拟自然现象,所需要做的就是通过对其具有的粒子属性进行相应的设置,仅此而已。

 

在ParticleTest中,共有42个粒子系统的展示,它们均是基于ParticleDemo这个类进行派生实现的, ParticleDemo是基于CCLayerColor所派生的一个类,实现了对于粒子系统演示的管理控制,下面我们来看一下头文件:

//粒子系统演示所用的场景
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();
};

//演示表现烟火的粒子系统,由基类ParticleDemo派生。
class DemoFirework : public ParticleDemo
{
public:
    virtual void onEnter();
    virtual std::string title();
};
//演示表现火焰的粒子系统,由基类ParticleDemo派生。
class DemoFire : public ParticleDemo
{
public:
    virtual void onEnter();
    virtual std::string title();
};
后面雷同不再赘述…

    看上面的代码,很容易理解,每个粒子系统的演示都是一个层,创建一个场景用来管理这些演示层。在CPP中,我们可以找到以下函数的实现。

//场景运行时调用的函数处理。
void ParticleTestScene::runThisTest()
{
	//创建下一个粒子系统的演示层,放入当前场景。
    addChild(nextParticleAction());
	//调用设备的更新场景函数,运行当前场景。
    CCDirector::sharedDirector()->replaceScene(this);
}

    CPP中有三个函数,分别是运行下一个粒子系统的演示,运行上一个粒子系统的演示,重新进行当前粒子系统的演示。

//下一个粒子系统的演示
CCLayer* nextParticleAction()
{
	//演示的索引加一并对最大演示数量取模防止出现无效值。
    sceneIdx++;
    sceneIdx = sceneIdx % MAX_LAYER;
	//由索引创建相应的粒子系统的演示层并设为由存管理器进行引用计数器的管理。
    CCLayer* pLayer = createParticleLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//上一个粒子系统的演示
CCLayer* backParticleAction()
{
	//演示的索引减一进行无效值处理。
    sceneIdx--;
    int total = MAX_LAYER;
    if( sceneIdx < 0 )
        sceneIdx += total;    
    //由索引创建相应的粒子系统的演示层并设为由存管理器进行引用计数器的管理。
    CCLayer* pLayer = createParticleLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//重新创建当前的粒子系统的演示层。
CCLayer* restartParticleAction()
{
//由索引创建相应的粒子系统的演示层并设为由存管理器进行引用计数器的管理。
    CCLayer* pLayer = createParticleLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}

可以看到,就如TestCpp类似的管理方案,所有的演示都会放在createParticleLayer中,通过索引来进行相应的控制管理。

//没错,就是这样。
CCLayer* createParticleLayer(int nIndex)
{
    switch(nIndex)
    {
        case 0: return new ParticleReorder();
        case 1: return new ParticleBatchHybrid();
        case 2: return new ParticleBatchMultipleEmitters();
        case 3: return new DemoFlower();
        case 4: return new DemoGalaxy();
        case 5: return new DemoFirework();
        case 6: return new DemoSpiral();
        case 7: return new DemoSun();
        case 8: return new DemoMeteor();
        case 9: return new DemoFire();
        case 10: return new DemoSmoke();
        case 11: return new DemoExplosion();
        case 12: return new DemoSnow();
        case 13: return new DemoRain();
        case 14: return new DemoBigFlower();
        case 15: return new DemoRotFlower();
        case 16: return new DemoModernArt();
        case 17: return new DemoRing();
        case 18: return new ParallaxParticle();
        case 19: return new DemoParticleFromFile("BoilingFoam");
        case 20: return new DemoParticleFromFile("BurstPipe");
        case 21: return new DemoParticleFromFile("Comet");
        case 22: return new DemoParticleFromFile("debian");
        case 23: return new DemoParticleFromFile("ExplodingRing");
        case 24: return new DemoParticleFromFile("LavaFlow");
        case 25: return new DemoParticleFromFile("SpinningPeas");
        case 26: return new DemoParticleFromFile("SpookyPeas");
        case 27: return new DemoParticleFromFile("Upsidedown");
        case 28: return new DemoParticleFromFile("Flower");
        case 29: return new DemoParticleFromFile("Spiral");
        case 30: return new DemoParticleFromFile("Galaxy");
        case 31: return new DemoParticleFromFile("Phoenix");
        case 32: return new RadiusMode1();
        case 33: return new RadiusMode2();
        case 34: return new Issue704();
        case 35: return new Issue870();
        case 36: return new Issue1201();
        // v1.1 tests
        case 37: return new MultipleParticleSystems();
        case 38: return new MultipleParticleSystemsBatched();
        case 39: return new AddAndDeleteParticleSystems();
        case 40: return new ReorderParticleSystems();
        case 41: return new PremultipliedAlphaTest();
        case 42: return new PremultipliedAlphaTest2();
        default:
            break;
    }

    return NULL;
}

然后是演示粒子效果所用的层的基类ParticleDemo:

//析构函数。
ParticleDemo::~ParticleDemo(void)
{
	//释放粒子系统。
    CC_SAFE_RELEASE(m_emitter);
}
//加载当前层时调用的函数。
void ParticleDemo::onEnter(void)
{
	//调用基类的相应函数。
    CCLayer::onEnter();
    //初始化背景色。
    initWithColor( ccc4(127,127,127,255) );
    //粒子系统置空。
    m_emitter = NULL;
    //设置接受触屏消息。
    setTouchEnabled( true );
    //取得屏幕大小。
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//创建主标题文字标签并放到当前层中的相应位置。
    CCLabelTTF* label = CCLabelTTF::create(title().c_str(), "Arial", 28);
    addChild(label, 100, 1000);
    label->setPosition( CCPointMake(s.width/2, s.height-50) );
    //创建幅标题文字标签并放到当前层中的相应位置。
    CCLabelTTF *sub = CCLabelTTF::create(subtitle().c_str(), "Arial", 16);
    addChild(sub, 100);
    sub->setPosition(CCPointMake(s.width/2, s.height-80));
    
    //创建菜单按钮用于控制演示。
    CCMenuItemImage* item1 = CCMenuItemImage::create(s_pPathB1, s_pPathB2, this, menu_selector(ParticleDemo::backCallback) );
    CCMenuItemImage* item2 = CCMenuItemImage::create(s_pPathR1, s_pPathR2, this, menu_selector(ParticleDemo::restartCallback) );
    CCMenuItemImage* item3 = CCMenuItemImage::create(s_pPathF1, s_pPathF2,  this, menu_selector(ParticleDemo::nextCallback) );
    //创建一个菜单按钮用于切换运动时的受影响类型,响应函数为ParticleDemo::toggleCallback。
    CCMenuItemToggle* item4 = CCMenuItemToggle::createWithTarget(this, 
                                                               menu_selector(ParticleDemo::toggleCallback), 
                                                               CCMenuItemFont::create( "Free Movement" ),
                                                               CCMenuItemFont::create( "Relative Movement" ),
                                                               CCMenuItemFont::create( "Grouped Movement" ),
                                                               NULL );
    //创建菜单。
    CCMenu *menu = CCMenu::create(item1, item2, item3, item4, NULL);
    //设置各菜单按钮的位置。
    menu->setPosition( CCPointZero );
    item1->setPosition( ccp( s.width/2 - item2->getContentSize().width*2, item2->getContentSize().height/2) );
    item2->setPosition( ccp( s.width/2, item2->getContentSize().height/2) );
    item3->setPosition( ccp( s.width/2 + item2->getContentSize().width*2, item2->getContentSize().height/2) );
    item4->setPosition( ccp( 0, 100) );
    item4->setAnchorPoint( ccp(0,0) );
    //将菜单放入当前层中。
    addChild( menu, 100 );
    //创建一个文字标签显示FPS
    CCLabelAtlas* labelAtlas = CCLabelAtlas::create("0000", "fps_images.png", 12, 32, '.');
    addChild(labelAtlas, 100, kTagParticleCount);
    labelAtlas->setPosition(ccp(s.width-66,50));
    
    //创建背景图精灵并放在当前层的相应位置。
    m_background = CCSprite::create(s_back3);
    addChild(m_background, 5);
    m_background->setPosition( ccp(s.width/2, s.height-180) );
    //创建移动动画以及反向动画。
    CCActionInterval* move = CCMoveBy::create(4, ccp(300,0) );
    CCActionInterval* move_back = move->reverse();
	//将两个动画放到一个动画序列中。
    CCFiniteTimeAction* seq = CCSequence::create( move, move_back, NULL);
    //让背景图精录无限循环执行这个动画序列。
m_background->runAction( CCRepeatForever::create((CCActionInterval*)seq) );
    
    //让当前层响应Update函数。
    scheduleUpdate();
	//取得主标题并设置显示文字串。
    CCLabelTTF* pLabel = (CCLabelTTF*)(this->getChildByTag(1000));
    pLabel->setString(title().c_str());
}
//取得主标题。
std::string ParticleDemo::title()
{
    return "No title";
}
//取得幅标题。
std::string ParticleDemo::subtitle()
{
    return "No titile";
}
//开启触屏消息响应。
void ParticleDemo::registerWithTouchDispatcher()
{
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, false);
}
//当手指按下屏幕时
bool ParticleDemo::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
    return true;
}
//当手指在按下屏幕状态并滑动时响应。
void ParticleDemo::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
    return ccTouchEnded(touch, event);
}
//当手指从按下状态抬起时响应。
void ParticleDemo::ccTouchEnded(CCTouch* touch, CCEvent* event)
{
	//取得按下的屏幕位置
    CCPoint location = touch->getLocation();
	//创建临时变量取得背景精灵的位置。
    CCPoint pos = CCPointZero;
    if (m_background)
    {
        pos = m_background->convertToWorldSpace(CCPointZero);
    }
	//如果粒子系统发射器有效,设置其位置为触点位置减去背景精零的零点世界位置(即当前层的左下角世界位置),因为粒子系统是当前层的子结点,所以ccpSub(location, pos)结果即为触点与当前层的相对位置。。
    if (m_emitter != NULL)
    {
        m_emitter->setPosition( ccpSub(location, pos) );    
    }
}

//更新函数。
void ParticleDemo::update(float dt)
{
	//显示粒子的数量。
    if (m_emitter)
    {
        CCLabelAtlas* atlas = (CCLabelAtlas*)getChildByTag(kTagParticleCount);
        char str[5] = {0};
        sprintf(str, "%04d", m_emitter->getParticleCount());
        atlas->setString(str);
    }
}

//点击切换粒子系统的运动影响方式时的响应函数。
void ParticleDemo::toggleCallback(CCObject* pSender)
{
	//根据当前粒子系统运动影响方式类型,切换到下一种枚举的运动影响方式类型。
    if (m_emitter != NULL)
    {
        if( m_emitter->getPositionType() == kCCPositionTypeGrouped )
            m_emitter->setPositionType( kCCPositionTypeFree );
        else if (m_emitter->getPositionType() == kCCPositionTypeFree)
            m_emitter->setPositionType(kCCPositionTypeRelative);
        else if (m_emitter->getPositionType() == kCCPositionTypeRelative)
            m_emitter->setPositionType( kCCPositionTypeGrouped );
    }
}
//重新启动当前的粒子系统。
void ParticleDemo::restartCallback(CCObject* pSender)
{
    if (m_emitter != NULL)
    {
        m_emitter->resetSystem(); 
    }
}
//切换到下一个粒子系统演示。
void ParticleDemo::nextCallback(CCObject* pSender)
{
    CCScene* s = new ParticleTestScene();
    s->addChild( nextParticleAction() );
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
}
//返回上一个粒子系统的演示。
void ParticleDemo::backCallback(CCObject* pSender)
{
    CCScene* s = new ParticleTestScene();
    s->addChild( backParticleAction() );
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
} 
//设置粒子系统发射器的位置为屏幕中心位置。
void ParticleDemo::setEmitterPosition()
{
    CCSize s = CCDirector::sharedDirector()->getWinSize();
    if (m_emitter != NULL)
    {
        m_emitter->setPosition( CCPointMake(s.width / 2, s.height / 2) );
    }
}

OK,这个基类搞定后,下面我们来一一把本演示DEMO中的演示实例进行解释:

(1)    ParticleReorder

描述:

粒子系统的排序。每秒对粒子系统进行一个不同的显示顺序的设定。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

 源码解析:

//当前层加载时的处理。
void ParticleReorder::onEnter()
{
	//调用基类的相应初始化函数。
    ParticleDemo::onEnter();
	//
    m_nOrder = 0;
	//设置层背景色为黑色。
    setColor(ccBLACK);
	//将背景精灵移除并置空
    removeChild(m_background, true);
    m_background = NULL;
	//从SmallSun.plist中加载创建一个粒子系统。
    CCParticleSystem* ignore = CCParticleSystemQuad::create("Particles/SmallSun.plist");
	//创建第一个父结点。
    CCNode *parent1 = CCNode::create();
	//由粒子系统取出的纹理对象创建一个粒子批次结点做为第二个父结点。
    CCNode *parent2 = CCParticleBatchNode::createWithTexture(ignore->getTexture());
	//粒子系统不调用Update函数。
    ignore->unscheduleUpdate();
	//
    for( unsigned int i=0; i<2;i++) 
    {
		//循环两次,第一次parent取parent1,第二次取parent2。
        CCNode *parent = ( i==0 ? parent1 : parent2 );
		//由SmallSun.plist文件创建第一个粒子系统。
        CCParticleSystemQuad *emitter1 = CCParticleSystemQuad::create("Particles/SmallSun.plist");
		//设置粒子系统的粒子的起始颜色。
        emitter1->setStartColor(ccc4f(1,0,0,1));
		//设置粒子系统不使用加亮模式。
        emitter1->setBlendAdditive(false);
		//由SmallSun.plist文件创建第二个粒子系统。
        CCParticleSystemQuad *emitter2 = CCParticleSystemQuad::create("Particles/SmallSun.plist");
		//设置粒子系统的粒子的起始颜色。
        emitter2->setStartColor(ccc4f(0,1,0,1));
		//设置粒子系统不使用加亮模式。
        emitter2->setBlendAdditive(false);
		//由SmallSun.plist文件创建第三个粒子系统。
        CCParticleSystemQuad *emitter3 = CCParticleSystemQuad::create("Particles/SmallSun.plist");
		//设置粒子系统的粒子的起始颜色。
        emitter3->setStartColor(ccc4f(0,0,1,1));
		//设置粒子系统不使用加亮模式。
        emitter3->setBlendAdditive(false);
		//取得屏幕大小。
        CCSize s = CCDirector::sharedDirector()->getWinSize();
		//根据遍历计数器值设定一个位置影响参数。
        int neg = (i==0 ? 1 : -1 );
		//设置三个粒子系统的位置。
        emitter1->setPosition(ccp( s.width/2-30,    s.height/2+60*neg));
        emitter2->setPosition(ccp( s.width/2,        s.height/2+60*neg));
        emitter3->setPosition(ccp( s.width/2+30,    s.height/2+60*neg));
		//将三个粒子系统放入相应的父结点。
        parent->addChild(emitter1, 0, 1);
        parent->addChild(emitter2, 0, 2);
        parent->addChild(emitter3, 0, 3);
		//将父结点做为子结点放入当前层。
        addChild(parent, 10, 1000+i);
    }
	//定时器每秒响应一次ParticleReorder::reorderParticles函数。
    schedule(schedule_selector(ParticleReorder::reorderParticles), 1.0f);
}
//取得标题。
std::string ParticleReorder::title()
{
    return "Reordering particles";
}
//取得幅标题。
std::string ParticleReorder::subtitle()
{
    return "Reordering particles with and without batches batches";
}
//每秒响应的回调函数。
void ParticleReorder::reorderParticles(float dt)
{
    for( int i=0; i<2;i++) {
		//取得不同的粒子系统父结点。
        CCNode *parent = getChildByTag(1000+i);
		//取得相应的粒子系统。
        CCNode *child1 = parent->getChildByTag(1);
        CCNode *child2 = parent->getChildByTag(2);
        CCNode *child3 = parent->getChildByTag(3);
		//对3取模后设置不同的排序。
        if( m_nOrder % 3 == 0 ) {
            parent->reorderChild(child1, 1);
            parent->reorderChild(child2, 2);
            parent->reorderChild(child3, 3);

        } else if (m_nOrder % 3 == 1 ) {
            parent->reorderChild(child1, 3);
            parent->reorderChild(child2, 1);
            parent->reorderChild(child3, 2);

        } else if (m_nOrder % 3 == 2 ) {
            parent->reorderChild(child1, 2);
            parent->reorderChild(child2, 3);
            parent->reorderChild(child3, 1);
        }
    }

    m_nOrder++;
}

(2)    ParticleBatchHybrid

描述:

表现使用批次结点

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码分析:

//加载当前层时的处理。
void ParticleBatchHybrid::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景色为黑色。
    setColor(ccBLACK);
	//删除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//由LavaFlow.plist文件创建粒子系统。
    m_emitter = CCParticleSystemQuad::create("Particles/LavaFlow.plist");
	//占用此粒子系统,对其引用计数器加一操作。
    m_emitter->retain();
	//由粒子系统的纹理对象创建一个粒子批次结点。
    CCParticleBatchNode *batch = CCParticleBatchNode::createWithTexture(m_emitter->getTexture());
	//将粒子系统加入到粒子批次结点中。
    batch->addChild(m_emitter);
	//将粒子批次结点加入到当前层中。
    addChild(batch, 10);
	//每2秒调用一次ParticleBatchHybrid::switchRender函数切换使用批次结点。
     schedule(schedule_selector(ParticleBatchHybrid::switchRender), 2.0f);
 	//创建一个结点做为子结点。
     CCNode *node = CCNode::create();
     addChild(node);
 	//第一父结点设为批次结点。
     m_pParent1 = batch;
	//第二父结点设为上面的子结点。
     m_pParent2 = node;
}
//切换使用或不使用批次结点的函数。
void ParticleBatchHybrid::switchRender(float dt)
{
	//取得是否使用了批次结点。
     bool usingBatch = ( m_emitter->getBatchNode() != NULL );
	//从父结点中移除。
     m_emitter->removeFromParentAndCleanup(false);
 	//根据上面的usingBatch来设置不同的父结点,这样就可以让粒子系统进行来回切换了。
     CCNode *newParent = (usingBatch ? m_pParent2  : m_pParent1 );
	//将粒子系统放入父结点中。
     newParent->addChild(m_emitter);
	//打印信息
     CCLog("Particle: Using new parent: %s", usingBatch ? "CCNode" : "CCParticleBatchNode");
}
//取得标题。
std::string ParticleBatchHybrid::title()
{
    return "Paticle Batch";
}
//取得幅标题。
std::string ParticleBatchHybrid::subtitle()
{
    return "Hybrid: batched and non batched every 2 seconds";
}

(3)    ParticleBatchMultipleEmitters

描述:

多个粒子系统应用一个批次结点。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码分析:

//加载当前层的处理。
void ParticleBatchMultipleEmitters::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并将指针置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建三个粒子系统,分别设置不同的初始颜色。
    CCParticleSystemQuad *emitter1 = CCParticleSystemQuad::create("Particles/LavaFlow.plist");
    emitter1->setStartColor(ccc4f(1,0,0,1));
    CCParticleSystemQuad *emitter2 = CCParticleSystemQuad::create("Particles/LavaFlow.plist");
    emitter2->setStartColor(ccc4f(0,1,0,1));
    CCParticleSystemQuad *emitter3 = CCParticleSystemQuad::create("Particles/LavaFlow.plist");
    emitter3->setStartColor(ccc4f(0,0,1,1));
	//取得屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//设置粒子系统的位置。
    emitter1->setPosition(ccp( s.width/1.25f, s.height/1.25f));
    emitter2->setPosition(ccp( s.width/2, s.height/2));
    emitter3->setPosition(ccp( s.width/4, s.height/4));
	//由粒子系统的纹理创建相应的批次结点。
    CCParticleBatchNode *batch = CCParticleBatchNode::createWithTexture(emitter1->getTexture());
	//将粒子系统都放入批次结点中。

    batch->addChild(emitter1, 0);
    batch->addChild(emitter2, 0);
    batch->addChild(emitter3, 0);
	//将批次结点放入当前层。
    addChild(batch, 10);
}
//标题。
std::string ParticleBatchMultipleEmitters::title()
{
    return "Paticle Batch";
}
//幅标题。
std::string ParticleBatchMultipleEmitters::subtitle()
{
    return "Multiple emitters. One Batch";
}

(4)    DemoFlower:

描述:

花?不像,倒不如说是星光闪烁。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码分析:

//加载当前层时的处理
void DemoFlower::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建粒子系统。
    m_emitter = CCParticleFlower::create();
    m_emitter->retain();
	//将粒子系统放入背景精灵结点下。
    m_background->addChild(m_emitter, 10);
	//设置粒子系统使用星星的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
    //调用基类的相应函数将粒子发射器置于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoFlower::title()
{
    return "ParticleFlower";
}

注:这里星星的纹理为:Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

这是一个PNG格式的ALPHA渐变图,本身色彩就是由白到黑的一个亮度变化。通过粒子的顶点着色就能出现五彩缤纷的星光了。

(5)    DemoGalaxy

描述:

星系。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码分析:

//加载当前层时的处理。
void DemoGalaxy::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleGalaxy类的实例。
    m_emitter = CCParticleGalaxy::create();
	//占用,则对其使用计数器加一。
    m_emitter->retain();
	//将粒子系统放入背景结点下。
    m_background->addChild(m_emitter, 10);
    //加载一个火焰的图片,设置为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //调用基类的相应函数,设置粒子系统的位置居于屏幕中央。
    setEmitterPosition(); 
}
//取得标题
std::string DemoGalaxy::title()
{
    return "ParticleGalaxy";
}

注:这里火焰的纹理为:Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

这是一个PNG格式的ALPHA渐变图,本身色彩就是由白到黑的一个亮度变化。通过粒子的顶点着色就能出现五彩的火火焰了。

(6)     DemoFirework

描述:

烟火的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoFirework::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleFireworks类的实例。
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";
}

(7)     DemoSpiral

描述:

螺旋线的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层的处理。
void DemoSpiral::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSpiral类的实例。
m_emitter = CCParticleSpiral::create();
//占用,对其引用计数器加一。
m_emitter->retain();
//将粒子系统放在精灵背景结点之下。
    m_background->addChild(m_emitter, 10);
    //加载火焰图片设置为粒子系统所用的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
   //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoSpiral::title()
{
    return "ParticleSpiral";
}

(8)    DemoSun

描述:

太阳的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoSun::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSun类的实例
    m_emitter = CCParticleSun::create();
	//占用粒子系统,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统放在背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
	//加载火焰图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题文字。
std::string DemoSun::title()
{
    return "ParticleSun";
}

(9)    DemoMeteor:

描述:

流星的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层的处理。
void DemoMeteor::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleMeteor类的实例。
    m_emitter = CCParticleMeteor::create();
	//占用它,对它的引用计数器加一。
    m_emitter->retain();
	//将粒子系统加入到当前背景精灵节点之下。
    m_background->addChild(m_emitter, 10);
    //加载火焰的图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //设置粒子系统居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoMeteor::title()
{
    return "ParticleMeteor";
}

(10)     DemoFire

描述:

火焰的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

void DemoFire::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleFire类的实例。
    m_emitter = CCParticleFire::create();
	//占用它,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统放在背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
    //加载一个火焰图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );//.pvr");
	//取得粒子系统的位置。设置高度在100。
    CCPoint p = m_emitter->getPosition();
    m_emitter->setPosition( CCPointMake(p.x, 100) );
    //将粒子系统居中。
    setEmitterPosition();
}
//取得标题。
std::string DemoFire::title()
{
    return "ParticleFire";
}

(11)    DemoSmoke

描述:

烟雾的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoSmoke::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSmoke的实例。
    m_emitter = CCParticleSmoke::create();
	//占用它,对它的引用计数器加一。
    m_emitter->retain();
	//将粒子系统放入到背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
	//加载一个火焰的图片设置为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //取得粒子系统的位置并限制其高度在100。
    CCPoint p = m_emitter->getPosition();
    m_emitter->setPosition( CCPointMake( p.x, 100) );
    //将粒子系统居于屏幕中央。
    setEmitterPosition();
}
//取得粒子系统的标题。
std::string DemoSmoke::title()
{
    return "ParticleSmoke";
}

(12)    DemoExplosion

描述:

爆炸的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoExplosion::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleExplosion类的实例。
    m_emitter = CCParticleExplosion::create();
	//占用实例,对其引用计数器加一。
    m_emitter->retain();
	//将粒子发射器放到背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
    //加载星星图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
    //设置粒子释放完成后自动移除并释放,这样的话,不会连续不断的发射粒子。
    m_emitter->setAutoRemoveOnFinish(true);
    //设置粒子发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoExplosion::title()
{
    return "ParticleExplosion";
}

(13)    DemoSnow

描述:

雪的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoSnow::onEnter()
{
	//先调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSnow的实例。
    m_emitter = CCParticleSnow::create();
	//占用它,对它的引用计数器加一。
    m_emitter->retain();
	//将其放在背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
    //取得粒子系统的位置,设置为当前位置往下移110像素。具体位置请参看前面CCParticleExample中CCParticleSnow的initWithTotalParticles函数。
    CCPoint p = m_emitter->getPosition();
    m_emitter->setPosition( CCPointMake( p.x, p.y-110) );
	//设置生命值为3
    m_emitter->setLife(3);
	//设置生命随机加值范围为1,也就是生命值在2~4间初始化。
    m_emitter->setLifeVar(1);
    
    // 设置重力加速度值。
    m_emitter->setGravity(CCPointMake(0,-10));
        
    // 设置初始速度值及随机加值范围,生命值在100~160间初始化。
    m_emitter->setSpeed(130);
    m_emitter->setSpeedVar(30);
    
    //取得粒子系统的起始色彩值。
    ccColor4F startColor = m_emitter->getStartColor();
    startColor.r = 0.9f;
    startColor.g = 0.9f;
    startColor.b = 0.9f;
    m_emitter->setStartColor(startColor);
    //取得粒子系统的起始随机色彩加值范围。
    ccColor4F startColorVar = m_emitter->getStartColorVar();
    startColorVar.b = 0.1f;
    m_emitter->setStartColorVar(startColorVar);
    //设置粒子系统发射的速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles()/m_emitter->getLife());
    //加载雪的图片并设置为当前粒子系统使用的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_snow) );
    //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoSnow::title()
{
    return "ParticleSnow";
}

(14)    DemoRain:

描述:

雨的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//当前层加载时的处理
void DemoRain::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleRain的实例,
    m_emitter = CCParticleRain::create();
	//占用,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统加入到背景精灵结点下。
    m_background->addChild(m_emitter, 10);
    //取得粒子系统的位置,设置为当前位置往下移110像素。具体位置请参看前面CCParticleExample中CCParticleRain的initWithTotalParticles函数。
    CCPoint p = m_emitter->getPosition();
    m_emitter->setPosition( CCPointMake( p.x, p.y-100) );
	//设置粒子的生命值为4
    m_emitter->setLife(4);
    //加载火焰图片做为当前粒子系统的纹理
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoRain::title()
{
    return "ParticleRain";
}

(15)    DemoBigFlower

描述:

大颗星光闪烁的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoBigFlower::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSystemQuad的实例。
    m_emitter = new CCParticleSystemQuad();
	//创建50个粒子。
    m_emitter->initWithTotalParticles(50);
    //m_emitter->autorelease();
	//将创建的粒子系统放入到背景精灵的结点之下。
    m_background->addChild(m_emitter, 10);
    ////m_emitter->release();    // win32 :  use this line or remove this line and use autorelease()
	//加载星星的图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
	//设置粒子系统更新没有时长限制。
    m_emitter->setDuration(-1);
    
    //设置重力加速度。
    m_emitter->setGravity(CCPointZero);
    
    // 设置初始角度及角度初始随机变化范围
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(360);
    
    // 设置初始速度及速度初始随机变化范围
    m_emitter->setSpeed(160);
    m_emitter->setSpeedVar(20);
    
    //设置初始半径长度及半径长度初始随机变化范围
    m_emitter->setRadialAccel(-120);
    m_emitter->setRadialAccelVar(0);
    
    //设置初始切角度及切角度初始随机变化范围
    m_emitter->setTangentialAccel(30);
    m_emitter->setTangentialAccelVar(0);
    
    //设置初始位置及切位置初始随机变化范围
    m_emitter->setPosition( CCPointMake(160,240) );
    m_emitter->setPosVar(CCPointZero);
    
    //设置初始生命值及生命值随机变化范围
    m_emitter->setLife(4);
    m_emitter->setLifeVar(1);
    
    // 设置粒子起始的旋转角度和随机的旋转角度加值范围,这里设置为不旋转。
    m_emitter->setStartSpin(0);
    m_emitter->setStartSizeVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(0);
    
    // 设置粒子的起始,结束色彩值和其相应的随机变化范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);
    
    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);
    
    ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);
    
    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);
    
    // 设置粒子的大小值及其随机变化范围。
    m_emitter->setStartSize(80.0f);
    m_emitter->setStartSizeVar(40.0f);
	//结束大小设为宏kParticleStartSizeEqualToEndSize,代表大小不变化。
    m_emitter->setEndSize(kParticleStartSizeEqualToEndSize);
    
    // 设置粒子系统的发射速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles()/m_emitter->getLife());
    
    // 设置为加亮模式。
    m_emitter->setBlendAdditive(true);
	//设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoBigFlower::title()
{
    return "ParticleBigFlower";
}

(16)    DemoRotFlower

描述:

旋转的星光的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)
源码解析:

void DemoRotFlower::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个粒子系统的实例。
m_emitter = new CCParticleSystemQuad();
//创建300个粒子。
    m_emitter->initWithTotalParticles(300);
    //m_emitter->autorelease();
	//将粒子系统加入到背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
////m_emitter->release();    // win32 : Remove this line
//加载星星的图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars2) );
    
    // 设置粒子系统一直进行update
    m_emitter->setDuration(-1);
    
    // 设置无重力加速度。
    m_emitter->setGravity(CCPointZero);
    
    // 设置起始的角度以及初始化时的随机角度加值范围。
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(360);
    
    //设置起始的速度以及初始化时的随机速度加值范围。
    m_emitter->setSpeed(160);
    m_emitter->setSpeedVar(20);
    
    //设置起始的半径以及初始化时的随机半径加值范围。
    m_emitter->setRadialAccel(-120);
    m_emitter->setRadialAccelVar(0);
    
//设置起始的切角以及初始化时的随机切角加值范围。
    m_emitter->setTangentialAccel(30);
    m_emitter->setTangentialAccelVar(0);
    
    //设置起始的位置以及初始化时的随机位置加值范围。
    m_emitter->setPosition( CCPointMake(160,240) );
    m_emitter->setPosVar(CCPointZero);
    
    //设置起始的生命值以及初始化时的随机生命值加值范围。
    m_emitter->setLife(3);
    m_emitter->setLifeVar(1);

//设置起始的旋转角度以及初始化时的随机旋转角度加值范围。
    m_emitter->setStartSpin(0);
    m_emitter->setStartSpinVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(2000);
    
    //设置起始的颜色以及初始化时起始颜色的随机颜色加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);
    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);
    //设置结束的颜色以及初始化时结束颜色的随机颜色加值范围。
    ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);
    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);

    //设置起始的粒子大小以及初始化时起始粒子大小的随机颜色加值范围。
    m_emitter->setStartSize(30.0f);
m_emitter->setStartSizeVar(00.0f);
//设置在Update过程中大小不变。
    m_emitter->setEndSize(kParticleStartSizeEqualToEndSize);
    
    // 设置粒子系统发射速率
    m_emitter->setEmissionRate(m_emitter->getTotalParticles()/m_emitter->getLife());

    // 设置不使用加亮模式。
    m_emitter->setBlendAdditive(false);
    //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoRotFlower::title()
{
    return "ParticleRotFlower";
}

(17)    DemoModernArt

描述:

大小的变化

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载本层的源码
void DemoModernArt::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();

//创建一个粒子系统。
 glDrawArrays(GL_POINTS, 0, m_uParticleIdx);
//  m_emitter = new CCParticleSystemPoint();
    m_emitter = new CCParticleSystemQuad();
	//创建1000个粒子
    m_emitter->initWithTotalParticles(1000);
    //m_emitter->autorelease();
	//将粒子系统放入到背景精灵的结点下。
    m_background->addChild(m_emitter, 10);
    ////m_emitter->release();
    //取得屏幕大小。
    CCSize s = CCDirector::sharedDirector()->getWinSize();
    //设置一直更新粒子系统。
    m_emitter->setDuration(-1);
    //设置重力加速度。
    m_emitter->setGravity(CCPointMake(0,0));
    
    //设置角度初始值及角度初始值的随机加值范围 。
    m_emitter->setAngle(0);
    m_emitter->setAngleVar(360);
    
    //设置半径初始值及半径初始值的随机加值范围。
    m_emitter->setRadialAccel(70);
    m_emitter->setRadialAccelVar(10);
    
    //设置切角初始值及切角初始值的随机加值范围。
    m_emitter->setTangentialAccel(80);
    m_emitter->setTangentialAccelVar(0);
    
    //设置速度初始值及速度初始值的随机加值范围。
    m_emitter->setSpeed(50);
    m_emitter->setSpeedVar(10);
    
    //设置位置初始值在屏幕中央。
    m_emitter->setPosition( CCPointMake( s.width/2, s.height/2) );
    m_emitter->setPosVar(CCPointZero);
    
    //设置粒子的生命初始值及初始时生命值的随机加值范围。
    m_emitter->setLife(2.0f);
    m_emitter->setLifeVar(0.3f);
    
    //设置粒子系统的发射速率。
m_emitter->setEmissionRate(m_emitter->getTotalParticles()/m_emitter->getLife());
    
    //设置粒子系统的起始颜色和起始颜色的随机加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);
    
    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);

    //设置粒子系统的结束颜色和结束颜色的随机加值范围。
    ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);
    
    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);
    
    //设置粒子系统的起始大小和起始大小的随机加值范围。
    m_emitter->setStartSize(1.0f);
    m_emitter->setStartSizeVar(1.0f);
	//设置粒子系统的结束大小和结束大小的随机加值范围。
    m_emitter->setEndSize(32.0f);
    m_emitter->setEndSizeVar(8.0f);
    
    //加载火焰的图片做为粒子系统的纹理。
m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    
    //设置不使用加亮模式。
    m_emitter->setBlendAdditive(false);
    //设置粒子系统居中。
    setEmitterPosition();
}
//取得标题。
std::string DemoModernArt::title()
{
    return "Varying size";
}

(18)    DemoRing

描述:

光环的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoRing::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleFlower类的粒子系统实例。
    m_emitter = CCParticleFlower::create();
	//占用它,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统加入到背景精灵的结点之下。
    m_background->addChild(m_emitter, 10);

    //加载星星的图片做为粒子系统的纹理。
m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
	//设置粒子的生命值为10。
    m_emitter->setLifeVar(0);
    m_emitter->setLife(10);
	//设置粒子的速度。
    m_emitter->setSpeed(100);
    m_emitter->setSpeedVar(0);
	//设置粒子发射器的发射速率。
    m_emitter->setEmissionRate(10000);
    //设置粒子系统的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoRing::title()
{
    return "Ring Demo";
}

(19)    ParallaxParticle

描述:

视差的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void ParallaxParticle::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
    //将背景精灵移除。
    m_background->getParent()->removeChild(m_background, true);
    m_background = NULL;
	//创建一个CCParallaxNode结点p。
    CCParallaxNode* p = CCParallaxNode::create(); 
	//将p放入当前层中。
    addChild(p, 5);
	//创建两个以s_back3为纹理的精灵p1,p2。
    CCSprite *p1 = CCSprite::create(s_back3);
    CCSprite *p2 = CCSprite::create(s_back3);
    //将p1,p2分别放入CCParallaxNode结点p中。
    p->addChild( p1, 1, CCPointMake(0.5f,1), CCPointMake(0,250) );
    p->addChild(p2, 2, CCPointMake(1.5f,1), CCPointMake(0,50) );
	//创建一个CCParticleFlower类的粒子系统实例。
    m_emitter = CCParticleFlower::create();
	//占用它,对其引用计数器加一。
    m_emitter->retain();
    //加载火焰的图片做为粒子系统的纹理。
m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
	//将粒子系统emitter放入p1的结点下。
    p1->addChild(m_emitter, 10);
	//设置粒子系统的位置。
    m_emitter->setPosition( CCPointMake(250,200) );
    
	//创建一个CCParticleSun类的粒子系统实例。
    CCParticleSun* par = CCParticleSun::create();
	//将粒子系统par放入p2的结点下。
    p2->addChild(par, 10);
    //加载火焰的图片做为粒子系统的纹理。par->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //创建动画move和反向动画move_back,由两个动画创建动画序列seq.
    CCActionInterval* move = CCMoveBy::create(4, CCPointMake(300,0));
    CCActionInterval* move_back = move->reverse();
    CCFiniteTimeAction* seq = CCSequence::create( move, move_back, NULL);
	//让CCParallaxNode结点p运行这个序列动画。
    p->runAction(CCRepeatForever::create((CCActionInterval*)seq));    
}
//取得标题。
std::string ParallaxParticle::title()
{
    return "Parallax + Particles";
}

(20)    DemoParticleFromFile

描述:

从PLIST文件中创建一个粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时进行的处理。
void DemoParticleFromFile::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景为黑色。
    setColor(ccBLACK);
	//将背景精灵移除并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建一个新的粒子系统。
    m_emitter = new CCParticleSystemQuad();
	//通过参数创建一个字符串做为PLIST文件名用于加载一个粒子系统。
    std::string filename = "Particles/" + m_title + ".plist";
    m_emitter->initWithFile(filename.c_str());
	//将粒子系统加入当前结点下。
    addChild(m_emitter, 10);
	//设置粒子系统居中。
    setEmitterPosition();
}

在createParticleLayer函数中有
        case 19: return new DemoParticleFromFile("BoilingFoam");
        case 20: return new DemoParticleFromFile("BurstPipe");
        case 21: return new DemoParticleFromFile("Comet");
        case 22: return new DemoParticleFromFile("debian");
        case 23: return new DemoParticleFromFile("ExplodingRing");
        case 24: return new DemoParticleFromFile("LavaFlow");
        case 25: return new DemoParticleFromFile("SpinningPeas");
        case 26: return new DemoParticleFromFile("SpookyPeas");
        case 27: return new DemoParticleFromFile("Upsidedown");
        case 28: return new DemoParticleFromFile("Flower");
        case 29: return new DemoParticleFromFile("Spiral");
        case 30: return new DemoParticleFromFile("Galaxy");
        case 31: return new DemoParticleFromFile("Phoenix");
大家可以一一查看,其实原理就是通过PLIST文件创建不同型态的粒子系统。

(21)    RadiusMode1

描述:

环形运动模式演示一。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理
void RadiusMode1::onEnter()
{
	//调用基类的相应处理
    ParticleDemo::onEnter();
	//设置背景色为黑色
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;

	//创建粒子系统,创建200个粒子。
    m_emitter = new CCParticleSystemQuad();
    m_emitter->initWithTotalParticles(200);
    addChild(m_emitter, 10);
    //加载图片做为粒子系统的纹理。
m_emitter->setTexture(CCTextureCache::sharedTextureCache()->addImage("Images/stars-grayscale.png"));

    //设置粒子系统永远更新,宏kCCParticleDurationInfinity值为-1。
    m_emitter->setDuration(kCCParticleDurationInfinity);

    // 设置粒子系统运动模式为环型模式。
    m_emitter->setEmitterMode(kCCParticleModeRadius);

    // 环型模式的起始和结束半径以及相应的半径随机加成范围值。
    m_emitter->setStartRadius(0);
    m_emitter->setStartRadiusVar(0);
    m_emitter->setEndRadius(160);
    m_emitter->setEndRadiusVar(0);

    // 设置环型运动模式的每秒旋转角度及相应的随机加成范围值。
    m_emitter->setRotatePerSecond(180);
    m_emitter->setRotatePerSecondVar(0);


    // 设置初始时粒子系统的角度以及相应的随机加成范围值。
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(0);

    // 设置粒子系统的位置居于屏幕中央。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    m_emitter->setPosition(ccp(size.width/2, size.height/2));
    m_emitter->setPosVar(CCPointZero);

    // 设置初始时粒子的生命值为5。
    m_emitter->setLife(5);
    m_emitter->setLifeVar(0);

    // 设置粒子本身起始和结束状态不旋转
    m_emitter->setStartSpin(0);
    m_emitter->setStartSpinVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(0);

    //设置粒子系统发射粒子的起始颜色和随机加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);

    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);

    //设置粒子系统发射粒子的终止颜色和随机加值范围。
    ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);

    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);

    // 设置粒子系统发射粒子的起始大小和随机加值范围,结束值指定为宏kCCParticleStartSizeEqualToEndSize代表大小在粒子运动时不变化。
    m_emitter->setStartSize(32);
    m_emitter->setStartSizeVar(0);
    m_emitter->setEndSize(kCCParticleStartSizeEqualToEndSize);

    // 设置粒子系统的发射速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles() / m_emitter->getLife());

    // 设置不使用高亮模式。
    m_emitter->setBlendAdditive(false);
}
//取得标题。
std::string RadiusMode1::title()
{
    return "Radius Mode: Spiral";
}

(22)    RadiusMode2

描述:

环形运动模式演示一。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void RadiusMode2::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建粒子系统,创建200个粒子。
    m_emitter = new CCParticleSystemQuad();
    m_emitter->initWithTotalParticles(200);
    addChild(m_emitter, 10);
    //加载图片做为粒子系统的纹理。m_emitter->setTexture(CCTextureCache::sharedTextureCache()->addImage("Images/stars-grayscale.png"));

    //设置粒子系统永远更新,宏kCCParticleDurationInfinity值为-1。
    m_emitter->setDuration(kCCParticleDurationInfinity);

    // 设置粒子系统运动模式为环型模式。
    m_emitter->setEmitterMode(kCCParticleModeRadius);

    // 环型模式的起始和结束半径以及相应的半径随机加成范围值。
    m_emitter->setStartRadius(100);
    m_emitter->setStartRadiusVar(0);
    m_emitter->setEndRadius(kCCParticleStartRadiusEqualToEndRadius);
    m_emitter->setEndRadiusVar(0);

    // 设置环型运动模式的每秒旋转角度及相应的随机加成范围值。
    m_emitter->setRotatePerSecond(45);
    m_emitter->setRotatePerSecondVar(0);


    // 设置初始时粒子系统的角度以及相应的随机加成范围值。
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(0);

    // 设置粒子系统的位置居于屏幕中央。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    m_emitter->setPosition(ccp(size.width/2, size.height/2));
    m_emitter->setPosVar(CCPointZero);

    // 设置初始时粒子的生命值为4。
    m_emitter->setLife(4);
    m_emitter->setLifeVar(0);

    // 设置粒子本身起始和结束状态不旋转
    m_emitter->setStartSpin(0);
    m_emitter->setStartSpinVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(0);

//设置粒子系统发射粒子的起始颜色和随机加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);

    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);

    //设置粒子系统发射粒子的终止颜色和随机加值范围。
        ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);

    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);

    // 设置粒子系统发射粒子的起始大小和随机加值范围,结束值指定为宏kCCParticleStartSizeEqualToEndSize代表大小在粒子运动时不变化。
    m_emitter->setStartSize(32);
    m_emitter->setStartSizeVar(0);
    m_emitter->setEndSize(kCCParticleStartSizeEqualToEndSize);

    // 设置粒子系统的发射速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles() / m_emitter->getLife());

    // 设置不使用高亮模式。
    m_emitter->setBlendAdditive(false);
}
//取得标题。
std::string RadiusMode2::title()
{
    return "Radius Mode: Semi Circle";
}

(23)    Issue704

描述:

从PLIST文件中创建一个粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void Issue704::onEnter()
{
	//调用基类的相应函数
    ParticleDemo::onEnter();
	//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建粒子系统,创建100个粒子。
    m_emitter = new CCParticleSystemQuad();
    m_emitter->initWithTotalParticles(100);
    addChild(m_emitter, 10);
    //加载图片做为粒子系统的纹理。m_emitter->setTexture(CCTextureCache::sharedTextureCache()->addImage("Images/fire.png"));

    //设置粒子系统永远更新,宏kCCParticleDurationInfinity值为-1。
    m_emitter->setDuration(kCCParticleDurationInfinity);

    // 设置粒子系统运动模式为环型模式。
    m_emitter->setEmitterMode(kCCParticleModeRadius);

    // 环型模式的起始和结束半径以及相应的半径随机加成范围值。
    m_emitter->setStartRadius(50);
    m_emitter->setStartRadiusVar(0);
    m_emitter->setEndRadius(kCCParticleStartRadiusEqualToEndRadius);
    m_emitter->setEndRadiusVar(0);

    // 设置环型运动模式的每秒旋转角度及相应的随机加成范围值。    
m_emitter->setRotatePerSecond(0);
    m_emitter->setRotatePerSecondVar(0);


// 设置初始时粒子系统的角度以及相应的随机加成范围值。
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(0);

    // 设置粒子系统的位置居于屏幕中央。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    m_emitter->setPosition(ccp(size.width/2, size.height/2));
    m_emitter->setPosVar(CCPointZero);

   // 设置初始时粒子的生命值为5。
    m_emitter->setLife(5);
    m_emitter->setLifeVar(0);

    // 设置粒子本身起始和结束状态不旋转
    m_emitter->setStartSpin(0);
    m_emitter->setStartSpinVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(0);

//设置粒子系统发射粒子的起始颜色和随机加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);

    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);

    //设置粒子系统发射粒子的终止颜色和随机加值范围。
        ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);

    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);

    // 设置粒子系统发射粒子的起始大小和随机加值范围,结束值指定为宏kCCParticleStartSizeEqualToEndSize代表大小在粒子运动时不变化。
    m_emitter->setStartSize(16);
    m_emitter->setStartSizeVar(0);
    m_emitter->setEndSize(kCCParticleStartSizeEqualToEndSize);

    // 设置粒子系统的发射速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles() / m_emitter->getLife());

    // 设置不使用高亮模式。
    m_emitter->setBlendAdditive(false);
	// 让粒子系统的发射器运行一个无限旋转的动画。
    CCRotateBy* rot = CCRotateBy::create(16, 360);
    m_emitter->runAction(CCRepeatForever::create(rot));
}
//取得标题。
std::string Issue704::title()
{
    return "Issue 704. Free + Rot";
}
//取得幅标题。
std::string Issue704::subtitle()
{
    return "Emitted particles should not rotate";
}

(24)    Issue870

描述:

从PLIST文件中创建一个粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void Issue870::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();

//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建一个粒子系统,由一个PLIST文件来创建相应的粒子。
    CCParticleSystemQuad *system = new CCParticleSystemQuad();
    system->initWithFile("Particles/SpinningPeas.plist");
    //加载纹得中的一个图块做为粒子的纹理。
system->setTextureWithRect(CCTextureCache::sharedTextureCache()->addImage("Images/particles.png"), CCRectMake(0,0,32,32));
    addChild(system, 10);
	//将粒子系统实例指针保存到成员变量中。在后面updateQuads使用。
    m_emitter = system;
//图片中的图块索引。
    m_nIndex = 0;
//让当前层每2秒调用一下更新粒子的绘制矩形函数。
    schedule(schedule_selector(Issue870::updateQuads), 2.0f);
}
//更新粒子的绘制矩形函数。
void Issue870::updateQuads(float dt)
{
	//图块索引加一,并限定在[0,4)间。
    m_nIndex = (m_nIndex + 1) % 4;
	//通过图块索引来取得相应的图块位置并设置为粒子使用的纹理。
    CCRect rect = CCRectMake(m_nIndex * 32, 0, 32, 32);
    CCParticleSystemQuad* system = (CCParticleSystemQuad*)m_emitter;
    system->setTextureWithRect(m_emitter->getTexture(), rect);
}
//取得标题
std::string Issue870::title()
{
    return "Issue 870. SubRect";
}
//取得幅标题
std::string Issue870::subtitle()
{
    return "Every 2 seconds the particle should change";
}

(25)    Issue1201

描述:

似乎从截图上看不到什么,仔细看,并移动鼠标。这个实例应该是一个类似慧尾的效果,只是很淡。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理
void Issue1201::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建一个RainbowEffect类的实例,创建5O个粒子。
    RainbowEffect *particle = new RainbowEffect();
    particle->initWithTotalParticles(50);
	//将其加入到当前层结点下。
    addChild(particle);
	//将粒子系统的位置居于屏幕中央。
    CCSize s = CCDirector::sharedDirector()->getWinSize();
    particle->setPosition(ccp(s.width/2, s.height/2));
    m_emitter = particle;
}
//取得标题。
std::string Issue1201::title()
{
    return "Issue 1201. Unfinished";
}
//取得幅标题。
std::string Issue1201::subtitle()
{
    return "Unfinished test. Ignore it";
}

(26)    MultipleParticleSystems

描述:

多个粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理
void MultipleParticleSystems::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
    //加载纹得中的一个图块做为粒子的纹理。CCTextureCache::sharedTextureCache()->addImage("Images/particles.png"); 
	//创建5个粒子系统。
    for (int i = 0; i<5; i++) {
		//由相应的PLIST创建出粒子系统来。
        CCParticleSystemQuad *particleSystem = CCParticleSystemQuad::create("Particles/SpinningPeas.plist");
		//根据索引将粒子系统放在不同的位置。
        particleSystem->setPosition(ccp(i*50 ,i*50));
		//设置粒子系统运动影响方式类型为按组方式。
        particleSystem->setPositionType(kCCPositionTypeGrouped);
        addChild(particleSystem);
    }
	//粒子发射器指针置空。
    m_emitter = NULL;
}
//取得标题。
std::string MultipleParticleSystems::title()
{
    return "Multiple particle systems";
}
//取得幅标题。
std::string MultipleParticleSystems::subtitle()
{
    return "v1.1 test: FPS should be lower than next test";
}
//更新函数。
void MultipleParticleSystems::update(float dt)
{
	//取得用于标识粒子数量的文字标签。
    CCLabelAtlas *atlas = (CCLabelAtlas*) getChildByTag(kTagParticleCount);
	//定义一个计数器变量count
    unsigned int count = 0; 
    //遍历所有粒子系统子结点
    CCObject* pObj = NULL;
	//CCARRAY_FOREACH为遍历CArray数组的宏
    CCARRAY_FOREACH(m_pChildren, pObj)
    {
		 //通过dynamic_cast来将数组元素转换成为相应的粒子系统指针。
        CCParticleSystem* item = dynamic_cast<CCParticleSystem*>(pObj);
        if (item != NULL)
        {
			 //当前计数器变量count值加上当前粒子系统子结点的粒子数量,统计粒子总数。
            count += item->getParticleCount();    
        }
    }
	//让文字标签显示相应的粒子数量。
    char str[100] = {0};
    sprintf(str, "%4d", count);
    atlas->setString(str);
}

(27)    MultipleParticleSystemBatched

描述:

使用批次结点进行多外粒子系统的渲染管理。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//当前层加载时的处理
void MultipleParticleSystemsBatched::onEnter()
{
	//先调用基类的相应处理。
    ParticleDemo::onEnter();
//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建一个新的粒子系统的批次结点。
    CCParticleBatchNode *batchNode = new CCParticleBatchNode();
	//初始化,渲染用的矩形信息容器的容量初始化为3000。
    batchNode->initWithTexture(NULL, 3000);
	//将粒子系统的批次结点放入当前结点之下。
    addChild(batchNode, 1, 2);
	//创建5个粒子系统。
    for (int i = 0; i<5; i++) {
		//由PLIST文件创建相应的粒子系统。
        CCParticleSystemQuad *particleSystem = CCParticleSystemQuad::create("Particles/SpinningPeas.plist");
		//设置粒子系统运动影响方式类型为按组方式。
        particleSystem->setPositionType(kCCPositionTypeGrouped);         
		//根据索引将粒子系统放在不同的位置。
        particleSystem->setPosition(ccp(i*50 ,i*50));
		//设置使用粒子系统的纹理。
        batchNode->setTexture(particleSystem->getTexture());
        batchNode->addChild(particleSystem);
    }
	//已经放入到当前结点下,不再占用粒子系统批次结点,所以对其引用计数器减一操作。
    batchNode->release();
	//使用批次结点,则原指向所用的粒子系统发射器的指针置空。
    m_emitter = NULL;
}
//更新处理。
void MultipleParticleSystemsBatched::update(float dt)
{   //取得用于标识粒子数量的文字标签。
    CCLabelAtlas *atlas = (CCLabelAtlas*) getChildByTag(kTagParticleCount);
	//定义一个计数器变量count
    unsigned count = 0; 
    //根据索引取得粒子系统的批次结点。
    CCNode* batchNode = getChildByTag(2);
    //遍历所有粒子系统子结点
    CCObject* pObj = NULL;
	//CCARRAY_FOREACH为遍历CArray数组的宏
    CCARRAY_FOREACH(batchNode->getChildren(), pObj)
    {
		 //通过dynamic_cast来将数组元素转换成为相应的粒子系统指针。
        CCParticleSystem* item = dynamic_cast<CCParticleSystem*>(pObj);
        if (item != NULL)
        {
			 //当前计数器变量count值加上当前粒子系统子结点的粒子数量,统计粒子总数。
            count += item->getParticleCount();    
        }
    }
	//让文字标签显示相应的粒子数量。

    char str[50] = {0};
    sprintf(str, "%4d", count);
    atlas->setString(str);
}
//取得标题。
std::string MultipleParticleSystemsBatched::title()
{
    return "Multiple particle systems batched";
}
//取得幅标题。
std::string MultipleParticleSystemsBatched::subtitle()
{
    return "v1.1 test: should perform better than previous test";
}

(28)    AddAndDeleteParticleSystems

描述:

动态的增加和删除粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理
void AddAndDeleteParticleSystems::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();

//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;

    //创建一个粒子系统的批次结点,渲染用的矩形信息容器的容量初始化为16000。
    m_pBatchNode = CCParticleBatchNode::createWithTexture((CCTexture2D*)NULL, 16000);
	//将批次结点加入当前层中。
    addChild(m_pBatchNode, 1, 2);
	//创建6个粒子系统。
    for (int i = 0; i<6; i++) {
	//由相应的PLIST文件创建出粒子系统。
        CCParticleSystemQuad *particleSystem = CCParticleSystemQuad::create("Particles/Spiral.plist");
		//取出粒子系统所用的纹理设置为批次结点的纹理。
        m_pBatchNode->setTexture(particleSystem->getTexture());
		//设置粒子系统的运动方式为组运动方式,最大粒子数量为200。
        particleSystem->setPositionType(kCCPositionTypeGrouped);         
        particleSystem->setTotalParticles(200);
		//设置每个粒子系统的位置。
        particleSystem->setPosition(ccp(i*15 +100,i*15+100));
		//将粒子系统放入到批次结点中。
        unsigned int randZ = rand() % 100; 
        m_pBatchNode->addChild(particleSystem, randZ, -1);

    }

   //让当前层每0.5秒触发一次函数removeSystem
chedule(schedule_selector(AddAndDeleteParticleSystems::removeSystem), 0.5f);
	//因为使用的是粒子系统的批次结点,所以指向粒子系统的指针置空。
    m_emitter = NULL;

}
//定时触发的函数。
void AddAndDeleteParticleSystems::removeSystem(float dt)
{
	//取得批次结点中的粒子系统的数量。
    int nChildrenCount = m_pBatchNode->getChildren()->count();
    if (nChildrenCount > 0) 
    {
        CCLOG("remove random system");
		//产生一个随机索引。
        unsigned int uRand = rand() % (nChildrenCount - 1);
        //将对应索引的粒子系统从批次结点中删除。
pBatchNode->removeChild((CCNode*)m_pBatchNode->getChildren()->objectAtIndex(uRand), true);
		//再由PLIST创建一个新的粒子系统。
        CCParticleSystemQuad *particleSystem = CCParticleSystemQuad::create("Particles/Spiral.plist");
        //add new
		//设置粒子系统运动方式为组方式,粒子总数量为200
        particleSystem->setPositionType(kCCPositionTypeGrouped);         
        particleSystem->setTotalParticles(200);
		//设置粒子系统处于一个随机的位置。
        particleSystem->setPosition(ccp(rand() % 300 ,rand() % 400));
		//打印日志,将新粒子系统放入到批次结点中。
        CCLOG("add a new system");
        unsigned int randZ = rand() % 100; 
        m_pBatchNode->addChild(particleSystem, randZ, -1);
    }
}
//更新处理
void AddAndDeleteParticleSystems::update(float dt)
{
	//取得用于显示粒子数量的文字标签。
    CCLabelAtlas *atlas = (CCLabelAtlas*) getChildByTag(kTagParticleCount);

    unsigned int count = 0; 
    //取得粒子系统的批次结点。
    CCNode* batchNode = getChildByTag(2);
	//定义临时指针变量pObj,用于遍历所有的粒子系统结点。
    CCObject* pObj = NULL;
    CCARRAY_FOREACH(batchNode->getChildren(), pObj)
    {
		//通过dynamic_cast将物体基类指针转化为粒子系统结点指针。
        CCParticleSystem* item = dynamic_cast<CCParticleSystem*>(pObj);
        if (item != NULL)
        {
			  //更新粒子的计数器值。
            count += item->getParticleCount();    
        }
}
//写字,显示数量。
    char str[100] = {0};
    sprintf(str, "%4d", count);
    atlas->setString(str);
}
//取得标题。
std::string AddAndDeleteParticleSystems::title()
{
    return "Add and remove Particle System";
}
//取得幅标题。
std::string AddAndDeleteParticleSystems::subtitle()
{
    return "v1.1 test: every 2 sec 1 system disappear, 1 appears";
}

(29)    ReorderParticleSystems

描述:

粒子系统的Z值重新排序。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void ReorderParticleSystems::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();

//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background ,true);
    m_background = NULL;
	//创建一个新的粒子系统的批次结点,渲染用的矩形信息容器的容量初始化为3000。
    m_pBatchNode = CCParticleBatchNode::create("Images/stars-grayscale.png" ,3000);
	//将粒子系统的批次结点放入到当前层结点下。
    addChild(m_pBatchNode, 1, 2);

	//使用for循环创建三个粒子系统。
    for (int i = 0; i<3; i++) {

		//创建第i个粒子系统。
        CCParticleSystemQuad* particleSystem = new CCParticleSystemQuad();
		//创建200个粒子。
        particleSystem->initWithTotalParticles(200);
		//取得批次结点的纹理设置为粒子系统的纹理。
        particleSystem->setTexture(m_pBatchNode->getTexture());

        //设置粒子系统一直调用Update
        particleSystem->setDuration(kCCParticleDurationInfinity);

        //设置粒子的运动方式为环型运动方式。
        particleSystem->setEmitterMode(kCCParticleModeRadius);

        //设置粒子系统的粒子起始角度为100。
        particleSystem->setStartRadius(100);
        particleSystem->setStartRadiusVar(0);
        //指定粒子系统的粒子结束角度与起始角度相同,即角度不变化。
particleSystem->setEndRadius(kCCParticleStartRadiusEqualToEndRadius);
        particleSystem->setEndRadiusVar(0);    // not used when start == end

        // 设置粒子系统的环型运动速率为每秒45度。
        particleSystem->setRotatePerSecond(45);
        particleSystem->setRotatePerSecondVar(0);


        // 设置粒子系统的角度为90度。
        particleSystem->setAngle(90);
        particleSystem->setAngleVar(0);

        //设置粒子系统的位置为零点
        particleSystem->setPosVar(CCPointZero);

        //设置粒子初始化的生命值为4。
        particleSystem->setLife(4);
        particleSystem->setLifeVar(0);

        //设置粒子运动过程中自身不旋转。
        particleSystem->setStartSpin(0);
        particleSystem->setStartSpinVar(0);
        particleSystem->setEndSpin(0);
        particleSystem->setEndSpinVar(0);

        //设置粒子的起始和结束的颜色,这里通过一个float数组来表示R,G,B的值,通过索引设置相应色彩分量值表现红,绿,蓝三种颜色。
        float color[3] = {0,0,0};
        color[i] = 1;
        ccColor4F startColor = {color[0], color[1], color[2], 1.0f};
        particleSystem->setStartColor(startColor);

        ccColor4F startColorVar = {0, 0, 0, 0};
        particleSystem->setStartColorVar(startColorVar);

        ccColor4F endColor = startColor;
        particleSystem->setEndColor(endColor);

        ccColor4F endColorVar = startColorVar;
        particleSystem->setEndColorVar(endColorVar);

        // 设置粒子的起始大小。
        particleSystem->setStartSize(32);
        particleSystem->setStartSizeVar(0);
        particleSystem->setEndSize(kCCParticleStartSizeEqualToEndSize);

        //设置粒子系统发射粒子的速率。
        particleSystem->setEmissionRate(particleSystem->getTotalParticles()/particleSystem->getLife());

        // 设置粒子系统的位置,

        particleSystem->setPosition(ccp(i*10+120 ,200));

		//将粒子系统放入到批次结点下,设置粒子系统的运动方式为自由运动方式。
        m_pBatchNode->addChild(particleSystem);
        particleSystem->setPositionType(kCCPositionTypeFree);
		//使用批次结点进行渲染,粒子系统已经放入到批次结点之下,故不再占用粒子系统,所以可以在这里对其引用计数器减一操作。
        particleSystem->release();

        //[pBNode addChild:particleSystem z:10 tag:0);

    }
	//每2秒调用一下reorderSystem函数。
    schedule(schedule_selector(ReorderParticleSystems::reorderSystem), 2.0f);
    m_emitter = NULL;

}
//定时触发的函数。
void ReorderParticleSystems::reorderSystem(float time)
{
	//通过索引取得相应的批次系统结点,进行重新的Z值排序。
    CCParticleSystem* system = (CCParticleSystem*)m_pBatchNode->getChildren()->objectAtIndex(1);
    m_pBatchNode->reorderChild(system, system->getZOrder() - 1);     
}

//更新函数。
void ReorderParticleSystems::update(float dt)
{
	//取得用于显示粒子数量的文字标签。
    CCLabelAtlas *atlas = (CCLabelAtlas*) getChildByTag(kTagParticleCount);
	//创建临时变量用于统计粒子数量。
    unsigned int count = 0; 
    //取得批次结点
    CCNode* batchNode = getChildByTag(2);
	//定义临时CCObject指针变量用于遍历所有的粒子系统。
    CCObject* pObj = NULL;
    CCARRAY_FOREACH(batchNode->getChildren(), pObj)
    {
		//通过dynamic_cast将物体基类指针转化为粒子系统结点指针。
        CCParticleSystem* item = dynamic_cast<CCParticleSystem*>(pObj);
        if (item != NULL)
        {
			 //更新粒子数量的计数器。
            count += item->getParticleCount();    
        }
    }
	//写字。
    char str[100] = {0};
    sprintf(str, "%4d", count);
    atlas->setString(str);
}
//返回标题。
std::string ReorderParticleSystems::title()
{
    return "reorder systems";
}
//返回幅标题。
std::string ReorderParticleSystems::subtitle()
{
    return "changes every 2 seconds";
}

(30)    PremultipliedAlphaTest

描述:

预乘aplha通道效果演示之一。什么是预乘?假设一个像素点,用RGBA 四个分量来表示,记做(R,G,B,A),那预乘后的像素就是(R*A,G*A,B*A, A),这里A的取值范围是[0,1]。所以,预乘就是每个颜色分量都与该像素的alpha分量预先相乘。可以发现,对于一个没有透明度,或者说透明度为1 的像素来说,预乘不预乘结果都是一样的。为什么要用预乘方式来表示像素?主要是这样会使公式更简单。而且实际上,在实际运算过程中,使用预乘像素进行运算在某些情况下效率会更高。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//返回标题。
std::string PremultipliedAlphaTest::title()
{
    return "premultiplied alpha";
}
//返回副标题。
std::string PremultipliedAlphaTest::subtitle()
{
    return "no black halo, particles should fade out";
}
//当前层加载时调用的处理。
void PremultipliedAlphaTest::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景为蓝色。
    this->setColor(ccBLUE);
	//删除原来的背景精灵并置空。
    this->removeChild(m_background, true);
    m_background = NULL;
	//由PLIST文件创建一个粒子系统。
    m_emitter = CCParticleSystemQuad::create("Particles/BoilingFoam.plist");
	//占用它,所以对其引用计数器加一。
    m_emitter->retain();
    //设置ALPHA混合方案为方案。
    ccBlendFunc tBlendFunc = { GL_ONE, GL_ONE_MINUS_SRC_ALPHA };
    m_emitter->setBlendFunc(tBlendFunc);
	//提示纹理必须有ALPHA通道
    CCAssert(m_emitter->getOpacityModifyRGB(), "Particle texture does not have premultiplied alpha, test is useless");

    // Toggle next line to see old behavior
    //	this->emitter.opacityModifyRGB = NO;
	//设置粒子系统的粒子起始和结束的颜色。
    m_emitter->setStartColor(ccc4f(1, 1, 1, 1));
    m_emitter->setEndColor(ccc4f(1, 1, 1, 0));
    m_emitter->setStartColorVar(ccc4f(0, 0, 0, 0));
    m_emitter->setEndColorVar(ccc4f(0, 0, 0, 0));
	//将粒子系统加入到当前结点下。
    this->addChild(m_emitter, 10);
}

(31)    PremultipliedAlphaTest2:

描述:

预乘aplha通道效果演示之二。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void PremultipliedAlphaTest2::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景为黑色。
    this->setColor(ccBLACK);
	//删除原来的背景精灵并置空。
    this->removeChild(m_background, true);
    m_background = NULL;
	//由PLIST文件创建一个粒子系统。具体的ALPHA混合方案都由PLIST文件中的数据进行读取设置。
m_emitter = CCParticleSystemQuad::create("Particles/TestPremultipliedAlpha.plist");
//占用粒子系统,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统放入到当前结点下。
    this->addChild(m_emitter ,10);
}
//取得标题。
std::string PremultipliedAlphaTest2::title()
{
    return "premultiplied alpha 2";
}
//取得副标题。
std::string PremultipliedAlphaTest2::subtitle()
{
    return "Arrows should be faded";
}

 

         我去~,累死哥了。不过经过这么痛苦的注释之后,我相信大家会跟我一样越来越清楚Cocos2d-x里的粒子系统是什么原理以及怎样使用的.我们可以看到,PLIST作为Cocos2d-x粒子系统数据的的存储文件,它可以很方便的产生出粒子系统。在实际的游戏开发过程中,我们一般都会为粒子系统开发一个专用的编辑器,它应该具有Cocos2d-x粒子系统中的所用属性并能编辑后保存为PLIST文件以方便游戏使用。比如这样:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

      粒子系统深入分析三部曲结束了,恰逢新的一年到来之际,我衷心的祝愿各位同学能够2013身体健康,事业有成。下课!

分类: cocos2d, cocos2d-x 标签:

Cocos2d-x 游戏中子弹的设计(一)

2013年2月4日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!

原文地址: http://www.cocos2dev.com/?p=379


FPS游戏中用的最多的就是子弹了,这几天有不少人问我游戏中如何设计子弹?大量子弹显示的时候,游戏会不会卡?


今天有点时间,我针对这两个问题,简单的写了一个子弹的发射。


效果图:

Cocos2d-x 游戏中子弹的设计(一)


首先我们来理下如何设计的思路。

一、首先一个简单的关系:子弹肯定是由武器发射的,武器是由人物操作的。


二、也就是说子弹Bullet类只负责创建一个新子弹、子弹的移动和子弹的消失、碰撞。

Bullet类提供给外界接口:增加一个新子弹(以及子弹的速度)。


三、武器weapon类给负责调用Bullet类创建一个新子弹。同时,weapon类也要负责计算发射子弹的速度,以及增加新子弹的时间间隔。

weapon类提供外界借口:开枪(发射子弹)。


四、人物类调用weap类的开枪接口。


OK,简单的整理了下(其实也挺简单的,是吧)。下面开始重要的实现,子弹的实现:


子弹bullet类的职能很单一,只负责创建一个新子弹、子弹的移动和子弹的消失、碰撞。


直接上代码吧:


Bullet.h

//
//  Bullet.h
//  FireDemo
//
//  Created by LiuYanghui on 13-2-4.
//
//

#ifndef __FireDemo__Bullet__
#define __FireDemo__Bullet__

#include "cocos2d.h"
USING_NS_CC;

class Bullet : public CCLayer {
public:
    Bullet();
    ~Bullet();
    CREATE_FUNC(Bullet);
    virtual bool init();
    
    void addNewBullet();//增加一个新子弹
    
private:
    CCSize winSize;
    CCArray* m_allBulletArray;//保存所有已存在的子弹对象
    
    CCSpriteBatchNode* m_bulletSprite;//使用BatchNode构建所有的子弹,减少gl绘画次数
    
    void initData();
    void moveBullet(float dt);//移动子弹
    
};


#endif /* defined(__FireDemo__Bullet__) */

Bullet.cpp


//
//  Bullet.cpp
//  FireDemo
//
//  Created by LiuYanghui on 13-2-4.
//
//

#include "Bullet.h"

#define MOVE_SPEED      100  //子弹的速度
#define REMOVE_POSY     -768 //子弹消失的位置,根据自己项目定

Bullet::Bullet(){
    
}

Bullet::~Bullet(){
    m_allBulletArray->release();
}

bool Bullet::init(){
    if (!CCLayer::init()) {
        return false;
    }
    initData();
    return true;
}

void Bullet::initData(){
    winSize = CCDirector::sharedDirector()->getWinSize();
    
    //创建对象数组,保存已存在的子弹
    m_allBulletArray = CCArray::create();
    m_allBulletArray->retain();
    
    //子弹贴图
    m_bulletSprite = CCSpriteBatchNode::create("bullet.png");
    m_bulletSprite->setPosition(CCPointZero);
    addChild(m_bulletSprite);
    
    //更新子弹速度
    schedule(schedule_selector(Bullet::moveBullet));
}

//增加一个新子弹
void Bullet::addNewBullet(){
    //构建一个子弹,添加到BatchNode中。
    CCSprite* newBullet = CCSprite::createWithTexture(m_bulletSprite->getTexture());
    newBullet->setPosition(CCPointZero);
    m_bulletSprite->addChild(newBullet);
    
    //添加子弹到已存在子弹数组
    m_allBulletArray->addObject(newBullet);
}

//移动子弹
void Bullet::moveBullet(float dt){
    //遍历更新每个子弹的位置
    for (int i = 0; i < m_allBulletArray->count(); ++i) {
        //获取一个子弹对象
        CCSprite* bullet = (CCSprite*)m_allBulletArray->objectAtIndex(i);
        
        //更新位置
        bullet->setPositionY(bullet->getPositionY()-MOVE_SPEED*dt);
        
        //如果已到达消失位置,就移除该子弹
        if (bullet->getPositionY() <= REMOVE_POSY) {
            m_allBulletArray->removeObjectAtIndex(i);
            m_bulletSprite->removeChild(bullet, true);
        }
    }
}

子弹生成了,现在写个简单的调用场景吧。


GameScene.h


//
//  GameScene.h
//  FireDemo
//
//  Created by LiuYanghui on 13-2-4.
//
//

#ifndef __FireDemo__GameScene__
#define __FireDemo__GameScene__

#include "cocos2d.h"
USING_NS_CC;

class Bullet;
class GameScene : public CCLayer {
public:
    GameScene();
    ~GameScene();
    CREATE_FUNC(GameScene);
    static CCScene* scene();
    virtual bool init();
    virtual void registerWithTouchDispatcher(void);
    virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
    virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
    virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
    virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);
    
private:
    CCSize winSize;
    Bullet* m_bullet;
    void initData();
    
};

#endif /* defined(__FireDemo__GameScene__) */


GameScene.cpp


//
//  GameScene.cpp
//  FireDemo
//
//  Created by LiuYanghui on 13-2-4.
//
//

#include "GameScene.h"
#include "Bullet.h"

GameScene::GameScene(){
    
}

GameScene::~GameScene(){
    
}

CCScene* GameScene::scene(){
    CCScene* scene = CCScene::create();
    GameScene* layer = GameScene::create();
    scene->addChild(layer);
    return scene;
}

bool GameScene::init(){
    if (!CCLayer::init()) {
        return false;
    }
    initData();
    return true;
}

void GameScene::initData(){
    winSize = CCDirector::sharedDirector()->getWinSize();
    
    CCSprite* mainBg = CCSprite::create("bg.jpg");
    mainBg->setPosition(ccp(winSize.width*.5,winSize.height*.5));
    addChild(mainBg);
    
    CCSprite* tank = CCSprite::create("tank.png");
    tank->setPosition(ccp(winSize.width*.5,winSize.height*.8));
    addChild(tank);
    
    //创建子弹对象
    m_bullet = Bullet::create();
    m_bullet->setPosition(winSize.width*.5,winSize.height*.8);
    addChild(m_bullet);
    
    setTouchEnabled(true);
}



void GameScene::registerWithTouchDispatcher(void){
    CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
}

bool GameScene::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
    //每次点击添加一个新子弹
    m_bullet->addNewBullet();
    return true;
}

void GameScene::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
{

}

void GameScene::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
{

}

void GameScene::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent)
{

}

好了,一个简单的点击屏幕发射子弹就出来了。这里只是提供了一个思路,具体的开发就自己去做了。















分类: 未分类 标签:

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

2013年2月2日 没有评论

[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中的两个有关粒子系统的类:

(1)      CCParticleSystem :粒子系统的基类,提供对粒子的创建和更新管理。

(2)      CCParticleBatchNode:粒子系统的批次结点,用于将使用相同纹理的粒子系统进行同批次渲染优化处理。

 

在学习CCParticleSystem时,我们留下了一些疑问,什么时候调用setBatchNode?以及做为基类,CCParticleSystem提供了两个供子类重载的纯虚函数postStep()和updateQuadWithParticle,它们的具体用法是什么?我们仍然有一些迷茫。

 

我们今天来了解一下CCParticleSystemQuad,这个类是CCParticleSystem的子类。它将解开我们的这些疑惑。

        

            打开CCParticleSystemQuad.h:

class CC_DLL CCParticleSystemQuad : public CCParticleSystem
{
protected:
	   //如果当前粒子系统未使用批次结点,则需要为粒子系统创建单独的顶点缓冲及索引缓冲以及OPENGL进行渲染的一些相关物件。
    ccV3F_C4B_T2F_Quad    *m_pQuads;    // OPENGL渲染图形所用的四边形顶点缓冲。
    GLushort            *m_pIndices;    // OPENGL渲染图形所用的索引缓冲。
//这里有宏判断当前OPENGL版本是否支持使用VAO处理顶点缓冲(VAO是什么?这个问题问的好,VAO是OPENGL3.X以上引入的新特性,VBO是Vertex Buffer Object, VAO是Vertex Array Object。 VAO是OpenGL 3.0以后才引入的新东西,但是在2.0版本中做为扩展接口。VBO其实就是显卡中的显存,为了提高渲染速度,可以将要绘制的顶点数据缓存在显存中,这样就不需要将要绘制的顶点数据重复从CPU发送到GPU, 浪费带宽资源。而VAO则是一个容器,可以包括多个VBO, 它类似于以前的call list, 由于它进一步将VBO容于其中,所以绘制效率将在VBO的基础上更进一步。)
#if CC_TEXTURE_ATLAS_USE_VAO
    GLuint                m_uVAOname;	   		//VAO的句柄。
#endif
	   GLuint                m_pBuffersVBO[2];  //VBO的两个句柄,第一个句柄对应顶点缓冲,第二个句柄对应索引缓冲。
public:
	   //构造函数。
    CCParticleSystemQuad();
	   //析构函数。
    virtual ~CCParticleSystemQuad();

    //创建函数,参为为PLIST,内部调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCParticleSystemQuad * particleWithFile(const char *plistFile);

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

    //初始化索引缓冲。
    void setupIndices();

    //初始化方理坐标。
    void initTexCoordsWithRect(const CCRect& rect);

    //设置显示一个精灵帧。
    void setDisplayFrame(CCSpriteFrame *spriteFrame);

    //设置使用纹理对象上指定的矩形图像区域做为粒子系统的贴图。
    void setTextureWithRect(CCTexture2D *texture, const CCRect& rect);
    // 重载基类粒子系统的相应函数。
	   //初始化粒子数量。
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
	   //设置所用的纹理对象指针
    virtual void setTexture(CCTexture2D* texture);
	   //上一篇留下的疑问,虽然明显是更新粒子顶点缓冲中的位置数据。但上一篇为什么没有实现?
    virtual void updateQuadWithParticle(tCCParticle* particle, const CCPoint& newPosition);
	   //上一篇留下的疑问,不知道是做什么,咱们到CPP中看吧。
    virtual void postStep();
	   //渲染处理。
    virtual void draw();
	   //设置批次结点。
    virtual void setBatchNode(CCParticleBatchNode* batchNode);
	   //设置总的粒子数量。
    virtual void setTotalParticles(unsigned int tp);
    //监听响应当前结点的EVNET_COME_TO_FOREGROUND事件的回调函数。
    void listenBackToForeground(CCObject *obj);

    //创建一个当前实例结点,内部调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCParticleSystemQuad * node();
	   //上面的create实现。
    static CCParticleSystemQuad * create();
private:
		//如果使用VAO
#if CC_TEXTURE_ATLAS_USE_VAO
	   //初始化VAO和VBO
    void setupVBOandVAO();
#else
    //初始化VBO
    void setupVBO();
#endif
	   //申请内存。
    bool allocMemory();
};

对应的实现:

//重载粒子系统基类的初始化函数,创建相应数量的粒子。
bool CCParticleSystemQuad::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
    if( CCParticleSystem::initWithTotalParticles(numberOfParticles) ) 
    {
        // 创建顶点和索引缓冲,如果失败释放并返回。
        if( ! this->allocMemory() ) {
            this->release();
            return false;
        }
		//填充索引缓冲。
        setupIndices();
		//如果当前OPENGL版本支持VAO,就创建VAO,如果不支持,只创建VBO。
#if CC_TEXTURE_ATLAS_USE_VAO
        setupVBOandVAO();
#else
        setupVBO();
#endif
//设置使用顶点格式为“位置+纹理+顶点色”的顶点格式组合。
setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));

//告诉通知中心,注册函数listenBackToForeground用来响应当前结点的EVNET_COME_TO_FOREGROUND事件。这个事件的意义是程序将由后面返回到前台。这个事件响应时可做资源的重新载入。

CCNotificationCenter::sharedNotificationCenter()->addObserver(this,
                                                                      callfuncO_selector(CCParticleSystemQuad::listenBackToForeground),
                                                                      EVNET_COME_TO_FOREGROUND,
                                                                      NULL);
        
        return true;
    }
    return false;
}
//构造函数。
CCParticleSystemQuad::CCParticleSystemQuad()
:m_pQuads(NULL)
,m_pIndices(NULL)
#if CC_TEXTURE_ATLAS_USE_VAO
,m_uVAOname(0)
#endif
{
    memset(m_pBuffersVBO, 0, sizeof(m_pBuffersVBO));
}
//析构函数。
CCParticleSystemQuad::~CCParticleSystemQuad()
{
	//对所创建的顶点缓冲,索引缓冲,以及VBO,VA0进行释放。
    if (NULL == m_pBatchNode)
    {
        CC_SAFE_FREE(m_pQuads);
        CC_SAFE_FREE(m_pIndices);
        glDeleteBuffers(2, &m_pBuffersVBO[0]);
#if CC_TEXTURE_ATLAS_USE_VAO
        glDeleteVertexArrays(1, &m_uVAOname);
#endif
    }
    
//注销对通知管理器注册的相应事件的响应处理函数。
CCNotificationCenter::sharedNotificationCenter()->removeObserver(this, EVNET_COME_TO_FOREGROUND);
}

// 静态创建函数。由PLIST文件创建相应的当前实例对象,内部调用create实现。
CCParticleSystemQuad * CCParticleSystemQuad::particleWithFile(const char *plistFile)
{
    return CCParticleSystemQuad::create(plistFile);
}
//上面的create实现。
CCParticleSystemQuad * CCParticleSystemQuad::create(const char *plistFile)
{
	//创建一个CCParticleSystemQuad实例对象,进行初始化后交由内存管理器进行引用计数器的管理。
    CCParticleSystemQuad *pRet = new CCParticleSystemQuad();
    if (pRet && pRet->initWithFile(plistFile))
    {
        pRet->autorelease();
        return pRet;
    }
	//如果失败,删除并置空,返回NULL。
    CC_SAFE_DELETE(pRet);
    return pRet;
}

// 初始化纹理坐标。
void CCParticleSystemQuad::initTexCoordsWithRect(const CCRect& pointRect)
{
    // 创建出相应的矩形。
    CCRect rect = CCRectMake(
        pointRect.origin.x * CC_CONTENT_SCALE_FACTOR(),
        pointRect.origin.y * CC_CONTENT_SCALE_FACTOR(),
        pointRect.size.width * CC_CONTENT_SCALE_FACTOR(),
        pointRect.size.height * CC_CONTENT_SCALE_FACTOR());
	//默认使用的是批次结点,以批次结点的纹理对设置所用的图像区域矩形宽高。
    GLfloat wide = (GLfloat) pointRect.size.width;
    GLfloat high = (GLfloat) pointRect.size.height;
	//如果使用单纹理对象,取得纹理的宽高。
    if (m_pTexture)
    {
        wide = (GLfloat)m_pTexture->getPixelsWide();
        high = (GLfloat)m_pTexture->getPixelsHigh();
    }
	//此宏是为了解决精灵边缘黑线而做的纹理坐标的微调。
#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
    GLfloat left = (rect.origin.x*2+1) / (wide*2);
    GLfloat bottom = (rect.origin.y*2+1) / (high*2);
    GLfloat right = left + (rect.size.width*2-2) / (wide*2);
    GLfloat top = bottom + (rect.size.height*2-2) / (high*2);
#else
    GLfloat left = rect.origin.x / wide;
    GLfloat bottom = rect.origin.y / high;
    GLfloat right = left + rect.size.width / wide;
    GLfloat top = bottom + rect.size.height / high;
#endif 

    //将top与bottom交换一下,因为在Cococs2d-x中坐标系Y轴是向上为正,这里处理一下后面可以做为顶点位置数据。
    CC_SWAP( top, bottom, float);
	//根据是否使用批次结点来取得相应的矩形顶点缓冲数组。
    ccV3F_C4B_T2F_Quad *quads = NULL;
    unsigned int start = 0, end = 0;
    if (m_pBatchNode)
    {	//如果使用批次结点,这里取得批次结点中所有粒子所对应的矩形顶点数组。
        quads = m_pBatchNode->getTextureAtlas()->getQuads();
		//取得起始和结束的粒子所对应的矩形索引。
        start = m_uAtlasIndex;
        end = m_uAtlasIndex + m_uTotalParticles;
    }
    else
    {	//如果使用单纹理对象,则起始矩形索引就是0,结束矩形索引就是最大粒子数量。
        quads = m_pQuads;
        start = 0;
        end = m_uTotalParticles;
    }
	//遍历所有的矩形顶点。
    for(unsigned int i=start; i<end; i++) 
    {
        // 设置四个顶点的纹理坐标。
        quads[i].bl.texCoords.u = left;
        quads[i].bl.texCoords.v = bottom;
        quads[i].br.texCoords.u = right;
        quads[i].br.texCoords.v = bottom;
        quads[i].tl.texCoords.u = left;
        quads[i].tl.texCoords.v = top;
        quads[i].tr.texCoords.u = right;
        quads[i].tr.texCoords.v = top;
    }
}
//设置使用纹理对象上指定的矩形图像区域做为粒子系统的贴图。
void CCParticleSystemQuad::setTextureWithRect(CCTexture2D *texture, const CCRect& rect)
{
    // 如果当前尚无纹理对或者使用的纹理与参数指定的纹理不同。则设置使用参数指定的纹理。
    if( !m_pTexture || texture->getName() != m_pTexture->getName() )
    {
        CCParticleSystem::setTexture(texture);
    }
	//设置指定的矩形图像区域做为贴图计算粒子系统的纹理坐标.
    this->initTexCoordsWithRect(rect);
}
//设置使用的纹理对象。
void CCParticleSystemQuad::setTexture(CCTexture2D* texture)
{
	//取得纹理的大小。
    const CCSize& s = texture->getContentSize();
	//设置使用纹理对象上指定的矩形图像区域做为粒子系统的贴图。    
	this->setTextureWithRect(texture, CCRectMake(0, 0, s.width, s.height));
}
//设置使用精灵帧中的纹理。
void CCParticleSystemQuad::setDisplayFrame(CCSpriteFrame *spriteFrame)
{
	//有效性判断。
    CCAssert(spriteFrame->getOffsetInPixels().equals(CCPointZero), 
             "QuadParticle only supports SpriteFrames with no offsets");

    // 如果当前尚无纹理对或者使用的纹理与参数指定的纹理不同。则设置使用参数指定的纹理。
    if ( !m_pTexture || spriteFrame->getTexture()->getName() != m_pTexture->getName())
    {
		//设置使用精灵帧中的纹理.
        this->setTexture(spriteFrame->getTexture());
    }
}
//填充索引缓冲。
void CCParticleSystemQuad::setupIndices()
{
	//遍历粒子数目计算索引缓冲值。
    for(unsigned int i = 0; i < m_uTotalParticles; ++i)
    {
        const unsigned int i6 = i*6;
        const unsigned int i4 = i*4;
        m_pIndices[i6+0] = (GLushort) i4+0;
        m_pIndices[i6+1] = (GLushort) i4+1;
        m_pIndices[i6+2] = (GLushort) i4+2;

        m_pIndices[i6+5] = (GLushort) i4+1;
        m_pIndices[i6+4] = (GLushort) i4+2;
        m_pIndices[i6+3] = (GLushort) i4+3;
    }
}
//更新指定粒子的顶点缓冲中的位置数据。
void CCParticleSystemQuad::updateQuadWithParticle(tCCParticle* particle, const CCPoint& newPosition)
{
	//定义临时指针变量用于取得相应的粒子所对应的矩形顶点缓冲。
    ccV3F_C4B_T2F_Quad *quad;
	//如果使用了批次结点。
    if (m_pBatchNode)
    {
		//取得批次结点中对应的矩形顶点缓冲数组指针。
        ccV3F_C4B_T2F_Quad *batchQuads = m_pBatchNode->getTextureAtlas()->getQuads();
		//通过索引取得相应的矩形顶点缓冲。
        quad = &(batchQuads[m_uAtlasIndex+particle->atlasIndex]);
    }
    else
    {
		//如果没有使用批次结点,直接取得相应的粒子的矩形顶点缓冲。
        quad = &(m_pQuads[m_uParticleIdx]);
    }
	//根据是否由ALPHA值来设定RGB取得相应的颜色值。
    ccColor4B color = (m_bOpacityModifyRGB)
        ? ccc4( particle->color.r*particle->color.a*255, particle->color.g*particle->color.a*255, particle->color.b*particle->color.a*255, particle->color.a*255)
        : ccc4( particle->color.r*255, particle->color.g*255, particle->color.b*255, particle->color.a*255);
	//填真顶点缓冲中的颜色信息。
    quad->bl.colors = color;
    quad->br.colors = color;
    quad->tl.colors = color;
    quad->tr.colors = color;

    // 设置顶点的位置信息
    GLfloat size_2 = particle->size/2;
	//判断是否进行旋转。
    if (particle->rotation) 
    {
		//定义临时变量来存放以size_2为半径的矩形外圆上的四个顶点。
        GLfloat x1 = -size_2;
        GLfloat y1 = -size_2;

        GLfloat x2 = size_2;
        GLfloat y2 = size_2;
		//定义临时变量来存放圆心。
        GLfloat x = newPosition.x;
        GLfloat y = newPosition.y;
		//求得旋转角度。
        GLfloat r = (GLfloat)-CC_DEGREES_TO_RADIANS(particle->rotation);
		//通过sin,cos来计算旋转后的矩形外圆上的四个角的顶点位置。
        GLfloat cr = cosf(r);
        GLfloat sr = sinf(r);
        GLfloat ax = x1 * cr - y1 * sr + x;
        GLfloat ay = x1 * sr + y1 * cr + y;
        GLfloat bx = x2 * cr - y1 * sr + x;
        GLfloat by = x2 * sr + y1 * cr + y;
        GLfloat cx = x2 * cr - y2 * sr + x;
        GLfloat cy = x2 * sr + y2 * cr + y;
        GLfloat dx = x1 * cr - y2 * sr + x;
        GLfloat dy = x1 * sr + y2 * cr + y;

        // 填充计算旋转后的顶点位置信息。
        quad->bl.vertices.x = ax;
        quad->bl.vertices.y = ay;
        quad->br.vertices.x = bx;
        quad->br.vertices.y = by;
        quad->tl.vertices.x = dx;
        quad->tl.vertices.y = dy;
        quad->tr.vertices.x = cx;
        quad->tr.vertices.y = cy;
    } 
    else 
    {
        //如果不旋转,直接填充位置信息。
        quad->bl.vertices.x = newPosition.x - size_2;
        quad->bl.vertices.y = newPosition.y - size_2;
        quad->br.vertices.x = newPosition.x + size_2;
        quad->br.vertices.y = newPosition.y - size_2;
        quad->tl.vertices.x = newPosition.x - size_2;
        quad->tl.vertices.y = newPosition.y + size_2;
        quad->tr.vertices.x = newPosition.x + size_2;
        quad->tr.vertices.y = newPosition.y + size_2;                
    }
}
//我们一直想知道在CCParticleSystem中每次update时,如果粒子批次结点为空时为什么要调用postStep?它倒底是干什么的,看完下面的代码,就很清楚了。
void CCParticleSystemQuad::postStep()
{
	//绑定顶点缓冲区对象。
    glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0] );
	//用m_pQuads中数据更新绑定的缓冲区数据。
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(m_pQuads[0])*m_uParticleCount, m_pQuads);
    //取消绑定缓冲区对象。
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    CHECK_GL_ERROR_DEBUG();
	//清楚了,原来这个函数是针对不使用批次结点时的VBO顶点缓冲的更新。
}

// 绘制粒子系统。
void CCParticleSystemQuad::draw()
{
    //如果当前在粒子的批次结点有值,则draw()不应该被调用!为什么呢?因为有批次结点的话,渲染交给批次结点的draw()函数而不是当前粒子系统的draw()函数来处理。
    CCAssert(!m_pBatchNode,"draw should not be called when added to a particleBatchNode");
	//使用相应的Shader
    CC_NODE_DRAW_SETUP();
	//绑定所用的纹理对象。
    ccGLBindTexture2D( m_pTexture->getName() );
	//设定所用的ALPHA混合方案。
    ccGLBlendFunc( m_tBlendFunc.src, m_tBlendFunc.dst );
	//判断当前粒子索引是否为粒子总数,也就是判断是否已经update完成未出错。
    CCAssert( m_uParticleIdx == m_uParticleCount, "Abnormal error in particle quad");
	//如果使用VAO。
#if CC_TEXTURE_ATLAS_USE_VAO
    //使用VAO绑定的顶点数组
    glBindVertexArray( m_uVAOname );
	//绑定索引数组
#if CC_REBIND_INDICES_BUFFER
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);
#endif
	//渲染调用
    glDrawElements(GL_TRIANGLES, (GLsizei) m_uParticleIdx*6, GL_UNSIGNED_SHORT, 0);
	//渲染完取消绑定索引数组
#if CC_REBIND_INDICES_BUFFER
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#endif
	//取消绑定顶点数组
    glBindVertexArray( 0 );

#else
    //
    // Using VBO without VAO
    //
	//
    #define kQuadSize sizeof(m_pQuads[0].bl)
	//设置使用相应的顶点格式为:位置+颜色+纹理坐标
    ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex );
	// 绑定顶点缓冲
    glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0]);
    // 设置顶点缓冲中位置数据的描述
    glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));
    //设置顶点缓冲中颜色数据的描述
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));
    //设置顶点缓冲中纹理坐标数据的描述
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));
    //绑定索引缓冲
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);
	//渲染调用
    glDrawElements(GL_TRIANGLES, (GLsizei) m_uParticleIdx*6, GL_UNSIGNED_SHORT, 0);
	//取消绑定
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

#endif
	//渲染调用计数器加一
    CC_INCREMENT_GL_DRAWS(1);
    CHECK_GL_ERROR_DEBUG();
}
//设置粒子数量
void CCParticleSystemQuad::setTotalParticles(unsigned int tp)
{
    // 如果要申请内存的粒子数量大于之前已经申请内存的粒子数量。
    if( tp > m_uAllocatedParticles )
    {
        // 计算要申请的内存大小。
		//粒子信息结构数组大小。 
        size_t particlesSize = tp * sizeof(tCCParticle);
		//顶点缓冲的大小
        size_t quadsSize = sizeof(m_pQuads[0]) * tp * 1;
		//索引缓冲的大小
        size_t indicesSize = sizeof(m_pIndices[0]) * tp * 6 * 1;
		//在m_pParticles指定的内存位置申请相应大小的内存用于存储粒子信息结构数组。
        tCCParticle* particlesNew = (tCCParticle*)realloc(m_pParticles, particlesSize);
		//在m_pQuads指定的内存位置申请相应大小的内存用于填充顶点缓冲。
        ccV3F_C4B_T2F_Quad* quadsNew = (ccV3F_C4B_T2F_Quad*)realloc(m_pQuads, quadsSize);
		//在m_pIndices指定的内存位置申请相应大小的内存用于填充索引缓冲。
        GLushort* indicesNew = (GLushort*)realloc(m_pIndices, indicesSize);
		//如果申请都成功。
        if (particlesNew && quadsNew && indicesNew)
        {
            // 将内存地址传值给成员指针。
            m_pParticles = particlesNew;
            m_pQuads = quadsNew;
            m_pIndices = indicesNew;

            // 内存清零
            memset(m_pParticles, 0, particlesSize);
            memset(m_pQuads, 0, quadsSize);
            memset(m_pIndices, 0, indicesSize);
			//记录申请内存的粒子数量。
            m_uAllocatedParticles = tp;
        }
        else
        {
            // 如果失败,记录成功申请的内存地址打印出错LOG。
            if (particlesNew) m_pParticles = particlesNew;
            if (quadsNew) m_pQuads = quadsNew;
            if (indicesNew) m_pIndices = indicesNew;

            CCLOG("Particle system: out of memory");
            return;
        }
		//更新粒子数量。
        m_uTotalParticles = tp;

        // 根据是否使用批次结点来设定每个粒子对应的矩形顶点块的索引。
        if (m_pBatchNode)
        {
            for (unsigned int i = 0; i < m_uTotalParticles; i++)
            {
                m_pParticles[i].atlasIndex=i;
            }
        }
		//填充索引缓冲。
        setupIndices();
		//初始化VAO
#if CC_TEXTURE_ATLAS_USE_VAO
        setupVBOandVAO();
#else
		//初始化VBO
        setupVBO();
#endif
    }
    else
    {
		//如果要申请内存的粒子数量小于原来申请的粒子数量,直接修改一下最大粒子数量就OK。
        m_uTotalParticles = tp;
    }
}
//如果当前OPENGL版本支持VAO。
#if CC_TEXTURE_ATLAS_USE_VAO
//初始化VBO与VAO
void CCParticleSystemQuad::setupVBOandVAO()
{	//初始化1个顶点数组对象,产生VAO对象的句柄
    glGenVertexArrays(1, &m_uVAOname);
	//绑定VAO。
    glBindVertexArray(m_uVAOname);

#define kQuadSize sizeof(m_pQuads[0].bl)
	//创建2个VBO缓冲区对象,产生两个句柄填充到句柄数组中。
    glGenBuffers(2, &m_pBuffersVBO[0]);
	//绑定第一个VBO缓冲区对象。 
    glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0]);
	//将顶点数据拷贝到绑定的缓冲区。
    glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0]) * m_uTotalParticles, m_pQuads, GL_DYNAMIC_DRAW);

    //设置使用位置数据
    glEnableVertexAttribArray(kCCVertexAttrib_Position);
	//设置位置数据在顶点缓冲中的描述
    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));

    //设置使用颜色数据
    glEnableVertexAttribArray(kCCVertexAttrib_Color);
	//设置颜色数据在顶点缓冲中的描述
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));

    //设置使用纹理坐标数据
    glEnableVertexAttribArray(kCCVertexAttrib_TexCoords);
	//设置纹理坐标数据在顶点缓冲中的描述
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));

	//绑定VBO的第二个缓冲区
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);
	//将索引缓冲区数据拷贝到绑定的缓冲区
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uTotalParticles * 6, m_pIndices, GL_STATIC_DRAW);
	//取消绑定
    glBindVertexArray(0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    CHECK_GL_ERROR_DEBUG();
}
#else
//否则只使用VBO
void CCParticleSystemQuad::setupVBO()
{
	//创建2个VBO缓冲区对象,产生两个句柄填充到句柄数组中。
    glGenBuffers(2, &m_pBuffersVBO[0]);
	//绑定第一个VBO缓冲区对象并填充数据。
    glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0]) * m_uTotalParticles, m_pQuads, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
	//绑定第二个VBO缓冲区对象并填充数据。
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uTotalParticles * 6, m_pIndices, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	
    CHECK_GL_ERROR_DEBUG();
}

#endif
//响应当前结点的EVNET_COME_TO_FOREGROUND事件。这个事件的意义是程序将由后面返回到前台。这个事件响应时可做资源的重新载入。
void CCParticleSystemQuad::listenBackToForeground(CCObject *obj)
{
	//重新初始化VBO和VAO。
#if CC_TEXTURE_ATLAS_USE_VAO
        setupVBOandVAO();
#else
        setupVBO();
#endif
}
//创建顶点缓冲和索引缓冲区。
bool CCParticleSystemQuad::allocMemory()
{
	//如果已经申请则中断。
    CCAssert( ( !m_pQuads && !m_pIndices), "Memory already alloced");
    CCAssert( !m_pBatchNode, "Memory should not be alloced when not using batchNode");
	//释放顶点与索引缓冲
    CC_SAFE_FREE(m_pQuads);
    CC_SAFE_FREE(m_pIndices);
	//为顶点与索引缓冲申请内存。
    m_pQuads = (ccV3F_C4B_T2F_Quad*)malloc(m_uTotalParticles * sizeof(ccV3F_C4B_T2F_Quad));
    m_pIndices = (GLushort*)malloc(m_uTotalParticles * 6 * sizeof(GLushort));
    //如果出现失败,则提示LOG并释放置空返回。
    if( !m_pQuads || !m_pIndices) 
    {
        CCLOG("cocos2d: Particle system: not enough memory");
        CC_SAFE_FREE(m_pQuads);
        CC_SAFE_FREE(m_pIndices);

        return false;
    }
	//如果成功,置零操作返回true.
    memset(m_pQuads, 0, m_uTotalParticles * sizeof(ccV3F_C4B_T2F_Quad));
    memset(m_pIndices, 0, m_uTotalParticles * 6 * sizeof(GLushort));

    return true;
}
//设置CCParticleSystemQuad使用的粒子批次结点。 
void CCParticleSystemQuad::setBatchNode(CCParticleBatchNode * batchNode)
{
	//如果当前使用的粒子批次结点与参数不同,则进行更换处理。
    if( m_pBatchNode != batchNode ) 
    {
		//先记录一下当前使用的。
        CCParticleBatchNode* oldBatch = m_pBatchNode;
		//将后调用基类相应函数,设置当前使用的粒子批次结点。
        CCParticleSystem::setBatchNode(batchNode);

        // 如果参数值为空,代表不使用批次结点。则进行顶点缓冲的相关初始化。
        if( ! batchNode ) 
        {
			//创建顶点缓冲,填充索引,并根据是否可用VAO设置顶点缓冲,设置纹理。
            allocMemory();
            setupIndices();
            setTexture(oldBatch->getTexture());
#if CC_TEXTURE_ATLAS_USE_VAO
            setupVBOandVAO();
#else
            setupVBO();
#endif
        }
        // 如果批次结点有效,且更换前没有使用粒子批次结点。
        else if( !oldBatch )
        {
            // 取得相应粒子批次结点的矩形数组,取出相应索引位置的矩形数据,将相应的顶点缓冲数据拷到矩形数组中。
            ccV3F_C4B_T2F_Quad *batchQuads = m_pBatchNode->getTextureAtlas()->getQuads();
            ccV3F_C4B_T2F_Quad *quad = &(batchQuads[m_uAtlasIndex] );
            memcpy( quad, m_pQuads, m_uTotalParticles * sizeof(m_pQuads[0]) );
			//释放当前用的顶点缓冲
            CC_SAFE_FREE(m_pQuads);
            CC_SAFE_FREE(m_pIndices);
			//释放所用的VBO顶点对象与AVO名称
            glDeleteBuffers(2, &m_pBuffersVBO[0]);
#if CC_TEXTURE_ATLAS_USE_VAO
            glDeleteVertexArrays(1, &m_uVAOname);
#endif
        }
    }
}
//创建一个CCParticleSystemQuad,内部调用create实现。
CCParticleSystemQuad * CCParticleSystemQuad::node()
{
    return CCParticleSystemQuad::create();
}
//上面函数的create实现。
CCParticleSystemQuad * CCParticleSystemQuad::create() {
	//创建一个CCParticleSystemQuad的实例对象。
    CCParticleSystemQuad *pParticleSystemQuad = new CCParticleSystemQuad();
	//如果创建成功,进行初始化。成功后交由内存管理器进行引用计数器的管理。
    if (pParticleSystemQuad && pParticleSystemQuad->init())
    {
        pParticleSystemQuad->autorelease();
        return pParticleSystemQuad;
    }
	//如果失败,释放置空并返回NULL。
    CC_SAFE_DELETE(pParticleSystemQuad);
    return NULL;
}

         总结: 类CCParticleSystemQuad的源码进一步完善了粒子系统的功能,使我们可以在不需要批次结点时也能够实现粒子系统的OPENGL顶点和索引缓冲的创建和渲染,这么看来CCParticleSystem是一个不完整的粒子系统的类,它只提供了使用粒子批次结点的粒子系统的渲染方案。

 

        到这里,我们基本算领悟了Cocos2d-x中粒子系统的完整功能基类。后面我们将由此进入到多彩的粒子系统的演示中去!下课~

 

                   

分类: cocos2d, cocos2d-x 标签:

to drawRect or not to drawRect (when should one use drawRect/Core Graphics vs subviews/images and why?)

2013年2月2日 没有评论

To clarify the purpose of this question: I know HOW to create complicated views with both subviews and using drawRect. I’m trying to fully understand the when’s and why’s to use one over the other.

I also understand that it doesn’t make sense to optimize that much ahead of time, and do something the more difficult way before doing any profiling. Consider that I’m comfortable with both methods, and now really want a deeper understanding.

A lot of my confusion comes from learning how to make table view scroll performance really smooth and fast. Of course the original source of this method is from the author behind twitter for iPhone (formerly tweetie). Basically it says that to make table scrolling buttery smooth, the secret is to NOT use subviews, but instead do all the drawing in one custom uiview. Essentially it seems that using lots of subviews slows rendering down because they have lots of overhead, and are constantly re-composited over their parent views.

To be fair, this was written when the 3GS was pretty brand spankin new, and iDevices have gotten much faster since then. Still this method is regularly suggested on the interwebs and elsewhere for high performance tables. In fact it’s a suggested method in Apple’s Table Sample Code, has been suggested in several WWDC videos (Practical Drawing for iOS Developers), and many iOS programming books.

There are even awesome looking tools to design graphics and generate Core Graphics code for them.

So at first I’m lead to believe “there’s a reason why Core Graphics exists. It’s FAST!”

But as soon as I think I get the idea “Favor Core Graphics when possible”, I start seeing that drawRect is often responsible for poor responsiveness in an app, is extremely expensive memory wise, and really taxes the CPU. Basically, that I should “Avoid overriding drawRect” (WWDC 2012 iOS App Performance: Graphics and Animations)

So I guess, like everything, it’s complicated. Maybe you can help myself and others understand the When’s and Why’s for using drawRect?

I see a couple obvious situations to use Core Graphics:

  1. You have dynamic data (Apple’s Stock Chart example)
  2. You have a flexible UI element that can’t be executed with a simple resizable image
  3. You are creating a dynamic graphic, that once rendered is used in multiple places

I see situations to avoid Core Graphics:

  1. Properties of your view need to be animated separately
  2. You have a relatively small view hierarchy, so any perceived extra effort using CG isn’t worth the gain
  3. You want to update pieces of the view without redrawing the whole thing
  4. The layout of your subviews needs to update when the parent view size changes

So bestow your knowledge. In what situations do you reach for drawRect/Core Graphics (that could also be accomplished with subviews)? What factors lead you to that decision? How/Why is drawing in one custom view recommended for buttery smooth table cell scrolling, yet Apple advises drawRect against for performance reasons in general? What about simple background images (when do you create them with CG vs using a resizable png image)?

A deep understanding of this subject may not be needed to make worthwhile apps, but I don’t love choosing between techniques without being able to explain why. My brain gets mad at me.

Question Update

Thanks for the information everyone. Some clarifying questions here:

  1. If you are drawing something with core graphics, but can accomplish the same thing with UIImageViews and a pre-rendered png, should you always go that route?
  2. A similar question: Especially with badass tools like this, when should you consider drawing interface elements in core graphics? (Probably when the display of your element is variable. e.g. a button with 20 different color variations. Any other cases?)
  3. Given my understanding in my answer below, could the same performance gains for a table cell possibly be gained by effectively capturing a snapshot bitmap of your cell after your complex UIView render’s itself, and displaying that while scrolling and hiding your complex view? Obviously some pieces would have to be worked out. Just an interesting thought I had.

Stick to UIKit and subviews whenever you can. You can be more productive, and take advantage of all the OO mechanisms that should things easier to maintain. Use Core Graphics when you can’t get the performance you need out of UIKit, or you know trying to hack together drawing effects in UIKit would be more complicated.

The general workflow should be to build the tableviews with subviews. Use Instruments to measure the frame rate on the oldest hardware your app will support. If you can’t get 60fps, drop down to CoreGraphics. When you’ve done this for a while, you get a sense for when UIKit is probably a waste of time.

So, why is Core Graphics fast?

CoreGraphics isn’t really fast. If it’s being used all the time, you’re probably going slow. It’s a rich drawing API, which requires its work be done on the CPU, as opposed to a lot of UIKit work that is offloaded to the GPU. If you had to animate a ball moving across the screen, it would be a terrible idea to call setNeedsDisplay on a view 60 times per second. So, if you have sub-components of your view that need to be individually animated, each component should be a separate layer.

The other problem is that when you don’t do custom drawing with drawRect, UIKit can optimize stock views so drawRect is a no-op, or it can take shortcuts with compositing. When you override drawRect, UIKit has to take the slow path because it has no idea what you’re doing.

These two problems can be outweighed by benefits in the case of table view cells. After drawRect is called when a view first appears on screen, the contents are cached, and the scrolling is a simple translation performed by the GPU. Because you’re dealing with a single view, rather than a complex hierarchy, UIKit’s drawRect optimizations become less important. So the bottleneck becomes how much you can optimize your Core Graphics drawing.

Whenever you can, use UIKit. Do the simplest implementation that works. Profile. When there’s an incentive, optimize.

I am a game developer, and I was asking the same questions when my friend told me that my UIImageView based view hierarchy was going to slow down my game and make it terrible. I then proceeded to research everything I could find about whether to use UIViews, CoreGraphics, OpenGL or something 3rd party like Cocos2D. The consistent answer I got from friends, teachers, and Apple engineers at WWDC was that there won’t be much of a difference in the end because at some level they are all doing the same thing. Higher-level options like UIViews rely on the lower level options like CoreGraphics and OpenGL, just they are wrapped in code to make it easier for you to use.

Don’t use CoreGraphics if you are just going to end up re-writing the UIView. However, you can gain some speed from using CoreGraphics, as long as you do all your drawing in one view, but is it really worth it? The answer I have found is usually no. When I first started my game, I was working with the iPhone 3G. As my game grew in complexity, I began to see some lag, but with the newer devices it was completely unnoticeable. Now I have plenty of action going on, and the only lag seems to be a drop in 1-3 fps when playing in the most complex level on an iPhone 4.

Still I decided to use Instruments to find the functions that were taking up the most time. I found that the problems were not related to my use of UIViews. Instead, it was repeatedly calling CGRectMake for certain collision sensing calculations and loading image and audio files separately for certain classes that use the same images, rather than having them draw from one central storage class.

So in the end, you might be able to achieve a slight gain from using CoreGraphics, but usually it will not be worth it or may not have any effect at all. The only time I use CoreGraphics is when drawing geometric shapes rather than text and images.

I’m going to try and keep a summary of what I’m extrapolating from other’s answers here, and ask clarifying questions in an update to the original question. But I encourage others to keep answers coming and vote up those who have provided good information.

General Approach

It’s quite clear that the general approach, as Ben Sandofsky mentioned in his answer, should be “Whenever you can, use UIKit. Do the simplest implementation that works. Profile. When there’s an incentive, optimize.”

The Why

  1. There are two main possible bottlenecks in an iDevice, the CPU and GPU
  2. CPU is responsible for the initial drawing/rendering of a view
  3. GPU is responsible for a majority of animation (Core Animation), layer effects, compositing, etc.
  4. UIView has a lot of optimizations, caching, etc, built in for handling complex view hierarchies
  5. When overriding drawRect you miss out on a lot of the benefits UIView’s provide, and it’s generally slower than letting UIView handle the rendering.

Drawing cells contents in one flat UIView can greatly improve your FPS on scrolling tables.

Like I said above, CPU and GPU are two possible bottlenecks. Since they generally handle different things, you have to pay attention to which bottleneck you are running up against. In the case of scrolling tables, it’s not that Core Graphics is drawing faster, and that’s why it can greatly improve your FPS.

In fact, Core Graphics may very well be slower than a nested UIView hierarchy for the initial render. However, it seems the typical reason for choppy scrolling is you are bottlenecking the GPU, so you need to address that.

Why overriding drawRect (using core graphics) can help table scrolling:

From what I understand, the GPU is not responsible for the initial rendering of the views, but is instead handed textures, or bitmaps, sometimes with some layer properties, after they have been rendered. It is then responsible for compositing the bitmaps, rendering all those layer affects, and the majority of animation (Core Animation).

In the case of table view cells, the GPU can be bottlenecked with complex view hierarchies, because instead of animating one bitmap, it is animating the parent view, and doing subview layout calculations, rendering layer effects, and compositing all the subviews. So instead of animating one bitmap, it is responsible for the relationship of bunch of bitmaps, and how they interact, for the same pixel area.

So in summary, the reason drawing your cell in one view with core graphics can speed up your table scrolling is NOT because it’s drawing faster, but because it is reducing the load on the GPU, which is the bottleneck giving you trouble in that particular scenario.

The difference is that UIView and CALayer essentially deal in fixed images. These images are uploaded to the graphics card (if you know OpenGL, think of an image as a texture, and a UIView/CALayer as a polygon showing such a texture). Once an image is on the GPU, it can be drawn very quickly, and even several times, and (with a slight performance penalty) even with varying levels of alpha transparency on top of other images.

CoreGraphics/Quartz is an API for generating images. It takes a pixel buffer (again, think OpenGL texture) and changes individual pixels inside it. This all happens in RAM and on the CPU, and only once Quartz is done, does the image get “flushed” back to the GPU. This round-trip of getting an image from the GPU, changing it, then uploading the whole image (or at least a comparatively large chunk of it) back to the GPU is rather slow. Also, the actual drawing that Quartz does, while really fast for what you are doing, is way slower than what the GPU does.

That’s obvious, considering the GPU is mostly moving around unchanged pixels in big chunks. Quartz does random-access of pixels and shares the CPU with networking, audio etc. Also, if you have several elements that you draw using Quartz at the same time, you have to re-draw all of them when one changes, then upload the whole chunk, while if you change one image and then let UIViews or CALayers paste it onto your other images, you can get away with uploading much smaller amounts of data to the GPU.

When you don’t implement -drawRect:, most views can just be optimized away. They don’t contain any pixels, so can’t draw anything. Other views, like UIImageView, only draw a UIImage (which, again, is essentially a reference to a texture, which has probably already been loaded onto the GPU). So if you draw the same UIImage 5 times using a UIImageView, it is only uploaded to the GPU once, and then drawn to the display in 5 different locations, saving us time and CPU.

When you implement -drawRect:, this causes a new image to be created. You then draw into that on the CPU using Quartz. If you draw a UIImage in your drawRect, it likely downloads the image from the GPU, copies it into the image you’re drawing to, and once you’re done, uploads this second copy of the image back to the graphics card. So you’re using twice the GPU memory on the device.

So the fastest way to draw is usually to keep static content separated from changing content (in separate UIViews/UIView subclasses/CALayers). Load static content as a UIImage and draw it using a UIImageView and put content generated dynamically at runtime in a drawRect. If you have content that gets drawn repeatedly, but by itself doesn’t change (I.e. 3 icons that get shown in the same slot to indicate some status) use UIImageView as well.

One caveat: There is such a thing as having too many UIViews. Particularly transparent areas take a bigger toll on the GPU to draw, because they need to be mixed with other pixels behind them when displayed. This is why you can mark a UIView as “opaque”, to indicate to the GPU that it can just obliterate everything behind that image.

If you have content that is generated dynamically at runtime but stays the same for the duration of the application’s lifetime (e.g. a label containing the user name) it may actually make sense to just draw the whole thing once using Quartz, with the text, the button border etc., as part of the background. But that’s usually an optimization that’s not needed unless the Instruments app tells you differently.

分类: stackoverflow精选, unity3d 标签: