$3b
Patterns canoniques pour développer sur Unity 6 / 2023.x LTS en suivant les meilleures pratiques 2026.
Type de jeu
├── 1-100 entités (RPG, puzzle, plateformer, mobile casual)
│ └── MonoBehaviour classique + ScriptableObjects
├── 100-10k entités (RTS, simulation économique, action multi-units)
│ └── Hybrid : MonoBehaviour pour gameplay, Job System + Burst pour calculs lourds
├── 10k+ entités (battle royale, MMO instanciel, voxel, cellular automata)
│ └── DOTS / ECS (Entities, Components, Systems) full
└── XR / VR / AR
└── XR Interaction Toolkit + URP, MonoBehaviour
Règle pratique : ne pas partir en DOTS par défaut. C'est puissant mais double la courbe d'apprentissage et casse beaucoup d'assets store. Justifié uniquement pour des cas où la perf MonoBehaviour est insuffisante.
// Items/ItemData.cs
using UnityEngine;
[CreateAssetMenu(fileName = "NewItem", menuName = "Game/Item Data")]
public class ItemData : ScriptableObject
{
public string itemName;
public Sprite icon;
public int maxStack = 99;
public ItemRarity rarity;
public int basePrice;
[TextArea(3, 10)] public string description;
}
// Inventory/Inventory.cs — utilise les ItemData comme références
public class Inventory : MonoBehaviour
{
[SerializeField] private List<ItemSlot> slots = new();
public bool TryAddItem(ItemData item, int quantity)
{
// logic
}
}
[System.Serializable]
public class ItemSlot
{
public ItemData item;
public int quantity;
}
Avantages :
// Async load
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public async Task LoadLevelAsync(string levelKey)
{
AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>(levelKey);
await handle.Task;
if (handle.Status == AsyncOperationStatus.Succeeded)
{
GameObject level = Instantiate(handle.Result);
// ... use level ...
// IMPORTANT: release when done
Addressables.Release(handle);
}
}
Bonnes pratiques :
Addressables.GetDownloadSizeAsync avant download<!-- MainMenu.uxml -->
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<ui:VisualElement class="root">
<ui:Label text="My Game" class="title" />
<ui:Button name="play-button" text="Play" class="primary-button" />
<ui:Button name="settings-button" text="Settings" class="secondary-button" />
<ui:Button name="quit-button" text="Quit" class="secondary-button" />
</ui:VisualElement>
</ui:UXML>
/* MainMenu.uss */
.root {
flex-grow: 1;
align-items: center;
justify-content: center;
background-color: rgb(20, 20, 30);
}
.title {
font-size: 64px;
color: white;
-unity-font-style: bold;
margin-bottom: 40px;
}
.primary-button {
width: 240px;
height: 60px;
background-color: rgb(0, 150, 200);
color: white;
margin-bottom: 12px;
border-radius: 8px;
}
// MainMenuController.cs
using UnityEngine;
using UnityEngine.UIElements;
public class MainMenuController : MonoBehaviour
{
[SerializeField] private UIDocument uiDocument;
private void OnEnable()
{
var root = uiDocument.rootVisualElement;
root.Q<Button>("play-button").clicked += OnPlay;
root.Q<Button>("settings-button").clicked += OnSettings;
root.Q<Button>("quit-button").clicked += Application.Quit;
}
private void OnPlay() => SceneManager.LoadScene("Level01");
private void OnSettings() => { /* show settings */ };
}
Avantages UI Toolkit vs uGUI :
// PlayerInput.inputactions (généré via Inspector)
// → ActionMap "Gameplay" avec actions Move, Jump, Attack, Interact
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(PlayerInput))]
public class PlayerController : MonoBehaviour
{
private Vector2 moveInput;
public void OnMove(InputAction.CallbackContext ctx) => moveInput = ctx.ReadValue<Vector2>();
public void OnJump(InputAction.CallbackContext ctx)
{
if (ctx.performed) Jump();
}
private void Update()
{
transform.position += new Vector3(moveInput.x, 0, moveInput.y) * speed * Time.deltaTime;
}
}
Avantages :
PlayerInputManager)using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
[BurstCompile]
public struct CalculatePositionsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float3> velocities;
public NativeArray<float3> positions;
public float deltaTime;
public void Execute(int index)
{
positions[index] += velocities[index] * deltaTime;
}
}
// Schedule
var job = new CalculatePositionsJob {
velocities = velocityArray,
positions = positionArray,
deltaTime = Time.deltaTime
};
JobHandle handle = job.Schedule(positionArray.Length, 64);
handle.Complete();
Cas d'usage : pathfinding mass, simulation physique custom, procedural generation, post-processing par pixel.
StringBuilder pour les concaténationsOnTriggerEnter light — déléguer le travail lourd à Update sur frame suivanteUpdateMode = ScriptOrder au lieu de chercher à chaque frameFind() / GameObject.Find() dans Update — refs sérialisées dans l'inspecteur ou cache au StartSendMessage() / Invoke() — préférer events C# typés ou UnityEventsgameObject.GetComponent<>() dans Update — cache au Startnew Vector3() dans Update boucle — préférer une variable locale.gitignore Unity correct — Library/, Temp/, obj/ committés = repo énormegame-multiplayer-patterns (ce plugin)game-asset-pipelines (ce plugin)game-architect (ce plugin)unity-expert (ce plugin)deploy-app-store, deploy-play-store (atum-workflows)