Files
test/Documentation/MODEL_LOADING_FLOW.md
2026-03-13 16:03:47 +07:00

9.8 KiB

Model Loading Flow: C++ to C# Conversion

This document maps the C++ model loading sequence to the C# Addressables-based implementation.

C++ Version Flow

1. INITIALIZATION
   ┌─────────────────────────┐
   │ CECPlayer Constructor    │
   │ memset(m_pModels, 0)     │
   └───────────┬───────────────┘
               │
               ▼
2. LOAD REQUEST
   ┌─────────────────────────┐
   │ LoadPlayerSkeleton()    │
   └───────────┬───────────────┘
               │
       ┌───────┴───────┐
       │               │
       ▼               ▼
   Synchronous    Asynchronous
       │               │
       │               ▼
       │      ┌────────────────────┐
       │      │ QueueECModelForLoad │
       │      │ (Background Thread)│
       │      └───────────┬────────┘
       │                  │
       │                  ▼
       │      ┌────────────────────┐
       │      │ ThreadLoadPlayerModel│
       │      └───────────┬────────┘
       │                  │
       │                  ▼
       │      ┌────────────────────┐
       │      │ m_aLoadedModels.Add │
       │      └───────────┬────────┘
       │                  │
       │                  ▼
       │      ┌────────────────────┐
       │      │ DeliverLoadedModels │
       │      │ (Every Frame Tick)  │
       │      └───────────┬────────┘
       │                  │
       └──────────────────┘
               │
               ▼
3. MODEL CREATION
   ┌─────────────────────────┐
   │ LoadPlayerModel()       │
   │ - new CECModel          │
   │ - Load model file       │
   │ - Load skins/equips     │
   └───────────┬───────────────┘
               │
               ▼
4. MODEL ASSIGNMENT
   ┌─────────────────────────┐
   │ SetPlayerLoadedResult() │
   │ - m_pPlayerModel = ...  │
   │ - m_pModels[MAJOR] = ...│
   └─────────────────────────┘

C# Version Flow (Using Addressables)

1. INITIALIZATION
   ┌─────────────────────────┐
   │ CECPlayer.Awake()        │
   │ - m_aEquips = new int[]  │
   │ - m_iShape = 0          │
   │ (No memset needed)      │
   └───────────┬───────────────┘
               │
               ▼
2. LOAD REQUEST
   ┌─────────────────────────┐
   │ CECHostPlayer.InitCharacter() │
   │ - LoadResources()       │
   │ - SetPlayerModel()      │
   └───────────┬───────────────┘
               │
       ┌───────┴───────┐
       │               │
       ▼               ▼
   LoadResources()  SetPlayerModel()
       │               │
       ▼               ▼
   ┌──────────────┐  ┌──────────────────────┐
   │LoadPlayerSkeleton│  │NPCManager.GetModelPlayer│
   └───────┬──────┘  └──────────┬───────────┘
           │                    │
           ▼                    ▼
   ┌──────────────┐  ┌──────────────────────────┐
   │SetPlayerLoadedResult│  │AddressableManager.LoadPrefabAsync│
   └───────┬──────┘  └──────────┬───────────────┘
           │                    │
           ▼                    │
   ┌──────────────┐            │
   │OnAllResourceReady│        │
   └──────────────┘            │
                               │
                               ▼
                    ┌──────────────────────┐
                    │ Unity Addressables   │
                    │ LoadAssetAsync()    │
                    │ (Async/Await)       │
                    └──────────┬───────────┘
                               │
                               ▼
                    ┌──────────────────────┐
                    │ Instantiate(prefab)  │
                    │ SetParent()         │
                    │ SetActive(true)     │
                    └──────────────────────┘

Key Differences

1. Initialization

C++:

  • memset(m_pModels, 0) - Zero-initializes model array

C#:

  • Awake() method initializes arrays:
    m_aEquips = new int[(int)IndexOfIteminEquipmentInventory.SIZE_ALL_EQUIPIVTR];
    m_iShape = 0;
    
  • Location: Assets/PerfectWorld/Scripts/Move/CECPlayer.cs:186-192

2. Load Request Entry Point

