使用工具: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<Button>();closebtn.onClick.AddListener(OnCloseClick);usernameInput = transform.Find("usernameinput").GetComponent<InputField>();passwordInput = transform.Find("passwordinput").GetComponent<InputField>();loginBtn = transform.Find("loginbtn").GetComponent<Button>();registerBtn = transform.Find("registerbtn").GetComponent<Button>();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<Button>();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<InputField>();passwordInput = transform.Find("passwordinput").GetComponent<InputField>();loginBtn = transform.Find("loginbtn").GetComponent<Button>();registerBtn = transform.Find("registerbtn").GetComponent<Button>();loginRequest = GetComponent<LoginRequest>();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;}}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(); });}
}

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 initializationpublic 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 initializationpublic 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 initializationvoid Start() {InitManager();}// Update is called once per framevoid 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<Button>();closebtn.onClick.AddListener(OnCloseClick);usernameInput = transform.Find("usernameinput").GetComponent<InputField>();passwordInput = transform.Find("passwordinput").GetComponent<InputField>();loginBtn = transform.Find("loginbtn").GetComponent<Button>();registerBtn = transform.Find("registerbtn").GetComponent<Button>();loginRequest = GetComponent<LoginRequest>();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<Button>();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<InputField>();passwordInput = transform.Find("passwordinput").GetComponent<InputField>();loginBtn = transform.Find("loginbtn").GetComponent<Button>();registerBtn = transform.Find("registerbtn").GetComponent<Button>();loginRequest = GetComponent<LoginRequest>();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
{//用来管理controllerclass ControllerManager{private Dictionary<RequestCode, BaseController> controllerDict = new Dictionary<RequestCode, BaseController>();//使用字典存储有哪些controllerprivate 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<LoginPanel>();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<Button>();closebtn.onClick.AddListener(OnCloseClick);usernameInput = transform.Find("usernameinput").GetComponent<InputField>();passwordInput = transform.Find("passwordinput").GetComponent<InputField>();loginBtn = transform.Find("loginbtn").GetComponent<Button>();registerBtn = transform.Find("registerbtn").GetComponent<Button>();loginRequest = GetComponent<LoginRequest>();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<Button>();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<InputField>();passwordInput = transform.Find("passwordinput").GetComponent<InputField>();loginBtn = transform.Find("loginbtn").GetComponent<Button>();registerBtn = transform.Find("registerbtn").GetComponent<Button>();loginRequest = GetComponent<LoginRequest>();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<Client> clientList=new List<Client>();//用来保存所有连接的客户端

2、服务器端启动之后启动客户端,无法看到连接情况,这并不是出现了问题,而是没有做相应的输出,所以我们可以在启动函数中输出服务器端启动成功和客户端连接成功的提示。

server类中进行的提示修改:

//建立连接public void Start(){serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);serverSocket.Bind(ipEndPoint);//绑定ipserverSocket.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<Button>();closebtn.onClick.AddListener(OnCloseClick);usernameInput = transform.Find("usernameinput").GetComponent<InputField>();passwordInput = transform.Find("passwordinput").GetComponent<InputField>();loginBtn = transform.Find("loginbtn").GetComponent<Button>();registerBtn = transform.Find("registerbtn").GetComponent<Button>();loginRequest = GetComponent<LoginRequest>();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<Button>();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<InputField>();passwordInput = transform.Find("passwordinput").GetComponent<InputField>();loginBtn = transform.Find("loginbtn").GetComponent<Button>();registerBtn = transform.Find("registerbtn").GetComponent<Button>();loginRequest = GetComponent<LoginRequest>();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>();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<UIPanelType, string> panelPathDict;//存储所有面板Prefab的路径private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件private Stack<BasePanel> panelStack;private MessagePanel msgPanel;public UIManager(GameFacade facade) : base(facade){ParseUIPanelTypeJson();}/// <summary>/// 把某个页面入栈,  把某个页面显示在界面上/// </summary>public void PushPanel(UIPanelType panelType){if (panelStack == null)panelStack = new Stack<BasePanel>();//判断一下栈里面是否有页面if (panelStack.Count > 0){BasePanel topPanel = panelStack.Peek();topPanel.OnPause();}BasePanel panel = GetPanel(panelType);panel.OnEnter();panelStack.Push(panel);}/// <summary>/// 出栈 ,把页面从界面上移除/// </summary>public void PopPanel(){if (panelStack == null)panelStack = new Stack<BasePanel>();if (panelStack.Count <= 0) return;//关闭栈顶页面的显示BasePanel topPanel = panelStack.Pop();topPanel.OnExit();if (panelStack.Count <= 0) return;BasePanel topPanel2 = panelStack.Peek();topPanel2.OnResume();}/// <summary>/// 根据面板类型 得到实例化的面板/// </summary>/// <returns></returns>private BasePanel GetPanel(UIPanelType panelType){if (panelDict == null){panelDict = new Dictionary<UIPanelType, BasePanel>();}//BasePanel panel;//panelDict.TryGetValue(panelType, out panel);//TODOBasePanel 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<BasePanel>().UIMng = this;panelDict.Add(panelType, instPanel.GetComponent<BasePanel>());return instPanel.GetComponent<BasePanel>();}else{return panel;}}[Serializable]class UIPanelTypeJson{public List<UIPanelInfo> infoList;}private void ParseUIPanelTypeJson(){panelPathDict = new Dictionary<UIPanelType, string>();TextAsset ta = Resources.Load<TextAsset>("UIPanelType");UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(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);}/// <summary>/// just for test/// </summary>//public void Test()//{//    string path ;//    panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);//    Debug.Log(path);//}
}

