跳转到主要内容

基本环境设置

介绍在Unity C#中使用PlayNANOO REST API的基本环境设置方法。

1. 前提条件

需要提前在PlayNANOO控制台获取以下信息。

获取API认证信息

请登录PlayNANOO控制台并确认以下信息。

获取路径: 控制台 > 频道 > 设置 > API & 平台管理

需要获取的信息:

  • Game ID: 频道唯一标识符
  • Service Key: API请求时使用的Service Key
  • Secret Key: 用于生成安全签名的密钥
安全注意事项

Secret Key绝对不能泄露,请通过环境变量或单独的配置文件进行管理。

2. Unity C# 实现

实现在Unity中调用PlayNANOO API的HTTP客户端。

2.1 基本设置

首先设置获取到的认证信息。

public static class HttpClient
{
// 콘솔에서 발급받은 인증 정보
private static string ServiceKey = "발급받은_서비스_키";
private static string GameId = "발급받은_게임_아이디";
private static string SecretKey = "발급받은_시크릿_키";

public static string GetServiceKey
{
get { return ServiceKey; }
}

public static string GetGameId
{
get { return GameId; }
}

public static string GetSecretKey
{
get { return SecretKey; }
}
}

2.2 请求类编写方法

所有API请求类必须继承DeviceInfo

[Serializable]
public class Req : DeviceInfo
{
public string my_param; // API 고유 파라미터
}

DeviceInfo自动包含以下信息:

  • uuid, nickname: 玩家信息(登录后自动包含)
  • device_id, version, platform: 设备信息
  • device_model, device_os, device_language: 设备详细信息
  • device_country, device_timeoffset: 地区/时区信息
提示

DeviceInfo类和Utils辅助函数的完整实现请参考第3节. 完整示例代码

2.3 HTTP请求函数实现

实现用于API请求的通用Send函数。

using System;
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

public static IEnumerator Send<TReq, TRes>(
string method, // HTTP 메서드 (GET, POST, PUT, DELETE)
string url, // API 전체 URL
bool requireToken, // 인증 토큰 필요 여부
TReq body, // 요청 바디 (GET 제외)
Action<TRes> onSuccess, // 성공 콜백
Action<BaseResponse> onError, // 실패 콜백
int timeoutSec = 10) // 타임아웃 (기본 10초)
{
UnityWebRequest www;

// GET 요청
if (method == UnityWebRequest.kHttpVerbGET)
{
www = UnityWebRequest.Get(url);
}
// POST, PUT, DELETE 등
else
{
string json = body != null ? JsonUtility.ToJson(body) : "{}";
www = new UnityWebRequest(url, method);
www.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(json));
www.downloadHandler = new DownloadHandlerBuffer();
www.SetRequestHeader("Content-Type", "application/json");
}

if (www.downloadHandler == null)
www.downloadHandler = new DownloadHandlerBuffer();

// 공통 헤더 설정
www.SetRequestHeader("Accept", "application/json");
www.SetRequestHeader("X-Playnanoo-Key", ServiceKey);
www.SetRequestHeader("X-Playnanoo-Id", GameId);

// 인증 토큰이 필요한 경우 Authorization 헤더 추가
if (requireToken && DataManager.Instance.AccessToken != null)
{
www.SetRequestHeader("Authorization", $"Bearer {DataManager.Instance.AccessToken}");
}

www.timeout = timeoutSec;
yield return www.SendWebRequest();

// 응답 처리
if (www.result == UnityWebRequest.Result.Success &&
www.responseCode >= 200 && www.responseCode < 300)
{
var text = www.downloadHandler.text;
var data = string.IsNullOrWhiteSpace(text)
? default
: JsonUtility.FromJson<TRes>(text);
onSuccess?.Invoke(data);
}
else
{
var text = www.downloadHandler.text;
BaseResponse data;

if (string.IsNullOrWhiteSpace(text))
{
// 응답 본문이 없으면 HTTP 상태 코드와 에러 메시지로 BaseResponse 생성
data = new BaseResponse
{
ErrorCode = www.responseCode.ToString(),
Message = www.error ?? "Unknown error"
};
}
else
{
data = JsonUtility.FromJson<BaseResponse>(text);
}

onError?.Invoke(data);
}

www.Dispose();
}

2.4 工具方法

以下是API调用所需的工具方法。

GenerateAuthKey()

生成客服中心认证用AuthKey。

