仿LOL項目開發第七天
by 草帽
不知不覺已經寫到了第七篇這種類型的博客,但是回過頭看看之前寫的,發現都只能我自己能看懂。
我相信在看的童鞋雲里霧里的,因為我基本上沒怎么詳細講一個腳本怎么用?但是你們可以自己看下代碼,很快你就知道怎么用!以后也可以用到自己的項目中。
所以說閱讀別人的代碼非常重要,因為你們從中發現他們代碼的優點和缺點。
OK,廢話不多說,我們繼續上節課,上節課我們已經封裝自己的UI框架,但是還少個沒講,可能有些童鞋也會遇到報錯,怎么解決?

在Window下新建一個UILib,然后里面新建一個腳本:
WidgetFactory:
using UnityEngine;
using System.Collections.Generic;
namespace UILib
{
public class WidgetFactory
{
/// <summary>
/// 查找節點下所有的UI組件,緩存到字典里面
/// </summary>
/// <param name="trans"></param>
/// <param name="parent"></param>
/// <param name="dicAllUIObjects"></param>
public static void FindAllUIObjects(Transform trans, IXUIObject parent, ref Dictionary<string, XUIObjectBase> dicAllUIObjects)
{
int i = 0;
while (i < trans.childCount)
{
Transform child = trans.GetChild(i);
XUIObjectBase component = child.GetComponent<XUIObjectBase>();
if (!(null != component))
{
goto IL_85;
}
if (component.GetType().GetInterface("IXUIListItem") == null)
{
if (dicAllUIObjects.ContainsKey(component.name))
{
Debug.Log(component.name);
Debug.LogError("m_dicId2UIObject.ContainsKey:" + WidgetFactory.GetUIObjectId(component));
}
dicAllUIObjects[component.name] = component;
component.Parent = parent;
goto IL_85;
}
else
{
Debug.Log("fdsfd");
}
IL_8F:
i++;
continue;
IL_85:
WidgetFactory.FindAllUIObjects(child, parent, ref dicAllUIObjects);
goto IL_8F;
}
}
/// <summary>
/// 取得該組件所在的id(包含父親節點的id)
/// </summary>
/// <param name="uiObject"></param>
/// <returns></returns>
public static string GetUIObjectId(IXUIObject uiObject)
{
string result;
if (null == uiObject)
{
result = string.Empty;
}
else
{
string text = uiObject.CacheGameObject.name;
IXUIListItem iXUIListItem = uiObject as IXUIListItem;
if (iXUIListItem != null)
{
text = iXUIListItem.Id.ToString();
}
while (null != uiObject.Parent)
{
uiObject = uiObject.Parent;
string arg = uiObject.CacheGameObject.name;
iXUIListItem = (uiObject as IXUIListItem);
if (null != iXUIListItem)
{
arg = iXUIListItem.Id.ToString();
}
text = string.Format("{0}#{1}", arg, text);
}
result = text;
}
return result;
}
}
}
這個腳本主要是用來找UI的子物體用的。
OK,我們正式進入到LoginWindow的完善。我們知道這個界面分為兩塊,一個是登陸,一個選擇服務器。
我們先來寫登陸的邏輯。

可以看到這個界面分為3個組件:
1.用戶名輸入框
2.密碼輸入框
3.確認登陸按鈕
所以我們在LoginWindow初始化這些組件:

然后在InitWidget()里面初始化這些定義的組件:

這里的名字是我自己去的比如Login/UserNameInput,你們可以自己取自己的名字。

Login是LoginWindow下面的子物體,然后UserNameInput是Login下面的子物體,我們用/來區分紫武器。
OK,當我們點擊確認登陸按鈕的時候,肯定需要一個登陸事件給這個按鈕,所以現在我們來寫這個事件監聽。

