PRELOADER

Dinghow的个人博客

Current post : 《cocos2dx-3.10期末项目踩坑记录》

10/22/2024 —— 

不得不吐槽在先,cocos整体框架十分强悍,但是很多细节的东西写得不够人性化,而且官方的文档也没有跟上,导致浪费大量的时间在一些不必要的坑上,实在是不划算,记录一下这次期末项目用cocos2d-x3.10开发泡泡堂踩到的坑吧!

1.cocosStudio导出的csb文件组件获取

auto rootNode = CSLoader::createNode("LoginScene/LoginScene.csb");//获取csb
Layout* background = (Layout*)rootNode->getChildByName("background");//获取csb上的层
Button* btnMenu = (Button*)Helper::seekWidgetByName(background, "menubutton");//获取层上的button

注意的是子控件名称只能使用小写,如果用大写的话获取不到,还不会报错(Orz,真不知道当时攻城狮是怎么想的,单步调了很久看地址才发现)

坑爹指数:★★★★

2.cocosStudio Button控件的Touch检测

范围是按照你Button图片来的,所以如果是圆形按钮,想做得准确,得把圆抠出来

坑爹质数:★

3.角色移动和动画播放

这个打算暑假抽空写个详细的专题,角色的位置移动是采用的默认调度器+键盘监听器,每一帧都会检测按键并做出相应的移动,而角色的动画则是在init里面另写了一个函数+键盘监听器,最开始没想这么多,两者是合在一起的,但是发现采用调度器放动画或者函数控制移动都会出现动作不连贯或配合失真,所以迫不得已只能把两个东西分开写,再分开调速度,终于实现了。

角色位置移动:

//调度器
void MapOfGame::update(float delta) {
    Node::update(delta);
    role1.loadPositon();
    auto upArrow = EventKeyboard::KeyCode::KEY_UP_ARROW,
        downArrow = EventKeyboard::KeyCode::KEY_DOWN_ARROW,
        leftArrow = EventKeyboard::KeyCode::KEY_LEFT_ARROW,
        rightArrow = EventKeyboard::KeyCode::KEY_RIGHT_ARROW;
    if (isKeyPressed(upArrow)) {
        keyPressedMovement(upArrow);
    }
    else if (isKeyPressed(downArrow)) {
        keyPressedMovement(downArrow);
    }
    else if (isKeyPressed(leftArrow)) {
        keyPressedMovement(leftArrow);
    }
    else if (isKeyPressed(rightArrow)) {
        keyPressedMovement(rightArrow);
    }
}

........
  //对应的移动函数
  void MapOfGame::keyPressedMovement(EventKeyboard::KeyCode keyCode) {
    CCPoint moveByPosition;
    RoleDirection tag;
    //you can set move speed here
    switch (keyCode) {
    case EventKeyboard::KeyCode::KEY_UP_ARROW:
        moveByPosition = ccp(0, role1.getSpeed());
        break;
    case EventKeyboard::KeyCode::KEY_DOWN_ARROW:
        moveByPosition = ccp(0, -role1.getSpeed());
        break;
    case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
        moveByPosition = ccp(-role1.getSpeed(), 0);
        break;
    case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
        moveByPosition = ccp(role1.getSpeed(), 0);
        break;
    default:
        moveByPosition = ccp(0, 0);
        break;
    }
  //创建MoveBy对象并执行移动的动作
    auto move = CCMoveBy::create(0.01f, moveByPosition);
    role1.role->runAction(move);
}

角色移动时的动画

//这些是在init里面写的
    //add keyboard listener
    auto listener = EventListenerKeyboard::create();
    //call responding animation when realted key is pressed
    listener->onKeyPressed = [=](EventKeyboard::KeyCode keyCode, Event *event) {
        keys[keyCode] = true;
        switch (keyCode){
        case EventKeyboard::KeyCode::KEY_UP_ARROW:
            keyPressedAnimation(keyCode);
            break;
        case EventKeyboard::KeyCode::KEY_DOWN_ARROW:
            keyPressedAnimation(keyCode);
            break;
        case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
            keyPressedAnimation(keyCode);
            break;
        case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
            keyPressedAnimation(keyCode);
            break;
        default:
            break;
    }
};    


