スキル一覧に戻る

game-development

Deefunxion / pixel-flick-frenzy

0🍴 0📅 2026年1月18日

Game development with Unity, Unreal Engine, and Godot. Use when building games, implementing game mechanics, physics, AI, or working with game engines.

SKILL.md

---
name: game-development
description: Game development with Unity, Unreal Engine, and Godot. Use when building games, implementing game mechanics, physics, AI, or working with game engines.
---

# Game Development

Comprehensive guide for building games across major engines and platforms.

## Engine Comparison

| Engine | Language | Best For | Platforms |
|--------|----------|----------|-----------|
| **Unity** | C# | Mobile, indie, VR/AR | All |
| **Unreal** | C++, Blueprints | AAA, realistic graphics | All |
| **Godot** | GDScript, C# | 2D, indie, open source | All |

---

## Unity (C#)

### Project Structure

```
Assets/
├── Scripts/
│   ├── Player/
│   │   ├── PlayerController.cs
│   │   └── PlayerInput.cs
│   ├── Enemies/
│   ├── Systems/
│   └── Utils/
├── Prefabs/
├── Scenes/
├── Materials/
├── Animations/
└── Resources/
```

### Player Controller

```csharp
using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(CharacterController))]
public class PlayerController : MonoBehaviour
{
    [Header("Movement")]
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float sprintMultiplier = 1.5f;
    [SerializeField] private float jumpHeight = 2f;
    [SerializeField] private float gravity = -9.81f;

    [Header("Look")]
    [SerializeField] private float lookSensitivity = 2f;
    [SerializeField] private float maxLookAngle = 80f;

    private CharacterController _controller;
    private Vector2 _moveInput;
    private Vector2 _lookInput;
    private Vector3 _velocity;
    private float _xRotation;
    private bool _isSprinting;

    private void Awake()
    {
        _controller = GetComponent<CharacterController>();
        Cursor.lockState = CursorLockMode.Locked;
    }

    private void Update()
    {
        HandleMovement();
        HandleLook();
        ApplyGravity();
    }

    private void HandleMovement()
    {
        float speed = _isSprinting ? moveSpeed * sprintMultiplier : moveSpeed;
        Vector3 move = transform.right * _moveInput.x + transform.forward * _moveInput.y;
        _controller.Move(move * speed * Time.deltaTime);
    }

    private void HandleLook()
    {
        float mouseX = _lookInput.x * lookSensitivity * Time.deltaTime;
        float mouseY = _lookInput.y * lookSensitivity * Time.deltaTime;

        _xRotation -= mouseY;
        _xRotation = Mathf.Clamp(_xRotation, -maxLookAngle, maxLookAngle);

        Camera.main.transform.localRotation = Quaternion.Euler(_xRotation, 0f, 0f);
        transform.Rotate(Vector3.up * mouseX);
    }

    private void ApplyGravity()
    {
        if (_controller.isGrounded && _velocity.y < 0)
        {
            _velocity.y = -2f;
        }

        _velocity.y += gravity * Time.deltaTime;
        _controller.Move(_velocity * Time.deltaTime);
    }

    // Input System callbacks
    public void OnMove(InputAction.CallbackContext context) =>
        _moveInput = context.ReadValue<Vector2>();

    public void OnLook(InputAction.CallbackContext context) =>
        _lookInput = context.ReadValue<Vector2>();

    public void OnJump(InputAction.CallbackContext context)
    {
        if (context.performed && _controller.isGrounded)
        {
            _velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
        }
    }

    public void OnSprint(InputAction.CallbackContext context) =>
        _isSprinting = context.performed;
}
```

### State Machine

