【Cocos Creator 3.8】BossHealthBar の実装:アタッチするだけで「画面下部に固定表示される巨大ボスHPバーUI」を実現する汎用スクリプト
この記事では、任意のCanvas配下のノードにアタッチするだけで、画面下部に固定表示されるボス用HPバーを制御できる汎用コンポーネント BossHealthBar を実装します。
ボス戦だけで使う大きなHPバーを毎回組むのは面倒ですが、このスクリプトを使えば、HP値の更新・アニメーション・色変化・自動非表示 まで一括で扱えるようになります。
コンポーネントの設計方針
機能要件の整理
- 画面下部に固定表示される、横長のボスHPバーUI
- Canvas配下の任意のノードにアタッチするだけで動作
- インスペクタから以下を調整可能にする
- 最大HP・現在HP
- バーの見た目(背景色・前景色)
- アニメーション(ダメージ時の減少速度・遅延)
- HP 0 で自動的にフェードアウトして非表示にするか
- 画面下部での位置・サイズ・マージン
- 表示/非表示の初期状態
- 外部スクリプト(GameManager や Boss スクリプトなど)への依存は一切禁止
- HPの更新は 公開メソッド(
setMaxHealth,setCurrentHealth,damage,healなど)経由で行う - 必要なUIノードは 自動生成 し、Prefab や別ノードへの依存をなくす
- Canvas配下でのレイアウトは
UITransformとWidgetを利用して「下部に固定」
このコンポーネントは、自身のノードを「ボスHPバーコンテナ」として再構成し、その子として
- 背景バー(BG)
- メインHPバー(即時反映)
- ダメージ遅延バー(ゆっくり追従する赤いバーなど)
の 3 レイヤーを自動生成して管理します。
インスペクタで設定可能なプロパティ設計
以下のようなプロパティを用意します。
- HP設定
maxHealth:最大HP(例: 10000)。初期最大値。currentHealth:現在HP(例: 10000)。開始時のHP。autoClampHealth:HP更新時に 0〜maxHealth に自動Clampするか。
- 表示・レイアウト
startVisible:ゲーム開始時に表示状態で始めるか。barWidth:バー全体の横幅(px)。barHeight:バー全体の高さ(px)。bottomMargin:画面下端からの距離(px)。horizontalMargin:左右マージン(px)。0 の場合は中央寄せで width 優先。useFullWidth:true の場合、画面幅いっぱいにバーを伸ばす。
- 色・見た目
backgroundColor:背景バーの色。foregroundColor:メインHPバーの色。damageDelayColor:ダメージ遅延バーの色(例: 赤)。cornerRadius:角丸の半径(Sprite.Type.SLICED前提で使用)。
- ダメージアニメーション
useDamageDelayBar:ダメージ遅延バーを表示するか。damageDelay:ダメージ後、遅延バーが追従し始めるまでの待ち時間(秒)。damageFollowSpeed:遅延バーがメインバーに追従する速度(1〜20 くらい)。
- 自動非表示・フェード
autoHideOnZero:HP が 0 になったら自動的に非表示にするか。fadeOutDuration:フェードアウト時間(秒)。0 の場合は即時非表示。hideAfterFade:フェード後にノード自体をactive = falseにするか。
また、HPを外部から更新するためのメソッドも用意します。
setMaxHealth(value: number, keepRatio = false)setCurrentHealth(value: number)damage(amount: number)heal(amount: number)show()/hide()reset()(最大HP・現在HPを初期値に戻す)
TypeScriptコードの実装
以下が、BossHealthBar.ts の完成コードです。
import { _decorator, Component, Node, UITransform, Widget, Color, Sprite, SpriteFrame, UIOpacity, tween, Vec3, math } from 'cc';
const { ccclass, property } = _decorator;
/**
* BossHealthBar
* 画面下部に固定表示される巨大ボスHPバーUIを制御する独立コンポーネント。
* - このコンポーネントを Canvas 配下の任意のノードにアタッチするだけで動作します。
* - 外部の GameManager や Boss スクリプトへの依存は一切ありません。
*/
@ccclass('BossHealthBar')
export class BossHealthBar extends Component {
// =========================
// HP 設定
// =========================
@property({
tooltip: 'ボスの最大HP。ゲーム開始時の最大値として使用されます。'
})
maxHealth: number = 10000;
@property({
tooltip: 'ボスの現在HP。onLoad 時に maxHealth を超えないように調整されます。'
})
currentHealth: number = 10000;
@property({
tooltip: 'HPを更新する際に 0〜maxHealth の範囲に自動Clampするかどうか。'
})
autoClampHealth: boolean = true;
// =========================
// 表示・レイアウト
// =========================
@property({
tooltip: 'ゲーム開始時にボスHPバーを表示状態で開始するかどうか。'
})
startVisible: boolean = true;
@property({
tooltip: 'ボスHPバー全体の横幅(ピクセル)。useFullWidth が true の場合は無視されます。'
})
barWidth: number = 800;
@property({
tooltip: 'ボスHPバー全体の高さ(ピクセル)。'
})
barHeight: number = 40;
@property({
tooltip: '画面下端からの距離(ピクセル)。Widget コンポーネントで制御されます。'
})
bottomMargin: number = 40;
@property({
tooltip: '左右マージン(ピクセル)。0 の場合は中央寄せで barWidth を優先します。'
})
horizontalMargin: number = 40;
@property({
tooltip: 'true の場合、画面幅いっぱいにバーを伸ばします(左右マージンを優先)。'
})
useFullWidth: boolean = false;
// =========================
// 色・見た目
// =========================
@property({
tooltip: '背景バーの色。'
})
backgroundColor: Color = new Color(30, 30, 30, 200);
@property({
tooltip: 'メインHPバーの色。'
})
foregroundColor: Color = new Color(0, 200, 0, 255);
@property({
tooltip: 'ダメージ遅延バーの色(メインバーより遅れて減少するバー)。'
})
damageDelayColor: Color = new Color(200, 0, 0, 200);
@property({
tooltip: '角丸の半径(Sprite.Type.SLICED を使用する場合に有効)。\n実際の見た目は使用するSpriteFrameに依存します。',
})
cornerRadius: number = 8;
@property({
type: SpriteFrame,
tooltip: 'バーの描画に使用するSpriteFrame(単色でもOK)。\n未設定の場合はエンジンのデフォルトSpriteFrameが使用されます。',
})
barSpriteFrame: SpriteFrame | null = null;
// =========================
// ダメージアニメーション
// =========================
@property({
tooltip: 'ダメージ遅延バー(赤いバー)を表示するかどうか。'
})
useDamageDelayBar: boolean = true;
@property({
tooltip: 'ダメージを受けた後、遅延バーが追従し始めるまでの待ち時間(秒)。'
})
damageDelay: number = 0.3;
@property({
tooltip: '遅延バーがメインバーに追従する速度。\n値が大きいほど素早く追従します(推奨: 5〜15)。'
})
damageFollowSpeed: number = 8;
// =========================
// 自動非表示・フェード
// =========================
@property({
tooltip: 'HPが0になったとき、自動的にフェードアウトして非表示にするかどうか。'
})
autoHideOnZero: boolean = true;
@property({
tooltip: 'フェードアウトにかける時間(秒)。0 の場合は即時非表示。'
})
fadeOutDuration: number = 0.4;
@property({
tooltip: 'フェードアウト完了後にこのノードを非アクティブ(active = false)にするかどうか。'
})
hideAfterFade: boolean = true;
// =========================
// 内部状態
// =========================
private _bgNode: Node | null = null;
private _fgNode: Node | null = null;
private _delayNode: Node | null = null;
private _bgTransform: UITransform | null = null;
private _fgTransform: UITransform | null = null;
private _delayTransform: UITransform | null = null;
private _uiOpacity: UIOpacity | null = null;
private _targetHealthRatio: number = 1; // currentHealth / maxHealth
private _displayHealthRatio: number = 1; // メインバーに表示されている比率
private _delayHealthRatio: number = 1; // 遅延バーに表示されている比率
private _damageDelayTimer: number = 0;
private _isTakingDamage: boolean = false;
private _isFadingOut: boolean = false;
onLoad() {
// HP の初期整合性をとる
if (this.maxHealth <= 0) {
console.warn('[BossHealthBar] maxHealth が 0 以下です。1 に補正します。');
this.maxHealth = 1;
}
if (this.autoClampHealth) {
this.currentHealth = math.clamp(this.currentHealth, 0, this.maxHealth);
}
this._targetHealthRatio = this.currentHealth / this.maxHealth;
this._displayHealthRatio = this._targetHealthRatio;
this._delayHealthRatio = this._targetHealthRatio;
// 必要な UI コンポーネントをセットアップ
this._setupRootNode();
this._createBarNodes();
this._applyInitialLayout();
this._applyColors();
// 初期表示状態
this._uiOpacity = this.node.getComponent(UIOpacity);
if (!this._uiOpacity) {
this._uiOpacity = this.node.addComponent(UIOpacity);
}
if (this.startVisible) {
this._uiOpacity.opacity = 255;
this.node.active = true;
} else {
this._uiOpacity.opacity = 0;
this.node.active = false;
}
}
start() {
// 特別な処理は不要だが、将来の拡張用に残しておく
}
update(deltaTime: number) {
// フェードアウト中は HP アニメーションを止める
if (this._isFadingOut) {
return;
}
// メインバーをターゲット比率にスムーズに追従させる
const lerpSpeed = 15; // メインバーの追従速度
this._displayHealthRatio = math.lerp(this._displayHealthRatio, this._targetHealthRatio, deltaTime * lerpSpeed);
this._displayHealthRatio = math.clamp01(this._displayHealthRatio);
// ダメージ遅延バーの挙動
if (this.useDamageDelayBar) {
if (this._isTakingDamage) {
// ダメージを受けた直後は一定時間待つ
if (this._damageDelayTimer > 0) {
this._damageDelayTimer -= deltaTime;
} else {
// 待機後、メインバーに向かって追従開始
this._delayHealthRatio = math.lerp(this._delayHealthRatio, this._displayHealthRatio, deltaTime * this.damageFollowSpeed);
this._delayHealthRatio = math.clamp01(this._delayHealthRatio);
// ほぼ同じになったら完了
if (Math.abs(this._delayHealthRatio - this._displayHealthRatio) < 0.001) {
this._delayHealthRatio = this._displayHealthRatio;
this._isTakingDamage = false;
}
}
} else {
// ダメージを受けていない時は同じ値にしておく
this._delayHealthRatio = this._displayHealthRatio;
}
} else {
this._delayHealthRatio = this._displayHealthRatio;
}
this._updateBarSizes();
// HP が 0 になったら自動非表示
if (this.autoHideOnZero && this.currentHealth <= 0 && !this._isFadingOut) {
this._startFadeOut();
}
}
// =========================
// 公開API
// =========================
/**
* 最大HPを設定します。
* @param value 新しい最大HP
* @param keepRatio true の場合、現在の割合を維持して currentHealth を再計算します。
*/
public setMaxHealth(value: number, keepRatio: boolean = false) {
if (value <= 0) {
console.warn('[BossHealthBar] setMaxHealth: value が 0 以下です。1 に補正します。');
value = 1;
}
if (keepRatio) {
const ratio = this.currentHealth / this.maxHealth;
this.maxHealth = value;
this.currentHealth = math.clamp(Math.round(this.maxHealth * ratio), 0, this.maxHealth);
} else {
this.maxHealth = value;
if (this.autoClampHealth) {
this.currentHealth = math.clamp(this.currentHealth, 0, this.maxHealth);
}
}
this._updateTargetRatio();
}
/**
* 現在HPを直接設定します。
* @param value 新しい現在HP
*/
public setCurrentHealth(value: number) {
if (this.autoClampHealth) {
this.currentHealth = math.clamp(value, 0, this.maxHealth);
} else {
this.currentHealth = value;
}
this._updateTargetRatio();
}
/**
* ダメージを与えます(現在HPを減少させます)。
* @param amount ダメージ量(正の値)
*/
public damage(amount: number) {
if (amount <= 0) {
return;
}
const newHealth = this.currentHealth - amount;
this.setCurrentHealth(newHealth);
// ダメージ遅延バー用のフラグ
if (this.useDamageDelayBar) {
this._isTakingDamage = true;
this._damageDelayTimer = this.damageDelay;
}
}
/**
* 回復させます(現在HPを増加させます)。
* @param amount 回復量(正の値)
*/
public heal(amount: number) {
if (amount <= 0) {
return;
}
const newHealth = this.currentHealth + amount;
this.setCurrentHealth(newHealth);
// 回復時は遅延バーもすぐ追従させる
this._delayHealthRatio = this.currentHealth / this.maxHealth;
this._isTakingDamage = false;
}
/**
* HPバーを表示します(フェードイン付き)。
*/
public show() {
if (!this._uiOpacity) {
this._uiOpacity = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
}
this.node.active = true;
this._isFadingOut = false;
tween(this._uiOpacity)
.to(0.2, { opacity: 255 })
.start();
}
/**
* HPバーを非表示にします(フェードアウト付き)。
*/
public hide() {
this._startFadeOut();
}
/**
* HPバーを初期状態にリセットします。
* - currentHealth を maxHealth に戻し、
* - フェード状態を解除し、
* - 表示状態にします。
*/
public reset() {
this.currentHealth = this.maxHealth;
this._targetHealthRatio = 1;
this._displayHealthRatio = 1;
this._delayHealthRatio = 1;
this._isTakingDamage = false;
this._damageDelayTimer = 0;
this._isFadingOut = false;
if (!this._uiOpacity) {
this._uiOpacity = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
}
this._uiOpacity.opacity = 255;
this.node.active = true;
this._updateBarSizes();
}
// =========================
// 内部処理
// =========================
private _updateTargetRatio() {
this._targetHealthRatio = math.clamp01(this.currentHealth / this.maxHealth);
if (this._targetHealthRatio > this._displayHealthRatio) {
// 回復時はメインバーを即座に反映させる
this._displayHealthRatio = this._targetHealthRatio;
}
}
private _startFadeOut() {
if (!this.autoHideOnZero) {
return;
}
if (!this._uiOpacity) {
this._uiOpacity = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
}
this._isFadingOut = true;
if (this.fadeOutDuration <= 0) {
this._uiOpacity.opacity = 0;
if (this.hideAfterFade) {
this.node.active = false;
}
return;
}
tween(this._uiOpacity)
.to(this.fadeOutDuration, { opacity: 0 })
.call(() => {
if (this.hideAfterFade) {
this.node.active = false;
}
})
.start();
}
/**
* このノード自体を「ボスHPバーコンテナ」としてセットアップします。
*/
private _setupRootNode() {
// UITransform が必須
let uiTrans = this.node.getComponent(UITransform);
if (!uiTrans) {
uiTrans = this.node.addComponent(UITransform);
}
// Widget を利用して画面下部に固定
let widget = this.node.getComponent(Widget);
if (!widget) {
widget = this.node.addComponent(Widget);
}
widget.isAlignTop = false;
widget.isAlignBottom = true;
widget.isAlignLeft = this.useFullWidth || this.horizontalMargin > 0;
widget.isAlignRight = this.useFullWidth || this.horizontalMargin > 0;
widget.bottom = this.bottomMargin;
widget.left = this.useFullWidth ? this.horizontalMargin : 0;
widget.right = this.useFullWidth ? this.horizontalMargin : 0;
// サイズ設定
if (this.useFullWidth) {
// 幅は Canvas の幅に依存(Widget に任せる)
uiTrans.height = this.barHeight;
} else {
uiTrans.width = this.barWidth;
uiTrans.height = this.barHeight;
// 中央寄せ
this.node.setPosition(new Vec3(0, -this.bottomMargin, 0));
}
}
/**
* 背景・メイン・遅延バーのノードを生成します。
*/
private _createBarNodes() {
// 既存の子ノードはそのまま残す(他用途との共存を許可)
const rootTransform = this.node.getComponent(UITransform);
if (!rootTransform) {
console.error('[BossHealthBar] UITransform がノードに存在しません。コンポーネントの追加に問題があります。');
return;
}
// 背景バー
this._bgNode = new Node('BossHP_BG');
this.node.addChild(this._bgNode);
this._bgTransform = this._bgNode.addComponent(UITransform);
this._bgTransform.setAnchorPoint(0, 0.5); // 左中央
this._bgNode.setPosition(new Vec3(-rootTransform.width / 2, 0, 0));
const bgSprite = this._bgNode.addComponent(Sprite);
if (this.barSpriteFrame) {
bgSprite.spriteFrame = this.barSpriteFrame;
}
bgSprite.type = Sprite.Type.SLICED;
// ダメージ遅延バー
this._delayNode = new Node('BossHP_Delay');
this.node.addChild(this._delayNode);
this._delayTransform = this._delayNode.addComponent(UITransform);
this._delayTransform.setAnchorPoint(0, 0.5);
this._delayNode.setPosition(new Vec3(-rootTransform.width / 2, 0, 0));
const delaySprite = this._delayNode.addComponent(Sprite);
if (this.barSpriteFrame) {
delaySprite.spriteFrame = this.barSpriteFrame;
}
delaySprite.type = Sprite.Type.SLICED;
// メインHPバー
this._fgNode = new Node('BossHP_FG');
this.node.addChild(this._fgNode);
this._fgTransform = this._fgNode.addComponent(UITransform);
this._fgTransform.setAnchorPoint(0, 0.5);
this._fgNode.setPosition(new Vec3(-rootTransform.width / 2, 0, 0));
const fgSprite = this._fgNode.addComponent(Sprite);
if (this.barSpriteFrame) {
fgSprite.spriteFrame = this.barSpriteFrame;
}
fgSprite.type = Sprite.Type.SLICED;
}
/**
* 初期レイアウトを適用します。
*/
private _applyInitialLayout() {
const rootTransform = this.node.getComponent(UITransform);
if (!rootTransform) {
console.error('[BossHealthBar] UITransform がノードに存在しません。');
return;
}
const width = rootTransform.width;
const height = rootTransform.height;
if (this._bgTransform) {
this._bgTransform.width = width;
this._bgTransform.height = height;
}
if (this._fgTransform) {
this._fgTransform.width = width * this._displayHealthRatio;
this._fgTransform.height = height;
}
if (this._delayTransform) {
this._delayTransform.width = width * this._delayHealthRatio;
this._delayTransform.height = height;
}
}
/**
* バーの色を適用します。
*/
private _applyColors() {
if (this._bgNode) {
const s = this._bgNode.getComponent(Sprite);
if (s) {
s.color = this.backgroundColor;
}
}
if (this._fgNode) {
const s = this._fgNode.getComponent(Sprite);
if (s) {
s.color = this.foregroundColor;
}
}
if (this._delayNode) {
const s = this._delayNode.getComponent(Sprite);
if (s) {
s.color = this.damageDelayColor;
}
}
}
/**
* 毎フレーム、HP比率に基づいてバーの幅を更新します。
*/
private _updateBarSizes() {
const rootTransform = this.node.getComponent(UITransform);
if (!rootTransform) return;
const width = rootTransform.width;
const height = rootTransform.height;
if (this._bgTransform) {
this._bgTransform.width = width;
this._bgTransform.height = height;
}
if (this._fgTransform) {
this._fgTransform.width = width * this._displayHealthRatio;
this._fgTransform.height = height;
}
if (this._delayTransform) {
this._delayTransform.width = width * this._delayHealthRatio;
this._delayTransform.height = height;
this._delayNode!.active = this.useDamageDelayBar;
}
// 左端基準位置を再設定(Canvas サイズが変わる可能性もあるため)
const leftX = -width / 2;
if (this._bgNode) this._bgNode.setPosition(new Vec3(leftX, 0, 0));
if (this._fgNode) this._fgNode.setPosition(new Vec3(leftX, 0, 0));
if (this._delayNode) this._delayNode.setPosition(new Vec3(leftX, 0, 0));
}
}
主要メソッドの解説
- onLoad
- maxHealth / currentHealth の整合性を取り、内部の比率変数を初期化。
- HPバー用の UI ノード(背景・メイン・遅延)を自動生成。
- Widget と UITransform を利用して「画面下部固定」のレイアウトを構築。
- UIOpacity を追加し、初期表示状態(startVisible)に応じて active / opacity を設定。
- update
- targetHealthRatio(論理的なHP比率)に対して、displayHealthRatio(見た目)を補間。
- ダメージ遅延バー用の delayHealthRatio を、タイマーと速度に応じて追従させる。
- HP が 0 になった場合、autoHideOnZero が true ならフェードアウト処理を開始。
- 毎フレーム _updateBarSizes() で UITransform の width を更新し、バーの長さを調整。
- 公開API(setCurrentHealth / damage / heal)
- 外部スクリプトから HP を直接変更するための窓口。
- damage() はダメージ遅延バー用のフラグやタイマーも更新。
- heal() は回復時に遅延バーも即座に追従させる仕様。
- show / hide / reset
- show(): HPバーをフェードイン表示。
- hide(): HPバーをフェードアウト非表示。
- reset(): HPを最大に戻し、バーを初期状態で表示。
- _setupRootNode / _createBarNodes
- BossHealthBar がアタッチされたノードを「コンテナ」として設定し、Widget で画面下配置。
- 背景・メイン・遅延バーを子ノードとして自動生成し、Sprite / UITransform を追加。
使用手順と動作確認
1. スクリプトファイルの作成
- Assets パネルで右クリック → Create → TypeScript を選択します。
- 新規スクリプトの名前を BossHealthBar.ts に変更します。
- ダブルクリックしてエディタで開き、中身をすべて削除して、上記のコードを貼り付けて保存します。
2. テスト用 Canvas / シーン準備
- Hierarchy パネルで、シーン内に Canvas が存在することを確認します。
- もし無ければ、右クリック → Create → UI → Canvas で作成します。
- Canvas の RenderMode が SCREEN_SPACE_CAMERA または SCREEN_SPACE_OVERLAY のいずれかになっていることを確認します(通常のUI用途)。
3. BossHealthBar 用ノードの作成とアタッチ
- Hierarchy で Canvas を右クリック → Create → Empty Node で空ノードを作成します。
- ノード名を分かりやすく BossHealthBarRoot などに変更します。
- BossHealthBarRoot ノードを選択し、Inspector 右下の Add Component をクリックします。
- Custom → BossHealthBar を選択してアタッチします。
4. Inspector でプロパティを設定
BossHealthBar コンポーネントを選択すると、多数のプロパティが表示されます。まずは基本的な動作を確認するため、以下のように設定してみてください。
- HP 設定
maxHealth:10000currentHealth:10000autoClampHealth:ON
- 表示・レイアウト
startVisible:ONbarWidth:800barHeight:40bottomMargin:40horizontalMargin:40useFullWidth:最初は OFF(後でONも試してみてください)
- 色・見た目
backgroundColor:暗いグレー(デフォルトのままでOK)foregroundColor:緑(デフォルトのままでOK)damageDelayColor:赤(デフォルトのままでOK)barSpriteFrame:任意の単色SpriteFrame を指定するのが望ましいですが、未設定でも動作します。
- ダメージアニメーション
useDamageDelayBar:ONdamageDelay:0.3damageFollowSpeed:8〜12 程度
- 自動非表示・フェード
autoHideOnZero:ONfadeOutDuration:0.4hideAfterFade:ON
5. シーン再生してHPバーの表示を確認
- 上部ツールバーの ▶(Play)ボタン を押してシーンを再生します。
- ゲーム画面の 下部中央に、横長のボスHPバー が表示されていることを確認します。
useFullWidthを ON にすると、画面幅いっぱいに伸びたバーが表示されます(左右マージンで内側に寄せることも可能)。
6. HPの変化をテストする(簡易テスト用スクリプト)
BossHealthBar 自体は完全に独立しており、外部からメソッドを呼べばHPが変化します。
動作確認のため、簡単なテストスクリプトを別途作って、キー入力でダメージ/回復させてみましょう。
- Assets パネルで右クリック → Create → TypeScript で
BossHealthBarTester.tsを作成します。 - 以下のような最低限のテストコードを貼り付けます(任意):
import { _decorator, Component, KeyCode, input, Input } from 'cc';
import { BossHealthBar } from './BossHealthBar';
const { ccclass, property } = _decorator;
@ccclass('BossHealthBarTester')
export class BossHealthBarTester extends Component {
@property({ type: BossHealthBar, tooltip: '操作対象の BossHealthBar コンポーネント' })
bossHealthBar: BossHealthBar | null = null;
onLoad() {
input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
}
onDestroy() {
input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
}
private onKeyDown(event: any) {
if (!this.bossHealthBar) return;
switch (event.keyCode) {
case KeyCode.KEY_Z:
// Z キーでダメージ
this.bossHealthBar.damage(500);
break;
case KeyCode.KEY_X:
// X キーで回復
this.bossHealthBar.heal(500);
break;
case KeyCode.KEY_C:
// C キーでリセット
this.bossHealthBar.reset();
break;
}
}
}
テスト手順:
- Hierarchy で Canvas を右クリック → Create → Empty Node で
BossHealthBarTesterRootを作成。 - Inspector の Add Component → Custom → BossHealthBarTester を追加。
- BossHealthBarTester の
bossHealthBarプロパティに、先ほど作成した BossHealthBarRoot ノードをドラッグ&ドロップして割り当て。 - ゲームを再生し、キーボードで以下を試す:
- Z キー:ダメージ(HPが減り、赤い遅延バーが追従してくる)
- X キー:回復(HPが増え、バーが伸びる)
- C キー:リセット(最大HPに戻る)
HP が 0 になるまで Z キーを連打すると、バーが空になったタイミングでフェードアウトし、非表示になることを確認できます。
まとめ
この BossHealthBar コンポーネントは、
- Canvas配下の任意のノードにアタッチするだけで 画面下部に固定表示されるボスHPバーを構築し、
- 最大HP・現在HP・見た目・アニメーション・フェードアウトを すべてインスペクタだけで調整 でき、
- HPの更新は
damage()/heal()/setCurrentHealth()などの シンプルな公開メソッド で完結します。
外部の GameManager や Boss スクリプトに依存しないため、
- どのプロジェクトにも そのままコピペで持ち込める
- ボスごとに HP バーの見た目やサイズを変えたい場合も、Prefab 化して複製 するだけ
- UI デザイナーとプログラマが インスペクタ経由で役割分担 しやすい
といった利点があります。
このコンポーネントをベースに、
- 残りHP割合で色を変える(緑→黄→赤)
- ボス名テキストやアイコンを追加する
- HP変化時に揺れアニメーションを追加する
などを拡張していけば、ボス戦用UI一式を「1ノード+1スクリプト」で完結させることも可能です。
まずはこの記事のコードをそのまま試し、動作を確認しながら、自分のゲーム用にカスタマイズしてみてください。




