うにてぃブログ

主にUnityとC#に関する記事を書いていきます

【Unity】URPにおける背景画像の取得方法:Shader GraphのSceneColorノードの活用

以前は背景画像を描画する際にGrabtextureを使用していましたが、

URP(Universal Render Pipeline)ではその機能が削除されたため、代替手段が必要です。

今回は、その代替手段の1つであるShader GraphのSceneColorノードを使用した方法を説明します。

SceneColor Node

「SceneColorノードは、画面上の指定された位置に対応する色情報を取得するために、UV座標(正規化されたスクリーン座標)を利用します。このノードを使用することで、現在のカメラのカラーバッファにアクセスできます。」

SceneColorノードは、Shader Graphで背景画像を利用する際に重要な役割を果たします。このノードを使うことで、画面上の特定の位置に対応する色情報を取得し、それを利用してさまざまなエフェクトや処理を行うことができます。 と説明があるとおり、現在のカラーを取得することができます

ただし、次の条件を満たしていないと正しく動作しません:

SurfaceTypeがTransparent

UniversalRenderPiplineAsset のOpaqueTextureが有効

もし有効でない場合は以下のように灰色で表示されるのみです

条件を満たした場合、描画前の画像を取得できます。以下の画像は、グレースケールに変換する処理を行っています。

今回は以下のようにグレースケールに変換する処理のため灰色に表示されています

【Unity】ドラッグ操作によるオブジェクトの回転:Space.Worldを考慮した正確な方法

オブジェクトをドラッグした方向に回転させるには、Transform.Rotateを使用します。

以下の例では、ドラッグの方向にオブジェクトを回転させる方法を示しています。

using UnityEngine;
using UnityEngine.EventSystems;

public class RotationTest : MonoBehaviour, IDragHandler
{
    [SerializeField]
    private Transform _target;
    [SerializeField]
    private float _speed = 1f;

    public void OnDrag(PointerEventData eventData)
    {
        _target.Rotate(Vector3.up, eventData.delta.x * -_speed);
        _target.Rotate(Vector3.right, eventData.delta.y * _speed);
    }
}

しかし、一度回転させた後にさらに回転させると、回転後の軸を考慮していないため、奇妙な回転が発生します。

これを正しく考慮するには、次のようにSpace.Worldを使用します。

        _target.Rotate(Vector3.up, eventData.delta.x * -_speed, Space.World);
        _target.Rotate(Vector3.right, eventData.delta.y * _speed, Space.World);

これにより、回転が正しく行われます。

【Unity】ScrollRect の子オブジェクトがドラッグ操作時に誤ったイベントが発生する問題

ScrollRectの子供に配置されたオブジェクトが、IPointerDownHandlerを持つとドラッグ操作時に誤って呼び出される問題があります。

例えば、以下のようなコンポーネントがScrollRectの子供オブジェクトに存在する場合、

public class EventHandleTest : MonoBehaviour, IPointerUpHandler
{
    public void OnPointerUp(PointerEventData eventData)
    {
        Debug.Log("OnPointerUp");
    }
}

クリックしてドラッグを開始すると、OnPointerUpが誤って呼び出されてしまいます。

しかし、IDragHandlerを実装した場合、正しくクリックを離したタイミングでOnPointerUpが呼び出されるようになります。

public class EventHandleTest : MonoBehaviour, IPointerUpHandler, IDragHandler
{
    public void OnPointerUp(PointerEventData eventData)
    {
        Debug.Log("OnPointerUp");
    }

    public void OnDrag(PointerEventData eventData)
    {
        Debug.Log("OnDrag");
    }
}

このように、IDragHandlerを実装することで、正確なイベントの発生タイミングを制御できます。

【Unity】uGUI での Raycast 時に親子関係と GraphicRaycaster の影響によるイベント挙動の解説

同じ階層にある子供オブジェクトに対して、親にGraphicRaycasterが存在しない場合、Rayが下のオブジェクトではなく上のオブジェクトに当たる問題が発生します。

例えば、以下のヒエラルキーが考えられます。ここで、GraphicRaycasterが「Node Up」と「Node Down」にのみ付いている場合、イベントは「Node Up」側のオブジェクトで発生します。しかし、「Root」にもGraphicRaycasterが存在する場合、イベントは「Node Down」のオブジェクトで発生します。なぜ親の有無で異なるオブジェクトがイベントを受け取るのでしょうか?

この問題を解決するため、uGUIでRaycastを行った後に取得されるデータを確認します。