```csharp
public interface IState
{
    void Enter();
    void Update();
    void Exit();
}

public class StateMachine
{
    private IState _currentState;

    public void ChangeState(IState newState)
    {
        _currentState?.Exit();
        _currentState = newState;
        _currentState.Enter();
    }

    public void Update() => _currentState?.Update();
}

// Example: Enemy AI States
public class IdleState : IState
{
    private readonly EnemyAI _enemy;

    public IdleState(EnemyAI enemy) => _enemy = enemy;

    public void Enter() => _enemy.Animator.SetTrigger("Idle");

    public void Update()
    {
        if (_enemy.CanSeePlayer())
        {
            _enemy.StateMachine.ChangeState(new ChaseState(_enemy));
        }
    }

    public void Exit() { }
}

public class ChaseState : IState
{
    private readonly EnemyAI _enemy;

    public ChaseState(EnemyAI enemy) => _enemy = enemy;

    public void Enter() => _enemy.Animator.SetTrigger("Run");

    public void Update()
    {
        _enemy.NavAgent.SetDestination(_enemy.Player.position);

        if (_enemy.InAttackRange())
        {
            _enemy.StateMachine.ChangeState(new AttackState(_enemy));
        }
        else if (!_enemy.CanSeePlayer())
        {
            _enemy.StateMachine.ChangeState(new IdleState(_enemy));
        }
    }

    public void Exit() { }
}
```

### Object Pooling

```csharp
public class ObjectPool<T> where T : Component
{
    private readonly T _prefab;
    private readonly Transform _parent;
    private readonly Queue<T> _pool = new();

    public ObjectPool(T prefab, int initialSize, Transform parent = null)
    {
        _prefab = prefab;
        _parent = parent;

        for (int i = 0; i < initialSize; i++)
        {
            CreateInstance();
        }
    }

    public T Get()
    {
        if (_pool.Count == 0) CreateInstance();

        T obj = _pool.Dequeue();
        obj.gameObject.SetActive(true);
        return obj;
    }

    public void Return(T obj)
    {
        obj.gameObject.SetActive(false);
        _pool.Enqueue(obj);
    }

    private void CreateInstance()
    {
        T obj = Object.Instantiate(_prefab, _parent);
        obj.gameObject.SetActive(false);
        _pool.Enqueue(obj);
    }
}
```

---

## Unreal Engine (C++)

### Actor Component

```cpp
// PlayerMovementComponent.h
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "PlayerMovementComponent.generated.h"

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class MYGAME_API UPlayerMovementComponent : public UActorComponent
{
    GENERATED_BODY()

public:
    UPlayerMovementComponent();

    virtual void TickComponent(float DeltaTime, ELevelTick TickType,
        FActorComponentTickFunction* ThisTickFunction) override;

    UFUNCTION(BlueprintCallable, Category = "Movement")
    void Move(FVector2D Input);

    UFUNCTION(BlueprintCallable, Category = "Movement")
    void Jump();

protected:
    virtual void BeginPlay() override;

private:
    UPROPERTY(EditAnywhere, Category = "Movement")
    float MoveSpeed = 600.0f;

    UPROPERTY(EditAnywhere, Category = "Movement")
    float JumpForce = 400.0f;

    UPROPERTY()
    class UCharacterMovementComponent* MovementComponent;
};

// PlayerMovementComponent.cpp
#include "PlayerMovementComponent.h"
#include "GameFramework/CharacterMovementComponent.h"

UPlayerMovementComponent::UPlayerMovementComponent()
{
    PrimaryComponentTick.bCanEverTick = true;
}

void UPlayerMovementComponent::BeginPlay()
{
    Super::BeginPlay();

    if (ACharacter* Owner = Cast<ACharacter>(GetOwner()))
    {
        MovementComponent = Owner->GetCharacterMovement();
    }
}

void UPlayerMovementComponent::Move(FVector2D Input)
{
    if (!MovementComponent) return;

    FVector Forward = GetOwner()->GetActorForwardVector();
    FVector Right = GetOwner()->GetActorRightVector();

    FVector Direction = (Forward * Input.Y + Right * Input.X).GetSafeNormal();
    MovementComponent->AddInputVector(Direction * MoveSpeed);
}

void UPlayerMovementComponent::Jump()
{
    if (ACharacter* Character = Cast<ACharacter>(GetOwner()))
    {
        Character->Jump();
    }
}
```

