VR越来越热,这个时间节点psvr还没正式发行,HTCvive属于VR设备里体验比较优秀的设备。开发vive应用上线主要有两个渠道,viveport官方商店及steam。两者官方都有详细的文档,但是上线steam平台,若是接入steamworks SDK可以提供更好的体验。steam官方提供的SDK都是C++代码,作为一个码农混子对C++只能一知半解。在选择sdk接入时找到别人用C#封装的原生steamworks SDK,官方链接:https://steamworks.github.io/。上述链接包含文档及sdk下载地址。接入时发现能查到的资料比较有限,自己在这尝试简单总结一下方便以后再次接入也希望能提供更容易理解的帮助。

导入SDK后主要脚本SteamManager,提供了Steamworks.NET的一些基础API供大家使用。

此SDK涵盖了原生steamworks提供的大部分功能,如:状态存储及成就,排行榜,用户授权,比赛安排,steam云等等功能,文档中都有详细的概述。使用过程中也发现他们很细心的使用了和C++相同的方法名来封装了C#的方法,这样在使用过程中对照这官方的文档可以轻易在sdk中找到自己需要调用的api。由于我的需求目前只限于成就、排行榜及用户状态量存储,以下都会围绕这三个模块展开。

首先第一步,作为测试,可以新脚本SteamScript.cs并加入如下代码:

public class SteamScript : MonoBehaviour {void Start() {if(SteamManager.Initialized) {string name = SteamFriends.GetPersonaName();Debug.LogError(name);}}
}

注意我们在调用任何Steamworks方法前需要先确认steam是否初始化完成,即SteamManager.Initialized。

SDK主要使用回调方式从服务器异步获取需要的数据避免暂停游戏进程。要使用此SDK的回调,必须在类中先定义protected Callback<T>作为一个成员变量注册到回调。如:

public class SteamScript : MonoBehaviour {protected Callback<GameOverlayActivated_t> m_GameOverlayActivated;
}

然后可以利用Callback<T>.Create()创建回调。通常把回调创建放在OnEnable方法内以确保Unity加载完成后重复创建回调。如:

