1. 程式人生 > >cocos2dx3.2開發 RPG《Flighting》(四)預先載入與選人介面

cocos2dx3.2開發 RPG《Flighting》(四)預先載入與選人介面

一、前言

假設你已經看懂了前面三節,那麼接下來我們就開始進入真正的開發啦~~

二、正文

1、預先載入

首先我們先談談預先載入,什麼叫預先載入,就是在資源沒有被用到的時候就先把資源載入到記憶體,等要用的時候直接從記憶體裡面獲取就好。

這樣的好處是當用的時候會省去載入的時間,但是壞處就是佔用一部分記憶體,這是一個時間與空間的選擇問題。

遊戲一開始進入的是MenuScene

bool MenuScene::init(){
	preLoadingSomethings();
	MenuLayer* layer = MenuLayer::create();
	this->addChild(layer);
	return true;
}

init函式很簡單,沒有什麼特別,需要注意的是preLoadingSomethings函式
void MenuScene::preLoadingSomethings(){
	
	std::map<int,HeroMessage> temp = HeroMessageUtil::getInstance()->getHeroMessageMap();
	

	for(auto it = temp.begin();it!=temp.end();it++){
		HeroMessage temp = it->second;
		std::string png = "BoneAnimate/" + temp.r_png;
		std::string plist = "BoneAnimate/" + temp.r_plist;
		std::string EJ = "BoneAnimate/" + temp.r_ExportJson;
		ArmatureDataManager::getInstance()->addArmatureFileInfo(png,plist,EJ);
	}

	std::map<int,MonsterMessage> monTemp = MonsterMessageUtil::getInstance()->getMonsterMessageMap();
	for(auto it = monTemp.begin();it!=monTemp.end();++it){
		MonsterMessage temp = it->second;
		std::string png = "BoneAnimate/" + temp.r_png;
		std::string plist = "BoneAnimate/" + temp.r_plist;
		std::string EJ = "BoneAnimate/" + temp.r_ExportJson;
		ArmatureDataManager::getInstance()->addArmatureFileInfo(png,plist,EJ);
	}

	StageMessageUtil::getInstance();
}

如果你有看上一節,那麼你應該知道XXXMessageUtil是什麼東東了。

因為如果要使用骨骼動畫,必須先把骨骼動畫的資源通過 ArmatureDataManager的addArmatureFileInfo()載入

所以為了以後能夠方便地使用骨骼動畫,先統一在MenuScene初始化的時候載入。

2、選人介面

好吧,MenuScene跟MenuLayer沒有什麼其他特別的,就提供了一個開始按鈕,轉到選人介面ChooseScene

ChooseScene也沒有什麼,就包含了一個ChooseLayer。

效果圖:


下面重點講ChooseLayer

#ifndef _CHOOSELAYER_H_
#define _CHOOSELAYER_H_
#include "cocos2d.h"
#include "ui/CocosGUI.h"
#include "cocostudio/CocoStudio.h"
#include "extensions/cocos-ext.h"
USING_NS_CC;
using namespace cocostudio;
using namespace ui;
class HeroItem;
class ChooseLayer : public Layer{
public:
	virtual bool init();
	CREATE_FUNC(ChooseLayer);
private:
	//載入可選英雄
	void loadHeroes();
	//初始化
	void initHeroMsg();
	//設定當前英雄
	void setCurHeroMsg(int index,int heroIndex);

	//按鈕回撥函式
	void startGame(Ref* pSender,ui::TouchEventType type);

	void leftHero1(Ref* pSender,ui::TouchEventType type);
	void rightHero1(Ref* pSender,ui::TouchEventType type);

	void leftHero2(Ref* pSender,ui::TouchEventType type);
	void rightHero2(Ref* pSender,ui::TouchEventType type);

	void leftHero3(Ref* pSender,ui::TouchEventType type);
	void rightHero3(Ref* pSender,ui::TouchEventType type);

	void backToMenu(Ref* pSender,ui::TouchEventType type);

private:
	std::vector<HeroItem> heroArray;
	Text* hero_name1;
	Text* hero_name2;
	Text* hero_name3;

	Widget* pNode;

	int cur_hero1_index;
	int cur_hero2_index;
	int cur_hero3_index;

	Armature* hero1;
	Armature* hero2;
	Armature* hero3;
};

