UE4のC++プログラミング入門2(Actorの作成)


初めに

  • 前回は指定子を中心に説明した.今回はActorを作成して,レベルに設置したいと思う.
  • 今回はActorが一定範囲内を行き来するプログラムの作成が目的である.
  • AActorクラスを継承します.AActorのAPIリファレンスを載せます.オールイングリッシュだけど頑張ろう←自分に対して

目次

  • C++でActorを作成
  • Actorに必要な変数やメソッドを宣言
  • Static Mesh Compmentの作成とStatic Meshの設定
  • 作ったActorを上下に行き来するようにする.
  • まとめと実行環境

C++でActorを作成

まずActorを継承したC++を作成する.新規追加から新規のC++クラスを選択.Actorを選択して次へを押す.

その後プライベートを選択して,名前を書く.今回はSampleActorとする.
そしてSampleActor.hを開く.中は下記の通りになっているはずだ.

SampleActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"//AActorのために必要
#include "SampleActor.generated.h"

UCLASS()
class ASampleActor : public AActor
{
    GENERATED_BODY()

public: 
    //コンストラクタ
    ASampleActor();

protected:
    //AActorで定義されているBeginPlay()をオーバーライド
    virtual void BeginPlay() override;

public: 
    //AActorで定義されているTick()をオーバーライド
    virtual void Tick(float DeltaTime) override;

};

説明は不要だと思うがBeginPlay()とTick()を説明する.BeginPlay()はゲームをプレイして一度だけ呼ばれる.Tick()はフレームごとに呼び出される.
ではBeginPlay()とコンストラクタのASampleActor()は何が違うのだろうか?という疑問が生まれる(かもしれない).
説明が不要な人も多いと思うが一応説明する.この2つは全くの別物である(いうまでもない).コンストラクタはビルドしたときに呼び出される.BeginPlay()はゲームをプレイしてから実行される.
またActorクラスを継承したら頭にAが付きます.前回のUObjectクラスを継承したら,Uが付きます.それ以外のクラス名をするとビルドできません.

Actorに必要な変数やメソッドを宣言

ActorにはRoot Componentという基準となるコンポーネントが必要だ.Root Componentをカプセルコンポーネントやシーンコンポーネントに設定する.そしてその後,Root Component以下に様々なコンポーネントを追加する.
下記に分かりやすく?した図を載せた.

そしてメッシュを追加するためにStatic Mesh Componentが必要だ.そのためのコンポーネントを追加する.まずStatic Mesh Componentを入れる変数を定義する.

SampleActor.h
/*includeを省略*/

UCLASS()
class ASampleActor : public AActor
{
    GENERATED_BODY()
private:
    UPROPERTY(EditAnywhere, Meta = (ClampMin = -100, ClampMax = 1000, UIMin = 50, UIMax = 500))
        float range;
    UPROPERTY(EditAnywhere, Meta = (ClampMin = 0, ClampMax = 100, UIMin = 0, UIMax = 10))
        float speed;
    UPROPERTY(VisibleAnywhere)
        FVector initialLocation;

public: 
    //コンストラクタ
    ASampleActor();
    //ここにStatic Mesh Componentを入れる
    UPROPERTY()
       UStaticMeshComponent* MyMeshCom;

protected:
    //AActorで定義されているBeginPlay()をオーバーライド
    virtual void BeginPlay() override;

public: 
    //AActorで定義されているTick()をオーバーライド
    virtual void Tick(float DeltaTime) override;
};

privateで定義されている,3つの変数の役割については後ほど説明する.ここではUPROPERTYの中身を説明したい.EditAnywhereとVisibleAnywhere,Metaの中身だ.
EditAnywherをつけるとエディタから変数を変えることができる.またVisibleAnywhereではエディタから見ることはできるが,変数を変えることはできない.
前回はMetaのDisplayNameを説明した.今回は変数の最大値,最小値をきめるClampMaxClampMin.変数の値をスライダで設定できる最大値,最小値をきめるUIMinUIMax.
これらはデザイナーなどの非プログラマのために重宝するらしい.
まあ説明するより実際に見てもらう方が早い.

プログラムをビルド後,エディタに戻る.

こうなっているはずだ.その後,これをドラック&ドロップして,レベルの適用な位置に設置しよう.
すると何も見えない.Actorがレベルに設置され.こうなる.

RangeやSpeedをいじってもほしい.スライダではそれぞれ50から500と0から10になると思う.次はキーボードで数字を打っていほしい.どんなに大きい数字を入力しても最終的に1000と100になると思う.

さてこのままレベル上で何も見えないなら,不便だ.次からはcppファイルを変えていこう.

Static Mesh Compmentの作成とStatic Meshの設定

SampleActor.cpp
#include "SampleActor.h"