public static string GenerateAuthKey(string userId)
{
string data = $"{GameId}{userId}";
return Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
}

使用示例:

string userId = DataManager.Instance.UUID;
string authKey = HttpClient.GenerateAuthKey(userId);

// 헬프데스크 URL에 포함
string url = $"https://help.playnanoo.com/{HttpClient.GetGameId}?userId={userId}&authKey={authKey}";

3. 完整示例代码

以下是包含上述所有内容的完整代码。DeviceInfo和HttpClient以单独文件进行管理。

DeviceInfo.cs

自动收集设备信息的基类。

using System;
using UnityEngine;

// 기기 정보 베이스 클래스
[Serializable]
public class DeviceInfo
{
public string uuid;
public string nickname;
public string device_id;
public string version;
public string platform;
public string device_model;
public string device_os;
public string device_language;
public string device_country;
public string device_timeoffset;

public DeviceInfo()
{
uuid = DataManager.Instance.UUID;
nickname = DataManager.Instance.Nickname;
device_id = SystemInfo.deviceUniqueIdentifier;
version = DataManager.Instance.VersionNumber.ToString();
platform = DataManager.Instance.Platform;
device_model = SystemInfo.deviceModel;
device_os = SystemInfo.operatingSystem;
device_language = GetDeviceLanguage();
device_country = GetCountryCode();
device_timeoffset = GetTimeOffset().ToString();
}

private string GetDeviceLanguage()
{
switch (Application.systemLanguage)
{
case SystemLanguage.Afrikaans: return "AF";
case SystemLanguage.Arabic: return "AR";
case SystemLanguage.Basque: return "EU";
case SystemLanguage.Belarusian: return "BE";
case SystemLanguage.Bulgarian: return "BG";
case SystemLanguage.Catalan: return "CA";
case SystemLanguage.Chinese:
case SystemLanguage.ChineseSimplified: return "zh-CN";
case SystemLanguage.ChineseTraditional: return "zh-TW";
case SystemLanguage.Czech: return "CS";
case SystemLanguage.Danish: return "DA";
case SystemLanguage.Dutch: return "NL";
case SystemLanguage.English: return "EN";
case SystemLanguage.Estonian: return "ET";
case SystemLanguage.Faroese: return "FO";
case SystemLanguage.Finnish: return "FI";
case SystemLanguage.French: return "FR";
case SystemLanguage.German: return "DE";
case SystemLanguage.Greek: return "EL";
case SystemLanguage.Hebrew: return "IW";
case SystemLanguage.Hungarian: return "HU";
case SystemLanguage.Icelandic: return "IS";
case SystemLanguage.Indonesian: return "IN";
case SystemLanguage.Italian: return "IT";
case SystemLanguage.Japanese: return "JA";
case SystemLanguage.Korean: return "KO";
case SystemLanguage.Latvian: return "LV";
case SystemLanguage.Lithuanian: return "LT";
case SystemLanguage.Norwegian: return "NO";
case SystemLanguage.Polish: return "PL";
case SystemLanguage.Portuguese: return "PT";
case SystemLanguage.Romanian: return "RO";
case SystemLanguage.Russian: return "RU";
case SystemLanguage.SerboCroatian: return "SH";
case SystemLanguage.Slovak: return "SK";
case SystemLanguage.Slovenian: return "SL";
case SystemLanguage.Spanish: return "ES";
case SystemLanguage.Swedish: return "SV";
case SystemLanguage.Thai: return "TH";
case SystemLanguage.Turkish: return "TR";
case SystemLanguage.Ukrainian: return "UK";
case SystemLanguage.Vietnamese: return "VI";
default: return "unknown";
}
}

private string GetCountryCode()
{
switch (Application.systemLanguage)
{
case SystemLanguage.Afrikaans: return "ZA";
case SystemLanguage.Arabic: return "SA";
case SystemLanguage.Basque: return "EU";
case SystemLanguage.Belarusian: return "BY";
case SystemLanguage.Bulgarian: return "BE";
case SystemLanguage.Catalan: return "EU";
case SystemLanguage.Chinese:
case SystemLanguage.ChineseSimplified: return "CN";
case SystemLanguage.ChineseTraditional: return "TW";
case SystemLanguage.Czech: return "CZ";
case SystemLanguage.Danish: return "DK";
case SystemLanguage.Dutch: return "NL";
case SystemLanguage.English: return "US";
case SystemLanguage.Estonian: return "EE";
case SystemLanguage.Faroese: return "FO";
case SystemLanguage.Finnish: return "FI";
case SystemLanguage.French: return "FR";
case SystemLanguage.German: return "DE";
case SystemLanguage.Greek: return "GR";
case SystemLanguage.Hebrew: return "IL";
case SystemLanguage.Hungarian: return "HU";
case SystemLanguage.Icelandic: return "IS";
case SystemLanguage.Indonesian: return "ID";
case SystemLanguage.Italian: return "IT";
case SystemLanguage.Japanese: return "JP";
case SystemLanguage.Korean: return "KR";
case SystemLanguage.Latvian: return "LV";
case SystemLanguage.Lithuanian: return "LT";
case SystemLanguage.Norwegian: return "NO";
case SystemLanguage.Polish: return "PL";
case SystemLanguage.Portuguese: return "PT";
case SystemLanguage.Romanian: return "RO";
case SystemLanguage.Russian: return "RU";
case SystemLanguage.SerboCroatian: return "HR";
case SystemLanguage.Slovak: return "SK";
case SystemLanguage.Slovenian: return "SI";
case SystemLanguage.Spanish: return "ES";
case SystemLanguage.Swedish: return "SE";
case SystemLanguage.Thai: return "TH";
case SystemLanguage.Turkish: return "TR";
case SystemLanguage.Ukrainian: return "UA";
case SystemLanguage.Vietnamese: return "VN";
default: return "US";
}
}

private double GetTimeOffset()
{
DateTime now = DateTime.Now;
DateTime utc = DateTime.UtcNow;
TimeSpan timeSpan = now.Subtract(utc);
return Math.Round(timeSpan.TotalSeconds);
}
}

