1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > cocos2dx游戏开发学习——虚拟摇杆(8方向)讲解

cocos2dx游戏开发学习——虚拟摇杆(8方向)讲解

时间:2023-02-25 02:57:22

相关推荐

cocos2dx游戏开发学习——虚拟摇杆(8方向)讲解

写这篇博客的目的主要是记录一下 虚拟摇杆的实现过程。虚拟摇杆一般分文四方向和八方向,也主要根据项目需求来决定。直接进入主题吧。

先上效果图:

方向的思路分析

看图,说先我们可以将8个方向在坐标系中画出来,这里涉及到余弦定理,高中数学可能经常看到这样的图,从图中可以很明确的看出每45°一个方向。这里我们只需要根据a的度数来判别方向。(例如:022.5°和337.5°360° 是右方向,67.5112.5是上方向,等等)。那么问题来了,怎么去算出a的角度呢,根据反余弦定义,我们一般只能算出0180°,那如何才能得到0~360°呢?这里隐约记得当a大于180度时候,a的余弦值等于(360-a)的余弦(如果不熟悉的话记得补一补高中数学了)。这样就解决了方向问题啦。

上代码:

/*** 获取以p1为圆心,p2p1与x轴的弧度值*/float GameRocker::getRad(cocos2d::Point p1, cocos2d::Point p2){float xx = p2.x - p1.x;float yy = p2.y - p1.y;// 斜边float xie = sqrt(pow(xx, 2) + pow(yy, 2));// yy >= 0 弧度在于 0 到 π 之间。(0~180°)// yy < 0 弧度在于 π 到 2π 之间。(180°~360°)float rad = yy >= 0 ? (acos(xx / xie)) : (PI * 2 - acos(xx / xie));return rad;}

虚拟摇杆类的实现

有一个问题需要思考,这个遥感类应该继承Node呢还是Layer,为什么我会考虑这个,因为我看到一些遥感类都是继承Layer,而我一般喜欢继承Node。而且我觉得继承Node要比继承Layer更加灵活(这个问题希望大牛过来指点)。

无论继承Node还是Layer还是其他,我们都需要添加触摸监听事件。

很多游戏中,我们发现虚拟摇杆会有个额外状态,常见的就是加速,玩儿过手机版的2K的应该熟悉。我也考虑做类似的功能,还有许多游戏中摇杆可能不是固定的。这里我就直接上代码了。

GameRocker.h

/*** 摇杆状态类*/class GameRockerState{public:/*** 摇杆方向枚举*/enum class DirectionType{StayType,LeftType,LeftUpType,UpType,RightUpType,RightType,RightDownType,DownType,LeftDownType,};/*** 速度状态*/enum class SpeedState{StayState,NormalState,QuickState,};public:GameRockerState():_direction(GameRockerState::DirectionType::StayType),_speedState(GameRockerState::SpeedState::StayState) {};GameRockerState(const GameRockerState& state){_direction = state.getDirection();_speedState = state.getSpeedState();};~GameRockerState(){};GameRockerState operator = (const GameRockerState& state){if(this == &state) return *this;_direction = state.getDirection();_speedState = state.getSpeedState();return *this;};bool operator == (const GameRockerState& state){return (_direction == state.getDirection() && _speedState == state.getSpeedState());}bool operator != (const GameRockerState& state){return !(*this==state);}protected:CC_SYNTHESIZE_PASS_BY_REF(GameRockerState::DirectionType,_direction,Direction);CC_SYNTHESIZE_PASS_BY_REF(GameRockerState::SpeedState,_speedState,SpeedState);};/*** 摇杆类*/class GameRocker:public cocos2d::Node{public:typedef std::function<void(const GameRockerState&)> StateChangeCallback;/*** 速度状态*/enum class KeysType{Key_4,// 四键位Key_8,// 八键位};public:GameRocker();~GameRocker();static GameRocker* createWithFilename(const std::string& barFilename,const std::string& bgFilename);virtual const cocos2d::Size& getContentSize()const;/*** 设置状态改变的监听回调*/void setupDirectionStateCallback(const StateChangeCallback& callback);/*** 获取当前摇杆状态*/inline const GameRockerState& getRockerState() const {return _rockerState; };protected:bool initWithFilename(const std::string& barFilename,const std::string& bgFilename);virtual void onEnter() override;void addTouchEvent();void changeRockerState(const GameRockerState& state);float getRad(cocos2d::Point p1,cocos2d::Point p2);private:cocos2d::Sprite* rockerBar;cocos2d::Sprite* rockerBG;/** 摇杆的正常活动半径 (指用于正常跑)*/float _normalRadius;/** 摇杆的加速活动半径(指用于快跑) */float _quickRadius;/** 摇杆状态 */GameRockerState _rockerState;/** 摇杆状态改变的回调函数 */StateChangeCallback _changeCallback;//** 最初的坐标 *//cocos2d::Vec2 _initPosition;/** 摇杆是否能够使用 */CC_SYNTHESIZE(bool,_enable,Enable);/** 摇杆是否是可移动 */CC_SYNTHESIZE(bool, _movable, Movable);/** 可移动的范围,GL坐标系 */CC_SYNTHESIZE_PASS_BY_REF(cocos2d::Rect,_moveRect,MoveRect);/** 键位方向 */CC_SYNTHESIZE(GameRocker::KeysType,_keysType,KeysType);};

GameRocker.cpp

#define PI 3.141592654GameRocker::GameRocker(){}GameRocker::~GameRocker(){}GameRocker* GameRocker::createWithFilename(const std::string &barFilename, const std::string &bgFilename){GameRocker* rocker = new (std::nothrow) GameRocker();if (rocker && rocker->initWithFilename(barFilename, bgFilename)) {rocker->autorelease();return rocker;}else{CC_SAFE_DELETE(rocker);return nullptr;}}const Size& GameRocker::getContentSize() const{if (rockerBG) {return rockerBG->getContentSize();}return Size::ZERO;}void GameRocker::setupDirectionStateCallback(const GameRocker::StateChangeCallback &callback){_changeCallback = callback;}bool GameRocker::initWithFilename(const std::string &barFilename, const std::string &bgFilename){if (Node::init()){rockerBG = Sprite::create(bgFilename);rockerBar = Sprite::create(barFilename);rockerBar->setPosition(rockerBG->getContentSize().width/2,rockerBG->getContentSize().height/2);rockerBG->addChild(rockerBar);this->addChild(rockerBG);setContentSize(rockerBG->getContentSize());_normalRadius = rockerBG->getContentSize().width/2;_quickRadius = rockerBG->getContentSize().width * 0.65;_enable = true;_movable = false;_moveRect = Rect(0, 0, 0, 0);_keysType = GameRocker::KeysType::Key_4;addTouchEvent();return true;}return false;}void GameRocker::onEnter(){Node::onEnter();_initPosition = this->getPosition();}void GameRocker::addTouchEvent(){auto listener = EventListenerTouchOneByOne::create();listener->setSwallowTouches(false);listener->onTouchBegan = [this](Touch* touch, Event* evnt)->bool{if (_movable){if (_moveRect.containsPoint(touch->getLocation())){this->setPosition(touch->getLocation());return true;}return false;}else{Rect rockerRect = Rect(this->getPositionX() - _normalRadius, this->getPositionY() - _normalRadius, _normalRadius*2, _normalRadius*2);Rect rect = this->getBoundingBox();rect.origin = rect.origin - Vec2(rockerBG->getContentSize().width/2,rockerBG->getContentSize().height/2);return rockerRect.containsPoint(touch->getLocation());}};listener->onTouchMoved = [this](Touch* touch, Event* evnt){if (!_enable) return;// 获取角度Point p1 = this->getPosition();Point p2 = touch->getLocation();float rad = getRad(p1, p2);// 用户触摸点到摇杆的中心的距离float touchRadius = sqrt(pow(touch->getLocation().x - this->getPositionX(), 2) + pow(touch->getLocation().y - this->getPositionY(), 2));float radius = MIN(_normalRadius, touchRadius);GameRockerState tempState;if (touchRadius >= _quickRadius) {// 加速状态radius = _quickRadius;rockerBG->setColor(Color3B::RED);tempState.setSpeedState(GameRockerState::SpeedState::QuickState);}else{// 普通状态rockerBG->setColor(Color3B::WHITE);tempState.setSpeedState(GameRockerState::SpeedState::NormalState);}// 设置 摇杆的位置Point barPoint = Vec2(radius * cos(rad), radius * sin(rad)) + Vec2(rockerBG->getContentSize().width/2,rockerBG->getContentSize().height/2);rockerBar->setPosition(barPoint);// 弧度转化成脚步float angle = 180.f / PI * rad;if (_keysType == GameRocker::KeysType::Key_4) {if ((angle >= 0 && angle < 45) || (angle >= 315 && angle < 360)) {//右tempState.setDirection(GameRockerState::DirectionType::RightType);}if (angle >= 45 && angle < 135) {//上tempState.setDirection(GameRockerState::DirectionType::UpType);}if (angle >= 135 && angle < 225) {//左tempState.setDirection(GameRockerState::DirectionType::LeftType);}if (angle >= 225 && angle < 315) {//下tempState.setDirection(GameRockerState::DirectionType::DownType);}}if (_keysType == GameRocker::KeysType::Key_8) {if ((angle >= 0 && angle < 22.5) || (angle >= 337.5 && angle < 360)) {//右tempState.setDirection(GameRockerState::DirectionType::RightType);}if (angle >= 22.5 && angle < 67.5) {//右上tempState.setDirection(GameRockerState::DirectionType::RightUpType);}if (angle >= 67.5 && angle < 112.5) {//上tempState.setDirection(GameRockerState::DirectionType::UpType);}if (angle >= 112.5 && angle < 157.5) {//左上tempState.setDirection(GameRockerState::DirectionType::LeftUpType);}if (angle >= 157.5 && angle < 202.5) {//左tempState.setDirection(GameRockerState::DirectionType::LeftType);}if (angle >= 202.5 && angle < 247.5) {//左下tempState.setDirection(GameRockerState::DirectionType::LeftDownType);}if (angle >= 247.5 && angle < 292.5) {//下tempState.setDirection(GameRockerState::DirectionType::DownType);}if (angle >= 292.5 && angle < 337.5) {//右下tempState.setDirection(GameRockerState::DirectionType::RightDownType);}}// 改变摇杆Bar的状态changeRockerState(tempState);};auto touchEndCallback = [this](Touch* touch, Event* evnt){CCLOG("Layer touch move (%f,%f)",touch->getLocation().x,touch->getLocation().y);rockerBar->setPosition(rockerBG->getContentSize().width/2,rockerBG->getContentSize().height/2);rockerBG->setColor(Color3B::WHITE);this->setPosition(_initPosition);GameRockerState tempState;changeRockerState(tempState);};listener->onTouchEnded = touchEndCallback;listener->onTouchCancelled = touchEndCallback;Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);}void GameRocker::changeRockerState(const GameRockerState &state){if (_rockerState == state) return;_rockerState = state;if (_changeCallback) _changeCallback(_rockerState);}/*** 获取以p1为圆心,p2p1与x轴的弧度值*/float GameRocker::getRad(cocos2d::Point p1, cocos2d::Point p2){float xx = p2.x - p1.x;float yy = p2.y - p1.y;// 斜边float xie = sqrt(pow(xx, 2) + pow(yy, 2));// yy >= 0 弧度在于 0 到 π 之间。(0~180°)// yy < 0 弧度在于 π 到 2π 之间。(180°~360°)float rad = yy >= 0 ? (acos(xx / xie)) : (PI * 2 - acos(xx / xie));return rad;}

最后提一句,这个虚拟键盘可拓展的东西很多,大家可以发挥一下。另外有什么不足的地方,大家可以提出来

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。