C++ 別ヘッダーファイルで宣言したテンプレート関数を呼ぶとリンカエラーになる

やあ

C++なにもわからない僕が、親クラスと子クラスのヘッダーファイルを分け、親クラスでテンプレート関数を実装して、子クラスで呼び出そうとしたらリンカエラーが出たので、解決策をメモ。

状況例

Parent.h

class Parent
{
public:
    template<typename T>
    T Sum(T a, T b);
};

Parent.cpp

#include "Parent.h"

template<typename T>
T Parent::Sum(T a, T b) {
    return a + b;
}

Child.h

#include "Parent.h"

class Child : public Parent
{
private:
    int a;
    int b;
public:
    void Calc();
};

Child.cpp

#include "Child.h"
#include <iostream>

void Child::Calc() {
    std::cout << Sum(a+b) << std::endl;
}

こんな感じに、親クラスと子クラスのヘッダーファイルが分かれていると起こる。

解決策1

テンプレート関数の実装をヘッダーファイル内で行う。
Parent.h

class Parent
{
public:
    template<typename T>
    T Sum(T a, T b) {
        return a + b;
    }
};

解決策2

あまりよくない気がします。
親クラスと子クラスを同じヘッダーファイル内で定義する。

class Parent
{
public:
    template<typename T>
    T Sum(T a, T b);
};

class Child : public Parent
{
private:
    int a;
    int b;
public:
    void Calc();
};

参考

stackoverflow.com

stackoverflow.com

UE4 Tips

やあ

ただひたすら見つけたことを書いていくだけ。
ここ違うよとか、こうすればいいよとか教えていただけたら助かります。

目次

UnrealC++全般

GENERATED_UCLASS_BODYマクロを使うとBlueprintNativeEventでコンパイルエラーが起こる

------- 例 -------

UCLASS()
class HUGA_API AHogeChara : public ACharacter
{
    GENERATED_UCLASS_BODY()
public:
    UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
        void PiyoPiyo();
    /* _Implementationで関数はすでに本体を持っていますとコンパイルエラーが出る。 */
    void PiyoPiyo_Implementation(); 
}

------- 解決策 -------
GENERATED_UCLASS_BODYGENERATED_BODYマクロに変える。
目次に戻る

UE4 全般

StaticMeshにもソケットを作れる

StaticMeshアセットを開いて、
ウィンドウ→ソケットマネージャー→ソケット作成
f:id:pto8913:20201203130045p:plain

目次に戻る

あまりにも遠い場所でStaticMeshをSkeletalMeshにアタッチするとStaticMeshの挙動がおかしくなる

x: 1e6, y: 0, z: 1e7という座標でSkeletalMeshにStaticMeshをアタッチすると、StaticMeshががくがくと震えて気持ち悪い挙動になる。

解決策
z: 1e6にすると直った。

UMG

Constructの呼ばれるタイミング

pto8913.hatenablog.com

目次に戻る

WBPのConstructの前後に処理をしたい(C++)

Super::NativeConstruct()を呼び出した時点でWBPConstructが呼ばれるので、
WBPConstruct前に処理をしたい場合は

void UMyWidget::NativeConstruct()
{
    Super::NativeConstruct();
    
    /* ~ Any ~ */
}

WBPConstruct後に処理をしたい場合は

void UMyWidget::NativeConstruct()
{
    /* ~ Any ~ */
    
    Super::NativeConstruct();
}

目次に戻る

WBPのConstructの前後の処理をしたい(WBP)

前に処理をしたい場合はPreConstruct
後に処理をしたい場合はConstructの後ろにくっつけるだけ

目次に戻る

ListViewについて

f:id:pto8913:20210209145147p:plain
ListViewEntryWidgetClassに設定するWidget(ListViewEntryWidget)のTickFrequencyは必ずAutoにしておく必要がある。
f:id:pto8913:20210209145236p:plain
Neverにすると、エラーを吐いて1~2秒ほどフリーズする。
ListViewEntryWidgetのルートのVisibilityVisibleにしておく必要がある。
それ以外の場合だと、HoverClickedなどのイベントは呼ばれない。

目次に戻る

パッケージ化

プラグインを含んだBPプロジェクトをパッケージ化するとプロジェクトが開けない

