• 分享
  • Cocos2d-x 场景编程入门:类定义、初始化与场景管理全流程

  • @ 2025-7-20 21:04:09

在Cocos2d-x中,场景(Scene)是游戏界面的容器,管理着游戏中的所有节点(Node),如层(Layer)、精灵(Sprite)等。下面为你提供一个完整的场景实现教程,包含代码和详细注释。

1. 创建场景类

首先需要创建一个继承自cocos2d::Scene的场景类,通常包含初始化、资源加载和界面构建等功能。

// HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

class HelloWorld : public cocos2d::Scene
{
public:
    // 创建场景的静态方法(推荐方式)
    static cocos2d::Scene* createScene();

    // 初始化函数(继承自Node)
    virtual bool init();

    // 菜单回调函数
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // 生命周期函数:场景进入前台时调用
    void onEnter() override;
    
    // 生命周期函数:场景退出时调用
    void onExit() override;

    // 使用CREATE_FUNC宏创建对象
    CREATE_FUNC(HelloWorld);

private:
    // 私有成员变量示例
    cocos2d::Label* _scoreLabel;
    int _score;
};

#endif // __HELLOWORLD_SCENE_H__

2. 实现场景类

下面是场景类的实现文件,包含场景初始化、界面构建和事件处理等功能。

// HelloWorldScene.cpp
#include "HelloWorldScene.h"

USING_NS_CC;

// 创建场景的静态方法实现
Scene* HelloWorld::createScene()
{
    // 创建一个自动释放的场景对象
    return HelloWorld::create();
}

// 初始化函数实现
bool HelloWorld::init()
{
    // 调用父类的初始化方法
    if ( !Scene::init() )
    {
        return false;
    }
    
    // 获取设备可见区域
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    // 创建背景精灵
    auto background = Sprite::create("background.png");
    if (background) {
        // 设置精灵位置为屏幕中心
        background->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
        // 将背景添加到场景中(z-order为0,表示最底层)
        this->addChild(background, 0);
    }

    // 创建一个标签(Label)显示游戏标题
    auto label = Label::createWithTTF("Hello Cocos2d-x", "fonts/Marker Felt.ttf", 24);
    if (label) {
        // 设置标签位置
        label->setPosition(Vec2(origin.x + visibleSize.width/2,
                                origin.y + visibleSize.height - label->getContentSize().height));
        // 将标签添加到场景中(z-order为1)
        this->addChild(label, 1);
    }

    // 创建"关闭"按钮
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
    if (closeItem) {
        // 设置按钮位置(右上角)
        closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                    origin.y + closeItem->getContentSize().height/2));
    
        // 创建菜单并添加按钮
        auto menu = Menu::create(closeItem, NULL);
        menu->setPosition(Vec2::ZERO);
        // 将菜单添加到场景中(z-order为1)
        this->addChild(menu, 1);
    }

    // 初始化分数标签
    _score = 0;
    _scoreLabel = Label::createWithTTF("Score: 0", "fonts/Marker Felt.ttf", 18);
    if (_scoreLabel) {
        _scoreLabel->setPosition(Vec2(origin.x + visibleSize.width/2,
                                     origin.y + visibleSize.height - 50));
        this->addChild(_scoreLabel, 1);
    }

    // 创建一个精灵
    auto sprite = Sprite::create("player.png");
    if (sprite) {
        // 设置精灵位置
        sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
        
        // 添加一个简单的动作:精灵闪烁
        auto blink = Blink::create(3, 3);
        sprite->runAction(RepeatForever::create(blink));
        
        // 将精灵添加到场景中(z-order为2,表示在背景和标签之上)
        this->addChild(sprite, 2);
    }

    // 添加触摸事件监听器
    auto touchListener = EventListenerTouchOneByOne::create();
    touchListener->setSwallowTouches(true);
    
    // 设置触摸开始回调
    touchListener->onTouchBegan = [](Touch* touch, Event* event){
        // 处理触摸开始事件
        log("Touch began at x: %f, y: %f", touch->getLocation().x, touch->getLocation().y);
        return true; // 返回true表示处理该触摸事件
    };
    
    // 设置触摸移动回调
    touchListener->onTouchMoved = [](Touch* touch, Event* event){
        // 处理触摸移动事件
    };
    
    // 设置触摸结束回调
    touchListener->onTouchEnded = [](Touch* touch, Event* event){
        // 处理触摸结束事件
    };
    
    // 注册触摸监听器
    _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);

    // 创建一个调度器,每秒更新一次分数
    this->schedule([this](float dt) {
        _score++;
        _scoreLabel->setString(StringUtils::format("Score: %d", _score));
    }, 1.0f, "scoreUpdate");

    return true;
}

