使用Cocos2d-x中的CCMenuItemToggle制作商店

本文介绍如何使用Cocos2d-x游戏引擎实现商店标签功能,包括使用CCMenuItemToggle创建开关菜单,实现标签间的切换效果,并提供封装函数简化代码。详细解释了各个步骤及关键代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近一直在和同学用Cocos2d-x做一款手机游戏,期间做过一个商店的功能,在此写一篇关于使用CCMenuItemToggle来实现商店标签功能。

首先把效果发出来,资源是用的《Banana Kong》的图片,上面的字是自己加的:(W == Weapon,H == Hat, I == Inventory, S == Skill)

达到以下几个效果:

1. 进入商店默认“W标签“是选中状态

2. 点击其他的标签后,前一个标签自动还原

3. 点击一个标签,弹出属于这个标签的元素


刚开始做的时候遇到了很多问题,想过用CCMenuItem,但是CCMenuitem的按下和弹起操作时一次性完成的,并且已经封装,网上有继承CCMenuItem重写的方法,感觉比较复杂。之后想过用CCSprite直接写精灵点击事件,结果也是遇到了一些麻烦,最终用CCMenuItemToggle写了出来,如果大家有什么更好的办法写出这个效果,欢迎拍砖!


然后我们来看创建一个CCMenuItemToggle需要的步骤:

CCMenuItemImage* toggleNormal = CCMenuItemImage::create("toggleNormal", "toggleSelect"); // 未点击时的图片
CCMenuItemImage* toggleSelect = CCMenuItemImage::create("toggleSelect", "toggleNormal"); // 点击后的图片
CCMenuItemToggle* myToggle = CCMenuItemToggle::createWithTarget(this,selector,toggleNormal,toggleSelect,NULL);// 使用以上两个图片菜单创建一个开关菜单

这样就创建了一个开关菜单(Toggle == "开关")。当然最后还要传入CCMenu中。


这里又有几点需要讲的:

1. 从这三句代码可以看到如果要创建CCMenuItemToggle的话首先要创建多个CCMenuItemImage,其实也可以是其他菜单项,我们这里只讲用图片创建。想要看其他创建方式以及想要深入研究的可以看源码,这里不赘述。

2. CCMenuItemToggle::createWithTarget(this, selector, imgNormal, imgSelect, NULL); 从这一句最后的参数NULL可以看出还可以继续添加菜单,创建出多选项开关菜单。同样,我们只需要两个图片。

3. 参数selector是回调函数。

如果我们就这样实现标签效果的话,意味着我们将写四遍上述代码,还要控制他们的位置等等,这样我们就理所当然的想到了将其封装成一个函数。我们定义这样一个函数,用来专门创建开关按钮。

CCMenuItemToggle* HShop::createToggle(SEL_MenuHandler selector, char * fileNameNormal, char * fileNameSelect)
{
    CCMenuItemImage* imgNormal = CCMenuItemImage::create(fileNameNormal, fileNameSelect);
    CCMenuItemImage* imgSelect = CCMenuItemImage::create(fileNameSelect, fileNameNormal);
    CCMenuItemToggle* imgToggle = CCMenuItemToggle::createWithTarget(this, selector, imgNormal, imgSelect, NULL);
    return imgToggle;
}

于是我们就可以这样创建标签:

wToggle = HShop::createToggle(menu_selector(HShop::toggleIsPressed), fileNameNormal, fileNameSelect);// HShop是商店类

hToggle = HShop::createToggle(menu_selector(HShop::toggleIsPressed), fileNameNormal, fileNameSelect);

iToggle = HShop::createToggle(menu_selector(HShop::toggleIsPressed), fileNameNormal, fileNameSelect);

sToggle = HShop::createToggle(menu_selector(HShop::toggleIsPressed), fileNameNormal, fileNameSelect);

但是这样依然比较麻烦,这四个标签的位置是有规律的,如果我们将他们使用的图片的文件名改得有规律一点,就可以用一个for循环控制。并且将toggle改为数组形式

于是修改后的代码如下:


