植物大战僵尸破产版--cocos studio

时间:2024-04-07 13:40:29

采用Cocos引擎cocos studio开发 平台为Android,开发语言为C++;

时间: 2020.4.29-5.14

  • 游戏说明

这是一款具有策略性的游戏。背景是僵尸入侵你的家,你需要用具有攻击性的植物才能抵御僵尸的入侵。玩家需要从第一关开始,选择左上角卡片放置豌豆射手,用豌豆射手防御僵尸,游戏结束条件是僵尸被打死或者僵尸越过屋子。过关就会得到卡片,收集植物卡片,用植物打倒僵尸!

 

  • 概要设计

游戏设计的框架主要有几部分构成:

游戏界面、动画设计、定时器设计、交互功能设计、菜单功能设计。

 

1游戏界面:

界面的大小、背景设置;

设置游戏卡片;

标题标签内容;

声音,背景音乐。

 

2动画设计:

僵尸移动;

豌豆射手动画;

豌豆飞行;

豌豆豆渣炸裂效果;

 

3定时器设计:

僵尸进场定时器;

豌豆发射定时器;

豌豆检测定时器;

4交互功能设计:

选卡的状态动作;

放置豌豆射手;

5菜单功能设计:

退出功能菜单exit;

下一关功能菜单next;

重新再来菜单resume;

 

 

             

  • 详细设计

3.1. 开始场景构造

 

    3.1.1标题标签

auto dict = Dictionary::createWithContentsOfFile("chnString.xml");

    const char* title = ((String*)dict->objectForKey("Title"))->getCString();

    auto chnTitle = Label::createWithTTF(title, "fonts/HBYY.TTF", 72);

    chnTitle->setPosition(320, 500);

    chnTitle->setColor(Color3B::RED);

    addChild(chnTitle);

利用Dictionary类的createWithContentsOfFile方法来读入XML文件,objectForKey方法来引用关键字,objectForKey方法来引用关键字,获得Title关键字代码的内容并转换为字符串title,并用字符串title创建标签。这种方法显示中文稳定,不需要引入外部库,增删改操作也比较方便,后续也很多用到的地方不再重复描述

   

    3.1.2按钮菜单

auto startLabelMenuItem = MenuItemLabel::create(start, this, menu_selector(HelloWorld::menu_start));

    auto StartMenu = Menu::create(startImgMenuItem, startLabelMenuItem, exitImgMenuItem, exitLabelMenuItem, NULL);

 

调用MenuItemLabel的create方法创建标签菜单项,点击后能产生相应的响应,并添加菜单项至菜单。

3.1.3音乐

    auto engine = CocosDenshion::SimpleAudioEngine::getInstance();

    engine->preloadBackgroundMusic("musics/bgMusic.wav");

    engine->preloadEffect("musics/start.wav");

    engine->preloadEffect("musics/exit.wav");

engine->playBackgroundMusic("musics/bgMusic.wav", true);

需要播放音乐时,通过getInstance()来“召唤”引擎,preloadBackgroundMusic为预加载背景音乐,参数为音乐的路径,playBackgroundMusic,播放背景音乐,第二个参数为是否重复播放。由preloadEffect预载音效。

 

 

3.2. 游戏场景

    3.2.1鼠标监听器响应

auto listener_mouse = EventListenerMouse::create();

    listener_mouse->onMouseMove = [=](Event* event)

    {

        auto e = (EventMouse*)event;

        if (IsCardSelected)

        {

           beanShooter->setOpacity(150);

           beanShooter->setPosition(e->getCursorX(), e->getCursorY());

        }

        else

        {

           beanShooter->setOpacity(0);

        }

    };

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener_mouse, this);

创建鼠标监听,设置如果卡片处于已选状态时,豌豆射手跟随鼠标移动x/y坐标,不然则设置豌豆射手不可见透明度为0;

 

