美文网首页
使用Unity和PlayFab制作简易账号系统

使用Unity和PlayFab制作简易账号系统

作者: 全新的饭 | 来源:发表于2023-07-11 14:12 被阅读0次

目标

  1. 玩家进入游戏后自动登录
    如果本地存有账号,则使用本地账号登录;如果本地不存在账号,则创建1个账号并登录,自动设置1个默认的玩家昵称。
  2. 玩家可在游戏中切换登录账号
    将一个合法账号复制到相应输入框中
  3. 玩家可在游戏中修改名称
  4. 玩家可在任意处(PC、移动端等)打开游戏,使用同一账号登录。

下面是实现了上述目标后的效果示意:


账号系统示意.gif

实现思路

实现LoginSys,提供如下功能

  1. 在游戏开始时自动登录
  2. 输入账号重新登录
  3. 设置玩家昵称
  4. 本地保存账号

实现IdSys,提供如下功能

  1. 随机生成1个账号
    账号 = x位字母+y位数字+z位校验位
    目标是尽量生成不重复的账号(但是实际还是有可能重复的)
  2. 判断给定的账号是否是有效账号

相关代码

LoginSys.cs 登录相关

using System.IO;
using System;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using PlayFab.ClientModels;

public class LoginSys : MonoBehaviour
{
    private string _loginUserId;
    public string UserId { get { return _loginUserId; } }

    [SerializeField]
    private Text _userNickNameText;
    public string UserName { get { return _userNickNameText.text; } }
    public string CustomId
    {
        get
        {
            if (!PlayerPrefs.HasKey(nameof(CustomId)))
            {
                PlayerPrefs.SetString(nameof(CustomId), CreateCustomId());
            }

            return PlayerPrefs.GetString(nameof(CustomId));
        }
        private set
        {
            PlayerPrefs.SetString(nameof(CustomId), value);
        }
    }

    public event Action<string> RefreshPlayerNickNameEvent;

    private IdSys _idSys;

    // 生成玩家Id
    private string CreateCustomId()
    {
        var id = _idSys.GenerateId();
        return id;
    }

    private void Start()
    {
        Init();
    }

    private void Update()
    {
        MyUpdate();
    }

    private void OnDestroy()
    {
        MyDestroy();
    }

    private void Init()
    {
        _idSys = new IdSys();
        Login();
    }

    private void MyUpdate()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            PlayerPrefs.DeleteKey(nameof(CustomId));
            Debug.Log("清除账号信息!");
        }
    }

    private void MyDestroy()
    {
        _idSys?.MyDestroy();
        _idSys = null;
    }

    private void Login()
    {
        var request = new LoginWithCustomIDRequest { CustomId = CustomId, CreateAccount = true };
        PlayFab.PlayFabClientAPI.LoginWithCustomID(request, OnLoginSuccess, OnLoginFailure);
    }

    private void OnLoginSuccess(LoginResult result)
    {
        Debug.Log("登录成功:" + CustomId);
        _loginUserId = result.PlayFabId;

        // 设置玩家昵称
        // 如果是新账号,需创建玩家昵称。
        if (result.NewlyCreated)
        {
            SetNickName(CreateDefaultNickName());
            Debug.Log("新账号,创建玩家昵称");
        }
        else
        {
            GetNickName(RefreshNickName);
            Debug.Log("老账号,获取玩家昵称");
        }
    }
    private void OnLoginFailure(PlayFab.PlayFabError error)
    {
        Debug.LogError(error.GenerateErrorReport());
    }

    // 生成默认玩家昵称
    private string CreateDefaultNickName()
    {
        int digitCnt = 5;
        return "Player_" + _loginUserId.Substring(_loginUserId.Length - digitCnt, digitCnt);
    }
    // 设置玩家昵称
    private void SetNickName(string nickName, Action successfulAction = null, Action failAction = null)
    {
        PlayFab.PlayFabClientAPI.UpdateUserTitleDisplayName(new UpdateUserTitleDisplayNameRequest
        {
            DisplayName = nickName

        },
            result =>
            {
                RefreshNickName(nickName);
                successfulAction?.Invoke();
                Debug.Log($"设置玩家昵称成功!{result.DisplayName}");

            },
            error =>
            {
                failAction?.Invoke();
                Debug.LogError($"设置玩家昵称失败!{ error.GenerateErrorReport()}");
            }
        );
    }
    private void GetNickName(Action<string> afterGetAction)
    {
        string nickName = string.Empty;
        PlayFab.PlayFabClientAPI.GetPlayerProfile(new GetPlayerProfileRequest(),
        result =>
        {
            nickName = result.PlayerProfile.DisplayName;
            Debug.Log($"获取玩家昵称成功!{nickName}");
            afterGetAction?.Invoke(nickName);
        },
        error =>
        {
            Debug.LogError($"获取玩家昵称失败!{error.GenerateErrorReport()}");
        }
        );
    }
    private void RefreshNickName(string nickName)
    {
        RefreshPlayerNickNameEvent?.Invoke(nickName);
        _userNickNameText.text = nickName;
    }

    // 验证是否是合法的CustomId
    private bool IsValidCustomId(string id)
    {
        return _idSys.IsValidId(id);
    }
    // 尝试重新登录(通过输入账号)
    public bool TryRelogin(string anotherCustomId)
    {
        bool ret = false;
        if (IsValidCustomId(anotherCustomId))
        {
            CustomId = anotherCustomId;
            Login();
            ret = true;
        }
        return ret;
    }
    public void ChangeNickName(string newNickName, Action successfulAction, Action failAction)
    {
        SetNickName(newNickName, successfulAction, failAction);
    }
}

