当前位置:网站首页>【Cocos2d-x】自定义环形菜单
【Cocos2d-x】自定义环形菜单
2022-06-23 04:21:00 【linchaolong】
思路
1.定义一个类继承CCMenu,并重写itemForTouch方法,该方法在CCMenu被触摸的时候调用,用于获取触摸的菜单条目。在itemForTouch方法中自定义触摸判定。

2.定义一个方法(这里是swirlItemsRadially),在该方法中调用this->getChildren()迭代菜单中每个条目,计算它们的位置,调用item的runAction方法增加动画效果(这里使用贝塞尔曲线实现动画效果【点击查看关于贝塞尔曲线】)。
代码
#ifndef __RADIALMENU_H__
#define __RADIALMENU_H__
#include "cocos2d.h"
USING_NS_CC;
/** 环形菜单控件,可以使菜单条目均匀地显示成一个圆形 **/
/**
工程git地址:https://coding.net/u/linchaolong/p/Cocos2d-x_RadialMenu/git
参考工程地址:https://github.com/sartak/CCRadialMenu
**/
class RadialMenu:public CCMenu
{
public:
static RadialMenu* create(CCArray* items, float radius);
static RadialMenu* create(CCArray* items, float radius, float swirlOutDuration);
/**
* items : CCMenuItem,菜单条目集合
* radius:环形菜单半径
* swirlOutDuration:动画时长
* adjustAnchors:是否自动调整锚点
**/
static RadialMenu* create(CCArray* items, float radius, float swirlOutDuration, bool adjustAnchors);
//菜单条目动画结束回调
void animEndedCallback(CCNode*, void*);
/** 直接排列菜单条目成环形 **/
void alignItemsRadially();
/** 排列菜单条目成环形(有动画效果) **/
void swirlItemsRadially(float duration);
/**
* duration:动画时长
* adjustAnchors:是否自动调整锚点
**/
void swirlItemsRadially(float duration, bool adjustAnchors);
protected:
//该方法在CCMenu的touchBegan和touchMove时候调用,用于查找触摸的菜单条目
CCMenuItem* itemForTouch(CCTouch * touch);
private:
RadialMenu* init(CCArray* items, float radius, float swirlOutDuration, bool adjustAnchors);
float radius_;
};
#endif
#include "RadialMenu.h"
RadialMenu* RadialMenu::create(CCArray* items, float radius){
RadialMenu *pRet = new RadialMenu();
if (pRet && pRet->init(items,radius,0,false))
{
pRet->autorelease();
return pRet;
}
else
{
delete pRet;
pRet = NULL;
return NULL;
}
}
RadialMenu* RadialMenu::create(CCArray* items, float radius, float swirlOutDuration){
RadialMenu *pRet = new RadialMenu();
if (pRet && pRet->init(items,radius,swirlOutDuration,false))
{
pRet->autorelease();
return pRet;
}
else
{
delete pRet;
pRet = NULL;
return NULL;
}
}
RadialMenu* RadialMenu::create(CCArray* items, float radius, float swirlOutDuration, bool adjustAnchors){
RadialMenu *pRet = new RadialMenu();
if (pRet && pRet->init(items,radius,swirlOutDuration,adjustAnchors))
{
pRet->autorelease();
return pRet;
}
else
{
delete pRet;
pRet = NULL;
return NULL;
}
}
RadialMenu* RadialMenu::init(CCArray* items, float radius, float swirlOutDuration, bool adjustAnchors){
if (CCMenu::initWithArray(items))
{
radius_ = radius;
if (swirlOutDuration)
{
this->swirlItemsRadially(swirlOutDuration,adjustAnchors);
}
}
return this;
}
void RadialMenu::alignItemsRadially(){
CCObject* obj = NULL;
CCMenuItem* item = NULL;
int count = this->getChildrenCount();
double sliceAngle = (2 * 3.14) / count;
int i = 0;
CCARRAY_FOREACH(this->getChildren(),obj){
double theta = sliceAngle * i;
double x = radius_ * sin(theta);
double y = radius_ * cos(theta);
item = (CCMenuItem*)obj;
item->setPosition(ccp(x,y));
++i;
}
obj = NULL;
item = NULL;
}
void RadialMenu::swirlItemsRadially(float duration){
this->swirlItemsRadially(duration,false);
}
void RadialMenu::swirlItemsRadially(float duration, bool adjustAnchors){
CCObject* obj = NULL;
CCMenuItem* item = NULL;
int count = this->getChildrenCount();
double sliceAngle = (2 * 3.14) / count;
double base_length = sqrt(2.0) * radius_;
int i = 0;
CCARRAY_FOREACH(this->getChildren(), obj){
double theta = sliceAngle * i;
double x = radius_ * sin(theta);
double y = radius_ * cos(theta);
item = (CCMenuItem*)obj;
if (adjustAnchors) {
if (theta <= 0.01) {
}
else if (theta <= M_PI) {
item->setAnchorPoint(ccp(0.2, 0.5));
}
else if (theta > M_PI) {
item->setAnchorPoint(ccp(0.8, 0.5));
}
}
/* swirl */
ccBezierConfig bezier;
bezier.controlPoint_1 = ccp(radius_ * sin(theta - 3.14/4), radius_ * cos(theta - 3.14/4));
bezier.controlPoint_2 = ccp(base_length * sin(theta - 3.14/8), base_length * cos(theta - 3.14/8));
bezier.endPosition = ccp(x, y);
item->runAction(CCBezierBy::create(duration,bezier));
/* rotate */
item->setRotation(90);
item->runAction(CCRotateBy::create(duration,item->getRotation()-90));
/* disable */
item->setEnabled(false);
item->runAction(CCSequence::create(
CCDelayTime::create(duration),
CCCallFuncND::create(this,callfuncND_selector(RadialMenu::animEndedCallback),item),
NULL));
/*
* brighten to white.
* I use TintTo gray in 0s to avoid setColor: not being a method in CCMenuItem
*/
item->runAction(CCSequence::create(
CCTintTo::create(0,192,192,192),
CCTintTo::create(duration,255,255,255),
NULL));
++i;
}
obj = NULL;
item = NULL;
}
void RadialMenu::animEndedCallback(CCNode*, void* item){
CCMenuItem* pItem = (CCMenuItem*)item;
pItem->setEnabled(true);
}
CCMenuItem* RadialMenu::itemForTouch(CCTouch * touch){
CCPoint touchLocation = touch->getLocationInView();
touchLocation = CCDirector::sharedDirector()->convertToGL(touchLocation);
CCPoint location = this->convertToNodeSpace(touchLocation);
float distance = sqrt( location.x*location.x + location.y*location.y );
if (distance >= radius_ * 0.5 && distance <= radius_ * 1.5) {
int count = this->getChildrenCount();
double factor = (2 * 3.14) / count;
double theta = atan2(location.x, location.y);
/* I use [0, 2pi) for layout, but atan2 returns [-pi, pi) */
if (theta < 0) {
theta += 2 * M_PI;
}
/* find closest item */
int i = theta/factor + 0.5;
/* don't let them tap halfway across the circle if there are few items */
double angle_distance = fabs(factor * i - theta);
if (angle_distance < M_PI/5) {
/* tapping at 11 o'clock is like tapping at 1 o'clock */
if (i == count) { i = 0; }
CCMenuItem* item = (CCMenuItem*)(this->getChildren()->objectAtIndex(i));
if (item->isVisible() && item->isEnabled())
{
return item;
}
}
}
/* delegate for items that move, etc */
return CCMenu::itemForTouch(touch);
}

