SyncVar Collections
네트워크 상에서 컬렉션을 동기화할 때 사용하는 특별한 컬렉션 타입들입니다. 서버에서 컬렉션이 변경되면 자동으로 모든 클라이언트에 전파됩니다.
SyncList
순서가 있는 데이터 목록을 동기화할 때 사용합니다. 퀘스트 목록, 인벤토리 등을 구현할 때 유용합니다.
public class Inventory : NetworkBehaviour
{
    public readonly SyncList<int> items = new SyncList<int>();
    public override void OnStartClient()
    {
        items.OnAdd += OnItemAdded;
        items.OnRemove += OnItemRemoved;
        // 초기 인벤토리 로드
        for (int i = 0; i < items.Count; i++)
            items.OnAdd.Invoke(i);
    }
    [Server]
    public void AddItem(int itemId)
    {
        items.Add(itemId);
    }
    void OnItemAdded(int index)
    {
        // 새 아이템 획득 UI 표시
        UIManager.Instance.ShowNewItemNotification(items[index]);
    }
    void OnItemRemoved(int index, int oldValue)
    {
        // 인벤토리 UI 업데이트
        UIManager.Instance.UpdateInventoryUI();
    }
}
SyncDictionary
키-값 쌍의 데이터를 동기화할 때 사용합니다. 장비 시스템, 스킬 시스템 등을 구현할 때 유용합니다.
public enum EquipSlot { Weapon, Armor, Helmet, Boots }
public struct EquipItem
{
    public int itemId;
    public int enhanceLevel;
    public bool isLocked;
}
public class EquipmentSystem : NetworkBehaviour
{
    public readonly SyncDictionary<EquipSlot, EquipItem> equipment = new SyncDictionary<EquipSlot, EquipItem>();
    public override void OnStartClient()
    {
        equipment.OnSet += OnEquipmentChanged;
        equipment.OnRemove += OnEquipmentRemoved;
        foreach (var slot in equipment.Keys)
            equipment.OnSet.Invoke(slot, default);
    }
    [Server]
    public void EquipItem(EquipSlot slot, EquipItem item)
    {
        equipment[slot] = item;
    }
    void OnEquipmentChanged(EquipSlot slot, EquipItem oldItem)
    {
        // 장비 변경 효과 표시
        VFXManager.Instance.PlayEquipEffect(slot);
        // 캐릭터 외형 업데이트
        CharacterCustomization.Instance.UpdateEquipment(slot, equipment[slot]);
    }
}
SyncHashSet
중복되지 않는 데이터 집합을 동기화할 때 사용합니다. 획득한 업적, 해금된 콘텐츠 등을 구현할 때 유용합니다.
public class AchievementSystem : NetworkBehaviour
{
    public readonly SyncHashSet<string> unlockedAchievements = new SyncHashSet<string>();
    public override void OnStartClient()
    {
        unlockedAchievements.OnAdd += OnAchievementUnlocked;
        foreach (string achievementId in unlockedAchievements)
            unlockedAchievements.OnAdd.Invoke(achievementId);
    }
    [Server]
    public void UnlockAchievement(string achievementId)
    {
        if (unlockedAchievements.Add(achievementId))
        {
            // 업적 달성 보상 지급 등의 서버 로직
        }
    }
    void OnAchievementUnlocked(string achievementId)
    {
        // 업적 달성 UI 표시
        UIManager.Instance.ShowAchievementPopup(achievementId);
        // 업적 효과음 재생
        AudioManager.Instance.PlayAchievementSound();
    }
}
SyncSortedSet
자동으로 정렬되는 중복되지 않는 데이터 집합을 동기화할 때 사용합니다. 랭킹 시스템, 점수판 등을 구현할 때 유용합니다.
public struct ScoreData : IComparable<ScoreData>
{
    public string playerName;
    public int score;
    public int CompareTo(ScoreData other) => other.score.CompareTo(score); // 높은 점수순
}
public class LeaderboardSystem : NetworkBehaviour
{
    public readonly SyncSortedSet<ScoreData> leaderboard = new SyncSortedSet<ScoreData>();
    public override void OnStartClient()
    {
        leaderboard.OnAdd += OnScoreAdded;
        leaderboard.OnRemove += OnScoreRemoved;
        foreach (var score in leaderboard)
            leaderboard.OnAdd.Invoke(score);
    }
    [Server]
    public void AddScore(string playerName, int score)
    {
        leaderboard.Add(new ScoreData { playerName = playerName, score = score });
    }
    void OnScoreAdded(ScoreData score)
    {
        // 리더보드 UI 업데이트
        UIManager.Instance.UpdateLeaderboard(leaderboard);
    }
}
노트
- 모든 SyncVar 컬렉션은 서버에서만 수정해야 합니다
- 클라이언트는 OnStartClient에서 이벤트 핸들러를 등록하고, OnStopClient에서 제거해야 합니다
- 초기 데이터는 이벤트 핸들러 등록 전에 이미 동기화되어 있으므로, 필요한 경우 수동으로 처리해야 합니다
- OnChange는 범용 이벤트이므로 가능한 구체적인 이벤트(OnAdd, OnRemove 등)를 사용하는 것이 좋습니다
- 컬렉션은 readonly로 선언하여 인스턴스가 변경되는 것을 방지합니다