【Unreal Engine】UE初心者の総仕上げ:コイン集めゲームを1本完成させる

作成: 2026-07-21最終更新: 2026-09-05

60秒で20枚のコインを集めるゲームを、Blueprintで作る総仕上げ。コインの取得、スコアと時間、HUD、結果とリトライをつなぎ、ハイスコア保存と演出を足して、Windowsで遊べる形にします。

キャラクターを動かす、触れたものを消す、画面に数字を出す。個別には作れるようになっても、それをつないでゲームを完成させるところで手が止まることがあります。

今回は、60秒で20枚のコインを集めるゲーム を作ります。まず「集める→結果を見る→もう一度遊ぶ」を一周できる形にし、その後でハイスコアと演出を足していきます。

新しい機能をたくさん覚えることより、すでに触れた部品の役割を決め、順番につなぐことが主題です。Blueprintの変数やBranchを使ったことがある人を想定しています。Branchは、条件が成り立つ場合はTrue、そうでない場合はFalseへ処理を分けるノードです。

コインが並ぶステージを走り、HUDのコイン数と残り時間を見ながら集めるゲームの完成イメージ

この記事でわかること

  • コイン・ゲームの状況・画面表示を分けて作る考え方
  • 得点とタイマーをHUDへ伝える方法
  • クリア、時間切れ、リトライ、記録の保存をつなぐ手順
  • 音やエフェクトを加え、配布できる形まで仕上げる流れ

Sponsored

完成の条件と、必要な部品を決める

最初に、何ができれば完成かを1行で決めます。

制限時間60秒のあいだに、1ステージに置いた20枚のコインを全部集めたらクリア。時間切れならゲームオーバー。

敵や複数のステージは、今回は加えません。この条件を先に決めると、必要な部品を順に揃えやすくなります。

60秒で20枚を集める完成条件から、ステージ・コイン・スコア・タイマー・HUD・結果画面を洗い出す
部品今回の担当
キャラクター、カメラ、移動入力Third Personテンプレートを使う
コインBP_Coin。触れたことを知らせ、消える
スコア、残り時間、終了判定BP_CoinGameState
数字、結果、リトライボタンWBP_HUD
前回までのハイスコアBP_CoinSave

新しく作るアセットはこの4つです。加えて、テンプレートのGameModeを編集し、GameStateの指定とHUDの表示を行います。1人で遊ぶゲーム として組み立てるため、オンライン対戦の同期は扱いません。

コインが取得を伝え、GameStateが得点と時間を管理し、HUDが通知を受けて表示する。SaveGameは記録をファイルへ残す

ステージとBlueprintを用意する

「Games → Third Person」でBlueprintプロジェクトを作ります。Variantを選べる版では「None」にします。Starter Contentは必須ではありません。

テンプレートのレベルを L_CoinStage という名前で保存します。キャラクターを動かせる床とPlayer Startを残し、段差を2〜3個置けば、最初のステージとして十分です。Playして移動とジャンプを確認しておきます(→ Modeling Modeで形を試す)。

Content/CoinGame フォルダを作り、先に次のアセットを用意します。中身はこのあと順に作るので、まず名前と種類を揃えます。

名前作成する種類・親クラス
BP_CoinBlueprint Class → Actor
BP_CoinGameStateBlueprint Class → All ClassesでGameStateBaseを検索
WBP_HUDUser Interface → Widget Blueprint → User Widget
BP_CoinSaveBlueprint Class → All ClassesでSaveGameを検索

テンプレートの BP_ThirdPersonGameMode を開き、「Class Defaults → Game State Class」をBP_CoinGameStateへ変えます。Default Pawn Classなど、キャラクターの設定はそのまま使います。

この先は、各節の処理を組んだらBlueprintをCompileして保存し、Playで結果を確かめます。参照先の変数や関数が候補に出ない場合は、先にそのBlueprintをCompileしたかも確認してください。

レベルの「World Settings → GameMode Override」に、このGameModeを指定します。ここがNoneの場合は「Project Settings → Maps & Modes → Default GameMode」が使われます。いま開いているレベルで、どのGameModeが使われるか を確認するのが要点です。

Content/CoinGameへ先に作る4つのアセットと、GameModeとWorld Settingsのつなぎ方

