0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Fortnite / UEFN] NPC behavior の実装; レベルに配置したプロップを順ぐり巡る(たまに跳ねる)

Last updated at Posted at 2025-09-08

この記事では UEFN (Unreal Editor for Fortnite) という無料の3Dゲーム開発環境を使って、NPCの振る舞いをコーディングするサンプルをご紹介いたします。なんか UEFNのコミュニティに書いたほうがいいような気もするけれども

今回は NPCが目標地点に移動しつつ、進行方向ベースでジャンプ演出をランダムで繰り返す NPC behavior を実装します。

test2.gif

★この NPC Behavior のポイント

  • 配列に任意数の経路を定義(歩く/走る/ダッシュの3挙動IDE選択可能)
  • 移動・ジャンプは並列処理でお互い邪魔しない挙動
  • ジャンプ演出は三角計算で現実?っぽい放物線
  • ジャンプ頻度、ジャンプ距離を用途に応じて調整可
  • 移動スピード調整可能
  • 旗はクマでも動きます(?)

この記事について

この記事のライターは趣味で子供に遊ばせるような簡単なゲームを作る大人のソフトウェアエンジニアです。興味があって取り組んでいる読者であれば内容の理解に壁はないものと思います。ただ完全に再現するには技術的な背景がいくらか必要です。

記事は 1.開発環境の概要、2.実装内容、3.サンプルコード で構成しています

UEFN (Unreal Editor for Fortnite)

UEFN は Epic Games, Inc. 社製のゲームエンジン Unreal Engine 向けのマップエディターつき統合環境です。カジュアルなプラットフォームを目指しておなじくプレイ無料のバトルロイヤルゲーム Fortnite と機能統合されており、本格的なプロツールよりいくぶん扱い易いものになっています

しかしながら内実は本物の Unreal Engine そのもので、性能は劣らず、機能的なシンラッパーとして実装されており、現在も発展を続けています。

※ Unreal Editor for Fortnite
UEFNエディター画像

開発はエディターと verse言語でおこない、テストプレイはふつうの Fortniteゲームクライアントをエディターが制御して起動・実行します

私が使っている動作バージョンは Windows PC版です。実行のためにそれほど高価なPCはいりません。ただすくなくとも Fornite がむりなく動く程度のスペックは必要です

※ 参考スペック;このくらいあれば簡単には使えます

ハードウェア 性能
CPU i5-10400 2GHz 6コア おさがりのビジネス用スリムPC
GPU NVIDIA GeForce RTX 3050 6GB LP (ロープロファイル。スリムPCに差せる細いやつ)
メモリー 16GB
モニター 25インチ~。144Hz 出るやつ1枚。+補助モニター1枚あればツールダイアログを主モニターから追い出せるので便利

ゲーム開発を始めるに際し、開発元 Epic Games, Inc. 社へ本人確認と納税者登録が必要です。だからごめん、ある程度大人にやってもらわないとだめかも。確定申告した経験くらいがあればなんとかできます。ひきかえにアカウントを発行してもらいます

掲載・収益化には別の登録も必要ですが、それはやってないのでよく調べていません。登録・設定系は本やネット記事に丁寧なものがみつかります

verse言語

verseはUnreal Editor でゲームをプログラムするための独自言語です。エディター同梱の専用プラグイン入り VSCode を起動してコーディングします

※ エディター同梱VSCode まぁこれは普通にVSCodeです。ふつうのVimキーバインドプラグインをいれて使っています(つまり普通のプラグインも使えるっちゃ使える)(いつかIntelliJでエディットできるようになるとうれしいのだけど)

エディター同梱VSCode画像

verseはクラスや非同期、例外といったモダンな概念を持っています。よもやゲームエンジンなので基本的には 60 fps といった速度のゲームループと非同期処理のかたまりなのです

あとひとつ覚えること、それは python ライクにインデントでコードブロックを表現します。たとえば、

if (IsLogic = true):
    #ここから if文の処理内容を書いたコードブロック
    set A = 0