public class SteamScript : MonoBehaviour {private CallResult<NumberOfCurrentPlayers_t> m_NumberOfCurrentPlayers;private void OnEnable() {if (SteamManager.Initialized) {m_NumberOfCurrentPlayers = CallResult<NumberOfCurrentPlayers_t>.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<T>接收。

public class SteamScript : MonoBehaviour {private CallResult<NumberOfCurrentPlayers_t> m_NumberOfCurrentPlayers;private void OnEnable() {if (SteamManager.Initialized) {m_NumberOfCurrentPlayers = CallResult<NumberOfCurrentPlayers_t>.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<NumberOfCurrentPlayers_t> m_NumberOfCurrentPlayers;private void OnEnable() {if (SteamManager.Initialized) {m_NumberOfCurrentPlayers = CallResult<NumberOfCurrentPlayers_t>.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都有详细的演示,下边总结下自己主要使用的功能模块。

using UnityEngine;
using Steamworks;public class SteamScript : MonoBehaviour {protected Callback<GameOverlayActivated_t> m_GameOverlayActivated;// Use this for initializationvoid Start () {if (SteamManager.Initialized){string name = SteamFriends.GetPersonaName();}}void OnEnable(){if (SteamManager.Initialized){m_GameOverlayActivated = Callback<GameOverlayActivated_t>.Create(OnGameOverlayActivated);}}private void OnGameOverlayActivated(GameOverlayActivated_t pCallback){if (pCallback.m_bActive != 0){Time.timeScale = 0;}else{Time.timeScale = 1;}}

上边这段代码即前边的演示代码,利用OnGameOverlayActivated获取当前steam窗口弹出回调来控制当前游戏主进程的暂停与恢复。

在项目通过青睐之光审核后填写相关公司及税务信息材料可注册成为value的合作伙伴以获取开发权限。之后,即可在项目的后台页面设定项目所需存储的用户信息,以及可自定义成就信息、排行榜信息等。同样这些操作也可以用代码直接完成。下边是完整的测试脚本:

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;}}#endregionvoid 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<GameObject> gos;// Our GameIDprivate 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 detailsprivate int m_nTotalGamesPlayed;private int m_nTotalNumWins;private int m_nTotalNumLosses;private int m_flGamePlayerAward;private int m_flMaxPlayerAward;protected Callback<UserStatsReceived_t> m_UserStatsReceived;protected Callback<UserStatsStored_t> m_UserStatsStored;protected Callback<UserAchievementStored_t> m_UserAchievementStored;protected CallResult<LeaderboardFindResult_t> m_LeaderboardFindResult;protected CallResult<LeaderboardScoreUploaded_t> m_LeaderboardScoreUploaded;protected CallResult<LeaderboardScoresDownloaded_t> m_LeaderboardScoresDownloaded;void OnEnable(){if (!SteamManager.Initialized)return;// Cache the GameID for use in the Callbacksm_GameID = new CGameID(SteamUtils.GetAppID());m_UserStatsReceived = Callback<UserStatsReceived_t>.Create(OnUserStatsReceived);m_UserStatsStored = Callback<UserStatsStored_t>.Create(OnUserStatsStored);m_UserAchievementStored = Callback<UserAchievementStored_t>.Create(OnAchievementStored);m_LeaderboardFindResult = CallResult<LeaderboardFindResult_t>.Create(OnLeaderboardFindResult);m_LeaderboardScoreUploaded = CallResult<LeaderboardScoreUploaded_t>.Create(OnLeaerboardScoreUploaded);m_LeaderboardScoresDownloaded = CallResult<LeaderboardScoresDownloaded_t>.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<UILabel>().text = leaderboardEntry.m_nGlobalRank + "  " + SteamFriends.GetFriendPersonaName(leaderboardEntry.m_steamIDUser);gos[i].transform.FindChild("score").GetComponent<UILabel>().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<UILabel>().text = pCallback.m_nGlobalRankNew + "  " + SteamFriends.GetPersonaName();gos[gos.Count - 1].transform.FindChild("score").GetComponent<UILabel>().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, doneif (!SteamManager.Initialized){m_bRequestedStats = true;return;}// If yes, request our statsbool 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 achievementsforeach (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 necessaryif (m_bStoreStats){// already set any achievements in UnlockAchievement// set statsSteamUserStats.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 downSteamUserStats.SetAchievement(achievement.m_eAchievementID.ToString());// Store stats end of framem_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 themif ((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 achievementsforeach (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 statsSteamUserStats.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 themif ((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 themif ((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;/// <summary>/// Creates an Achievement. You must also mirror the data provided here in https://partner.steamgames.com/apps/achievements/yourappid/// </summary>/// <param name="achievement">The "API Name Progress Stat" used to uniquely identify the achievement.</param>/// <param name="name">The "Display Name" that will be shown to players in game and on the Steam Community.</param>/// <param name="desc">The "Description" that will be shown to players in game and on the Steam Community.</param>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<T>.Create()来绑定数据返回的后续操作。有两点要注意在使用排行榜上传数据时,需要指定排行榜的行为(分为none,保留玩家最佳成绩及总是替换玩家当前数据)。若使用ELeaderboardUploadScoreMethod.k_ELeaderboardUploadScoreMethodKeepBest,需注意第一次上传时不会更新排行榜数据。这里测试后暂时还没找到强制更新排行榜数据的方法,所以采用了ELeaderboardUploadScoreMethod.k_ELeaderboardUploadScoreMethodForceUpdate。

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

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

差不多就是这些,第一次写博客,本人技术能力也不是很强,作为总结也希望能给小伙伴们带来一定帮助。

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

unity steamworksdk简单接入相关推荐

  1. 手把手教你简单接入微信SDK

    就看微信现在这么火的样子,如果你的APP不接入微信的SDK好像就有点脱离了时代大车轮一样.一个成功的APP,不单单凭借着一个好的想法,一个好的功能,最主要还是用户量.用户量就好像是水,我们的APP就一 ...

  2. 微信公众号自动回复html,[.NET] 简单接入微信公众号开发:实现自动回复