var hits = new List<RaycastResult>();
UnityEngine.EventSystems.EventSystem.current.RaycastAll(eventData, hits);
foreach (var hit in hits)
{
    Debug.Log(hit.ToString());
}

以下は取得されるデータの一部です。

上にあるオブジェクトの場合:

Name: Image (UnityEngine.GameObject)
module: Name: Node Up (UnityEngine.GameObject)
eventCamera: 
sortOrderPriority: 100
renderOrderPriority: 0
distance: 0
index: 0
depth: 0

下にあるオブジェクトの場合:

Name: Image (UnityEngine.GameObject)
module: Name: Node Down (UnityEngine.GameObject)
eventCamera: 
sortOrderPriority: 100
renderOrderPriority: 0
distance: 0
index: 1
depth: 1

実際にRayを飛ばしてソートする処理を見てみましょう。

uGUIのRaycastを行った後にソートされる処理は次のようになっています。

private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
{
    if (lhs.module != rhs.module)
    {
        var lhsEventCamera = lhs.module.eventCamera;
        var rhsEventCamera = rhs.module.eventCamera;
        if (lhsEventCamera != null && rhsEventCamera != null && lhsEventCamera.depth != rhsEventCamera.depth)
        {
            // 標準的なcompareToを逆転させる必要があります
            if (lhsEventCamera.depth < rhsEventCamera.depth)
                return 1;
            if (lhsEventCamera.depth == rhsEventCamera.depth)
                return 0;

            return -1;
        }

        if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
            return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);

        if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
            return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
    }

    // Renderer sorting
    if (lhs.sortingLayer != rhs.sortingLayer)
    {
        // レイヤー値を使用して、レイヤーの相対順序を適切に比較します。
        var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
        var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
        return rid.CompareTo(lid);
    }

    if (lhs.sortingOrder != rhs.sortingOrder)
        return rhs.sortingOrder.CompareTo(lhs.sortingOrder);

    // 2つのRaycast結果が同じルートキャンバスでなければ、深度を比較することは意味があります。
    if (lhs.depth != rhs.depth && lhs.module.rootRaycaster == rhs.module.rootRaycaster)
        return rhs.depth.CompareTo(lhs.depth);

    if (lhs.distance != rhs.distance)
        return lhs.distance.CompareTo(rhs.distance);

    return lhs.index.CompareTo(rhs.index);
}

親の GraphicRaycaster が同じ場合以下のifのソートになります、そのため depth が大きいほど優先順位が高くなります

if (lhs.depth != rhs.depth && lhs.module.rootRaycaster == rhs.module.rootRaycaster)


しかし、親のGraphicRaycasterが異なる場合、最後まで条件判定を通過せず、indexが小さいほど優先順位が高くなります。

この例では、上のオブジェクトのindexとdepthが0であり、下のオブジェクトのindexとdepthが1であるため、親のGraphicRaycasterの有無によってソート順が異なり、この結果が生じたことがわかります。

【Unity】アセット管理を効率化するためのガイド: AssetPostprocessorとAssetModificationProcessorの活用方法

Unityの開発過程において、アセットのインポートパイプラインをカスタマイズすることは、効率的なアセット管理に不可欠です。このカスタマイズを行うための主なツールは「AssetPostprocessor」と「AssetModificationProcessor」です。

AssetPostprocessorの概要

AssetPostprocessorは、アセットがインポートされた際に特定の処理を実行するために用いられます。これにより、アセットのインポートプロセスをフックし、必要な操作を自動化することができます。

主なメソッド

この表は、AssetPostprocessorを使用して、特定のアセットタイプのインポート処理をカスタマイズする際に役立ちます。それぞれのメソッドがどのようなタイミングで呼び出されるかを理解することで、より効果的なアセット管理が可能になります。

メソッド名 説明
OnPostprocessAllAssets すべてのアセットに共通して行う処理を定義する
OnAssignMaterialModel ソースマテリアルの設定を行う
OnPostprocessAssetbundleNameChanged アセットが他のアセットバンドルに指定された時に呼び出される
OnPostprocessAudio オーディオクリップのインポート完了時に通知される
OnPostprocessCubemap キューブマップテクスチャのインポート完了直前に通知される
OnPostprocessGameObjectWithUserProperties ユーザープロパティが付けられたゲームオブジェクトのインポート時に呼び出される
OnPostprocessMaterial マテリアルアセットのインポート完了時に通知される
OnPostprocessModel モデルのインポート完了時に通知される
OnPostprocessSpeedTree SpeedTreeアセットのインポート完了時に通知される
OnPostprocessSprites スプライトテクスチャのインポート完了時に通知される
OnPostprocessTexture テクスチャのインポート完了直前に通知される
OnPreprocessAnimation アニメーションのインポート直前に通知される
OnPreprocessAsset 任意のアセットのインポート直前に通知される
OnPreprocessAudio オーディオクリップのインポート直前に通知される
OnPreprocessModel モデルのインポート直前に通知される
OnPreprocessSpeedTree SpeedTreeアセットのインポート直前に通知される
OnPreprocessTexture テクスチャインポーター実行前に通知される