# ここから別のブロック
set A = 1

とかなんとか詳しいことは AIチャットの Epic Developer Assistant にお尋ねください。
https://dev.epicgames.com/community/assistant/fortnite

Epic Assistant、すごく丁寧に教えてくれます。たまに間違うこともあるので AIチャットを使うときの裏取り練習にもなります。じつのところ verseはまだ生まれたての環境なので、API が行き届かないこともままあります。そのため開拓者精神が重要になってきます。チュートリアルとリファレンスもおなじURLの周辺にインデックスされています

スクリーンショット 2025-09-08 212315.png

NPC behavior

UEFNでマップのことは「レベル」と呼びます(慣れます)

レベルの座標系はエディター上で「Up-Forward-Left」です。ただこれは最近の仕様変更でそれに替わりました。ながらく「X-Y-Z」だったので、スクリプトのAPI はベクトルを XYZ で指示することがまだ多いです(慣れます)

レベル上にプレイヤーじゃないキャラクター Non Player Character / NPC を配置したいときは NPC Spawner を使います。レベル上に置いてなにかのゲーム機能を負わせる部品のことを「デバイス」と呼びます(慣れます)

デバイス NPC Spawnerを配置した図

余談; そこに気持ち悪いフィッシュがいますね。NPC Spawner は外見を後付けすることもできます。UEFNのすごいところはこういった再利用できる画像(アセット、プロップ)を大量に無料開放しているところです。プロップはエディターウィンドウのコンテンツブラウザから探せます。見ているだけで時間をつぶせます。通常のUEのように自分でつくったSTLをメッシュにインポート等ももちろんできます

で、この NPC Spawner でスポーン(出現)させる NPC には振る舞いをコーディング付加できます。その振る舞いを表現するカスタムクラスの基底クラスが npc_behavior です。ようやくプログラミングっぽくなりましたね。私は今回その派生クラスassault_target_npc_behaviorクラスを作成しました

assault_target_npc_behavior := class(npc_behavior):

ここに天から指示を与えるごとく操り人形 NPC behavior を操るコードを実装します

自分のverseコードがコンパイルできると、これもコンテンツブラウザから参照できるようになります。NPC Spawnerデバイスの Details で NPC Bebehavior Script Override へ設定することができるようになります

デバイスに振る舞いクラスを設定する図

レベル上のアセットを NPC behavior のプロパティとして参照する

今回はレベル上に旗を立てておいて、NPCにその間を行ったり来たり、歩いたり走ったりしてもらいます。旗はcreative_propクラスのインスタンスです。振る舞いクラスに参照を追加します

レベル上に建てた旗

実装は簡単で自分のカスタムクラスにアノーテーションつきのプロパティを定義するだけです。エディターのピッカーや値のバリデーション、配列の管理などはIDEがやってくれます

ここでは旗のアセットとそこまでの移動方法をプロパティにもつクラスmovement_targetをつくり、振る舞いクラスにはその配列を定義しました

enum_movement_type := enum:
    Walking
    Running
    Sprinting

movement_target := class:
    @editable MoveTarget :creative_prop = creative_prop{}
    @editable MovementType :enum_movement_type = enum_movement_type.Running

assault_target_npc_behavior := class(npc_behavior):
    @editable MovementTargets :[]movement_target = array{}
 

あとは配列を順番に参照してなんやかやしてから Navigatable.NavigateTo(NavTarget, ~)と呪文を唱えると NPC がそちらに向かって歩き出します。ちょっとスクリプトするだけでなにもかもやってくれるゲームエンジン、ほんとにすごいですね

たまにジャンプする

EPIC Assistant が教えてくれるには、NPCのAI には「ジャンプせよ」という命令が実装されていないそうです。Fortnite 特有の挙動がUEFNに対して公開APIとしてverse実装されていないケースがままあります。そのうち実装されるのかもしれません

対症療法として Assistantが教えてくれることには、 Z 軸を 220 UU (220cm) 上にテレポートさせたらいいよ、ということでした。浮かせてテレポートしたら着地動作はNPCのもとある挙動でめんどうみてくれるそうです。なおNPCが自発的にジャンプすることは(すでに)あるそうです。

