您当前的位置: 首页 >  游戏

Peter_Gao_

暂无认证

  • 5浏览

    0关注

    621博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

steam游戏上架流程四:Steamworks SDK 的API 方法回调方式 ( Callback )

Peter_Gao_ 发布时间:2020-02-17 17:56:35 ,浏览量:5

 参考:

steamworks.net 官方文档的说明 Steamworks.NET - Getting Started

steam游戏上架流程一:使用官方SDK上传游戏  steam游戏上架流程一:使用官方SDK上传游戏_Peter_Gao_的博客-CSDN博客_steamapi_init

steam游戏上架流程二:steamworks.net  SDK API对接unity steam游戏上架流程二:steamworks SDK API steamworks.net 对接 unity_Peter_Gao_的博客-CSDN博客

steam游戏上架流程三: 游戏的发布配置与测试 steam游戏上架流程三: 游戏的发布配置与测试_Peter_Gao_的博客-CSDN博客_steam游戏上架流程

steam游戏上架流程四:Steamworks的API 方法回调方式  steam游戏上架流程四:Steamworks SDK 的API 方法回调方式 ( Callback ) _Peter_Gao_的博客-CSDN博客

 Steamworks SDK主要使用 回调方式 ( Callback ) 从服务器异步获取需要的数据,而且这个过程中并不需要暂停游戏进程。

其中有一个你肯定希望用到的回调是 GameOverlayActivated_t, 没错!意思就是每次你的Steam的 Steam Overlay 打开或关闭时服务器都会发送给你的客户端一个callback,来控制游戏的暂停。

要想使用这些回调,客户端(委托方)必须先声明 一个回调(委托) protected Callback< > 作为客户端(委托方)的一个成员变量。如:

public class SteamScript : MonoBehaviour
{
    protected Callback  m_GameOverlayActivated;
}

然后通过调用 Callback< >.Create() 创建一个具体的回调(委托)实例。

public class SteamScript : MonoBehaviour
{
    protected Callback m_GameOverlayActivated;

    private void OnEnable()
    {
        //通常把回调创建放在OnEnable方法内以确保Unity加载完成后重复创建回调
        if (SteamManager.Initialized)
        {
            //在调用任何Steamworks方法前需要先判断steam是否初始化完成

            m_GameOverlayActivated = Callback.Create(OnGameOverlayActivated);
        }
    }

    private void OnGameOverlayActivated(GameOverlayActivated_t pCallback)
    {
        if (pCallback.m_bActive != 0)
        {
            Debug.Log("Steam Overlay has been activated");
        }
        else
        {
            Debug.Log("Steam Overlay has been closed");
        }
    }
}
public class SteamScript : MonoBehaviour 
{
    private CallResult m_NumberOfCurrentPlayers;

    private void OnEnable()
    {
        if (SteamManager.Initialized)
        {
            m_NumberOfCurrentPlayers = CallResult.Create(OnNumberOfCurrentPlayers);
        }
    }

    private void OnNumberOfCurrentPlayers(NumberOfCurrentPlayers_t pCallback, bool bIOFailure)
    {
        if (pCallback.m_bSuccess != 1 || bIOFailure)
        {
            Debug.Log("There was an error retrieving the NumberOfCurrentPlayers.");
        }
        else
        {
            Debug.Log("The number of players playing your game: " + pCallback.m_cPlayers);
        }
    }
}

关于回调的结果,声明CallResult接收。

public class SteamScript : MonoBehaviour 
{
    private CallResult m_NumberOfCurrentPlayers;

    private void OnEnable()
    {
        if (SteamManager.Initialized)
        {
            m_NumberOfCurrentPlayers = CallResult.Create(OnNumberOfCurrentPlayers);
        }
    }

    private void OnNumberOfCurrentPlayers(NumberOfCurrentPlayers_t pCallback, bool bIOFailure)
    {
        if (pCallback.m_bSuccess != 1 || bIOFailure)
        {
            Debug.Log("There was an error retrieving the NumberOfCurrentPlayers.");
        }
        else
        {
            Debug.Log("The number of players playing your game: " + pCallback.m_cPlayers);
        }
    }
}

完整的调用方法如下:

public class SteamScript : MonoBehaviour
{
    private CallResult m_NumberOfCurrentPlayers;

