Update
This commit is contained in:
1
.gemini-workspace-history/active-context.md
Normal file
1
.gemini-workspace-history/active-context.md
Normal file
@@ -0,0 +1 @@
|
||||
No previous session history found for this workspace.
|
||||
21
.gemini-workspace-history/summary-2026-04-30.md
Normal file
21
.gemini-workspace-history/summary-2026-04-30.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Technical Summary - HALLUCINATE Project
|
||||
|
||||
## Overview
|
||||
Project HALLUCINATE is a Unity-based multiplayer game utilizing **Photon Fusion** for networking and **UI Toolkit** for front-end management. Recent development has concentrated on the core networking infrastructure and the lobby system.
|
||||
|
||||
## Modified Files
|
||||
- `Assets/Scripts/Network/BasicSpawner.cs`: Centralized `NetworkRunner` management, handling session creation, joining, and player profile initialization.
|
||||
- `Assets/Scripts/UI/LobbyController.cs`: UI logic for room management, password protection, and lounge interactions.
|
||||
- `Assets/Scripts/Player Controller/PlayerStateMachine.cs`: Core player logic (referenced in memory).
|
||||
|
||||
## Key Logic & Decisions
|
||||
- **Network Architecture**: Singleton `BasicSpawner` manages the Fusion runner lifecycle. Session joining uses `SessionLobby.ClientServer`.
|
||||
- **UI Architecture**: `LobbyController` inherits from a base UI class and uses direct `VisualElement` queries for dynamic UI updates (UXML/USS).
|
||||
- **Security**: Implementation of session passwords via custom properties in Photon sessions.
|
||||
- **State Sync**: Player status (Ready/Start) and lounge info are synchronized between clients, likely via `PlayerDataManager`.
|
||||
|
||||
## Next Steps
|
||||
- [ ] Verify RPC/SyncVar logic in `LobbyController.cs`.
|
||||
- [ ] Connect `PlayerStateMachine` to the spawning flow in `BasicSpawner`.
|
||||
- [ ] Implement/Finalize the chat system within the lobby lounge.
|
||||
- [ ] Ensure all scene transitions (Main Scene) are robust.
|
||||
6
.idea/.idea.HALLUCINATE/.idea/workspace.xml
generated
6
.idea/.idea.HALLUCINATE/.idea/workspace.xml
generated
@@ -6,9 +6,9 @@
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="f9183c68-daf0-43b8-be4c-fad79983f91b" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.HALLUCINATE/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.HALLUCINATE/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Network/BasicSpawner.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Network/BasicSpawner.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Player Controller/PlayerStateMachine.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Player Controller/PlayerStateMachine.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/LobbyController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/LobbyController.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Assets/UI/MainPanelSettings.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/UI/MainPanelSettings.asset" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/UIManager.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/UIManager.cs" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -148,7 +148,7 @@
|
||||
<workItem from="1777376778745" duration="10727000" />
|
||||
<workItem from="1777392719306" duration="13382000" />
|
||||
<workItem from="1777443280908" duration="5223000" />
|
||||
<workItem from="1777484328779" duration="17223000" />
|
||||
<workItem from="1777484328779" duration="21208000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
||||
@@ -29,9 +29,15 @@ namespace OnlyScove.Scripts
|
||||
// Pass-through properties for State Compatibility
|
||||
public Vector2 MoveInput { get; private set; }
|
||||
public bool IsSprintHeld { get; private set; }
|
||||
public float VelocityY { get => Movement.VelocityY; set => Movement.VelocityY = value; }
|
||||
public bool IsGrounded => Movement.IsGrounded;
|
||||
public bool WasGrounded => Movement.WasGrounded;
|
||||
|
||||
public float VelocityY
|
||||
{
|
||||
get => (Object != null && Object.IsValid && Movement != null) ? Movement.VelocityY : 0f;
|
||||
set { if (Object != null && Object.IsValid && Movement != null) Movement.VelocityY = value; }
|
||||
}
|
||||
|
||||
public bool IsGrounded => (Object != null && Object.IsValid && Movement != null) ? Movement.IsGrounded : true;
|
||||
public bool WasGrounded => (Object != null && Object.IsValid && Movement != null) ? Movement.WasGrounded : true;
|
||||
|
||||
public float WalkSpeed => Movement.WalkSpeed;
|
||||
public float RunSpeed => Movement.RunSpeed;
|
||||
@@ -48,11 +54,11 @@ namespace OnlyScove.Scripts
|
||||
public static PlayerStateMachine Local { get; private set; }
|
||||
public string CurrentStateName => currentState != null ? currentState.GetType().Name : "None";
|
||||
|
||||
public Quaternion CameraRotation
|
||||
public Quaternion CameraRotation
|
||||
{
|
||||
get
|
||||
get
|
||||
{
|
||||
if (Runner != null && Runner.IsRunning && Object != null) return NetworkedCameraRotation;
|
||||
if (Runner != null && Runner.IsRunning && Object != null && Object.IsValid) return NetworkedCameraRotation;
|
||||
return Cam != null ? Cam.PlanarRotation : transform.rotation;
|
||||
}
|
||||
}
|
||||
@@ -67,7 +73,7 @@ namespace OnlyScove.Scripts
|
||||
Input = GetComponent<InputReader>();
|
||||
Anim = GetComponentInChildren<Animator>();
|
||||
Scanner = GetComponent<EnvironmentScanner>();
|
||||
|
||||
|
||||
Stats = GetComponent<PlayerStats>();
|
||||
Interaction = GetComponent<PlayerInteraction>();
|
||||
Movement = GetComponent<PlayerMovement>();
|
||||
@@ -107,12 +113,28 @@ namespace OnlyScove.Scripts
|
||||
Cam.followTarget = transform;
|
||||
Cam.inputReader = Input;
|
||||
}
|
||||
Input.OnNextInteractEvent += Interaction.NextInteract;
|
||||
Input.OnPreviousInteractEvent += Interaction.PreviousInteract;
|
||||
|
||||
if (Input != null)
|
||||
{
|
||||
Input.OnNextInteractEvent -= Interaction.NextInteract;
|
||||
Input.OnNextInteractEvent += Interaction.NextInteract;
|
||||
Input.OnPreviousInteractEvent -= Interaction.PreviousInteract;
|
||||
Input.OnPreviousInteractEvent += Interaction.PreviousInteract;
|
||||
}
|
||||
|
||||
if (Controller != null) Controller.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Input != null && Interaction != null)
|
||||
{
|
||||
Input.OnNextInteractEvent -= Interaction.NextInteract;
|
||||
Input.OnPreviousInteractEvent -= Interaction.PreviousInteract;
|
||||
}
|
||||
}
|
||||
|
||||
public void Rotate(Vector3 moveDirection, float deltaTime)
|
||||
{
|
||||
Movement.Rotate(transform, moveDirection, deltaTime);
|
||||
@@ -120,13 +142,13 @@ namespace OnlyScove.Scripts
|
||||
|
||||
public void Move(Vector3 velocity, float animatorSpeed, float deltaTime)
|
||||
{
|
||||
bool canMove = (Runner == null || !Runner.IsRunning) || Object.HasInputAuthority || Runner.IsServer;
|
||||
bool canMove = (Runner == null || !Runner.IsRunning) || (Object != null && Object.IsValid && (Object.HasInputAuthority || Runner.IsServer));
|
||||
if (!canMove) return;
|
||||
|
||||
Movement.Move(Controller, velocity, deltaTime);
|
||||
|
||||
|
||||
localAnimatorSpeed = animatorSpeed;
|
||||
if (Object != null && Object.HasStateAuthority)
|
||||
if (Object != null && Object.IsValid && Object.HasStateAuthority)
|
||||
{
|
||||
NetworkedSpeed = animatorSpeed;
|
||||
NetworkedMoveInput = MoveInput;
|
||||
@@ -136,20 +158,21 @@ namespace OnlyScove.Scripts
|
||||
|
||||
private void UpdateAnimator(float deltaTime)
|
||||
{
|
||||
float speedValue = (Runner == null || !Runner.IsRunning || Object.HasInputAuthority) ? localAnimatorSpeed : NetworkedSpeed;
|
||||
Vector2 inputVector = (Runner == null || !Runner.IsRunning || Object.HasInputAuthority) ? MoveInput : NetworkedMoveInput;
|
||||
bool isNetworked = Runner != null && Runner.IsRunning && Object != null && Object.IsValid;
|
||||
float speedValue = (!isNetworked || Object.HasInputAuthority) ? localAnimatorSpeed : NetworkedSpeed;
|
||||
Vector2 inputVector = (!isNetworked || Object.HasInputAuthority) ? MoveInput : NetworkedMoveInput;
|
||||
AnimationHandler.UpdateAnimator(speedValue, inputVector, deltaTime);
|
||||
}
|
||||
|
||||
public override void FixedUpdateNetwork()
|
||||
{
|
||||
bool isRunning = Runner != null && Runner.IsRunning;
|
||||
if (Object == null && isRunning) return;
|
||||
if (isRunning && (Object == null || !Object.IsValid)) return;
|
||||
|
||||
if (GetInput(out PlayerInputData data))
|
||||
{
|
||||
MoveInput = data.Direction;
|
||||
IsSprintHeld = data.sprint;
|
||||
IsSprintHeld = (bool)data.sprint;
|
||||
if (isRunning) NetworkedCameraRotation = data.rot;
|
||||
}
|
||||
else if (!isRunning)
|
||||
@@ -158,7 +181,7 @@ namespace OnlyScove.Scripts
|
||||
IsSprintHeld = UnityEngine.Input.GetKey(KeyCode.LeftShift);
|
||||
}
|
||||
|
||||
if (!isRunning || Object.HasInputAuthority || Runner.IsServer)
|
||||
if (!isRunning || (Object != null && Object.IsValid && (Object.HasInputAuthority || Runner.IsServer)))
|
||||
{
|
||||
if (hasControl)
|
||||
{
|
||||
@@ -172,7 +195,7 @@ namespace OnlyScove.Scripts
|
||||
public override void Render()
|
||||
{
|
||||
bool isRunning = Runner != null && Runner.IsRunning;
|
||||
if (isRunning && !Object.HasInputAuthority)
|
||||
if (isRunning && Object != null && Object.IsValid && !Object.HasInputAuthority)
|
||||
{
|
||||
// Smooth interpolation for proxies
|
||||
if (Movement.NetworkedPosition != Vector3.zero)
|
||||
|
||||
@@ -236,7 +236,13 @@ namespace Hallucinate.UI
|
||||
: null;
|
||||
|
||||
bool success = await spawner.StartHost(id, name, pass);
|
||||
if (success) ShowLounge(name);
|
||||
if (success)
|
||||
{
|
||||
ShowLounge(name);
|
||||
// Explicitly push the LobbyController to ensure it's the active UI screen.
|
||||
// This helps prevent unintended navigation away from the lounge.
|
||||
await uiManager.Push<LobbyController>();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRoomList(List<SessionInfo> sessions)
|
||||
@@ -319,12 +325,12 @@ namespace Hallucinate.UI
|
||||
BasicSpawner.Instance?.StartGame();
|
||||
}
|
||||
|
||||
private void OnLeaveLoungeClicked()
|
||||
private async void OnLeaveLoungeClicked()
|
||||
{
|
||||
var runner = Object.FindFirstObjectByType<NetworkRunner>();
|
||||
if (runner != null)
|
||||
{
|
||||
runner.Shutdown();
|
||||
await runner.Shutdown();
|
||||
}
|
||||
if (_playerDataManager != null)
|
||||
{
|
||||
@@ -355,7 +361,7 @@ namespace Hallucinate.UI
|
||||
}
|
||||
}
|
||||
|
||||
if (_playerDataManager == null || !_playerDataManager.Object.IsValid) return;
|
||||
if (_playerDataManager == null || _playerDataManager.Object == null || !_playerDataManager.Object.IsValid) return;
|
||||
|
||||
PlayerRef hostRef = PlayerRef.None;
|
||||
PlayerRef guestRef = PlayerRef.None;
|
||||
|
||||
@@ -63,6 +63,9 @@ namespace Hallucinate.UI
|
||||
|
||||
private const string UI_SCALE_KEY = "UIScale";
|
||||
|
||||
[Header("Development Settings")]
|
||||
[SerializeField] private bool allowMultipleInstances = true;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
@@ -73,6 +76,19 @@ namespace Hallucinate.UI
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
// Single instance guard
|
||||
if (!Application.isEditor && !allowMultipleInstances)
|
||||
{
|
||||
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
|
||||
var processes = System.Diagnostics.Process.GetProcessesByName(currentProcess.ProcessName);
|
||||
if (processes.Length > 1)
|
||||
{
|
||||
Debug.LogError("[UIManager] Another instance is already running. Quitting to prevent save conflict.");
|
||||
Application.Quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_uiDocument = GetComponent<UIDocument>();
|
||||
UnityEngine.Cursor.visible = false;
|
||||
|
||||
@@ -92,10 +108,10 @@ namespace Hallucinate.UI
|
||||
public void SetUIScale(float scale)
|
||||
{
|
||||
if (_uiDocument == null || _uiDocument.panelSettings == null) return;
|
||||
|
||||
|
||||
// Unity UI Toolkit dùng panelSettings để điều khiển tỉ lệ
|
||||
// Chúng ta thay đổi scale multiplier
|
||||
_uiDocument.panelSettings.scale = scale;
|
||||
// Chúng ta thay đổi scale multiplier. Mặc định 1.0 sẽ tương ứng với visual scale là 1.3
|
||||
_uiDocument.panelSettings.scale = scale * 1.3f;
|
||||
PlayerPrefs.SetFloat(UI_SCALE_KEY, scale);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
@@ -105,7 +121,6 @@ namespace Hallucinate.UI
|
||||
float savedScale = PlayerPrefs.GetFloat(UI_SCALE_KEY, 1.0f);
|
||||
SetUIScale(savedScale);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_uiDocument == null) _uiDocument = GetComponent<UIDocument>();
|
||||
|
||||
Reference in New Issue
Block a user