いきなり空中に放り出すのも漫画みたいなので、0.01秒づつループしながら、いい感じに放物線っぽくキャラクターを持ち上げてみることにしました。

    Jump(Character: fort_character)<suspends>:void=
        Zs : []float = array { 80.0, 40.0, 40.0, 20.0, 0.0, 0.0, 20.0, 20.0, 0.0, 0.0, 11.0, 10.0, 10.0, 10.0, 0.0, 0.0, 0.0, 0.0 }
        for (OfsZ : Zs):
            if:
                Transform := Character.GetTransform()
                Rotation := Transform.Rotation
                Pos := Transform.Translation
                YawPitchRoll := Rotation.GetYawPitchRollDegrees()
                YawDegrees := YawPitchRoll[0]
            then:
                YawRadians := DegreesToRadians(YawDegrees)
                OfsX := Cos(YawRadians) * DoJumpAdd
                OfsY := Sin(YawRadians) * DoJumpAdd
                JumpPos := vector3{X := Pos.X + OfsX, Y := Pos.Y + OfsY, Z := Pos.Z + OfsZ}
                if (Character.TeleportTo[JumpPos, Rotation]):
                Sleep(0.01)

どうもまだカクカクしますが対症療法なのでよしとします。

サンプルコード

ということで実際のコードです。

このVerseコードは、NPCが目標地点に移動しつつ、進行方向ベースでジャンプ演出をランダムで繰り返す NPC behavior を実装します

Assistant に質問しながら書いた悩みの跡がみえますが、私の趣味マップでは動いています

★ポイント

  • MovementTargets配列に任意数の creative_prop で経路定義
  • 移動・ジャンプを並列処理し、自然なNPC挙動
  • ジャンプ演出はYawの三角関数計算で現実的?な挙動
  • DoJumpRate でジャンプ頻度調整可
  • BaseSpeedMultiplier/MovementType でダッシュ(1.2倍速)の実現

補足:

  • ジャンプ高さやジャンプ追加距離 (DoJumpAdd) はマップ/障害物に応じ調整
  • 旗は creative_propであればクマでも動きます(Hidden in Game False設定でゲーム中隠しても動きます)
using { /Fortnite.com/AI }
using { /Fortnite.com/Characters}
using { /Fortnite.com/Devices}
using { /Verse.org/Simulation }
using { /Verse.org/Random }
using { /UnrealEngine.com/Temporary/SpatialMath}

enum_movement_type := enum:
    Walking
    Running
    Sprinting

movement_target := class:
    @editable MoveTarget :creative_prop = creative_prop{}
    @editable MovementType :enum_movement_type = enum_movement_type.Running

