【Cocos Creator 3.8】BlinkEffect の実装:アタッチするだけでノードを一定間隔で点滅させる汎用スクリプト
イベント演出やダメージ表現など、「このノードをチカチカ点滅させたい」という場面はよくあります。この記事では、任意のノードにアタッチするだけで、そのノード(と子ノード)の表示/非表示を一定間隔で自動切り替える汎用コンポーネント BlinkEffect を Cocos Creator 3.8 + TypeScript で実装します。
外部の GameManager などには一切依存せず、インスペクタのプロパティだけで完結する設計です。
コンポーネントの設計方針
1. 機能要件の整理
- このコンポーネントをアタッチしたノード(親ノード)の
active(≒表示状態)を一定間隔で ON/OFF する。 - 点滅の ON/OFF 間隔をインスペクタから変更できる。
- 常に点滅し続けるだけでなく、「指定時間だけ点滅して自動停止」もできる。
- ゲームのポーズなどで一時停止したい場合に備え、「点滅の一時停止/再開」もできる。
- 外部スクリプトへの依存は一切なし。このスクリプト単体で完結させる。
ここでは「visible」という表現を、Cocos Creator 3.x の実装上は Node.active の ON/OFF として扱います。
active = false にすると、そのノードと子孫ノードがまとめて非表示になるため、演出としての「点滅」には十分です。
2. インスペクタで設定可能なプロパティ設計
BlinkEffect では以下のプロパティを用意します。
- enabledOnStart: boolean
– デフォルト:true
– 説明: ゲーム開始時(onEnable)に自動で点滅を開始するかどうか。
–falseにすると、最初は点滅せず、後からstartBlink()を呼ぶ(他コンポーネントから)などの制御が可能です。この記事では外部呼び出しは必須ではありませんが、将来の拡張を見据えた設計です。 - blinkInterval: number
– デフォルト:0.3秒
– 説明: 1 回の点滅で「表示→非表示」または「非表示→表示」に切り替えるまでの間隔(秒)。
– 小さくすると高速点滅、大きくするとゆっくり点滅になります。
– 防御的実装として、0.01秒未満の値が設定された場合は内部で0.01秒に補正します。 - startVisible: boolean
– デフォルト:true
– 説明: 点滅開始時にノードを「最初に表示状態にするかどうか」。
– 例えばダメージ演出で「一瞬消えてから点滅させたい」といった場合にfalseに設定できます。 - limitDuration: boolean
– デフォルト:false
– 説明: 点滅に制限時間を設けるかどうか。
–trueにすると、duration秒後に自動で点滅を停止し、ノードを元の表示状態(開始前の状態)に戻します。 - duration: number
– デフォルト:2.0秒
– 説明:limitDurationがtrueのときにだけ有効。点滅を続ける総時間(秒)。
– 0 以下の値が入ってしまった場合は、内部で無視(= 無制限)するように防御的に扱います。 - pauseOnDisable: boolean
– デフォルト:true
– 説明: このコンポーネントがdisabledになったとき(またはノードが非アクティブになったとき)に、点滅を自動停止するかどうか。
– 通常はtrue推奨。falseにすると、enable/disable の再切り替えで挙動を変えたい特殊なケース向けになります。
3. ライフサイクルと挙動の設計
- onLoad
– ノードの初期表示状態(_initialActive)を記録しておく。
– これにより、点滅終了後に「元の状態」に戻すことができます。 - onEnable
–enabledOnStartがtrueなら点滅を開始。
– タイマー用の内部カウンタをリセットし、startVisibleに応じて最初の表示状態を決定します。 - onDisable
–pauseOnDisableがtrueの場合、点滅を停止し、ノードの表示状態を元に戻します。 - update(dt)
– 点滅中のみ動作。
– 経過時間を積算し、blinkIntervalを超えたらnode.activeを反転、積算タイマーをリセット。
–limitDurationが有効な場合、総経過時間を監視し、durationを超えたら自動停止します。
このように、外部からの参照やシングルトンを一切使わず、ノード自身の状態だけを見て完結する設計にします。
TypeScriptコードの実装
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
/**
* BlinkEffect
* 任意のノードにアタッチするだけで、Node.active を一定間隔で ON/OFF する点滅コンポーネント。
* 外部スクリプトへの依存は一切なく、このスクリプト単体で完結します。
*/
@ccclass('BlinkEffect')
export class BlinkEffect extends Component {
@property({
tooltip: 'true の場合、ノードが有効化されたタイミングで自動的に点滅を開始します。'
})
public enabledOnStart: boolean = true;
@property({
tooltip: '点滅の切り替え間隔(秒)。値が小さいほど高速に点滅します。内部的には 0.01 秒未満にはなりません。'
})
public blinkInterval: number = 0.3;
@property({
tooltip: '点滅開始時に、最初の状態を「表示(true) / 非表示(false)」のどちらにするか。'
})
public startVisible: boolean = true;
@property({
tooltip: 'true の場合、指定時間(duration)だけ点滅し、その後自動で停止して元の表示状態に戻します。'
})
public limitDuration: boolean = false;
@property({
tooltip: '点滅を続ける合計時間(秒)。limitDuration が true のときのみ有効です。0 以下の場合は無視されます。'
})
public duration: number = 2.0;
@property({
tooltip: 'コンポーネントが無効化されたときに点滅を停止し、ノードの表示状態を元に戻すかどうか。'
})
public pauseOnDisable: boolean = true;
// --- 内部状態管理用のフィールド ---
/** 点滅中かどうか */
private _isBlinking: boolean = false;
/** 点滅開始前の Node.active の状態を保存 */
private _initialActive: boolean = true;
/** 点滅開始からの経過時間(総時間) */
private _elapsedTotal: number = 0;
/** 前回の ON/OFF 切り替えからの経過時間(インターバル用) */
private _elapsedInterval: number = 0;
onLoad() {
// ノードの初期表示状態を記録しておく
this._initialActive = this.node.active;
}
onEnable() {
// enable 時に自動開始するかどうか
if (this.enabledOnStart) {
this.startBlink();
}
}
onDisable() {
if (this.pauseOnDisable) {
this.stopBlink(true);
}
}
update(dt: number) {
if (!this._isBlinking) {
return;
}
// blinkInterval を防御的に補正
const interval = Math.max(0.01, this.blinkInterval);
this._elapsedInterval += dt;
this._elapsedTotal += dt;
// 一定間隔ごとに active を反転
if (this._elapsedInterval >= interval) {
this._elapsedInterval -= interval;
this.node.active = !this.node.active;
}
// 制限時間が有効なら、duration を超えたら停止
if (this.limitDuration && this.duration > 0 && this._elapsedTotal >= this.duration) {
this.stopBlink(true);
}
}
/**
* 点滅を開始します。
* すでに点滅中の場合はタイマーをリセットして再スタートします。
*/
public startBlink(): void {
// 現在の active を初期状態として保存
this._initialActive = this.node.active;
// 開始時の表示状態を設定
this.node.active = this.startVisible;
this._elapsedTotal = 0;
this._elapsedInterval = 0;
this._isBlinking = true;
}
/**
* 点滅を停止します。
* @param restoreInitial true の場合、開始前の表示状態(_initialActive)に戻します。
*/
public stopBlink(restoreInitial: boolean = true): void {
this._isBlinking = false;
this._elapsedTotal = 0;
this._elapsedInterval = 0;
if (restoreInitial) {
this.node.active = this._initialActive;
}
}
/**
* 現在点滅中かどうかを返します。
*/
public isBlinking(): boolean {
return this._isBlinking;
}
}
コードのポイント解説
- 完全な独立性
– 他のコンポーネントやシングルトンを一切参照していません。
– 必要な情報はすべてthis.nodeとインスペクタで設定したプロパティだけで完結しています。 - onLoad
–this.node.activeを_initialActiveに記録し、後からstopBlink()で元に戻せるようにしています。 - onEnable / onDisable
–enabledOnStartがtrueのときだけ自動開始するので、細かい制御が必要な場合にはfalseにしておき、別コンポーネントからstartBlink()を呼ぶといった拡張も可能です。
–pauseOnDisableがtrueのときは、コンポーネントやノードが無効化されたタイミングで点滅を停止し、_initialActiveに復帰させます。 - update(dt)
–_isBlinkingがtrueのときだけ動きます。
–blinkIntervalを防御的に0.01秒以上に補正してから使用することで、誤設定による極端な負荷やバグを防いでいます。
–_elapsedIntervalがインターバルを超えたらnode.activeを反転。
–limitDuration+duration > 0のときは、_elapsedTotalを監視し、制限時間を超えたらstopBlink(true)を呼んで終了します。 - startBlink / stopBlink
–startBlinkは、現在のnode.activeを_initialActiveに保存してから、startVisibleに応じた初期状態に切り替えます。
–stopBlinkは、restoreInitialがtrueのときに限り、開始前の状態に戻すようにしています。これにより、「点滅後は消えたままにしたい」などの特殊な用途にも対応できます(その場合はstopBlink(false)を呼ぶ)。
使用手順と動作確認
1. スクリプトファイルの作成
- エディタ上部の Assets パネルで、任意のフォルダ(例:
assets/scripts)を右クリックします。 - Create → TypeScript を選択します。
- 新しく作成されたスクリプトの名前を
BlinkEffect.tsに変更します。 - ダブルクリックしてエディタ(VS Code など)で開き、既存のテンプレートコードをすべて削除して、上記の TypeScript コードをそのまま貼り付けて保存します。
2. テスト用ノードの作成
- Hierarchy パネルで右クリックし、Create → 2D Object → Sprite など、表示を確認しやすいノードを作成します。
(3D の場合は Create → 3D Object → Cube などでも構いません) - 作成したノードに適当な画像(SpriteFrame)やマテリアルを設定して、シーンビューで見える状態にしておきます。
3. BlinkEffect コンポーネントのアタッチ
- 先ほど作成した Sprite(または Cube)ノードを Hierarchy から選択します。
- Inspector パネルの下部にある Add Component ボタンをクリックします。
- メニューから Custom → BlinkEffect を選択します。
(@ccclass('BlinkEffect')で定義しているため、この名前で表示されます)
4. プロパティの設定例
Inspector に追加された BlinkEffect の各プロパティを次のように設定してみます。
- Enabled On Start:
true
→ シーン再生と同時に点滅が始まります。 - Blink Interval:
0.2
→ 0.2 秒ごとに表示/非表示が切り替わる、比較的速い点滅。 - Start Visible:
true
→ 再生直後は表示状態からスタートします。 - Limit Duration:
true
→ 一定時間だけ点滅させて自動停止したい場合に有効にします。 - Duration:
1.5
→ 1.5 秒間だけ点滅し、その後は元の表示状態に戻ります。 - Pause On Disable:
true
→ ノードやコンポーネントを無効化した際には点滅を停止し、表示状態を元に戻します。
5. 再生して動作を確認する
- エディタ上部の Play ボタン(プレビューボタン)をクリックしてゲームを実行します。
- シーンビューまたはゲームビューで、先ほどの Sprite(または Cube)が 一定間隔で表示/非表示を繰り返していることを確認します。
Limit Duration = true, Duration = 1.5にしている場合は、約 1.5 秒後に点滅が自動で止まり、元の状態に戻ることも確認してください。
6. 動作パターンのバリエーション
いくつか設定を変えて試すと、用途ごとの感覚がつかみやすくなります。
- 常時点滅させたい場合
–Enabled On Start = true
–Limit Duration = false
→ ゲーム中ずっと点滅させたい UI や警告ランプなどに使えます。 - ダメージ時など、一瞬だけ点滅させたい場合
–Enabled On Start = true
–Limit Duration = true,Duration = 0.5など短め
–Blink Interval = 0.1くらいにすると「バチバチッ」とした感じの演出になります。 - 最初は非表示から点滅させたい場合
–Start Visible = false
→ 「一瞬消えて、そこから点滅しながら現れる」といった演出に使えます。
まとめ
今回実装した BlinkEffect は、任意のノードにアタッチするだけで、点滅演出を即座に導入できる汎用コンポーネントです。
- 外部の GameManager やシングルトンには一切依存せず、このスクリプト単体で完結するように設計しました。
blinkIntervalやdurationなどをインスペクタから調整するだけで、さまざまな点滅パターンを実現できます。- キャラクターのダメージ演出、危険エリアの警告表示、UI ボタンの強調、チュートリアルの注目誘導など、多くの場面で再利用できます。
こうした「アタッチするだけで動く」汎用コンポーネントを積み重ねていくことで、毎回同じような処理を書く手間が減り、ゲーム開発全体のスピードと保守性が大きく向上します。
まずは BlinkEffect から、自分のプロジェクトに合った汎用コンポーネント群を少しずつ増やしていくとよいでしょう。