GameStateでスコアと時間を管理する

GameState は、そのゲームでいま何が起きているかを置く場所です。今回は「何枚取ったか」「あと何秒か」「もう終了したか」をまとめます。コインは加点を頼み、HUDは数字を表示する。両方が同じ場所を参照するので、表示と実際の得点が食い違いにくくなります。

小さなゲームではプレイヤーにスコアを置く作り方もできます。今回は部品の役割を分ける練習としてGameStateを使います(→ Game Framework入門)。

値と、変化を知らせる通知を用意する

BP_CoinGameStateに次の変数を作り、Compile後に既定値を設定します。Integerは整数、Booleanはtrue/falseの2つの値を扱う型です。

変数既定値用途
ScoreInteger0取った枚数
TargetCoinsInteger20クリアに必要な枚数
TimeRemainingInteger60残り秒数
bFinishedBooleanfalse終了済みか
BestScoreInteger0結果画面へ出す最高記録
CountdownHandleTimer Handle未設定このタイマーを後で止めるための識別情報

続いて「My Blueprint → Event Dispatchers」の「+」で、次の3つを作ります。

Dispatcher入力名と型知らせること
OnScoreChangedNewScore:Integerスコアがこの値へ変わった
OnTimeChangedNewTime:Integer残り時間がこの値へ変わった
OnGameFinishedbCleared:Boolean終了した。trueならクリア

Event Dispatcher は、変化があったときに、登録された処理を呼ぶ仕組みです。GameState側で通知を呼び、後でHUD側に「通知されたら表示を書き換える」と登録します。作っただけでHUDへ自動接続されるわけではありません。

終了処理を先に作る

「Functions」の「+」から関数 FinishGame を作り、入力にBooleanの bCleared を追加します。関数は、ひとまとまりの処理を名前で呼び出せる形にしたものです。

次は実行順を読むための表記です。条件 はBranchのCondition、値を入れる は変数のSetノードに対応します。

FinishGame(入力:bCleared)
  条件:bFinished
    True  → そのまま終了
    False → bFinishedへtrueを入れる
          → Clear and Invalidate Timer by Handle
             Handle = CountdownHandle
          → Call OnGameFinished(bCleared = 関数の入力)

bFinishedを先にtrueへ変えると、後からもう一度この関数が呼ばれても、終了通知を繰り返しません。まだタイマーを作っていない段階では、未設定のHandleを渡しても止める対象がないだけです。

FinishGameの入口でbFinishedを確認する。TrueならReturnで終え、FalseならbFinishedをtrueにしてタイマー停止と通知へ進む

コインを1枚数える

関数 AddCoin を作り、出力にBooleanの Accepted を追加します。Acceptedは「この1枚を数えたか」という返事です。コイン側はこの返事を受け取ってから、自分を消します。

AddCoin(出力:Accepted)
  条件:bFinished
    True  → Return(Accepted = false)
    False → Scoreへ「Score + 1」を入れる
          → Call OnScoreChanged(NewScore = Score)
          → 条件:Score >= TargetCoins
               True  → FinishGame(bCleared = true)
                     → Return(Accepted = true)
               False → Return(Accepted = true)

Return Nodeは、関数を終えて結果を呼び出し元へ返すノードです。終了済みの場合だけfalse、数えた場合はクリアしたかどうかにかかわらずtrueを返します。2つのtrue側には、それぞれReturn Nodeを置いて構いません。

Get Scoreに1を足してSet Scoreへ保存し、その値をOnScoreChangedのNewScoreへ渡す。通知後にクリア条件を判定する

図ではSet Score右側の値の出力を使い、保存した値を通知へ渡しています。通知の後にGet Scoreで現在値を読み、TargetCoinsと比べます。

1秒ごとに残り時間を減らす

Event GraphにCustom Eventの StartCountdownTick1Second を作ります。Custom Eventは、自分で名前を付けて呼べる処理の入口です。

StartCountdownの白い実行線を Set Timer by Event へつなぎ、Timeを1.0、Loopingをオンにします。Tick1Secondの赤いデリゲート出力を、タイマーノードの赤いEvent入力へつなぎます。これは「1秒たつたび、このイベントを呼ぶ」という登録です。Tick1Secondの白い実行出力は、下の減算処理へ使います。

