1. 程式人生 > >遊戲開發,叢林戰爭3

遊戲開發,叢林戰爭3

34.訊息面板的顯示

首先的話,我們建立我們的MessagePanel指令碼,這裡我們提供了建立,顯示,隱藏三個方法,並且的話我們這個面板指令碼的控制是交給Uimanager進行管理的

publicclassMessagePanel : BasePanel {

private Texttext;

privatefloat showTime = 1f;

publicoverridevoid OnEnter()

{   //這個是用來建立訊息面板的

base.OnEnter();

text = this.GetComponent<Text>();

text.enabled = false

;

uiMng.InjectMsg(this);

}

publicvoid ShowMessage(string data)

{

text.text = data;

//將alpha值重新設定為1

text.enabled = true;

text.color = Color.white;

//一秒種後隱藏這個方法

Invoke("HideMessage", showTime);

}

publicvoid HideMessage()

{

//這個是用來修改text的Alpha值使得它的顯示為隱藏

text.CrossFadeAlpha(0, 1, false);

}

}

因為所有的Ui面板都是用UiManager進行管理的,因此我們提供在父類提供一個方法來獲取到Uimanager

public UIManager UIManager

{

//提供一個set方法可以進行賦值

set { uiMng= value; }

}

然後在這個例項化面板的方法裡,當我們獲得到對應的面板的時候,就會對它進行賦值,讓UiManager進行管理

privateBasePanel GetPanel(UIPanelType panelType)