    private void OnEnable()
    {
        if (SteamManager.Initialized)
        {
            m_NumberOfCurrentPlayers = CallResult.Create(OnNumberOfCurrentPlayers);
        }
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            SteamAPICall_t handle = SteamUserStats.GetNumberOfCurrentPlayers();
            m_NumberOfCurrentPlayers.Set(handle);
            Debug.Log("Called GetNumberOfCurrentPlayers()");
        }
    }

    private void OnNumberOfCurrentPlayers(NumberOfCurrentPlayers_t pCallback, bool bIOFailure)
    {
        if (pCallback.m_bSuccess != 1 || bIOFailure)
        {
            Debug.Log("There was an error retrieving the NumberOfCurrentPlayers.");
        }
        else
        {
            Debug.Log("The number of players playing your game: " + pCallback.m_cPlayers);
        }
    }
}

关于SteamManager的工作原理,可参考之前链接文档中的相关模块。

三、涉及SDK中所有API的用法,前文链接中提供的Demo都有详细的演示,下边总结下自己主要使用的功能模块。

如:下边这段unity客户端代码即利用OnGameOverlayActivated获取当前steam窗口弹出回调来控制当前游戏主进程的暂停与恢复。

using UnityEngine;
using Steamworks;

public class SteamScript : MonoBehaviour
{
    // 声明委托类
    protected Callback m_GameOverlayActivated;


    // Use this for initialization
    void Start()

    {
        if (SteamManager.Initialized)
        {
            string name = SteamFriends.GetPersonaName();
        }
    }

    void OnEnable()
    {
        if (SteamManager.Initialized)
        {
            // 实例化委托,并把被委托方拥有的方法做为参数传入
            m_GameOverlayActivated = Callback.Create(OnGameOverlayActivated);
        }
    }


    // 被委托方拥有的方法
    private void OnGameOverlayActivated(GameOverlayActivated_t pCallback)
    {
        if (pCallback.m_bActive != 0)
        {
            Time.timeScale = 0;
        }
        else
        {
            Time.timeScale = 1;
        }
    }

}

在项目的后台页面设定项目所需存储的用户信息,以及可自定义成就信息、排行榜信息等。同样这些操作也可以用代码直接完成。下边是完整的测试脚本:


using UnityEngine;
using Steamworks;
using System.Collections.Generic;

public enum EClientGameState
{
    k_EClientGameCount,
    k_EClientGameWinner,
    k_EClientGameLoser,
    k_EClientGameAward,
};


// This is a port of StatsAndAchievements.cpp from SpaceWar, the official Steamworks Example.


public class SteamStatsAndAchievements : MonoBehaviour
{
    #region 单例
    private static SteamStatsAndAchievements ins;

    public static SteamStatsAndAchievements Ins
    {
        get { return ins; }
    }


    #endregion

    void Awake()
    {
        ins = this;
    }


    private enum Achievement : int
    {
        ACH_WIN_ONE_GAME,
        ACH_WIN_100_GAMES,
        ACH_LEVEL1,
        ACH_LEVEL2,
        ACH_LEVEL3,
        ACH_LEVEL4,
    };

    private Achievement_t[] m_Achievements = new Achievement_t[]
    {
        new Achievement_t(Achievement.ACH_WIN_ONE_GAME, "初心者", ""),
        new Achievement_t(Achievement.ACH_WIN_100_GAMES, "Champion", ""),
        new Achievement_t(Achievement.ACH_LEVEL1, "LEVEL1", ""),
        new Achievement_t(Achievement.ACH_LEVEL2, "LEVEL2", ""),
        new Achievement_t(Achievement.ACH_LEVEL3, "LEVEL3", ""),
        new Achievement_t(Achievement.ACH_LEVEL4, "LEVEL4", ""),
    };

    public List gos;


    // Our GameID
    private CGameID m_GameID;

    // Did we get the stats from Steam?
    private bool m_bRequestedStats;
    private bool m_bStatsValid;

    // Should we store stats this frame?
    private bool m_bStoreStats;

    private SteamLeaderboard_t m_steamLeaderboard;

