Unityで開発する際、構造体(struct)の適切な使用はパフォーマンスとコード品質に大きな影響を与えます。この記事では、Unity C#での構造体活用に関する実践的なアプローチを紹介します。
// 例: 3次元の座標を表す小さな構造体
public struct Position
{
    public float x;
    public float y;
    public float z;
}
// 例: 複雑なゲームオブジェクトデータ
public class EnemyData
{
    public string name;
    public List<Ability> abilities;
    public Dictionary<string, float> stats;
}
// 良い例: 必要最小限のフィールドを持つ構造体
public struct PlayerInput
{
    public float horizontal;
    public float vertical;
    public bool jumpPressed;
}
// 避けるべき例: 大きすぎる構造体
public struct GameState
{
    public string levelName;  // 文字列は参照型
    public List<Enemy> enemies;  // コレクションは参照型
    public Dictionary<int, PlayerData> players;
    // 他多数のデータ...
}
// イミュータブルな構造体の例
public readonly struct GameTime
{
    private readonly float elapsedTime;
    public GameTime(float time)
    {
        elapsedTime = time;
    }
    public float ElapsedTime => elapsedTime;
    // 変更する代わりに新しいインスタンスを返す
    public GameTime AddTime(float deltaTime) => new GameTime(elapsedTime + deltaTime);
}
// 小さな構造体は値渡し
public void MoveCharacter(Vector3 direction)
{
    transform.position += direction * speed * Time.deltaTime;
}
// 大きめの構造体は参照渡し
public void ProcessPhysics(ref PhysicsData physicsData)
{
    // データを処理・更新
}
// 読み取り専用の場合はinキーワード
public float CalculateTrajectory(in ProjectileData data)
{
    // データを読み取るだけの処理
    return /* 計算結果 */;
}
using Unity.Entities;
using Unity.Mathematics;
// DOTSでのコンポーネントとして使用する構造体
public struct TransformComponent : IComponentData
{
    public float3 position;
    public quaternion rotation;
    public float3 scale;
}
using Unity.Jobs;
using Unity.Collections;
using Unity.Burst;
[BurstCompile]
public struct ParticleUpdateJob : IJobParallelFor
{
    public NativeArray<float3> positions;
    public NativeArray<float3> velocities;
    public float deltaTime;
    public void Execute(int index)
    {
        positions[index] += velocities[index] * deltaTime;
    }
}
// ゲーム内のタイルデータを表す構造体
public struct TileData
{
    public byte type;
    public byte elevation;
    public ushort flags;
    // 32ビットに収まるようパッキング
}
// 使用例
public class ChunkManager : MonoBehaviour
{
    private NativeArray<TileData> tiles;
    private const int ChunkSize = 16 * 16;
    void Awake()
    {
        tiles = new NativeArray<TileData>(ChunkSize, Allocator.Persistent);
    }
    void OnDestroy()
    {
        if (tiles.IsCreated)
            tiles.Dispose();
    }
}
// 問題: ボックス化が発生するコード
private List<Vector3> positions = new List<Vector3>();
Dictionary<Vector3, int> positionMap = new Dictionary<Vector3, int>();
// 解決策: ジェネリック型やNativeCollectionの活用
private NativeList<Vector3> positions;
private NativeParallelHashMap<Vector3, int> positionMap;
// 問題: 構造体のフィールドを直接変更できない
public struct Enemy
{
    public Vector3 position;
    public int health;
}
void UpdateEnemy()
{
    Enemy enemy = enemies[0];
    enemy.position += Vector3.forward; // これだけではenemies[0]は更新されない
    enemies[0] = enemy; // 正しい方法: 変更後に再代入する
}
// 問題: 大きな構造体の過剰コピー
public struct LargeData
{
    public Vector3[] points; // 配列は参照なのでOK
    public float[,] heightMap; // 2次元配列も参照
}
// メソッド呼び出しでのコピー問題を解決
public void ProcessData(ref LargeData data)
{
    // refで参照渡しすることでコピーを防止
}
// 構造体とクラスの性能比較例
public class PerformanceTest : MonoBehaviour
{
    private const int Count = 100000;
    void Start()
    {
        TestStructs();
        TestClasses();
    }
    void TestStructs()
    {
        Profiler.BeginSample("Struct Creation");
        var positions = new Vector3[Count];
        for (int i = 0; i < Count; i++)
            positions[i] = new Vector3(i, i, i);
        Profiler.EndSample();
    }
    void TestClasses()
    {
        Profiler.BeginSample("Class Creation");
        var positions = new Vector3Wrapper[Count];
        for (int i = 0; i < Count; i++)
            positions[i] = new Vector3Wrapper(i, i, i);
        Profiler.EndSample();
    }
}
public class Vector3Wrapper
{
    public float x, y, z;
    public Vector3Wrapper(float x, float y, float z)
    {
        this.x = x; this.y = y; this.z = z;
    }
}
Unity 2022.3LTS以降およびC# 9.0以上を想定しています。バージョンによって一部挙動が異なる場合があります。