class HeroItem : public Ref{
friend class ChooseLayer;
public:
	HeroItem(const std::string& name,const int id,const std::string& resourceName,const int o_x,const int o_y);
	~HeroItem();
private:
	std::string name;
	std::string resourceName;
	int id;
	int o_x;
	int o_y;
};
#endif

注意的是,我這裡沒有用陣列,而是直接用hero1、hero2、hero3這樣子的列舉,所以就限定死了只有3個位置。。這是一種笨方法,會出現程式碼的冗餘,在開發中不應該出現,這裡懶得修改。。所以請各位不要學習,見諒見諒。

而且這裡的佈局我也是用cocostudio1.6的,所以你們可能有點看不懂。如果需要補充cocostudio的UI編輯器知識,推薦以下文章

這裡我先定義HeroItem結構儲存我在選人介面需要用到的資訊,其實他就是簡化版的HeroMessage(因為在選人介面只用到一部分資訊)

而且注意我定義了一個vector<HeroItem>。

下面看各個函式的具體實現:

bool ChooseLayer::init(){
	pNode = GUIReader::getInstance()->widgetFromJsonFile("UI/ChooseLayerUI.json");
	this->addChild(pNode,0);

	Button* enterBtn = (Button*)Helper::seekWidgetByName(pNode,"enterBtn");
	enterBtn->addTouchEventListener(this,toucheventselector(ChooseLayer::startGame));

	Button* backBtn = (Button*)Helper::seekWidgetByName(pNode,"backBtn");
	backBtn->addTouchEventListener(this,toucheventselector(ChooseLayer::backToMenu));

	Button* leftBtn1 = (Button*)Helper::seekWidgetByName(pNode,"leftBtn1");
	leftBtn1->addTouchEventListener(this,toucheventselector(ChooseLayer::leftHero1));

	Button* leftBtn2 = (Button*)Helper::seekWidgetByName(pNode,"leftBtn2");
	leftBtn2->addTouchEventListener(this,toucheventselector(ChooseLayer::leftHero2));
	
	Button* leftBtn3 = (Button*)Helper::seekWidgetByName(pNode,"leftBtn3");
	leftBtn3->addTouchEventListener(this,toucheventselector(ChooseLayer::leftHero3));


	Button* rightBtn1 = (Button*)Helper::seekWidgetByName(pNode,"rightBtn1");
	rightBtn1->addTouchEventListener(this,toucheventselector(ChooseLayer::rightHero1));

	Button* rightBtn2 = (Button*)Helper::seekWidgetByName(pNode,"rightBtn2");
	rightBtn2->addTouchEventListener(this,toucheventselector(ChooseLayer::rightHero2));

	Button* rightBtn3 = (Button*)Helper::seekWidgetByName(pNode,"rightBtn3");
	rightBtn3->addTouchEventListener(this,toucheventselector(ChooseLayer::rightHero3));

	
	initHeroMsg();

	return true;
}
init函式就是負責獲取按鈕,併為按鈕繫結回撥函式的,至於回撥函式的實現等下再說。

注意最後一行呼叫了initHeroMsg

看initHeroMsg函式之前,先看loadHeroes函式

void ChooseLayer::loadHeroes(){
	map<int,HeroMessage> temp = HeroMessageUtil::getInstance()->getHeroMessageMap();
	for(auto it = temp.begin();it!=temp.end();it++){
		HeroMessage msg = (*it).second;
		HeroItem item(msg.name,msg.id,msg.r_name,msg.offset_x,msg.offset_y);
		heroArray.push_back(item);
	}
}
loadHeroes函式就是對heroArray的初始化,呼叫過一次這個函式之後,以後就只需要從heroArray裡面拿資料就好了

好的,回過頭來看initHeroMsg吧

void ChooseLayer::initHeroMsg(){
	loadHeroes();
	
	hero_name1 = (Text*)Helper::seekWidgetByName(pNode,"heroName1");
	hero_name2 = (Text*)Helper::seekWidgetByName(pNode,"heroName2");
	hero_name3 = (Text*)Helper::seekWidgetByName(pNode,"heroName3");

	cur_hero1_index = 0;
	cur_hero2_index = 0;
	cur_hero3_index = 0;

	hero1 = nullptr;
	hero2 = nullptr;
	hero3 = nullptr;

	setCurHeroMsg(0,1);
	setCurHeroMsg(0,2);
	setCurHeroMsg(0,3);
}
先loadHeroes初始化array

