เอกสาร Steamworks
คำแนะนำทีละขั้นตอน: รางวัลความสำเร็จ

บทนำ

ต่อไปนี้เป็นคู่มือคำแนะนำขั้นตอนการดำเนินงานสั้น ๆ เพื่อผสานระบบรางวัลความสำเร็จ Steam ขั้นพื้นฐานเข้าไปสู่แอปพลิเคชันของคุณ โดยใช้เวลาไม่ถึง 10 นาทีและเพิ่มโค้ดไม่ถึง 10 บรรทัดไปยังโค้ดเดิมของคุณ Steamworks SDK จะมาพร้อมกับแอปพลิเคชันตัวอย่างชั้นเลิศที่เรียกว่า Spacewar ที่นำเสนอคุณสมบัติของ Steam อย่างเต็มรูปแบบ และคุณควรจะลองใช้งานเพื่อเรียนรู้การทำงานของคุณสมบัติต่าง ๆ ทั้งหมดของ Steam การฝึกสอนนี้จะกลั่นกรองข้อมูลที่อยู่ใน Spacewar และ API สถิติและรางวัลความสำเร็จ ลงมาให้เหลือเฉพาะข้อมูลที่จำเป็นต้องใช้ในสถิติ Steam เพื่อให้ตัวอย่างนี้ตรงประเด็นที่สุดเท่าที่จะเป็นไปได้ โปรดทราบว่าระบบสถิติและรางวัลความสำเร็จ มีฟังก์ชันซ้ำกันมากพอสมควร ดังนั้น ถ้าต้องการใช้ทั้งสองอย่าง โปรดทราบว่าสามารถรวมการเรียกฟังก์ชันหลายอย่างเข้าด้วยกันได้

ขั้นตอนที่ 1 - นิยามรางวัลความสำเร็จให้กับเกมของคุณ

รางวัลความสำเร็จจะใช้งานได้เฉพาะกับแอปพลิเคชันของตัวเอง และสามารถตั้งค่าได้ที่หน้า การกำหนดค่ารางวัลความสำเร็จ ในส่วนแบ็กเอนด์ของผู้ดูแลแอป Steamworks รายการของรางวัลความสำเร็จจาก Spacewar แอปตัวอย่างของ Steamworks:
spacewar_achievements.png

ขั้นตอนที่ 2 - การเอ็นแค็ปซูเลตรางวัลความสำเร็จ

โค้ดดังต่อไปนี้เป็นโค้ดที่ไม่ขึ้นอยู่กับเกมใดเกมหนึ่ง และสามารถใส่เพิ่มเข้าในเกมของคุณได้ตามต้องการ คลาสดังกล่าวเป็นคลาสที่ทำงานได้ครบถ้วนอยู่แล้ว แต่ก็สามารถขยายให้ตรงกับความต้องการได้อย่างง่ายดาย โค้ดทั้งหมดนี้นำมาจากไฟล์ตัวอย่างของ Spacewar โดยตรงคือ StatsAndAchievements.cpp/h

ไฟล์ส่วนหัว

เริ่มแรกเราได้นิยามโครงสร้างสำหรับบรรจุข้อมูลรางวัลความสำเร็จของเราที่ได้รับจาก Steam และเตรียมมาโครสำหรับสร้างออบเจ็กต์ประเภทนั้น ข้อมูลนี้จะแมปกับสิ่งที่พบในหน้า การกำหนดค่ารางวัลความสำเร็จ โดยตรง
#define _ACH_ID( id, name ) { id, #id, name, "", 0, 0 } struct Achievement_t { int m_eAchievementID; const char *m_pchAchievementID; char m_rgchName[128]; char m_rgchDescription[256]; bool m_bAchieved; int m_iIconImage; };