    // Persisted Stat details
    private int m_nTotalGamesPlayed;
    private int m_nTotalNumWins;
    private int m_nTotalNumLosses;
    private int m_flGamePlayerAward;
    private int m_flMaxPlayerAward;

    protected Callback m_UserStatsReceived;
    protected Callback m_UserStatsStored;
    protected Callback m_UserAchievementStored;

    protected CallResult m_LeaderboardFindResult;
    protected CallResult m_LeaderboardScoreUploaded;
    protected CallResult m_LeaderboardScoresDownloaded;


    void OnEnable()
    {
        if (!SteamManager.Initialized)
            return;

        // Cache the GameID for use in the Callbacks
        m_GameID = new CGameID(SteamUtils.GetAppID());


        m_UserStatsReceived = Callback.Create(OnUserStatsReceived);
        m_UserStatsStored = Callback.Create(OnUserStatsStored);
        m_UserAchievementStored = Callback.Create(OnAchievementStored);

        m_LeaderboardFindResult = CallResult.Create(OnLeaderboardFindResult);

        m_LeaderboardScoreUploaded = CallResult.Create(OnLeaerboardScoreUploaded);
        m_LeaderboardScoresDownloaded = CallResult.Create(OnLeaderboardScoresDownloaded);
        // These need to be reset to get the stats upon an Assembly reload in the Editor.
        m_bRequestedStats = false;
        m_bStatsValid = false;
        SteamAPICall_t handle = SteamUserStats.FindOrCreateLeaderboard("PlayerValue",
            ELeaderboardSortMethod.k_ELeaderboardSortMethodDescending,
            ELeaderboardDisplayType.k_ELeaderboardDisplayTypeNumeric);
        m_LeaderboardFindResult.Set(handle);
    }

    private void OnLeaderboardScoresDownloaded(LeaderboardScoresDownloaded_t pCallback, bool bIOFailure)
    {
        if (pCallback.m_hSteamLeaderboard == m_steamLeaderboard)
        {
            if (pCallback.m_cEntryCount > 0)
            {
                Debug.LogError("排行榜数据量:" + pCallback.m_cEntryCount);
                for (int i = 0; i < pCallback.m_cEntryCount; i++)
                {
                    LeaderboardEntry_t leaderboardEntry;
                    int[] details = new int[pCallback.m_cEntryCount];
                    SteamUserStats.GetDownloadedLeaderboardEntry(pCallback.m_hSteamLeaderboardEntries, i,
                        out leaderboardEntry, details, pCallback.m_cEntryCount);
                    Debug.LogError("用户ID:" + leaderboardEntry.m_steamIDUser + "用户分数" + leaderboardEntry.m_nScore +
                                   "用户排名" + leaderboardEntry.m_nGlobalRank + "Details" + leaderboardEntry.m_cDetails +
                                   "  " + SteamFriends.GetFriendPersonaName(leaderboardEntry.m_steamIDUser));
                    gos[i].transform.FindChild("name").GetComponent().text = leaderboardEntry.m_nGlobalRank +
                        "  " + SteamFriends.GetFriendPersonaName(leaderboardEntry.m_steamIDUser);
                    gos[i].transform.FindChild("score").GetComponent().text = leaderboardEntry.m_nScore + "";
                }
            }
            else
            {
                Debug.LogError("排行榜数据为空!");
            }
        }
    }

    private void OnLeaerboardScoreUploaded(LeaderboardScoreUploaded_t pCallback, bool bIOFailure)
    {
        if (pCallback.m_bSuccess != 1)
        {
            UploadScore(PlayerControl.Ins.Award);
        }
        //else if(pCallback.m_hSteamLeaderboard!= m_steamLeaderboard)
        //{
        //    Debug.LogError("获取排行榜数据信息有误!!");
        //}
        else
        {
            Debug.LogError("成功上传价值数据:" + pCallback.m_nScore + "榜内数据是否需要变更:" + pCallback.m_bScoreChanged
                           + "新的排名:" + pCallback.m_nGlobalRankNew + "上次排名:" + pCallback.m_nGlobalRankPrevious);
            gos[gos.Count - 1].transform.FindChild("name").GetComponent().text =
                pCallback.m_nGlobalRankNew + "  " + SteamFriends.GetPersonaName();
            gos[gos.Count - 1].transform.FindChild("score").GetComponent().text = pCallback.m_nScore + "";
            if (pCallback.m_nGlobalRankPrevious == 0 || pCallback.m_bScoreChanged != 0)
            {
            }

            DownloadLeaderboardEntries();
        }
    }