// 创建商店里的标签
for(int i = 0; i < AllToggle; i++)
{
    char fileNameNormal[50];	// 未选中图片的文件名
    char fileNameSelect[50];	// 选中图片的文件名
    sprintf(fileNameNormal, "Toggle_Normal%d.png", i);
    sprintf(fileNameSelect, "Toggle_Select%d.png", i);
    toggle[i] = HShop::createToggle(menu_selector(HShop::toggleIsPressed), fileNameNormal, fileNameSelect);
    toggle[i]->setTag(i);	// 为每个标签设置Tag值
    float padding = 4;		// 标签之间的间隔
    toggle[i]->setPosition(ccp(48 + toggle[0]->getContentSize().width/2, (visibleSize.height/2 + 3 * (padding + toggle[0]->getContentSize().height)/2) - (toggle[0]->getContentSize().height + padding) * i));	// 标签位置,这个大家自己调一下,因为visibleSize是为了适应分辨率专写的
    double f = CCDirector::sharedDirector()->getContentScaleFactor();	// 调整一下大小,这个大家看情况
}
toggle[weaponToggle]->setSelectedIndex(1);	// 我们刚进入的时候武器标签是被选中的
toggle[weaponToggle]->setEnabled(false);	// 选中的标签不能再点击
CCMenu *toggleMenu = CCMenu::create(toggle[0], toggle[1], toggle[2], toggle[3], NULL);	// 加入到CCMenu对象中
toggleMenu->setAnchorPoint(ccp(0, 0));
toggleMenu->setPosition(ccp(origin.x, origin.y - 1));
this->addChild(toggleMenu, 0);



1. 这里我把需要的文件都改为Toggle_NormalX.png这种形式的(X代表数字),这样就可以用for循环控制载入相应图片。

2. 使用setSelectedIndex(int n)函数可以把标签设置为需要的状态,n对应的即是函数CCMenuItemToggle* imgToggle = CCMenuItemToggle::createWithTarget(this, selector, imgNormal, imgSelect, NULL);中从第三个参数到之后的各种状态

接下来我们就可以点击各种标签了,并且被点击后的标签不能再被点击(实现选中状态),但是现在呈现的状态确实这样的

这样明显是不对的,应该点击了”I标签“后,”W标签“应该恢复,于是剩下的操作我们就在回调函数toggleIsPressed()中进行操作,先贴代码:

 

void HShop::toggleIsPressed(CCObject * menuItem)
{
	CCMenuItemToggle* tmp = (CCMenuItemToggle*)menuItem;

	switch(tmp->getTag())
	{
	case weaponToggle:
		toggle[weaponToggle]->setEnabled(false);
		toggle[hatToggle]->setSelectedIndex(0);			toggle[hatToggle]->setEnabled(true);
		toggle[inventoryToggle]->setSelectedIndex(0);	toggle[inventoryToggle]->setEnabled(true);
		toggle[skillToggle]->setSelectedIndex(0);		toggle[skillToggle]->setEnabled(true);
		
		break;
	case hatToggle:
		toggle[hatToggle]->setEnabled(false);
		toggle[weaponToggle]->setSelectedIndex(0);		toggle[weaponToggle]->setEnabled(true);
		toggle[inventoryToggle]->setSelectedIndex(0);	toggle[inventoryToggle]->setEnabled(true);
		toggle[skillToggle]->setSelectedIndex(0);		toggle[skillToggle]->setEnabled(true);
		
		break;
	case inventoryToggle:
		toggle[inventoryToggle]->setEnabled(false);
		toggle[weaponToggle]->setSelectedIndex(0);		toggle[weaponToggle]->setEnabled(true);
		toggle[hatToggle]->setSelectedIndex(0);			toggle[hatToggle]->setEnabled(true);
		toggle[skillToggle]->setSelectedIndex(0);		toggle[skillToggle]->setEnabled(true);
		mc->createInventoryCard();
		CCScrollView* tem= (CCScrollView*)this->getChildByTag(1);
		tem->setContentOffset(ccp(0, -(containLayer->getContentSize().height - MYSCROLLVIEW_HEIGHT)), true);
		break;
	case skillToggle:
		toggle[skillToggle]->setEnabled(false);
		toggle[weaponToggle]->setSelectedIndex(0);		toggle[weaponToggle]->setEnabled(true);
		toggle[hatToggle]->setSelectedIndex(0);			toggle[hatToggle]->setEnabled(true);
		toggle[inventoryToggle]->setSelectedIndex(0);	toggle[inventoryToggle]->setEnabled(true);
		
		break;
	default:
		break;
	}
}


这样,用一个switch语句分别处理这四个标签被按下后的操作,他们的操作都可以总结为:

1. 被点击后不能再进行点击 toggle[XXX]->setEnabled(false);

2. 之前被点击的标签应该复原 toggle[XXX]->setSelectedIndex(0);

其实第二点是可以优化的,我的代码写得非常冗余——点击一个菜单,要复原其他三个菜单。之前我想过用一些方法解决,但是都出了一些莫名其妙的问题,具体我猜是回调函数里这个CCObject * menuItem参数的问题,我使用CCMenuItemToggle* tmp来操作被点击的Toggle总是出问题,如果有哪位大侠解决了的话希望不吝赐教!


这样就实现了我们的标签功能,差不多就这样吧,欢迎拍砖!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值