Set Timer by EventのReturn Valueを Set CountdownHandle へ渡し、白い実行線もSetへつなぎます。Timer Handle を保存するのは、FinishGameから同じタイマーを指定して止めるためです。

StartCountdownで1秒間隔のタイマーを登録し、Tick1SecondのデリゲートをEventへ、タイマーの戻り値をCountdownHandleへつなぐ
Tick1Second
  条件:bFinished
    True  → 終了
    False → TimeRemainingへ Max(TimeRemaining - 1, 0)を入れる
          → Call OnTimeChanged(NewTime = TimeRemaining)
          → 条件:TimeRemaining <= 0
               True → FinishGame(bCleared = false)

Maxは2つの値の大きいほうを選ぶので、残り時間がマイナスになりません。タイマーは、後でHUDを表示した後にStartCountdownを呼んで始めます。ここではGameStateのBeginPlayからは開始しません。

残り時間から1を引き、Maxで0以上に収めてSet TimeRemainingへ保存する。その値をOnTimeChangedのNewTimeへ渡す

図のSetノード右側にある値の出力は、保存した値を次へ渡すために使えます。減算の結果を保存してから通知し、最後の条件判定では現在のTimeRemainingをGetで読みます。

Sponsored

触れると取れるコインを作る

BP_Coinを開き、Componentsに次を追加します。

部品設定
Sphere CollisionDefaultSceneRootへドラッグしてルートにする。Sphere Radiusは60
Static MeshSphereの子にする。Cylinderを割り当て、Scaleは (1, 1, 0.1)、RotationのXは90、Collision PresetsはNoCollision
Rotating MovementRotation RateのZを90にする

CylinderはUEの基本形状を使えます。メッシュの選択欄で見つからなければ「Show Engine Content」をオンにし、Engine/BasicShapes/Cylinder を探します。回る速さは1秒あたり90度です。

SphereのCollision PresetsをCustomにし、Collision Enabledは「Query Only」、各チャンネルへの応答はIgnore、PawnだけOverlap にします。Generate Overlap Eventsもオンにします。プレイヤーのCapsule Component側でも、Generate Overlap Eventsがオンかを確認してください。

Overlap は、2つの当たり判定が重なったことを知らせる仕組みです。薄いコインの見た目は衝突させず、少し大きい球で取得を判定すると、横から触れても取りやすくなります。

プレイヤーが触れた1回だけ数える

Sphereを選び、Detailsの「On Component Begin Overlap」の「+」からイベントを作ります。

  1. Cast To BP_ThirdPersonCharacter を置き、ObjectへOverlapのOther Actorをつなぎます。白い実行線もつなぎます。
  2. 成功側から Cast To BP_CoinGameState へ進め、Objectには Get Game State のReturn Valueを渡します。
  3. 成功側に DoOnce を挟み、そこから AddCoin を呼びます。AddCoinのTargetは、Castの As BP Coin Game State です。
  4. AddCoinのAcceptedをBranchのConditionへつなぎ、True側から Destroy Actor(TargetはSelf)を呼びます。False側は何もしません。

Cast は、取得した相手を指定の種類として扱えるかを確かめます。最初のCastでプレイヤーを、次のCastで自作のGameStateを確認しています。失敗側は加点へ進めません。

OverlapのOther Actorをプレイヤーとして確認し、Get Game Stateの戻り値を自作GameStateとして確認する2つのCast

2つの確認を通ってから、加算と後始末へ進みます。

GameStateのCast成功後、DoOnceからAddCoinを呼び、Acceptedがtrueの場合だけコイン自身をDestroy Actorで消す

DoOnce は、最初に通った1回だけ先へ進めるノードです。1枚のコインへ複数のOverlapが届いても、加点を繰り返さないようにします。今回はコインを取り直さないのでResetはつなぎません。

まず1枚だけレベルへ置き、Playして触れます。コインが消えれば、Overlap→GameStateへの加点→取得成功の返事が通っています。Playを止めるとレベル上のコインは元へ戻ります。ここまで動いたら、Alt を押しながらドラッグして20枚に増やします。