จากนั้นเราจะสร้างคลาสช่วยเหลือที่จะมาแรปการเรียก API สถิติของ Steam ทั้งหมดพร้อมกับสร้าง คอลแบ็ก Steam ทั้งหมด
class CSteamAchievements { private: int64 m_iAppID; // Our current AppID Achievement_t *m_pAchievements; // Achievements data int m_iNumAchievements; // The number of Achievements bool m_bInitialized; // Have we called Request stats and received the callback? public: CSteamAchievements(Achievement_t *Achievements, int NumAchievements); ~CSteamAchievements(); bool RequestStats(); bool SetAchievement(const char* ID); STEAM_CALLBACK( CSteamAchievements, OnUserStatsReceived, UserStatsReceived_t, m_CallbackUserStatsReceived ); STEAM_CALLBACK( CSteamAchievements, OnUserStatsStored, UserStatsStored_t, m_CallbackUserStatsStored ); STEAM_CALLBACK( CSteamAchievements, OnAchievementStored, UserAchievementStored_t, m_CallbackAchievementStored ); };

ไฟล์โค้ด

คอนสตรัคเตอร์

พารามิเตอร์ - คอนสตรัคเตอร์จะรับพอยน์เตอร์ที่ชี้ไปยังอาร์เรย์ของรางวัลความสำเร็จพร้อมกับความยาวของอาร์เรย์ จะมีการอธิบายเรื่องการจัดรูปแบบอาร์เรย์ในส่วนโค้ดเกมหลักต่อไปในภายหลัง
คืนค่า - N/A
สิ่งที่ทำ - คอนสตรัคเตอร์จะเริ่มต้นสมาชิกจำนวนหนึ่งไปพร้อมกับจับไอดีแอปของโปรแกรมที่เรากำลังใช้งาน นอกจากนั้นก็ยังจะฮุคกับเมธอดคอลแบ็กเพื่อจัดการการเรียกแบบอะซิงโครนัสที่ทำไปยัง Steam และสุดท้ายก็จะเรียกไปยัง RequestStats() ครั้งแรก เพื่อรับสถิติและรางวัลความสำเร็จของผู้ใช้ปัจจุบัน
CSteamAchievements::CSteamAchievements(Achievement_t *Achievements, int NumAchievements): m_iAppID( 0 ), m_bInitialized( false ), m_CallbackUserStatsReceived( this, &CSteamAchievements::OnUserStatsReceived ), m_CallbackUserStatsStored( this, &CSteamAchievements::OnUserStatsStored ), m_CallbackAchievementStored( this, &CSteamAchievements::OnAchievementStored ) { m_iAppID = SteamUtils()->GetAppID(); m_pAchievements = Achievements; m_iNumAchievements = NumAchievements; RequestStats(); }

RequestStats()

พารามิเตอร์ - ไม่มี
คืนค่า - bool ที่จะแสดงว่าการเรียกนั้นประสบความสำเร็จหรือไม่ หากการเรียกล้มเหลว ก็เป็นไปได้สูงว่ายังไม่ได้เริ่มใช้งาน Steam กรุณาตรวจสอบให้มั่นใจว่า ไคลเอนต์ Steam เปิดอยู่ในเวลาที่คุณพยายามจะเรียกและมีการเรียก SteamAPI_Init ก่อนหน้านี้แล้ว
สิ่งที่ทำ - เมธอดนี้จะแรปการเรียกไปยัง ISteamUserStats::RequestCurrentStats ซึ่งก็คือการเรียกแบบอะซิงโครนัสไปยัง Steam เพื่อร้องขอสถิติและรางวัลความสำเร็จของผู้ใช้ปัจจุบัน จะต้องเรียกก่อนจึงจะสามารถตั้งค่าสถิติหรือรางวัลความสำเร็จได้ การเรียกเมธอดนี้ครั้งแรกจะเกิดขึ้นในคอนสตรัคเตอร์ คุณสามารถเรียกได้อีกครั้งในเวลาใดก็ได้หลังจากนั้น หากคุณต้องการที่จะตรวจสอบสถิติหรือรางวัลความสำเร็จที่ได้รับการอัปเดต
bool CSteamAchievements::RequestStats() { // Is Steam loaded? If not we can't get stats. if ( NULL == SteamUserStats() || NULL == SteamUser() ) { return false; } // Is the user logged on? If not we can't get stats. if ( !SteamUser()->BLoggedOn() ) { return false; } // Request user stats. return SteamUserStats()->RequestCurrentStats(); }

SetAchievement()

พารามิเตอร์ - ตัวระบุสตริงของรางวัลความสำเร็จที่คุณต้องการจะตั้งค่า (ซึ่งในที่นี้คือ "ACH_WIN_ONE_GAME")
คืนค่า - bool ที่จะแสดงว่าการเรียกนั้นประสบความสำเร็จหรือไม่ หากการเรียกล้มเหลว ก็เป็นไปได้ว่ายังไม่ได้เปิดใช้งาน Steam หรือไม่เช่นนั้นคุณก็ยังไม่ได้ประมวลผลคอลแบ็กที่มาจากการเรียกเริ่มแรกไปยัง RequestStats คุณจะไม่สามารถตั้งค่ารางวัลความสำเร็จใด ๆ ได้จนกว่าจะได้รับคอลแบ็ก
สิ่งที่ทำ - เมธอดนี้จะตั้งค่ารางวัลความสำเร็จที่ระบุให้มีสถานะ สำเร็จแล้ว และจะส่งผลลัพธ์ไปยัง Steam คุณสามารถตั้งค่ารางวัลความสำเร็จได้หลายครั้ง ดังนั้นไม่ต้องกังวลเกี่ยวกับการตั้งค่าเฉพาะรางวัลความสำเร็จที่ยังไม่ได้ตั้งค่า การเรียกนี้เป็นการเรียกแบบอะซิงโครนัสที่จะเรียกใช้ 2 คอลแบ็ก ได้แก่: OnUserStatsStored() และ OnAchievementStored()
bool CSteamAchievements::SetAchievement(const char* ID) { // Have we received a call back from Steam yet? if (m_bInitialized) { SteamUserStats()->SetAchievement(ID); return SteamUserStats()->StoreStats(); } // If not then we can't set achievements yet return false; }

OnUserStatsReceived()

พารามิเตอร์ - N/A
คืนค่า - ไม่คืนค่าใด ๆ
สิ่งที่ทำ - เมธอดนี้คือคอลแบ็กที่จะถูกเรียกในทุกครั้งที่คุณพยายามจะร้องขอค่าสถิติ สามารถร้องขอค่าสถิติและรางวัลความสำเร็จได้ด้วยการใช้ RequestStats() เมธอดดังกล่าวจะอัปเดตตัวแปรสมาชิก m_pAchievements เพื่อสะท้อนถึงข้อมูลค่าสถิติและรางวัลความสำเร็จล่าสุดที่ได้คืนค่ามาจาก Steam
void CSteamAchievements::OnUserStatsReceived( UserStatsReceived_t *pCallback ) { // we may get callbacks for other games' stats arriving, ignore them if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString("Received stats and achievements from Steam\n"); m_bInitialized = true; // load achievements for ( int iAch = 0; iAch < m_iNumAchievements; ++iAch ) { Achievement_t &ach = m_pAchievements[iAch]; SteamUserStats()->GetAchievement(ach.m_pchAchievementID, &ach.m_bAchieved); _snprintf( ach.m_rgchName, sizeof(ach.m_rgchName), "%s", SteamUserStats()->GetAchievementDisplayAttribute(ach.m_pchAchievementID, "name")); _snprintf( ach.m_rgchDescription, sizeof(ach.m_rgchDescription), "%s", SteamUserStats()->GetAchievementDisplayAttribute(ach.m_pchAchievementID, "desc")); } } else { char buffer[128]; _snprintf( buffer, 128, "RequestStats - failed, %d\n", pCallback->m_eResult ); OutputDebugString( buffer ); } } }

OnUserStatsStored()

พารามิเตอร์ - N/A
คืนค่า - ไม่คืนค่าใด ๆ
สิ่งที่ทำ - เมธอดนี้คือคอลแบ็กที่จะถูกเรียกในทุกครั้งที่คุณพยายามจะจัดเก็บค่าสถิติบน Steam
void CSteamAchievements::OnUserStatsStored( UserStatsStored_t *pCallback ) { // we may get callbacks for other games' stats arriving, ignore them if ( m_iAppID == pCallback->m_nGameID ) { if ( k_EResultOK == pCallback->m_eResult ) { OutputDebugString( "Stored stats for Steam\n" ); } else { char buffer[128]; _snprintf( buffer, 128, "StatsStored - failed, %d\n", pCallback->m_eResult ); OutputDebugString( buffer ); } } }

OnAchievementStored()

พารามิเตอร์ - N/A
คืนค่า - ไม่คืนค่าใด ๆ
สิ่งที่ทำ - เมธอดนี้คือคอลแบ็กที่จะถูกเรียกในทุกครั้งที่จัดเก็บรางวัลความสำเร็จบน Steam ได้สำเร็จ
void CSteamAchievements::OnAchievementStored( UserAchievementStored_t *pCallback ) { // we may get callbacks for other games' stats arriving, ignore them if ( m_iAppID == pCallback->m_nGameID ) { OutputDebugString( "Stored Achievement for Steam\n" ); } }

ขั้นตอนที่ 3 - การผสานเข้ากับเกมของคุณ

รายละเอียดดังต่อไปนี้คือรายการของ Code Snippet ที่คุณจะต้องผสานเข้าไปในเกมของคุณในตำแหน่งที่เหมาะสม

ค่านิยามและค่าโดยรวม

รายละเอียดดังต่อไปนี้เป็นรายการของ Includes ที่ต้องสร้างไว้กับรางวัลความสำเร็จ, Enum ของรางวัลความสำเร็จเฉพาะสำหรับเกมของเรา และพอยน์เตอร์โดยรวมที่ชี้ไปยังออบเจ็กต์ช่วยเหลือของเรา โปรดทราบว่ารางวัลความสำเร็จจะตรงกับรางวัลความสำเร็จที่อยู่ในหน้าผู้ดูแลบน Steamworks
... #include "steam_api.h" // Defining our achievements enum EAchievements { ACH_WIN_ONE_GAME = 0, ACH_WIN_100_GAMES = 1, ACH_TRAVEL_FAR_ACCUM = 2, ACH_TRAVEL_FAR_SINGLE = 3, }; // Achievement array which will hold data about the achievements and their state Achievement_t g_Achievements[] = { _ACH_ID( ACH_WIN_ONE_GAME, "Winner" ), _ACH_ID( ACH_WIN_100_GAMES, "Champion" ), _ACH_ID( ACH_TRAVEL_FAR_ACCUM, "Interstellar" ), _ACH_ID( ACH_TRAVEL_FAR_SINGLE, "Orbiter" ), }; // Global access to Achievements object CSteamAchievements* g_SteamAchievements = NULL; ...

การเริ่มเปิดใช้งาน

การเรียกไปยัง SteamAPI_Init จะเริ่มเปิดใช้งาน Steam ทั้งหมด และจะต้องเรียกก่อนทุกอย่างเสมอ หากการเรียกประสบความสำเร็จ เราก็จะสามารถสร้างออบเจ็กต์ช่วยเหลือได้ด้วยการส่งเข้าไปในอาร์เรย์ของรางวัลความสำเร็จ พร้อมกับขนาดของอาร์เรย์
... // Initialize Steam bool bRet = SteamAPI_Init(); // Create the SteamAchievements object if Steam was successfully initialized if (bRet) { g_SteamAchievements = new CSteamAchievements(g_Achievements, 4); } ...

การประมวลผลการคอลแบ็ก

เพื่อให้มั่นใจว่าทางเราได้ประมวลคอลแบ็กของ Steam ทั้งหมด เราจะต้องทำการปั๊มข้อความใหม่อยู่เป็นประจำ ซึ่งจะสามารถดำเนินการได้โดยเพิ่มการเรียกนี้เข้าไปยังลูปของเกม
... SteamAPI_RunCallbacks(); ...

การเรียกใช้งานรางวัลความสำเร็จ

สามารถเรียกใช้งานรางวัลความสำเร็จได้ง่าย ๆ ด้วยการส่งการเรียกเดียวเข้าไปพร้อมกับตัวระบุรางวัลความสำเร็จ
... if (g_SteamAchievements) g_SteamAchievements->SetAchievement("ACH_WIN_100_GAMES"); ...

การปิดใช้งาน

การเรียกไปยัง SteamAPI_Shutdown ควรจะเป็นบางสิ่งบางอย่างที่คุณได้ใส่เข้าไว้ในโค้ดของคุณแล้ว โดยการเรียกนี้จะปิดการใช้งาน Steam และจะต้องเรียกก่อนออกจากแอปพลิเคชัน และในขั้นตอนสุดท้าย เราก็จะลบออบเจ็กต์ช่วยเหลือที่ได้สร้างขึ้น
... // Shutdown Steam SteamAPI_Shutdown(); // Delete the SteamAchievements object if (g_SteamAchievements) delete g_SteamAchievements; ...

ขั้นตอนที่ 4 - การทดสอบและการแก้ไขปัญหา

โค้ดตัวอย่างนี้จะให้ผลลัพธ์ข้อมูลการดีบักไปยังคอนโซลดีบักที่จะสามารถช่วยให้คุณเข้าใจได้ว่าการเรียกใดสำเร็จหรือล้มเหลว ต่อไปนี้เป็นข้อความและการแก้ไขความล้มเหลวที่พบได้โดยทั่วไป:

ไม่สามารถเริ่มแอปพลิเคชันได้เพราะหา steam_api.dll ไม่พบ การติดตั้งแอปพลิเคชันซ้ำอีกครั้งอาจช่วยแก้ปัญหานี้ได้
ตรวจสอบให้แน่ใจว่า steam_api.dll อยู่ในไดเรกทอรีเดียวกันกับตัวโปรแกรม

[S_API FAIL] SteamAPI_Init() ล้มเหลว หาอินสแตนซ์ของ Steam ที่ทำงานอยู่ หรือ steamclient.dll ภายในเครื่องไม่พบ
เป็นไปได้สูงว่าคุณไม่ได้มีไคลเอนต์ Steam ที่กำลังทำงานอยู่ ให้เริ่มต้น Steam แล้วเข้าสู่ระบบ

[S_API FAIL] SteamAPI_Init() ล้มเหลว หาไอดีแอปไม่พบ
เป็นไปได้สูงว่าคุณไม่ได้เตรียมไฟล์ steam_appid.txt ไว้ ให้วางไฟล์ดังกล่าวลงในโฟลเดอร์แหล่งที่มา แล้วตรวจสอบให้แน่ใจว่ามีหมายเลขไอดีแอป