使用工具:VS2017,unity3d
使用语言:c#
作者:Gemini_xujian
参考:siki老师-《丛林战争》视频教程
上一篇文章中,已经完成了游戏场景与开始界面UI的搭建,接下来将对数据库和登录请求响应等操作进行处理。
01-设计数据库表(用户表和战绩表)
首先,我们需要设计数据库中的表,第一个就是用户表,用来存储用户信息,包含的列有id/name/password,第二个表是数据表,用来存储玩家的战绩,包含的列有id/userid/totalCount/winCount,在创建数据库表的时候,需要我们提前安装好MySQL,对于数据库的一些操作,我在前面的文章中有提到过,感兴趣的同学可以翻一翻前面的内容。创建好两个表之后,需要将战绩表的外键设置为用户表中的主键。数据库结构及表内容如图所示:
02-处理登录按钮的点击,校验账号信息是否为空并提示
我们需要在本地对输入的用户名和密码进行简单的校验,最基本的校验就是空的情况处理,在loginpanel脚本中,我们首先得到用户名输入框和密码输入框以及登录和注册按钮,然后赋值,赋值的时候需要注意要在start方法和enter方法中都进行赋值,然后为按钮注册事件,在LoginPanel中分别创建登陆点击处理方法和注册点击处理方法,在onloginclick方法中对输入内容进行判断,判断其是否为空。修改后代码如下:
LoginPanel.cs:
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LoginPanel : BasePanel
{
private Button closebtn;
private InputField usernameInput;
private InputField passwordInput;
private Button loginBtn;
private Button registerBtn;
private void Start()
{
closebtn = transform.Find("closebtn").GetComponent
03-代码修改
在baserequest类中添加一个requestcode枚举类型变量,对应一个响应的requestcode类型,修改后的代码如下:
baserequest.cs:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BaseRequest : MonoBehaviour {
protected RequestCode requestCode = RequestCode.None;
protected ActionCode actionCode=ActionCode.None;
protected GameFacade facade;
// Use this for initialization
public virtual void Awake () {
GameFacade.Instance.AddRequest(actionCode,this);
facade = GameFacade.Instance;
}
//请求的发起
public virtual void SendRequest()
{
}
//请求的响应
public virtual void OnResponse(string data)
{
}
public virtual void OnDestroy()
{
}
}
04-在客户端发送登录请求
在共享工程common中,我们在ActionCode枚举中需要添加Login和Register枚举值,在RequestCode枚举中需要添加User枚举值,actioncode中的值枚举值对应了我们服务器端发送数据到客户端的对应处理类以及客户端发送数据到服务器端找到对应的方法, RequestCode用于客户端发送数据到服务器端时用于找到对应的处理类,添加完后,替换掉unity工程中的common.dll,修改后的共享工程代码如下:
ActionCode.cs:
using System;
using System.Collections.Generic;
using System.Text;
namespace Common
{
public enum ActionCode
{
None,
Login,
Register,
}
}
RequestCode.cs:
using System;
using System.Collections.Generic;
using System.Text;
namespace Common
{
public enum RequestCode
{
None,
User,
}
}
接下来,需要创建一个用于处理登录请求的类LoginRequest类,这个类继承自BaseRequest类,为登录请求类中的请求类型requestCode和事件类型变量actionCode进行赋值,然后创建一个发送请求的方法用于发送用户名和密码,最终发起请求需要通过clientmanager类来进行,为了降低耦合性,我们在gamefacade类中对发送请求的方法进行转接,所以需要我们在gamefacade中创建一个方法调用clientmanager中的发起请求方法,为了能够使loginrequest类调用gamefacade,我们可以在baserequest类中得到gamefacade实例,然后在baserequest类中再重载一个sendrequest方法,这个方法与我们之前创建的此方法不同之处在于,此处的sendrequest方法我们传入了一个字符串类型的参数,参数表示的含义是数据信息data,然后在loginrequest类就可以调用父类中的发起请求的方法。
为了方便请求与UI的交互,我们将loginrequest类直接绑定到loginpanel的面板上,然后在loginpanel类中得到loginrequest类的实例,然后在loginpanel类中就可以在点击登录按钮后调用登录请求,向服务器端发起登录请求。修改后的代码类如下所示:
LoginRequest.cs:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoginRequest : BaseRequest {
private void Start()
{
requestCode = RequestCode.User;
actionCode = ActionCode.Login;
}
public void SendRequest(string username,string password)
{
string data = username + "," + password;
base.SendRequest(data);
}
}
BaseRequest.cs:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BaseRequest : MonoBehaviour {
protected RequestCode requestCode = RequestCode.None;
protected ActionCode actionCode=ActionCode.None;
protected GameFacade facade;
// Use this for initialization
public virtual void Awake () {
GameFacade.Instance.AddRequest(actionCode,this);
facade = GameFacade.Instance;
}
public void SendRequest(string data)
{
facade.SendRequest(requestCode, actionCode, data);
}
//请求的发起
public virtual void SendRequest()
{
}
//请求的响应
public virtual void OnResponse(string data)
{
}
public virtual void OnDestroy()
{
}
}
GameFacade.cs:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameFacade : MonoBehaviour {
private static GameFacade _instance;
public static GameFacade Instance
{
get
{
return _instance;
}
}
private UIManager uiMng;
private AudioManager audioMng;
private PlayerManager playerMng;
private RequestManager requestMng;
private CameraManager cameraMng;
private ClientManager clientMng;
private void Awake()
{
if (_instance != null)
{
Destroy(this.gameObject);
return;
}
_instance = this;
}
// Use this for initialization
void Start() {
InitManager();
}
// Update is called once per frame
void Update() {
}
private void InitManager()
{
uiMng = new UIManager(this);
audioMng = new AudioManager(this);
playerMng = new PlayerManager(this);
requestMng = new RequestManager(this);
cameraMng = new CameraManager(this);
clientMng = new ClientManager(this);
uiMng.OnInit();
audioMng.OnInit();
playerMng.OnInit();
requestMng.OnInit();
cameraMng.OnInit();
clientMng.OnInit();
}
private void DestroyManager()
{
uiMng.OnDestory();
audioMng.OnDestory();
playerMng.OnDestory();
requestMng.OnDestory();
cameraMng.OnDestory();
clientMng.OnDestory();
}
private void OnDestroy()
{
DestroyManager();
}
public void AddRequest(ActionCode actionCode, BaseRequest request)
{
requestMng.AddRequest(actionCode, request);
}
public void RemoveRequest(ActionCode actionCode)
{
requestMng.RemoveRequest(actionCode);
}
public void HandleResponse(ActionCode actionCode, string data)
{
requestMng.HandleResponse(actionCode, data);
}
public void ShowMessage(string msg)
{
uiMng.ShowMessage(msg);
}
public void SendRequest(RequestCode requestCode, ActionCode actionCode, string data)
{
clientMng.SendRequest(requestCode, actionCode, data);
}
}
LoginPanel.cs:
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LoginPanel : BasePanel
{
private Button closebtn;
private InputField usernameInput;
private InputField passwordInput;
private Button loginBtn;
private Button registerBtn;
private LoginRequest loginRequest;
private void Start()
{
closebtn = transform.Find("closebtn").GetComponent();
closebtn.onClick.AddListener(OnCloseClick);
usernameInput = transform.Find("usernameinput").GetComponent();
passwordInput = transform.Find("passwordinput").GetComponent();
loginBtn = transform.Find("loginbtn").GetComponent();
registerBtn = transform.Find("registerbtn").GetComponent();
loginRequest = GetComponent();
loginBtn.onClick.AddListener(OnLoginClick);
registerBtn.onClick.AddListener(OnRegisterClick);
}
public override void OnEnter()
{
base.OnEnter();
gameObject.SetActive(true);
if (closebtn == null)
{
closebtn = transform.Find("closebtn").GetComponent();
closebtn.onClick.AddListener(OnCloseClick);
}
transform.localScale = Vector3.zero;
transform.localPosition = new Vector3(800,0,0);
transform.DOScale(1, 0.4f);
transform.DOLocalMove(Vector3.zero, 0.4f);
usernameInput = transform.Find("usernameinput").GetComponent();
passwordInput = transform.Find("passwordinput").GetComponent();
loginBtn = transform.Find("loginbtn").GetComponent();
registerBtn = transform.Find("registerbtn").GetComponent();
loginRequest = GetComponent();
loginBtn.onClick.AddListener(OnLoginClick);
registerBtn.onClick.AddListener(OnRegisterClick);
}
public void OnLoginClick()
{
string msg = string.Empty;
if (string.IsNullOrEmpty(usernameInput.text))
{
msg += "用户名不能为空 ";
}
if (string.IsNullOrEmpty(passwordInput.text))
{
msg += "密码不能为空";
}
if (msg != string.Empty)
{
uiMng.ShowMessage(msg);return;
}
loginRequest.SendRequest(usernameInput.text, passwordInput.text);
}
public void OnRegisterClick()
{
}
public override void OnExit()
{
base.OnExit();
gameObject.SetActive(false);
}
public override void OnPause()
{
base.OnPause();
}
public override void OnResume()
{
base.OnResume();
}
public void OnCloseClick()
{
transform.DOScale(0, 0.4f);
transform.DOLocalMove(new Vector3(800,0,0), 0.4f).OnComplete(()=> { uiMng.PopPanel(); });
}
}
05-创建UserController、User和UserDAO做数据库查询校验
经过以上几个步骤,我们已经处理完了客户端请求的发起,接下来需要对服务器端进行登录请求的处理。
在服务器端,我们需要创建一个对应的controller来处理登录请求,新建一个UserController类,继承BaseController类,将basecontroller类中的请求类型变量设置权限为protected,然后在usercontroller的初始化方法中对请求类型进行赋值,之后,我们需要在controller统一管理类controllermanager类中将usercontroller添加到字典中进行保存。
在usercontroller中,我们需要创建事件处理方法,即登录方法Login,登录方法用于处理登录请求。每一个请求需要一些参数,我们在controllermanager类的请求处理方法中,我们将数据,客户端,服务器自身作为参数传递了过去,所以我们的登录方法也需要有这几个参数。
在这里我们需要发起对数据库的请求,所以需要添加一个与数据库对应的表类,以及一个数据库的用户操作类。首先,我们新建一个user类与数据库的表的信息相对应,然后,我们新建一个userdao类来对数据库进行sql指令的增删改查操作,修改后的类代码如下所示:
BaseController:
using Common;
using GameServer.Servers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameServer.Controller
{
abstract class BaseController
{
protected RequestCode requestCode = RequestCode.None;//设置请求类型
public RequestCode RequestCode
{
get
{
return requestCode;
}
}
//默认的处理方法
public virtual string DefaultHandle(string data,Client client,Server server)
{
return null;
}
}
}
UserController:
using Common;
using GameServer.Servers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameServer.Controller
{
class UserController:BaseController
{
public UserController()
{
requestCode = RequestCode.User;
}
public void Login(string data,Client client,Server server)
{
}
}
}
ControllerManager:
using Common;
using GameServer.Servers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace GameServer.Controller
{//用来管理controller
class ControllerManager
{
private Dictionary controllerDict = new Dictionary();//使用字典存储有哪些controller
private Server server;
//构造方法
public ControllerManager(Server server)
{
this.server = server;
InitController();
}
//初始化方法
void InitController()
{
DefaultController defaultController = new DefaultController();
controllerDict.Add(defaultController.RequestCode,defaultController);
controllerDict.Add(RequestCode.User, new UserController());
}
//处理请求
public void HandleRequest(RequestCode requestCode,ActionCode actionCode,string data,Client client)
{
BaseController controller;
bool isGet = controllerDict.TryGetValue(requestCode, out controller);
if (isGet == false)
{
Console.WriteLine("无法得到requestcode:"+requestCode+"所对应的controller,无法处理请求");
return;
}
//通过反射得到
string methodName = Enum.GetName(typeof(ActionCode),actionCode);//得到方法名
MethodInfo mi= controller.GetType().GetMethod(methodName);//得到方法的信息
if (mi == null)
{
Console.WriteLine("[警告]在controller【"+controller.GetType()+"】"+"中没有对应的处理方法【"+methodName+"】");
return;
}
object[] parameters = new object[] { data,client,server};
object o= mi.Invoke(controller, parameters);//反射调用方法并将得到返回值
//如果返回值为空,则表示没有得到,那么就结束请求的进一步处理
if(string.IsNullOrEmpty(o as string))
{
return;
}
server.SendResponse(client,actionCode,o as string);//调用server类中的响应方法,给客户端响应
}
}
}
User:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameServer.Model
{
class User
{
public User(int id,string username,string password)
{
Id = id;
Username = username;
Password = password;
}
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
}
UserDAO:
using GameServer.Model;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameServer.DAO
{
class UserDAO
{
//验证用户
public User VerifyUser(MySqlConnection conn, string username,string password)
{
MySqlDataReader reader=null;
try
{
MySqlCommand cmd = new MySqlCommand("select * from user where username=@username and password=@pwd", conn);
cmd.Parameters.AddWithValue("username", username);
cmd.Parameters.AddWithValue("pwd", password);
reader = cmd.ExecuteReader();
if (reader.Read())
{
int id = reader.GetInt32("id");
string name = reader.GetString("username");
string pwd = reader.GetString("password");
User user = new User(id, name, pwd);
return user;
}
else
{
return null;
}
}
catch (Exception e)
{
Console.WriteLine("在verifyuser的时候出现异常:"+e);
}
finally
{
reader.Close();
}
return null;
}
}
}
06-在服务器端发送登录的响应
前面我们完成了对数据库中数据的操作函数,接下里,我们需要在usercontroller中完成对登录请求的响应。
在login方法中,我们首先需要将客户端发送过来的数据进行分割,得到字符串数组,然后得到通过userdao的返回值得到user变量,如果user为空,则表示没有查询到相应的结果,我们通过新建一个枚举类型returncode来对结果进行返回,所以我们还需要在共享工程common中新建一个returncode的枚举类型。需要注意的是,在调用userdao中的查询方法时,需要得到mysqlconnection实例,所以我们需要在client中定义一个外界可访问的mysqlconnection变量的get方法用于获得此变量。修改后的代码类如下所示:
UserController:
using Common;
using GameServer.DAO;
using GameServer.Model;
using GameServer.Servers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameServer.Controller
{
class UserController:BaseController
{
private UserDAO userDAO=new UserDAO();
public UserController()
{
requestCode = RequestCode.User;
}
public string Login(string data,Client client,Server server)
{
string[] strs = data.Split(',');
User user= userDAO.VerifyUser(client.MySQLConn, strs[0], strs[1]);
if (user == null)
{
//Enum.GetName(typeof(ReturnCode), ReturnCode.Fail);
return ((int)ReturnCode.Fail).ToString();
}else
{
return ((int)ReturnCode.Success).ToString();
}
}
}
}
Client:
using Common;
using GameServer.Tool;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace GameServer.Servers
{//用来处理与客户端的通信问题
class Client
{
private Socket clientSocket;
private Server server;//持有一个server类的引用
private Message msg = new Message();
private MySqlConnection mysqlConn;//持有一个对数据库的连接
public MySqlConnection MySQLConn
{
get
{
return mysqlConn;
}
}
public Client() { }
public Client(Socket clientSocket,Server server)
{
this.clientSocket = clientSocket;
this.server = server;
mysqlConn = ConnHelper.Connect();//建立于数据库的连接
}
//开启监听
public void Start()
{
clientSocket.BeginReceive(msg.Data,msg.StartIndex, msg.RemainSizs, SocketFlags.None,ReceiveCallBack, null);
}
//接收监听的回调函数
private void ReceiveCallBack(IAsyncResult ar)
{
//做异常捕捉
try
{
int count = clientSocket.EndReceive(ar);//结束监听,并返回接收到的数据长度
//如果count=0说明客户端已经断开连接,则直接关闭
if (count == 0)
{
Close();
}
msg.ReadMessage(count,OnProcessMessage);//对消息的处理,进行消息的解析
Start();//重新调用监听函数
}
catch (Exception e)
{
Console.WriteLine(e);
//出现异常则退出
Close();
}
}
private void OnProcessMessage(RequestCode requestCode,ActionCode actionCode,string data)
{
server.HandleRequest(requestCode, actionCode, data, this);
}
private void Close()
{
ConnHelper.CloseConnection(mysqlConn);
if (clientSocket != null)
{
clientSocket.Close();
}
server.RemoveClient(this);
}
//向客户端发送数据
public void Send(ActionCode actionCode,string data)
{
byte[] bytes = Message.PackData(actionCode, data);
clientSocket.Send(bytes);
}
}
}
ReturnCode:
using System;
using System.Collections.Generic;
using System.Text;
namespace Common
{
public enum ReturnCode
{
Success,
Fail,
}
}
07-在客户端处理登录的响应
在unity工程中,我们需要处理客户端对登录的响应,在LoginRequest类中,我们重写父类中的OnResonse方法,将服务器端返回的结果进行解析并做出相应的处理。
首先,将returncode值转成ReturnCode类型,然后在loginpanel中,我们定义一个对返回值进行相应处理的方法,然后,在LoginRequest类中,我们得到loginpanel并调用此方法。修改后的代码如下:
LoginRequest.cs:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoginRequest : BaseRequest {
private LoginPanel loginPanel;
private void Start()
{
loginPanel = GetComponent();
requestCode = RequestCode.User;
actionCode = ActionCode.Login;
}
public void SendRequest(string username,string password)
{
string data = username + "," + password;
base.SendRequest(data);
}
public override void OnResponse(string data)
{
ReturnCode returnCode = (ReturnCode)int.Parse(data);
loginPanel.OnLoginResponse(returnCode);
}
}
LoginPanel.cs:
using Common;
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LoginPanel : BasePanel
{
private Button closebtn;
private InputField usernameInput;
private InputField passwordInput;
private Button loginBtn;
private Button registerBtn;
private LoginRequest loginRequest;
private void Start()
{
closebtn = transform.Find("closebtn").GetComponent();
closebtn.onClick.AddListener(OnCloseClick);
usernameInput = transform.Find("usernameinput").GetComponent();
passwordInput = transform.Find("passwordinput").GetComponent();
loginBtn = transform.Find("loginbtn").GetComponent();
registerBtn = transform.Find("registerbtn").GetComponent();
loginRequest = GetComponent();
loginBtn.onClick.AddListener(OnLoginClick);
registerBtn.onClick.AddListener(OnRegisterClick);
}
public override void OnEnter()
{
base.OnEnter();
gameObject.SetActive(true);
if (closebtn == null)
{
closebtn = transform.Find("closebtn").GetComponent();
closebtn.onClick.AddListener(OnCloseClick);
}
transform.localScale = Vector3.zero;
transform.localPosition = new Vector3(800,0,0);
transform.DOScale(1, 0.4f);
transform.DOLocalMove(Vector3.zero, 0.4f);
usernameInput = transform.Find("usernameinput").GetComponent();
passwordInput = transform.Find("passwordinput").GetComponent();
loginBtn = transform.Find("loginbtn").GetComponent();
registerBtn = transform.Find("registerbtn").GetComponent();
loginRequest = GetComponent();
loginBtn.onClick.AddListener(OnLoginClick);
registerBtn.onClick.AddListener(OnRegisterClick);
}
public void OnLoginClick()
{
string msg = string.Empty;
if (string.IsNullOrEmpty(usernameInput.text))
{
msg += "用户名不能为空 ";
}
if (string.IsNullOrEmpty(passwordInput.text))
{
msg += "密码不能为空";
}
if (msg != string.Empty)
{
uiMng.ShowMessage(msg);return;
}
loginRequest.SendRequest(usernameInput.text, passwordInput.text);
}
public void OnRegisterClick()
{
}
public override void OnExit()
{
base.OnExit();
gameObject.SetActive(false);
}
public override void OnPause()
{
base.OnPause();
}
public override void OnResume()
{
base.OnResume();
}
public void OnCloseClick()
{
transform.DOScale(0, 0.4f);
transform.DOLocalMove(new Vector3(800,0,0), 0.4f).OnComplete(()=> { uiMng.PopPanel(); });
}
public void OnLoginResponse(ReturnCode returnCode)
{
if (returnCode == ReturnCode.Success)
{
}
else
{
uiMng.ShowMessage("密码或用户名错误,无法登录,请重新输入");
}
}
}
08-测试登录流程
首先,我们需要将服务器端启动起来,在启动时需要我们在主函数中调用server.start()方法,这是服务器端初始化的方法。
遇到的问题:
1、在服务器端的server类中,我们定义了一个clientList的集合,这个集合需要进行初始化
private List clientList=new List();//用来保存所有连接的客户端
2、服务器端启动之后启动客户端,无法看到连接情况,这并不是出现了问题,而是没有做相应的输出,所以我们可以在启动函数中输出服务器端启动成功和客户端连接成功的提示。
server类中进行的提示修改:
//建立连接
public void Start()
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(ipEndPoint);//绑定ip
serverSocket.Listen(0);//设置监听,为0表示不限制连接数
Console.WriteLine("服务器端启动");////////////添加的提示/////////////
serverSocket.BeginAccept(AcceptCallBack, null);//开始接收客户端连接
}
//创建接收连接的回调函数
private void AcceptCallBack(IAsyncResult ar)
{
Socket clientSocket = serverSocket.EndAccept(ar);//接收到连接并将返回的客户端socket进行得到
Client client = new Client(clientSocket, this);//创建一个client类,用来管理一个与客户端的连接
client.Start();
Console.WriteLine("客户端连接成功");//////////添加的提示//////////
clientList.Add(client);//将此客户端添加到list集合中
}
3、在unity工程中报错,不能找到对应的reqeust,查找后发现是由于Awake方法执行比start方法早,所以还没有给requestcode和actioncode赋上新值。修改的方法是在loginrequest类中重写awake方法,然后将所有在start方法中的方法放在base.awake()这句代码的上面,这样就可以在赋完值之后再调用父类中的awake方法了。
4、在unity工程中报错,原因是我们使用了多线程,所以在调用信息显示的方法时不在同一个线程会出错,解决办法是在messagepanel中定义一个异步的信息显示方法,然后用update方法来实时判断是否需要显示信息,然后在uimanager中转发调用messagepanel中的异步消息显示方法,最后在loginpanel中调用这个方法来显示我们的信息即可。修改后的代码如下所示:
LoginPanel.cs:
using Common;
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LoginPanel : BasePanel
{
private Button closebtn;
private InputField usernameInput;
private InputField passwordInput;
private Button loginBtn;
private Button registerBtn;
private LoginRequest loginRequest;
private void Start()
{
closebtn = transform.Find("closebtn").GetComponent();
closebtn.onClick.AddListener(OnCloseClick);
usernameInput = transform.Find("usernameinput").GetComponent();
passwordInput = transform.Find("passwordinput").GetComponent();
loginBtn = transform.Find("loginbtn").GetComponent();
registerBtn = transform.Find("registerbtn").GetComponent();
loginRequest = GetComponent();
loginBtn.onClick.AddListener(OnLoginClick);
registerBtn.onClick.AddListener(OnRegisterClick);
}
public override void OnEnter()
{
base.OnEnter();
gameObject.SetActive(true);
if (closebtn == null)
{
closebtn = transform.Find("closebtn").GetComponent();
closebtn.onClick.AddListener(OnCloseClick);
}
transform.localScale = Vector3.zero;
transform.localPosition = new Vector3(800,0,0);
transform.DOScale(1, 0.4f);
transform.DOLocalMove(Vector3.zero, 0.4f);
usernameInput = transform.Find("usernameinput").GetComponent();
passwordInput = transform.Find("passwordinput").GetComponent();
loginBtn = transform.Find("loginbtn").GetComponent();
registerBtn = transform.Find("registerbtn").GetComponent();
loginRequest = GetComponent();
loginBtn.onClick.AddListener(OnLoginClick);
registerBtn.onClick.AddListener(OnRegisterClick);
}
public void OnLoginClick()
{
string msg = string.Empty;
if (string.IsNullOrEmpty(usernameInput.text))
{
msg += "用户名不能为空 ";
}
if (string.IsNullOrEmpty(passwordInput.text))
{
msg += "密码不能为空";
}
if (msg != string.Empty)
{
uiMng.ShowMessage(msg);return;
}
loginRequest.SendRequest(usernameInput.text, passwordInput.text);
}
public void OnRegisterClick()
{
}
public override void OnExit()
{
base.OnExit();
gameObject.SetActive(false);
}
public override void OnPause()
{
base.OnPause();
}
public override void OnResume()
{
base.OnResume();
}
public void OnCloseClick()
{
transform.DOScale(0, 0.4f);
transform.DOLocalMove(new Vector3(800,0,0), 0.4f).OnComplete(()=> { uiMng.PopPanel(); });
}
public void OnLoginResponse(ReturnCode returnCode)
{
if (returnCode == ReturnCode.Success)
{
}
else
{
uiMng.ShowMessageSync("密码或用户名错误,无法登录,请重新输入");
}
}
}
MessagePanel.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MessagePanel : BasePanel
{
private Text text;
private float showTime=1f;
private string message = null;
public override void OnEnter()
{
base.OnEnter();
text = GetComponent();
text.enabled = false;
uiMng.InjectMsgPanel(this);
}
private void Update()
{
if (message != null)
{
ShowMessage(message);
}
}
public override void OnExit()
{
base.OnExit();
}
public override void OnPause()
{
base.OnPause();
}
public override void OnResume()
{
base.OnResume();
}
public void ShowMessage(string msg)
{
text.CrossFadeAlpha(1, 0.2f, true);
text.text = msg;
text.enabled = true;
Invoke("Hide", showTime);
}
public void ShowMessageSync(string msg)
{
message = msg;
}
private void Hide()
{
text.CrossFadeAlpha(0, showTime, true);
}
}
UIManager.cs:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class UIManager:BaseManager {
///
/// 单例模式的核心
/// 1,定义一个静态的对象 在外界访问 在内部构造
/// 2,构造方法私有化
//private static UIManager _instance;
//public static UIManager Instance
//{
// get
// {
// if (_instance == null)
// {
// _instance = new UIManager();
// }
// return _instance;
// }
//}
public override void OnInit()
{
base.OnInit();
PushPanel(UIPanelType.Message);
PushPanel(UIPanelType.Start);
}
private Transform canvasTransform;
private Transform CanvasTransform
{
get
{
if (canvasTransform == null)
{
canvasTransform = GameObject.Find("Canvas").transform;
}
return canvasTransform;
}
}
private Dictionary panelPathDict;//存储所有面板Prefab的路径
private Dictionary panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
private Stack panelStack;
private MessagePanel msgPanel;
public UIManager(GameFacade facade) : base(facade)
{
ParseUIPanelTypeJson();
}
///
/// 把某个页面入栈, 把某个页面显示在界面上
///
public void PushPanel(UIPanelType panelType)
{
if (panelStack == null)
panelStack = new Stack();
//判断一下栈里面是否有页面
if (panelStack.Count > 0)
{
BasePanel topPanel = panelStack.Peek();
topPanel.OnPause();
}
BasePanel panel = GetPanel(panelType);
panel.OnEnter();
panelStack.Push(panel);
}
///
/// 出栈 ,把页面从界面上移除
///
public void PopPanel()
{
if (panelStack == null)
panelStack = new Stack();
if (panelStack.Count <= 0) return;
//关闭栈顶页面的显示
BasePanel topPanel = panelStack.Pop();
topPanel.OnExit();
if (panelStack.Count <= 0) return;
BasePanel topPanel2 = panelStack.Peek();
topPanel2.OnResume();
}
///
/// 根据面板类型 得到实例化的面板
///
///
private BasePanel GetPanel(UIPanelType panelType)
{
if (panelDict == null)
{
panelDict = new Dictionary();
}
//BasePanel panel;
//panelDict.TryGetValue(panelType, out panel);//TODO
BasePanel panel = panelDict.TryGet(panelType);
if (panel == null)
{
//如果找不到,那么就找这个面板的prefab的路径,然后去根据prefab去实例化面板
//string path;
//panelPathDict.TryGetValue(panelType, out path);
string path = panelPathDict.TryGet(panelType);
GameObject instPanel = GameObject.Instantiate(Resources.Load(path)) as GameObject;
instPanel.transform.SetParent(CanvasTransform,false);
instPanel.GetComponent().UIMng = this;
panelDict.Add(panelType, instPanel.GetComponent());
return instPanel.GetComponent();
}
else
{
return panel;
}
}
[Serializable]
class UIPanelTypeJson
{
public List infoList;
}
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary();
TextAsset ta = Resources.Load("UIPanelType");
UIPanelTypeJson jsonObject = JsonUtility.FromJson(ta.text);
foreach (UIPanelInfo info in jsonObject.infoList)
{
//Debug.Log(info.panelType);
panelPathDict.Add(info.panelType, info.path);
}
}
public void InjectMsgPanel(MessagePanel msgPanel)
{
this.msgPanel = msgPanel;
}
public void ShowMessage(string msg)
{
if (msg == null)
{
Debug.Log("无法显示提示信息,msgpanel为空");
return;
}
msgPanel.ShowMessage(msg);
}
public void ShowMessageSync(string msg)
{
if (msg == null)
{
Debug.Log("无法显示提示信息,msgpanel为空");
return;
}
msgPanel.ShowMessageSync(msg);
}
///
/// just for test
///
//public void Test()
//{
// string path ;
// panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);
// Debug.Log(path);
//}
}
最终的运行结果: