Unity
https://unity.com/ja/download
https://qiita.com/studio_meowtoon/items/7985bac57b1fa4435e87
Unity6.2で環境を作って、HelloWorldした
https://scrapbox.io/files/68ef3e49b0403ad88845fcfd.png
動きそう
学習用のメモ
The Ultimate Guide to Creating an RPG Game in Unity 6
Unity_udemy_3d_shooting
C#
2D
Unity_ブロック崩し
Unity_2Dシューティング
Unity_2Dアクション
3D
Unity_3d_practice
Unity_3dタワーディフェンス
https://note.com/indie_game_dev/n/n5b47ae3c0c5d
Unityの各用語
UpdateとTime.deltaTime
https://qiita.com/toRisouP/items/930100e25e666494fcd6
Time.deltaTime
直前フレームと今のフレーム間で経過した秒数を返す。下記コードのように移動をスムーズに表現するために使うが、実際は何をしているのか?
code:c#
private Vector3 _velocity = new Vector3(1, 0, 0);
void Update()
transform.position = transform.position + (_velocity * Time.deltaTime);
}
今回はUpdate()を例とした。Update()は1フレームごとに呼ばれる(30fpsなら30回)。距離 = 速度 * 時間。上の式は、キャラクターの1フレームで進む距離はどの程度になるかということで、現在位置 + 速度(_velocity) * 時間(Time.deltaTime)という形で求めている。
deltaTimeを使わずUpdate()で移動処理を実現したとする。速度が5としてUpdate()は1フレームごとに呼ばれるので、
60fps: 1秒で60回呼ばれ、5 * 60 = 300進む
30fps: 1秒で30回呼ばれ、5 * 30 = 150進む
と、描画フレームの違いで処理にも違いが出てくる。
deltaTimeは、フレーム間の時間を数として返してくれるため齟齬を防止できる。進んだ距離を、ベースで設定した速度と時間を掛け合わせて、求めるために使っているだけ。
code:txt
// positionm = positionm + (velocitym/s * times)
// m = m + (m/s * s)
// m = m + m ←両辺で単位が同じになった
FixedUpdate
固定の時間ステップで実行する。物理演算に使う。下記のコードがあったとして、
code:c#
rb.AddForce(Vector3.forward * 10f); // ForceMode.Force(既定)
これはざっくり、10fの力を加えるという意味のコード。
Update()を用いた場合、フレームごとに10fの力を加えることになる。30fps環境だと30回、120fps環境だと120回。FixedUpdate()を使うと固定のステップで実行されるので、環境に依存しない結果が出る。
? Time.deltaTimeを活用すれば、Update()でもある程度安定した結果が得られるのではないか?
Update()は刻み幅が環境依存するので、フレーム落ちやスパイクが発生した場合、deltaTimeが大きく変わることがある。そこで壁との衝突処理などがあった場合すり抜けるケースが発生する。FixedUpdateは内部で値を持っているので、そういったことを防げる。なので可能な限り物理演算にはFixedUpdateを用いよう。
座標
ワールド座標
transform.positionが返す、ゲーム内の絶対的な位置を表す座標
スクリーン座標
画面のピクセル座標。左下が(0, 0), 右上が画面最大の座標。
UIの設定などでよく使う。
ビューポート座標
画面を0から1の範囲で正規化した座標。
要するに左下が(0, 0), 中央が(0.5, 0.5), 右上が(1, 1)である
つまり、以下のように判定することができる
左に飛び出した: x < 0
右に飛び出した: x > 1
下に飛び出した: y < 0
上に飛び出した: y > 1
座標変換のメソッド
WorldToViewportPointなど、ワールド座標をビューポート座標に変換できる
他の変換メソッドも当然ある。ViewportToWorldPoint, WorldToScreenPoint...
Objectの現在のポジションをビューポートとして、画面外に出たらゲームオーバーにしたい処理を書く場合はこんな感じで書ける。
code:c#
Vector3 v = Camera.WorldToViewportPoint(transform.position);
if (v.y < 0f)
hasGameOvered = true;
RectTransformとTransform
UIはRectTransformを持ち、ObjectはTransformを持つ。
Transform
position(ワールド座標), rotation, scaleなど、空間位置を粟原素。
RectTransform
Transformを継承したものだが、UI専用の情報が付与されている。
sizeDelta
UIの幅と高さ。文字だけなら、最初は文字サイズのAuto Sizeに任せても良い。
pivot
中心位置
...
UIの位置をtransform.positionで動かすとCanvas上の位置関係が崩れるので非推奨。基本的には、RectTransform.ancoredPositionやRectTransfrom用のライブラリで動かす。
UIの基準点について
アンカー + anchoredPositionを元に座標が決定する
アンカーがどの位置を基準とするか決定するののが、十字の照準みたいなマーク
こうなら↓、xは0, yはマイナスに下がっている(アンカーが原点。それより下なので)
https://scrapbox.io/files/69284c1c409ac14e55c3bb4f.png
こうなら↓、x軸を+, y軸がマイナスの当たりで設定されていると読める
https://scrapbox.io/files/69284c5a495a3a9d833e05b0.png
Anchor Min / Max
UIの基準となる点と、範囲。親の中央が原点
たとえばmin(0,0), max(1,1)とすると、勝手にストレッチして全画面に被せるようなエフェクトにできる
アンカーの種類
単点アンカー(UIを固定位置に置きたいとき)
ストレッチアンカー(親サイズが変化しても伸びてくれるので、いろんなプラットフォームで出すときに使う)
フルストレッチ
Animator
基本用語
Clip
指定のオブジェクトを時間によってどう変化させるかのデータ(.anim)
Controller
複数のClipを、どのタイミングで切り替えるかのStateMachine
コンポーネント
GameObjectに付与させる、Controllerを再生させるためのパーツ
カメラ(2D)
2DのカメラはZ座標が-10でデフォルトで設定されていることが多い。
code:txt
カメラ(Z=-10)
|
| 距離: 10
↓
プレイヤー(Z=0)
このような形だが、2DのカメラはOrthograpihc設定になっているので、平面的に切り取られている
ViewportToWorldPointで定義するとき
code:c#
float zDistance = transform.position.z - cam.transform.position.z;
Vector3 leftBottom = cam.ViewportToWorldPoint(new Vector3(0f, 0f, zDistance));
Vector3 rightTop = cam.ViewportToWorldPoint(new Vector3(1f, 1f, zDistance));
例えばこれは、画面の左下と右上の座標を取っている
float zDistance = transform.position.z - cam.transform.position.z;
transformをPlayer z=0, カメラ z = -10とする。
cam.ViewportToWorldPointの第三引数には、カメラから見てz方向にいくつ離れているのかを指定する必要がある。
今回の場合、 0 - (-10)で、10離れているということを渡している
⭐つまりPlayerをz=5の場所に置きたいのであれば、この計算式を流用してはいけない。ちゃんと15という値を静的クラスなどにまとめて、渡してやる。
Sorting Layerの注意点
Sorting LayerはSprite Renderer用のソート設定のため、UIなどの制御には使えない。
具体的には、Screen, Overlay, Space, Canvas配下のUIには影響しない。
(Canvasの場合)UIは、基本的に子階層が上のものほど奥に、下のものほど手前に表示される。
なのでシンプルに優先度順に並べてしまえば解決する。
UIが増えてきたら、キャンバスを分けて、Sort Orderという別値を調整する。
code:イメージ
UIRoot
├─ Canvas_BaseHUD (Sort Order: 0)
├─ Canvas_StageCall (Sort Order: 1)
└─ Canvas_Pause (Sort Order: 2) ← 一番上
キー入力を取る
NewInputActionの場合、割り当てたキーバインドを取ることができる。
そちらを使うことで、操作説明画面などでハードコーディングしたテキストを柔軟に変えられる
code:c#
// イメージ
SerializeField private InputActionReference attackAction;
...
string binding = attackAction.action.GetBindingDisplayString();
⭐複数の割り当てがある場合
https://scrapbox.io/files/692aabd6d8c7c91771988437.png
たとえばAttackなら、attackAction.action.GetBindingDisplayString()で取得できる。ただ、Movementは1つの設定にWASD, 上下左右キーが割り当てられている(複合入力: コンポジットComposite)。
code:c#
SerializeField private InputActionReference moveAction;
var bindings = moveAction.bindings;
中身は、こんな感じ
code:txt
0 Movement:2DVector(mode=1)
1 Movement:<Keyboard>/w;Keyboard & Mouse
2 Movement:<Keyboard>/s;Keyboard & Mouse
3 Movement:<Keyboard>/a;Keyboard & Mouse
4 Movement:<Keyboard>/d;Keyboard & Mouse
5 Movement:
6 Movement:2DVector(mode=1)
7 Movement:<Keyboard>/upArrow;Keyboard & Mouse
8 Movement:<Keyboard>/downArrow;Keyboard & Mouse
9 Movement:<Keyboard>/leftArrow;Keyboard & Mouse
10 Movement:<Keyboard>/rightArrow;Keyboard & Mouse
なので、以下のチェックが必要
まずコンポジットが始まったかどうかをチェック
indexを確認して、適宜処理
ちなみにindex=[1]の入力が欲しいなら、こんな感じでindexを指定することで取れる。
code:c#
Debug.Log(
moveAction.GetBindingDisplayString(1) // W
);
StateMachine
プレイヤーや敵は、状況によって異なる挙動を取る。
if分岐だけでは膨大な分岐となり、コードが読めなくなってしまうため、それを防ぐ役割を持つ。
状況ごとに責務を分け、1つの状態で今どう動くべきかだけを担当させる。
基本的な決め事
一度にアクティブにできる状態は1つだけ
各状態には独特の振る舞いがあり、状態を出た後度の状態に映るかのルールを正しく決める
例えばAttackStateは、敵を検知してダメージを与える。アニメが終わればIdleに戻る。
手順
⭐EntityStateの設定
まずEntityState.csの用意。MonoBehaviourは不要。
このクラスは、状態全ての親クラスとなる。
protected StateMachine stateMachineとして、子クラスから遷移できるようにする。
定義した変数に、作成時引数で渡したstateMachineを代入させるようにする(コンストラクタを使う)。
code:c#
public EntityState(StateMachine stateMachine, string stateName)
{
this.stateMachine = stateMachine;
this.statename = statename;
}
状態の入口処理Enter()と出口処理Exit()を定義。ExitはState終了時と、新しいStateを呼ぶ際の処理を書く。
次に、状態中にすることを書くUpdate()を定義。
EntityStateはMonoBehaviourではないのでこれは定時実行されない。なので、Player側などで定時実行する。
code:Player.cs
// イメージ
private void Update() {
stateMachine.currentState.Update(); // 現在のstateのUpdate()処理をここで回す
⭐StateMachineの設定
StateMachine.csの用意。MonoBehaviourは不要。
このクラスの役割は、現在のアクティブな状態への参照を持つこと。しかし勝手に書き換えられては困るので、public EntityState currentState { get; private set; }として所持すればよい。
currentStateにはゲーム開始時のstate設定メソッドと、状態変更時のメソッドを持たせる。
その時に先ほど用意した、入口処理と出口処理を実行しておく。
code:StateMachine.cs
// ゲーム開始時など、初期stateを割り当てるためのメソッド
public void Initialize(EntityState startState)
{
currentState = startState;
currentState.Enter(); // 入口処理
}
// 現状態を変更するためのメソッド
public void ChangeState(EntityState newState)
{
currentState.Exit(); // 出口処理
currentState = newState;
currentState.Enter(); // 新しいStateの入口処理
}
⭐Playerなどから使う
自身でStateMachineを持つ。他のコードでもplayer.stateMachine.currentStateを参照することで、プレイヤーが現在何の情報かを確認することができる。ただし、むやみに変更されても困るので、同じようにpublic StateMachine stateMachine { get; private set; }としておく。
PlayerなどはMonoBehaviourを所持しているので、
Awake()でnew StateMachine()とし、初期Stateを持たせる。
Start()でstateMachine.Initialize(idleState)などとし、初期設定 + 入口処理
Update()でstateMachine.currentState.Update()という流れになる。
code:Player.cs
private void Awake()
{
stateMachine = new StateMachine();
idleState = new PlayerIdleState(stateMachine, "Idle");
}
private void Start()
{
stateMachine.Initialize(idleState); // 初期状態の設定 + 入口処理
}
private void Update()
{
stateMachine.currentState.Update(); // 状態中の処理
}
⭐PlayerIdleStateなど、子要素を作る
EntityStateは単体では使わない、抽象クラスで基本定義する。
そちらを親として子クラスでStateを定義。コンストラクタの対応を行っていく。
Enter()等の入口処理を調整したい場合は都度overrideしていけばよい。
新しい状態を作るときの流れ(たとえば、IdleにMoveを追加する)
Stateのスクリプトを用意。コンストラクタ等設定。
Player側で、新しくそのスクリプトを持てるようにする。
code:Player.cs
private PlayerMoveState moveState;
// Awake
moveState = new PlayerMoveState(stateMachine, "move");
Moveはプレイヤーの入力を受け付け、移動する。つまり状態中の処理Update()で書くようにする。
プレイヤーの入力はPlayerが持っているが、現状まだ情報を所持していない。
EntityStateに取得処理を書くと、別Object(たとえばEnemy)にもEntityStateを割り当てたとき、Playerの情報が不要になる。なので、PlayerStateという要素をさらに作ってしまうのがよい。
code:txt
EntityState
- PlayerState
- PlayerIdleState
- PlayerMoveState
Unity_2Dアクション#692d42c4000000000061c8e4 側で対応したこと
まず、EntityStateに敵味方共通の変数を定義する。rb, co, animなど。
次に、PlayerStateにはプレイヤーを操作するためのplayerを持たせるようにする。そのとき、rb, co, animをplayerが所持していたものをコンストラクタで割り当てる。今後EnemyStateを作るならenemyを持たせて、rb,co,animにenemyが所持しているものを割り当てればよい。
PlayerMoveStateなどでは、Update()中にrbを操作することで移動を実現させる。
NewInputSystemと、EventSystemへの紐づけ
Input Actionsは、キーやマウスの入力を意味のある操作に変換するための定義ファイル。
Action Mapを分けることで、UIを開いている途中はこの操作、通常プレイ中はこの操作、と分割できる。
InputSystemUIInputModuleは、EventSystemにくっついているcomponent。「決定」や「キャンセル」等の処理を、どのInput Setの割当で実行するのかを決める。
UI/NavigateというAction Mapを、EventSystemでmoveとして割り当てる
EventSystemは、今Selectedされている要素が何かという情報を持っている
https://scrapbox.io/files/6942753c6e9685e2d6561b1e.png
画像のケースなら、QuitというButtonのNavigation(Explicit)を見る。
Downに割り当てた項目に、Current Selectionを移す。
この辺をスクリプトで監視しておけば、コード上でやりたいことが実現できる
code:UITitleMenu.cs
private void Update()
{
var es = EventSystem.current;
if (es == null)
return;
// 例えば、この辺りでオブジェクトが変わったかを検知させる
var current = es.currentSelectedGameObject;
if (current == null || current == lastSelectedObject)
return;
// オブジェクトが変化し、ここまで来た場合は特定条件で音を鳴らすとか。
if (!suppressNextSelectSfx)
AudioManager.Instance?.PlayUI(selectClip);
...
ボタンが押されたとか、セレクト状態が解除されたとかも、インターフェースで管理できる。public void OnSelect(BaseEventData eventData) とか。
===============
基礎回り
基本概念
GameObjectとComponent
Objectが入れものとして存在し、Componentを付与して機能拡張を進める。RendererやColliderなどで振る舞いを構成していく。
Transform
位置、回転、拡縮を持つ。
親子関係があるが、子の変換は親の変換を合成した結果にもなる。
インスペクタの値はローカルの値を示す。ワールド座標は親を含めた最終結果となる。
Prefab
同じ構成を複数生成したいときに使う。
共通のPrefabをもとに、差分だけを派生として管理したいときにPrefab Variantを使うと良い。
共通修正が親のPrefabを修正するだけで済む。
SerializeField
privateのままInspector上に表示できて書き換えられる。未設定や参照切れ、依存関係が見えにくくなるのでコメントなどで補完する。
MonoBehavior
LateUpdate
Update後に呼ばれる。カメラ追従などの処理を触るときによく使う。
Unity_2Dアクションではテレポート時の背景ブレやカメラ揺れとかを抑えるためにいじった
イベント購読と解除
OnEnableとOnDisableで管理するのが基本。
Coroutine
一定時間待つ、段階的に進めるなどの時間制御に便利。
Updateでも毎フレーム自前で状態管理することができるが、基本こちらで管理したほうがよい。
ただし、停止時要件や破棄されたときにOnDisableなどで止めるように意識しないと不具合に繋がる。
スクリプト間の初期化依存を減らすためには
Awakeで参照を確定させる
OnEnableで購読を済ませる
Startで初期状態を反映するという流れが好ましい。
2D: 物理 /当たり判定
RBのBodyType
Dynamic: 物理で動く対象に使う。Kinematicはスクリプト手動で動かしたい対象(移動床など)に適する。
Staticは動かない地形や壁に。一番処理としては軽い。
Collider2DとisTrigger, OnCollisionとOnTrigger
IsTriggerがfalseの場合、OnCollision系統が呼ばれる。
壁や床
IsTriggerがTrueの場合、物理反発が起きない。また、OnTrigger系が呼ばれる。
アイテム取得とかダメージ判定はこっち。
LayerとSorting Layer
Layerは物理、Raycastなどの判定に使う。
Sorting LayerはSprite描画順の制御で、判定とは関係ない。
Continuousについて
弾丸など高速で移動する物体が、コライダーを突き抜ける不具合を補うときに使う。
この挙動はトンネリングと呼ぶ
めりこみ、すり抜け対策
Continuousを使う
Raycastで対策。
移動処理に物理を使い、接地判定と速度補正をかけるのもよい。
移動系
Unity_3d_practice#6964b66800000000008e5089
MovePosition
衝突を考慮しつつ位置を移せる。Kinematicなど移動床はこれを使うことが多い。
AddForce
力で動かす、慣性や加速を重視するときに使う
velocity
速度を直接指定する。
プレイヤー移動とか操作感を反映しやすいが、物理感はない。
入力
旧Input
Input.GetKeyなど
入力デバイスの追加やリバインドが難しい。
New Input Systemはこの辺りを抽象化で着て、デバイス差やUI連携が設計しやすい。
Input Actionの構成
Action: ジャンプや攻撃など、意図。
Binding: キーやボタンの割当。
Control Scheme: どのデバイスセットか(マウス, gamepad, キーボードなど)
キーコンフィグ
対話的にBindingを差し替えて、結果をjsonde保存して復元する形になる。作らないと詳細に理解できないかも。
UI時とプレイ時の操作切り替え
Action Mapを分けておけば、メニューを表示するときに片方をDisable, Enableとすれば入力競合を防げる。
UpdateとFixedUpdate
入力をUpdateで取得し、RBを伴う物理反映はFixedUpdateでやるのが安全。
UI周り
CanvasのScreen Space
Overlay: 画面最前面に直接描画される。
Camera: UIを特定のカメラで描画する。
World: 看板のUIなど、ゲーム世界のUIはこれを使おう。
EventSystemの役割
選択中のUI要素の管理や入力のルーティング、イベント発火を担当する。
ちなみにNew Input Systemは、UI Input ModleがActionを受け取り、ES経由でボタンなどにイベントを流す。
ボタンナビゲーションの管理
ボタンごとに、どの方向キーでどこに移動するかを決める。Explicitとして、初期選択等を設定していこう。
解像度対応で意識すること
低解像度でつけるとUIが大きくなったりする。Scale With Screen Size設定などで防止できるので、こまめにチェック。
TextMeshPro
文字の見た目やアウトラインが安定している。描画品質も高い。
フォントを差し替えた時文字がかけたり、WebGLでは重かったりするので覚えておこう。
Animation周り
Animation Controllerについて
State: 再生するアニメのこと
Transition: 状態遷移の条件。
Parameter: 遷移判断に使う入力。
スクリプト側はParameterだけを操作するようにすると、上手く責務が分離できる。
BlendTreeの使いどころ
速度に応じてIdle, Walk, Runを使い分けるなど、連続した値でモーションを切り替えるとき。
Animation Eventトリガーについて
特定のフレームでSEを出したり攻撃判定を出せる。
イベント名の文字列に依存したり、参照がスクリプトから見えづらい。変更時に検知しづらい。
Sprite Rendererのレイヤー設計
Sorting Layerで大枠の描画グループを分けて、Order in Layerで、グループ内の前後関係を決めよう。
パララックス背景とかなら、BackGroundの前、中、後をきめたり。
データ設計
ScriptableObject
データ定義をアセットとして分離する
skill構造とか敵情報、ドロップテーブルを管理するのが一般的。
セーブロード
jsonで管理するのが一般的。作るとわかるかも。
スクリプトとかでも一時データは持たせられるが大変。
パフォーマンス
Instantiate / Destroy
コストが高いので、ガベージコレクションやフレーム落ちの原因に繋がる。ObjectPoolを使おう。
GC Allocを抑えるためには?
Updateで走らせる不要な割り当てを避ける。
文字列の連結とか、そういうのは必要なときにフラグで動かす。
Unity Profiler
どの関数が重いのか、どこで割り当てが出ているかを調査できるパフォーマンス計測ツール。
アセット周り
Addressables
https://orotiyamatano.hatenablog.com/entry/Addressabes1
アセットの管理や配信を効率化するシステム。企業ならDLC配信とかに使う。
ロード画面について
ロード開始時に入力を止めて、プログレスバーを出す。完了後に遷移。
Coroutineやasyncを使って完了を待ち、フェード演出などで遷移時のちらつきを回避すると綺麗に仕上がる。
ビルドについて
Development Build
デバッグしやすいビルド。ログなどが見れるが、リリースするものと挙動差が出るケースがある。
Deep Profilingは詳細な計測ができるがかなり重い。特定箇所について調査するために使うといい。
ビルドタイプで起こりがちな内容
性能差はもちろん、入力など。WebGLは結構制約が大きかったり、メモリ不足が懸念になることが多い。
バグ再現が難しいときの切り分け方
再現条件を出来るだけ固定して、ログを出す。
フレームレート制限や負荷をかけて再現率を上げる。
入力や乱数のシードを固定するなど、少しずつ進めていこう。
3D周り
Quaternion
回転を安定して表現できる。Slerpによる補間処理もかなり自然に動くので、回転表現は基本的にこれ。
RaycastとCamera
画面上でクリックした箇所を取りたいとする。その位置はスクリーン座標なのでCamera.ScreenPointToRayなどでワールドへのRayに変換することで、マウスの位置へのオブジェクトを取ったりできる。
Transform.forward
そのObjectの前方向ベクトル。移動や射撃は方向ベクトルで扱おう。
その他
UnityWebRequest
URLにつないで、API通信などができる。ランキングとかもWebで出せちゃうかも。
できるっぽい。Unityで投げてLaravelで受け取ってDBに保管するイメージ。
todo
よく使う設計を自分なりにまとめる
クールタイム
攻撃被弾時の光る処理とか
dotween
===============
遭遇エラーのまとめ
アニメーションのループが終わらない
アニメーションにイベントを付与し忘れていないか
https://scrapbox.io/files/68f83f8b420e13ebe0651c66.png
ループタイムのフラグは適切だろうか
https://scrapbox.io/files/68f83fd1d228159e98bb0354.png
タイルマップを敷いた時だけ挙動がおかしい
コライダーのマージが悪さをしている可能性がある。
Section5のClean up & Projectを参考にしてみる
Sprite EditorのSliceが押せず、ドット絵の調整ができない
https://pg.kdtk.net/2143#google_vignette
https://scrapbox.io/files/68fdd7f9f7836a8122f50946.png
Sprite Mode がMultipleでないと押せない。
Skeletonに背中からダメージを与えても振り向かない
振り向き処理のスクリプトを割り当てていなかった
Entity_Healthを、Enemy_Healthに置き換えたらいけた
エディタ系のエラー
code:txt
NullReferenceException: Object reference not set to an instance of an object
UnityEditor.Graphs.Edge.WakeUp
単純に、Unity自体を再起動すると治ることが多い
NullReferenceExceptionのエラー
code:error
NullReferenceException: Object reference not set to an instance of an object
UI_SkillToopTip.ShowToolTip (System.Boolean show, UnityEngine.RectTransform targetRect, UI_TreeNode node) (at Assets/Scripts/UI/UI_SkillToopTip.cs:53)
UI_TreeNode.OnPointerEnter (UnityEngine.EventSystems.PointerEventData eventData) (at Assets/Scripts/UI/UI_TreeNode.cs:101)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerEnterHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at ./Library/PackageCache/com.unity.ugui@27299721f2fd/Runtime/UGUI/EventSystem/ExecuteEvents.cs:29)
UnityEngine.EventSystems.ExecuteEvents.ExecuteT (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1T1 functor) (at ./Library/PackageCache/com.unity.ugui@27299721f2fd/Runtime/UGUI/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at ./Library/PackageCache/com.unity.ugui@27299721f2fd/Runtime/UGUI/EventSystem/EventSystem.cs:530)
変数名を変えた後に出るケースがほとんど
今回のケースだと、
code:c#
SerializeField private TextMeshProUGUI skillName;
SerializeField private TextMeshProUGUI skillDescription;
SerializeField private TextMeshProUGUI skillRequirements; // <- typoがあったので直した
というケースで、修正後コンパイルすると、以下のように割り当てが消えている
なので、自分で紐づけるデータを再度設定しなければならないので注意。
https://scrapbox.io/files/69105482231676dfe0e9cfc9.png
配置したボタンの反応がない
SortingLayerが背景などより低い可能性が高い。