//コンストラクタ
ASampleActor::ASampleActor()
{
    //Tick()を有効にする.これでフレームごとにTick()が呼び出される
    PrimaryActorTick.bCanEverTick = true;
    //Static Mesh Componentを作成
    MyMeshCom = CreateDefaultSubobject<UStaticMeshComponent>("MyMesh");
    //Static Mesh ComponentをRoot Componentに付ける
    MyMeshCom->SetupAttachment(RootComponent);
    //Static Meshを読み込む
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereMesh(TEXT("/Game/StarterContent/Shapes/Shape_Sphere"));

    if (SphereMesh.Succeeded()) {
        //読み込みに成功したらStatic Mesh ComponentにStatic Meshをセットする
        MyMeshCom->SetStaticMesh(SphereMesh.Object);
        //Root Componentからの相対位置をセット.これだとRoot Componentと同じ位置になる
        MyMeshCom->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
    }

    //初期値を代入
    range = 250.0f;
    speed = 3.0f;
}
void ASampleActor::BeginPlay()
{
    Super::BeginPlay();
}
void ASampleActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

一気に複雑になったように見えるが意外とそれほど難しいことはやっていない.コメントアウトされた内容を読んでほしい.大事だと思うところをさらに説明していく.
まずCreateDefaultSubobject("MyMesh")だ.これは新たなオブジェクトを作る際に使う。<>の中に作りたいオブジェクトの種類を書く。
これと見た関数でNewObject<>()がある。これもオブジェクトを作る関数だが、CreateDefaultSubobjectとは使う場所が違う。
CreateDefaultSubobjectはコンストラクタの中で使用し、それ以外で使うのがNewObjectだ。

そしてFObjectFinderでStatic Meshをロードしている。公式のチュートリアルでも使われており、使いやすい。
ロード方法はいろいろとある。他のは公式でも見てもらいたい。
この方法と直接入れる方法をよく使うと思う。

最後にsuper::BeginPlay()とsuper::Tick(DeltaTime)を説明しよう。
他の言語でsuperの意味を知っている人からすると何となく意味はわかると思う。superは親クラスを示している。よって今回はAActorのBeginPlay関数とTick関数を呼び出している。
ここでC++の経験者ならあれっ?superってC++にあったけ?と思うかもしれない。私はそうなった。
調べてみるとこのsuperはUE C++で定義されているみたいだ。普通のC++はsuperがないと思う。

さて説明も終わったところでビルドをしよう.ビルドするとコンストラクタが実行される.そうしたら,Static Mesh Componentが作られ,Static Mesh(球体)が設定される.
そうしてこうなっていると思う.

次はこれをレベルにドラック&ドロップしよう.

作ったActorを上下に行き来するようにする.

最後におまけでActorが動くするようにする.

//includeは省略
void ASampleActor::BeginPlay()
{
    Super::BeginPlay();
    //初期座標を代入
    initialLocation = GetActorLocation();
}
void ASampleActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    //現在の座標を取得
    FVector nowLocation = GetActorLocation();
    //初期値より小さいか,設定範囲外か
    if (nowLocation.Z > initialLocation.Z + range || nowLocation.Z < initialLocation.Z)
        speed *= -1.0f;//+と-を入れ替える
    //座標の更新
    nowLocation.Z += speed;
    //新たな座標をセットする
    SetActorLocation(nowLocation);
    //デバックするとき使う.詳しく説明する.ここでは座標を表示している.
    GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Red, nowLocation.ToString());
}

このプログラムは初期値からrangeだけ上に上昇する.そして範囲外になるとプラスとマイナスを入れ替える.最後に新たな座標をActorに設定する.

ここでデバックでよく使う.AddOnScreenDebugMessageを説明する.
まずgEngineとはUObjectの子クラスである.公式の説明では「エディターまたはゲームシステムに不可欠なシステムの管理を担当する」(日本語訳)と書いている.
そこで定義されているのがAddOnScreenDebugMessageだ.
引数の説明をする.
第一引数は0か-1である.0にするとデバックの内容が上書きされる.-1だと上書きされない.今回は上書きされない.
第二引数は描画する時間である.
第三引数は色である,
第四引数で表示する内容を書く,型はFStringである.今回はFVectorを表示するために変換を行っています.詳しくは公式に任せます.公式をみると大体はわかると思う.

さてビルドしてみよう.そしてエディタに戻ります.そのままプレイしてみましょう.

おそらく,設置したActorが上下に移動したと思います.プロパティにてrangeやspeedを変えてみよう.
上下する範囲が増えたり,スピードが上がったりします.

まとめと実行環境

ここではActorを中心に説明した.私は公式の説明ではわかりにくかったのでここで詳しく説明をしてみました.コンポーネントなどの概念は重要ですのでイメージをつかむことが大事だと思う.多分
次はUSTRUCTやUENUMなどの説明をしたい.できならいいな.

実行環境

Visual Stdio 2017
Unreal Engine 4.24.3