ハムちゃんのブログ

ゲームの専門学校に通う学生です。個人の活動した内容を投稿を行う予定です!

【Unity】エディタ拡張初心者がゲームをビルド出来なくて詰んだ話

はじめに

この記事に書いてある内容はエラー原因を探っただけなので、根本はまだ未解決です。

作った機能

参照したAnimatorControllerを元に、レイヤーとステートをドロップダウンメニューから指定できるエディタ拡張を作成。

失敗した点

失敗①
レイヤーとステートを取ってくるクラスに、UnityEditorに関する処理が入っていたためビルドが出来なくなったこと。

失敗②
上のクラスをスーパークラスに持たせた結果、スーパークラスと継承先もエディタ対象クラスになりビルド時にエラーが出るようになってしまった。

解決方法を模索して気付いたこと

エラー原因がEditor関連の処理を含んだら駄目なのか、それともエディタ拡張で対象のクラスを指定したら駄目なのか...とりあえず原因が分からないので、テスト用のクラスを作成することに。

もし、後者がエラー原因だったら困るなぁ..................

これが、エディタ拡張から値を受け取るクラス。UI関連はビルド時に値が反映されたかどうかの確認用です。

using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    [SerializeField, HideInInspector]
    public int Number;

    [SerializeField]
    private Text _text;

    private void Start()
    {
        _text.text = Number.ToString();
    }
}

エディタ拡張でドロップダウンメニューを表示するクラス。

# if UNITY_EDITOR

using UnityEditor;

[CustomEditor(typeof(Test), true)]
public class TestInspector : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        EditorGUI.BeginChangeCheck();

        Test test = target as Test;
        var displayOptions = new string[] { "1", "2", "3" };
        var optionValues = new int[] { 1, 2, 3 };
        var newNumber = EditorGUILayout.IntPopup(
            "Select Number",
            test.Number,
            displayOptions,
            optionValues);

        if (EditorGUI.EndChangeCheck())
        {
            test.Number = newNumber;
            EditorUtility.SetDirty(test);
        }
    }
}

#endif

作ったTestを適当なGameObjectにアタッチして値を設定してビルドを通すと....

さっき設定した1が出てるね!
ということは、エラー原因がEditor関連の処理を含んだら駄目でエディタ拡張から値を渡すことは大丈夫だということが分かった。

ひとまず、原因が分かったことだしエディタの処理が紛れてるクラスを改良しなくては 😕

【Unity, C#】スクリプトの実行順を変更する

DefaultExecutionOrderを使う


MonoBehaviourの場合、DefaultExecutionOrderという属性を付ければ実行順を変更することができるらしい。

docs.unity3d.com

クラスに属性をつけるだけで良いみたい。

using UnityEngine;

[DefaultExecutionOrder(0)]
public class Test : MonoBehaviour
{
    void Start()
    {
        
    }

    void Update()
    {
        
    }
}

試しにStateBehaviourで使ってみたけど、実行順が変わらなかったからMonoBehaviour限定かも。

参考リンク


www.hanachiru-blog.com

作業日記 # 1

Arbor関連


・ステート遷移を直接呼び出す

ダメージを受けた時のステートを作成を考え、Editor上にノードをとりあえず配置。

ここで、困ったことがステートの遷移条件の追加。

約1年前に、Arborを利用してゲーム制作を行ったことがあるが、ダメージステートなど、どのステートからも遷移するタイプのステート管理が本当に面倒くさい。

AnimatorのAnyTransitionのような機能があれば管理が楽ではあるが現状調べた限りはその機能はなさそう...

GUI上から全てのステートを繋げて値による条件遷移が一番楽な管理方法ではあるが、Editorが物凄く見にくくなることからできることなら避けたい。

考えたこと

Animatorでも、スクリプトからPlayやCrossFadeなどのステート変更処理があったことからArborにもステート変更処理がどこかにあるのではないかと考えた。

ドキュメントから調べたところ、ArborFMS内にステート変更を行うTransition(StateLink)メソッドを見つけた。そして、StateLinkを取得するメソッドとしてFindState(string)というメソッドがある。

引き数のStateLinkを取得するために、FindStateというメソッドを利用し、_arborFMSのTransitionメソッドの引き数に渡すことでステート遷移が出来るようになった。

// 変数
 private ArborFSM _arborFSM;
 private State _damageState;

// ステートの取得
_damageState = _arborFSM.FindState("Damage");

// ステートの変更
_arborFSM.Transition(_damageState);

動作確認を行うと、ステートが繋がっていない場所からDamageStateに遷移することができた。

ステートの遷移条件を行うパラメーターのリセット処理をAnimationEventで配置を行い、連続で動作を確認をしてみると...

悲しいことにステートが遷移されず、止まる状態になってしまった。

DamageState一つで実験しても止まることがあるので...何か原因がありそう。

