【Cocos Creator 3.8】CooldownTimer の実装:アタッチするだけで「スキルのクールダウン管理」を実現する汎用スクリプト
本記事では、任意のノードにアタッチするだけで「スキルのクールダウン状態(使用可能かどうか)」を管理できる汎用コンポーネント CooldownTimer を実装します。
クールダウン時間の管理は多くのゲームで必要になりますが、毎回ロジックを書き直すのは非効率です。この CooldownTimer を使えば、
- クールダウン秒数の指定
- 使用可能かどうかの判定(bool)
- スキル使用時にクールダウンを開始するトリガ
- 残り時間の参照
といった処理を、外部スクリプトに依存せず、このコンポーネント単体で完結させることができます。
コンポーネントの設計方針
このコンポーネントは「スキル(あるいは任意のアクション)のクールダウン状態」を管理することが目的です。
要件整理
- クールダウン時間(秒)をインスペクタで設定できる。
- 現在スキルが「使用可能かどうか」を
booleanで取得できる。 - スキルを使用したタイミングで「クールダウン開始」を呼び出せる公開メソッドを用意する。
- 残りクールダウン時間(秒)を参照できる。
- ゲーム開始時に「すぐ使用可能」か「最初からクールダウン状態にしておくか」を選べる。
- クールダウンを任意のタイミングでリセット/強制完了できる。
- 外部の GameManager やシングルトンに依存せず、このスクリプト単体で完結する。
外部依存をなくすため、以下の方針で設計します。
- クールダウン時間や初期状態など、すべてを
@property経由でインスペクタから設定する。 - 他のノードやカスタムスクリプトへの参照は持たない。
- クールダウンの状態は、外部から
isReadyやremainingTimeのgetアクセサで読めるようにする。 - UI 更新やサウンド再生などはこのコンポーネントの責務に含めず、必要なら他のコンポーネントからこのコンポーネントを参照して処理する。
インスペクタで設定可能なプロパティ
今回の CooldownTimer では、以下のプロパティをインスペクタから設定できるようにします。
-
cooldownDuration(number)- クールダウンにかかる時間(秒)。
- 例:
3.0にすると、スキル使用後 3 秒間は再使用不可。 - 0 以下が設定された場合は内部で 0 として扱い、常に即時使用可能な状態になります。
-
startReady(boolean)- ゲーム開始時に「使用可能状態」でスタートするかどうか。
true:開始直後からスキル使用可能。false:開始時に自動でクールダウンを開始(開始直後は使用不可)。
-
autoStartOnEnable(boolean)- コンポーネントが有効化されたタイミング(
onEnable)で、自動的にクールダウンを初期化するかどうか。 true:有効化されるたびにstartReadyの設定に従って状態を初期化。false:状態は前回のまま維持(手動でresetCooldownなどを呼ぶ前提)。
- コンポーネントが有効化されたタイミング(
-
debugLog(boolean)- クールダウン開始や完了時に
console.logでログを出すかどうか。 - 挙動確認やデバッグ用。リリース時は
false推奨。
- クールダウン開始や完了時に
外部から利用するための公開 API
このコンポーネントは、他のスクリプトから使いやすいように、いくつかの公開メソッドとアクセサを用意します。
-
get isReady(): boolean- 現在スキルが使用可能かどうかを返す読み取り専用プロパティ。
true:使用可能、false:クールダウン中。
-
get remainingTime(): number- 現在の残りクールダウン時間(秒)。
- 使用可能な状態の場合は 0 を返す。
-
get progress(): number- クールダウン進行度(0.0〜1.0)。
- 0:クールダウン未開始 or 完了(使用可能)、1:クールダウン直後(残り時間が最大)。
- UI のゲージなどにそのまま使える値。
-
tryUse(): boolean- スキルを使用しようと試みるメソッド。
- 使用可能な場合:クールダウンを開始し、
trueを返す。 - クールダウン中の場合:何もせず
falseを返す。 - ボタンのクリックイベントなどに直接紐づけやすい形。
-
startCooldown(): void- 明示的にクールダウンを開始する。
- クールダウン中に再度呼ばれた場合は、タイマーをリスタート(残り時間をリセット)する。
- 「スキル使用確定」のタイミングで呼ぶ想定。
-
resetCooldown(): void- クールダウン状態をリセットして、即時使用可能な状態にする。
- 「ステージ開始時に全スキルをリセット」などに使える。
-
forceComplete(): void- クールダウンを強制的に完了させる(残り時間 0 にして使用可能にする)。
- 実質的には
resetCooldown()と同じ挙動だが、意味的に分けておくと読みやすい。
TypeScriptコードの実装
それでは、上記設計に基づいた CooldownTimer.ts の実装コードを示します。
import { _decorator, Component, game } from 'cc';
const { ccclass, property } = _decorator;
/**
* CooldownTimer
* 任意のノードにアタッチして使用する、汎用クールダウン管理コンポーネント。
*
* 主な機能:
* - クールダウン時間の管理
* - 使用可能かどうかの判定 (isReady)
* - 残り時間・進行度の取得 (remainingTime, progress)
* - スキル使用トリガ (tryUse, startCooldown)
*/
@ccclass('CooldownTimer')
export class CooldownTimer extends Component {
@property({
tooltip: 'クールダウン時間(秒)。\n例: 3.0 にすると、スキル使用後 3 秒間は再使用不可になります。'
})
public cooldownDuration: number = 3.0;
@property({
tooltip: 'true の場合、ゲーム開始時(もしくは有効化時)に使用可能状態でスタートします。\nfalse の場合、開始時に自動でクールダウンが開始されます。'
})
public startReady: boolean = true;
@property({
tooltip: 'true の場合、コンポーネントが有効化されるたびにクールダウン状態を初期化します。\nfalse の場合、前回の状態を維持します。'
})
public autoStartOnEnable: boolean = true;
@property({
tooltip: 'デバッグ用ログ出力を有効にします。クールダウン開始・完了時に console.log へ出力します。'
})
public debugLog: boolean = false;
// 内部状態管理用
private _isReady: boolean = true;
private _remainingTime: number = 0;
private _elapsedTime: number = 0;
private _cooldownActive: boolean = false;
/**
* 現在スキルが使用可能かどうか。
* true: 使用可能, false: クールダウン中
*/
public get isReady(): boolean {
return this._isReady;
}
/**
* 現在の残りクールダウン時間(秒)。
* 使用可能な状態の場合は 0 を返します。
*/
public get remainingTime(): number {
return this._remainingTime;
}
/**
* クールダウン進行度 (0.0〜1.0)。
* 0: 使用可能 or 未開始, 1: クールダウン直後(残り時間が最大)。
*/
public get progress(): number {
if (!this._cooldownActive || this.cooldownDuration <= 0) {
return 0;
}
// 経過時間 / 全体時間
return Math.min(1, Math.max(0, this._elapsedTime / this.cooldownDuration));
}
onLoad() {
// 不正なクールダウン時間が設定されていた場合の防御的処理
if (this.cooldownDuration < 0) {
if (this.debugLog) {
console.warn('[CooldownTimer] cooldownDuration が負の値です。0 に補正します。');
}
this.cooldownDuration = 0;
}
}
onEnable() {
if (this.autoStartOnEnable) {
this._initializeState();
}
}
start() {
// autoStartOnEnable が false の場合でも、初期状態が未設定ならここで初期化
if (!this.autoStartOnEnable) {
this._initializeState();
}
}
/**
* 状態の初期化。
* startReady のフラグに応じて、使用可能 or クールダウン状態から開始します。
*/
private _initializeState() {
if (this.startReady || this.cooldownDuration <= 0) {
this._setReadyState();
} else {
this.startCooldown();
}
}
/**
* フレームごとの更新処理。
* クールダウン中であれば時間を進め、完了したら使用可能状態に戻します。
*/
update(deltaTime: number) {
if (!this._cooldownActive) {
return;
}
// エディタの一時停止中は game.deltaTime が 0 になる場合があるが、
// update の deltaTime をそのまま利用する。
this._elapsedTime += deltaTime;
this._remainingTime = Math.max(0, this.cooldownDuration - this._elapsedTime);
if (this._remainingTime <= 0) {
this._setReadyState();
if (this.debugLog) {
console.log(`[CooldownTimer] クールダウン完了 (node: ${this.node.name})`);
}
}
}
/**
* スキルを使用しようと試みます。
* 使用可能な場合はクールダウンを開始し、true を返します。
* クールダウン中の場合は何もせず false を返します。
*/
public tryUse(): boolean {
if (this.isReady) {
this.startCooldown();
return true;
}
return false;
}
/**
* クールダウンを開始します。
* すでにクールダウン中の場合は、タイマーをリスタートします。
*/
public startCooldown(): void {
if (this.cooldownDuration <= 0) {
// クールダウン時間が 0 以下の場合は即時使用可能のまま
this._setReadyState();
if (this.debugLog) {
console.log('[CooldownTimer] cooldownDuration が 0 以下のため、クールダウンは開始されません。');
}
return;
}
this._cooldownActive = true;
this._isReady = false;
this._elapsedTime = 0;
this._remainingTime = this.cooldownDuration;
if (this.debugLog) {
console.log(`[CooldownTimer] クールダウン開始: ${this.cooldownDuration.toFixed(2)} 秒 (node: ${this.node.name})`);
}
}
/**
* クールダウン状態をリセットし、即時使用可能な状態に戻します。
*/
public resetCooldown(): void {
this._setReadyState();
if (this.debugLog) {
console.log(`[CooldownTimer] クールダウンリセット (node: ${this.node.name})`);
}
}
/**
* クールダウンを強制的に完了させます。
* 内部的には resetCooldown() と同等ですが、意味的な区別のために用意しています。
*/
public forceComplete(): void {
this.resetCooldown();
if (this.debugLog) {
console.log(`[CooldownTimer] クールダウン強制完了 (node: ${this.node.name})`);
}
}
/**
* 内部的に「使用可能状態」に遷移させるヘルパー。
*/
private _setReadyState() {
this._cooldownActive = false;
this._isReady = true;
this._elapsedTime = 0;
this._remainingTime = 0;
}
}
コードのポイント解説
-
onLoadcooldownDurationが負の値になっている場合に 0 に補正し、警告ログを出します。- インスペクタで誤った値を入れてしまった場合の防御的実装です。
-
onEnable/startautoStartOnEnableがtrueの場合、onEnableで状態を初期化します。falseの場合はstartで一度だけ初期化します。- これにより、「一度無効化して再度有効化したときにクールダウンをリセットしたいかどうか」を選べます。
-
update_cooldownActiveがtrueのときだけ、deltaTimeを加算して経過時間を進めます。- 残り時間を
cooldownDuration - _elapsedTimeから計算し、0 未満にならないようMath.maxで補正。 - 残り時間が 0 以下になったら
_setReadyState()で使用可能状態に戻します。
-
tryUse()- スキルボタンなどから直接呼び出すことを想定したメソッドです。
isReadyがtrueのときだけstartCooldown()を呼び、trueを返します。- クールダウン中に呼ばれても何もせず
falseを返すので、呼び出し側で「使用できたかどうか」を簡単に判定できます。
-
startCooldown()- クールダウンを開始するメソッドです。すでにクールダウン中でもタイマーをリスタートします。
cooldownDuration <= 0の場合はクールダウンを開始せず、即時使用可能状態のままにします。
-
remainingTime/progress- UI ゲージやクールダウン表示に使いやすいように、読み取り専用の
getアクセサで提供しています。 progressは「0〜1 の割合」なので、fillRangeやscaleなどにそのまま使えます。
- UI ゲージやクールダウン表示に使いやすいように、読み取り専用の
使用手順と動作確認
ここからは、実際に Cocos Creator 3.8.7 のエディタ上で CooldownTimer を使う手順を説明します。
1. スクリプトファイルの作成
- エディタ下部の Assets パネルで、任意のフォルダ(例:
scriptsフォルダ)を右クリックします。 - Create → TypeScript を選択します。
- 作成されたファイル名を
CooldownTimer.tsに変更します。 CooldownTimer.tsをダブルクリックしてエディタ(VSCode など)で開き、先ほどのコードを丸ごと貼り付けて保存します。
2. テスト用ノードの作成
クールダウンは見た目では分かりにくいので、まずはログで動作を確認します。
- Hierarchy パネルで右クリック → Create → Empty Node を選択し、
TestCooldownなど分かりやすい名前を付けます。 - このノードに、後でボタンから呼び出すための簡易テストスクリプトを追加しても良いですが、まずは
CooldownTimer単体を動かしてみます。
3. CooldownTimer コンポーネントをアタッチ
- Hierarchy で先ほど作成した
TestCooldownノードを選択します。 - Inspector パネルの下部にある Add Component ボタンをクリックします。
- Custom → CooldownTimer を選択します。
これで TestCooldown ノードに CooldownTimer コンポーネントがアタッチされました。
4. プロパティを設定して挙動を確認
Inspector 上で、CooldownTimer の各プロパティを設定します。
Cooldown Duration:3.0Start Ready:チェックを ON(true)Auto Start On Enable:チェックを ONDebug Log:チェックを ON(ログ確認用)
続いて、実際にクールダウンを動かしてみます。
- エディタ右上の Play ボタンを押してプレビューを開始します。
- Developer Tools(ブラウザ)や Console(エディタ内プレビュー)を開いてログを確認できるようにします。
- 一度、コンソールから以下のように呼び出してみます(ブラウザのコンソールを想定):
// 例: 最初のノードから CooldownTimer を取得して tryUse を呼ぶ
const node = cc.director.getScene().getChildByName('TestCooldown');
const timer = node.getComponent('CooldownTimer');
timer.tryUse(); // 使用可能なら true を返し、クールダウンが始まる
Debug Log を ON にしていれば、コンソールに
[CooldownTimer] クールダウン開始: 3.00 秒 (node: TestCooldown)
というログが出力され、3 秒後に
[CooldownTimer] クールダウン完了 (node: TestCooldown)
というログが表示されます。
5. ボタンからクールダウンを使ってみる(UI 連携例)
より実践的な確認として、UI ボタンから tryUse() を呼び出してみます。
- Hierarchy パネルで右クリック → Create → UI → Button を選択します。
- 作成された Button ノードを選択し、名前を
SkillButtonなどに変更します。 - Button ノードの子として自動で
Labelが付いているので、テキストを「Use Skill」などに変更します。
次に、CooldownTimer を呼び出すための簡単なスクリプトを作ります。
- Assets パネルで右クリック → Create → TypeScript を選択し、
SkillButtonHandler.tsという名前にします。 - 以下のコードを貼り付けます。
import { _decorator, Component, Node } from 'cc';
import { CooldownTimer } from './CooldownTimer';
const { ccclass, property } = _decorator;
@ccclass('SkillButtonHandler')
export class SkillButtonHandler extends Component {
@property({
type: CooldownTimer,
tooltip: 'このボタンが使用する CooldownTimer コンポーネントへの参照。'
})
public cooldownTimer: CooldownTimer | null = null;
/**
* Button の Click イベントから呼び出されるメソッド。
*/
public onClickSkillButton() {
if (!this.cooldownTimer) {
console.warn('[SkillButtonHandler] cooldownTimer が設定されていません。');
return;
}
const used = this.cooldownTimer.tryUse();
if (used) {
console.log('[SkillButtonHandler] スキル使用! クールダウン開始');
} else {
console.log(`[SkillButtonHandler] スキルはクールダウン中です。残り: ${this.cooldownTimer.remainingTime.toFixed(2)} 秒`);
}
}
}
このスクリプトは補助的なものであり、本記事の主役である CooldownTimer とは独立しています。CooldownTimer 自体は他のスクリプトがなくても完結して動作します。
続いて、このハンドラをボタンに設定します。
- Hierarchy で
SkillButtonノードを選択します。 - Inspector の Add Component → Custom → SkillButtonHandler を追加します。
SkillButtonHandlerのCooldown Timerプロパティに、先ほどCooldownTimerをアタッチしたTestCooldownノードをドラッグ&ドロップします。SkillButtonノードの Button コンポーネントを確認し、Click Events セクションで+ボタンを押してイベントを追加します。- 追加されたイベントの Target に
SkillButtonノードをドラッグ&ドロップします。 - Component プルダウンから
SkillButtonHandlerを選択し、Handler プルダウンからonClickSkillButtonを選択します。
これで、ゲーム実行中にボタンを押すと CooldownTimer の tryUse() が呼ばれ、クールダウンに応じてログが変化するようになります。
まとめ
本記事では、Cocos Creator 3.8.7 と TypeScript を用いて、外部の GameManager やシングルトンに一切依存しない、汎用クールダウン管理コンポーネント CooldownTimer を実装しました。
- クールダウン時間・初期状態・自動初期化・デバッグログをインスペクタから簡単に設定可能。
isReadyで「スキル使用可否」をbooleanで取得できる。remainingTimeやprogressで、UI ゲージやテキスト表示にそのまま使える情報を提供。tryUse()/startCooldown()/resetCooldown()/forceComplete()といった明確な API により、他のスクリプトから直感的に利用可能。- 任意のノードにアタッチするだけで完結し、プロジェクト間でもそのまま再利用しやすい。
このコンポーネントをベースに、
- UI のクールダウンゲージ(
progressをSprite.fillRangeに反映) - スキルアイコンのグレイアウト(
isReadyに応じてButton.interactableを切り替え) - 複数スキルの個別クールダウン管理(各スキルごとに
CooldownTimerをアタッチ)
など、さまざまな場面で簡単に拡張・応用できます。
プロジェクトの共通コンポーネントとして CooldownTimer.ts を 1 つ用意しておけば、どのシーン・どのゲームでも同じ使い勝手でクールダウンを扱えるようになり、開発効率が大きく向上します。ぜひ、自分のプロジェクト用にカスタマイズしつつ、再利用可能なコンポーネントとして育てていってください。