HUDに数字を出す

HUD は、遊びながら見るスコアや残り時間の表示です。WBP_HUDのDesignerにCanvas Panelをルートとして置き、その中へ次の部品を並べます。

HUDの左上にスコア、上中央に時間、中央に結果とリトライボタンを置く。結果用の部品はPanel_Resultの中へまとめる
部品名種類配置・初期設定
Text_ScoreText Block左上。初期文字はCOINS 0 / 20
Text_TimeText Block上中央。初期文字はTIME 60
Panel_ResultVertical Box画面中央。VisibilityはCollapsed
Text_ResultText BlockPanel_Resultの子。結果表示用
Text_BestScoreText BlockPanel_Resultの子。記録表示用
Text_SaveStatusText BlockPanel_Resultの子。初期文字は空
Btn_RetryButtonPanel_Resultの子。その中にRETRYというText Blockを置く

Graphから指定するこれらの部品は「Is Variable」をオンにします。Btn_Retryの「Is Focusable」もオンにします。Canvasの子のアンカーを配置に合わせ、Panel_Resultは画面中央のアンカーとAlignment (0.5, 0.5)、Position (0, 0) にすると中央へ揃えられます。文字が切れないサイズへ調整してください(→ UMG入門)。

通知と、表示を変える処理を結ぶ

WBP_HUDに、BP_CoinGameStateのObject Reference型で GameStateRef 変数を作ります。参照 は、後で同じGameStateを指定するために持っておくものです。Class Referenceではありません。

Event Constructから、次の順で接続します。

  1. Get Game StateのReturn ValueをObjectにして、Cast To BP_CoinGameStateを実行する。
  2. 成功側でAs BP Coin Game Stateを Set GameStateRef へ入れる。
  3. GameStateRefから Bind Event to OnScoreChangedBind Event to OnTimeChangedBind Event to OnGameFinished を作り、白い実行線をこの順でつなぐ。3つともTargetはGameStateRef。
  4. 各Bindの赤いEventピンからドラッグし、対応するCustom Eventを作る。名前を順に UpdateScoreUpdateTimeShowResult にする。

Bind は「この通知が来たら、この処理を呼んで」と登録する操作です。赤い線は通知先の登録、白い線は処理の実行順を表します。Bindの白い出力をCustom Eventの入口へつなぐ形ではありません。

GameStateRefをBindのTargetへ、UpdateTimeの赤いデリゲート出力をEventへつなぎ、残り時間の通知先を登録する

UpdateScoreとUpdateTimeの中身を作ります。Text BlockをGetで置き、そこから Set Text (Text) を作ると、表示先のTargetを指定できます。

イベントSet TextのTargetIn Textへ渡すFormat Text
UpdateScore(NewScore:Integer)Text_ScoreCOINS {Score} / {Target}。ScoreへNewScore、TargetへGameStateRefのTargetCoins
UpdateTime(NewTime:Integer)Text_TimeTIME {Seconds}。SecondsへNewTime

Format Textは、文中の {名前} を指定した値で埋めるノードです。数字をそのままText Blockへ渡すのでなく、表示したい文章にしてから渡します。

UpdateTimeで届いたNewTimeをFormat TextのSecondsへ入れ、できたTIMEの文章をText_TimeのSet Textへ渡す

Constructの3つ目のBindの後に、UpdateScoreとUpdateTimeの呼び出しも置き、GameStateRefの現在のScoreとTimeRemainingを渡します。通知を待つだけでは最初の値が届かないため、初期表示は自分から一度行います。 ShowResultの中身は次の節で作ります。

HUDを表示してから、時計を動かす

BP_ThirdPersonGameModeのEvent BeginPlayへ、次を組みます。テンプレートに既存処理がある場合は、それにつなげて追加します。

Create Widget(Class = WBP_HUD、Owning Player = Get Player Controller 0)
  → Add to Viewport(Target = Create WidgetのReturn Value)
  → Cast To BP_CoinGameState(Object = Get Game StateのReturn Value)
  → StartCountdown(Target = As BP Coin Game State)