// 菜单关闭回调函数实现
void HelloWorld::menuCloseCallback(Ref* pSender)
{
    // 退出游戏(在移动设备上相当于返回主屏幕)
    Director::getInstance()->end();

    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
    #endif
}

// 场景进入前台时调用
void HelloWorld::onEnter() {
    Scene::onEnter();
    log("Scene entered");
}

// 场景退出时调用
void HelloWorld::onExit() {
    Scene::onExit();
    log("Scene exited");
}

3. 在AppDelegate中设置初始场景

在应用程序启动时,需要设置初始场景。

// AppDelegate.cpp
#include "AppDelegate.h"
#include "HelloWorldScene.h"

USING_NS_CC;

AppDelegate::AppDelegate() {
}

AppDelegate::~AppDelegate() {
}

// if you want a different context, modify the value of glContextAttrs
// it will affect all platforms
void AppDelegate::initGLContextAttrs() {
    // set OpenGL context attributes: red,green,blue,alpha,depth,stencil
    GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};

    GLView::setGLContextAttrs(glContextAttrs);
}

bool AppDelegate::applicationDidFinishLaunching() {
    // 初始化导演
    auto director = Director::getInstance();
    auto glView = director->getOpenGLView();
    if(!glView) {
        glView = GLViewImpl::create("My Game");
        director->setOpenGLView(glView);
    }

    // 设置设计分辨率
    glView->setDesignResolutionSize(480, 320, ResolutionPolicy::SHOW_ALL);

    // 关闭自动FPS显示
    director->setDisplayStats(false);

    // 设置FPS
    director->setAnimationInterval(1.0f / 60);

    // 创建场景
    auto scene = HelloWorld::createScene();

    // 运行场景
    director->runWithScene(scene);

    return true;
}

// 当应用程序进入后台时调用
void AppDelegate::applicationDidEnterBackground() {
    Director::getInstance()->stopAnimation();

    // 如果使用了音频,这里可以暂停音频
    // SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
}

// 当应用程序从后台返回时调用
void AppDelegate::applicationWillEnterForeground() {
    Director::getInstance()->startAnimation();

    // 如果使用了音频,这里可以恢复音频
    // SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
}

4. 场景管理与切换

在游戏中,经常需要在不同场景间切换。以下是场景切换的示例代码:

// 切换到游戏场景
void gotoGameScene() {
    auto gameScene = GameScene::createScene();
    
    // 直接切换场景(无过渡效果)
    // Director::getInstance()->replaceScene(gameScene);
    
    // 带过渡效果的场景切换(这里使用淡入淡出效果,持续时间0.5秒)
    Director::getInstance()->replaceScene(TransitionFade::create(0.5, gameScene));
}

// 返回主菜单
void returnToMainMenu() {
    auto mainMenuScene = MainMenu::createScene();
    Director::getInstance()->replaceScene(TransitionSlideInL::create(0.3, mainMenuScene));
}

总结

通过以上步骤,你可以创建一个完整的Cocos2d-x场景。关键点包括:

  1. 继承cocos2d::Scene创建自定义场景类
  2. init()方法中初始化界面元素
  3. 使用addChild()添加节点到场景中
  4. 通过Director::getInstance()->runWithScene()replaceScene()来运行场景
  5. 可以添加触摸事件、调度器等功能
  6. 场景切换时可以使用过渡效果增强用户体验

记得将所需的图片资源(如background.pngplayer.png等)放入项目的资源目录中。