.......
  //对应的动画函数,walkAnimation是之前弄的四个方向序列帧动画数组
  void MapOfGame::keyPressedAnimation(EventKeyboard::KeyCode keyCode) {
    RoleDirection tag;
    //you can set move speed here
    switch (keyCode) {
    case EventKeyboard::KeyCode::KEY_UP_ARROW:
        tag = kUp;
        break;
    case EventKeyboard::KeyCode::KEY_DOWN_ARROW:
        tag = kDown;
        break;
    case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
        tag = kLeft;
        break;
    case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
        tag = kRight;
        break;
    default:
        break;
    }
    animations[tag] = RepeatForever::create(CCAnimate::create(walkAnimations[tag]));
    //animations[tag] = CCAnimate::create(walkAnimations[tag]);
    role1.role->runAction(animations[tag]);
}

坑爹指数:★★

4.采用调度器碰撞检测的注意

我的碰撞检测的思路是把相关函数在调度器里每一帧调用一次(实际上和位置移动函数写在一起的,非法就不会执行移动),保证检测的及时性,但是却意外发现单步调试时就没有问题,一旦运行起来就会出现“惯性过大”冲出碰撞区域后卡住的情况(此时的坐标已经非法了,所以移动不了),耗了几个小时都没有检测出逻辑错误,第二天睡一觉后突然发现,我的MoveBy设置的duration是0.28f,也就是说我每一帧的移动动作需要0.28秒来完成,然而我每一秒有60帧,也就是说我如果摁住移动键,完成这一秒的移动动作需要60*0.28s,这是不可能了,所以系统可能在执行下一次时直接中断了上一次duration的过程,造成了碰撞检测失效,为了验证这一猜想,我把每一帧调用的默认调度器scheduleUpdate换成了可以设置时间间隔的调度器,不废话,上代码

//现在调度器就可以设置时间间隔了
this->schedule(schedule_selector(MapOfGame::update), 0.05f);
//碰撞检测部分的代码,和之前贴的移动位置代码是一个函数
    //collision check
    CCPoint targetPosition = ccpAdd(role1.role->getPosition(), moveByPosition);
    if (checkCollision(targetPosition, tag) == kWall) {
        setFaceDirection(tag);
        return;
    }
    auto move = CCMoveBy::create(0.01f, moveByPosition);
    role1.role->runAction(move);

我把调度器的时间间隔设置了>MoveBy的时间间隔,也就是每一次调度键盘监听和碰撞检测都能保证上一次移动的过程已经完成,果然,我的角色没有再出现“惯性过大”冲入非法区域卡住的情况。

坑爹指数:★★★★★★

5.不同场景下背景音乐的切换

在完成碰撞检测后的上午,我满心欢喜的以为提前完成了计划,想顺手牵羊把音效给加上去,在我的设想下一个游戏引擎放几个背景音乐应该是再容易不过的事情,然而,cocos蛋疼的设计却让这一简单的设想实现起来变得困难重重。

我采用的是cocos2d-x自带的音频组件#include "SimpleAudioEngine.h",然后在每个场景中调用对应函数播放背景音乐,坑爹的地方来了,比如说我的Scene1和Scene2打算使用不同的背景音乐,从前者切换到后者后,背景音乐的播放会停止,后者的音乐只能放几秒就卡住,在查阅了网上大量的资料后,发现这是cocos2d-x场景切换的特性所致,具体见关东升的博文:

场景切换与背景音乐

特别需要注意,在这种情况下,除了需要自己重写一遍场景生命周期函数外

void MapOfGame::onEnter() {
    Layer::onEnter();
}

void MapOfGame::onEnterTransitionDidFinish() {
    Layer::onEnterTransitionDidFinish();
    //play music
    SimpleAudioEngine::getInstance()->playBackgroundMusic("MusicSource/bg/Village.mp3", true);
    SimpleAudioEngine::getInstance()->playEffect("MusicSource/appear.wav");
}

void MapOfGame::onExit() {
    Layer::onExit();
}

void MapOfGame::onExitTransitionDidStart() {
    Layer::onExitTransitionDidStart();
}

void MapOfGame::cleanup() {
    Layer::cleanup();
}

还要把对应的切换方式设置为 pushScene,采用repeatScene的话系统会自动调动音乐停止函数,再怎么弄后面的Scene也出不了声

void Login::StartGameTouch(Ref* pSender, Widget::TouchEventType type) {
    switch (type) {
    case Widget::TouchEventType::ENDED:
        auto director = Director::getInstance();
        //transfer to MapScene
        auto scene = MapOfGame::createScene();
        auto transition = TransitionCrossFade::create(1.0f, scene);
        SimpleAudioEngine::getInstance()->stopBackgroundMusic();
        director->pushScene(transition);
        break;
    }
}

坑爹指数:★★★★★(真乃神坑,花了我一天就弄一个背景音乐Orz……是我太菜)