プラグイン(多分C++のコミュニティプラグイン)を含んだBPプロジェクトをパッケージ化すると、
Plugin "PluginName" failed to load because module "PluginName" could not be found
というエラーが出る。

解決策
1. .uprojectファイルを右クリックして、Generate Visual Studio project filesをクリック。
f:id:pto8913:20201225180040p:plain

  1. 生成された.slnを開いて、Ctrl+Bでビルド。

  2. ビルドが終わったら再びパッケージ化してみる。

  3. 原因がこれだけならこれで終わるはずです。

目次に戻る

UE4 C++ GameplayTagが追加/削除されたときに処理をする方法

やあ

制作中のゲームで状態異常を実装しようと思ったときに、どうすればいいんだーってAbilitySystemComponentのソースを読んでたらタグが追加/削除されたときに処理ができるのを見つけたのでメモとして書いておきます。

やる

MyCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h"
#include "MyCharacter.generated.h"

class UAbilitySystemComponent;

UCLASS()
class MYPROJECT_API AMyCharacter
    : public ACharacter, public IAbilitySystemInterface
{
    GENERATED_BODY()
public:
    AMyCharacter();

protected:
    virtual void BeginPlay() override;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Character|Ability")
        UAbilitySystemComponent* AbilitySystemComponent;

public:
    UAbilitySystemComponent* GetAbilitySystemComponent() const override;

    /* このタグが追加/削除されたときにイベントを受け取ります。 */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Character|State")
        FGameplayTag StunTag;
    /* タグが追加/削除されたときに呼び出される関数。 */
    virtual void StunTagChanged(const FGameplayTag CallBackTag, int32 NewCount);
};

MyCharacter.cpp

#include "MyCharacter.h"

AMyCharacter::AMyCharacter()
{
    AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("Ability System Comp"));
}

void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();
    if (AbilitySystemComponent) 
    {
        /* タグが追加/削除されたときに処理が走るようにバインドする */
        AbilitySystemComponent->RegisterGameplayTagEvent(StunTag, EGameplayTagEventType::NewOrRemoved).AddUObject(this, &AMyCharacter::StunTagChanged);
        /* すでに追加されているタグが追加/削除されたときに処理が走るようにバインドする */
        AbilitySystemComponent->RegisterGameplayTagEvent(StunTag, EGameplayTagEventType::AnyCountChange).AddUObject(this, &AMyCharacter::StunTagChanged);
    }
}

UAbilitySystemComponent* AMyCharacter::GetAbilitySystemComponent() const
{
    return AbilitySystemComponent;
}

void AMyCharacter::StunTagChanged(const FGameplayTag CallBackTag, int32 NewCount)
{
    UE_LOG(LogTemp, Log, TEXT("StunTagChanged %s %d"), *CallBackTag.GetTagName().ToString(), NewCount);

    if (NewCount > 0)
    {
        /* DoStun */
    }
    else
    {
        /* StopStun */
    }
}

おまけ。GameplayEffectが適用/削除されたときに処理をしたい場合。

/* Hoge.h */
/* GameplayEffectが適用されたときに呼び出される処理 */
void EffectAdded(
    UAbilitySystemComponent* Source, 
    const FGameplayEffectSpec& SpecApplied, 
    FActiveGameplayEffectHandle ActiveHandle
);

/* GameplayEffectが削除されたときに呼び出される処理 */
void EffectRemoved(
    const FActiveGameplayEffect& RemovedEffect
);
/* Hoge.cpp */

void AHoge::BeginPlay()
{
    AbilitySystemComponent->OnGameplayEffectAppliedDelegateToSelf.AddUObject(
        this, &AHoge::EffectAdded
    );
    AbilitySystemComponent->OnAnyGameplayEffectRemovedDelegate().AddUObject(
        this, &AHoge::EffectRemoved
    );
}

void AHoge::EffectAdded(
    UAbilitySystemComponent* Source,
    const FGameplayEffectSpec& EffectSpec,
    FActiveGameplayEffectHandle EffectHandle
)
{
    UE_LOG(LogTemp, Log, TEXT("EffectAdded"));
}

void AHoge::EffectRemoved(const FActiveGameplayEffect& RemovedEffect)
{
    UE_LOG(LogTemp, Log, TEXT("EffectRemoved"));
}

UE4 GameplayTagが追加/削除されたときにイベントを受け取って処理をしたいときに見るメモ

目次

やあ