下面的也是對一些變數初始化。

cur_hero1_index表示的是1號位置當前的角色對應array中的位置

setCurHeroMsg(0,1);表示設定1號位置的角色的index為0

具體實現如下:

void ChooseLayer::setCurHeroMsg(int index,int heroIndex){
	if(index < 0){
		index = index + heroArray.size();
	}

	if(index >= heroArray.size()){
		index = index - heroArray.size();
	}
	switch(heroIndex){
	case 1:
		cur_hero1_index = index;
		hero_name1->setText(heroArray[cur_hero1_index].name);
		if(hero1)	hero1->removeFromParentAndCleanup(true);
		hero1 = Armature::create(heroArray[cur_hero1_index].resourceName);
		hero1->setPosition(hero_name1->getPositionX()+heroArray[cur_hero1_index].o_x,
						   hero_name1->getPositionY()+heroArray[cur_hero1_index].o_y);
		hero1->getAnimation()->play("stand");
		this->addChild(hero1);
		break;
	case 2:
		cur_hero2_index = index;
		hero_name2->setText(heroArray[cur_hero2_index].name);
		if(hero2)	hero2->removeFromParentAndCleanup(true);
		hero2 = Armature::create(heroArray[cur_hero2_index].resourceName);
		hero2->setPosition(hero_name2->getPositionX()+heroArray[cur_hero2_index].o_x,
						   hero_name2->getPositionY()+heroArray[cur_hero2_index].o_y);
		hero2->getAnimation()->play("stand");
		this->addChild(hero2);
		break;
	case 3:
		cur_hero3_index = index;
		hero_name3->setText(heroArray[cur_hero3_index].name);
		if(hero3)	hero3->removeFromParentAndCleanup(true);
		hero3 = Armature::create(heroArray[cur_hero3_index].resourceName);
		hero3->setPosition(hero_name3->getPositionX()+heroArray[cur_hero3_index].o_x,
						   hero_name3->getPositionY()+heroArray[cur_hero3_index].o_y);
		hero3->getAnimation()->play("stand");
		this->addChild(hero3);
		break;
	default:
		break;
	
	}
}
第一個引數:index,表示對應heroArray的位置

第二個引數:heroIndex,表示哪一個位置的英雄

一開始先判斷index的合法性,這樣的實現能夠使其達到一種首尾迴圈的效果。

接著的switch語句是根據英雄的位置,去修改相應的物件,應該不能理解。

PS:再強調一次,我一開始寫這個程式碼的時候圖個簡單,所以用了列舉的笨方法,大家不要學習,看一看,笑一笑就好。

如果都看懂了最重要的setCurHeroMsg()函式,那麼很容易就想到,3對左右選擇按鈕的實現就可以依靠setCurHeroMsg函數了

void ChooseLayer::leftHero1(Ref* pSender,TouchEventType type){
	if(type == TouchEventType::TOUCH_EVENT_BEGAN){
		setCurHeroMsg(cur_hero1_index - 1,1);
	}
}

void ChooseLayer::leftHero2(Ref* pSender,TouchEventType type){
	if(type == TouchEventType::TOUCH_EVENT_BEGAN){
		setCurHeroMsg(cur_hero2_index - 1,2);
	}
}

void ChooseLayer::leftHero3(Ref* pSender,TouchEventType type){
	if(type == TouchEventType::TOUCH_EVENT_BEGAN){
		setCurHeroMsg(cur_hero3_index - 1,3);
	}
}

void ChooseLayer::rightHero1(Ref* pSender,TouchEventType type){
	if(type == TouchEventType::TOUCH_EVENT_BEGAN){
		setCurHeroMsg(cur_hero1_index + 1,1);
	}
}

void ChooseLayer::rightHero2(Ref* pSender,TouchEventType type){
	if(type == TouchEventType::TOUCH_EVENT_BEGAN){
		setCurHeroMsg(cur_hero2_index + 1,2);
	}
}

void ChooseLayer::rightHero3(Ref* pSender,TouchEventType type){
	if(type == TouchEventType::TOUCH_EVENT_BEGAN){
		setCurHeroMsg(cur_hero3_index + 1,3);
	}
}

