cocos2dx 实现图片椭圆旋转选择物品界面

好久没写博客了,这几天弄了一个椭圆旋转选择的界面,手生了好多东西都不记得了,这次用的引擎的cocos2.2.3 

界面效果如下:

cocos2dx 实现图片椭圆旋转选择物品界面_第1张图片

cocos2dx 实现图片椭圆旋转选择物品界面_第2张图片


图片有个遮罩效果 后面的图片会变暗 前面的图片会变亮, 然后前面的图片会把后面的图片遮挡住。

图片旋转是根据滑动事件旋转的,基本原理与UIPageView差不多,但是调试椭圆轨迹、落点以及偏移角度弄了很多天,下面直接上代码:(头文件)


#ifndef SELECT_PEOPLE_TOUCH_LAYER
#define SELECT_PEOPLE_TOUCH_LAYER
#include 
#include "cocos-ext.h" 
#include "WelcomeScene.h"
#define MAX_NUM 16 //最多支持16个精灵在一个椭圆上
USING_NS_CC;
USING_NS_CC_EXT;
using namespace cocos2d::ui;
class WelcomeScene;
class SelectPeopleTouchLayer : public CCLayer
{
public:
	static SelectPeopleTouchLayer* create(WelcomeScene* welcome_scene);
	bool init();
	void InitConfig(); //设置配置参数
	void InitEllipse(); //初始化椭圆
	void SetScene(WelcomeScene* welcome_scene);

	virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
	virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
	virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);

	virtual void onEnter();
	virtual void onExit();

	//移动
	virtual void handleMoveLogic(const CCPoint &touchPoint);
	virtual void handleReleaseLogic(const CCPoint &touchPoint);

	virtual bool scrollPages(float touchOffset);
	//页面切换
	void scrollToPage(int idx);
	void movePages(float panyi_angle,int m_changeNum = -1);

	void drawEffects(int who); // 绘制特效 parm:绘制对象的索引

	void update(float dt);

	float getWeight(float realAngle); //获取权重 parm:真实角度
protected:
	inline float tuoyuanXat(float angle )//返回X坐标
	{
		//参数方程
		return tuoyuan_a*cos(angle);
	}
	inline float tuoyuanYat(float angle )//返回Y坐标
	{
		return tuoyuan_b*sin(angle);
	}
	inline CCPoint gettuoyuanPointAt(float angle)//使用参数方程获取椭圆点
	{
		return ccp(tuoyuanXat(angle) + tuoyuan_center.x,tuoyuanYat(angle) + tuoyuan_center.y);
	}
	inline float getRealAngle(int pageIdx) //获取当前页面的偏移角度
	{
		float realAngle = ((int)(_nowAngle[pageIdx]*180/M_PI))%(360); //求余 且减90度
		if(realAngle < 0) realAngle = 360 - abs(realAngle); //负角度转正角度
		return realAngle; //真实偏移角度
	}
	inline float getAngleFromOffset(float offset)
	{
		if(offset != 0) return atan(offset/tuoyuan_b); //偏移角
		else return 0.0f;
	}
	inline float radianToAngle(float radian) //弧度转换到角度
	{
		return radian * 180 / M_PI;
	}
	inline float angleToRadian(float angle) //角度转换到弧度
	{
		return angle * M_PI / 180;
	}
private:
	//精灵变量
	CCArray* m_Childrens; //存储子节点 也就是精灵
	UIImageView* _page[MAX_NUM]; //精灵的实例存储
	float _nowAngle[MAX_NUM]; //当前各个精灵的角度 (非绝对角度,可能会大于360度)
	float now_weight[MAX_NUM]; //当前精灵权重 用于判断特效以及显示层级

	static WelcomeScene * p_welcome_scene; //顶层场景

	//下面是涉及到滚动end事件触发的回滚效果的变量
	bool _isAutoScrolling; //自动滚动 滑动的回滚效果
	float _autoScrollSpeed; //滚动速度(越大滚动的越慢)
	//变化的角度以及偏移量
	float m_changeAngle;
	float m_changeOffset;
	int m_changeNum; //偏移倍数
	int last_pageIdx; //滑动前的当前页
	float m_endAngle; //滑动结束后 当前页的角度

	float _touchStartLocation; //触摸开始时X的位置
	float _touchMoveStartLocation; //一次移动前的开始位置(用于计算偏移)

	int _curPageIdx; //当前页面 使用 _page[_curPageIdx] 可获取当前选中精灵

	//椭圆参数
	float tuoyuan_a;
	float tuoyuan_b;
	CCPoint tuoyuan_center;
};
#endif

