Cocos2d-x3.x塔防遊戲(保衛蘿蔔)從零開始(一)

1、前提:html

完成Hello Game項目的建立編譯。node

具體參考:Cocos2dx.3x_Hello Game項目建立篇android

2、本篇目標:windows

l  說說關於塔防遊戲的想法和思路網絡

l  實現一個簡單的塔防遊戲原型app

3、內容:eclipse

l  說說關於塔防遊戲的想法和思路測試

首先上一張塔防遊戲PSD設計效果圖this

遊戲故事設定:spa

這個遊戲說是保衛蘿蔔,但不能真的是保衛蘿蔔了,由於保衛蘿蔔的遊戲已經有了,只是借用一下這個大名鼎鼎的塔防遊戲宣傳和參照一下。如今網絡上主流遊戲都會先講一下故事讓玩家有一種入戲感,那咱們的這個故事是這樣的:好久好久之前,在美麗的大學宿舍區住着一羣美麗天真的女孩,可是邪惡的色狼大叔們總想對她們作一些壞事,那麼咱們的英雄善良勇敢的女生宿舍管理員利用生活中的武器菜刀、皮鞋、玩具飛機等在大叔必經的路上狙擊他們,保護女孩們免受這些大叔的傷害。

遊戲元素組成:

一、地圖:每一關地圖均不相同,主要是道路不一樣和炮臺位的不一樣。

二、炮塔:水果刀、菜刀、老鼠藥、高跟鞋、玩具飛機等,不一樣的炮臺具有不一樣的價格、攻擊速度、攻擊屬性、攻擊方式。

三、子彈:由炮臺發射的,具有不一樣的攻擊值、擴散值、遲緩值、攻擊範圍值等。

四、怪物:各種猥瑣大叔、叫獸、色狼,不一樣的色狼具有不一樣的速度值、傷害值、耐揍值,沿着地圖上的道路不斷的靠近道路終點的女主角。

五、女主角:道路終點的女孩,不具有攻擊力須要炮塔的保護,具備必定的純潔值,當純潔值被大叔玷污光了就自殺了,遊戲也就結束了。

6、分數&資源:殺死不一樣的色狼能得到必定的分數,分數能夠用來購買新的炮臺,每一關都會有必定的初始分數用來支撐遊戲最初的消耗,每一關的分數只限在本關使用,下一關開啓時前面積累的分數清空。

七、寶箱:用分數資源購買寶箱,能有必定概率得到比投入分數幾倍的回報。

八、醫生:用分數購買醫療,對女主角的純潔值進行修補。

遊戲開發模式:

整個遊戲開發的方式是這樣,首先實現一個很小的遊戲核心原型,而後不斷的修改擴大這個遊戲原型直至遊戲完成爲止。本人認爲這樣的方式比較適合讀者理解,而且跟着文章本身學會理解這個遊戲的開發。

l  實現一個簡單的遊戲原型

新建遊戲工程名爲DefendTheGirl(保衛女孩),包名爲:com.game. defendthegirl。若是還不會建立工程請參照:Cocos2dx.3x入門三部曲-Hello Game項目建立(二)篇

本篇原型須要實現內容:

一、  在主場景中載入一張地圖。

二、  在地圖終點放置一個女主角,在地圖的起點放置一個色狼大叔。

三、  讓色狼大叔沿着地圖指定的路線向女主角的位置靠近。

素材圖片準備:

地圖圖片level_bg_1.png  960px, 640px

女主角圖片 girl.png

 

色狼大叔圖片 dashu.png

 

把這幾張素材圖片拷貝到文件夾Resources下面便可

1、在主場景中載入一張地圖

第一步:

用Microsoft Visual Studio 2012打開proj.win32工程,而後在src下新建MainScene.h、MainScene.cpp做爲遊戲的主場景(Scene不會建?參考:Cocos2dx.3x入門三部曲-Hello Game項目解析(三)篇)