好的,現在就只剩下進入遊戲和返回這兩個按鈕的回撥函式沒有解釋了。

返回按鈕很簡單

void ChooseLayer::backToMenu(Ref* pSender,TouchEventType type){
	if(type == TouchEventType::TOUCH_EVENT_ENDED){
		Director::getInstance()->replaceScene(MenuScene::create());
	}
}
其實進入遊戲的函式也很簡單啦,看不懂沒關係,畢竟我們還沒有講戰鬥場景嘛。哈哈
void ChooseLayer::startGame(Ref* pSender,TouchEventType type){
	if(type == TouchEventType::TOUCH_EVENT_ENDED){

		GameScene* scene = GameScene::create();
		scene->setHeroTeam(
						HeroMessageUtil::getInstance()->getMessageById(heroArray[cur_hero1_index].id),
						HeroMessageUtil::getInstance()->getMessageById(heroArray[cur_hero2_index].id),
						HeroMessageUtil::getInstance()->getMessageById(heroArray[cur_hero3_index].id)
		);
		scene->setMonsterDeq(StageMessageUtil::getInstance()->getMessageById(3001).monsterDeq);
		Director::getInstance()->replaceScene(scene);
	}
}

大致的意思就是把當前三個位置的3個資訊傳到GameScene,由GameScene想幹嘛就幹嘛去

還有這裡預設把id為3001的地圖資訊(StageMessage)也傳到GameScene了,因為當時懶得弄關卡選擇器哈哈。關於關卡選擇和StageMessage的問題,後面在將戰鬥場景的時候還會跟大家交代清楚的,現在可以先視而不見吧。

至此,選人介面開發完畢。

郵箱地址:[email protected]

如有問題或指教,歡迎與我交流,謝謝。


相關推薦

cocos2dx3.2開發 RPG《Flighting》預先載入選人介面

一、前言 假設你已經看懂了前面三節,那麼接下來我們就開始進入真正的開發啦~~ 二、正文 1、預先載入 首先我們先談談預先載入,什麼叫預先載入,就是在資源沒有被用到的時候就先把資源載入到記憶體,等要用的時候直接從記憶體裡面獲取就好。 這樣的好處是當用的時候會省去載入的時間,

QtFFmpeg聯合開發指南——編碼2:完善功能和基礎封裝

v_op buffer 目前 front from 幀率 inter 博客 int 上一章我用一個demo函數演示了基於Qt的音視頻采集到編碼的完整流程,最後經過測試我們也發現了代碼中存在的問題。本章我們就先處理幾個遺留問題,再對代碼進行完善,最後把編碼功能做基礎封裝。 一

API開發實踐 返回HTML

acea 指定 win filename static box 拖動地圖 ive let 分為兩個部分:生成HTML和返回HTML 生成HTML: 最終想要的時顯示地圖,不可避免的使用高德地圖的API。 【地圖API】地址錄入時如何獲得準確的經緯度?淘寶收貨地址詳解 改變幾

學習 MeteoInfo二次開發教程

手動添加 map dem itl 數控 nas 字號 south title 教程四的問題不大。 1.private void AddMapFrame_ChinaSouthSea()、private void AddTitle()兩個函數和public Form1()函數並

iOS項目開發實戰——學會使用TableView列表控件plist讀取Section顯示

track arr 學會 有關 control label center head fcm 文本將會實現把數據存儲到plist文件裏。然後在程序中進行讀取。在TableView控件中依據不同的類別顯示Section。有關TableView 的其它實現,

Angular開發實踐:組件之間的交互

lec previous call 私有 判斷 處理方法 數組 依然 處理 在Angular應用開發中,組件可以說是隨處可見的。本篇文章將介紹幾種常見的組件通訊場景,也就是讓兩個或多個組件之間交互的方法。 根據數據的傳遞方向,分為父組件向子組件傳遞、子組件向父組件傳遞及通過

LayIM.AspNetCore Middleware 開發日記主角登場LayIM介紹

融雲 文檔 應該 步驟 end 想要 .sql 味道 asp.net 前言   在前幾篇中已經初步介紹了開發AspNetCore中間件的一些基礎知識,不過都沒有很深入的去研究,後續還是需要去看看源碼。本篇呢,終於有點開頭的味道了,就是要介紹LayIM了,其實標題寫的是主角,

ASP.NET Core 2 學習筆記依賴註入