・止まる原因を考える
1.Transitionの直接呼びが良くない?
2.AnimationEventとの併用が良くない
3.ArborFMSと通常スクリプトの実行タイミングがずれている

などのことを考えたが、確実にこれだ!という確信はどれもない。
一旦、この方法によるステート遷移は保留。

・コンポーネント取得関連

Arborでは変数やコンポーネントを管理することができる、ParameterContainerがある。利用中に気づいたことも沢山ある。

GetComponentで追加した変数等を取得

ParameterContainerからGetComponentを行うと、指定した変数等を取得することができる。自分が使った時は登録したAnimatorやCharacterControllerなどを取得する際に利用。ただ、boolやintなどは試していないので分からない。

コンポーネントを追加する

ParameterContainerにコンポーネントを登録する時は、UnityObjectのComponentを選択して、▽タブから登録するコンポーネントの名称を設定を 行った後に、追加できるようになる。

インスペクターからコンポーネントをアタッチする

MonoBehaviourでは、SerializeFieldでインスペクターからアタッチできますが、ArborEditor上でも同じ用にアタッチできます。

[SerializeField]
private Transform _cacheTransform;

ですが、上の方法だとParameterContainerからアタッチできないので、FlexibleComponentを利用します。

[SerializeField]
private FlexibleComponent _flexibleCacheTransform;

Editorを確認するとちゃんと表示されています!ただ、インスペクターの参照設定が最初はConstantになっているので、Parameterに変更するのを忘れ内容に...▽マークを押して変更できます。

ParameterContainerの青色の矢印を長押しして、ドラッグ&ドロップで参照します

これで、利用できます...というわけではなく、キャストやasをして変数に入れる必要があります。どっちでも取得できますが、現時点で違いはあまり分かっていないです。

_cacheCharacterController = (CharacterController)_flexibleMyCharacterController.GetValueObject();

_cacheTransform = (Transform)_flexibleCacheTransform;

2024/12/12の活動記録

はじめに

このブログはHamster Output Advent Calendar 2024の12日目の記事です。

アウトプット速度が間に合わなくなってきたことと、記事にするにも内容が薄くなるということがあったので...1日ごとにやってきたことを、これから出していきます。

やったこと一覧

・ArborFMSとAnimatorの連携
・MoveStateの時にプレイヤーの座標移動を行う

ArborFMSとAnimatorの連携

ArborFMSからAnimatorCrossFadeをステートごとに呼び出す処理を作る。

1.Animatorに再生したいAnimationを配置する


2.ArborFMSからAnimatorCrossFadeを呼び出す


3.ArborEditorのParametersにAnimatorと条件を遷移追加


4.ステートの条件分岐を追加する

これで、ArborFMSのステート連携の準備は完了!
ここでまでに設定する上で、悩んだこと。

実は、数日前まではParameterContainerをPlayerのインスペクターに直接アタッチしてパラメーターの参照を行っていた。

制作当初はBehaviourTreeとArborFMSを二刀流で使おうと考えていました。

・使用用途としては
BehaviourTree = Animation管理
ArborFMS = プレイヤーの動作処理を管理

2つで管理することから、パラメーターの管理を一つのArborEditorで行うとパラメーター参照がめんどくさそうになると感じました。

ただ、制作が進んでかんじたことはBehaviourTreeとArborFMSを二刀流すること自体が面倒だということに気づき、上の方法で落ち着きました。

MoveStateの時にプレイヤーの座標移動を行う

今回、プレイヤーの座標移動をするためにCharacterController使うことにしました。まぁ...Rigidbodyで座標移動でも良かったんですが、今まで使ってこなかったので...この機にと

CharacterControllerの参照をSerializeFieldで行っても良かったのですが、毎回アタッチするのが面倒だな...と思いParameterContainerにコンポーネントを入れて参照するようにしました。結局、毎回アタッチするのであまり代わってないです。インスペクターまで毎回行かなくても良いからそこは便利かも?

ArborEditor上だと、右側にちょろっとUIが出てます。これはArborの変数だと出てくるUIみたいなもので、

FlexibleComponentを使うと、ArborEditor上でUIを出すことができます。

Parametersの青色の部分を掴んで、Flexibleの所まで持っていくと、Drag and DropというUIに変わるので、これでコンポーネントをアタッチできます。

ただ、FlexibleComponentでどのコンポーネントかなどを指定していないので、現在の状態だと使えません。なので、CharacterControllerにasかcastをします。

後は、入力の値をCharacterController.Move()のメソッド引き数に渡すだけですキャラクターが移動します。

初ブログ投稿!

このブログについて

初めまして、ゲームの専門学校に通っているハムスターです。普段はUnityを使ってゲーム制作を行ったり、Qiita、noteなどで記事投稿を行っています!

このブログでは、主にUnityやUnity C#などゲーム制作に関する内容を日記的なような形で投稿していく予定ですが...それ以外にも雑多な内容も投稿する予定です!

活動リンクについて

qiita.com

note.com

github.com