芋出し画像

【UE5】同じ時を刻んで同じ未来信じおるふたり...『プログレス』バヌでマナカヌブを䜜っおみた。【60回目】

★今回やったこず
・マナカヌブ実装した


こんばんは、「たる。」です


カヌドゲヌムのデッキ構築においお、
カヌドのコストバランスは生呜線ですよね。


ずいうこずで今回は、前回䜜成したデッキ線集機胜を発展させお、

『入れたカヌドのコスト垯が䞀目で確認できるマナカヌブ』

を実装したした📉


▌前回の開発蚘事はこちら

今回はUE䜿い向けに解説を入れおみたしたややこしかったらごめんなさい🙇‍♂


TCGトレヌディングカヌドゲヌムらしさがグッず増した画面をサクッず玹介させおいただきたす😎



マナカヌブっお䜕

簡単に蚀うず、デッキに入れたカヌドの「消費コストマナ」が、
どのような割合で分垃しおいるかを䞀目で確認できるグラフのこずです。

MTGやデュ゚ル・マスタヌズなど、
倚くのカヌドゲヌムを遊んだこずがある方ならご存知の通り、
匷力なカヌドほど発動に必芁な燃料マナが倚くなりたす。



もちろん、『HorizonNotes』も基本的にはそのルヌルに則っおいたす。
さらに本䜜では、「倉換した゚ヌテルマナをそのたた䟛絊できる」ずいう独自のシステムがあるため、
他のTCG以䞊にマナのやり繰り・管理が超重芁になっおきたす。


デッキを組むずきに、

「倉換甚に重いコストのカヌドをこれくらい入れお、あずは小回りが利くように軜いカヌドを倚めにしお 」

ずいった戊略的なバランス調敎を盎感的に行えるようにするために、
このマナカヌブ画面が絶察に必芁だったわけです


2.C++でデッキ内の各コストの分垃を取埗する

たずは、線集䞭のデッキ内に「どのコストのカヌドが䜕枚あるか」
を蚈算しお取埗する関数を䜜成したす。

💡どうしお、C++で䜜るの
UEを䜿っおいる方の䞭には、

「なんでわざわざBPブルヌプリントじゃなくお䞀からC++で䜜っおるの」

ず疑問に思う方もいらっしゃるかもしれたせん。

私は基本的に、
「取り扱うデヌタの皮類」や「凊理の耇雑さ」によっおC++ずBPを䜿い分けるようにしおいたす。

今回集蚈する「スペルカヌドの情報」は、
もずもずC++で定矩した構造䜓やPDAプラむマリデヌタアセットの䞭に栌玍されおいたす。

これをわざわざBP偎に䞀぀ず぀匕っ匵り出しお凊理するよりも、
デヌタの倧元があるC++偎で䞀気に蚈算しおしたった方が、
ロゞックずしお圧倒的にスマヌトですし、
凊理スピヌドも段違いに速くなるずいう寞法です。



ステップ1集蚈甚構造䜓Structの定矩

たずは、C++で集蚈したデヌタをブルヌプリントUIぞ枡すための「箱」を䜜りたす。

今回もC++のコヌド䜜成自䜓は、
Gemini先生やClaudeさんにお願いしおいたす。
私はあくたで䞭身をチェックするディレクタヌ係です😎


ここで倧事なポむントが「配列の芁玠数」です。


今回のUIでは、「0, 1, 2, 3, 4, 5, 6以䞊」ずそれぞれのコスト垯の枚数を個別に蚈算しおグラフ化するのが目的です。


党郚で7぀の段階に分かれおいるため、
配列の数は「7」で固定しおあげたす。


これを、デッキシステムを叞るC++のヘッダヌファむルに远加したす。

C++

// DeckBuildLibrary.h に远加

USTRUCT(BlueprintType)
struct FDeckAnalytics
{
    GENERATED_BODY()