    private void OnLeaderboardFindResult(LeaderboardFindResult_t pCallback, bool bIOFailure)
    {
        if (!SteamManager.Initialized)
            return;
        if (pCallback.m_hSteamLeaderboard.m_SteamLeaderboard == 0 || pCallback.m_bLeaderboardFound == 0)
        {
            Debug.LogError("There is no Leaderboard found");
        }
        else
        {
            m_steamLeaderboard = pCallback.m_hSteamLeaderboard;
            UploadScore(PlayerControl.Ins.Award);
        }
    }

    public void UploadScore(int score)
    {
        if (m_steamLeaderboard.m_SteamLeaderboard != 0)
        {
            Debug.LogError("上传分数");
            var handle = SteamUserStats.UploadLeaderboardScore(m_steamLeaderboard,
                ELeaderboardUploadScoreMethod.k_ELeaderboardUploadScoreMethodForceUpdate, score, null, 0);
            m_LeaderboardScoreUploaded.Set(handle);
        }
    }

    public void DownloadLeaderboardEntries()
    {
        if (m_steamLeaderboard.m_SteamLeaderboard != 0)
        {
            Debug.LogError("请求排行榜数据");
            var handle = SteamUserStats.DownloadLeaderboardEntries(m_steamLeaderboard,
                ELeaderboardDataRequest.k_ELeaderboardDataRequestGlobal, 1, 8);
            m_LeaderboardScoresDownloaded.Set(handle);
        }
    }

    private void Update()
    {
        if (!SteamManager.Initialized)
            return;

        if (!m_bRequestedStats)
        {
            // Is Steam Loaded? if no, can't get stats, done
            if (!SteamManager.Initialized)
            {
                m_bRequestedStats = true;
                return;
            }

            // If yes, request our stats
            bool bSuccess = SteamUserStats.RequestCurrentStats();

            // This function should only return false if we weren't logged in, and we already checked that.
            // But handle it being false again anyway, just ask again later.
            m_bRequestedStats = bSuccess;
        }

        if (!m_bStatsValid)
            return;

        // Get info from sources

        // Evaluate achievements
        foreach (Achievement_t achievement in m_Achievements)
        {
            if (achievement.m_bAchieved)
                continue;
            switch (achievement.m_eAchievementID)
            {
                case Achievement.ACH_WIN_ONE_GAME:
                    if (m_nTotalNumWins != 0)
                    {
                        UnlockAchievement(achievement);
                    }

                    break;
                case Achievement.ACH_WIN_100_GAMES:
                    if (m_nTotalNumWins >= 100)
                    {
                        UnlockAchievement(achievement);
                    }

                    break;
                case Achievement.ACH_LEVEL1:
                    if (m_flMaxPlayerAward >= 25000)
                    {
                        UnlockAchievement(achievement);
                    }

                    break;
                case Achievement.ACH_LEVEL2:
                    if (m_flMaxPlayerAward >= 90000)
                    {
                        UnlockAchievement(achievement);
                    }

                    break;
                case Achievement.ACH_LEVEL3:
                    if (m_flMaxPlayerAward >= 200000)
                    {
                        UnlockAchievement(achievement);
                    }

                    break;
                case Achievement.ACH_LEVEL4:
                    if (m_flMaxPlayerAward >= 400000)
                    {
                        UnlockAchievement(achievement);
                    }

                    break;
            }
        }

        //Store stats in the Steam database if necessary
        if (m_bStoreStats)
        {
            // already set any achievements in UnlockAchievement

            // set stats
            SteamUserStats.SetStat("NumGames", m_nTotalGamesPlayed);
            SteamUserStats.SetStat("NumWins", m_nTotalNumWins);
            SteamUserStats.SetStat("NumLosses", m_nTotalNumLosses);
            SteamUserStats.SetStat("GamePlayerAward", m_flGamePlayerAward);
            SteamUserStats.SetStat("MaxPlayerAward", m_flMaxPlayerAward);

            bool bSuccess = SteamUserStats.StoreStats();
            // If this failed, we never sent anything to the server, try
            // again later.
            m_bStoreStats = !bSuccess;
        }
    }