攻撃されて毒になったり、混乱状態になったりとしたいときに、GameplayTagが追加されたらエフェクトをだして、GameplayTagが削除されたらエフェクトを消したいぜって思ったので書きます。

環境

実装?

Hoge.h

/* このタグが追加/削除されたときにイベントを受け取る */
UPROPERTY(EditAnywhere, BlueprintReadOnly)
    FGameplayTag StunTag;
/* イベントを受け取った時に呼び出される関数 */
virtual void StunTagChanged(const FGameplayTag CallBackTag, int32 NewCount);

Hoge.cpp

void AHoge::BeginPlay()
{
    Super::BeginPlay();

    /* タグの追加/削除イベントに関数をバインド */
    AbilitySystemComponent->RegisterGameplayTagEvent(StunTag, EGameplayTagEventType::NewOrRemoved).AddUObject(this, &ACharacterBase::StunTagChanged);
}

void AHoge::StunTagChanged(const FGameplayTag CallBackTag, int32 NewCount)
{
    UE_LOG(LogTemp, Log, TEXT("StunTagChanged %s %d"), *CallBackTag.GetTagName().ToString(), NewCount);

    if (NewCount > 0)
    {
        /* DoStun */
    }
    else
    {
        /* StopStun */
    }
}

おまけ(GameplayEffectを適用したときに適用した相手にタグを追加したい)

GrantedTagsにタグを登録すると適用した相手に自動的にタグを追加してくれます。
すごいですね。
f:id:pto8913:20201026111424p:plain

UE4 GameplayEffectが適用された/削除されたときになにかしたいときに見るメモ

目次

やあ

GameplayEffectが適用された/削除されたときになにかしたいなーって思って。

環境

  • UE 4.25.0

内容

hoge.h

/* 自分にGameplayEffectが適用されたときに呼ばれる関数 */
UFUNCTION(BlueprintCallable)
    virtual void OnAppliedGameplayEffectToSelf(
        UAbilitySystemComponent* Source,
        const FGameplayEffectSpec& Spec,
        FActiveGameplayEffectHandle Handle
    );
/* GameplayEffectが削除されたときに呼ばれる関数 */
UFUNCTION(BlueprintCallable)
    virtual void OnGameplayEffectRemoved(
        const FActiveGameplayEffect& EffectRemoved
    );

hoge.cpp

void AHoge::BeginPlay()
{
    Super::BeginPlay();

    /* hoge.hで宣言した関数にバインドする */ 
    AbilitySystemComponent->OnGameplayEffectAppliedDelegateToSelf.AddUObject(
        this, &AHoge::OnAppliedGameplayEffectToSelf
    );
    AbilitySystemComponent->OnAnyGameplayEffectRemovedDelegate().AddUObject(
        this, &AHoge::OnGameplayEffectRemoved
    );
}

void AHoge::OnAppliedGameplayEffectToSelf(
    UAbilitySystemComponent* Source,
    const FGameplayEffectSpec& Spec,
    FActiveGameplayEffectHandle Handle
)
{
    /* なにかの処理 */ 
}

void AHoge::OnGameplayEffectRemoved(
    const FActiveGameplayEffect& EndedGameplayEffect
)
{
    /* なにかの処理 */
    /* 削除されたGameplayEffectのGameplayTagが欲しい時 */
    FGameplayTagContainer EndedGameplayEffectAssetTag;
    EndedGameplayEffect.Spec.GetAllAssetTag(EndedGameplayEffectAssetTag);
}

UE4 C++ BTTaskのTickTaskを走らせる方法

目次

やあ

メモだよ

環境

UE4.25.0

実装

BT_T_Hoge.h

// Copyright(C)write by pto8913. 2020. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/Tasks/BTTask_BlackboardBase.h"
#include "BT_T_FaceToTarget.generated.h"

UCLASS()
class Huga_API UBT_T_Hoge : public UBT_T_Base
{
    GENERATED_BODY()
public:
    UBT_T_Hoge();

    virtual EBTNodeResult::Type ExecuteTask(
        UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory
    ) override;

    virtual void TickTask(
        UBehaviorTreeComponent& OwnerComp, 
        uint8* NodeMemory, 
        float DeltaSeconds
    ) override;
};

BT_T_Hoge.cpp

// Copyright(C)write by pto8913. 2020. All Rights Reserved.