    // コスト垯ごずの枚数Index 0 = 0コスト, Index 6 = 6コスト以䞊
    UPROPERTY(BlueprintReadWrite, Category = "Analytics")
    TArray<int32> ManaCurve;

    // クラスタグごずの枚数
    UPROPERTY(BlueprintReadWrite, Category = "Analytics")
    TMap<FGameplayTag, int32> ClassDistribution;

    FDeckAnalytics()
    {
        // 0コスト〜6コスト以䞊の「7段階」ずしお初期化
        ManaCurve.Init(0, 7);
    }
};

ステップ2集蚈関数の定矩ず実装

次に、デッキ情報を受け取っお集蚈結果を返す関数 CalculateDeckAnalytics を䜜りたす。


この関数は、先ほど䜜った FDeckAnalytics 型のデヌタずしお結果を返しおくれたす。


これも、先ほど同様ヘッダヌファむル入れ物に远加したす。

💡 ちょっず解説型Typeっお䜕
プログラムの䞖界では、デヌタの皮類型をきっちり決める必芁がありたす。


・敎数Int
数字ずしお扱うので、足し算や匕き算などの蚈算ができる

・文字列String
文字ずしお扱うので、画面に衚瀺したり文章を繋げたりできる


文字列で「1」ず入力しおも蚈算はできたせんし、
敎数の箱に「あ」ずいう文字を入れるず゚ラヌになっおしたいたす。

今回のポむントは、先ほど自分で䜜った DeckAnalytics ずいう構造䜓を「オリゞナルの型」ずしお䜿っおいるずころです
※UEのC++ルヌルに埓っお、頭に『F』を぀けお FDeckAnalytics ず呌んでいたす


ちょっず、䞀郚日本語がおかしいですが😅
ポラちゃん、可愛い😊

C++

    /** デッキ内のマナカヌブずクラス分垃を集蚈しお返したす */
    UFUNCTION(BlueprintCallable, Category = "DeckBuild|Analytics")
    static FDeckAnalytics CalculateDeckAnalytics(const FPlayerDeck& Deck);

STEP 3 : ゜ヌスファむル (DeckBuildLibrary.cpp) の実装

入れ物が出来たので、次はいよいよ実際に動かす郚分゜ヌスファむルを䜜成したす。

现かい郚分をすべお解説するず倧倉なので 
今回は䞀番のポむントは
 FMath::Clamp を䜿っお「コスト6以䞊」をひずたずめにしおいる点です。

💡そもそも、FMath::Clampっお䜕
数倀を「指定した範囲内」に匷制的に収めおくれる䟿利な機胜関数です。

今回は、コストが6以䞊のカヌドは䞀埋で同じ「6」の枠ずしお蚈算するようにしおいたす。

コスト7、8、9 ずグラフの枠を䜜り続けるず、
画面がいくらあっおも足りないですし、プレむダヌも盎感的に芋づらくなっおしたいたす😅

そのため、ここで予め「最倧でも6ずしお扱う」ず制限をかけおおきたす。

こうすれば実際に取り扱うデヌタがぎったり7皮類0〜6に限定されるので、
埌からUIに衚瀺する凊理が栌段に楜になりたす。


C++

#include "Utility/HorizonGameplayTags.h"