このHUDは1プレイにつき1つだけ作り、終了画面も同じWidgetの中で表示します。Playして、最初に「COINS 0 / 20」「TIME 60」が出るか、1秒ごとに時間が減るか、コイン1枚につき1点増えるかを確認します。今の段階では、終了時の結果画面はまだ出ません。

結果を表示して、もう一度遊べるようにする

WBP_HUDのShowResultへ処理を追加します。入力のbClearedがtrueならクリア、falseなら時間切れです。

  1. bClearedを条件にした Select で、true側は CLEAR!、false側は TIME UP のTextを選び、Text_ResultへSet Textする。
  2. Panel_Resultを Set Visibility でVisibleにする。
  3. Get Player Character (0) からCharacter Movementを取得し、それをTargetに Stop Movement Immediately、続いて Disable Movement を実行する。
  4. Get Owning Player をTargetに、Show Mouse Cursorをtrueにする。
  5. Set Input Mode UI Only を実行する。Player ControllerはGet Owning Player、In Widget to FocusはBtn_Retry、Flush Inputを選べる場合はオン。

移動の停止と、操作の切り替えは別の仕事です。前者で走っていたキャラクターを止め、後者でボタンを操作できる状態にします。ゲーム全体にはポーズを掛けません。 後で足すコインの光や音を、結果画面が出た後も再生するためです。得点はbFinishedで、時計はタイマーの停止で、それぞれ止まっています。

リトライボタンをつなぐ

Btn_RetryのOn Clickedを作り、次の順で組みます。

Set Input Mode Game Only(Player Controller = Get Owning Player)
  → Set Show Mouse Cursor(Target = Get Owning Player、false)
  → Remove from Parent(Target = Self)
  → Open Level (by Name)
     Level Name = Get Current Level NameのReturn Value

Get Current Level NameのRemove Prefix Stringはオンにします。エディタ内プレイ用に付いた接頭辞を除き、保存したレベル名で開き直すためです。

Open LevelではキャラクターやGameStateが作り直されるので、移動できるキャラクター、20枚のコイン、60秒の時計から再開します。画面切り替えの前に入力をゲームへ戻すところまでが、リトライの処理です(→ Open Level入門)。

終了時は得点・時計・移動を止めてUIを操作する。リトライ時は入力をゲームへ戻し、同じレベルを開き直して初期状態から始める

ここで一度、取らずに60秒待つ→TIME UP→RETRY→20枚取る→CLEAR! を試します。クリアしづらければ、確認中だけコインを近くへ並べて構いません。結果が出た後にスコアと時間が変わらず、リトライで初期状態へ戻れば、ゲームの基本形は完成です。

Sponsored

ハイスコアを次のプレイへ残す

GameStateの値はレベルを開き直すと消えます。最高記録を次のプレイへ残すために、SaveGame を使います。保存したい値を入れる器を作り、その中身をファイルへ書く仕組みです。

BP_CoinSaveへIntegerの BestScore を追加し、既定値を0にします。BP_CoinGameStateには次を追加します。

変数既定値
SaveObjectBP_CoinSaveのObject Reference未設定
SaveStatusText

保存データを用意する

BP_CoinGameStateに関数 SaveHighScoreWriteBestScore を作ります。2つに分けるのは、「既存データを読む/初めて作る」の分岐の後を、同じ処理へつなぐためです。

SaveHighScoreの先頭でBestScoreへScoreを入れ、SaveStatusを空にします。次に Does Save Game Exist の結果でBranchを分けます。Slot Nameは CoinHighScore、User Indexは0で統一します。スロット名は、どの保存ファイルを読み書きするかを指定する名前です。

保存ファイル実行する処理
ない(False)Create Save Game Object(ClassはBP_CoinSave)→ Return ValueをSet SaveObjectへ入れる → WriteBestScore
ある(True)Load Game from Slot → Return ValueをCast To BP_CoinSaveのObjectへ → 成功したらAs BP Coin SaveをSet SaveObjectへ入れる → WriteBestScore
読み込み側のCastが失敗SaveStatusへ「記録を読み込めませんでした」を入れて関数を終える。保存へ進めない

2つの正常な枝には、それぞれSet SaveObjectとWriteBestScoreの呼び出しを置きます。どちらから来ても、同じ変数に入った保存データを使う形です。Load Game from Slotにも、同じスロット名とUser Indexを渡してください。