第二步:在init()方法裏載入地圖圖片level_bg_1.png,代碼以下:

bool MainScene::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    //載入地圖背景
    auto sprite = Sprite::create("level_bg_1.png");
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
    this->addChild(sprite, 0);
    return true;
}

第三步:打開AppDelegate.cpp文件,引入MainScene.h頭文件,而且在applicationDidFinishLaunching方法中把auto scene = HelloWorld::createScene(); 改爲auto scene = MainScene::createScene();而後運行。

2、在地圖終點放置一個女主角,在地圖的起點放置一個色狼大叔

第一步:在init()方法裏添加以下代碼

//載入地圖背景
    ……
    auto sprite = Sprite::create("level_bg_1.png");
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
    this->addChild(sprite, 0);

    //在地圖起點處放置一個色狼
    auto dsSprite = Sprite::create("dashu.png");
    dsSprite->setPosition(Vec2(40, 390));
    this->addChild(dsSprite, 0);

    //在地圖終點處放置一個女主角
    auto nhSprite = Sprite::create("girl.png");
    nhSprite->setPosition(Vec2(920, 480));
    this->addChild(nhSprite, 0);
…...

第二步:而後運行就能夠在畫面上看到色狼大叔和女主角了

一、 讓色狼大叔沿着地圖指定的路線向女主角的位置靠近

這個一個有點難度的任務,首先咱們對這張地圖的道路路徑進行一下座標分析:

如上圖所示,以地圖的右下頂點爲座標系原點,整個道路分紅12個座標點,女主角在1號座標點,色狼大叔在12號座標點。如今色狼大叔將沿着圖中黃色的線路從12點開始 11點、10點、9點…直至到達1點,個人實現思路是這樣,色狼從12點出發時告訴它目標點是11點,當色狼到達11點的時候繼續告訴它下一個目標點是10點直到1點。

幾個實現技術點:

一、  對Sprite(色狼)進行setPosition的方式能夠改變Sprite在圖上的位置從而實現Sprite的移動

二、  計算Sprite(色狼)位置到目標點的向量值,而後根據這個向量值和色狼移動速度計算Sprite在x,y方向上的距離偏移值,用這個偏移值調整Sprite的位置

判斷Sprite(色狼)到達目標點(如:11點),以下圖所示經過Sprite座標點和目標點之間的距離小於必定值的時就斷定爲到達目標點,須要設置新的下一個目標點。

有了這些實現思路,如今開始代碼編寫:

第一步:由上面是實現思路可知咱們的基本實現是路徑點,那麼這裏先新建一個路徑點類對象,名稱爲:Waypoint.h、Waypoint.cpp,繼承自:cocos2d::CCNode。

Waypoint.h:

class Waypoint: public cocos2d::CCNode
{
public:
    Waypoint(void);
    ~Waypoint(void);
    //初始化方法
    static Waypoint* nodeWithTheLocation(cocos2d::Point location);
    bool initWithTheLocation(cocos2d::Point location);
    //設置當前點的下一個路徑點
    void setNextWaypoint(Waypoint* waypoint);
    //獲取當前點的下一個路徑點
    Waypoint* getNextWaypoint();
    
    //當前路徑點位置
    CC_SYNTHESIZE(cocos2d::Point,_myPosition,MyPosition);

private:
    //下一個路徑點
    Waypoint* _nextWaypoint;
};
Waypoint.cpp:
Waypoint::Waypoint(void)
{
    _nextWaypoint=NULL;
}
Waypoint::~Waypoint(void)
{
}
Waypoint* Waypoint::nodeWithTheLocation(cocos2d::Point location)
{
    Waypoint* pRet=new Waypoint();
    if (pRet && pRet->initWithTheLocation(location))
    {
        pRet->autorelease();
        return pRet;
    } 
    else
    {
        delete pRet;
        pRet=NULL;
        return NULL;
    }
}

