今天開始做戰鬥,回合制戰鬥程式碼實現第四篇 刀塔傳奇戰鬥模式(即時卡牌戰鬥模式)
說是即時卡牌戰鬥,其實在我看來這種玩法也是回合制戰鬥的一種,差不多算是九宮格戰鬥的一種變種,在一個回合120秒內,分成了3次小規模的遇怪自動戰鬥,而這種自動戰鬥不在是回合而是即時的,但整個戰鬥過程,都不是做即時通訊的,所以對網路要求也不是很高,一般2d網路就可以很流暢的玩這種弱聯網的手遊,所以近期這種模式成為了手機網遊的比較流行的一種戰鬥模式,當然卡牌類遊戲還有幾種比如就秒開源的暗黑世界類,採用這種戰鬥方式的手遊現在有去吧比卡丘這樣的作品,再就是益智類戰鬥卡牌代表作品爐石傳說,這兩種卡牌的實現方式我們後面來研究(本來許諾的仙劍demo3d回合戰鬥我們先等等),循例我們先看看這種戰鬥模式是什麼樣子的。
1.站位:英雄出場的位置應該在數值表中進行了固定的配置,英雄移動差不多遵循,選定技能---選定技能目標----判斷技能攻擊距離-----尋路,
2.出手:
1) 第一次出手順序:除了有白虎這種一出場就buff的,一般都是固定。那麼做法有兩種,一種是英雄表中配置出手權重值,還有一種就是出場陣容中計算出手順序。
2) 英雄技能出手順序:這個是根據英雄資料表中配置的,如普通攻擊→第二個技能→第三個技能→第四個技能→普通攻擊→第二個技能,被動技能則是戰鬥一開始就釋放,效果持續到戰鬥結束。
3) 英雄攻擊間隔:實際上刀塔傳奇中是沒有攻擊速度這一概念的,那麼果斷推測每個英雄的攻擊間隔應該都是固定。即英雄出手順序,普攻,小技能之間的間隔也是固定的。有個特例就是手動釋放的大招,在這裡英雄普攻之後,也能立即釋放大招,沒有間隔。那麼釋放完大招後,攻擊間隔怎樣算,我的理解是大招重新整理了攻擊間隔,英雄開始重複之前的攻擊間隔。(不過新手遊《那年那兔那些事》加入了攻擊速度這個概念,所以並不是絕對的)
3. 狀態及buff
dota和刀塔傳奇的魅力就在這裡,幾十種狀態,幾十種buff。
我們把戰鬥場景中的會造成模型動作改變的稱為狀態,而只造成屬性上改變的稱為buff,這個概念在這裡再提一下。一個英雄的狀態是唯一性的,buff卻有很多種。例如,一個英雄同時中了眩暈和毒技能,那麼這個時候他擁有一個眩暈的顯示狀態,以及眩暈帶來的不可攻擊不可釋放技能不可行動的buff和毒技能帶來的減速和持續掉血的buff。
其中刀塔傳奇還做了一個特殊的狀態,就是受擊,當敵方英雄對此英雄造成的傷害超過到最大血量的一定百分比的時候,此英雄處於受擊狀態,並重新整理攻擊間隔。
整個戰鬥流程可以歸為,出場->站位->出手->狀態及buff改變->結束戰鬥,
以上這些只是刀塔傳奇一些皮毛的分析,因為如果做大規模的分析可能要分析很多問題,拿站位來說,那年那兔那些事裡面的火力支援大招經常會出現攻擊位置判定跟敵人位置不同步的情況,經常出現大招放完,沒有對敵人造成傷害,這種情況可能刀塔傳奇和其他類似的遊戲也有,我們這裡就不討論了,因為這個到底是什麼情況,可能只有遊戲的當值策劃才能說的明白,在製作遊戲過程中,可能需要策劃進行長時間討論和除錯才能確定。
好了,我們現在開始搞乾貨,如果就是上面這幾行網上也能看到的文字,可能觀眾要罵人了,當然這也不符合我部落格的習慣,既然是講做戰鬥,當然要做了,所以下面我們看看如何實現這種戰鬥模式,(事先說明,程式碼不是我寫的,程式碼來自於網路,作者不詳,我這裡只貼出程式碼和相應的講解,無法提供程式碼工程,需要工程請度娘自尋,或者私信留言給我,不是我小氣,因為不是我的程式碼可能涉及一些商業利益等問題,所以沒辦法開源共享),原作者的開發環境是winxp+vs2012+cocos2dx3.0,因為xp系統的編譯和win7以後的編譯有差別,直接編譯會報錯,所以我對程式碼做了重構(開始想過直接修改平臺工具集的選項,不過報error LNK2038: 檢測到“_MSC_VER”的不匹配項: 值“1700”不匹配,重新生成解決方案也沒有成功,所以只好重新搭建工程,編譯器改變為v110),並且升級引擎(我電腦裡沒有裝cocos2dx3.0版本)改為了win764位+vs2012+cocos2dx3.5,因為引擎升級到了cocos2dx3.5,所以適當的修改了些程式碼,好了廢話就到這我們開始。
程式碼的目錄結構非常清晰,注意開發語言是c++11(有興趣的小夥伴可以改成lua或者js等等),
程式碼可以說寫的非常完整,戰鬥需要實現的東西都實現了,下面我們就來看看這個東東到底是怎麼寫的,.H檔案就不貼出來,我們直接帖Cpp,如果真對這段程式碼感興趣,可以百度或者私信給我,我們開始,首先建好工程,之後我們開始程式碼之旅
先是AppDelegate這個類不用多說吧,cocos2dx生成之後需要這個來做啟動,如果不懂請去看cocos2dx基礎,我們直接看程式碼
#include "AppDelegate.h"
//#include "HelloWorldScene.h"
#include "GameScene.h"
#include "LoadScene.h"
#include "OverScene.h"
USING_NS_CC;
AppDelegate::AppDelegate() {
}
AppDelegate::~AppDelegate()
{
}
bool AppDelegate::applicationDidFinishLaunching() {
// initialize director
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
if(!glview) {
glview = GLViewImpl::create("GameDT");
// glview = GLView::create("My Game");
glview->setFrameSize(600, 360);
director->setOpenGLView(glview);
}
// turn on display FPS
director->setDisplayStats(true);
// set FPS. the default value is 1.0/60 if you don't call this
director->setAnimationInterval(1.0 / 60);
glview->setDesignResolutionSize(800, 480, kResolutionExactFit);
// create a scene. it's an autorelease object
// auto scene = HelloWorld::createScene();
//auto scene = GameScene::createScene();
auto scene = LoadScene::createScene();
//auto scene = OverScene::createScene();
// run
director->runWithScene(scene);
return true;
}
// This function will be called when the app is inactive. When comes a phone call,it's be invoked too
void AppDelegate::applicationDidEnterBackground() {
Director::getInstance()->stopAnimation();
// if you use SimpleAudioEngine, it must be pause
// SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
}
// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground() {
Director::getInstance()->startAnimation();
// if you use SimpleAudioEngine, it must resume here
// SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
}
這個類程式碼沒什麼可說的,建立工程自動就可以生成,我們修改的除了包含標頭檔案之外,其實最重要的只有改auto scene = LoadScene::createScene();這句,因為cocos2dx工作的方式是,AppDelegate這個類啟動一個Scene類,然後建立很多Layer類,然後把畫出的Layer新增到Scene上,所以我們現在開始看scene類,在我們這叫做LoadScene
#include "LoadScene.h"
#include "GameScene.h"
LoadScene::LoadScene()
{
numOfRes = 12;
numOfLoadedRes = 0;
}
Scene* LoadScene::createScene()
{
auto scene = Scene::create();
auto layer = LoadScene::create();
scene->addChild(layer);
return scene;
}
bool LoadScene::init()
{
if (!Layer::init())
{
return false;
}
auto size = Director::getInstance()->getWinSize();
labelLoad = Label::createWithTTF("loading...", "fonts/Marker Felt.ttf", 25);
labelPercent = Label::createWithTTF("0", "fonts/Marker Felt.ttf", 25);
labelLoad->setPosition(size.width/2, size.height/2 -140);
labelPercent->setPosition(size.width/2, size.height/2 -100);
this->addChild(labelLoad);
this->addChild(labelPercent);
this->loadRes();
return true;
}
//非同步載入
void LoadScene::loadRes()
{
//載入精靈動畫
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/magic.plist");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/magic2.plist");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/magic3.plist");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/magic4.plist");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/monster1.plist");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/monster2.plist");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/monster3.plist");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/monster4.plist");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/monster5.plist");
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("plist/monster6.plist");
TextureCache::getInstance()->addImageAsync("plist/magic.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
TextureCache::getInstance()->addImageAsync("plist/magic2.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
TextureCache::getInstance()->addImageAsync("plist/magic3.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
TextureCache::getInstance()->addImageAsync("plist/magic4.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
TextureCache::getInstance()->addImageAsync("plist/monster1.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
TextureCache::getInstance()->addImageAsync("plist/monster2.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
TextureCache::getInstance()->addImageAsync("plist/monster3.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
TextureCache::getInstance()->addImageAsync("plist/monster4.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
TextureCache::getInstance()->addImageAsync("plist/monster5.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
TextureCache::getInstance()->addImageAsync("plist/monster6.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
//載入地圖
TextureCache::getInstance()->addImageAsync("map/bbg_spring2.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
//戰鬥場景元件
TextureCache::getInstance()->addImageAsync("card/panel.png",
CC_CALLBACK_1(LoadScene::loadCallback, this));
}
void LoadScene::loadCallback(Texture2D* texture)
{
numOfLoadedRes++;
char tmp[10];
sprintf(tmp, "%d", (int)(((float)numOfLoadedRes/numOfRes) * 100));
labelPercent->setString(tmp);
if (numOfLoadedRes == numOfRes)
{
//this->removeChild(labelLoad, true);
//this->removeChild(labelPercent, true);
this->scheduleOnce(schedule_selector(LoadScene::toStartScene), 1.0f);
}
}
void LoadScene::toStartScene(float dt)
{
auto gameScene = GameScene::createScene();
Director::getInstance()->replaceScene(TransitionCrossFade::create(0.5f, gameScene));
}
這個類看上去程式碼量不大,是幹什麼用的呢?其實是畫進度條,要實現的東西也很簡單,畫一個loading文字,然後,畫一個動態0到100的遞增,那下面的程式碼是幹什麼的呢?我們看toStartScene方法是跳轉到我們真正的遊戲Scene介面類GameScene,而loadRes就是非同步載入資源,當然別忘了回撥函式loadCallback,函式裡numOfLoadedRes == numOfRes就是載入完成時,呼叫跳轉方法toStartScene,好了我們看下一個類GameScene
#include "GameScene.h"
//#include "json/document.h"
//#include "GameInstance.h"
GameScene::GameScene()
{
}
GameScene::~GameScene()
{
}
Scene* GameScene::createScene()
{
auto scene = Scene::create();
auto layer = GameScene::create();
scene->addChild(layer);
return scene;
}
bool GameScene::init()
{
if (!Layer::init())
{
return false;
}
this->initMap();
spriteSystem = SpriteSystem::create();
this->addChild(spriteSystem, 1);
/*magicSystem = MagicSystem::create();
this->addChild(magicSystem, 2);*/
return true;
}
void GameScene::initMap()
{
auto size = Director::getInstance()->getVisibleSize();
auto texMap1 = TextureCache::getInstance()->getTextureForKey("map/bbg_spring2.png");
auto map1 = Sprite::createWithTexture(texMap1);
map1->setPosition(size.width/2, size.height/2);
this->addChild(map1);
}
真是一個類比一個類簡單,這個類看上去程式碼更少,我們看看都幹了什麼東東,
this->initMap();
spriteSystem = SpriteSystem::create();
this->addChild(spriteSystem, 1);看過之後發現init這3行最重要,具體工作是,initMap載入繪製地圖,其實就是畫一張背景圖,然後對spriteSystem類進行初始化,然後把這個Layer類繪製到Scene上面,下面我們開始研究這個這段程式碼中最重要的類,
#include "SpriteSystem.h"
#include "GameInstance.h"
#include "Magic.h"
#include "OverScene.h"
#include "Resource.h"
SpriteSystem::SpriteSystem()
{
m_bIsAttack = false;
m_fElapseTime = 0;
}
SpriteSystem::~SpriteSystem()
{
}
bool SpriteSystem::init()
{
if (!Layer::init())
{
return false;
}
m_bIsAttack = false;
m_fElapseTime = 0;
m_fOverTime = 0;
bool spSel[6] = {true, true, true, true, true, true};
for (unsigned int i = 0; i < 6; i++)
{
GameInstance::getInstance()->spSel[i] = spSel[i];
m_fOldScheTime[i] = 0;
m_fScheduleTime[i] = 0;
}
GameInstance::getInstance()->heroNum = 3;
GameInstance::getInstance()->enemyNum = 3;
this->readJson(spConf);
this->readMagicJson(magConf);
this->initSprite();
this->setMagicPanel();
return true;
}
_stSpriteConfig SpriteSystem::readConfig(SpriteId id)
{
_stSpriteConfig tmp;
tmp = spConf[id-1];
return tmp;
}
void SpriteSystem::readJson(_stSpriteConfig (&spConf)[6])
{
//_stSpriteConfig spConf[6];
rapidjson::Document doc;
std::string str = FileUtils::getInstance()->getStringFromFile(s_spConfJson);
doc.Parse<rapidjson::kParseDefaultFlags>(str.c_str());
for(unsigned int i = 0; i < 6; i++)
{
rapidjson::Value &val =doc[(rapidjson::SizeType)(i)];
if (val.HasMember("id")&&val.HasMember("name")&&val.HasMember("type")&&val.HasMember("life")&&
val.HasMember("attack")&&val.HasMember("attackSpeed")&&val.HasMember("power")&& val.HasMember("magicGroup"))
{
spConf[i].id = (SpriteId)val["id"].GetInt();
spConf[i].name = val["name"].GetString();
spConf[i].type = (SpriteType)val["type"].GetInt();
spConf[i].life = val["life"].GetInt();
spConf[i].attack = val["attack"].GetInt();
spConf[i].attackSpeed = val["attackSpeed"].GetDouble();
spConf[i].power = val["power"].GetInt();
}
rapidjson::Value& num = val["stateNum"][(rapidjson::SizeType)0];
spConf[i].stateNum.idleNum = num["idleNum"].GetInt();
spConf[i].stateNum.runNum = num["runNum"].GetInt();
spConf[i].stateNum.attackNum = num["attackNum"].GetInt();
spConf[i].stateNum.hurtNum = num["hurtNum"].GetInt();
spConf[i].stateNum.deadNum = num["deadNum"].GetInt();
rapidjson::Value& group = val["magicGroup"][(rapidjson::SizeType)0];
spConf[i].magicGroup.magic1 = (MagicId)group["magic1"].GetInt();
spConf[i].magicGroup.magic2 = (MagicId)group["magic2"].GetInt();
}
//return spConf;
}
_stMagicConfig SpriteSystem::readMagConfig(MagicId id)
{
_stMagicConfig tmp;
tmp = magConf[id-1];
return tmp;
}
void SpriteSystem::readMagicJson(_stMagicConfig (&magConf)[11])
{
rapidjson::Document doc;
std::string str = FileUtils::getInstance()->getStringFromFile(s_spMagJson);
doc.Parse<rapidjson::kParseDefaultFlags>(str.c_str());
for (unsigned int i = 0; i < 11; i++)
{
rapidjson::Value &val =doc[(rapidjson::SizeType)(i)];
if (val.HasMember("id")&&val.HasMember("name")&&val.HasMember("count"))
{
magConf[i].id = (MagicId)val["id"].GetInt();
magConf[i].name = val["name"].GetString();
magConf[i].count = val["count"].GetInt();
}
}
}
void SpriteSystem::update(float dt)
{
float fOldTime = m_fElapseTime;
m_fElapseTime = m_fElapseTime + (dt*1000);
if(fOldTime < 5000.0f && m_fElapseTime >= 5000.0f && GameInstance::getInstance()->isGameOver == false)
{
m_bIsAttack = true;
}
if (GameInstance::getInstance()->isGameOver == false && m_bIsAttack == true)
{
this->spAttackSchedule(dt);
}
for (unsigned int i = 0; i < 6; i++)
{
if (m_bsSp[i]->getCurLife() < 0 && m_bsSp[i]->getSpriteState() != SPRITESTATE_DEAD)
{
m_bsSp[i]->startAction(SPRITESTATE_DEAD);
if (i < 3)
{
m_itItem[i]->setEnabled(false);
m_ptLife[i]->setPercentage(0);
m_ptPower[i]->setPercentage(0);
//CCLOG("Set false%d", m_ptPower[i]->getPercentage());
}
if (m_bsSp[i]->getSpConf().id == SPRITEID_1 ||m_bsSp[i]->getSpConf().id == SPRITEID_2 || m_bsSp[i]->getSpConf().id == SPRITEID_3)
{
GameInstance::getInstance()->heroNum--;
}
else
{
GameInstance::getInstance()->enemyNum--;
}
}
}
if (GameInstance::getInstance()->isGameOver == false)
{
if (GameInstance::getInstance()->heroNum == 0)
{
GameInstance::getInstance()->isWin = false;
GameInstance::getInstance()->isGameOver = true;
m_fOverTime = m_fElapseTime;
}
if (GameInstance::getInstance()->enemyNum == 0)
{
GameInstance::getInstance()->isWin = true;
GameInstance::getInstance()->isGameOver = true;
m_fOverTime = m_fElapseTime;
}
}
if (GameInstance::getInstance()->isGameOver == true)
{
if (m_fElapseTime >= m_fOverTime + 2000.0f)
{
unscheduleUpdate();
Director::getInstance()->replaceScene(TransitionCrossFade::create(1.5f, OverScene::createScene()));
}
}
for (unsigned int i = 0; i < 6; i++)
{
if (m_bsSp[i]->getSpriteState() != SPRITESTATE_DEAD)
{
if (i < 3)
{
this->ergodicSprite(m_bsSp[i], m_bsSp[3], m_bsSp[4], m_bsSp[5]);
}
else
{
this->ergodicSprite(m_bsSp[i], m_bsSp[0], m_bsSp[1], m_bsSp[2]);
}
}
else if(i < 3)
{
m_itItem[i]->setEnabled(false);
}
}
for (unsigned int i = 0; i < 3; i++)
{
if (m_bsSp[i]->getSpriteState() != SPRITESTATE_DEAD)
{
this->showBottomLife(m_bsSp[i]);
this->showPower(m_bsSp[i]);
}
}
}
void SpriteSystem::initSprite()
{
struct _stSpConf
{
float spScale[6];
Point spPosStart[6];
Point spPosEnd[6];
bool spFlip[6];
_stSpConf()
{
spScale[0] = 0.8f; spScale[1] = 1.0f; spScale[2] = 1.0f;
spScale[3] = 1.0f; spScale[4] = 1.0f; spScale[5] = 1.0f;
spFlip[0] = true; spFlip[1] = true; spFlip[2] = true;
spFlip[3] = false; spFlip[4] = false; spFlip[5] = false;
spPosStart[0] = PointS_1; spPosStart[1] = PointS_2; spPosStart[2] = PointS_3;
spPosStart[3] = PointS_4; spPosStart[4] = PointS_5; spPosStart[5] = PointS_6;
spPosEnd[0] = PointE_1; spPosEnd[1] = PointE_2; spPosEnd[2] = PointE_3;
spPosEnd[3] = PointE_4; spPosEnd[4] = PointE_5; spPosEnd[5] = PointE_6;
}
}_stSpConf;
for (unsigned int i = 0; i < 6; i++)
{
if (GameInstance::getInstance()->spSel[i])
{
m_bsSp[i] = BaseSprite::create(spConf[i]);
m_bsSp[i]->setScale(_stSpConf.spScale[i]);
m_bsSp[i]->setPosition(_stSpConf.spPosStart[i]);
m_bsSp[i]->setFlippedX(_stSpConf.spFlip[i]);
m_bsSp[i]->startAction(SPRITESTATE_IDLE);
m_bsSp[i]->move(MoveType_To, _stSpConf.spPosEnd[i], 4.5f);
//m_fScheduleTime[i] = m_bsSp[i]->getSpConf().attackSpeed;
i < 3 ? m_bsSp[i]->setTargetSp(m_bsSp[3]) : m_bsSp[i]->setTargetSp(m_bsSp[0]);
this->addChild(m_bsSp[i], 1);
}
}
scheduleUpdate();
}
void SpriteSystem::spAttackSchedule(float dt)
{
for (unsigned int i = 0; i <6; i++)
{
//釋放魔法狀態,將定時器時間設為0
if (m_bsSp[i]->getIsMagic() == true)
{
m_fScheduleTime[i] = 0;
}
//普通攻擊狀態
if (m_bsSp[i]->getSpriteState() != SPRITESTATE_DEAD && m_bsSp[i]->getIsMagic() == false)
{
m_fOldScheTime[i] = m_fScheduleTime[i];
m_fScheduleTime[i] += dt;
if (m_fOldScheTime[i] < m_bsSp[i]->getSpConf().attackSpeed && m_fScheduleTime[i] >= m_bsSp[i]->getSpConf().attackSpeed)
{
m_bsSp[i]->startAction(SPRITESTATE_ATTACK);
//遠距離攻擊
if (m_bsSp[i]->getSpConf().type != SPRITETYPE_FRONT)
{
this->magicMove(m_bsSp[i]->getSpConf().magicGroup.magic1, m_bsSp[i], m_bsSp[i]->getTargetSp());
}
//直接攻擊
else
{
m_bsSp[i]->getTargetSp()->setCurLife(m_bsSp[i]->getTargetSp()->getCurLife() - m_bsSp[i]->getSpConf().attack);
this->showLife(m_bsSp[i]->getTargetSp());
if (m_bsSp[i]->getCurPower() <= m_bsSp[i]->getSpConf().power)
{
m_bsSp[i]->setCurPower(m_bsSp[i]->getCurPower() + m_bsSp[i]->getSpConf().attack*2);
}
}
m_fOldScheTime[i] = 0;
m_fScheduleTime[i] = 0;
}
}
}
}
void SpriteSystem::magicMove(MagicId id, BaseSprite* attackSprite, BaseSprite* hurtSprite)
{
auto attackPos = attackSprite->getPosition();
auto hurtPos = hurtSprite->getPosition();
auto size = Director::getInstance()->getVisibleSize().width;
std::string name = GameInstance::getInstance()->getMagicName(id);
name += "_001.png";
Sprite* magic = CCSprite::createWithSpriteFrameName(name.c_str());
magic->setScale(0.6f);
if (id == MAGIC_9)
{
magic->setRotation(-90);
}
magic->setPosition(attackPos);
this->addChild(magic, 2);
float s = ccpDistance(attackPos, hurtPos);
float v = 300.0f;
auto move = MoveTo::create(s/v, hurtPos);
auto callfunc = CallFunc::create(std::bind(&SpriteSystem::magicMoveCallback, this, magic,attackSprite));
magic->runAction(Sequence::create(move, callfunc, NULL));
}
void SpriteSystem::magicMoveCallback(Node* node, BaseSprite* sp)
{
node->setVisible(false);
this->removeChild(node);
if (sp)
{
sp->getTargetSp()->setCurLife(sp->getTargetSp()->getCurLife() - sp->getSpConf().attack);
this->showLife(sp->getTargetSp());
if (sp->getCurPower() <= sp->getSpConf().power)
{
sp->setCurPower(sp->getCurPower() + sp->getSpConf().attack*2);
}
}
}
void SpriteSystem::showLife(BaseSprite* sp)
{
auto startPos = Point(sp->getPositionX(), sp->getPositionY() + sp->getContentSize().height/2 + 5);
auto endPos = Point(sp->getPositionX(),sp->getPositionY() + sp->getContentSize().height/2 + 20);
//資源尺寸不一導致
if (sp->getSpConf().id == 1)
{
startPos = Point(sp->getPositionX() - 30, sp->getPositionY() + sp->getContentSize().height/2 + 5 - 90);
endPos = Point(sp->getPositionX() - 30,sp->getPositionY() +sp->getContentSize().height/2 + 20 - 90);
}
if (sp->getSpConf().id == 6)
{
startPos = Point(sp->getPositionX(), sp->getPositionY() + sp->getContentSize().height/2 - 30);
endPos = Point(sp->getPositionX(),sp->getPositionY() +sp->getContentSize().height/2 -15);
}
float per = float(sp->getCurLife() )/ (float)sp->getSpConf().life;
Node* node = Node::create();
Sprite* lifebg = Sprite::create(s_ptBg);
ProgressTimer* life = ProgressTimer::create(Sprite::create(sp->getSpConf().id<3 ? s_ptLife : s_ptLife2));
life->setType(ProgressTimer::Type::BAR);
life->setMidpoint(Point::ANCHOR_MIDDLE_LEFT);
life->setBarChangeRate(Point::ANCHOR_MIDDLE_RIGHT);
life->setPercentage(per*100);
node->setPosition(startPos);
node->addChild(lifebg, 0);
node->addChild(life, 1);
this->addChild(node, 2);
auto moveUp = MoveTo::create(0.5f, endPos);
auto fadeout = FadeOut::create(0.5f);
auto spawn = Spawn::create(moveUp, fadeout, NULL);
auto callfunc = CallFunc::create(std::bind(&SpriteSystem::showLifeCallback, this, node));
node->runAction(Sequence::create(spawn, callfunc, NULL));
}
void SpriteSystem::showLifeCallback(Node* node)
{
node->setVisible(false);
this->removeChild(node);
}
void SpriteSystem::ergodicSprite(BaseSprite* attack,BaseSprite* tmp1, BaseSprite* tmp2, BaseSprite* tmp3)
{
BaseSprite* tmps[3] = {tmp1, tmp2, tmp3};
for (unsigned int i = 0; i < 3; i++)
{
if (attack && tmps[i] && tmps[i]->getSpriteState() != SPRITESTATE_DEAD)
{
attack->setTargetSp(tmps[i]);
break;
}
}
}
void SpriteSystem::setMagicPanel()
{
Sprite* panel = Sprite::create(s_magPan);
panel->setPosition(400, 90);
struct _stMagPan
{
float scale[3];
Point spPos[3];
bool enable[3];
Point ptPos[6];
_stMagPan()
{
scale[0] = 0.8f; scale[1] = 0.8f; scale[2] = 0.8f;
spPos[0] = Point(534, 90); spPos[1] = Point(318, 90); spPos[2] = Point(106, 90);
enable[0] = false; enable[1] = false; enable[2] = false;
ptPos[0] = Point(106, 30); ptPos[1] = Point(318, 30); ptPos[2] = Point(534, 30);
ptPos[3] = Point(106, 10); ptPos[4] = Point(318, 10); ptPos[5] = Point(534, 10);
}
}_stMagPan;
Sprite* spNor[3];
Sprite* spDis[3];
Sprite* life[3];
Sprite* power[3];
Sprite* ptBg[6];
for (unsigned int i = 0; i < 3; i++)
{
//英雄頭像設為按鈕
spNor[i] = Sprite::create(s_panSpNor[i]);
spDis[i] = Sprite::create(s_panSpDis[i]);
m_itItem[i] = MenuItemSprite::create(spNor[i], spNor[i], spDis[i], std::bind(&SpriteSystem::itemCallback, this, std::placeholders::_1,i));
m_itItem[i]->setScale(_stMagPan.scale[i]);
m_itItem[i]->setPosition(_stMagPan.spPos[i]);
m_itItem[i]->setEnabled(_stMagPan.enable[i]);
//生命值,魔法值
m_ptLife[i] = ProgressTimer::create(Sprite::create(s_ptLife));
m_ptLife[i]->setAnchorPoint(Point::ANCHOR_BOTTOM_LEFT);
m_ptLife[i]->setType(ProgressTimer::Type::BAR);
m_ptLife[i]->setMidpoint(Point::ANCHOR_MIDDLE_LEFT);
m_ptLife[i]->setBarChangeRate(Point::ANCHOR_MIDDLE_RIGHT);
m_ptLife[i]->setPercentage(100);
m_ptPower[i] = ProgressTimer::create(Sprite::create(s_ptPower));
m_ptPower[i]->setAnchorPoint(Point::ANCHOR_BOTTOM_LEFT);
m_ptPower[i]->setType(ProgressTimer::Type::BAR);
m_ptPower[i]->setMidpoint(Point::ANCHOR_MIDDLE_LEFT);
m_ptPower[i]->setBarChangeRate(Point::ANCHOR_MIDDLE_RIGHT);
m_ptPower[i]->setPercentage(0);
}
auto menu = Menu::create(m_itItem[0], m_itItem[1], m_itItem[2], NULL);
menu->setPosition(Point::ZERO);
panel->addChild(menu,1);
for (unsigned int i = 0; i < 6; i++)
{
ptBg[i] = Sprite::create(s_ptBg);
ptBg[i]->setPosition(_stMagPan.ptPos[i]);
panel->addChild(ptBg[i]);
}
ptBg[0]->addChild(m_ptLife[0],2);
ptBg[1]->addChild(m_ptLife[1],2);
ptBg[2]->addChild(m_ptLife[2],2);
ptBg[3]->addChild(m_ptPower[0],2);
ptBg[4]->addChild(m_ptPower[1],2);
ptBg[5]->addChild(m_ptPower[2],2);
this->addChild(panel, 1);
}
void SpriteSystem::itemCallback(Ref* pSender,int Num)
{
m_itItem[Num]->setEnabled(false);
CCLOG("Num%d", Num);
m_bsSp[Num]->setCurPower(0);
m_bsSp[Num]->startAction(SPRITESTATE_MAGIC);
this->startMagic(m_bsSp[Num], m_bsSp[Num]->getSpConf().magicGroup.magic2, 3);
}
void SpriteSystem::showBottomLife(BaseSprite* sprite)
{
float per = (((float)sprite->getCurLife()/sprite->getSpConf().life)*100);
SpriteId id = sprite->getSpConf().id;
switch (id)
{
case SPRITEID_1:
m_ptLife[2]->setPercentage(per);
break;
case SPRITEID_2:
m_ptLife[1]->setPercentage(per);
break;
case SPRITEID_3:
m_ptLife[0]->setPercentage(per);
break;
case SPRITEID_4:
break;
case SPRITEID_5:
break;
case SPRITEID_6:
break;
default:
break;
}
}
void SpriteSystem::showPower(BaseSprite* sprite)
{
float per = (((float)sprite->getCurPower()/sprite->getSpConf().power)*100);
SpriteId id = sprite->getSpConf().id;
switch (id)
{
case SPRITEID_1:
m_ptPower[2]->setPercentage(per);
if (per >= 100)
{
m_itItem[0]->setEnabled(true);
}
break;
case SPRITEID_2:
m_ptPower[1]->setPercentage(per);
if (per >= 100)
{
m_itItem[1]->setEnabled(true);
}
break;
case SPRITEID_3:
m_ptPower[0]->setPercentage(per);
if (per >= 100)
{
m_itItem[2]->setEnabled(true);
}
break;
case SPRITEID_4:
break;
case SPRITEID_5:
break;
case SPRITEID_6:
break;
default:
break;
}
}
void SpriteSystem::startMagic(BaseSprite* tmp,MagicId id, int times)
{
Magic* magic = Magic::create(magConf[tmp->getSpConf().magicGroup.magic2-1]);
magic->setPosition(600, 300);
this->addChild(magic, 1);
magic->startMagic(12, times);
this->magicHurt(tmp, m_bsSp[3], m_bsSp[4], m_bsSp[5]);
}
void SpriteSystem::magicHurt(BaseSprite* attack,BaseSprite* tmp1, BaseSprite* tmp2, BaseSprite* tmp3)
{
BaseSprite* tmps[3] = {tmp1, tmp2, tmp3};
for (unsigned int i = 0; i < 3; i++)
{
if (tmps[i] && tmps[i]->getSpriteState() != SPRITESTATE_DEAD)
{
tmps[i]->setCurLife(tmps[i]->getCurLife() - attack->getSpConf().attack);
CCLOG("showlifes");
this->showLife(tmps[i]);
}
}
}
一下程式碼量就變多了,差不多700行,我們慢慢來看,init開始看裡面開始都是些初始化資料
比如GameInstance::getInstance()->heroNum = 3;
GameInstance::getInstance()->enemyNum = 3;關於GameInstance這個靜態類我們後面再來看
this->readJson(spConf);
this->readMagicJson(magConf);
this->initSprite();
this->setMagicPanel();看看這4個方法呼叫,先看看readJson方法,這個方法用來讀取sp精靈的資料(這個資料在網遊中應該由伺服器傳過來),這裡是本地的我們看看
如圖,我們看到這個資料由外部json檔案得到,網路遊戲流程大致也差不多,我們後面以暗黑世界物件看看網路通訊怎麼來解析這個地方,這裡就先不講,具體的解析過程readJson裡面很詳細
接著readMagicJson方法跟上面的讀取方式幾乎相同,,我們看下json檔案裡面寫了什麼東西,先是spriteConfig.json
[
{
"id": 1,
"name": "yemanshouren",
"type": 1,
"life": 100,
"attack": 15,
"attackSpeed": 2.5,
"power": 100,
"magicGroup": [
{
"magic1": 0,
"magic2": 8
}
],
"stateNum": [
{
"idleNum": 1,
"runNum": 6,
"attackNum": 12,
"hurtNum": 1,
"deadNum": 6
}
]
},
{
"id": 2,
"name": "quanjiniao",
"type": 2,
"life": 100,
"attack": 18,
"attackSpeed": 5.2,
"power": 100,
"magicGroup": [
{
"magic1": 9,
"magic2": 3
}
],
"stateNum": [
{
"idleNum": 1,
"runNum": 6,
"attackNum": 10,
"hurtNum": 1,
"deadNum": 3
}
]
},
{
"id": 3,
"name": "duyanguai",
"type": 3,
"life": 100,
"attack": 10,
"attackSpeed": 6.3,
"power": 100,
"magicGroup": [
{
"magic1": 9,
"magic2": 10
}
],
"stateNum": [
{
"idleNum": 1,
"runNum": 8,
"attackNum": 12,
"hurtNum": 1,
"deadNum": 10
}
]
},
{
"id": 4,
"name": "manzutu",
"type": 1,
"life": 100,
"attack": 13,
"attackSpeed": 3.1,
"power": 100,
"magicGroup": [
{
"magic1": 9,
"magic2": 3
}
],
"stateNum": [
{
"idleNum": 1,
"runNum": 6,
"attackNum": 6,
"hurtNum": 1,
"deadNum": 6
}
]
},
{
"id": 5,
"name": "xiaoemo",
"type": 2,
"life": 100,
"attack": 15,
"attackSpeed": 6.3,
"power": 100,
"magicGroup": [
{
"magic1": 5,
"magic2": 1
}
],
"stateNum": [
{
"idleNum": 1,
"runNum": 6,
"attackNum": 10,
"hurtNum": 1,
"deadNum": 12
}
]
},
{
"id": 6,
"name": "jielingqishi",
"type": 3,
"life": 100,
"attack": 18,
"attackSpeed": 7.5,
"power": 100,
"magicGroup": [
{
"magic1": 5,
"magic2": 4
}
],
"stateNum": [
{
"idleNum": 1,
"runNum": 2,
"attackNum": 6,
"hurtNum": 1,
"deadNum": 9
}
]
}
]
之後是magicConfig.json[
{
"id": 1,
"name": "fireblast",
"count": 10
},
{
"id": 2,
"name": "burn",
"count": 6
},
{
"id": 3,
"name": "icepiton",
"count": 8
},
{
"id": 4,
"name": "shocked",
"count": 4
},
{
"id": 5,
"name": "firedevil",
"count": 6
},
{
"id": 6,
"name": "lightstrike",
"count": 6
},
{
"id": 7,
"name": "bossfire",
"count": 4
},
{
"id": 8,
"name": "firedevilblast",
"count": 6
},
{
"id": 9,
"name": "fireball",
"count": 4
},
{
"id": 10,
"name": "bossstore",
"count": 5
},
{
"id": 11,
"name": "magicring",
"count": 1
}
]
下面的載入sprite精靈方法,initSprite我們加個斷點直觀的看一下,一看嚇一跳,原來這個create方法裡面包含了這麼多東西,的確,不過這個東西就是我們上面json裡面的id1的資料,之後的方法我們截個圖來看
把方法摺疊起來一下變得直觀了,我們一個方法一個方法的看,spAttackSchedule方法是一個攻擊的計時器,通過這個計時,來控制自動攻擊的頻率,裡面有個特別的地方就是,當魔法攻擊的時候,計時會歸0,然後等待魔法之後重新開始計時,magicMove和magicMoveCallback兩個方法用來控制攻擊子彈的移動及消亡,因為刀塔傳奇這一類的遊戲,不像格鬥類遊戲攻擊的碰撞盒子安在身上,而是靠子彈(魔法)來攻擊,所以這個方法控制能不能打倒怪物就很重要了,showLife這個就不用說了,傷血之後,頭上會有個血條,那年那兔那些事這樣的遊戲會有個選項,是否顯示,showLifeCallback就是隱藏方法,setMagicPanel是設定魔法的面板方法,畫出面板肯定要有按鍵響應itemCallback就是用來響應我們的輸入的。showBottomLife和showPower是用來畫出和控制魔法控制條的(只有集滿氣才可以釋放),釋放出startMagic技能,當然要判斷傷害了magicHurt,這樣這個類就看完了,雖然看上去程式碼很多,卻並不複雜,當然僅有這個類遊戲還不能正常運轉,這個類裡面用到了幾個靜態類,我們來看看都有哪些,作用又是幹什麼的。
#include "GameInstance.h"
#include "Resource.h"
static GameInstance *s_SharedGameInstance = nullptr;
GameInstance::GameInstance()
{
for (unsigned int i = 0; i <6; i++)
{
spSel[i] = false;
}
isGameOver = false;
isWin = false;
}
GameInstance::~GameInstance()
{
}
GameInstance* GameInstance::getInstance()
{
if (!s_SharedGameInstance)
{
s_SharedGameInstance = new GameInstance();
//s_SharedGameInstance->init();
}
return s_SharedGameInstance;
}
Animate* GameInstance::setAnimate(const char* frameName, int frameCount, int fps, bool restore,int times)
{
using namespace cocos2d;
Vector<SpriteFrame*> frames;
for (int i = 1; i <= frameCount; i++)
{
const char* imgName = String::createWithFormat(frameName, i)->getCString();
SpriteFrame* frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(imgName);
frames.pushBack(frame);
}
auto animation = Animation::createWithSpriteFrames(frames, 1.0f/fps, times);
animation->setRestoreOriginalFrame(restore);
auto animate = Animate::create(animation);
return animate;
}
//MagicConfig GameInstance::readDataBase(MagicId id)
//{
// MagicConfig magicConf;
// sqlite3* pdb;
// std::string path = "database/magic.db3";
// int result;
// char** re;
// int row;
// int col;
//
// result = sqlite3_open(path.c_str(), &pdb);
//
// sqlite3_get_table(pdb, "select * from magicConfig", &re, &row,&col, NULL);
//
// magicConf.id = (MagicId)atoi(re[2*col+0]);
// magicConf.name = re[id*col+1];
// magicConf.count = atoi(re[id*col+2]);
//
// sqlite3_close(pdb);
//
// return magicConf;
//}
std::string GameInstance::getMagicName(MagicId id)
{
_stMagicConfig mgConf;
rapidjson::Document doc;
std::string str = FileUtils::getInstance()->getStringFromFile(s_spMagJson);
doc.Parse<rapidjson::kParseDefaultFlags>(str.c_str());
rapidjson::Value &val =doc[(rapidjson::SizeType)(id-1)];
std::string name = val["name"].GetString();
return name;
}
int GameInstance::getMagicNum(MagicId id)
{
_stMagicConfig mgConf;
rapidjson::Document doc;
std::string str = FileUtils::getInstance()->getStringFromFile(s_spMagJson);
doc.Parse<rapidjson::kParseDefaultFlags>(str.c_str());
rapidjson::Value &val =doc[(rapidjson::SizeType)(id-1)];
int count = val["count"].GetInt();
return count;
}
std::string GameInstance::getSpriteName(SpriteId id)
{
std::string name;
_stSpriteConfig spConf;
rapidjson::Document doc;
std::string str = FileUtils::getInstance()->getStringFromFile(s_spConfJson);
doc.Parse<rapidjson::kParseDefaultFlags>(str.c_str());
rapidjson::Value &val =doc[(rapidjson::SizeType)(id-1)];
if (val.HasMember("name"))
{
name = val["name"].GetString();
}
return name;
}
std::string GameInstance::getStateName(SpriteId id,SpriteState state)
{
std::string str = GameInstance::getInstance()->getSpriteName(id);
switch (state)
{
case SPRITESTATE_IDLE:
str += "_run_%03d.png";
break;
case SPRITESTATE_RUN:
str += "_run_%03d.png";
break;
case SPRITESTATE_ATTACK:
str += "_attack_%03d.png";
break;
case SPRITESTATE_HURT:
str += "_run_%03d.png";
break;
case SPRITESTATE_DEAD:
str += "_dead_%03d.png";
break;
default:
break;
}
return str;
}
這個類很直觀,是一個精靈動畫控制類,可以看到這樣的語句,當xx的時候使用哪張圖片,cocos2dx提供的精靈動畫畫法各有千秋,用哪種自己習慣,#include "Magic.h"
#include "sqlite3.h"
#include "GameInstance.h"
Magic::Magic():
magicAction(nullptr)
{
}
Magic::~Magic()
{
//CC_SAFE_RELEASE_NULL(magicAction);
}
Magic* Magic::create(_stMagicConfig magConfig)
{
Magic* sprite = new Magic();
sprite->magicConf = magConfig;
std::string spriteName = GameInstance::getInstance()->getMagicName(sprite->magicConf.id);
spriteName += "_001.png";
auto spriteFrame = SpriteFrameCache::getInstance()->spriteFrameByName(spriteName);
if (spriteFrame && sprite->initWithSpriteFrame(spriteFrame))
{
sprite->autorelease();
return sprite;
}
else
{
delete sprite;
sprite = NULL;
return NULL;
}
return NULL;
}
void Magic::setMagic()
{
std::string str;
str = this->magicConf.name;
str += "_%03d.png";
this->magicAction = GameInstance::getInstance()->setAnimate(str.c_str(), this->magicConf.count, this->magicFps, false, this->magicTimes);
}
void Magic::startMagic(int fps, int times)
{
this->magicFps = fps;
this->magicTimes = times;
this->setMagic();
Animate* tmp = (Animate*)this->magicAction;
auto callfunc = CallFunc::create(CC_CALLBACK_0(Magic::startMagicCallback, this));
auto sequence = Sequence::create(tmp, callfunc, NULL);
this->runAction(sequence);
}
void Magic::startMagicCallback()
{
this->setVisible(false);
}
void Magic::move(Point endPoint, int duration, float speed)
{
float s= ccpDistance(this->getPosition(), endPoint);
auto move = MoveTo::create(s/speed,endPoint);
this->runAction(move);
}
上面畫出了人物,下面理所應當的應該把魔法畫出來了,魔法精靈控制類就這樣出現了,
#include "OverScene.h"
#include "GameInstance.h"
#include "GameScene.h"
OverScene::OverScene()
{
}
Scene* OverScene::createScene()
{
auto scene = Scene::create();
auto layer = OverScene::create();
scene->addChild(layer);
GameInstance::getInstance()->isGameOver = false;
return scene;
}
bool OverScene::init()
{
if (!Layer::init())
{
return false;
}
auto ui = cocostudio::GUIReader::getInstance()->widgetFromJsonFile("gameover/NewUI4_1.ExportJson");
this->addChild(ui);
Button* back = (Button*)Helper::seekWidgetByName(ui,"back");
Button* fight = (Button*)Helper::seekWidgetByName(ui,"fight");
if (GameInstance::getInstance()->isWin)
{
Sprite* title =Sprite::create("map/stagedetail_raid_title.png");
title->setPosition(400, 240);
this->addChild(title);
}
else
{
Sprite* title = Sprite::create("map/failed_title.png");
title->setPosition(400, 240);
this->addChild(title);
}
back->addTouchEventListener(this, toucheventselector(OverScene::tbackCallback));
fight->addTouchEventListener(this, toucheventselector(OverScene::tfightCallback));
/*Sprite* bg = Sprite::create("map/gameover.png");
bg->setPosition(400, 240);
this->addChild(bg);
if (GameInstance::getInstance()->isWin)
{
Sprite* title =Sprite::create("map/stagedetail_raid_title.png");
title->setPosition(400, 240);
this->addChild(title);
}
else
{
Sprite* title = Sprite::create("map/failed_title.png");
title->setPosition(400, 240);
this->addChild(title);
}
auto back = MenuItemSprite::create(Sprite::create("button/replaybtn.png"), Sprite::create("button/replaybtn-disabled.png"),CC_CALLBACK_1(OverScene::backCallback,this));
back->setPosition(back->getContentSize().width/2, back->getContentSize().height/2);
auto fight = MenuItemSprite::create(Sprite::create("button/prepare_go_battle.png"), Sprite::create("button/prepare_go_battle_press.png"),CC_CALLBACK_1(OverScene::fightCallback,this));
fight->setPosition(800-back->getContentSize().width/2, back->getContentSize().height/2);
auto menu =Menu::create(back, fight, NULL);
menu->setPosition(Point::ZERO);
this->addChild(menu);*/
return true;
}
void OverScene::backCallback(Ref* sender)
{
Director::getInstance()->end();
}
void OverScene::fightCallback(Ref* sender)
{
Director::getInstance()->replaceScene(GameScene::createScene());
}
void OverScene::tbackCallback(Ref* sender, TouchEventType type)
{
switch (type)
{
case TOUCH_EVENT_ENDED:
Director::getInstance()->end();
break;
}
}
void OverScene::tfightCallback(Ref* sender, TouchEventType type)
{
switch (type)
{
case TOUCH_EVENT_ENDED:
Director::getInstance()->replaceScene(GameScene::createScene());
break;
}
}
當戰鬥全都結束了,自然要看戰鬥結束了。好了,到這程式碼差不多快完事了,其中有兩個.H檔案
#ifndef _CONFIG_
#define _CONFIG_
USING_NS_CC;
//精靈初始位置
const Point PointS_1 = Point(355, 317) - Point(380, 0);
const Point PointS_2 = Point(190, 290)- Point(380, 0);
const Point PointS_3 = Point(80, 300)- Point(380, 0);
const Point PointS_4 = Point(445, 300)+ Point(380, 0);
const Point PointS_5 = Point(550, 313)+ Point(380, 0);
const Point PointS_6 = Point(720, 355)+ Point(380, 0);
const Point PointE_1 = Point(355, 317);
const Point PointE_2 = Point(190, 290);
const Point PointE_3 = Point(80, 300);
const Point PointE_4 = Point(445, 300);
const Point PointE_5 = Point(550, 313);
const Point PointE_6 = Point(720, 355);
//精靈的唯一標示
enum SpriteId
{
SPRITEID_1 = 1,
SPRITEID_2,
SPRITEID_3,
SPRITEID_4,
SPRITEID_5,
SPRITEID_6
};
//每個技能的唯一標示
enum MagicId
{
MAGIC_1 = 1,
MAGIC_2,
MAGIC_3,
MAGIC_4,
MAGIC_5,
MAGIC_6,
MAGIC_7,
MAGIC_8,
MAGIC_9,
MAGIC_10,
MAGIC_11,
};
//精靈狀態
enum SpriteState
{
SPRITESTATE_IDLE,
SPRITESTATE_RUN,
SPRITESTATE_ATTACK,
SPRITESTATE_MAGIC,
SPRITESTATE_HURT,
SPRITESTATE_DEAD
};
//精靈型別 前排、中排、後排
enum SpriteType
{
SPRITETYPE_FRONT = 1, //前排
SPRITETYPE_MIDDLE, //中排
SPRITETYPE_BACK //後排
};
enum MoveType
{
MoveType_To,
MoveType_By
};
//精靈每個狀態的動畫數量
struct StateNum
{
int idleNum; //空閒
int runNum; //跑動
int attackNum; //攻擊
int hurtNum; //受傷
int deadNum; //死亡
};
//精靈屬性
struct MagicGroup
{
MagicId magic1;
MagicId magic2;
};
struct _stSpriteConfig
{
SpriteId id; //精靈ID,唯一識別
std::string name; //名字
SpriteType type; //型別,前排、中排、後排
int life; //生命值
int attack; //攻擊力
double attackSpeed; //攻擊速度
int power; //魔法值
MagicGroup magicGroup; //魔法類別,遠距離、直接攻擊
StateNum stateNum; //各動畫幀數
};
//技能屬性
struct _stMagicConfig
{
MagicId id;
std::string name;
int count;
};
struct AttackId
{
bool s1;
bool s2;
bool s3;
};
#endif
#ifndef _RESOURCE_H_
#define _RESOURCE_H_
static const char s_magPan[] = "panel/panel.png";
static const char s_panSpNor[3][64] = {"panel/sprite1.png","panel/sprite2.png","panel/sprite3.png"};
static const char s_panSpDis[3][64] = {"panel/sprite1no.png","panel/sprite2no.png","panel/sprite3no.png"};
static const char s_spConfJson[] = "json/spriteConfig.json";
static const char s_ptBg[] = "button/lifebg2.png";
static const char s_ptLife[] = "button/herolife.png";
static const char s_ptLife2[] = "button/life.png";
static const char s_ptPower[] = "button/power.png";
static const char s_spMagJson[] = "json/magicConfig.json";
#endif
到這裡全部程式碼差不多貼完了,不知道為什麼原作者程式碼裡有沒有用到的東東,我懷疑是這段程式碼可能是從某個專案中拆出來的,因為我認為跟戰鬥沒啥關係,所以就不貼出來,因為沒辦法直接開放原始碼,看過這篇的小夥伴需要程式碼的話,可以自行搜尋,或者私信給我,好了,這篇就到這,我們後面再見。