TapTap單機小遊戲《TouchStar》的聯網功能擴充套件

xoxkom發表於2024-03-06

TapTap連結:https://www.taptap.cn/app/190392

遊戲介紹

《TouchStar》於2022年上架TapTap遊戲中心,如今已有4萬+的下載量。遊戲是一款輕量偏休閒向的單機手遊,玩法簡單,沒有複雜的作業系統,無廣告無氪金。遊戲的主介面非常簡單,只有模式選擇和開始按鈕。
image
遊戲有三種模式:“持久模式”、”爆發模式“、”動點模式“,在持久模式中,玩家需要在60秒內以儘可能快的速度點選螢幕上的白色方塊,60秒時間結束後將無法繼續點選,遊戲將實時反饋玩家的點選次數和平均點選速率。

image
(注:此處為遊戲bug,實際應為持久模式)
在爆發模式中,該時間被縮短為15秒。

image
而動點模式是另一種玩法,在主介面選擇動點模式並開始遊戲後,螢幕上會隨機出現綠色的圓點,同一時刻最多出現三個,在玩家點選圓點後此處的圓點會立刻消失,並隨機在另一個位置出現新的圓點,玩家需要在60秒內點選儘可能多的圓點。遊戲會實時反饋玩家的平均點選速率。
image
遊戲玩法簡單而不失趣味性,玩法相當完善,也逐漸形成了自己的玩家生態,在TapTap上獲得了玩家的高度評價(評分9.2)。如今TapTap論壇上超過萬人參加討論,在論壇中紛紛展曬出自己的手速,表達對遊戲的喜歡以及給開發者的反饋意見。

聯網功能擴充套件

在TapTap玩家論壇中,玩家們曬出自己的最終成績,使作為一款單機遊戲的《TouchStar》擁有了一定的競技性質,玩家間得分的相互對比,讓我萌生了為該遊戲內新增線上排行榜的想法,於是我聯絡了遊戲開發者,並得到了遊戲的原始碼用作學習和二次開發,由於此專案並不開源,因而無法在此放出原始碼,敬請諒解。
遊戲為Unity工程,程式碼封裝完美,具有良好的擴充套件性。由於遊戲單機玩法已經相當完善,因此我沒有在玩法上做過多改動。在遊戲本身的基礎上,我為遊戲建立了賬戶系統,並接入TapTap登入,呼叫TapTap賬號的使用者名稱等資訊完成線上排行榜的搭建。而對於不想聯網的玩家,我也為其實現了遊客登入功能,不需要網路也能進入遊戲並正常遊玩,但對於遊客登入的玩家,其得分不能上傳至線上排行榜,並且無法獲取到線上排行榜資料。
程式碼中有些資訊不便展示,敬請諒解。
新增程式碼如下:

賬戶系統類:

public class AccountManager : MonoBehaviour
{
public AccountManager instance;
private static SUser user;  //儲存當前使用者資訊

public class SUser  //使用者資訊類
{
    public TDSUser tdsUser;
    public string nickname;
    public bool isOnline;
    public SUser(TDSUser tdsUser, string nickname, bool isOnline)
    {
        this.tdsUser = tdsUser;
        this.nickname = nickname;
        this.isOnline = isOnline;
    }
}

private void Awake()
{
    //初始化SDK
    var config = new TapConfig.Builder()
        .ClientID("...")
        .ClientToken("...")
        .ServerURL("...")
        .RegionType(RegionType.CN)
        .ConfigBuilder();
    TapBootstrap.Init(config);

    //初始化單例
    if(instance == null)
    {
        instance = this;
        DontDestroyOnLoad(gameObject);
    }
    else
    {
        Destroy(gameObject);
    }
}

// Start is called before the first frame update
void Start()
{
    //使用者資訊初始化
    user = new SUser(null, null, false);
}

//獲取使用者資訊介面
public static SUser GetUser()
{
    return user;
}

#region Login

//TapTap登入介面
public static async Task Login_WithTaptap()
{
    var currentUser = await TDSUser.GetCurrent();
    if(currentUser == null)
    {
        try
        {
            var tdsUser = await TDSUser.LoginWithTapTap();
            user.tdsUser = tdsUser;
            user.nickname = tdsUser["nickname"].ToString();
            user.isOnline = true;
        }
        catch { }
    }
    else
    {
        if(currentUser.IsAnonymous)
        {
            await TDSUser.Logout();
            await Login_WithTaptap();
        }
        else
        {
            user.tdsUser = currentUser;
            user.nickname = currentUser["nickname"].ToString();
            user.isOnline = true;
        }
    }
}

//遊客登入介面
public static async Task Login_WithGuest()
{
    var currentUser = await TDSUser.GetCurrent();
    if (currentUser == null)
    {
        try
        {
            var tdsUser = await TDSUser.LoginAnonymously();
            user.tdsUser = tdsUser;
            user.nickname = "遊客使用者";
            user.isOnline = false;
        }
        catch { }
    }
    else
    {
        await TDSUser.Logout();
        await Login_WithGuest(); ;
    }
}

#endregion
}