サンプル

using UnityEngine;
using UnityEditor;

public class MyAssetPostprocessor : AssetPostprocessor
{
    void OnPreprocessTexture()
    {
        if (assetPath.Contains("SomeSpecificFolder"))
        {
            TextureImporter textureImporter = (TextureImporter)assetImporter;
            textureImporter.textureCompression = TextureImporterCompression.Uncompressed;
        }
    }

    static void OnPostprocessAllAssets(
        string[] importedAssets, string[] deletedAssets, 
        string[] movedAssets, string[] movedFromAssetPaths)
    {
        foreach (string str in importedAssets)
        {
            Debug.Log("Asset Imported: " + str);
        }
    }
}

このサンプルでは、特定のフォルダ内のテクスチャの圧縮設定を変更し、インポートされたアセットのパスをログに出力しています。

AssetModificationProcessorの概要

Unityのエディタ内でのアセットの管理と操作をより細かくコントロールするためには、「AssetModificationProcessor」が重要な役割を果たします。このクラスは、アセットの作成、削除、移動、保存などのイベントに対してカスタム処理を実装することができます。これにより、開発プロセス中にアセットに対する特定の操作が行われる際に、追加の処理や制約を設定することが可能となります。

主なメソッド

メソッド名 説明
OnWillCreateAsset アセットが作成される直前に呼び出される
OnWillDeleteAsset アセットが削除される直前に呼び出される
OnWillMoveAsset アセットが移動される直前に呼び出される
OnWillSaveAssets アセットが保存される直前に呼び出される
IsOpenForEdit アセットが編集可能かどうかを判断する
CanOpenForEdit アセットを編集するために開くことができるかどうかを判断する
StartAssetEditing アセットの編集セッションを開始する
EndAssetEditing アセットの編集セッションを終了する

AssetModificationProcessorは、Unityエディタ内でのアセットの変更や保存のプロセスをカスタマイズするために使用されます。これらのメソッドを使用して、特定のアセットの操作に特別な処理を追加したり、特定の条件下での操作を制限したりすることができます。例えば、アセットが削除される前に特定のクリーンアップ処理を実行する、または特定のアセットの編集を制限するなどのカスタマイズが可能です。

サンプル

using UnityEngine;
using UnityEditor;

public class MyAssetModificationProcessor : UnityEditor.AssetModificationProcessor
{
    public static string[] OnWillSaveAssets(string[] paths)
    {
        Debug.Log("Assets being saved:");
        foreach (string path in paths)
        {
            Debug.Log(path);
        }
        // ファイル保存の前に行いたい処理をここに記述
        return paths;
    }

    public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions option)
    {
        Debug.Log("Asset being deleted: " + assetPath);
        // アセット削除の前に行いたい処理をここに記述
        return AssetDeleteResult.DidNotDelete;
    }
}

このサンプルでは、アセットが保存または削除される前に、そのパスをログに出力しています。また、任意の追加処理を行うことも可能です。

リファレンス

UnityEditor.AssetPostprocessor - Unity スクリプトリファレンス
AssetModificationProcessor - Unity スクリプトリファレンス

【Unity】UnityのMathfクラス: Round, Ceil, Floorの理解と実用例

はじめに

Unityでのゲーム開発では、数値の丸め処理がしばしば必要となります。MathfクラスのRound, Ceil, Floorメソッドは、これらの処理に不可欠です。この記事では、これらのメソッドの使い方と実例を紹介し、初心者から上級者まで理解しやすく解説します。

ToIntが最後についているメソッドは、結果を整数(int型)で返します。

Mathf.Round

  • 概要: Mathf.Round(float f)メソッドは、最も近い整数に値を丸めます。もし丸める値が中間点(例: 3.5)の場合、偶数に丸められます(例: 4)。
  • 実例:
    • 入力: Mathf.Round(2.3f) → 出力: 2
    • 入力: Mathf.Round(3.5f) → 出力: 4(中間点は偶数に丸められる)
    • 入力: Mathf.Round(-2.7f) → 出力: -3

