底部操作栏的封装

本文深入解析游戏开发中按钮与手柄的操作实现机制,详细讲解了按钮和手柄的交互过程,包括触摸事件处理、按钮冷却机制以及手柄单位向量计算方法。同时,文章介绍了通过MVC模式将协议与功能类分离,实现灵活的按钮功能定制。

底部操作栏包括手柄,按钮和一个梯度



手柄:

手柄的实现来自于Joystick(网上有其实现的源码)

当把摇杆往最右方向移动,然后靠着边绕一圈,发现其值是-1和1之间,即单位向量,所以如何计算出这个单位向量是实现的核心。

void Joystick::updateVelocity(CCPoint point) {
	if(_diable) //不可用,则返回,不需计算
		return;
	// Calculate distance and angle from the center.
	float dx = point.x;//当前的触摸点的x值
	float dy = point.y;//当前的触摸点的y值
	if(!_autoCenter && dx == 0 && dy == 0) {//原点则返回,不需计算
		return;
	}
	float dSq = dx * dx + dy * dy;//计算x的平方加上y的平方
	
	if(dSq <= _deadRadiusSq) {//如果小于死亡半径的平方(如果有设置死亡半径的话),返回原点坐标和向量
		_velocity = CCPointZero;
		_degrees = 0.0f;
		_stickPosition = point;
		return;
	}

	float angle = atan2f(dy, dx); //计算弧度
	if(angle < 0){//弧度小于0,则加上2 * pi,即弧度的范围控制在0到2 * pi之间
		angle		+= SJ_PI_X_2;
	}
	float cosAngle;
	float sinAngle;
	
	if(_isDPad){
		float anglePerSector = 360.0f / _numberOfDirections * SJ_DEG2RAD;
		angle = round(angle/anglePerSector) * anglePerSector;
	}
	
	cosAngle = cosf(angle);//计算余弦
	sinAngle = sinf(angle);//计算正弦
	
	// NOTE: Velocity goes from -1.0 to 1.0.
	if (dSq > _joystickRadiusSq || _isDPad) { //计算x和y值
		dx = cosAngle * _joystickRadius;
		dy = sinAngle * _joystickRadius;
	}
	
	_velocity = CCPointMake(dx / _joystickRadius, dy / _joystickRadius);//计算控制器的点
	_degrees = angle * SJ_RAD2DEG;
	if(_isOverRange) {
		dx = dx / 2;
		dy = dy / 2;
	}
	// Update the thumb's position
	_stickPosition = ccp(dx, dy);
}
其思路可以概括为:

1、当触摸移动时,获取当前的坐标(节点内),根据坐标的x值和y值利用反正切函数求出当前的坐标的弧度

2、确保弧度范围是在0到2 * pi之间

3、利用弧度计算正弦和余弦

4、利用正弦和余弦计算出单位向量值


按钮:

实现思路如下:

1、判断触摸点是否在按钮的范围内

2、判断按钮是否需要冷却

2.1 不需冷却:那么在触摸移动时继续判断是否还是在按钮范围内,如果是,按钮的功能继续保持

2.2 需要冷却:那么当点击完按钮后,不需要往下分发触摸事件,点击完后执行冷却效果

bool Button::ccTouchBegan(CCTouch *touch, CCEvent *event) {
	if(_active || _disable || GameLayer::shareGameLayer()->getShip()->getIsHyperspaceJump()) {//处于激活状态或者超空间跳跃状态
		return false;
	}
	float radiusSq = _radius * _radius;
	CCPoint location = CCDirector::sharedDirector()->convertToGL(touch->getLocationInView());
	location = this->convertToNodeSpace(location);
	//判读触摸点是否在按钮上
	if(location.x < -_radius || location.x > _radius || location.y < -_radius || location.y > _radius){
		return false;
	}else{
		float dSq = location.x * location.x + location.y * location.y;
		if(radiusSq > dSq){
			if(this->_coolingEnd) {// 是否冷却结束
				this->_active = true;
				if(_cool && !_limit) {//执行冷却效果
					_target->triggerButton(this->_buttonType);//执行按钮功能
					this->_coolingEnd = false;

					//执行冷却
					CCProgressFromTo* to = CCProgressFromTo::create(_coolingTime, _percentage, 0);
					CCProgressTimer* progress = CCProgressTimer::create(_sprite);
					progress->setType(kCCProgressTimerTypeRadial);
					progress->setTag(99);
					progress->setReverseProgress(true);
					this->getParent()->addChild(progress);
					CCCallFunc* func = CCCallFunc::create(this, callfunc_selector(Button::onCallback));//按钮重置可用
					progress->runAction(CCSequence::create(to, func, NULL));

					return false;//结束触摸分发
				}else {
					_target->triggerButton(this->_buttonType);
				}
			}
			return true;
		}
	}
	return false;
}
void Button::ccTouchMoved(CCTouch *touch, CCEvent *event) {
	if(_limit && _active && !_cool) {//判断是否需要冷却
		float radiusSq = _radius * _radius;
		CCPoint location = CCDirector::sharedDirector()->convertToGL(touch->getLocationInView());
		location = this->convertToNodeSpace(location);
		if(location.x < -_radius || location.x > _radius || location.y < -_radius || location.y > _radius){//触摸点是否离开按钮
			_active = false;
			return;
		}else{
			float dSq = location.x*location.x + location.y*location.y;
			if(radiusSq > dSq){//触摸点还在按钮上,继续执行该按钮的功能
				_target->triggerButton(this->_buttonType);
				_active = true;
			}
		}
	}
}

其中按钮的功能通过一个协议来触发

在放置按钮的层上,继承了该协议,再由此层指派按钮的功能到各自实现类去实现,即MVC模式

首先定义一个协议

class ButtonDelegate
{
public:
	virtual void triggerButton(HW_BUTTON_TYPE buttonType) = 0;//触发按钮事件
	virtual void unTriggerButton(HW_BUTTON_TYPE buttonType) = 0;//触发结束事件
};

其次定义按钮的功能类别

typedef enum {
	MIN_SPPED = 0,
	ADD_SPEED,
	DODGE,
	SHOOT_QUANTUM_GUN,
	HYPERSPACE_JUMP,
	SWITCH_HEAVY_MISSILE,
	ADD_ION_LIMIT_TIME,
	SAME_LOCKING_ENEMY,
	BLACK_HOLE,
	EMP
}HW_BUTTON_TYPE;

最后,继承该协议,重写协议的两个方法

class BorderLayer : public CCLayer, public ButtonDelegate

void BorderLayer::triggerButton(HW_BUTTON_TYPE buttonType) {
    switch (buttonType) {
    case MIN_SPPED:
	break;
    case ADD_SPEED:
	break;
    case DODGE:
	break;
    case SHOOT_QUANTUM_GUN:
	break;
    ...
    }
}
void BorderLayer::unTriggerButton(HW_BUTTON_TYPE buttonType) {
    //do something
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值