C++:

  • LoadPlayerSkeleton() directly calls LoadPlayerModel() or QueueECModelForLoad()

C#:

  • LoadPlayerSkeleton() is simplified and mostly commented out
  • Actual loading happens in InitCharacter()SetPlayerModel()
  • Location: Assets/Scripts/CECHostPlayer.cs:1173-1175

3. Asynchronous Loading Mechanism

C++:

  • Background thread: QueueECModelForLoad()ThreadLoadPlayerModel()
  • Queue system: m_aLoadedModels.Add()
  • Frame-based delivery: DeliverLoadedModels() called every frame tick

C#:

  • No background thread - Uses Unity's async/await pattern
  • No queue system - Direct async/await handling
  • No frame-based delivery - Completion handled by async/await
  • Flow: SetPlayerModel()NPCManager.GetModelPlayer()AddressableManager.LoadPrefabAsync()
  • Location: Assets/PerfectWorld/Scripts/Move/CECPlayer.cs:195-209

4. Model Loading Implementation

C++:

  • LoadPlayerModel() creates CECModel, loads model file, loads skins/equips

C#:

  • NPCManager.GetModelPlayer() loads model via Addressables:
    var prefab = await AddressableManager.Instance.LoadPrefabAsync(_playerModelPaths[profession * GENDER.NUM_GENDER + gender]);
    var player = Instantiate(prefab);
    
  • Location: Assets/PerfectWorld/Scripts/Managers/NPCManager.cs:101-113

5. Addressables System

C# Implementation:

  • AddressableManager wraps Unity Addressables API
  • Uses Addressables.LoadAssetAsync<GameObject>() internally
  • Caches loaded assets in _loadedPrefabAssets dictionary
  • Location: Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs:135-170

6. Model Assignment

C++:

  • SetPlayerLoadedResult() assigns:
    • m_pPlayerModel = ...
    • m_pModels[MAJOR] = ...

C#:

  • Model assignment happens directly in SetPlayerModel():
    _pPlayerModel = await NPCManager.Instance.GetModelPlayer(profession, gender);
    _pPlayerModel.transform.SetParent(parentModel);
    _pPlayerModel.transform.localPosition = Vector3.zero;
    _pPlayerModel.SetActive(true);
    
  • Location: Assets/PerfectWorld/Scripts/Move/CECPlayer.cs:203-208
  • SetPlayerLoadedResult() only calls OnAllResourceReady() (simplified)
  • Location: Assets/Scripts/CECHostPlayer.cs:375-378

Code Locations Summary

C# Files:

  1. CECPlayer.cs (Base class)

    • Awake(): Initialization
    • SetPlayerModel(): Main model loading entry point
    • Location: Assets/PerfectWorld/Scripts/Move/CECPlayer.cs
  2. CECHostPlayer.cs (Host player implementation)

    • LoadResources(): Entry point for resource loading
    • LoadPlayerSkeleton(): Simplified skeleton loading (mostly commented)
    • SetPlayerLoadedResult(): Simplified result handler
    • InitCharacter(): Calls LoadResources() and SetPlayerModel()
    • Location: Assets/Scripts/CECHostPlayer.cs
  3. NPCManager.cs (Model provider)

    • GetModelPlayer(): Loads model via Addressables
    • Location: Assets/PerfectWorld/Scripts/Managers/NPCManager.cs
  4. AddressableManager.cs (Addressables wrapper)

    • LoadPrefabAsync(): Core Addressables loading method
    • Location: Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs
  5. CECHostPlayer.Model.cs (Shape transformation)

    • TransformShape(): Handles shape changes
    • QueueLoadDummyModel(): Referenced but implementation may be elsewhere
    • Location: Assets/Scripts/CECHostPlayer.Model.cs

Notes

  • The C# version removed the background thread loading system in favor of Unity's async/await pattern
  • The queue-based delivery system (DeliverLoadedModels) is replaced by async/await completion handlers
  • Addressables provide built-in caching and memory management, eliminating the need for manual queue management
  • Some C++ methods like LoadPlayerModel() and QueueLoadDummyModel() are commented out in the C# version, suggesting they may be refactored or replaced
  • The model loading is now more streamlined with direct async calls rather than a multi-step queue system