使用工具:VS2017,unity3d
使用语言:c#
作者:Gemini_xujian
参考:siki老师-《丛林战争》视频教程
上一篇文章中,已经完成了数据库和登录请求响应等操作进行处理,接下来将完善注册面板UI逻辑以及处理用户注册请求和响应。
01-注册面板显示和隐藏动画的设计
先上修改的代码:
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
RegisterPanel.cs:
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RegisterPanel : BasePanel
{
private Button closebtn;
private InputField usernameInput;
private InputField passwordInput;
private InputField repasswordInput;
private Button registerbtn;
private void Start()
{
usernameInput = transform.Find("usernameinput").GetComponent();
passwordInput = transform.Find("passwordinput").GetComponent();
repasswordInput = transform.Find("repasswordinput").GetComponent();
closebtn = transform.Find("closebtn").GetComponent();
registerbtn = transform.Find("registerbtn").GetComponent();
closebtn.onClick.AddListener(OnCloseClick);
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();
repasswordInput = transform.Find("repasswordinput").GetComponent();
closebtn = transform.Find("closebtn").GetComponent();
registerbtn = transform.Find("registerbtn").GetComponent();
}
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 OnRegisterClick()
{
}
}
说明:在LoginPanel类中,主要是对注册按钮的点击事件做了处理,因为我们需要显示出来注册面板,所以需要在点击注册按钮后将注册面板加载进来并将当前面板隐藏。在RegisterPanel类中,参照了LoginPanel类的实现,有异曲同工之妙,所以就不详细解释了,目前主要只是实现了面板的关闭按钮的点击事件以及隐藏动画的处理。
02-在客户端发送注册请求以及处理响应
客户端发送注册请求到服务端思路跟登录请求类似,在RegisterPanel类中,我处理了点击注册按钮时的事件,首先是本地对输入的用户名、密码、重复密码做简单验证,如果符合要求就可以发起注册请求,代码如下:
using Common;
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RegisterPanel : BasePanel
{
private Button closebtn;
private InputField usernameInput;
private InputField passwordInput;
private InputField repasswordInput;
private Button registerbtn;
private RegisterRequest registerRequest;
private void Start()
{
registerRequest = GetComponent();
usernameInput = transform.Find("usernameinput").GetComponent();
passwordInput = transform.Find("passwordinput").GetComponent();
repasswordInput = transform.Find("repasswordinput").GetComponent();
closebtn = transform.Find("closebtn").GetComponent();
registerbtn = transform.Find("registerbtn").GetComponent();
closebtn.onClick.AddListener(OnCloseClick);
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();
repasswordInput = transform.Find("repasswordinput").GetComponent();
closebtn = transform.Find("closebtn").GetComponent();
registerbtn = transform.Find("registerbtn").GetComponent();
}
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 OnRegisterClick()
{
string msg = "";
if (string.IsNullOrEmpty(usernameInput.text))
{
msg = "用户名不能为空";
}else if (string.IsNullOrEmpty(passwordInput.text))
{
msg = "密码不能为空";
}else if(string.IsNullOrEmpty(repasswordInput.text))
{
msg = "重复密码不能为空";
}else if (!passwordInput.text.Equals(repasswordInput.text))
{
msg = "密码与重复密码不一致";
}
if (!string.IsNullOrEmpty(msg))
{
uiMng.ShowMessage(msg);
return;
}
registerRequest.SendRequest(usernameInput.text,passwordInput.text);
}
public void OnRegisterResponse(ReturnCode returnCode)
{
if (returnCode == ReturnCode.Success)
{
uiMng.ShowMessageSync("注册成功");
}
else
{
uiMng.ShowMessageSync("注册失败,用户名重复,请重新注册");
}
}
}
可以看到我在注册点击事件的处理方法中做了各种校验,如果通过校验,就会发起注册请求。这个注册请求会调用注册请求类(RegisterRequest),此类的实现代码如下;
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RegisterRequest : BaseRequest {
private RegisterPanel registerPanel;
public override void Awake()
{
requestCode = RequestCode.User;
actionCode = ActionCode.Register;
registerPanel = GetComponent();
base.Awake();
}
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);
registerPanel.OnRegisterResponse(returnCode);
}
}
这个类是在写完注册点击事件方法之后创建的c#脚本,并将此脚本绑定在了注册面板上,并应用了预制体。在发起请求后,当接收到来自服务端的响应,我们使用OnResponse方法来处理,将返回结果进行显示。
03-在服务器端处理注册请求
首先上代码:
UserDAO.cs:
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;
}
//根据用户名查找用户是否存在
public bool GetUserByUsername(MySqlConnection conn, string username)
{
MySqlDataReader reader = null;
try
{
MySqlCommand cmd = new MySqlCommand("select * from user where username=@username", conn);
cmd.Parameters.AddWithValue("username", username);
reader = cmd.ExecuteReader();
if (reader.HasRows)
{
return true;
}
else
{
return false;
}
}
catch (Exception e)
{
Console.WriteLine("在getuserbyusername的时候出现异常:" + e);
}
finally
{
reader.Close();
}
return false;
}
public void AddUser(MySqlConnection conn, string username,string password)
{
try
{
MySqlCommand cmd = new MySqlCommand("insert into user set username=@username , password=@pwd", conn);
cmd.Parameters.AddWithValue("username", username);
cmd.Parameters.AddWithValue("pwd", password);
cmd.ExecuteNonQuery();
}
catch (Exception e)
{
Console.WriteLine("在adduser的时候出现异常:" + e);
}
}
}
}
UserController.cs:
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();
}
}
public string Register(string data, Client client, Server server)
{
string[] strs = data.Split(',');
string username = strs[0];
string password = strs[1];
bool res = userDAO.GetUserByUsername(client.MySQLConn,username);
if (res==false)
{
userDAO.AddUser(client.MySQLConn, username, password);
return ((int)ReturnCode.Success).ToString();
}
else
{
return ((int)ReturnCode.Fail).ToString();
}
}
}
}
在UserDAO中,新建了两个方法,一个是通过用户名判断用户是否已经存在数据库中,一个是添加用户。
在UserController中,我们创建了一个响应请求的方法--Register方法,这个方法用来判断注册是否成功,首先判断用户是否已经存在,若存在返回fail,如果不存在则进行注册添加用户并返回success。
04-测试注册功能
测试发现问题:客户端在关闭时会在客户端以及服务端出现异常。
问题截图:
服务器端:
客户端:
问题分析以及解决办法:
分析:报错原因是因为在客户端强制关闭后,客户端仍然尝试接受数据,但此时socket连接可能已经断开或为空,所以会报错,而服务器端也是因此导致的报错。
解决:在客户端ClientManager类中的ReceiveCallBack方法内加入以下代码:
在服务器端Client类中的Start方法中添加如下代码:
这样就不会报异常了,再测试一遍注册功能效果如下:
好了,今天就先写到这里,最近在学网站开发,忙里偷闲来写写博客,保持自己的思维,感谢siki老师带给我的成长,也希望我的记录能够对自己也能对他人有所帮助。