边栏推荐
- 三项最高级认证,两项创新技术、两大优秀案例,阿里云亮相云原生产业大会
- Jvm: when a method is overloaded, the specific method to call is determined by the static type of the incoming parameter rather than the actual type of the parameter
- jvm-04.对象的内存布局
- Redis cache penetration solution - bloom filter
- Leetcode topic analysis add binary
- How does win11 enable mobile hotspot? How to enable mobile hotspot in win11
- 数字藏品市场才刚刚开始
- Opencv display image
- MySQL面试真题(二十三)——拼多多-球赛分析
- June 22, 2022: golang multiple choice question, what does the following golang code output? A:3; B:1; C:4; D: Compilation failed. package main import ( “fmt“ ) func mai
猜你喜欢

三项最高级认证,两项创新技术、两大优秀案例,阿里云亮相云原生产业大会

The performance of nonstandard sprintf code in different platforms

Visual Studio调试技巧

MySQL面试真题(二十八)——案例-通讯运营商指标分析

数字藏品火热背后需要强大的技术团队支持 北方技术团队

MDM data cleaning function development description

Prometheus, incluxdb2.2 installation and flume_ Export download compile use

华为软硬件生态圈成型,从根子上改变美国对软硬件体系的领导地位

jvm-05. garbage collection

How to specify the output path of pig register Project Log
随机推荐
Real MySQL interview question (30) -- shell real estate order analysis
vant weapp日历组件性能优化 Calendar 日历添加min-date最小日期页面加载缓慢
jvm-01. Instruction rearrangement
Pat class B 1012 C language
MySQL面试真题(二十六)——滴滴2020年笔试题
Excel sheet column number for leetcode topic resolution
Dolphin scheduler dolphin scheduling upgrade code transformation -upgradedolphin scheduler
Prometheus, incluxdb2.2 installation and flume_ Export download compile use
三项最高级认证,两项创新技术、两大优秀案例,阿里云亮相云原生产业大会
The performance of nonstandard sprintf code in different platforms
【斯坦福计网CS144项目】Lab2: TCPReceiver
Skill self check | do you know these 6 skills if you want to be a test leader?
node中操作mongoDB
PAT 乙等 1018 C语言
Android handler memory leak kotlin memory leak handling
jvm-02. Guarantee of orderliness
Wechat applet: a new interesting test
Real MySQL interview question (23) -- pinduoduo ball game analysis
Leetcode topic analysis: factorial training zeroes
Real MySQL interview questions (XXVII) -- Classification of users by RFM analysis method