最终的运行结果:

unity网络实战开发(丛林战争)-正式开发阶段(016-数据库设计以及登录处理)相关推荐

  1. 仿联想商城laravel实战---1、仿联想商城需求和数据库设计(lavarel如何搭建项目)...

    仿联想商城laravel实战---1.仿联想商城需求和数据库设计(lavarel如何搭建项目) 一.总结 一句话总结: composer引入lavarel.配置域名.配置apache 1.项目名 le ...

  2. unity网络实战开发(丛林战争)-正式开发阶段(013-游戏服务器端框架搭建)

    使用工具:VS2015 使用语言:c# 作者:Gemini_xujian 参考:siki老师-<丛林战争>视频教程 继上一篇文章内容,这节课讲解一下游戏服务器端的开发. 01-项目目录结构 ...

  3. unity网络实战开发(丛林战争)-正式开发阶段(014-游戏客户端与服务器端连接搭建)

    使用工具:VS2017,unity3d 使用语言:c# 作者:Gemini_xujian 参考:siki老师-<丛林战争>视频教程 上一篇文章中,我已经把服务器端的框架进行了搭建,接下来, ...

  4. unity网络实战开发(丛林战争)-正式开发阶段(018-声音管理器模块的完善)

    使用工具:VS2017,unity3d 使用语言:c# 作者:Gemini_xujian 参考:siki老师-<丛林战争>视频教程 上一篇文章中,已经完成了注册事件的处理,接下来将完善声音 ...

  5. unity网络实战开发(丛林战争)-前期知识准备(012-UI框架开发)

    使用工具:VS2017,Unity2017.3,DoTween插件 使用语言:c# 作者:Gemini_xujian 参考:siki老师-<丛林战争>视频教程 继上一篇文章内容,这节课讲解 ...

  6. unity网络实战开发(丛林战争)-前期知识准备(011-c#连接数据库并实现增删改查以及sql注入问题)

    使用工具:VS2015,Mysql 使用语言:c# 作者:Gemini_xujian 参考:siki老师-<丛林战争>视频教程 继上一篇文章内容,这节课讲解一下数据库的前期连接准备以及通过 ...

  7. unity网络实战开发(丛林战争)-前期知识准备(004-开发TCP客户端的接收数据和发送数据)

    使用工具:VS2015 使用语言:c# 作者:Gemini_xujian 参考:siki老师-<丛林战争>视频教程 继上一篇文章内容,这节课讲解一下客户端的接收数据和发送数据. 首先在现有 ...

  8. unity网络实战开发(丛林战争)-前期知识准备(010-在服务器端解析数据)

    使用工具:VS2015 使用语言:c# 作者:Gemini_xujian 参考:siki老师-<丛林战争>视频教程 继上一篇文章内容,这节课讲解一下在服务器端解析数据. 首先,同前文类同, ...

  9. unity网络实战开发(丛林战争)-前期知识准备(006-修改服务器端开启异步处理客户端连接请求)

    使用工具:VS2015 使用语言:c# 作者:Gemini_xujian 参考:siki老师-<丛林战争>视频教程 继上一篇文章内容,这节课讲解一下修改服务器开启一步处理客户端连接请求. ...

最新文章

  1. 演示:思科IPS在线模式下Inline Interface Mode的响应行为(区别各个防御行为)
  2. Inscribed Figures
  3. js判断wifi_使用JS在浏览器中判断当前网络连接状态的几种方法
  4. BBR如何让Spotify流媒体更流畅?
  5. 如何简单快速调试高大上的谷歌浏览器
  6. 项目下创建文件_Linux 下创建和使用交换文件
  7. 问题 | kali2019.04版中文语言安装后就会乱码
  8. 计算机出现黑屏问题方法派出,电脑重装系统开机常见黑屏问题的解决方法
  9. 飞克速读_5个开源速读应用程序
  10. linux下解包bin二进制文件_Linux系统bin文件打包方法
  11. visual studio 2008 提示 “函数xxx 已有主体”
  12. Intel NUC10i7FNH 寒霜峡谷测试体验
  13. 每天学命令deletePlaceBlockage
  14. d610网络计算机,尼康D610评测:机身细节
  15. pySerial使用初步
  16. SpringBoot获取企业微信token
  17. Python中‘r‘,‘r+‘,‘w‘,‘w+‘,‘a‘,‘a+‘区别总结
  18. UVa1618 弱键
  19. sqlserver数据库导入
  20. enq: TX - row lock contention等待事件

热门文章

  1. LCM通信包在Windows 64位环境下的编译
  2. html+js实现日期倒计时
  3. php smarty模板配置,Smarty模板配置实例简析
  4. django搭建 - windos 服务器配置与设置
  5. 破解微信本地数据库,无法找回删除的聊天记录
  6. 人力资源管理系统开发----项目构建
  7. tensorflow建立模型失败The following previous layers were accessed without issue
  8. MUI底部菜单栏 uniapp 底部菜单栏
  9. 1278: 实现计算器基本功能(1级)编写程序分模块设计实现计算器的基本运算功能(加减乘除,除数默认不为0)。
  10. 轻松记账工程冲刺第二阶段4