bool Waypoint::initWithTheLocation(cocos2d::Point location)
{
    bool bRet=false;
    do 
    {
        _myPosition=location;
        this->setPosition(Point::ZERO);
        bRet=true;
    } while (0);

    return bRet;
}

void Waypoint::setNextWaypoint(Waypoint* waypoint)
{
    _nextWaypoint=waypoint;
}

Waypoint* Waypoint::getNextWaypoint()
{
    return _nextWaypoint;
}

第二步:在MainScene.h裏聲明以下代碼

public:
    ……
    //重寫Layer的update方法
    //咱們主要在這個方法裏實現色狼移動
    virtual void update(float delta);

    CREATE_FUNC(MainScene);

private:
    //路徑開始點
    Waypoint *beginningWaypoint;
    //路徑目標點
    Waypoint *destinationWaypoint;
    //色狼的移動速度
    float walkingSpeed;
    //色狼當前位置
    cocos2d::Vec2 myPosition;
    //色狼大叔
    cocos2d::Sprite* dsSprite;
    //路徑點集合
    cocos2d::Vector<Waypoint*> wayPositions;
    //判斷2個點是否靠近
    bool collisionWithCircle(cocos2d::Vec2 circlePoint,float radius,cocos2d::Vec2 circlePointTwo, float radiusTwo);

這裏代碼,也對以前代碼進行了修改重構,好比把以前在init()方法裏聲明的dsSprite(色狼)改到了這裏變成了一個全局變量,由於在後續的update方法中須要對它進行操做。

第二步:在MainScene.cpp的init()方法中建立路徑點集合編寫以下代碼

……

   //得到色狼大叔的高
   float dsh=dsSprite->getTextureRect().size.height;