    //-----------------------------------------------------------------------------
    // Purpose: Game state has changed
    //-----------------------------------------------------------------------------
    public void OnGameStateChange(EClientGameState eNewState)
    {
        if (!m_bStatsValid)
            return;
        //游戏场次
        if (eNewState == EClientGameState.k_EClientGameCount)
        {
            m_nTotalGamesPlayed++;
        }
        //玩家价值(成就)
        else if (eNewState == EClientGameState.k_EClientGameAward)
        {
            m_flGamePlayerAward = PlayerControl.Ins.Award;
            if (m_flGamePlayerAward > m_flMaxPlayerAward)
                m_flMaxPlayerAward = m_flGamePlayerAward;
        }
        //玩家获胜场次
        else if (eNewState == EClientGameState.k_EClientGameWinner)
        {
            m_nTotalNumWins++;
        }
        //玩家失败场次
        else
        {
            m_nTotalNumLosses++;
        }

        // We want to update stats the next frame.
        m_bStoreStats = true;
    }

    //-----------------------------------------------------------------------------
    // Purpose: Unlock this achievement
    //-----------------------------------------------------------------------------
    private void UnlockAchievement(Achievement_t achievement)
    {
        achievement.m_bAchieved = true;

        // the icon may change once it's unlocked
        //achievement.m_iIconImage = 0;

        // mark it down
        SteamUserStats.SetAchievement(achievement.m_eAchievementID.ToString());

        // Store stats end of frame
        m_bStoreStats = true;
    }

    //-----------------------------------------------------------------------------
    // Purpose: We have stats data from Steam. It is authoritative, so update
    //            our data with those results now.
    //-----------------------------------------------------------------------------
    private void OnUserStatsReceived(UserStatsReceived_t pCallback)
    {
        if (!SteamManager.Initialized)
            return;

        // we may get callbacks for other games' stats arriving, ignore them
        if ((ulong) m_GameID == pCallback.m_nGameID)
        {
            if (EResult.k_EResultOK == pCallback.m_eResult)
            {
                Debug.Log("Received stats and achievements from Steam\n");

                m_bStatsValid = true;

                // load achievements
                foreach (Achievement_t ach in m_Achievements)
                {
                    bool ret = SteamUserStats.GetAchievement(ach.m_eAchievementID.ToString(), out ach.m_bAchieved);
                    if (ret)
                    {
                        ach.m_strName =
                            SteamUserStats.GetAchievementDisplayAttribute(ach.m_eAchievementID.ToString(), "name");
                        ach.m_strDescription =
                            SteamUserStats.GetAchievementDisplayAttribute(ach.m_eAchievementID.ToString(), "desc");
                        Debug.LogError(ach.m_eAchievementID.ToString() + "  " + ach.m_bAchieved + "  " + ach.m_strName +
                                       "  " + ach.m_strDescription);
                    }
                    else
                    {
                        Debug.LogWarning("SteamUserStats.GetAchievement failed for Achievement " +
                                         ach.m_eAchievementID + "\nIs it registered in the Steam Partner site?");
                    }
                }

                // load stats
                SteamUserStats.GetStat("NumGames", out m_nTotalGamesPlayed);
                SteamUserStats.GetStat("NumWins", out m_nTotalNumWins);
                SteamUserStats.GetStat("NumLosses", out m_nTotalNumLosses);
                SteamUserStats.GetStat("GamePlayerAward", out m_flGamePlayerAward);
                SteamUserStats.GetStat("MaxPlayerAward", out m_flMaxPlayerAward);
            }
            else
            {
                Debug.Log("RequestStats - failed, " + pCallback.m_eResult);
            }
        }
    }