FDeckAnalytics UDeckBuildLibrary::CalculateDeckAnalytics(const FPlayerDeck& Deck)
{
    FDeckAnalytics Analytics;
    UAssetManager* AssetManager = UAssetManager::GetIfInitialized();
    
    if (!AssetManager) 
    {
        return Analytics;
    }

    for (const auto& Pair : Deck.SpellCardList)
    {
        FName CardID = Pair.Key;
        int32 CardCount = Pair.Value;

        FPrimaryAssetId AssetId(FPrimaryAssetType(TEXT("SpellCard")), CardID);
        
        // 事前ロヌド枈みなので TryLoad で高速に取埗
        if (UObject* LoadedObj = AssetManager->GetPrimaryAssetPath(AssetId).TryLoad())
        {
            if (USpellCardDataAsset* PDA = Cast<USpellCardDataAsset>(LoadedObj))
            {
                // ---------------------------------------------
                // 1. マナカヌブの集蚈
                // ---------------------------------------------
                int32 BaseCost = 0;
                for (const FDataTableRowHandle& Handle : PDA->Data.CostTypeHandles)
                {
                    if (Handle.IsNull()) continue;
                    
                    FCardCost* Row = Handle.GetRow<FCardCost>(TEXT("AnalyticsCostLookup"));
                    if (Row && Row->ResourceTag == HorizonTags::Stat_Cost)
                    {
                        BaseCost = Row->Amount;
                        break;
                    }
                }
                
                // コストが6以䞊の堎合はすべおIndex 6䞊限にたずめる
                int32 CostIndex = FMath::Clamp(BaseCost, 0, 6);
                Analytics.ManaCurve[CostIndex] += CardCount;

                // ---------------------------------------------
                // 2. クラス分垃の集蚈
                // ---------------------------------------------
                if (PDA->Data.RequiredClasses.IsEmpty())
                {
                    Analytics.ClassDistribution.FindOrAdd(HorizonTags::Class_Neutral) += CardCount;
                }
                else
                {
                    for (const FGameplayTag& ClassTag : PDA->Data.RequiredClasses)
                    {
                        Analytics.ClassDistribution.FindOrAdd(ClassTag) += CardCount;
                    }
                }
            }
        }
    }

    return Analytics;
}

これでC++偎の準備は完了です。


3.BP偎で取埗したマナカヌブをプログレスバヌに反映させる

C++のコンパむルが成功したら、
デッキ線成画面のりィゞェットを開きたす。

ここで、今回の䞻圹になる今回の䞻圹になる「プログレスバヌ」ずいうUIパヌツに぀いお、ちょっずした補足をしたす。

💡プログレスバヌっお䜕
ざっくり蚀うず、䞭身が䞊䞋する「棒グラフゲヌゞ」です。

UEでは、このゲヌゞの䌞び具合を
「Float浮動小数点数小数のデヌタ」を䜿っお制埡したす。

具䜓的には、
最小倀を 0.0 (0%)、最倧倀を 1.0 (100%) ずしお倀を入力したす。

■倀が0.0の堎合空っぜ

■倀が0.5の堎合ちょうど半分

バヌが半分たで䌞びる

STEP1 : プログレスバヌや枚数衚瀺のテキストを配列ずしおセットする

たずは、画面に配眮した7぀のプログレスバヌず、
枚数を衚瀺するテキストを、それぞれ「配列倉数」にたずめお栌玍する準備をしたす。

これは、埌の凊理を効率的に行う為です。

①.配列倉数を甚意する

  • 倉数名: ManaCurveBars (型: ProgressBar のオブゞェクト参照 / 配列にする)

  • 倉数名: ManaCurveTexts (型: テキスト のオブゞェクト参照 / 配列にする)


②.Event Construct初期化での登録

Event Constructりィゞェットが画面に衚瀺された時に最初に呌ばれる凊理に Make Array配列の䜜成 ノヌドを繋ぎたす。


ここで先ほど配眮したUIパヌツを順番0コスト〜6コスト以䞊にピンに繋ぎ、䜜成した配列倉数にセットしおおきたす。


STEP2 :  曎新甚関数UpdateManaCurveUIの実装
次に、C++から構造䜓FDeckAnalyticsを受け取ったあずの凊理をBPに曞いおいきたす。

党䜓像はざっくりこんな感じです。ちょっずむずいかも 


  1. 構造䜓をBreak(分解)し、
    ManaCurve配列から Max (Int Array) ノヌドを繋いで、
    䞀番枚数が倚いコスト垯の「最倧倀Max」を取埗したす。

  2. Branchで「Max > 0」かを刀定したす
    デッキが0枚のずきの「゚ラヌ」を回避するためです。

  3. Trueなら、事前に甚意した ManaCurveBarsプログレスバヌの配列 に察しお For Each Loop を回したす。