今回と以前の記録を比べて書く

WriteBestScoreでは、最初にSaveObjectを Is Valid で確認します。無効ならSaveStatusへ「記録を保存できませんでした」と入れて終えます。有効なら、次の順です。

  1. GameStateのBestScoreへ Max(Score, SaveObjectのBestScore) を入れる。
  2. SaveObjectをTargetにした Set BestScore で、その値を保存データにも写す。
  3. Save Game to Slot を呼ぶ。Save Game ObjectはSaveObject、Slot NameはCoinHighScore、User Indexは0。
  4. Return Valueがfalseなら、SaveStatusへ「記録を保存できませんでした」と入れる。

同じBestScoreという名前でも、GameState側は画面に出す値、SaveObject側はファイルへ書く値 です。SetノードのTargetを区別します。例えば以前12枚、今回8枚なら12を残し、今回15枚なら15へ更新します。

以前の記録と今回のスコアを比べ、大きいほうを保存する。以前12枚なら今回8枚では12を維持し、15枚なら15へ更新する

記録を決めたら、ファイルへ書き出します。

SaveObjectをSave Game to Slotへ渡し、Return ValueをBranchで確認する。Falseなら保存できなかったことを画面へ知らせる

保存に失敗しても結果画面は出します。その場合、表示中のBESTが次回にも残ったとは限らないため、SaveStatusで知らせます。読み込めない既存ファイルを、新しい空の記録で上書きする処理にはしません。

終了処理と表示へ組み込む

FinishGameの「タイマーを止める」と「Call OnGameFinished」の間へ、SaveHighScoreを挟みます。WBP_HUDのShowResultでは、Panel_Resultを表示する前に次を足します。

  • Text_BestScoreへ、GameStateRefのBestScoreを Format TextBEST {Score} に入れて表示する。
  • Text_SaveStatusへ、GameStateRefのSaveStatusを表示する。

保存の処理を終えてから通知するので、HUDはその結果を読むだけで済みます。まず3枚取って時間切れ、リトライして1枚で時間切れ、次は5枚で時間切れ、と試すとBESTが3→3→5になるか確認できます。確認中だけTimeRemainingの既定値を10へ変え、終わったら60へ戻します。

すでに高い記録を保存している場合は、その値が残るのが正しい動作です。3→3→5の順を試すなら、読み書きすべてのSlot Nameを練習用の別名へ揃えてから確認します。

さらにPlayを止めて再び始め、記録が残ることも確かめます。セーブとロードを詳しく作り込みたい場合は、Save/Load入門を参照してください。

実践:音・光・残り時間の警告を足す

ゲームが一周できたら、同じルールのまま反応を分かりやすくします。触れた瞬間の音、コインの位置から散る光、残り10秒の点滅を加えます。1つ加えるたびにPlayし、変化を確かめてください。

コインを消すだけの状態に、取得位置の光と音、赤く点滅するTIME 8を加えた完成イメージ

① 取った音を鳴らす

短い効果音をSound Waveなどで用意します。BP_Coinの「AcceptedのTrue」とDestroy Actorの間へ、Play Sound at Location を入れます。Soundに用意した音、LocationにSelfの Get Actor Location を渡します。

このノードは、指定した場所で音を再生します。直後にコインを消しても、音をコインへ付けたまま一緒に消す構成にはなりません。

慣れたら、Pitch Multiplierへ次の値を渡すと、取るたびに音が少しずつ高くなります。Floatは小数を扱う型で、この計算は小数として行います。

Clamp(1.0 + (GameStateのScore - 1) × 0.03, 1.0, 1.6)

コインのCastで得たGameStateからScoreを読みます。AddCoinの後なので、1枚目はScoreが1、音の倍率は1.0です。2枚目は1.03、20枚目は1.57になります。Clampは指定範囲へ値を収めるノードです。Pitch Multiplierが見えない場合は、ノード下端の矢印で追加ピンを展開します。

② コインの場所から光を散らす

1回弾けて終わるNiagara Systemを用意し、NS_CoinPickup とします(→ Niagara入門)。ループし続けるものではなく、短い取得演出を使います。