#include "BT_T_Hoge.h"

#include "BehaviorTree/BTTaskNode.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/Blackboard/BlackboardKeyAllTypes.h"

UBT_T_Hoge::UBT_T_Hoge()
{
    bNotifyTick = true;
}

EBTNodeResult::Type UBT_T_Hoge::ExecuteTask(
    UBehaviorTreeComponent& OwnerComp,
    uint8* NodeMemory
)
{
    return EBTNodeResult::InProgress;
}

void UBT_T_Hoge::TickTask(
    UBehaviorTreeComponent& OwnerComp,
    uint8* NodeMemory,
    float DeltaSeconds
)
{
    Super::TickTask(OwnerComp, NodeMemory, DeltaSeconds);
    /* なにかの処理 */
}

UE4 アニムグラフからAnimNotifyを飛ばす方法

目次

やあ

AnimationBPのアニムグラフのステートマシンからAnimNotifyを飛ばしたいなーって思ってたら出来たのでメモとして残しておくよ。
f:id:pto8913:20201011221119p:plain
赤丸の部分どこからでも飛ばせるよ。

環境

UE4.25.0

目次に戻る

やる

ステートマシンからAnimNotifyを飛ばす場合

AnimNotifyを飛ばしたいステートマシンを選択します。
このとき詳細パネルのアニメーションステートにAnimNotifyの名前を入力することでAnimNotifyを飛ばせます。
f:id:pto8913:20201011222145p:plain

ステート開始イベントからAnimNotifyを飛ばしてみる

今回は、移動し始めたらAnimNotifyが飛ばされ、移動速度が表示されるようにします。

  1. 詳細パネルのアニメーションステートにAnimNotifyの名前を入力します。
    f:id:pto8913:20201011222724p:plain

  2. イベントグラフでAnimNotifyイベントを作成します。
    f:id:pto8913:20201011223134p:plain

  3. テストしてみます。
    youtu.be
    期待通り、動き始めた瞬間に移動速度が表示されました。

目次に戻る

ステート離脱イベントからAnimNotifyを飛ばしてみる

今回は、移動し終わったらAnimNotifyが飛ばされ、移動速度が表示されるようにします。

  1. 詳細パネルのアニメーションステートにAnimNotifyの名前を入力します。
    f:id:pto8913:20201011224135p:plain

  2. イベントグラフでAnimNotifyイベントを作成します。
    f:id:pto8913:20201011223134p:plain

  3. テストしてみます。
    youtu.be
    期待通り、動き終わったら移動速度が表示されるようになりました。

目次に戻る

ステートブレンド完了イベントからAnimNotifyを飛ばしてみる

見た目には開始イベントとほぼ変わらないように思いますが全然違います。

  1. 詳細パネルのアニメーションステートにAnimNotifyの名前を入力します。
    f:id:pto8913:20201011230012p:plain

  2. イベントグラフでAnimNotifyイベントを作成します。
    f:id:pto8913:20201011223134p:plain

  3. テストしてみます。
    youtu.be
    わかりづらいですが待機状態から歩行状態へブレンドが完了したら移動速度が表示されています。

目次に戻る

トランジションイベントからAnimNotifyを飛ばす場合

AnimNotifyを飛ばしたいトランジションを選択します。
このとき詳細パネルの通知にAnimNotifyの名前を入力することでAnimNotifyを飛ばせます。
f:id:pto8913:20201011230923p:plain

やることはステートマシンの時と同じなので省略します。

おまけ : アニムグラフからキャラクターBPにAnimNotifyを飛ばす

下準備(BPインターフェースを作成)

  1. コンテンツブラウザを右クリック->ブループリント->ブループリントインターフェースから作成します。
    f:id:pto8913:20201011232009p:plain

  2. 関数を追加します。
    f:id:pto8913:20201011232103p:plain

やる

  1. AnimBPのイベントグラフ上でさっき作った関数を、AnimNotifyイベントから呼び出します。
    f:id:pto8913:20201011232335p:plain

  2. AnimNotifyを受け取りたいキャラクターBPにインターフェースを追加します。
    f:id:pto8913:20201011232544p:plain

  3. さっき作った関数をオーバーライドします。
    f:id:pto8913:20201011232931p:plain
    f:id:pto8913:20201011233030p:plain

  4. テストします。
    youtu.be
    動きだすたびに名前が表示されます。