//初始化地圖路徑點集合
    this->wayPositions = Vector<Waypoint*>();

    //添加地圖1號路徑點到集合中
    Waypoint *waypoint1=Waypoint::nodeWithTheLocation(Point(920, 435+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        //設置下一個節點
        waypoint1->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint1);
    //添加地圖2號路徑點到集合中
    Waypoint *waypoint2=Waypoint::nodeWithTheLocation(Point(762, 435+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint2->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint2);
    //添加地圖3號路徑點到集合中
    Waypoint *waypoint3=Waypoint::nodeWithTheLocation(Point(762, 360+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint3->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint3);
    //添加地圖4號路徑點到集合中
    Waypoint *waypoint4=Waypoint::nodeWithTheLocation(Point(685, 360+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint4->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint4);
    //添加地圖5號路徑點到集合中
    Waypoint *waypoint5=Waypoint::nodeWithTheLocation(Point(685, 116+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint5->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint5);
    //添加地圖6號路徑點到集合中
    Waypoint *waypoint6=Waypoint::nodeWithTheLocation(Point(520, 116+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint6->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint6);
    //添加地圖7號路徑點到集合中
    Waypoint *waypoint7=Waypoint::nodeWithTheLocation(Point(520, 180+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint7->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint7);
    //添加地圖8號路徑點到集合中
    Waypoint *waypoint8=Waypoint::nodeWithTheLocation(Point(285, 180+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint8->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint8);
    //添加地圖9號路徑點到集合中
    Waypoint *waypoint9=Waypoint::nodeWithTheLocation(Point(285, 268+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint9->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint9);
    //添加地圖10號路徑點到集合中
    Waypoint *waypoint10=Waypoint::nodeWithTheLocation(Point(204, 268+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint10->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint10);
    //添加地圖11號路徑點到集合中
    Waypoint *waypoint11=Waypoint::nodeWithTheLocation(Point(204, 350+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint11->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint11);
    //添加地圖12號路徑點到集合中
    Waypoint *waypoint12=Waypoint::nodeWithTheLocation(Point(50, 350+dsh/2.0f));
    if(this->wayPositions.size()>0)
    {
        waypoint12->setNextWaypoint(this->wayPositions.back());
    }
    this->wayPositions.pushBack(waypoint12);
……

第三步:在MainScene.cpp的init()方法中初始化幾個變量以及精靈的初始位置編寫以下代碼

 ……
   //獲取集合中的最後一個點,12號點
    Waypoint *waypoint0=wayPositions.back();
    //設置運動的開始點
    beginningWaypoint=waypoint0;
    //設置運動的目標點爲12號點的下一個點,11號點
    destinationWaypoint=waypoint0->getNextWaypoint();
    //設置色狼當前位置值
    myPosition=waypoint0->getMyPosition();
    //設置色狼在地圖的初始位置
    dsSprite->setPosition(myPosition);
    //設置女主角在地圖的初始位置,爲集合中的1號點
    nhSprite->setPosition(wayPositions.front()->getMyPosition());
//設置移動速度
    this->walkingSpeed=0.2f;
    //定時器
    this->scheduleUpdate();
……

第四步:在MainScene.cpp中對collisionWithCircle方法進行實現編寫以下代碼

//判斷2個圓點是否靠近
//circlePoint:第一個圓點座標
//radius:第一個圓半徑
//circlePointTwo:第二個圓點座標
//radiusTwo:第二個圓半徑
bool MainScene::collisionWithCircle(cocos2d::Vec2 circlePoint,float radius,cocos2d::Vec2 circlePointTwo, float radiusTwo)
{
    //2點間距離公式計算
    float xdif = circlePoint.x - circlePointTwo.x;
    float ydif = circlePoint.y - circlePointTwo.y;
    float distance = sqrt(xdif * xdif + ydif * ydif);

    if(distance <= radius + radiusTwo) 
    {
        return true;
    }
    return false;
}

第五步:在MainScene.cpp中對update方法進行實現:

//判斷色狼大叔是否和目標點碰到
    if (this->collisionWithCircle(myPosition,1,destinationWaypoint->getMyPosition(),1) )
    {
        //是否還有下一個目標點
        if (destinationWaypoint->getNextWaypoint())
        {
            //從新設定開始點和目標點
            beginningWaypoint=destinationWaypoint;
            destinationWaypoint=destinationWaypoint->getNextWaypoint();
        } 
    }
    //獲取目標點的座標
    Point targetPoint=destinationWaypoint->getMyPosition();
    //計算目標點的向量
    Point normalized=Point(targetPoint.x-myPosition.x,targetPoint.y-myPosition.y).getNormalized();

    //根據速度和向量分別計算x,y方式上的偏移值
    float ox=normalized.x * walkingSpeed;
    float oy=normalized.y *walkingSpeed;
    myPosition = Point(myPosition.x + ox, myPosition.y +oy);
    //從新設定色狼的位置實現移動
    dsSprite->setPosition(myPosition);

第六步:運行測試遊戲效果,看看色狼會不會沿着咱們設定的路徑移動。

Windows下的效果

在看看android手機下的效果,把新加的2個cpp文件添加到Android.mk文件裏而後開始編譯打包so文件(不會請參考:Cocos2dx.3x入門三部曲-Hello Game項目解析(三)篇)。完成打包後在eclipse中鏈接手機運行看到以下效果:

發如今真機上運行時女主角、色狼的位置至關於道路都有點偏上了,而且好像背景地圖也沒有顯示全背景的頂部和底部有一部分沒有顯示出來,可是在windows下運行確正常,這個是什麼緣由呢,該怎麼調整呢?個人手機分辨率是:960x540 而咱們的地圖素材圖片分辨率是:960x640,因此致使了這個問題,這個是關於不一樣手機屏幕分辨率適配問題,在下一篇中咱們將繼續的完善修改這個遊戲原型解決這個問題。

做者交流QQ:2303452599

           郵箱:mymoney1001@126.com

相關文章
相關標籤/搜索