1 条评论

  • @ 2025-7-20 21:05:54

    以下是代码中出现的英语单词的翻译和汉字音译表格:

    英语单词 翻译 汉字音译(仅供参考,非标准发音方式,仅辅助记忆)
    Scene 场景 思恩(sī ēn)
    Node 节点 诺德(nuò dé)
    Layer 雷尔(léi ěr)
    Sprite 精灵 斯普莱特(sī pǔ lái tè)
    HelloWorld 你好世界(通常作为示例程序名称) 哈喽沃尔德(hā lōu wò ěr dé)
    createScene 创建场景 克睿特思恩(场景)(kè ruì tè sī ēn)
    init 初始化 伊尼特(yī ní tè)
    menuCloseCallback 菜单关闭回调 麦纽(菜单)克洛斯(关闭)卡乐贝克(回调)(mài niǔ kè luò sī kǎ lè bèi kè)
    Ref 引用(在Cocos2d - x中通常表示引用对象) 瑞夫(ruì fū)
    onEnter 进入时(场景进入前台时调用的函数) 昂恩特(场景进入相关,可理解为“在进入时”)(āng ēn tè)
    onExit 退出时(场景退出时调用的函数) 昂艾克斯特(场景退出相关,可理解为“在退出时”)(āng ài kè sī tè)
    CREATE_FUNC 创建函数宏(Cocos2d - x中用于简化对象创建代码的宏) 克睿特放克(克睿特:创建,放克:函数相关)(kè ruì tè fàng kè)
    Label 标签(用于显示文本) 雷布尔(léi bù ěr)
    score 分数 斯高(sī gāo)
    visibleSize 可见尺寸 维兹伯(可见)赛斯(尺寸)(wéi zī bó sài sī)
    origin 原点 奥瑞金(ào ruì jīn)
    background 背景 拜克格朗德(bài kè gé lǎng dé)
    setPosition 设置位置 赛特(设置)坡西讯(位置)(sài tè pō xī xùn)
    addChild 添加子节点 艾迪(添加)柴尔德(子节点相关)(āi dí chái ěr dé)
    z - order 层级顺序(用于确定节点显示层级) 兹(z音)奥德尔(顺序)(zī ào dé ěr)
    TTF TrueTypeFont的缩写,一种字体格式 踢踢爱福(tī tī ài fū)
    MenuItemImage 图片菜单项 麦纽(菜单)艾特姆(项目)因麦基(图片)(mài niǔ ài tè mǔ yīn mài jī)
    Touch 触摸 踏吃(tà chī)
    Event 事件 伊文特(yī wén tè)
    EventListenerTouchOneByOne 单点触摸事件监听器 伊文特(事件)利斯滕(监听)踏吃(触摸)万白万(单点)(yī wén tè lì sī tēn tà chī wàn bái wàn)
    setSwallowTouches 设置吞噬触摸(通常用于设置是否拦截触摸事件) 赛特(设置)斯沃罗(吞噬)踏吃(触摸)(sài tè sī wò luó tà chī)
    onTouchBegan 触摸开始时(回调函数) 昂踏吃(触摸)比根(开始)(āng tà chī bǐ gēn)
    onTouchMoved 触摸移动时(回调函数) 昂踏吃(触摸)穆夫德(移动过去式)(āng tà chī mù fū dé)
    onTouchEnded 触摸结束时(回调函数) 昂踏吃(触摸)恩得德(结束过去式)(āng tà chī ēn dé dé)
    schedule 调度(通常用于设置定时任务) 斯凯聚(sī kǎi jù)
    Director 导演(在Cocos2d - x中是引擎的“总指挥”) 迪雷克特(导演相关,可理解为“指挥者”)(dí léi kè tè)
    GLView OpenGL视图(与图形显示相关) 机爱勒(GL相关)维尤(视图)(jī ài lè wéi yōu)
    ResolutionPolicy 分辨率策略 睿兹露深(分辨率)坡里西(策略)(ruì zī lù shēn pō lǐ xī)
    SHOW_ALL 全部显示(一种分辨率策略) 秀奥(全部显示)(xiù ào)
    displayStats 显示统计信息 迪斯普雷(显示)斯太茨(统计信息)(dí sī pǔ léi sī tài cì)
    FPS Frames Per Second的缩写,帧率 爱福匹艾斯(ài fū pǐ āi sī)
    animationInterval 动画间隔 安妮美神(动画)因特尔沃(间隔)(ān nī měi shén yīn tè ěr wò)
    runWithScene 运行场景 润(运行)维斯(和)思恩(场景)(rùn wéi sī sī ēn)
    applicationDidFinishLaunching 应用程序完成启动 艾普利克神(应用程序)迪德(过去式)飞尼什(完成)朗琴(启动)(ài pǔ lì kè shén dí dé fēi ní shī lǎng qín)
    applicationDidEnterBackground 应用程序进入后台 艾普利克神(应用程序)迪德(过去式)恩特(进入)拜克格朗德(背景)(ài pǔ lì kè shén dí dé ēn tè bài kè gé lǎng dé)
    applicationWillEnterForeground 应用程序将进入前台 艾普利克神(应用程序)威尔(将)恩特(进入)佛格朗德(前台)(ài pǔ lì kè shén wēi ěr ēn tè fó gé lǎng dé)
    TransitionFade 淡入淡出过渡(一种场景过渡效果) 传思(过渡相关)菲德(淡入淡出相关)(chuán sī fēi dé)
    TransitionSlideInL 从左边滑入过渡(一种场景过渡效果) 传思(过渡相关)斯莱德(滑动)因(进入)艾尔(左)(chuán sī sī lái dé yīn ài ěr)
    • 1