然后我們來編寫OnLoginSumbit方法,主要處理一些登陸邏輯的事情。
public void OnLoginSumbit(GameObject go)
{
string username = this.m_Input_UsernameInput.value;
string password = this.m_Input_PasswordInput.value;
//如果用戶名或者密碼為空的話,就顯示提示消息
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
CEvent evt = new CEvent(EGameEvent.eGameEvent_ShowMessage);
evt.AddParam("content", StringConfigManager.GetString("MessageWindow.EMT_SureTip.LoginNullUsernameOrPassword"));
EventCenter.SendEvent<EMessageType, Action<bool>>(evt, EMessageType.EMT_SureTip, (isOk) =>
{
EventCenter.Broadcast(EGameEvent.eGameEvent_HideMessage);
});
}
LoginCtrl.singleton.Login(username, password);
}
可以看到我們搞了一個當確定消息提示,如果用戶名和密碼為空的話。
然后為了符合類的單一職責,我們把所有的邏輯放在LoginCtrl中。
新建文件夾取名為Controller,然后在里面新建一個LoginCtrl類:(我忘記之前是不是講過這個類,如果有講過這個類,因為我太久之前寫的,都忘記了,讀者自行改過)。
在寫這個邏輯之前,我們先來想下我們登陸進去在選擇服務器,所以登陸這個驗證並沒有進入到游戲服務器中,因為我們根本沒有選擇服務器,怎么能進到服務器中呢?
所以我認為就只是網頁簡單的驗證用戶名密碼,然后選擇服務器之后才真正的進入到游戲服務器。
所以這個登陸的邏輯步驟是:
1.開啟協程訪問一個網站,這個網站驗證用戶名和密碼。(所以我決定用PHP來寫這個驗證)
2.如果驗證通過之后,然后發送服務器列表給客戶端,客戶端再進入到選擇服務器界面。
3.客戶端選擇某個服務器,然后進入到這個游戲服務器。
所以首先,我在網站文件夾下面新建一個check.php文件:

這個腳本就是通過數據庫連接,然后進行驗證,這里我簡單一點,我就直接都放回成功。因為我們只是做下測試,不用每步都詳細。

所以我這里只是簡單放回一個success字符串,然后在LoginCtrl中:
public void Login(string account, string password)
{
this.username = account;
this.password = password;
LOLGameDriver.Instance.StartCoroutine(CheckUserPass());
}
IEnumerator CheckUserPass()
{
if (string.IsNullOrEmpty(this.username) || string.IsNullOrEmpty(this.password))
{
yield break;
}
WWW www = new WWW("http://127.0.0.1/LOLGameDemo/check.php");
bool success = false;
for (int i = 0; i < 10; i++)
{
yield return new WaitForSeconds(0.5f);
if (www.isDone)
{
if (string.IsNullOrEmpty(www.error))
{
if (!string.IsNullOrEmpty(www.text))
{
if (www.text == "success")
{
//加載服務器信息
SystemConfig.LoadServerList();
success = true;
EventCenter.Broadcast(EGameEvent.eGameEvent_ShowSelectServerList);
}
else
{
//登陸失敗
}
}
}
else
{
this.m_log.Error(www.error.ToString());
}
break;
}
}
if (www != null)
{
www.Dispose();
www = null;
}
//如果不成功
if (!success)
{
}
yield break;
}
不知讀者注意到沒有,如果登陸成功的話,我們就開始加載服務器列表。
//加載服務器信息 SystemConfig.LoadServerList();
所以我們回到SystemConfig里面:
/// <summary>
/// 加載服務器列表
/// </summary>
public static void LoadServerList()
{
try
{
List<ServerInfo> servers;
var url = GetCfgInfoUrlByName("ServerList");
string xmlSerList = "";
if (!string.IsNullOrEmpty(url))
{
xmlSerList = DownloadMgr.Instance.DownLoadHtml(url);
}
servers = LoadXMLText<ServerInfo>(xmlSerList);
if (servers.Count != 0)
{
ServerList = servers;
}
for (int i = 0; i < ServerList.Count; i++)
{
if (ServerList[i].id == LocalSetting.SelectedServer)
{
SelectedServerIndex = ServerList[i].id;
break;
}
}
}
catch (Exception e)
{
m_log.Error(e.ToString());
}
}
/// <summary>
/// 根據名字取得服務端配置信息Url
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static string GetCfgInfoUrlByName(string name)
{
string result = "";
foreach (var item in CfgInfoList)
{
if (item.name == name)
{
result = item.url;
break;
}
}
return result;
}
然后我們到Cfg.xml文件添加這個url=>id是1,名字是ServerList:
<?xml version="1.0" encoding="utf-8"?>
<root>
<cfg>
<id>0</id>
<name>version</name>
<url>http://127.0.0.1/LOLGameDemo/ServerVersion.xml</url>
</cfg>
<cfg>
<id>1</id>
<name>ServerList</name>
<url>http://127.0.0.1/LOLGameDemo/ServerList.xml</url>
</cfg>
</root>
然后我們添加4個游戲服務器,分別是電信2個(type為0),聯通兩個(type為1):
<?xml version="1.0" encoding="utf-8"?>
<root>
<list>
<id_i>0</id_i>
<name_s>艾歐尼亞</name_s>
<type_i>0</type_i>
<flag_i>0</flag_i>
<text_s>fe</text_s>
</list>
<list>
<id_i>1</id_i>
<name_s>戰爭學院</name_s>
<type_i>0</type_i>
<flag_i>0</flag_i>
<text_s>csc</text_s>
</list>
<list>
<id_i>2</id_i>
<name_s>黑色玫瑰</name_s>
<type_i>1</type_i>
<flag_i>0</flag_i>
<text_s>greger</text_s>
</list>
<list>
<id_i>3</id_i>
<name_s>洛克薩斯</name_s>
<type_i>1</type_i>
<flag_i>0</flag_i>
<text_s>greger</text_s>
</list>
</root>