IdSys.cs 账号相关

using System.Text;
using System.Data;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class IdSys
{
    private Char[] _letterArray;
    private Char[] _numArray;

    // 有效Id的结构:x个小写字母+y个数字+z个加校验数字
    private const int LetterCnt = 5;
    private const int NumCnt = 3;
    private const int CheckNumCnt = 2;

    public IdSys()
    {
        var ids = new List<Char>();
        for (char i = 'a'; i <= 'z'; i++)
        {
            ids.Add(i);
        }
        _letterArray = ids.ToArray();

        _numArray = new Char[10];
        for (int i = 0; i < _numArray.Length; i++)
        {
            _numArray[i] = char.Parse(i.ToString());
        }
    }
    public void MyDestroy()
    {
        _letterArray = null;
        _numArray = null;
    }

    private string CharArrayToString(Char[] chars)
    {
        StringBuilder sb = new StringBuilder();
        foreach (var c in chars)
        {
            sb.Append(c.ToString());
        }
        return sb.ToString();
    }

    // validStr:只包含小写字母和数字
    private bool TryToValidStr(string str, out string validStr)
    {
        str = str.ToLower();
        bool ret = true;
        validStr = string.Empty;
        StringBuilder tempSb = new StringBuilder();
        for (int i = 0; i < str.Length; i++)
        {
            var s = str[i];
            if (IsValidChar(s))
            {
                tempSb.Append(s);
            }
            else
            {
                ret = false;
                break;
            }
        }

        if (ret)
        {
            validStr = tempSb.ToString();
        }

        return ret;
    }
    // validChar:是小写字母或数字
    private bool IsValidChar(Char c)
    {
        return IsLetter(c) || IsNum(c);
    }
    private bool IsLetter(Char c)
    {
        return Array.IndexOf(_letterArray, c) >= 0;
    }
    private bool IsNum(Char c)
    {
        return Array.IndexOf(_numArray, c) >= 0;
    }

    private bool IsLetterStr(string str)
    {
        return IsTargetTypeStr(str, IsLetter);
    }
    private bool IsNumStr(string str)
    {
        return IsTargetTypeStr(str, IsNum);
    }

    private bool IsTargetTypeStr(string str, Func<Char, bool> checkFunc)
    {
        bool ret = true;
        foreach (var s in str)
        {
            if (!checkFunc(s))
            {
                ret = false;
                break;
            }
        }
        return ret;
    }


    private int CharToNum(Char c)
    {
        var index = 0;
        if (IsLetter(c))
        {
            index = Array.IndexOf(_letterArray, c);
        }
        else if (IsNum(c))
        {
            index = Array.IndexOf(_numArray, c);
        }
        return index;
    }

    // 验证是否是合法Id
    public bool IsValidId(string idStr)
    {
        var isValid = true;
        if (idStr.Length != LetterCnt + NumCnt + CheckNumCnt)
        {
            isValid = false;
        }
        else if (!TryToValidStr(idStr, out string newIdStr))
        {
            isValid = false;
        }
        else
        {
            var letterStr = newIdStr.Substring(0, LetterCnt);
            var numStr = newIdStr.Substring(LetterCnt, NumCnt);
            var checkNumStr = newIdStr.Substring(LetterCnt + NumCnt, CheckNumCnt);

            isValid = IsLetterStr(letterStr)
                && IsNumStr(numStr)
                && checkNumStr == GetCheckNumStr(letterStr + numStr, CheckNumCnt);
        }

        return isValid;
    }

    // 生成1个Id(几位字母、几位数字、几位校验)
    public string GenerateId()
    {
        var sb = new StringBuilder();
        for (int i = 0; i < LetterCnt; i++)
        {
            sb.Append(_letterArray[UnityEngine.Random.Range(0, _letterArray.Length)]);
        }
        for (int i = 0; i < NumCnt; i++)
        {
            sb.Append(_numArray[UnityEngine.Random.Range(0, _numArray.Length)]);
        }
        var checkNumStr = GetCheckNumStr(sb.ToString(), CheckNumCnt);
        sb.Append(checkNumStr);
        return sb.ToString();
    }
    // 获取n位校验数字
    private string GetCheckNumStr(string str, int cnt)
    {
        var checkNumStr = string.Empty;
        if (TryToValidStr(str, out string validStr))
        {
            int num = 0;
            for (int i = 0; i < validStr.Length; i++)
            {
                num += CharToNum(validStr[i]) * i * 10;
            }
            checkNumStr = num.ToString();
            if (checkNumStr.Length < cnt)
            {
                checkNumStr = checkNumStr.PadLeft(cnt, '0');
            }
            else if (checkNumStr.Length > cnt)
            {
                checkNumStr = checkNumStr.Substring(checkNumStr.Length - cnt);
            }
        }
        return checkNumStr;
    }
}