### Gameplay Ability System (GAS)

```cpp
// MyGameplayAbility.h
#pragma once

#include "Abilities/GameplayAbility.h"
#include "MyGameplayAbility.generated.h"

UCLASS()
class MYGAME_API UMyGameplayAbility : public UGameplayAbility
{
    GENERATED_BODY()

public:
    UMyGameplayAbility();

    virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        const FGameplayEventData* TriggerEventData) override;

    virtual void EndAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        bool bReplicateEndAbility, bool bWasCancelled) override;

protected:
    UPROPERTY(EditDefaultsOnly, Category = "Ability")
    float Damage = 50.0f;

    UPROPERTY(EditDefaultsOnly, Category = "Ability")
    TSubclassOf<UGameplayEffect> DamageEffect;
};
```

---

## Godot (GDScript)

### Player Controller

```gdscript
extends CharacterBody3D

@export var move_speed: float = 5.0
@export var jump_velocity: float = 4.5
@export var mouse_sensitivity: float = 0.002

var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")

@onready var camera: Camera3D = $Camera3D

func _ready() -> void:
    Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func _input(event: InputEvent) -> void:
    if event is InputEventMouseMotion:
        rotate_y(-event.relative.x * mouse_sensitivity)
        camera.rotate_x(-event.relative.y * mouse_sensitivity)
        camera.rotation.x = clamp(camera.rotation.x, -PI/2, PI/2)

func _physics_process(delta: float) -> void:
    # Gravity
    if not is_on_floor():
        velocity.y -= gravity * delta

    # Jump
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = jump_velocity

    # Movement
    var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
    var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()

    if direction:
        velocity.x = direction.x * move_speed
        velocity.z = direction.z * move_speed
    else:
        velocity.x = move_toward(velocity.x, 0, move_speed)
        velocity.z = move_toward(velocity.z, 0, move_speed)

    move_and_slide()
```

### State Machine (GDScript)

```gdscript
# state_machine.gd
class_name StateMachine
extends Node

@export var initial_state: State

var current_state: State
var states: Dictionary = {}

func _ready() -> void:
    for child in get_children():
        if child is State:
            states[child.name.to_lower()] = child
            child.transitioned.connect(_on_child_transitioned)

    if initial_state:
        initial_state.enter()
        current_state = initial_state

func _process(delta: float) -> void:
    if current_state:
        current_state.update(delta)

func _physics_process(delta: float) -> void:
    if current_state:
        current_state.physics_update(delta)

func _on_child_transitioned(state: State, new_state_name: String) -> void:
    if state != current_state:
        return

    var new_state: State = states.get(new_state_name.to_lower())
    if not new_state:
        return

    current_state.exit()
    new_state.enter()
    current_state = new_state

# state.gd
class_name State
extends Node

signal transitioned(state: State, new_state_name: String)

func enter() -> void:
    pass

func exit() -> void:
    pass

func update(_delta: float) -> void:
    pass

func physics_update(_delta: float) -> void:
    pass
```

---

## Game Patterns

### Entity Component System (ECS)

```csharp
// Unity DOTS example
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;

public struct MoveSpeed : IComponentData
{
    public float Value;
}

public partial class MovementSystem : SystemBase
{
    protected override void OnUpdate()
    {
        float deltaTime = SystemAPI.Time.DeltaTime;

        Entities.ForEach((ref LocalTransform transform, in MoveSpeed speed) =>
        {
            transform.Position += new float3(0, 0, speed.Value * deltaTime);
        }).ScheduleParallel();
    }
}
```

### Event System