HttpClient.cs

用于调用PlayNANOO API的HTTP客户端。

using System;
using System.Collections;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

public static class HttpClient
{
private static string ServiceKey = "발급받은_서비스_키";
private static string GameId = "발급받은_게임_아이디";
private static string SecretKey = "발급받은_시크릿_키";

public static string GetServiceKey
{
get { return ServiceKey; }
}

public static string GetGameId
{
get { return GameId; }
}

public static string GetSecretKey
{
get { return SecretKey; }
}

public static IEnumerator Send<TReq, TRes>(
string method,
string url,
bool requireToken,
TReq body,
Action<TRes> onSuccess,
Action<BaseResponse> onError,
int timeoutSec = 10)
{
UnityWebRequest www;

// GET 요청
if (method == UnityWebRequest.kHttpVerbGET)
{
www = UnityWebRequest.Get(url);
}
// POST, PUT, DELETE 등
else
{
string json = body != null ? JsonUtility.ToJson(body) : "{}";
www = new UnityWebRequest(url, method);
www.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(json));
www.downloadHandler = new DownloadHandlerBuffer();
www.SetRequestHeader("Content-Type", "application/json");
}

if (www.downloadHandler == null)
www.downloadHandler = new DownloadHandlerBuffer();

// 공통 헤더 설정
www.SetRequestHeader("Accept", "application/json");
www.SetRequestHeader("X-Playnanoo-Key", ServiceKey);
www.SetRequestHeader("X-Playnanoo-Id", GameId);

// 인증 토큰이 필요한 경우 Authorization 헤더 추가
if (requireToken && DataManager.Instance.AccessToken != null)
{
www.SetRequestHeader("Authorization", $"Bearer {DataManager.Instance.AccessToken}");
}

www.timeout = timeoutSec;
yield return www.SendWebRequest();

// 응답 처리
if (www.result == UnityWebRequest.Result.Success &&
www.responseCode >= 200 && www.responseCode < 300)
{
var text = www.downloadHandler.text;
var data = string.IsNullOrWhiteSpace(text)
? default
: JsonUtility.FromJson<TRes>(text);
onSuccess?.Invoke(data);
}
else
{
var text = www.downloadHandler.text;
BaseResponse data;

if (string.IsNullOrWhiteSpace(text))
{
// 응답 본문이 없으면 HTTP 상태 코드와 에러 메시지로 BaseResponse 생성
data = new BaseResponse
{
ErrorCode = www.responseCode.ToString(),
Message = www.error ?? "Unknown error"
};
}
else
{
data = JsonUtility.FromJson<BaseResponse>(text);
}

onError?.Invoke(data);
}

www.Dispose();
}

public static string GenerateAuthKey(string userId)
{
string data = $"{GameId}{userId}";
return Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
}
}