{

if(panelDict == null)

{

panelDict = new Dictionary<UIPanelType,BasePanel>();

}

//BasePanel panel;

//panelDict.TryGetValue(panelType, outpanel);//TODO

BasePanel panel = panelDict.TryGet(panelType);

if (panel== null)

{

//如果找不到,那麼就找這個面板的prefab的路徑,然後去根據prefab去例項化面板

//stringpath;

//panelPathDict.TryGetValue(panelType,out path);

string path = panelPathDict.TryGet(panelType);

GameObject instPanel =GameObject.Instantiate(Resources.Load(path)) as GameObject;

instPanel.transform.SetParent(CanvasTransform,false);

//例項化對應面板後我們要給這個面板提供UiManager這個值

instPanel.GetComponent<BasePanel>().UIManager = this;

panelDict.Add(panelType,instPanel.GetComponent<BasePanel>());

return instPanel.GetComponent<BasePanel>();

}

然後我們在UIManager裡提供兩個方法,來引用和操控我們的MessagePanel

publicvoid ShowMessage(string msg)

{

if(messagePanel == null)

{

Debug.Log("無法顯示該面板");

     return;

}

messagePanel.ShowMessage(msg);

}

privatevoid HideMessage()

{

messagePanel.HideMessage();

}

publicvoidInjectMsg(MessagePanel messagePanel)

{

//當面板被建立的時候,注入這個Panel

messagePanel = this.messagePanel;

}

如果其他的模組也想要用這個提示錯誤資訊的話,我們只要在GameFacade裡提供這樣一個介面方法來呼叫Uimanager裡的ShowMessage即可

publicvoid ShowMessage(string msg)

{

uIManager.ShowMessage(msg);

}

35.這裡我們來開發我們的UI介面

這裡實現的是點選登入彈出登入框,這個的話,首先,我們用UIManager來創建出我們的訊息面板和登入面板

publicoverridevoid OnInit()

{

base.OnInit();

PushPanel(UIPanelType.Start);

PushPanel(UIPanelType.Message);

}

然後的話,我們修改我們的StartPanel,給登入註冊上點選建立登入頁面的按鈕

publicclassStartPanel : BasePanel {

private ButtonloginButton;

privateAnimator btnAnimator;

publicoverridevoid OnEnter()

{

base.OnEnter();

gameObject.SetActive(true);

loginButton = transform.Find("LoginButton").GetComponent<Button>();

btnAnimator = transform.Find("LoginButton").GetComponent<Animator>();

loginButton.onClick.AddListener(OnClick);

}

publicvoid OnClick()

{

uiMng.PushPanel(UIPanelType.Login);

}

然後給我們的登入頁面新增一個關閉功能即可,這裡我們使用DoTween控制頁面的切換

publicclassLoginPanel : BasePanel {

private ButtoncloseButton;

// Use this for initialization

publicoverridevoid OnEnter()

{

base.OnEnter();

gameObject.SetActive(true);

    transform.DOScale(1, 0.4f);

transform.DOLocalMove( new Vector3(35,0,0), 0.4f);

closeButton = transform.Find("Exit").GetComponent<Button>();

closeButton.onClick.AddListener(OnCloseClick);

}

publicvoid OnCloseClick()

 {

uiMng.PopPanel();

transform.DOScale(0, 0.4f);

//transform.DOLocalMove(new Vector3(1000,0, 0), 0.4f).OnComplete(()=>uiMng.PopPanel());

}

publicoverridevoid OnExit()

{

base.OnExit();

gameObject.SetActive(false);

}

}

36.建立兩個資料表用來管理我們的資訊

一個使用者表,包含id,使用者名稱,密碼

一個戰機表,包含id,使用者id,總局數,勝利局數

這裡我們建立這樣兩個資料表,戰績表的使用者id,作為使用者表id的外來鍵相關聯

37.登入面板檢測你是否輸入了使用者名稱或者密碼

首先,我們獲得一下兩個輸出域

userIf = transform.Find("userName/InputField").GetComponent<InputField>();

passwordIf = transform.Find("password/InputField").GetComponent<InputField>();

然後我們註冊一下登入按鈕

loginButton = transform.Find("LoginButton").GetComponent<Button>();

loginButton.onClick.AddListener(OnLoginClick);

根據輸出域判斷訊息並且決定訊息面板顯示什麼資訊

publicvoid OnLoginClick()

{

string msg = "";

if (string.IsNullOrEmpty(userIf.text))

{

msg += "使用者名稱為空";

}

if (string.IsNullOrEmpty(passwordIf.text))

{

msg += "密碼為空";

}

if (msg !=null)

{

uiMng.ShowMessage(msg);

return;

}

Debug.Log(msg + "登入按鈕被點選了");

}

1.  修改讓requestCode指定對應的ActionCode

這裡我們要知道,我們是通過RequestCode來找到對應的Controller,通過ActionCode區別Request

1.  首先的話,我們修改BaseRequest,子類繼承的時候都要修改他們的值

publicclassBaseRequest : MonoBehaviour {

protectedRequestCode requestCode = RequestCode.None;

protected ActionCodeactionCode = ActionCode.None;

2.  修改RequestManager,我們是通過RequestCode獲得對應的ActionCode

publicclassBaseRequest : MonoBehaviour {

protected RequestCoderequestCode = RequestCode.None;

protectedActionCode actionCode = ActionCode.None;

3.  修改GameFacade中對應的獲得Request的方法

publicvoid AddRequest(ActionCode actionCode,BaseRequestbaseRequest)

{

requestManager.AddRequest(actionCode,baseRequest);

}

publicvoid RemoveRequest(ActionCode actionCode)

{

requestManager.RemoveRequest(actionCode);

}

publicvoid HandleResponse(ActionCode actionCode, string data)

{

requestManager.HandleResponse(actionCode, data);

}

4.  修改伺服器端的各種RequestCode,這裡的話只要根據報錯改就可以了,還是很簡單的,目的就是每個RequestCode用ActionCode區分

2.  客戶端向伺服器端發起登入請求

首先,我們要給RequestCode和ActionCode提供幾個code

publicenumRequestCode

{

None,

User

}

publicenumActionCode

{

None,

Login,

Regist

}

然後我們重新生成一下,把dll放到plugins下,這個一般是提前設計好,因為是案例,所以會將框架修改一下

這裡的話我們做的是客戶端傳送登入請求,於是我們建立一個LoginRequest

void Start()

{

//指定好LoginRequest中的requestCode和actionCode

requestCode = RequestCode.User;

actionCode = ActionCode.Login;

}

publicvoid SendRequest(string username,string password)

{

string data =username + "," +password;

base.SendRequest(data);

}

指定好它對應的requestCode和ActionCode,然後呼叫ClientManager裡的SendMessage方法,這個方法的呼叫我們通過GameFacade作為中介,而GameFacade的持有,我們放在父類BaseRequest,這裡還要修改一下GameFacade,使它持有一下ClientManager裡的SendMessage,這裡程式碼就不貼了,和之前的一樣

protectedGameFacade gameFacade;

publicvirtualvoid Awake()

{

GameFacade._instance.AddRequest(actionCode, this);

gameFacade = GameFacade._instance;

}

3.  新增使用者的Dao層和Model層來進行校驗

在Model層裡我們給它提供使用者屬性

namespace遊戲伺服器.Model

{

classUser

{

//這裡我們設定它的幾個屬性

public User(int id,string username,string password)

{

this.id = id;

this.username =username;

this.password =password;

}

publicint id { get; set; }

publicstring username { get; set; }

publicstring password { get; set; }

}

}

在Dao層的話,我們提供它的解析方法

using System;

usingSystem.Collections.Generic;

using System.Linq;

usingSystem.Text;

usingSystem.Threading.Tasks;

usingMySql.Data.MySqlClient;

using遊戲伺服器.Model;

namespace遊戲伺服器.Dao

{

classUserDao

{

//提供校驗方法,一個model對應一個Dao

public UserVerifyUser(MySqlConnection conn,string username,string password)

{

MySqlCommand cmd = new MySqlCommand("select * from user where [email protected] [email protected]", conn);

//插值作為連線查詢

MySqlDataReader mySqlDataReader = null;

try

{

mySqlDataReader =cmd.ExecuteReader();

cmd.Parameters.AddWithValue("username",username);

cmd.Parameters.AddWithValue("password",password);

if (mySqlDataReader.Read())

        {

int id = mySqlDataReader.GetInt32("id");

User user = new User(id, username,password);

return user;

}

else

{

returnnull;

}

}

catch(Exception e)

{

Console.WriteLine("讀取使用者資訊出現異常" + e);

}

finally

{

mySqlDataReader.Close();

}

           returnnull;

}

}

建立後我們在Controller層進行控制Dao層方法的呼叫即可

4.  讓伺服器端對我們的客戶端登入請求做出響應

首先我們在Common類裡提供一個列舉型別的方法判斷是否正確返回資訊

publicenumReturnCode

{

Success,

Fail

}

然後我們回到我們的UserController裡判斷我們是否登入成功

首先我們要用spilt方法分割使用者名稱和密碼,再用上一節寫的方法判斷是不是正確的使用者名稱和密碼,再返回對應的判斷即可,這裡要獲取conn,要給Client提供一個構造方法獲取,這裡的話就不寫了

publicstring Login(string data,Clientclient,Sever server)

{

string[] str = data.Split(',');

User user =userDao.VerifyUser(client.MysqlConn, str[0], str[1]);

if (user == null)

{

return ((int)(ReturnCode.Fail)).ToString();

}

else

{

return ((int)(ReturnCode.Success)).ToString();

}

}

5.  客戶端響應伺服器做出的迴應

這裡我們回到unity客戶端,這裡的話,伺服器傳送了資訊,需要客戶端做出響應,我們在LoginRequest對它做出響應,將伺服器返回的ReturnCode進行轉型,然後呼叫LoginPanel對UI進行反應

publicoverridevoid OnResponse(string data)

{

ReturnCode returnCode = (ReturnCode)(int.Parse(data));

loginPanel.OnResponse(returnCode);

}

這裡的OnResponse,會對你的returnCode進行反應,當然前提是連線了資料庫

publicvoidOnResponse(ReturnCode returnCode )

{

if(returnCode == ReturnCode.Success)

{

//判斷登入成功,進入房間列表

}

else

{

uiMng.ShowMessage("登入失敗,使用者名稱或者密碼不正確");

 }

}

6.  測試登入效果

在測試登入效果的時候,不得不說,我碰到了很多的障礙,這也是之前沒有debug的原因,這裡講述一下

1.  在loginRequest中呼叫MessagePanel下的ShowMessage時,因為是非同步呼叫,所以無法修改,解決辦法嘛,就是呼叫Unity自帶的Update方法進行傳值,

publicvoid Update()

{

//因為這個BasePanel是繼承自MonBehaviour,所以我們能呼叫Update方法

if(message != null)

{

ShowMessage(message);

message = null;

}

}

publicvoid ShowMessage(string data)

{

//將alpha值重新設定為1

text.enabled = true;

  text.text = data;

text.CrossFadeAlpha(1, 0.2f, false);

//一秒種後隱藏這個方法

Invoke("HideMessage", showTime);

}

publicvoid ShowMessageSync(string msg)

{

//非同步呼叫顯示資訊的方法,因為想要修改unity元件是不能直接修改

message = msg;

1.  }

2.  這個是之前的程式碼問題,我在client裡面回撥資訊時候,寫錯了clientSocket的傳值方式

我把clientSocket=this.clientSocket寫成了this.clientSocket=clientSocket,不得不說,這一塊造成了很大的困擾,因為Bug很隱晦,看了很久,所幸最後還是找出來了,這裡提示了我修改的重要性,當然也因此重溫了一遍伺服器和客戶端的資訊派發機制

3.  接下來就很簡單了,我們手動給資料庫新增一條使用者名稱密碼做校驗就行,當然記得select資料的時候要先注入值再查詢,之前這一塊沒有做出來,導致Bug,這裡也標記一下

4.  那麼到這裡,我們的登入功能也就算完成了

7.  註冊頁面的一些Ui調整

這裡的話比較簡單,就是一個彈出頁面的操作,我就貼一下程式碼了

publicclassRegistPanel : BasePanel {

private ButtoncloseButton;

publicoverridevoid OnEnter()

{

base.OnEnter();

this.gameObject.SetActive(true);

transform.DOScale(1, 0.4f);

closeButton = transform.Find("Exit").GetComponent<Button>();

closeButton.onClick.AddListener(OnCloseButtonClick);

}

publicvoidOnCloseButtonClick()

{

transform.DOScale(0, 0.4f);

uiMng.PopPanel();

}

publicoverridevoid OnExit()

{

base.OnExit();

this.gameObject.SetActive(false);

}

}

8.  傳送資訊到伺服器端

這裡的話我們建立一個RegisterRequest來處理註冊的請求

也是持有我們的註冊頁面,然後傳送請求,根據ReturnCode判斷是否正確

publicclassRegisterRequest : BaseRequest {

privateRegistPanel registPanel;

publicoverridevoid Awake()

{

//指定好LoginRequest中的requestCode和actionCode

requestCode = RequestCode.User;

actionCode = ActionCode.Regist;

registPanel = GetComponent<RegistPanel>();

base.Awake();

}

publicvoid SendRequest(string username, string password)

{

string data =username + "," +password;

base.SendRequest(data);

}

publicoverridevoid OnResponse(string data)

{

ReturnCode returnCode = (ReturnCode)(int.Parse(data));

}

然後我們修改一下我們的RegisterPanel做註冊校驗和傳送資料

publicvoid Start()

{

userInfo = transform.Find("userName/InputField/Text").GetComponent<Text>();

passwordInfo = transform.Find("password/InputField/Text").GetComponent<Text>();

rePasswordInfo = transform.Find("RePassword/InputField/Text").GetComponent<Text>();

registerRequest = this.GetComponent<RegisterRequest>();

}

publicoverridevoid OnEnter()

{

base.OnEnter();

this.gameObject.SetActive(true);

transform.DOScale(1, 0.4f);

closeButton = transform.Find("Exit").GetComponent<Button>();

closeButton.onClick.AddListener(OnCloseButtonClick);

registerButton = transform.Find("RegeistButton").GetComponent<Button>();

      registerButton.onClick.AddListener(OnRegisterClick);

}

publicvoidOnCloseButtonClick()

{

transform.DOScale(0, 0.4f);

uiMng.PopPanel();

}

publicvoid OnRegisterClick()

{

string msg = "";

if (string.IsNullOrEmpty(userInfo.text)){

msg += "使用者名稱不能為空";

}

if (string.IsNullOrEmpty(passwordInfo.text))

{

msg += "/n密碼不能為空";

}

if(passwordInfo.text != rePasswordInfo.text)

{

msg += "密碼和重複密碼不一致";

}

if (msg !=null)

{

uiMng.ShowMessage(msg);

}

//進行註冊請求傳送資訊到伺服器端

registerRequest.SendRequest(userInfo.text, passwordInfo.text);

}

publicoverridevoid OnExit()

{

base.OnExit();

this.gameObject.SetActive(false);

}

}

9.  使伺服器對於客戶端的註冊請求做響應

這裡的話,當我們想要對客戶端的註冊做出響應,這裡牽扯到的Controller和Dao就是UserController和UserDao了,一個負責呼叫Dao裡的方法進行檢驗和註冊,一個來書寫註冊方法

UserController

publicstring Regist(string data, Clientclient, Sever server)

{

string[] str = data.Split(',');

string username = str[0];

string password = str[1];

bool res = userDao.GetUserByUsername(client.MysqlConn,username);

if (res)

{

Console.WriteLine("使用者名稱已經存在");

return ((int)(ReturnCode.Fail)).ToString();

}

userDao.RegisterUser(client.MysqlConn, username, password);

return ((int)(ReturnCode.Success)).ToString();

}

publicvoid RegisterUser(MySqlConnectionconn, stringusername, stringpassword)

{

//插值作為連線查詢

try

{

MySqlCommand cmd = new MySqlCommand("insert into user where [email protected] [email protected]", conn);

               cmd.Parameters.AddWithValue("username",username);

cmd.Parameters.AddWithValue("password",password);

//執行插入語句

cmd.ExecuteNonQuery();

}

catch (Exception e)

{

Console.WriteLine("插入使用者資訊出現異常" + e);

}

}

10.建立聲音的模組AudioMannager

我們將在AudioMannager裡來管理所有我們的聲音,這裡的話,我們先持有所有的聲音並且為我們的遊戲新增背景音樂

publicclassAudioManager : BaseManager {

//重寫基類對於facade的呼叫

publicAudioManager(GameFacade facade) : base(facade) { }

//接下來的話,我們將在這個類裡面,持有所有的聲音

privateconststring sound_prepath = "Sounds/";

privateconststring sound_alert = "Alert";

privateconststring sound_Bgfast = "Bg(fast)";

privateconststring sound_Bgmoderate = "Bg(moderate)";

privateconststring sound_ButtonClick ="ButtonClick";

privateconststring sound_Miss="Miss";

privateconststring sound_ShootPerson ="ShootPerson";

privateconststring sound_Timer = "Timer";

privateAudioSource bgAudioSource;

privateAudioSource normalAudioSource;

publicoverridevoid OnInit()

{

//在這裡我們初始化我們的音樂

base.OnInit();

GameObject audioPlay = new GameObject("AudioSource(GameObject)");

bgAudioSource= audioPlay.AddComponent<AudioSource>();

normalAudioSource = audioPlay.AddComponent<AudioSource>();

bgAudioSource.clip = LoadClip(sound_prepath, sound_Bgfast);

PlaySound(bgAudioSource, true);

}

publicAudioClip LoadClip(stringprepath,stringsoundName)

{

//指定型別後在AudioClip中進行載入

returnResources.Load<AudioClip>(prepath + soundName);

}

publicvoidPlaySound(AudioSource audioSource,bool isLoop=false)

{

audioSource.Play();

audioSource.volume = 0.2f;

audioSource.loop = isLoop;

}

}

11.新增按鍵聲音

這裡的話,我們通過GameFacade實現對AudioMannager的呼叫,然後的話我們處理點選事件都是在UiPanel下呼叫的,這樣的話,我們通過GameFacde中介,然後給BasePanel寫一個點選播放音樂的方法,這樣,各個子panel就都能播放聲音了

1.提供兩個方法,分別播放迴圈的音樂和不迴圈的音樂

publicvoid PlayBgSound(string soundName)

{

bgAudioSource.clip= LoadClip(sound_prepath, soundName);

PlaySound(bgAudioSource, true,0.2f);

}

publicvoid PlayNormalSound(string soundName)

{

normalAudioSource.clip = LoadClip(sound_prepath, soundName);

PlaySound(normalAudioSource, false,1f);

}

2.  在GameFacade裡呼叫

publicvoid PlayBgsound(string soundName)

{

audioManager.PlayBgSound(soundName);

}

publicvoid PlayNormalSound(string soundName)

{

audioManager.PlayNormalSound(soundName);

}

3.在建立UIPanel的時候注入GameFacade的值

instPanel.GetComponent<BasePanel>().Facade= facade;

3.  在BasePanel裡建立播放點選聲音的方法,設定GameFacade的值

protectedGameFacade facade;

public GameFacade Facade

{

set

{

this.facade = value;

}

}

4.  接下來在各個面板呼叫即可

12.讓我們點選註冊時候直接彈出面板

這一塊的話我們做的和之前彈出其他面板思路是一樣,將RoomList做成Prefab,然後修改我們的panelType新增上roomlist,當然同時我們也要修改一下我們的的json檔案裡的路徑保證我們的PanelType能被讀取到

當然這裡有個問題,我們的登入是非同步處理,而不是在主執行緒中處理,這裡的話和之前的ShowMessage是一樣的,那我們解決辦法也和那個類似,我們更改PanelType也放在非同步中進行處理即可

1.我們給UiManager的父類BaseManager裡實現一個方法Update

2.這個互動我們放在GameFacade裡去實現,因為我們這個更改Panel要在主執行緒中進行

publicvoid UpdateMannager()

{

audioManager.Update();

cameraManager.Update();

requestManager.Update();

playerManager.Update();

clientManager.Update();

uIManager.Update();

}

3. 這樣我們就能在Uimanager裡重寫我們的Update方法來執行了,在裡面修改我們的PanelType,呼叫這個非同步方法

publicoverridevoid Update()

{

base.Update();

if(panelTypeToPush != UIPanelType.None)

{

PushPanel(panelTypeToPush);

panelTypeToPush = UIPanelType.None;

}

}

publicvoidPushPanelSync(UIPanelType panelType)

{

panelTypeToPush = panelType;

}

4.這樣的話,我們就可以在LoginPanel裡的OnResPonse,對是否登入成功,做出響應了

publicvoidOnResponse(ReturnCode returnCode )

{

if(returnCode == ReturnCode.Success)

{

        //判斷登入成功,進入房間列表

uiMng.PushPanelSync(UIPanelType.RoomList);

}

else

{

uiMng.ShowMessageSync("登入失敗,使用者名稱或者密碼不正確");

}

}

13.伺服器查詢使用者資訊返回給客戶端

這裡的話我們仿照一下UserDao的寫法就行,實際就是對result表通過userid進行查詢,再讓伺服器在登入成功時轉發給客戶端

1.  建立Model層的Result類

classResult

{

//這裡我們設定返回Result的幾個屬性

public Result(int id, int userid, int totalcount,int wincount)

{

this.Id = id;