BP_CoinのDestroy Actorの前に Spawn System at Location を入れます。System TemplateはNS_CoinPickup、LocationはSelfのGet Actor Location、Auto ActivateとAuto Destroyはオンにします。

今回はSphereとメッシュの中心を揃えているので、その位置から出します。Niagara Systemと、旧形式のCascade Particle Systemは別のアセットです。Spawn System at Locationへ渡すのはNiagara側です。

終了時にゲーム全体を止めていないため、20枚目でも光を最後まで再生できます。結果画面が出た瞬間だけ演出が止まる場合は、追加した処理にSet Game Pausedが入っていないかを確認します。

③ 残り10秒で時間を点滅させる

WBP_HUDのDesignerで Anim_Warning というアニメーションを作ります。Text_TimeのColor and Opacityに、0秒で白、0.25秒で赤、0.5秒で白のキーを置きます。文字の不透明度は1のままにします。

UpdateTimeのSet Textの後に、次の条件をANDでまとめたBranchを置きます。

  • NewTimeが10以下。
  • NewTimeが0より大きい。
  • Is Animation Playing(In AnimationはAnim_Warning)がfalse。

True側で Play Animation を呼び、TargetはSelf、In AnimationはAnim_Warning、Num Loops to Playは0にします。この0は無限ループの指定 です。すでに再生中なら呼び直さないので、1秒ごとに先頭へ戻らず点滅を続けられます。

ShowResultの先頭には Stop Animation(Anim_Warning)を加えます。これで結果画面になった後の点滅も止まります。

11秒以上は通常表示、残り1〜10秒は白・赤・白のアニメーションを繰り返し、結果表示で停止する

最後に、残り10秒まで待って点滅を見る、コインを取って音と光を見る、20枚目でも演出が出る、リトライで警告のないTIME 60へ戻る、の順に確かめます。

Sponsored

Windowsで遊べる形にして確かめる

「Project Settings → Maps & Modes → Game Default Map」をL_CoinStageにし、パッケージへ含めるマップにも指定します。Windows向けにまずDevelopmentでパッケージ化し、出力先のexeから遊びます。手順はパッケージ化入門にまとめています。

Game Default Mapと含めるマップを指定し、Developmentでパッケージしてexeから遊ぶ
確認すること期待する結果
exeを起動するコインのステージ、COINS 0 / 20、TIME 60が出る
コインを1枚取る1点だけ増え、そのコインだけ消える
時間切れ/20枚取得TIME UP/CLEAR!が出て、移動・得点・時計が止まる
RETRYを押す移動とジャンプができ、20枚・60秒から再開する
アプリを閉じて開く以前のハイスコアが次の結果画面にも反映される

配布用をShippingへ変えた場合は、その出力でも同じ確認をします。渡すのはexe単体ではなく、出力フォルダ全体 をまとめたZIPです。自分でも展開したフォルダから起動してみると、渡す形で遊べるかを確認できます。

おまけ:遊びながら調整する

19枚しか置いていないとクリアできない

TargetCoinsは必要な枚数で、配置数を自動で数える変数ではありません。OutlinerでBP_Coinを確認し、20枚揃っているかを見ます。枚数を変えるときは、配置数とTargetCoinsを合わせます。

次の工夫は、配置だけでも試せる

同じ20枚でも、一直線に並べる、段差の上へ置く、遠回りするとまとめて取れる場所を作る、と配置を変えるだけで遊び方が変わります。まず自分が60秒で届くかを試し、次に誰かに遊んでもらうと、作った本人には気付けない取りにくさが見えてきます。

追加ステージへ進めたくなったら、レベル遷移GameInstanceで値を持ち越す方法が次の題材になります。

まとめ

コインは取得を知らせ、GameStateは得点と時間を管理し、HUDは通知を受けて表示する。この役割分担で、コイン集めから結果、リトライまでをつなぎました。SaveGameを加えると、前回の記録も次のプレイへ残せます。

小さなゲームでも、最後まで遊べる形にすると、部品をどうつなぐかが具体的に見えてきます。ここからは配置や制限時間を変え、自分がもう一度遊びたくなる調整を試してみてください。

参考リンク

Unreal Engine このセクションのノート98