3.2.2单点触摸

    auto listener_touch = EventListenerTouchOneByOne::create();

    listener_touch->onTouchBegan = [=](Touch* touch, Event* event)

    {

        auto cardSize = card->getContentSize();

        auto cardRect = Rect(0, 0, cardSize.width, cardSize.height);

        auto cardTouch = card->convertToNodeSpace(touch->getLocation());

 

创建单点触摸响应的监听器,设置为按下时响应,获得当前目标的大小,将目标大小所围成的范围定义为矩形类,最后获得触摸位置,该坐标要转换为目标坐标系中。

 

        if(cardRect.containsPoint(cardTouch)){

CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("card.wav");

           IsCardSelected = !IsCardSelected;

           auto TC = TextureCache::getInstance();

           if (IsCardSelected){

           auto texture = TC->addImage("card_selected.png");

           card->setTexture(texture);}

           else{

            auto texture = TC->addImage("card_normal.png");

           card->setTexture(texture);}

           listener_mouse->setEnabled(true);

           beanShooter->stopAllActions();

            unschedule(schedule_selector(StartScene::shootBean));

 

            else if (IsCardSelected){

CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("plant.wa);

           IsCardSelected = !IsCardSelected;

           auto TC = TextureCache::getInstance();

           auto texture = TC->addImage("card_normal.png");

           card->setTexture(texture);

           beanShooter->setOpacity(255);

        listener_mouse->setEnabled(false);

如果触点在卡片内时,卡片为已选状态,切换为未选状态,更换素材为card_normal.png,若卡片为未选状态,切换为已选状态,更换素材为card_selected.png;如果触点不在卡片内,卡片为已选状态,放置豌豆射手,切换为未选状态,更换素材为card_normal.png,卡片为未选状态,则不作任何处理。

 

 

3.2.3豌豆射手动画

 

auto animation = Animation::create();

    char name[20];

    auto SFC = SpriteFrameCache::getInstance();

    SFC->addSpriteFramesWithFile("beanShooter.plist");

    for (int i = 1; i <= 9; i++){

    sprintf(name, "beanShooter_%d.png", i);

animation->addSpriteFrame(SFC->getSpriteFrameByName(name));}

    animation->setDelayPerUnit(0.15);

    animation->setLoops(-1);

    auto shake = Animate::create(animation);

    beanShooter->runAction(shake);

schedule(schedule_selector(StartScene::shootBean), 3, -1, 0.01);

SpriteFrameCache,精灵帧缓存类,用来存放每帧图像,共享对象,getinstance获得该共享对象后,加载动画图像plist文件,通过plist中的动作名获取帧图像。Animation对象用循环依次加载剩余的图像,并设置帧间隔及重复播放,由配置好的Animation对象创建animate对象,让精灵执行animate对象动作。最后设置定时器,被执行的时间间隔为3s,在0.01s后启动。

 

3.2.4粒子系统

auto sun = ParticleSun::create();

sun->setScale(1.5);

sun->setPosition(850, 550);

addChild(sun);//

//auto starbg = ParticleSystemQuad::create("snow.plist");

//starbg->setPosition(450, 600);

//addChild(starbg);

 

3.2.5僵尸进场定时函数

auto SFC = SpriteFrameCache::getInstance();

SFC->addSpriteFramesWithFile("roadZombie.plist");

auto animation = Animation::create();

char name[20];

for (int i = 1; i <= 21; i++)

{

    sprintf(name, "roadZombie_%d.png", i);

animation->addSpriteFrame(SFC->getSpriteFrameByName(name));

}

animation->setDelayPerUnit(0.1);

animation->setLoops(-1);

auto animate = Animate::create(animation);

auto roadZombie = Sprite::createWithSpriteFrameName("roadZombie_1.png");

roadZombie->setPosition(900, 100);

addChild(roadZombie, 0, "roadZombie");

auto move = MoveBy::create(100, Vec2(-1000, 0));

auto gameover = CallFunc::create(CC_CALLBACK_0(StartScene::GameOver, this));

auto seq = Sequence::create(move, gameover, NULL);

roadZombie->runAction(seq);

roadZombie->runAction(animate);

动作类moveby是指移动到指定的距离,创建CallFunc对象,绑定定义好的回调函数GameOver,在精灵执行完动作后,触发另一个动作。Sequence类让多个动作依次执行,形成一连串的动作,最后让精灵roadZombie执行这一串动作。

 

3.2.6回调函数GameOver

void StartScene::GameOver()

{

OverMenu();

CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("losemusic.wav");

CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("scream.wav");

unschedule(schedule_selector(StartScene::shootBean));

unscheduleUpdate();

removeChildByName("roadZombie");

}僵尸越过屋子后执行函数overMenu,播放指定音乐音效,停止定时器并注销,移除僵尸精灵。

 

3.2.7豌豆发射器时间函数

void StartScene::shootBean(float dt)

{

auto beanShooter = getChildByName("beanShooter");

auto bean = Sprite::create("bean.png");

bean->setPosition(beanShooter->getPositionX() + 50, beanShooter->getPositionY() + 30);

auto move = MoveTo::create(3, Vec2(920, bean->getPositionY()));

auto removeBean = CallFunc::create(CC_CALLBACK_0(StartScene::removeBeanCB, this));

auto seq = Sequence::create(move, removeBean, NULL);

bean->runAction(seq);

addChild(bean, 0, "bean");

void StartScene::removeBeanCB()

{

removeChildByName("bean");

}

创建豆的精灵,设置好图片素材bean.png,豌豆初始位置相对豌豆射手的坐标为(50,30),豌豆水平飞行至右边界,水平坐标为920处,飞行时间为3s,再执行回调函数removeBeanCB移除原来的豌豆。效果为在豌豆射手放置后,每隔3秒时间发射一颗豌豆。

 

3.2.8豌豆检测器函数

void StartScene::update(float dt)

{

    auto bean = getChildByName("bean");

    auto roadZombie = getChildByName("roadZombie");

    if (!bean || !roadZombie) return;

    auto ZombieSize = roadZombie->getContentSize();

    auto ZombieRect = Rect(100, 0, ZombieSize.width, ZombieSize.height);

    auto beanPos = roadZombie->convertToNodeSpace(bean->getPosition());

    if (ZombieRect.containsPoint(beanPos))

    { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("shoot.wav");

        ZombieHP--;

        if (ZombieHP == 0)

        {

            unschedule(schedule_selector(StartScene::shootBean));

           unscheduleUpdate();

           removeChildByName("roadZombie");

           WinMenu();

        }

        auto beanSplat = Sprite::create("pea_splats.png");

        beanSplat->setPosition(bean->getPosition());

        addChild(beanSplat);

        auto fadeout = FadeOut::create(0.5);

        beanSplat->runAction(fadeout);

        removeChildByName("bean");

    }

检测豌豆是否打中僵尸。若打中,进行下面处理:在当前豌豆位置,添加显示豆渣,并执行淡出动作,自行销毁删除豌豆,播放shoot.wav音效,设置僵尸减血Zombiehp--,若僵尸血为0,显示菜单,停止豌豆发射器和检测器,删除僵尸。

 

 

 

 

 

 

 

 

 

3.2.9僵尸血量显示函数

hp = 10;

    auto hpLabel = Label::create("10", "Arial", 30);

    hpLabel->setAnchorPoint(Vec2(1, 1));

    hpLabel->setPosition(800, 550);

    addChild(hpLabel, 0, "hpLabel");

void StartScene::ZombieHpshow(float dt)

{

    auto hpLabel = (Label*)getChildByName("hpLabel");

    char s[15];

    sprintf(s, "ZombieHp=%d", hp);

    hpLabel->setString(s);

}

3.2.10菜单功能

void StartScene::WinMenu()

{

    if (!getChildByName("menu"))

    {

       auto resumeLabel = Label::create("Next", "Arial", 30);

        auto resumeMI = MenuItemLabel::create(resumeLabel, this, menu_selector(StartScene::resume));

        resumeMI->setPosition(-100, 100);

        auto exitLabel = Label::create("Exit", "Arial", 30);

        auto exitMI = MenuItemLabel::create(exitLabel, this, menu_selector(StartScene::exit));

        exitMI->setPosition(100, 100);

        auto menu = Menu::create(resumeMI, exitMI, NULL);

        addChild(menu, 0, "menu");

    }

    else

        getChildByName("menu")->setVisible(true);

}

void StartScene::OverMenu()

{

    if (!getChildByName("menu"))

    {

        auto resumeLabel = Label::create("Lose!", "Arial", 30);

        auto resumeMI = MenuItemLabel::create(resumeLabel, this, menu_selector(StartScene::resume));

        resumeMI->setPosition(-100, 100);

        auto exitLabel = Label::create("Exit", "Arial", 30);

        auto exitMI = MenuItemLabel::create(exitLabel, this, menu_selector(StartScene::exit));

        exitMI->setPosition(100, 100);

        auto menu = Menu::create(resumeMI, exitMI, NULL);

        addChild(menu, 0, "menu");

    }

    else

        getChildByName("menu")->setVisible(true);

 

}当僵尸被击败或僵尸越过房子后出现菜单,菜单包含两个标签类菜单项,内容分别为Next和Exit。Next和Exit采用系统自带的Arial字体,大小为40;next的坐标为(-100,0);Exit的坐标为(100,0), 点击Next,重新启动僵尸进场定时器,卡片重置为未选状态,豌豆射手不可见,重新播放background.wav,并且进入下一个场景NextScene;点击Exit 退出程序。

 

 

 

 

 

 

 

 

  • 测试

植物大战僵尸破产版--cocos studio

初始界面

植物大战僵尸破产版--cocos studio

游戏开始场景

植物大战僵尸破产版--cocos studio

豌豆发射动画和僵尸进场动画

植物大战僵尸破产版--cocos studio

豌豆发射检测器,攻击僵尸僵尸减血,数值显示在标签上。

植物大战僵尸破产版--cocos studio

击败僵尸弹出菜单选项进行下一关或者退出。

植物大战僵尸破产版--cocos studio

卡片的选取状态显示

 

植物大战僵尸破产版--cocos studio

豆渣出现

 

 

植物大战僵尸破产版--cocos studio

僵尸越过屋子,游戏失败。

 

 

 

  • 总结

本次交互技术开发项目实践cocos类课设时间做的时间大概花了我半个月吧,但是这次课设让我学到了很多很多东西并且巩固了对Cocos引擎的理解,提高了动手实践能力。

开始的时候吧,也算是一点头绪都没有,要求是开发语言C++,可这咱也没系统学习过C++语言鸭。那然后我去不停得翻看上年学习cocos的书看老师的课件,问同学,上网查资料,这也豁然开朗了点。首先,在开发前,我需要先配置好环境。主要开发环境是visual studio2013;然后是Cocos Studio,cocos项目创建的强力辅助工具,少不了;然后是CocosFrameWork,作为cocos核心引擎,包含着不少cocos开发需要的各类函数。

接着是选择游戏主题,大概对自己的能力和创意担忧吧,我根据老师课件里的示例程序选择了植物大战僵尸,经典的一款游戏了。为满足基本要求,我先构思好游戏框架,需要额外添加中英文标签,音乐,两个场景(开始场景和游戏场景),一种交互功能,一种动画效果和一种定时器;拓展功能的我考虑了瓦片地图和粒子系统设计。

此外,这次开发过程里,我还学习了如何使用粒子编辑器ParticleEditor,瓦片地图编辑器Tiled和图片打包软件TexturePacker。因此此次开发主题是植物大战僵尸,我也在www.6m5m.com上找了些素材,但是都是得要钱得。吝啬的我只能每天签到得积分,于是半个月了我终于攒积分购买一些资源素材。

接着说一下我遇到的一些问题,刚开始做场景时编译后报错,说SimpleAudioEngine找不到,错误原因是没有导入#include "SimpleAudioEngine.h"头文件,即没有引用using namespace CocosDenshion;命名空间。然后在用VS2017编译后的工程,再用VS2013运行时报出如下错误:fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏,解决方法如下:项目\属性\配置属性\清单工具\输入和输出\嵌入清单:原来是“是”,改成“否”。另外,在完成项目之时,我在打包APK时发现打包失败,刚开始我以为是设置的SDK和NDK目录路径不正确,但调整后依然报错。后面在CSDN博客查了很久,发现要在proj.android/jni/路径下换一个android.mk,如果还报错就proj.android/assets里的fonts删掉,重新打包。

收获任何一门知识的掌握,仅靠学习理论知识是远远不够的,要与实际动手操 作相结合才能达到功效。在这次项目开发中,虽然对专业知识有了更深的理解,但是自己还是觉得学习编程任重道远。在上网查资料过程中,发现我在课程学习里使用的很多东西都是“过时”的、浅薄的。我需要学习的还有很多,也认识到不断学习不断实践的重要性。