所以我們回到LoginWindow中,添加該事件:
protected override void OnAddListener()
{
EventCenter.AddListener(EGameEvent.eGameEvent_ShowSelectServerList, ShowSelectServer);
}
protected override void OnRemoveListener()
{
EventCenter.RemoveListener(EGameEvent.eGameEvent_ShowSelectServerList, Show);
}
在寫ShowSelectServer方法之前,我們得先定義選擇服務器界面組件:
#region 選擇服務器界面
private UIPanel m_Panel_Select;//選擇服務器界面整體Panel
private UIButton m_Button_Select;//確認選擇按鈕
private UIButton m_Button_LeftButton;
private UIButton m_BUtton_RightButton;
private UIButton m_Button_ServerListButton;//服務器列表按鈕
private XUIList m_List_Dianxin;
private XUIList m_List_Wangtong;
private XUISprite m_Sprite_IconBG;
private XUISprite m_Sprite_ServerName;
private XUISprite m_Sprite_Icon;
private XUILabel m_Label_ServerName;
private XUILabel m_Label_NetworkSpeed;//測試速度Label
#endregion
InitWeight:
this.m_Button_ServerListButton = this.mRoot.FindChild("Select/Button/ServerListButton").GetComponent<UIButton>();
this.m_Button_Select = this.mRoot.FindChild("Select/Button/SelectButton").GetComponent<UIButton>();
this.m_Panel_Select = this.mRoot.FindChild("Select").GetComponent<UIPanel>();
this.m_List_Dianxin = this.mRoot.FindChild("Select/ServerList/Table/Dianxin/DianxinGrid").GetComponent<XUIList>();
this.m_List_Wangtong = this.mRoot.FindChild("Select/ServerList/Table/Wangtong/WangtongGrid").GetComponent<XUIList>();
this.m_Sprite_IconBG = this.mRoot.FindChild("Select/Signal/IconBG").GetComponent<XUISprite>();
this.m_Sprite_Icon = this.mRoot.FindChild("Select/Signal/IconAnim").GetComponent<XUISprite>();
this.m_Sprite_ServerName = this.mRoot.FindChild("Select/Signal/Name/ServerNameIcon").GetComponent<XUISprite>();
OK,初始化好界面后,我們回過頭寫ShowSelectServer:
/// <summary>
/// 顯示選擇服務器界面
/// </summary>
public void ShowSelectServer()
{
if (this.m_Panel_Select != null)
{
if (this.ShowServerList())
{
//服務器列表按鈕的sprite替換
if (this.m_bShowServerList)
{
this.m_Button_ServerListButton.normalSprite = "image 378";
}
else
{
this.m_Button_ServerListButton.normalSprite = "image 383";
}
this.m_Panel_Select.enabled = true;
GameObject serverList = this.m_Panel_Select.transform.FindChild("ServerList").gameObject;
//serverList.
serverList.SetActive(this.m_bShowServerList);
//如果顯示服務器列表面板,播放偏移動畫
if (this.m_bShowServerList)
{
serverList.transform.localPosition = new Vector3(14, 0, 0);
TweenAlpha.Begin(serverList, 1, 1);
TweenPosition.Begin(serverList, 1, Vector3.zero);
}
}
else
{
Debug.LogError("服務器列表還沒有初始化");
}
}
}
/// <summary>
/// 顯示服務器列表
/// </summary>
public bool ShowServerList()
{
if (this.m_bHasLoadedServerList)
{
return true;
}
if (SystemConfig.ServerList != null)
{
this.m_selectedServerId = SystemConfig.SelectedServerIndex;
this.m_lastSelectServerId = SystemConfig.SelectedServerIndex;
int indexDianxin = 0;
int indexWangtong = 0;
foreach (var serverInfo in SystemConfig.ServerList)
{
IXUIListItem serverItem;
switch (serverInfo.type)
{
case 0:
if (serverInfo.flag == (int)ServerType.Recommend)
{
this.m_reDianxinServerId = serverInfo.id;
}
if (indexDianxin < this.m_List_Dianxin.Count)
{
serverItem = this.m_List_Dianxin.GetItemByIndex(indexDianxin);
}
else
{
serverItem = this.m_List_Dianxin.AddListItem();
}
if (serverItem != null)
{
serverItem.SetText("ServerName",serverInfo.name);
serverItem.SetVisible(true);
serverItem.TipParam = new TipParam
{
Tip = serverItem.Tip
};
serverItem.Id = serverInfo.id;
}
else
{
serverItem.SetVisible(false);
}
indexDianxin++;
break;
case 1:
if (serverInfo.flag == (int)ServerType.Recommend)
{
this.m_reWangtongServerId = serverInfo.id;
}
if (indexWangtong < this.m_List_Wangtong.Count)
{
serverItem = this.m_List_Wangtong.GetItemByIndex(indexWangtong);
}
else
{
serverItem = this.m_List_Wangtong.AddListItem();
}
if (serverItem != null)
{
serverItem.SetText("ServerName", serverInfo.name);
serverItem.SetVisible(true);
serverItem.TipParam = new TipParam
{
Tip = serverItem.Tip
};
serverItem.Id = serverInfo.id;
}
else
{
serverItem.SetVisible(false);
}
indexWangtong++;
break;
}
}
this.m_bHasLoadedServerList = true;
return true;
}
else
{
return false;
}
}
#region 變量定義
private bool m_bShowServerList = false;//是否顯示服務器列表
private bool m_bHasLoadedServerList = false;//是否已經加載過服務器列表
private int m_selectedServerId = -1;
private int m_lastSelectServerId = SystemConfig.SelectedServerIndex;
private int m_reDianxinServerId = -1;//電信推薦服務器id
private int m_reWangtongServerId = -1;//網通推薦服務器idn
#endregion
然后在InitWeight中,讓m_Button_ServerListButton監聽顯示服務器列表和不顯示的事件監聽:
UIEventListener.Get(this.m_Button_ServerListButton.gameObject).onClick = (x) => { this.m_bShowServerList = !this.m_bShowServerList; ShowSelectServer(); };
還有我們點擊某個服務器,得把那個服務器的id,記錄下來,所以我們還是注冊一個事件:
this.m_List_Dianxin.RegisterListOnClickEventHandler(new ListOnClickEventHandler(this.OnServerListItemClick));
/// <summary>
/// 服務器某個被點擊
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private bool OnServerListItemClick(IXUIListItem item)
{
if (null == item)
{
return false;
}
this.m_selectedServerId = item.Id;
ServerInfo info = SystemConfig.GetServerInfoById(this.m_selectedServerId);
bool active = true;
if (info.flag == (int)ServerType.Close || info.flag == (int)ServerType.Maintain)
{
active = false;
}
ShowServerSignal(active, info);
return true;
}
/// <summary>
/// 播放動畫
/// </summary>
/// <param name="bActive"></param>
/// <param name="info"></param>
private void ShowServerSignal(bool bActive,ServerInfo info)
{
if (bActive)
{
if (info.id == 0)
{
this.m_Sprite_Icon.SetSprite("image 967", "Atlas/SelectAtlas/SelectServerAtlas");
this.m_Sprite_ServerName.SetSprite("image 975");
}
else
{
this.m_Sprite_Icon.SetSprite("image 1015", "Atlas/SelectAtlas/SelectServerAtlas");
this.m_Sprite_ServerName.SetSprite("image 1012");
}
if (this.m_Sprite_IconBG != null)
{
this.m_Sprite_IconBG.PlayFlash(false);
this.m_Sprite_ServerName.Alpha = 0f;
this.m_Sprite_Icon.Alpha = 0f;
TweenAlpha.Begin(this.m_Sprite_ServerName.gameObject, 0.8f, 1f);
TweenAlpha.Begin(this.m_Sprite_Icon.gameObject, 0.8f, 1f);
}
}
}
OK,大致就是這么多的代碼,具體步驟:
1.如果選擇服務器Panel不激活的話,就顯示服務器列表,然后激活。
2.注冊一些按鈕的事件
3.記錄下來選擇的服務器id
然后我們點擊選擇按鈕,就發送這個服務器id給游戲網關服務器,網關服務器就把用戶連接到這個游戲服務器中。
下節課,我們就開始進入游戲服務器連接,當然這里就涉及到NetworkManager的編寫,主要是TCp的連接。