#include "SelectPeopleTouchLayer.h"
#include "EllipseAction.h"
#include "cocos-ext.h" 
#include 

USING_NS_CC;
USING_NS_CC_EXT;
using namespace cocos2d::ui;
WelcomeScene * SelectPeopleTouchLayer::p_welcome_scene=NULL;

SelectPeopleTouchLayer* SelectPeopleTouchLayer::create(WelcomeScene* welcome_scene)
{
	SelectPeopleTouchLayer* layer = new SelectPeopleTouchLayer();
	if(layer)
	{
		layer->SetScene(welcome_scene);
		if(layer->init())
		{
			layer->autorelease();
			return layer;
		}
	}
	delete layer;
	layer = NULL;
	return NULL;
}

bool SelectPeopleTouchLayer::init()
{
	bool bRet = false;  
	do   
	{  
		CC_BREAK_IF(! CCLayer::init());  
		setTouchEnabled(true);
		scheduleUpdate();

		InitConfig();
		InitEllipse();

		bRet = true;  
	} while (0);  

	return bRet;  
}
//初始化参数
void SelectPeopleTouchLayer::InitConfig()
{
	CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
	CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
	//初始化参数
	_isAutoScrolling = false; //默认不执行update
	_curPageIdx = 0;
	//设置椭圆参数
	tuoyuan_a = 300;
	tuoyuan_b = 100;
	tuoyuan_center = ccp(origin.x + visibleSize.width/2,
		origin.y + visibleSize.height/2);
}
//初始化椭圆
void SelectPeopleTouchLayer::InitEllipse()
{
	const int length = m_Childrens->count();
	for (int i=0; iobjectAtIndex(i);
		ui->setPosition(gettuoyuanPointAt(angle));

		_nowAngle[i] = angle;
		_page[i] = ui;

		float Aangle = radianToAngle(angle);
		//设置当前页面
		if(Aangle >= 269 && Aangle <= 271) 
		{
			_curPageIdx = i; //页面
			last_pageIdx = _curPageIdx;
		}
		//绘制特效
		drawEffects(i);
	}
}
//设置场景
void SelectPeopleTouchLayer::SetScene(WelcomeScene* welcome_scene) { 
	p_welcome_scene = welcome_scene;
	//加载层
	UILayer * ui=static_cast(p_welcome_scene->getChildByTag(1));
	ui->addWidget(GUIReader::shareReader()->widgetFromJsonFile("NewUi_1.json"));
	//存储椭圆的精灵
	m_Childrens = ui->getWidgetByName("Panel_14")->getChildren();

}


bool SelectPeopleTouchLayer::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent){
	_touchMoveStartLocation = convertToNodeSpace(pTouch->getLocation()).x;
	_touchStartLocation = _touchMoveStartLocation;
	m_endAngle = 0.0f; //滑动前初始化当前页偏移角度
	CCLog("Touch began");
	return true;
}

void SelectPeopleTouchLayer::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
{
	handleMoveLogic(pTouch->getLocation());
}

void SelectPeopleTouchLayer::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent){
	CCLog("Touch end");
	handleReleaseLogic(pTouch->getLocation());
}

void SelectPeopleTouchLayer::handleReleaseLogic(const CCPoint &touchPoint)
{
		int pageCount = m_Childrens->count();
		float whith = 360.0f / (pageCount * 2); //偏移角度临界值 8个精灵的话是22.5°
		float angle = radianToAngle(m_endAngle);
		//判断是否需要切换页面
		int p = angle  / whith; //角度与22.5度做倍数比较
		if(p < 0) //这个判断是获取到页面的偏移量
		{
			if(p % 2 != 0) p = (p - 1) / 2;
			else p = p / 2;
		}
		else
		{
			if(p % 2 != 0) p = (p + 1) / 2;
			else p = p / 2;
		}
		_curPageIdx = (_curPageIdx - p) % pageCount; //获取到新页面
		if(_curPageIdx < 0) _curPageIdx += 7; //小于0 就回转
		scrollToPage(_curPageIdx);
}

