This shows you the differences between two versions of the page.
|
pjv:laboratoare:2025:a01 [2025/10/05 10:16] alexandru.gradinaru [🧩 Integrare în scenă] |
pjv:laboratoare:2025:a01 [2025/10/06 13:54] (current) alexandru.gradinaru [Concept general – „Replay / Ghost Car”] |
||
|---|---|---|---|
| Line 24: | Line 24: | ||
| </hidden> | </hidden> | ||
| - | === 🎮 Cerință : Joc Split-Screen Time Trial === | + | === Cerință : Joc Split-Screen Time Trial === |
| * Fiecare jucător concurează pe o pistă proprie (split-screen orizontal/vertical). | * Fiecare jucător concurează pe o pistă proprie (split-screen orizontal/vertical). | ||
| Line 30: | Line 30: | ||
| * Se afișează în UI timpul curent și cel mai bun timp. | * Se afișează în UI timpul curent și cel mai bun timp. | ||
| - | === ⚙️ Cerințe minime === | + | === Cerințe minime === |
| - Două camere active simultan (split-screen orizontal sau vertical). | - Două camere active simultan (split-screen orizontal sau vertical). | ||
| - Două entități controlabile independent (ex: mașini, personaje). | - Două entități controlabile independent (ex: mașini, personaje). | ||
| Line 40: | Line 40: | ||
| ---- | ---- | ||
| - | === 🚀 Cerințe opționale (bonus) === | + | === Cerințe opționale (bonus) === |
| * Adăugarea unui **Player Manager** care detectează automat dispozitivele conectate și alocă controlul. | * Adăugarea unui **Player Manager** care detectează automat dispozitivele conectate și alocă controlul. | ||
| * Introducerea unui **AIPlayer** controlat de un `Strategy` diferit (folosind strategy pattern). | * Introducerea unui **AIPlayer** controlat de un `Strategy` diferit (folosind strategy pattern). | ||
| Line 49: | Line 49: | ||
| ---- | ---- | ||
| - | === 🎯 Scopul laboratorului === | + | === Scopul laboratorului === |
| În acest laborator, studenții vor recapitula noțiuni esențiale de dezvoltare a jocurilor multiplayer locale (split-screen), cu accent pe gestionarea camerelor, viewport-urilor și sistemelor de input independente. | În acest laborator, studenții vor recapitula noțiuni esențiale de dezvoltare a jocurilor multiplayer locale (split-screen), cu accent pe gestionarea camerelor, viewport-urilor și sistemelor de input independente. | ||
| De asemenea, se va introduce conceptul de **arhitectură extensibilă** prin utilizarea unor **design patterns** fundamentale în dezvoltarea jocurilor video. | De asemenea, se va introduce conceptul de **arhitectură extensibilă** prin utilizarea unor **design patterns** fundamentale în dezvoltarea jocurilor video. | ||
| Line 55: | Line 55: | ||
| ---- | ---- | ||
| - | === 🧩 Obiective === | + | === Obiective === |
| * Înțelegerea conceptului de **split-screen** și a modului de configurare a camerelor multiple. | * Înțelegerea conceptului de **split-screen** și a modului de configurare a camerelor multiple. | ||
| * Crearea unui **sistem de input modular** pentru controlul a doi jucători independenți. | * Crearea unui **sistem de input modular** pentru controlul a doi jucători independenți. | ||
| Line 64: | Line 64: | ||
| ---- | ---- | ||
| - | === 🧠 Noțiuni teoretice === | + | === Noțiuni teoretice === |
| Exista mai multe metode de a face un joc controlabil de mai multi jucatori in acelasi timp: | Exista mai multe metode de a face un joc controlabil de mai multi jucatori in acelasi timp: | ||
| Line 109: | Line 109: | ||
| ==== 3. Sisteme de input ==== | ==== 3. Sisteme de input ==== | ||
| + | <hidden> | ||
| Controlul jucatorilor va trebui sa se codeze separat prin definirea de axe sau taste specifice fiecarui jucator. | Controlul jucatorilor va trebui sa se codeze separat prin definirea de axe sau taste specifice fiecarui jucator. | ||
| Line 115: | Line 116: | ||
| * Se pot instanția automat controale multiple prin `PlayerInputManager`. | * Se pot instanția automat controale multiple prin `PlayerInputManager`. | ||
| * Concept: fiecare jucător are un **InputHandler** propriu. | * Concept: fiecare jucător are un **InputHandler** propriu. | ||
| + | </hidden> | ||
| + | |||
| + | === Sistemul de input legacy (Unity) === | ||
| + | |||
| + | În sistemul legacy de input din Unity testarea acțiunilor este destul de simplă în cod, dar are câteva limitări. În cel mai simplu mod, input-ul se poate testa prin polling-ul unor butoane de input specifice. | ||
| + | |||
| + | <code c#> | ||
| + | public class LegacyInputExample : MonoBehaviour | ||
| + | { | ||
| + | // Update is needed to poll every frame. | ||
| + | private void Update() | ||
| + | { | ||
| + | // I want 3 buttons to call the same logic: LMB (mouse), Space (key), LCtrl (key). | ||
| + | // In the legacy system, I need to hardcode the keycodes. | ||
| + | if (Input.GetMouseButtonDown(0) || | ||
| + | Input.GetKeyDown(KeyCode.Space) || | ||
| + | Input.GetKeyDown(KeyCode.LeftControl)) | ||
| + | { | ||
| + | Debug.Log("[LegacyInputExample] Action performed!"); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Definirea unui control split-scren / local co-op se poate realiza și folosind sistemul legacy, dar este probabil să necesite unele hardcoding-uri, așadar varianta recomandată pentru tratarea input-ului (fie ca e single-player sau nu) este folosind noul sistem bazat pe acțiuni, prezentat în continuare. | ||
| + | | ||
| + | === Sistemul de input baza pe acțiuni (Unity) === | ||
| + | |||
| + | Noul sistem de input abstractizează partea de testare manuală a acestor input-uri prin **mapping-uri** și **acțiuni**. | ||
| + | |||
| + | Un **mapping** definește un set de acțiuni legate de anumite input-uri **fizice** (cum ar fi tastatura, mouse-ul, controllerele, etc.). Practic, un mapping este o **schemă de control** care asociază diferite acțiuni cu diverse metode de input. Puteți să vă gândiți la un mapping ca la modul în care un player interacționează cu jocul în funcție de context. Câteva exemple de mapping-uri: | ||
| + | * **MovementOnFoot** pentru mișcare pedestră (WASD pe tastatură sau joystick pe un controller) | ||
| + | * **MovementInCar** pentru condusul unei mașini (**aceleași taste WASD** pe tastatură sau joystick pe un controller, dar într-un alt context!) | ||
| + | * **MovementInBoat** pentru navigarea cu o barcă | ||
| + | |||
| + | O **acțiune** reprezintă o interacțiune definită de utilizator care poate fi activată prin diverse tipuri de input-uri. Acțiunile se pot lega la una sau mai multe intrări fizice printr-un **binding**. | ||
| + | |||
| + | Pentru a folosi acest sistem de input: | ||
| + | * Trebuie să aveți pachetul **Input System** integrat în proiect | ||
| + | * La o cale dorită în **Project window** din Unity, **click-dreapta -> Create -> Input Actions** | ||
| + | * În acest asset puteți crea mapping-uri, acțiuni și binding-uri, un exemplu este prezentat în imaginea de mai jos. | ||
| + | * De asemenea, este util să bifați **Generate C# Class** din inspectorul acestui asset pentru a-l utiliza mai ușor în script-urile voastre | ||
| + | |||
| + | {{ :irva:laboratoarevr:irva_2024_vr_l3_newinputsystem.png?700 |}} | ||
| + | |||
| + | În final, următorul snippet prezintă modul de utilizare al acestui sistem. Comentariile din cod ar trebuie să fie explicative. | ||
| + | |||
| + | <code c#> | ||
| + | public class ActionsInputExample : MonoBehaviour | ||
| + | { | ||
| + | private InputActionAssetExample playerInputActions; | ||
| + | private InputAction fireAction; | ||
| + | | ||
| + | // Initialize input asset. | ||
| + | private void Awake() => playerInputActions = new InputActionAssetExample(); | ||
| + | | ||
| + | // Get the action `Fire` from the mapping `PlayerMapping`. Enable it & bind event. | ||
| + | private void OnEnable() | ||
| + | { | ||
| + | fireAction = playerInputActions.PlayerMapping.Fire; | ||
| + | fireAction.Enable(); | ||
| + | fireAction.performed += FirePerformed; | ||
| + | } | ||
| + | | ||
| + | private void OnDisable() | ||
| + | { | ||
| + | fireAction.Disable(); | ||
| + | fireAction.performed -= FirePerformed; | ||
| + | } | ||
| + | | ||
| + | // Called whenever any of the bindings defined for the `Fire` action are called. | ||
| + | // No need for explicit checks or the `Update` method! | ||
| + | private void FirePerformed(InputAction.CallbackContext context) => Debug.Log("Fired action!"); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | În mod alternativ, aceeași funcționalitate se poate obține prin polling în ''Update'' folosind ''ReadValue'' astfel: | ||
| + | |||
| + | <code c#> | ||
| + | public class ActionsInputExample : MonoBehaviour | ||
| + | { | ||
| + | private InputActionAssetExample _playerInputActions; | ||
| + | |||
| + | private void Awake() => _playerInputActions = new InputActionAssetExample(); | ||
| + | |||
| + | private void OnEnable() => _playerInputActions.Enable(); | ||
| + | | ||
| + | private void OnDisable() => _playerInputActions.Disable(); | ||
| + | |||
| + | private void Update() | ||
| + | { | ||
| + | var fireAction = _playerInputActions.PlayerMapping.Fire.ReadValue<float>(); | ||
| + | if (fireAction > 0.5f) | ||
| + | { | ||
| + | Debug.Log("Fire action is currently being pressed."); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Control schemes (grupuri de binding-uri) utile pentru split-screen === | ||
| + | |||
| + | Pentru a diferenția input-ul fiecărui jucător, o metodă facilă este reprezentată de **control schemes**. Din **Input Action Editor** puteți crea un nou **Control scheme** sau grup, căruia în alăturăm binding-uri, astfel diferențiind input-ul fiecărui jucător. De exemplu, se poate crea o schemă de control pentru tastatură și alta pentru gamepad -- apoi, fiecare binding este selectat, iar în panoul din dreapta **Action Properties** se poate atribui apoi acest binding unui scheme de control. | ||
| + | |||
| + | Mai jos se observă un binding în grupul **Keyboard**. | ||
| + | |||
| + | {{ :irva:laboratoarevr:pajv_2025_l1_schema_1.png?500 |}} | ||
| + | |||
| + | În următoarea imagine sunt afișate toate schemele de control. Observați numele grupului între paranteze pătrate. | ||
| + | |||
| + | {{ :irva:laboratoarevr:pajv_2025_l1_schema_2.png?500 |}} | ||
| ---- | ---- | ||
| Line 120: | Line 232: | ||
| - | === 🧩 Exemple de design pattern-uri === | + | === Exemple de design pattern-uri === |
| - | ==== 🔸 Command Pattern (pentru input) ==== | + | ==== Command Pattern (pentru input) ==== |
| <code csharp> | <code csharp> | ||
| public interface ICommand { | public interface ICommand { | ||
| Line 141: | Line 253: | ||
| </code> | </code> | ||
| - | ==== 🔸 Observer Pattern (pentru UI) ==== | + | ==== Observer Pattern (pentru UI) ==== |
| <code csharp> | <code csharp> | ||
| public class Car : MonoBehaviour { | public class Car : MonoBehaviour { | ||
| Line 161: | Line 273: | ||
| ---- | ---- | ||
| - | ==== 🎬 Concept general – „Replay / Ghost Car” ==== | + | ==== Concept general – „Replay / Ghost Car” ==== |
| În timpul cursei, fiecare acțiune de control (accelerare, frânare, viraj) este înregistrată ca un Command cu un timestamp. | În timpul cursei, fiecare acțiune de control (accelerare, frânare, viraj) este înregistrată ca un Command cu un timestamp. | ||
| Line 175: | Line 287: | ||
| * CarController – execută acțiunile asupra mașinii. | * CarController – execută acțiunile asupra mașinii. | ||
| + | {{ :pjv:laboratoare:2025:chatgpt_image_oct_5_2025_10_18_30_am.png?800 |}} | ||
| <code csharp> | <code csharp> | ||
| Line 252: | Line 365: | ||
| void Start() => startTime = Time.time; | void Start() => startTime = Time.time; | ||
| - | void Update() | + | void FixedUpdate() |
| { | { | ||
| if (currentIndex >= replayCommands.Count) return; | if (currentIndex >= replayCommands.Count) return; | ||
| Line 284: | Line 397: | ||
| - | ==== 🧩 Integrare în scenă ==== | + | ==== Integrare în scenă ==== |
| Creezi două mașini: | Creezi două mașini: | ||
| Line 299: | Line 412: | ||
| + | Avantaje ale Command Pattern aici: | ||
| + | |||
| + | * Separă complet inputul de logică — același cod de mișcare poate fi refolosit pentru AI, replay sau multiplayer. | ||
| + | * Poți salva comenzile pe disc (JSON) și relua oricând. | ||
| + | * Permite debugging ușor: poți „derula” fiecare comandă înapoi sau înainte. | ||
| + | |||
| + | Extensii posibile: | ||
| + | * Adaugă un RecordToFile() care serializează comenzile în JSON. | ||
| + | * Adaugă un replay speed slider (1x, 2x, slow-motion). | ||
| + | * Adaugă vizualizare „fantomă” cu shader semitransparent pentru GhostCar. | ||
| - | === 📚 Resurse recomandate === | + | === Resurse recomandate === |
| * Unity Manual – [[https://docs.unity3d.com/Manual/MultipleCameras.html|Multiple Cameras and Split Screen]] | * Unity Manual – [[https://docs.unity3d.com/Manual/MultipleCameras.html|Multiple Cameras and Split Screen]] | ||
| * Unity Learn – [[https://learn.unity.com/project/input-system|New Input System Fundamentals]] | * Unity Learn – [[https://learn.unity.com/project/input-system|New Input System Fundamentals]] | ||