PlayerInfoWnd.cs 测试用:显示和修改昵称、显示和输入(以便重新登录)账号

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

// 玩家信息窗口:显示玩家账号和昵称,可修改玩家账号和昵称。
public class PlayerInfoWnd : MonoBehaviour
{
    [SerializeField]
    private LoginSys _loginSys;
    [SerializeField]
    private GameObject _wndGo;
    [SerializeField]
    private InputField _customIdInputField;
    [SerializeField]
    private InputField _nickNameInputField;

    [SerializeField]
    private Button _closeBtn;

    [SerializeField]
    private GameObject _successTips;
    [SerializeField]
    private GameObject _failTips;

    private string _curName;

    private void Start()
    {
        Init();
    }
    private void OnDestroy()
    {
        MyDestroy();
    }

    private void Init()
    {
        Close();
        _loginSys.RefreshPlayerNickNameEvent += OnRefreshPlayerNickName;
        _customIdInputField.onEndEdit.AddListener(OnEndEditCustomId);
        _nickNameInputField.onEndEdit.AddListener(OnEndEditNickName);
        _closeBtn.onClick.AddListener(OnClickCloseBtn);
    }
    private void MyDestroy()
    {
        if (_loginSys != null)
        {
            _loginSys.RefreshPlayerNickNameEvent -= OnRefreshPlayerNickName;
        }
        _customIdInputField?.onEndEdit.RemoveListener(OnEndEditCustomId);
        _nickNameInputField?.onEndEdit.RemoveListener(OnEndEditNickName);
        _closeBtn?.onClick.RemoveListener(OnClickCloseBtn);
    }

    public void Open()
    {
        _customIdInputField.SetTextWithoutNotify(_loginSys.CustomId);
        _wndGo.SetActive(true);
    }
    private void Close()
    {
        HideAllTips();
        _wndGo.SetActive(false);
    }
    private void OnClickCloseBtn()
    {
        Close();
    }
    private void OnEndEditCustomId(string id)
    {
        if (id != _loginSys.CustomId)
        {
            bool isSuccess = _loginSys.TryRelogin(id);
            ShowTips(isSuccess);
            if (!isSuccess)
            {
                _customIdInputField.SetTextWithoutNotify(_loginSys.CustomId);
            }
        }
    }
    private void OnEndEditNickName(string name)
    {
        _loginSys.ChangeNickName(name, () =>
        {
            ShowTips(true);
        },
        () =>
        {
            ShowTips(false);
            _nickNameInputField.SetTextWithoutNotify(_loginSys.UserName);
        });
    }
    private void OnRefreshPlayerNickName(string name)
    {
        _nickNameInputField.SetTextWithoutNotify(name);
    }

    private void HideAllTips()
    {
        _successTips.SetActive(false);
        _failTips.SetActive(false);
    }

    private void ShowTips(bool isSuccess)
    {
        var go = isSuccess ? _successTips : _failTips;
        go.SetActive(true);
        MonoSys.Instance.DelayCall(2f, () => go.SetActive(false));
    }
}

其他说明

  1. Unity打包Webgl会出现移动端点击InputField无法唤起输入法窗口的情况,需通过以下方式解决:将WebGLInput组件添加到InputField所在的GameObject上。
    kou-yeung/WebGLInput: IME for Unity WebGL

  2. PlayFab的UnityPackage的下载链接无法打开,需通过以下方式解决
    国内访问raw.github…被拒绝的解决方法

相关文章

网友评论

      本文标题:使用Unity和PlayFab制作简易账号系统

      本文链接:https://www.haomeiwen.com/subject/dsayudtx.html