【Cocos Creator 3.8】VersionCheckerの実装:アタッチするだけで起動時にサーバーへHTTPリクエストを送り、最新版があるか確認・通知する汎用スクリプト
このコンポーネントは、ゲーム起動時(または任意のタイミング)に指定したURLへHTTPリクエストを送り、サーバー側に置かれた「最新バージョン情報」とローカルのアプリバージョンを比較して、最新版があるかどうかを判定・通知するための汎用スクリプトです。
VersionChecker を任意のノードにアタッチし、インスペクタから URL や現在のバージョン、比較方法などを設定するだけで動作します。他の GameManager やシングルトンに依存しないため、どのプロジェクトにも簡単に組み込めます。
コンポーネントの設計方針
1. 機能要件の整理
- 起動時(
start())に HTTP(S) リクエストを送る。 - 任意のタイミングで再チェックできるように、パブリックメソッド
checkNow()も用意する。 - レスポンスから「最新バージョン文字列」を取得し、インスペクタで指定した「現在のバージョン」と比較する。
- バージョンが異なる場合(または新しい場合)、ログ・UI(任意)で通知できるようにする。
- 外部スクリプトに依存せず、インスペクタの設定だけで完結する。
- HTTP エラーや JSON パースエラーなどを防御的に扱い、エラー内容をログ出力する。
2. 通信仕様の前提
汎用性を高めるため、レスポンス形式を以下のようなシンプルな JSON とします:
{
"latestVersion": "1.2.0",
"message": "新しいバージョンがあります。",
"forceUpdate": false
}
ただし、キー名やパスはインスペクタから設定できるようにし、異なる API 仕様にも対応できるようにします。
3. インスペクタで設定可能なプロパティ設計
- enabledOnStart: boolean
- 起動時に自動でバージョンチェックを行うかどうか。
- true の場合、
start()で自動実行。
- requestUrl: string
- バージョン情報を取得する HTTP/HTTPS エンドポイント URL。
- 例:
https://example.com/api/version
- httpMethod: Enum ("GET" | "POST")
- HTTP メソッドの指定。
- GET の場合はクエリパラメータ、POST の場合は JSON ボディを送信(必要な場合)。
- requestBodyJson: string
- POST 時に送信する JSON 文字列。
- 空文字ならボディ送信なし。
- currentVersion: string
- クライアント側の現在バージョン。
- 例:
1.0.0(セマンティックバージョン形式を推奨)
- responseLatestKey: string
- レスポンス JSON 内の「最新バージョン」が入っているキー名。
- デフォルト:
"latestVersion"
- responseMessageKey: string
- レスポンス JSON 内のユーザー向けメッセージが入っているキー名。
- デフォルト:
"message"
- compareAsSemantic: boolean
- バージョンを
major.minor.patchの数値として比較するかどうか。 - true: 数値として比較(1.10.0 > 1.2.0)。
- false: 文字列比較(単純な < / > 比較)。
- バージョンを
- logDetail: boolean
- 詳細ログをコンソールに出力するかどうか。
- デバッグ中は true、本番では false 推奨。
- showResultAsAlert: boolean
- ブラウザ環境で
window.alertによる簡易ダイアログ表示を行うかどうか。 - ネイティブ環境ではログ出力のみ。
- ブラウザ環境で
- timeoutMs: number
- リクエストのタイムアウト時間 (ミリ秒)。
- 0 以下ならタイムアウトなし。
UI コンポーネント(Label など)には依存せず、すべてログ出力 + 任意のアラートで完結させることで、完全な独立性を保ちます。必要であれば、後から Label 連携などを追加するのは簡単です。
TypeScriptコードの実装
以下が、Cocos Creator 3.8.7 向けの VersionChecker コンポーネントの完全実装です。
import { _decorator, Component, sys } from 'cc';
const { ccclass, property } = _decorator;
/**
* VersionChecker
* 起動時または任意のタイミングでサーバーに HTTP リクエストを送り、
* 最新バージョンと現在バージョンを比較して結果を通知する汎用コンポーネント。
*
* このスクリプト単体で完結し、他のカスタムスクリプトには依存しません。
*/
enum HttpMethod {
GET = 0,
POST = 1,
}
@ccclass('VersionChecker')
export class VersionChecker extends Component {
@property({
tooltip: '起動時(start)に自動でバージョンチェックを行うかどうか。',
})
public enabledOnStart: boolean = true;
@property({
tooltip: 'バージョン情報を取得する HTTP/HTTPS エンドポイント URL。\n例: https://example.com/api/version',
})
public requestUrl: string = '';
@property({
type: HttpMethod,
tooltip: 'HTTP メソッドの指定。\nGET: クエリパラメータなどで送信\nPOST: JSON ボディを送信(requestBodyJson が空でなければ)',
})
public httpMethod: HttpMethod = HttpMethod.GET;
@property({
tooltip: 'POST 時に送信する JSON 文字列。\n空文字の場合はボディ送信なし。\n例: {"platform":"web","channel":"release"}',
multiline: true,
})
public requestBodyJson: string = '';
@property({
tooltip: '現在のアプリバージョンを文字列で指定します。\n例: 1.0.0',
})
public currentVersion: string = '1.0.0';
@property({
tooltip: 'レスポンス JSON 内で最新バージョンが格納されているキー名。\n例: latestVersion',
})
public responseLatestKey: string = 'latestVersion';
@property({
tooltip: 'レスポンス JSON 内でメッセージが格納されているキー名。\n例: message',
})
public responseMessageKey: string = 'message';
@property({
tooltip: 'バージョンをセマンティックバージョン(major.minor.patch)として数値比較するかどうか。\nfalse の場合は単純な文字列比較になります。',
})
public compareAsSemantic: boolean = true;
@property({
tooltip: '詳細ログをコンソールに出力するかどうか。\nデバッグ時は true、本番では false 推奨。',
})
public logDetail: boolean = true;
@property({
tooltip: 'ブラウザ環境で結果を window.alert で表示するかどうか。\nネイティブ環境ではログ出力のみになります。',
})
public showResultAsAlert: boolean = false;
@property({
tooltip: 'リクエストのタイムアウト時間(ミリ秒)。\n0 以下の場合はタイムアウトなし。',
min: 0,
})
public timeoutMs: number = 5000;
// 内部状態
private _isChecking: boolean = false;
private _lastResultText: string = '';
onLoad() {
// 特別な標準コンポーネント依存はないのでチェック不要。
// ただし、URL 未設定などの初期状態を警告しておく。
if (!this.requestUrl) {
console.warn('[VersionChecker] requestUrl が設定されていません。インスペクタから URL を設定してください。');
}
}
start() {
// 起動時に自動チェック
if (this.enabledOnStart) {
this.checkNow();
}
}
/**
* 外部(ボタンの onClick など)から呼び出して、
* 任意のタイミングでバージョンチェックを実行できます。
*/
public checkNow() {
if (this._isChecking) {
if (this.logDetail) {
console.warn('[VersionChecker] 既にバージョンチェック中です。二重実行はスキップします。');
}
return;
}
if (!this.requestUrl) {
console.error('[VersionChecker] requestUrl が空です。インスペクタから有効な URL を設定してください。');
return;
}
this._isChecking = true;
if (this.logDetail) {
console.log('[VersionChecker] バージョンチェック開始: ', this.requestUrl);
}
this._sendRequest()
.then((responseText) => {
this._handleResponse(responseText);
})
.catch((err) => {
console.error('[VersionChecker] リクエストエラー: ', err);
this._lastResultText = `バージョンチェックに失敗しました: ${err}`;
this._notifyUser(this._lastResultText);
})
.finally(() => {
this._isChecking = false;
});
}
/**
* 実際の HTTP リクエスト送信処理。
* Cocos Creator 3.8 ではブラウザ/ネイティブともに fetch が利用可能なため、
* fetch API を使用しています。
*/
private _sendRequest(): Promise<string> {
const url = this.requestUrl;
const method = this.httpMethod === HttpMethod.POST ? 'POST' : 'GET';
const fetchOptions: RequestInit = {
method,
headers: {
'Content-Type': 'application/json',
},
};
if (method === 'POST' && this.requestBodyJson.trim().length > 0) {
fetchOptions.body = this.requestBodyJson;
}
let fetchPromise = fetch(url, fetchOptions).then((res) => {
if (!res.ok) {
throw new Error(`HTTP エラー: ${res.status} ${res.statusText}`);
}
return res.text();
});
if (this.timeoutMs > 0) {
// タイムアウト制御
const timeoutPromise = new Promise<never>((_, reject) => {
const id = setTimeout(() => {
clearTimeout(id);
reject(new Error(`タイムアウト: ${this.timeoutMs}ms`));
}, this.timeoutMs);
});
// 先に完了した方を採用
fetchPromise = Promise.race([fetchPromise, timeoutPromise]) as Promise<string>;
}
return fetchPromise;
}
/**
* レスポンス文字列を解析し、バージョン比較と通知を行う。
*/
private _handleResponse(responseText: string) {
if (this.logDetail) {
console.log('[VersionChecker] レスポンス受信: ', responseText);
}
let json: any;
try {
json = JSON.parse(responseText);
} catch (e) {
console.error('[VersionChecker] JSON 解析に失敗しました。レスポンスが JSON 形式ではありません。', e);
this._lastResultText = 'サーバーからのレスポンス形式が不正です。';
this._notifyUser(this._lastResultText);
return;
}
const latestKey = this.responseLatestKey || 'latestVersion';
const messageKey = this.responseMessageKey || 'message';
const latestVersion = json[latestKey];
const messageFromServer = json[messageKey];
if (typeof latestVersion !== 'string') {
console.error(`[VersionChecker] レスポンス JSON に文字列型の "${latestKey}" が存在しません。`);
this._lastResultText = 'サーバーから最新バージョン情報を取得できませんでした。';
this._notifyUser(this._lastResultText);
return;
}
const current = this.currentVersion;
const latest = latestVersion;
let comparisonResult: number; // -1: 最新より古い, 0: 同じ, 1: 最新より新しい
if (this.compareAsSemantic) {
comparisonResult = this._compareSemanticVersion(current, latest);
} else {
// 文字列比較(辞書順)
if (current === latest) {
comparisonResult = 0;
} else if (current < latest) {
comparisonResult = -1;
} else {
comparisonResult = 1;
}
}
if (comparisonResult < 0) {
// クライアントが古い
this._lastResultText = `新しいバージョンが利用可能です。\n現在: ${current}\n最新: ${latest}`;
if (messageFromServer) {
this._lastResultText += `\nメッセージ: ${messageFromServer}`;
}
console.warn('[VersionChecker] アップデートが必要です: ', this._lastResultText);
this._notifyUser(this._lastResultText);
} else if (comparisonResult === 0) {
this._lastResultText = `現在のバージョンは最新です。\nバージョン: ${current}`;
console.log('[VersionChecker] 最新バージョンを使用しています。', this._lastResultText);
this._notifyUser(this._lastResultText, /*onlyLogIfAlertDisabled*/ true);
} else {
// クライアントがサーバーより新しい(テスト中など)
this._lastResultText = `クライアントのバージョンがサーバーより新しい可能性があります。\n現在: ${current}\nサーバー: ${latest}`;
console.warn('[VersionChecker] クライアントの方が新しいバージョンです。', this._lastResultText);
this._notifyUser(this._lastResultText, /*onlyLogIfAlertDisabled*/ true);
}
}
/**
* セマンティックバージョン (major.minor.patch) を比較する。
* v1 < v2 の場合 -1, v1 == v2 の場合 0, v1 > v2 の場合 1 を返す。
*/
private _compareSemanticVersion(v1: string, v2: string): number {
const parse = (v: string): number[] => {
return v.split('.').map((s) => {
const n = parseInt(s, 10);
return isNaN(n) ? 0 : n;
});
};
const a = parse(v1);
const b = parse(v2);
const len = Math.max(a.length, b.length);
for (let i = 0; i < len; i++) {
const x = a[i] || 0;
const y = b[i] || 0;
if (x < y) return -1;
if (x > y) return 1;
}
return 0;
}
/**
* 結果をユーザーに通知する。
* showResultAsAlert が true かつブラウザ環境のときは window.alert で表示。
* それ以外はコンソールログのみ。
*
* @param text 表示するメッセージ
* @param onlyLogIfAlertDisabled true の場合、showResultAsAlert が false のときのみ通知する
*/
private _notifyUser(text: string, onlyLogIfAlertDisabled: boolean = false) {
if (this.showResultAsAlert && sys.isBrowser && typeof window !== 'undefined' && typeof (window as any).alert === 'function') {
if (!onlyLogIfAlertDisabled) {
(window as any).alert(text);
}
} else {
// アラート未使用の場合はログのみ
if (this.logDetail || !onlyLogIfAlertDisabled) {
console.log('[VersionChecker] 結果: ', text);
}
}
}
/**
* 直近のチェック結果テキストを取得するためのヘルパー。
* UI に表示したい場合などに利用できます(このコンポーネント単体では使用しません)。
*/
public getLastResultText(): string {
return this._lastResultText;
}
}
コードの要点解説
- onLoad()
- 必須コンポーネント依存はないため取得処理は不要。
- ただし
requestUrlが未設定の場合に警告ログを出し、エディタでの設定漏れに気づけるようにしています。
- start()
enabledOnStartが true のとき、自動でcheckNow()を呼び出します。- ゲーム起動時に一度だけチェックしたい場合、このフラグを true のままにしておけば OK です。
- checkNow()
- 外部(ボタンの onClick など)からも呼び出せる公開メソッド。
- 二重実行防止のため、
_isCheckingフラグで制御。 - URL 未設定の場合はエラーログを出して即終了。
- 内部で
_sendRequest()→_handleResponse()の流れを Promise チェーンで処理しています。
- _sendRequest()
fetchAPI を用いて HTTP リクエストを送信。httpMethodに応じて GET / POST を切り替え。- POST かつ
requestBodyJsonが空でない場合のみボディを送信。 timeoutMs> 0 の場合はPromise.raceでタイムアウト制御。
- _handleResponse()
- レスポンス文字列を JSON.parse し、
responseLatestKey/responseMessageKeyで必要な情報を取り出します。 compareAsSemanticに応じてセマンティック比較 or 文字列比較を実行。- 結果に応じて
_lastResultTextを更新し、ログ +_notifyUser()で通知。
- レスポンス文字列を JSON.parse し、
- _compareSemanticVersion()
1.2.3のような形式を[1, 2, 3]に分解して数値比較。- 桁数が異なる場合にも対応(例: 1.2 vs 1.2.1)。
- _notifyUser()
showResultAsAlertが true かつブラウザ環境のとき、window.alertで簡易ダイアログ表示。- ネイティブ環境やアラート無効時はコンソールログのみ。
使用手順と動作確認
1. スクリプトファイルの作成
- エディタ左下の Assets パネルで、任意のフォルダ(例:
assets/scripts)を右クリックします。 - Create → TypeScript を選択します。
- ファイル名を
VersionChecker.tsに変更します。 - 作成された
VersionChecker.tsをダブルクリックして開き、内容をすべて削除して、前述の TypeScript コードをそのまま貼り付けて保存します。
2. テスト用ノードの作成
- エディタ左上の Hierarchy パネルで、Canvas(なければ先に Create → UI → Canvas で作成)を選択します。
- Canvas を右クリックして Create → Empty Node を選択し、名前を
VersionCheckerNodeなどに変更します。
このノードは UI などを持たない空ノードで構いません。VersionChecker は他のコンポーネントに依存していないため、空ノードにアタッチするだけで動作します。
3. VersionChecker コンポーネントのアタッチ
- Hierarchy で先ほど作成した
VersionCheckerNodeを選択します。 - 右側の Inspector パネルで、Add Component ボタンをクリックします。
- Custom カテゴリの中から VersionChecker を選択して追加します。
4. インスペクタでのプロパティ設定例
Inspector に表示された VersionChecker の各プロパティを、次のように設定してみましょう。
- Enabled On Start:
true- ゲーム起動時に自動チェックしたいので ON。
- Request Url:
https://example.com/api/version- 実際の API エンドポイントに置き換えてください。
- Http Method:
GET- まずは GET でテストするのがおすすめです。
- Request Body Json: 空のままで OK(GET のため無視されます)。
- Current Version:
1.0.0- 今のアプリバージョンを入力します。
- Response Latest Key:
latestVersion- サーバーのレスポンスが
{"latestVersion":"1.2.0"}であることを想定。
- サーバーのレスポンスが
- Response Message Key:
message- 任意のメッセージ(「新しいバージョンがあります」など)が入るキー名。
- Compare As Semantic:
true- セマンティックバージョンでの比較を有効化。
- Log Detail:
true- 動作確認中は詳細ログを見たいので ON。
- Show Result As Alert:
true(Web ビルドで確認する場合)- ブラウザ上で
alertダイアログが表示されます。
- ブラウザ上で
- Timeout Ms:
5000- 5 秒以内に応答がない場合はタイムアウト扱い。
5. サーバーレスポンスの準備例
テスト用に、以下のような JSON を返す簡易 API を用意すると分かりやすいです(ローカルサーバーでも可)。
{
"latestVersion": "1.2.0",
"message": "新しいバージョンが公開されています。ストアからアップデートしてください。"
}
この場合、Current Version = 1.0.0 としておけば、「新しいバージョンが利用可能です」という結果になります。
6. 実行して動作を確認する
- エディタ右上の Play ボタン(プレビュー)をクリックします。
- Game ビューが開き、シーンが再生されます。
- Console(右下または別ウィンドウ)を開き、ログを確認します。
- 設定が正しければ、数秒以内に次のようなログ(またはアラート)が表示されます。
- アップデートが必要な場合:
[VersionChecker] アップデートが必要です: 新しいバージョンが利用可能です。 現在: 1.0.0 最新: 1.2.0 メッセージ: 新しいバージョンが公開されています。ストアからアップデートしてください。 - 最新バージョンの場合:
[VersionChecker] 最新バージョンを使用しています。 現在のバージョンは最新です。 バージョン: 1.2.0 - エラーやタイムアウトの場合:
[VersionChecker] リクエストエラー: Error: タイムアウト: 5000ms
- アップデートが必要な場合:
7. 任意タイミングでの再チェック(ボタン連携例)
VersionChecker は外部から checkNow() を呼び出せるようになっているため、UI ボタンから再チェックすることもできます。
- Hierarchy で Canvas を右クリック → Create → UI → Button を選択します。
- 作成された Button ノードを選択し、Inspector の Button コンポーネントの Click Events に新しい要素を追加します。
- Click Events の Target に
VersionCheckerNodeをドラッグ&ドロップします。 - Component ドロップダウンから VersionChecker を選択します。
- Handler ドロップダウンから checkNow を選択します。
これで、実行中にボタンをクリックするたびにサーバーへバージョンチェックをリクエストできるようになります。
まとめ
今回実装した VersionChecker コンポーネントは、
- 起動時に自動でバージョンチェックを行う。
- ボタンなどから任意タイミングで再チェックできる。
- レスポンス形式やバージョン比較方法をインスペクタから柔軟に変更できる。
- UI コンポーネントや他のカスタムスクリプトに一切依存しない。
という特徴を持つ、完全に独立した汎用コンポーネントです。
実プロジェクトでは、このコンポーネントの結果をもとに、
- 「アップデートしてください」ダイアログを自作 UI で表示する。
- 強制アップデートフラグ(
forceUpdateなど)をレスポンスに追加し、ゲーム進行をブロックする。 - プラットフォーム情報(iOS/Android/Web)やチャンネル情報を
requestBodyJsonに含め、サーバー側でバージョンを出し分ける。
といった拡張が簡単に行えます。
まずはこの記事のコードをそのまま導入し、ログやアラートで動作を確認したうえで、自分のプロジェクトの仕様に合わせて UI 連携などを追加していくと、バージョン管理まわりの実装を安全かつ効率的に進められます。