void SelectPeopleTouchLayer::scrollToPage(int idx)
{
	_isAutoScrolling = true;
	//需要变化的多大的角度
	int length = m_Childrens->count();
	_autoScrollSpeed = 10;

	m_changeAngle = 270 - getRealAngle(_curPageIdx); //270 - 滑动页面当前角度(这是相对偏移的角度)
	m_changeAngle = angleToRadian(m_changeAngle); //角度转弧度
	m_changeOffset = tuoyuan_b * tan(m_changeAngle);
	
	m_changeOffset = m_changeOffset / _autoScrollSpeed;
	m_changeAngle = m_changeAngle / _autoScrollSpeed;
}

void SelectPeopleTouchLayer::handleMoveLogic(const CCPoint &touchPoint)
{
	float offset = 0.0;
	float moveX = convertToNodeSpace(touchPoint).x;
	offset = moveX - _touchMoveStartLocation;
	_touchMoveStartLocation = moveX;
	scrollPages(offset);
}

bool SelectPeopleTouchLayer::scrollPages(float touchOffset)
{
	movePages(getAngleFromOffset(touchOffset));
	return true;
}

void SelectPeopleTouchLayer::movePages(float panyi_angle,int m_changeNum)
{
	const int length = m_Childrens->count();
	for (int i=0; isetPosition(gettuoyuanPointAt(_nowAngle[i]));
		//特效
		drawEffects(i);
	}
}

void SelectPeopleTouchLayer::drawEffects(int who)
{
	float realAngle = getRealAngle(who); //真实偏移角度
	//计算权重
	float Weight = getWeight(realAngle);

	_page[who]->setColor(ccc3(255/Weight,255/Weight,255/Weight));
	_page[who]->setZOrder(10-abs(Weight));
}

float SelectPeopleTouchLayer::getWeight(float realAngle)
{
	//计算权重
	float Weight = realAngle - 90;//减90°
	Weight = 5 - abs(Weight / 45);      //除以45 取绝对值 得到中间的权值
	if(realAngle > 180) //这里是处理下部分的权值
	{
		Weight = abs(Weight-1);
		Weight++;
	}

	if(Weight < 1) Weight = 1;
	return Weight;
}

void SelectPeopleTouchLayer::onEnter()
{
	CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false);  
	CCLayer::onEnter();
	CCLog("enter");
}

void SelectPeopleTouchLayer::onExit()
{
	CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);  
	CCLayer::onExit();  
	CCLog("exit");
}

void SelectPeopleTouchLayer::update(float dt)
{
	if (_isAutoScrolling)
	{
		float realAngle = getRealAngle(_curPageIdx); //真实偏移角度
		if(realAngle <= 271.0f && realAngle >= 269.0f) 
		{
			last_pageIdx = _curPageIdx;
			CCLog("OK name: %s   now _curPageIdx = %d",_page[_curPageIdx]->getName() , _curPageIdx);
			_isAutoScrolling = false;
		}
		movePages(m_changeAngle,--m_changeNum);

	}
}



主要原理如下:

在刚开始触摸的时候,设置椭圆的初始角度,以及一些初始信息;


滑动的时候通过ccTouchMoved调用handleMoveLogic 这个函数主要是计算偏移量 并调用scrollPages函数,这个函数主要是将偏移量转换成偏移角度,传入movePages函数中,这个函数的后面一个参数是没有用的 忘记删除了,这个函数的作用是通过原来的角度加上偏移角度,获取最终精灵应该变换的角度,并且通过这个角度和椭圆的参数方程计算此时精灵应该占的位置,然后设置位置,最后调用特效函数drawEffects,这个函数中,我提出了一个权重的概念,权重是根据椭圆的角度进行计算的,最终结果是,越往后方,权重越高,最前方的权重是1。


触摸结束之后,调用handleReleaseLogic判断精灵是否翻页,以本文的8个精灵为例,只要当前页的偏移角度超过22.5或者-22.5,就进行翻页,但是,这有一个问题,就是超过45度,不会翻两页,于是我做出了一个算法,判定是22.5这个角度的多少倍(变量p),通过这这个倍数判断翻多少页。


判断页码之后,调用scrollToPage设定一些参数使精灵能自动归位,自动将离最下方点的精灵归位到最下方,设置完成后update中的语句会执行,这里面的语句的功能是将精灵按照设定的偏移量进行偏移,如果偏移量在271与269之间(存在误差),就停止执行这些语句。

你可能感兴趣的:(cocos2dx,算法,C++)