    //-----------------------------------------------------------------------------
    // Purpose: Our stats data was stored!
    //-----------------------------------------------------------------------------
    private void OnUserStatsStored(UserStatsStored_t pCallback)
    {
        // we may get callbacks for other games' stats arriving, ignore them
        if ((ulong) m_GameID == pCallback.m_nGameID)
        {
            if (EResult.k_EResultOK == pCallback.m_eResult)
            {
                Debug.Log("StoreStats - success");
            }
            else if (EResult.k_EResultInvalidParam == pCallback.m_eResult)
            {
                // One or more stats we set broke a constraint. They've been reverted,
                // and we should re-iterate the values now to keep in sync.
                Debug.Log("StoreStats - some failed to validate");
                // Fake up a callback here so that we re-load the values.
                UserStatsReceived_t callback = new UserStatsReceived_t();
                callback.m_eResult = EResult.k_EResultOK;
                callback.m_nGameID = (ulong) m_GameID;
                OnUserStatsReceived(callback);
            }
            else
            {
                Debug.Log("StoreStats - failed, " + pCallback.m_eResult);
            }
        }
    }

    //void OnGUI()
    //{
    //    if (GUI.Button(new Rect(10, 10, 50, 50), "reset"))
    //    {
    //        SteamUserStats.ResetAllStats(true);
    //        SteamUserStats.RequestCurrentStats();
    //    }

    //}

    //-----------------------------------------------------------------------------
    // Purpose: An achievement was stored
    //-----------------------------------------------------------------------------
    private void OnAchievementStored(UserAchievementStored_t pCallback)
    {
        // We may get callbacks for other games' stats arriving, ignore them
        if ((ulong) m_GameID == pCallback.m_nGameID)
        {
            if (0 == pCallback.m_nMaxProgress)
            {
                Debug.Log("Achievement '" + pCallback.m_rgchAchievementName + "' unlocked!");
            }
            else
            {
                Debug.Log("Achievement '" + pCallback.m_rgchAchievementName + "' progress callback, (" +
                          pCallback.m_nCurProgress + "," + pCallback.m_nMaxProgress + ")");
            }
        }
    }

    private class Achievement_t
    {
        public Achievement m_eAchievementID;
        public string m_strName;
        public string m_strDescription;
        public bool m_bAchieved;

        /// 
        /// Creates an Achievement. You must also mirror the data provided here in https://partner.steamgames.com/apps/achievements/yourappid
        /// 
        /// The "API Name Progress Stat" used to uniquely identify the achievement.
        /// The "Display Name" that will be shown to players in game and on the Steam Community.
        /// The "Description" that will be shown to players in game and on the Steam Community.
        public Achievement_t(Achievement achievementID, string name, string desc)
        {
            m_eAchievementID = achievementID;
            m_strName = name;
            m_strDescription = desc;
            m_bAchieved = false;
        }
    }
}

EClientGameState作为自定义的成就类型划分,以区分预设成就,在玩家游戏状态发生改变时上传更新服务器端数据。提示下没接触过steamworks的小伙伴,value允许搭建自己的服务器做后台,也可以直接使用steam提供的后台作为服务器。后者类似于Unity提供的PlayerPrefs,数据存取十分简单。 以上逻辑总体遵循前边所述的规则,注册回调后调用Callback.Create()来绑定数据返回的后续操作。有两点要注意在使用排行榜上传数据时,需要指定排行榜的行为(分为none,保留玩家最佳成绩及总是替换玩家当前数据)。若使用ELeaderboardUploadScoreMethod.k_ELeaderboardUploadScoreMethodKeepBest,需注意第一次上传时不会更新排行榜数据。这里测试后暂时还没找到强制更新排行榜数据的方法,所以采用了ELeaderboardUploadScoreMethod.k_ELeaderboardUploadScoreMethodForceUpdate。

另一个要注意的点就是有的回调采用了handle的形式,每次调用需获取对应的句柄并使用Set方法设置句柄。这里最初没有注意采用数据存储一样的方式注册回调导致无法上传成功取得返回结果。

最后值得注意的一点是向项目后台上传项目元数据时要使用原生sdk中提供的工具,在sdk目录的tools文件夹下。根据steam提供给你的项目的两个id,修改对应的配置文件以后把所有项目元数据置于文件夹下利用命令行上传。这个在原生sdk的文档中有youtube视频教程说明。要注意的是视频中的命令无法全部上传成功,所以我尝试采用了数据库的select *思路加了*强行上传目录下全部文件,成功上传全部数据。

最后感谢github上开源的Steamworks.NET  

关注
打赏
1664521772
查看更多评论
立即登录/注册

微信扫码登录

0.2165s