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?

UE4で複数のアニメーションファイルに通知キーを一括で設定する

Posted at

UE4にて選んだアニメーションシーケンスの全てに通知を追加する。

通知するフレームはアニメーションが99%終了時点。

UE4でアニメーションの終了を感知してタスクを進めてみたいから。
(モンタージュファイルなら終了を検知できるとか言うな)

コードは最後に貼ってあります

C++でプロジェクトを作る。

スクリーンショット 2025-10-18 181230.png

ブループリント→クラス→下のコードで作ったBP_AnimNotifyAdderを作る。

スクリーンショット 2025-10-18 181307.png

続いてC++のコードを実行してくれるメニューボタン的なのを作ってくれる『ウィジット』を作る。

ウィジットちゃんの中身。

最初に開いたときには『デザイナー』のタブになってると思うので、
そこでボタンButtonを画面に配置しましょう。したらば、ON_Clickイベントを追加する項目があるはずなので追加しましょう。
(グレーのボタンを選択して、右下の緑色のやつを押す)

image.png

次はアニメーションシーケンス用の変数の配列『Animation』を作ってやり、その中に通知キーを打ちたいアニメーションファイルをぶち込む

スクリーンショット 2025-10-18 181440.png

(右下はグレイマンのアニメ)

そしたらレベルブループリントを開く。

スクリーンショット 2025-10-18 181415.png

プレイしたときにウィジットのメニューが出るようにする。

スクリーンショット 2025-10-18 183032.png

プレイ画面
(左上の■ボタンを押すと変換開始)

これでアニメーションファイルの最後らへんに一括でキーを打てた。
ファイル自体は複製されます。

以下コード。
プロジェクト名は『KA_EUW_CallBP』

キンアジ氏のブログでやってたことを試したプロジェクトの流用だって言わなきゃバレへんか…

ヘッダーファイル

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "UAnimNotifyAdder.generated.h"

class UAnimSequence;

/**
 * 下の~APIはちゃんとプロジェクト名ごとに変えてね
 */
UCLASS(Blueprintable)
class KA_EUW_CALLBP_API UAnimNotifyAdder : public UObject
{
    GENERATED_BODY()

public:

    /** 複数のアニメーションに通知を追加して保存する */
    UFUNCTION(BlueprintCallable, Category = "Animation")
    void AddNotifyAndSave(const TArray<UAnimSequence*>& AnimSequences);
};

ソースファイル

#include "UAnimNotifyAdder.h"
#include "Animation/AnimSequence.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "AssetRegistryModule.h"
#include "Misc/PackageName.h"
#include "UObject/Package.h"

void UAnimNotifyAdder::AddNotifyAndSave(const TArray<UAnimSequence*>& AnimSequences)
{
    UE_LOG(LogTemp, Warning, TEXT("AddNotifyAndSave called with %d sequences"), AnimSequences.Num());

    for (UAnimSequence* AnimSequence : AnimSequences)
    {
        if (!IsValid(AnimSequence))
        {
            UE_LOG(LogTemp, Warning, TEXT("Invalid AnimSequence skipped"));
            continue;
        }

        if (AnimSequence->GetLinker() == nullptr)
        {
            UE_LOG(LogTemp, Warning, TEXT("AnimSequence %s is not fully loaded, skipping"), *AnimSequence->GetName());
            continue;
        }

        // 保存用パッケージ作成
        FString AssetName = AnimSequence->GetName() + TEXT("_Notified");
        FString PackagePath = TEXT("/Game/ModifiedAnimations/") + AssetName;
        UPackage* Package = CreatePackage(*PackagePath);
        if (!IsValid(Package))
        {
            UE_LOG(LogTemp, Error, TEXT("Failed to create package for %s"), *AssetName);
            continue;
        }

        Package->FullyLoad();

        // アニメーションを複製
        UAnimSequence* NewAsset = DuplicateObject<UAnimSequence>(AnimSequence, Package, FName(*AssetName));
        if (!IsValid(NewAsset))
        {
            UE_LOG(LogTemp, Error, TEXT("Failed to duplicate AnimSequence %s"), *AnimSequence->GetName());
            continue;
        }

        // 通知オブジェクトを NewAsset に属する形で生成(RF_Transactional 付き)
        UAnimNotify* NotifyObj = NewObject<UAnimNotify>(NewAsset, UAnimNotify::StaticClass(), NAME_None, RF_Transactional);
        if (!IsValid(NotifyObj))
        {
            UE_LOG(LogTemp, Error, TEXT("Failed to create generic NotifyObj"));
            continue;
        }

        // 通知イベント作成(構造を完全初期化)
        float NotifyTime = NewAsset->SequenceLength * 0.99f;
        FAnimNotifyEvent NewNotify;
        NewNotify.Notify = NotifyObj;
        NewNotify.NotifyName = FName("EndAnim");
        NewNotify.NotifyTriggerChance = 1.0f;
        NewNotify.NotifyFilterType = ENotifyFilterType::NoFiltering;
        NewNotify.NotifyFilterLOD = 0;
        NewNotify.TriggerWeightThreshold = 0.0f;
        NewNotify.TrackIndex = 0;

        // 通知時間を安全に設定(正しい型で)
        NewNotify.SetTime(NotifyTime, EAnimLinkMethod::Absolute);

        // 通知追加
        NewAsset->Modify();
        NewAsset->Notifies.Add(NewNotify);

        // アセット登録と保存準備
        FAssetRegistryModule::AssetCreated(NewAsset);
        NewAsset->PostEditChange();
        NewAsset->MarkPackageDirty();

        FString FilePath = FPackageName::LongPackageNameToFilename(PackagePath, FPackageName::GetAssetPackageExtension());

        UE_LOG(LogTemp, Warning, TEXT("Saving to: %s"), *FilePath);
        UE_LOG(LogTemp, Warning, TEXT("Added notify 'EndAnim' at %.2f to %s"), NotifyTime, *NewAsset->GetName());

        UPackage::SavePackage(Package, NewAsset, EObjectFlags::RF_Public | RF_Standalone, *FilePath);
    }
}

Build.cs
モジュールを色々突っ込んでますが何がどんな機能を提供してるかは知らない
でもないと動かない

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class KA_EUW_CallBP : ModuleRules
{
	public KA_EUW_CallBP(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

		PublicDependencyModuleNames.AddRange(new string[] { "Core", "LevelEditor", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "Slate", "SlateCore", "UnrealEd", "Blutility", "UMG", "UMGEditor", "AnimGraph" });

        
    }
}

実はUnityでC#使ってたけどC++なんぞ1ミリも分からないがAI虐めながら書きました。
AIも全く完璧じゃないので間違えまくりエラー吐きまくりの中ちゃんとテストして動いたので動作はします。

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?