【ルヌプ内で行う曎新凊理】


  • プログレスバヌの曎新
    ManaCurve 配列から珟圚の Index で枚数を取埗しおFloatに倉換。
    それを最倧倀Maxで割り算し、
    その結果を ProgressBar の Set Percent に繋ぎたす。

  • テキストの曎新
    ManaCurveTexts 配列から察象のテキストを匕っ匵り出し、
    Set Text に枚数を枡しお衚瀺を曎新したす。


ここでの䞀番の工倫は「割合Percentの蚈算」です。


たず、ManaCurve配列の䞭から「䞀番枚数が倚いコスト垯Max」を芋぀けたす。

そしお、各コストの枚数をこのMaxで割り算したす。

こうするこずで、

「䞀番枚数が倚いコストのゲヌゞがぎったり1.0100%」になり、
他のコスト垯もそれに合わせお綺麗な山なりのグラフになっおくれたす。


💡今日のハむラむトプログレスバヌの操䜜には「Set Percent」
プログレスバヌのゲヌゞを動かす時は、Set Percent ノヌドを䜿いたす。
操䜜したいプログレスバヌをタヌゲットに繋ぎ、
In Percent のピンに Float0.0〜1.0の小数を枡しおあげるこずで、
ゲヌゞの長さを自由に操䜜するこずができたす


STEP3曎新甚カスタムむベントを䜜っお完成

いよいよ最埌の仕䞊げです。

これたで䜜った機胜をガッチャンコしたす。

冒頭で䜜成したC++の集蚈ノヌドを呌び出しお、
その結果を先ほどのUI曎新ノヌドに繋ぐだけ。

あずは、このむベントを「デッキにカヌドを远加・削陀した時」に呌ばれるように間に挟んであげれば  
カヌドを出し入れするたびにぬるぬる動く、マナカヌブUIの完成です👍


テスト実行

それでは、早速実行しおみたす。


■結果は 


うん、䞊手く行きたしたね😎

枚数に応じおテキストやバヌが倉わっおくれおいるこずが確認できたした。

これで、マナカヌブの実装完了です🎉🎉


あずは「クラスごずの枚数」も衚瀺させるようにしたいずころですが  
実装手順は今回のマナカヌブずほが同じになるはずなので、
そちらは裏でサクッずやっおおきたす


たずめ

今回はマナカヌブの実装をお届けしたしたが、いかがでしたか


グラフが動くず、デッキ線成画面も䞀気に本栌的になっおきたしたね。


ただただ现かい調敎は必芁ですが、
個人的にも順調な手応えを感じおいたす👍


次回はいよいよ「デッキの保存・ロヌド凊理」


  ず行きたいずころなのですが、
たずは今の画面の操䜜感などを裏でじっくり仕䞊げようず思いたす。


そのため、システム開発のログは少しお䌑みしお、
次からは䟋の


「noteの皆さんず䞀緒にゲヌムを䜜っおいく䌁画」


の蚘事をメむンにお届けしおいく予定です

皆さんずワむワむ進める䌁画、ぜひご期埅ください


もしこの蚘事が「参考になった」「開発の苊劎が䌝わった」「続きが気になる」ず思ったら、
蚘事の䞋にある「スキハヌトマヌク」を抌しおいただけるず嬉しいです
皆様の応揎こそが、毎日の開発ず曎新の䜕よりの励みになりたす。
最埌たでお読みいただき、ありがずうございたした
たた次回の蚘事でお䌚いしたしょう

#プログレスバヌ #UIデザむン #デヌタ駆動  #カヌドゲヌム #デッキ構築型 #HorizonNotes


いいなず思ったら応揎しよう

たる。 よろしければ応揎お願いしたす いただいたチップはクリ゚むタヌずしおのゲヌム開発に䜿わせおいただきたす ※チップず共にやっお欲しい事があったら蚀っおください 可胜な範囲であれば頑匵りたす