    简单接入微信公众号开发:实现自动回复 一.前提 先申请微信公众号的授权,找到或配置几个关键的信息(开发者ID.开发者密码.IP白名单.令牌和消息加解密密钥等). 二.基本配置信息解读 开发者ID:固定 ...

  3. 微信公众号简单接入springboot集成weixin4j

    微信公众号简单接入springboot集成weixin4j 内网穿透 登录地址:https://natapp.cn/ 注册用户,购买免费渠道 进行配置端口号(我配置的是8802) 根据网址进行下一步操 ...

  4. Unity 制作简单的任务动画

    Unity 制作简单的任务动画 1.添加人物模型到unity 我使用的是unity store中的免费模型: https://assetstore.unity.com/packages/3d/char ...

  5. Unity:Firebase接入Apple登录

    Unity:Firebase接入Apple登录 开启Firebase的登录方式 设置Apple的后台信息 从Assets Store下载 Singn In With Apple Xcode设置 添加A ...

  6. Unity Apple登录接入

    Unity Apple登录接入 引言 在2019年6月份的全球开发者大会(WWDC)上,苹果宣布了一款新产品:Sign In With Apple.随后苹果更新了App Store审查指南,现在他们要 ...

  7. unity实现简单fps游戏功能

    unity实现简单fps游戏鼠标功能 1.unity实现瞄准镜功能 实现原理 按下鼠标右键镜头拉近,再次按下镜头拉远(设置Camrea的FOV属性) 逐渐拉近(例如:60->20逐渐拉近) 设置 ...

  8. unity实现简单巡逻兵

    unity实现简单巡逻兵 游戏视频 游戏要求 创建一个地图和若干巡逻兵(使用动画): 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址.即每次确定下一个目标位置,用自己当前位置为原点计算: 巡 ...

  9. unity实现简单坦克对战

    unity实现简单坦克对战 游戏要求 使用"感知-思考-行为"模型,建模 AI 坦克 场景中要放置一些障碍阻挡对手视线 坦克需要放置一个矩阵包围盒触发器,以保证 AI 坦克能使用射 ...

最新文章

  1. 关于OpenGL环境配置问题(2015)
  2. 蓝桥杯-表达式计算(java)
  3. 推荐计算机与通信领域SCI期刊,因子稳中看升,对中国作者友好,毕业优选!
  4. Boost:boost::atomic用法实例
  5. sharepoint安装心得_过程
  6. Intel Core Enhanced Core架构/微架构/流水线 (13) - 存储转发/访存消歧
  7. 新一代企业级大数据应用方案
  8. Kotlin入门(17)等式判断的情况
  9. 计算机的组成 —— 鼠标
  10. Android原生开发学习笔记(java)
  11. oracle数据库imp命令,数据库imp导入命令
  12. 计算机网络10种,(完整版)计算机网络10种硬件设备介绍.doc
  13. 3分钟掌握7个XD基础操作
  14. 想创业成功?先看看这25家千亿美金的公司是如何炼成的!
  15. .Delphi7升级到Delphi 2010、Delphi XE、Delphi XE2总结
  16. 使用UltraISO制作Ubuntu16.04 U盘启动盘
  17. NLP语义技术演进:从DP依存句法到SDP依存语义再到AMR抽象语义分析概述与开源实现...
  18. Photoshop制作木板雕刻看图识字
  19. 【论文笔记】开放场景下的实时视觉重定位方法 HF-Net 2019
  20. javascript 判断美国现在是冬令时、夏令时

热门文章

  1. IBM ACE User Defined Node
  2. 10分钟用Python制作恋爱日志
  3. Word文档怎么转PDF?这里有需要掌握的方法
  4. 数学推导+纯Python实现机器学习算法14:Ridge岭回归
  5. Batch Normalize的几点说明
  6. 张超 计算机 清华 论文,张超-清华大学航天航空学院
  7. 单片机中断实验2 EX0
  8. java通过aspose实现文档间格式转换
  9. 如何才能做到色彩平衡?
  10. 爱签:行业利好不断 电子签章、电子合同将扩大应用