排行榜系統類:

public class LeaderboardManager : MonoBehaviour
{
public static LeaderboardManager leaderboardManager;
private static List<UserScore> leaderboard;    //本地儲存排行榜資料

public class UserScore  //使用者得分資料類
{
    public string name;
    public int score;
    public UserScore(string name, int score)
    {
        this.name = name;
        this.score = score;
    }
}
public List<UserScore> GetLeaderboard() //獲取排行榜介面
{
    return leaderboard;
}

//用於向服務端傳送使用者得分資訊
public const string url = "...";
public const string privateCode = "...";
public const string publicCode = "...";

private void Awake()
{
    //初始化單例
    if(leaderboardManager == null)
    {
        leaderboardManager = this;
        DontDestroyOnLoad(gameObject);
    }
    else
    {
        Destroy(gameObject);
    }
}

void Start()
{
    //本地排行榜初始化
    leaderboard = new List<UserScore>();
}

//向服務端傳送分數資料
public static IEnumerator UplodeScore(int score)
{
    string name = AccountManager.GetUser().nickname;
    UnityWebRequest request = new UnityWebRequest(url + privateCode + "/add/" + UnityWebRequest.EscapeURL(name) + "/" + score);
    yield return request.SendWebRequest();
}

//從服務端下載xml資料並更新本地排行榜
[System.Obsolete]
public static IEnumerator UpdateLeaderboard()
{
    leaderboard.Clear();
    UnityWebRequest request = UnityWebRequest.Get(url + publicCode + "/xml");
    yield return request.SendWebRequest();
    string str = request.downloadHandler.text;
    //Debug.Log(request.downloadHandler.text);
    XmlDocument xml = new XmlDocument();
    xml.LoadXml(str);
    XmlNodeList xmlNodeList = xml.SelectSingleNode("dreamlo").ChildNodes;
    foreach(XmlNode xmlNode in xmlNodeList)
    {
        if(xmlNode.Name == "entry")
        {
            string name = "";
            int score = -1;
            foreach(XmlNode xmlData in xmlNode.ChildNodes)
            {
                if(xmlData.Name == "name")
                {
                    name = xmlData.InnerText;
                    Debug.Log(name);
                }
                if(xmlData.Name == "score")
                {
                    score = int.Parse(xmlData.InnerText);
                }
            }
            leaderboard.Add(new UserScore(name, score));
        }
    }
}
}

底層程式碼完成後,我為上述聯網功能新增了儘可能符合原作風格的UI介面,並對UI進行了輕度修改,使其看起來更加符合人的直覺。
登入介面:
image
主介面:
image
UI介面相關程式碼如下:

按鈕管理:

public class LoginController : MonoBehaviour
{
public Button taptap;
public Button guest;
public Button play;

public async void TaptapLogin()
{
    await AccountManager.Login_WithTaptap();
    if(AccountManager.GetUser().tdsUser != null)
    {
        taptap.gameObject.SetActive(false);
        guest.gameObject.SetActive(false);
        play.gameObject.SetActive(true);
    }
}
public async void GuestLogin()
{
    await AccountManager.Login_WithGuest();
    taptap.gameObject.SetActive(false);
    guest.gameObject.SetActive(false);
    play.gameObject.SetActive(true);
}
public void EnterGame()
{
    SceneManager.LoadScene("Home");
}
}

排行榜效果:
(圖中資料皆為測試資料)
持久模式:
image
動點模式:
image
順便修復了一下原作持久模式遊戲下模式顯示為爆發模式的bug:
image

經多次執行除錯後,程式碼並無重大bug,可以正常執行
本二次開發程式碼量較小,所作大部分修改都在Unity Editor中完成,因版權原因恕不能在此展示,再次表示抱歉。

總結

本次開發中我遇到了很多以前沒有遇到過的問題,其中最大的問題便是Unity的版本不一致導致的各種相容性bug,排查這類bug花費了我大量的時間,並無數次請求原作者的幫助,好在最終基本解決了這些問題。其次,在除錯的過程中,我遇到過一些由於Unity或第三方包本身的問題導致的報錯,這類問題基本很難解決甚至無法解決,只能臨時更換方案。事實上,在我最開始的設想中,服務端獲取資料時採用json格式來傳輸,但由於字元編碼問題一直無法執行,我搜尋了無數方法,最終沒有解決,不得已才更換了我並不熟悉的xml格式來傳輸資料。在除錯過程中,我學到了很多關於相容性和異性屏適配問題的解決思路,這會對我未來的學習產生莫大的幫助。在此特別感謝遊戲原作者提供的專案工程檔案,感謝原作者的支援。

相關文章