```csharp
public static class GameEvents
{
    public static event Action<int> OnScoreChanged;
    public static event Action<float> OnHealthChanged;
    public static event Action OnGameOver;

    public static void ScoreChanged(int newScore) => OnScoreChanged?.Invoke(newScore);
    public static void HealthChanged(float newHealth) => OnHealthChanged?.Invoke(newHealth);
    public static void GameOver() => OnGameOver?.Invoke();
}

// Usage
public class UIManager : MonoBehaviour
{
    private void OnEnable()
    {
        GameEvents.OnScoreChanged += UpdateScoreUI;
        GameEvents.OnHealthChanged += UpdateHealthUI;
    }

    private void OnDisable()
    {
        GameEvents.OnScoreChanged -= UpdateScoreUI;
        GameEvents.OnHealthChanged -= UpdateHealthUI;
    }
}
```

### Save System

```csharp
[Serializable]
public class SaveData
{
    public int Level;
    public float PlayTime;
    public Vector3Serializable PlayerPosition;
    public List<string> UnlockedAchievements;
}

public static class SaveSystem
{
    private static readonly string SavePath =
        Path.Combine(Application.persistentDataPath, "save.json");

    public static void Save(SaveData data)
    {
        string json = JsonUtility.ToJson(data, true);
        File.WriteAllText(SavePath, json);
    }

    public static SaveData Load()
    {
        if (!File.Exists(SavePath)) return new SaveData();

        string json = File.ReadAllText(SavePath);
        return JsonUtility.FromJson<SaveData>(json);
    }
}
```

---

## Performance Optimization

### LOD (Level of Detail)

```csharp
// Unity LOD Group setup
[RequireComponent(typeof(LODGroup))]
public class LODSetup : MonoBehaviour
{
    public Renderer[] lodRenderers;
    public float[] screenRelativeTransitions = { 0.5f, 0.2f, 0.01f };

    void Start()
    {
        LODGroup lodGroup = GetComponent<LODGroup>();
        LOD[] lods = new LOD[lodRenderers.Length];

        for (int i = 0; i < lodRenderers.Length; i++)
        {
            lods[i] = new LOD(screenRelativeTransitions[i], new[] { lodRenderers[i] });
        }

        lodGroup.SetLODs(lods);
        lodGroup.RecalculateBounds();
    }
}
```

### Spatial Partitioning

```csharp
public class QuadTree<T> where T : class
{
    private readonly int _maxObjects = 10;
    private readonly int _maxLevels = 5;
    private readonly int _level;
    private readonly List<(Rect bounds, T obj)> _objects = new();
    private readonly Rect _bounds;
    private QuadTree<T>[] _nodes;

    public QuadTree(int level, Rect bounds)
    {
        _level = level;
        _bounds = bounds;
    }

    public void Insert(Rect objBounds, T obj)
    {
        if (_nodes != null)
        {
            int index = GetIndex(objBounds);
            if (index != -1)
            {
                _nodes[index].Insert(objBounds, obj);
                return;
            }
        }

        _objects.Add((objBounds, obj));

        if (_objects.Count > _maxObjects && _level < _maxLevels)
        {
            if (_nodes == null) Split();

            int i = 0;
            while (i < _objects.Count)
            {
                int index = GetIndex(_objects[i].bounds);
                if (index != -1)
                {
                    var item = _objects[i];
                    _objects.RemoveAt(i);
                    _nodes[index].Insert(item.bounds, item.obj);
                }
                else i++;
            }
        }
    }

    public List<T> Retrieve(Rect area)
    {
        List<T> result = new();
        RetrieveAll(area, result);
        return result;
    }
}
```

---

## Checklist

### Pre-Production
- [ ] Game Design Document (GDD)
- [ ] Technical Design Document
- [ ] Art style guide
- [ ] Target platforms defined
- [ ] Performance budgets set

### Development
- [ ] Version control setup
- [ ] CI/CD for builds
- [ ] Automated testing
- [ ] Performance profiling regular
- [ ] Memory leak checks

### Release
- [ ] Platform certification requirements
- [ ] Localization complete
- [ ] Accessibility options
- [ ] Analytics integration
- [ ] Crash reporting