pub framework 三次 DDM order 包裝 差異 限制 cto 原文:ASP.NET Core 2 學習筆記(四)依賴註入ASP.NET Core使用了大量的依賴註入(Dependency Injection, DI),把控制反轉(Inversion Of

Java開發筆記Java帝國的度量衡

時間 我們 true 都是 不難 class 右鍵菜單 imp 既然 秦始皇統一中國之後,實行“書同文,車同軌”,把貨幣和各種度量衡都統一起來,從而締造了一個秩序井然的帝國。既然統一度量衡是每個帝國都要做的事情,Java帝國也不例外,對於人生地不熟的初學者來說,只有認識了J

NeuChar 平臺使用及開發教程:使用 NeuChar 的素材服務

  各類公眾號的功能之一就是為使用者提供各類圖文和多媒體的資訊,因此素材是必不可少的。   進入 Neural Cell 設定介面,點選右側【素材管理】按鈕,進入素材管理介面。       目前系統提供了文字、多圖文、圖片三種類型的素材,後續將會有更多型別提供。   點選按鈕

Android 開發Button圓角實現

本節學習button的圓角實現: 利用上節登入button示例: 1.效果對比: <Button android:layout_width="match_parent" android:layout_height="45dp

使用SpringBoot2.0搭建企業級應用開發框架配置LogBack日誌

前言 SpringBoot預設配置提供了對常用日誌的支援,如:Java Util Logging、Log4J、Log4J2和Logback,每種Logger都可以通過配置使用控制檯或者檔案輸出日誌內容 Logback是log4j框架的作者開發的新一代日誌框架,它效率更高、能夠適

3dContactPointAnnotationTool開發日誌

手動 unity title 裏的 界面 con 分享 obj 還得 ??沒辦法,為了能在寢室接著做這玩意只好又在電腦上裝一個和實驗室版本一樣的unity了。雖然打開後UI界面還是一團糟,不過至少要的東西都在,又手動調了調UI界面。 ??然後把旋轉視角功能加上了。鼠標右鍵

DevExpress WinForms v18.2新版亮點

行業領先的.NET介面控制元件2018年第二次重大更新——DevExpress v18.2日前正式釋出,本站將以連載的形式為大家介紹各版本新增內容。本文將介紹了DevExpress WinForms v18.2 的新功能,新版30天免費試用!點選下載>> Pivot Grid 未繫結

工控機上位機軟體的開發歷程

上位機架構 上位機使用C#、WPF技術開發。軟體並不是一個exe了事,這樣維護和協作都很難去做。 整個系統主要包括以下模組: 主程式 EQMSClient 負責將各模組組裝在一起 基礎類庫

基於java的微信小程式的實現使用者個人資訊相關介面開發

1.查詢使用者個人資訊介面開發 1.需求分析 需要通過前端傳來的使用者的userid去資料庫中進行查詢,並將查詢到的物件封裝為usersVo返回給前端 dao層程式碼 public interface UsersDao extends JpaRepository<Use

【一步步一起學DApp開發web3.js 基本使用 | 連線geth | 建立web客戶端

概述 web3.js內部使用JSONRPC與geth通訊。它把所有JSON-RPC API當作JavaScript API,也就是說,它不僅支援所有與以太坊相關的API,還支援與Whisper和Swarm相關的API。 相關連結 web3.js託管地址

Unity ShaderLab開發實戰描邊

      之前可能在面剔除中提到過,面剔除可以用來實現描邊效果。(以下效果圖來自Unity3D ShaderLab開發實戰詳解)        原理:這是一個最簡單的描邊,使用面剔除:Cull指令,上圖中 ,

Android開發筆記字串格式化

字串的格式化 博主原來是搞C的,C裡面有sprintf來格式化字串,後來轉到java變傻了,拼接String只會用n個“+”,要麼就是用StringBuilder的append方法,但要是遇上把數字格式化的情況(比如左補0等等),就只能傻乎乎的if else判斷該補幾個0。

床頭筆記之Android開發學習

新建HelloWorld工程專案目錄說明: 工程目錄: 按著下圖順序講解 HelloWorld: 專案名 src: 自己編寫的程式存放處 gen: 系統自動生成的檔案R.java(和res資原始檔關聯,為其子檔案drawable下每個圖片及values下的鍵值