Mathf.Ceil

  • 概要: Mathf.Ceil(float f)メソッドは、与えられた値を超える最小の整数に丸めます。
  • 実例:
    • 入力: Mathf.Ceil(2.3f) → 出力: 3
    • 入力: Mathf.Ceil(-2.7f) → 出力: -2(負の値ではより大きい整数に丸められる)

Mathf.Floor

  • 概要: Mathf.Floor(float f)メソッドは、与えられた値以下の最大の整数に丸めます。
  • 実例:
    • 入力: Mathf.Floor(2.8f) → 出力: 2
    • 入力: Mathf.Floor(-2.8f) → 出力: -3(負の値ではより小さい整数に丸められる)

この表は、UnityのMathfクラスにおけるRound, Ceil, FloorおよびそれらのToIntバージョンのメソッドが異なる数値にどのように作用するかを示しています。特にMathf.Roundは中間値(.5)を最も近い偶数に丸めることに注意してください。。

入力値 Mathf.Round Mathf.Ceil Mathf.Floor
-3.5 -4 -3 -4
-2.8 -3 -2 -3
-2.5 -2 -2 -3
-2.3 -2 -2 -3
2.3 2 3 2
2.5 2 3 2
2.8 3 3 2
3.5 4 4 3

まとめ

これらのメソッドは、ゲーム内のスコア計算、位置調整、時間管理など、さまざまな場面で活用できます。それぞれのメソッドの違いを理解し、適切な場面で使用することが重要です。Unity開発におけるこれらの基本的な数学関数の理解と適用で、より洗練されたゲームを作成しましょう。

【Unity】UnityのUGUIイベントハンドラインターフェースまとめ

はじめに

Unityのユーザーインターフェース(UI)構築において、イベントハンドラーは重要な役割を果たします。UGUI(Unity GUI)システムでは、さまざまなユーザー操作に応じてイベントを処理するために、多くのインターフェースが提供されています。この記事では、これらのイベントハンドラーについて詳しく解説します。

IBeginDragHandler

IBeginDragHandlerは、ドラッグ操作が開始される前にBaseInputModuleによって呼び出されるインターフェースです。これを使用することで、ドラッグ開始時の動作をカスタマイズできます。

ICancelHandler

ICancelHandlerは、キャンセルイベントが発生したときに呼び出されます。これは、ユーザーが操作を中断した場合などに使用されることがあります。

IDeselectHandler

IDeselectHandlerは、UI要素の選択が解除されたときに呼び出されます。

IDragHandler

IDragHandlerは、ドラッグ操作中に連続して呼び出されます。ドラッグ中のオブジェクトの挙動を制御するのに適しています。

IDropHandler

IDropHandlerは、ドラッグアンドドロップ操作が完了したとき、つまりドロップされたときに呼び出されます。

IEndDragHandler

IEndDragHandlerは、ドラッグ操作が終了したときに呼び出されます。

IInitializePotentialDragHandler

IInitializePotentialDragHandlerは、ドラッグが可能な状態になる前に呼び出されます。

IMoveHandler

IMoveHandlerは、キーボードやゲームパッドによるナビゲーション操作が行われたときに呼び出されます。

IPointerClickHandler

IPointerClickHandlerは、マウスクリックやタップなどのポインタークリックイベントに対応します。

IPointerDownHandler

IPointerDownHandlerは、ポインター(マウスやタッチ)がUI要素上で押されたときに呼び出されます。

IPointerEnterHandler

IPointerEnterHandlerは、ポインターがUI要素の上に入ったときに呼び出されます。

IPointerExitHandler

IPointerExitHandlerは、ポインターがUI要素から出たときに呼び出されます。

IPointerUpHandler

IPointerUpHandlerは、ポインターがUI要素上で放された(上げられた)ときに呼び出されます。

IScrollHandler

IScrollHandlerは、マウスホイールのスクロールやタッチパッドジェスチャーなどのスクロールイベントに対応します。

ISelectHandler

ISelectHandlerは、UI要素が選択されたときに呼び出されます。

ISubmitHandler

ISubmitHandlerは、UI要素が「送信」操作(例えば、ボタンが押されたとき)を受けたときに呼び出されます。

IUpdateSelectedHandler

IUpdateSelectedHandlerは、UI要素が選択されている間、連続して呼び出されます。