assault_target_npc_behavior := class(npc_behavior):

    @editable MovementTargets :[]movement_target = array{}
    @editable BaseSpeedMultiplier :float = 1.0
    @editable DoJumpRate :float = 0.3
    @editable DoJumpAdd :float = 8.0

    Jump(Character: fort_character)<suspends>:void=
        Zs : []float = array { 80.0, 40.0, 40.0, 20.0, 0.0, 0.0, 20.0, 20.0, 0.0, 0.0, 11.0, 10.0, 10.0, 10.0, 0.0, 0.0, 0.0, 0.0 }
        for (OfsZ : Zs):
            if:
                Transform := Character.GetTransform()
                Rotation := Transform.Rotation
                Pos := Transform.Translation
                YawPitchRoll := Rotation.GetYawPitchRollDegrees()
                YawDegrees := YawPitchRoll[0]
            then:
                YawRadians := DegreesToRadians(YawDegrees)
                OfsX := Cos(YawRadians) * DoJumpAdd
                OfsY := Sin(YawRadians) * DoJumpAdd
                JumpPos := vector3{X := Pos.X + OfsX, Y := Pos.Y + OfsY, Z := Pos.Z + OfsZ}
                if (Character.TeleportTo[JumpPos, Rotation]):
                Sleep(0.01)

    SometimesInstractJumping()<suspends>:void=
        if:
            Agent := GetAgent[]
            Character := Agent.GetFortCharacter[]
        then:
            loop:
                if:
                    Character.IsOnGround[]
                    GetRandomFloat(0.0, 1.0) < DoJumpRate
                then:
                    Jump(Character)
                Sleep(1.0)


    OnBegin<override>()<suspends>:void=
        if (MovementTargets.Length < 1):
            return

        spawn{ SometimesInstractJumping() }

        if:
            Agent := GetAgent[]
            Character := Agent.GetFortCharacter[]
            Navigatable := Character.GetNavigatable[]
        then:
            loop:
                for:
                    MovementTarget :MovementTargets
                    Transform := MovementTarget.MoveTarget.GetTransform()
                    Type := MovementTarget.MovementType
                do:
                    NavTarget := MakeNavigationTarget(Transform.Translation)
                    NavType := case(Type):
                       enum_movement_type.Sprinting => movement_types.Running
                       enum_movement_type.Running => movement_types.Running
                       _ => movement_types.Walking

                    if (Type = enum_movement_type.Sprinting):
                        Navigatable.SetMovementSpeedMultiplier(BaseSpeedMultiplier * 1.2)
                    else:
                        Navigatable.SetMovementSpeedMultiplier(BaseSpeedMultiplier * 1.0)

                    Navigatable.NavigateTo(NavTarget, ?MovementType:=NavType)


実装手順まとめ(UEFN内)

  1. Verseファイル作成

    • エディターメニューで Verse → Verse Explorer
    • プロジェクト名下で右クリック → Create New Verse File
    • → ファイル名 assault_target_npc_behavior
    • Create Empty を押して開く
  2. コード貼付・保存

    • 今のVerseコードをファイルへ貼り付けて保存
  3. Verseコードをビルド

    • UEFNで Verse → Build Verse Code (Ctrl + Shift + B)
    • "Build Succeeded" 表示後、新しい NPC Character Definition を Content Browser で確認
  4. 旗の設置

    • レベルに一つ以上のプロップを任意配置
  5. NPC Character Definition のVerse Behaviorに割り当て

    • レベルに NPC Spawner デバイス配置
    • NPC Spawner をレベル上で選択して Details 表示
    • Behavior Script Override へ assault_target_npc_behavior を設定
    • MovementTargets の各 MoveTarget に進行目標となるプロップを割当
    • ほかパラメータ値は任意で調整
      • BaseSpeedMultiplier, DoJumpRate, DoJumpAdd など
  6. テスト&調整

    • Launch Session で動作検証
      • NPCが目標地点へ進みつつ、たまに進行方向へジャンプ
      • 種々速度で移動

以上です。ご清聴ありがとうございました。

P.S.
なにかを保証はいたしませんが、コードは自由にご利用ください

補足; UEFNメモ レベル内の距離 UU について

  • Unreal Units (UU) が Unreal Engine の基本単位です

  • UE の 1メートルは 100 (UU)です

  • UE / Fortnite の1マスが具体的に何メートルかはグリッド設定によります

  • ただし Fortnite の標準的なグリッドサイズは 512 UU(5.12メートル)です

  • また Fortnite の標準的な壁の高さは 386 UU(3.86メートル)です

  • マップの1マスは 250m です

  • バトルロイヤルにおける射撃距離:

    • 近距離武器(SG): 数メートルから十数メートル
    • 中距離武器(AR): 数十メートルから百メートル程度
    • 遠距離武器(砂): 百メートル以上
  • 各射程をUEFNのマス数(5.12メートル/マスと仮定)に換算すると以下になります

    • 近距離武器(SG)は1~3マス(5m→0.98、15m→2.93)
    • 中距離武器(AR)は 10 ~ 20マス(50m→9.77、100m→19.53)
    • 遠距離武器(砂)は 40マス(200m→39.06)

たぶん。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?