Fix : House of leaves compiler bug
This commit is contained in:
+226
-76
@@ -8,11 +8,13 @@ namespace Unheimlich.World.HouseOfLeaves
|
||||
/// <summary>
|
||||
/// Orchestrates the House of Leaves "endless corridor" illusion:
|
||||
/// - The exit is slowly pushed away as the player walks, so the corridor
|
||||
/// never seems to actually run out (progress feels real, but is deliberately
|
||||
/// much slower than the player's own walking speed - no speed/FOV trickery involved).
|
||||
/// - A darkness/vignette effect kicks in if the player turns back towards
|
||||
/// where they came from, discouraging backtracking.
|
||||
/// No fixed corridor length is required - this works indefinitely.
|
||||
/// never seems to actually run out.
|
||||
/// - A darkness/vignette effect kicks in if the player turns back.
|
||||
/// - Exposes a normalized Progression value [0,1] for visual effects
|
||||
/// such as sand, walls, shaders, etc.
|
||||
///
|
||||
/// The corridor itself remains infinite. Progression only represents
|
||||
/// visual advancement over a configurable distance.
|
||||
/// </summary>
|
||||
public class HouseOfLeavesController : MonoBehaviour
|
||||
{
|
||||
@@ -22,38 +24,69 @@ namespace Unheimlich.World.HouseOfLeaves
|
||||
|
||||
[Header("Endless Corridor (Receding Exit)")]
|
||||
|
||||
[Tooltip("The transform representing the visual end of the corridor (exit door, light at the end, etc). " +
|
||||
"It will be slowly pushed further away as the player walks, to create the illusion of an endless corridor.")]
|
||||
[SerializeField] private Transform _exit;
|
||||
[Tooltip(
|
||||
"The transform representing the visual end of the corridor " +
|
||||
"(exit door, light at the end, etc).")]
|
||||
[SerializeField]
|
||||
private Transform _exit;
|
||||
|
||||
[Tooltip("Fraction of each meter walked that pushes the exit further away. " +
|
||||
"0 = exit never recedes (normal corridor). Close to 1 = exit recedes almost as fast as the player walks " +
|
||||
"(barely any felt progress). Keep this comfortably below 1 so the player still senses real progress, just a lot slower than expected.")]
|
||||
[SerializeField, Range(0f, 0.95f)] private float _recedeFactor = 0.4f;
|
||||
[Tooltip(
|
||||
"Fraction of each meter walked that pushes the exit further away. " +
|
||||
"0 = exit never recedes. Close to 1 = exit recedes almost as fast " +
|
||||
"as the player walks.")]
|
||||
[SerializeField, Range(0f, 0.95f)]
|
||||
private float _recedeFactor = 0.4f;
|
||||
|
||||
[Tooltip(
|
||||
"How slowly the exit catches up to its receded target, in seconds.")]
|
||||
[SerializeField, Min(0.01f)]
|
||||
private float _recedeSmoothTime = 2f;
|
||||
|
||||
|
||||
[Header("Visual Progression")]
|
||||
|
||||
[Tooltip(
|
||||
"Distance in meters required for visual effects to reach " +
|
||||
"Progression = 1. The corridor itself remains infinite.")]
|
||||
[SerializeField, Min(0.01f)]
|
||||
private float _progressionDistance = 50f;
|
||||
|
||||
[Tooltip("How slowly the exit's position catches up to its receded target, in seconds. " +
|
||||
"Higher values make the shift smoother and harder to consciously notice.")]
|
||||
[SerializeField] private float _recedeSmoothTime = 2f;
|
||||
|
||||
[Header("Darkness (Turning Back)")]
|
||||
[SerializeField] private Volume _postProcessVolume;
|
||||
[SerializeField] private float _turningBackAngle = 100f;
|
||||
[SerializeField] [Range(0f, 1f)] private float _maxVignetteIntensity = 0.6f;
|
||||
[SerializeField] private float _maxExposureReduction = -3f;
|
||||
[SerializeField] private float _darknessLerpSpeed = 2f;
|
||||
|
||||
[SerializeField]
|
||||
private Volume _postProcessVolume;
|
||||
|
||||
[SerializeField]
|
||||
private float _turningBackAngle = 100f;
|
||||
|
||||
[SerializeField, Range(0f, 1f)]
|
||||
private float _maxVignetteIntensity = 0.6f;
|
||||
|
||||
[SerializeField]
|
||||
private float _maxExposureReduction = -3f;
|
||||
|
||||
[SerializeField]
|
||||
private float _darknessLerpSpeed = 2f;
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Private Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Vignette _vignette;
|
||||
private Vignette _vignette;
|
||||
private ColorAdjustments _colorAdjustments;
|
||||
|
||||
private Vector3 _lastPlayerPosition;
|
||||
private float _distanceTravelled; // Unbounded - there is no fixed corridor length anymore.
|
||||
|
||||
// Unbounded distance travelled through the corridor.
|
||||
private float _distanceTravelled;
|
||||
|
||||
private Vector3 _lightForwardDirection;
|
||||
private Vector3 _exitRestPosition; // The exit's original position, used as the recede baseline.
|
||||
private bool _isInitialized;
|
||||
private Vector3 _exitRestPosition;
|
||||
|
||||
private bool _isInitialized;
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Unity Messages
|
||||
@@ -61,25 +94,29 @@ namespace Unheimlich.World.HouseOfLeaves
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (!TryResolvePlayerReferences()) return;
|
||||
if (!TryResolvePlayerReferences())
|
||||
return;
|
||||
|
||||
InitializeLightDirection();
|
||||
InitializePostProcess();
|
||||
InitializeExit();
|
||||
|
||||
_lastPlayerPosition = PlayerController.Instance.transform.position;
|
||||
_isInitialized = true;
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_isInitialized) return;
|
||||
if (!_isInitialized)
|
||||
return;
|
||||
|
||||
TrackDistance();
|
||||
TickExitRecede();
|
||||
TickDarkness();
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Initialization
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
@@ -88,7 +125,10 @@ namespace Unheimlich.World.HouseOfLeaves
|
||||
{
|
||||
if (PlayerController.Instance == null)
|
||||
{
|
||||
Debug.LogError("[HouseOfLeavesController] PlayerController.Instance not found.");
|
||||
Debug.LogError(
|
||||
"[HouseOfLeavesController] PlayerController.Instance not found."
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -96,22 +136,28 @@ namespace Unheimlich.World.HouseOfLeaves
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The corridor's reference direction is simply wherever the player is facing when this
|
||||
/// controller starts - no manual light/reference transform needed. This becomes both the
|
||||
/// "forward" axis used to detect turning back, and the axis the exit recedes along.
|
||||
/// Uses the player's initial facing direction as the corridor's forward axis.
|
||||
/// </summary>
|
||||
private void InitializeLightDirection()
|
||||
{
|
||||
_lightForwardDirection = PlayerController.Instance.transform.forward;
|
||||
_lightForwardDirection =
|
||||
PlayerController.Instance.transform.forward;
|
||||
|
||||
_lightForwardDirection.y = 0f;
|
||||
_lightForwardDirection.Normalize();
|
||||
|
||||
if (_lightForwardDirection.sqrMagnitude > 0f)
|
||||
_lightForwardDirection.Normalize();
|
||||
}
|
||||
|
||||
private void InitializePostProcess()
|
||||
{
|
||||
if (_postProcessVolume == null)
|
||||
{
|
||||
Debug.LogWarning("[HouseOfLeavesController] No URP Volume assigned — darkness effect disabled.");
|
||||
Debug.LogWarning(
|
||||
"[HouseOfLeavesController] No URP Volume assigned — " +
|
||||
"darkness effect disabled."
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -123,116 +169,220 @@ namespace Unheimlich.World.HouseOfLeaves
|
||||
{
|
||||
if (_exit == null)
|
||||
{
|
||||
Debug.LogWarning("[HouseOfLeavesController] No Exit transform assigned — endless-corridor recede disabled.");
|
||||
Debug.LogWarning(
|
||||
"[HouseOfLeavesController] No Exit transform assigned — " +
|
||||
"endless-corridor recede disabled."
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_exitRestPosition = _exit.position;
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Distance Tracking
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void TrackDistance()
|
||||
{
|
||||
Vector3 currentPosition = PlayerController.Instance.transform.position;
|
||||
Vector3 delta = currentPosition - _lastPlayerPosition;
|
||||
delta.y = 0f;
|
||||
Vector3 currentPosition =
|
||||
PlayerController.Instance.transform.position;
|
||||
|
||||
// No clamp here on purpose: the corridor has no defined length, so this can grow forever.
|
||||
_distanceTravelled += Vector3.Dot(delta, _lightForwardDirection);
|
||||
Vector3 delta =
|
||||
currentPosition - _lastPlayerPosition;
|
||||
|
||||
delta.y = 0f;
|
||||
|
||||
// Signed distance along the corridor direction.
|
||||
_distanceTravelled +=
|
||||
Vector3.Dot(delta, _lightForwardDirection);
|
||||
|
||||
// Prevent the visual progression from becoming negative if
|
||||
// the player walks backwards past the starting point.
|
||||
_distanceTravelled =
|
||||
Mathf.Max(0f, _distanceTravelled);
|
||||
|
||||
_lastPlayerPosition = currentPosition;
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Tick Methods
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the exit further away along the corridor direction, proportionally to how far
|
||||
/// the player has walked. Because _recedeFactor is less than 1, the exit still slowly
|
||||
/// gets closer overall - just much slower than the player's actual walking speed - which
|
||||
/// is what sells the "this corridor never seems to end" feeling without ever touching
|
||||
/// the player's movement speed or FOV.
|
||||
/// </summary>
|
||||
private void TickExitRecede()
|
||||
{
|
||||
if (_exit == null) return;
|
||||
if (_exit == null)
|
||||
return;
|
||||
|
||||
float recedeDistance = _distanceTravelled * _recedeFactor;
|
||||
Vector3 targetPosition = _exitRestPosition + _lightForwardDirection * recedeDistance;
|
||||
float recedeDistance =
|
||||
_distanceTravelled * _recedeFactor;
|
||||
|
||||
// Smoothly drift towards the target rather than snapping, so the shift is never perceptible
|
||||
// as a sudden jump - it should read as "the corridor is just long", not as a visible trick.
|
||||
float t = Time.deltaTime / Mathf.Max(_recedeSmoothTime, 0.01f);
|
||||
_exit.position = Vector3.Lerp(_exit.position, targetPosition, t);
|
||||
Vector3 targetPosition =
|
||||
_exitRestPosition +
|
||||
_lightForwardDirection * recedeDistance;
|
||||
|
||||
float t =
|
||||
Time.deltaTime /
|
||||
Mathf.Max(_recedeSmoothTime, 0.01f);
|
||||
|
||||
_exit.position =
|
||||
Vector3.Lerp(
|
||||
_exit.position,
|
||||
targetPosition,
|
||||
t
|
||||
);
|
||||
}
|
||||
|
||||
private void TickDarkness()
|
||||
{
|
||||
if (_vignette == null && _colorAdjustments == null) return;
|
||||
if (_vignette == null &&
|
||||
_colorAdjustments == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 playerForward =
|
||||
PlayerController.Instance.transform.forward;
|
||||
|
||||
Vector3 playerForward = PlayerController.Instance.transform.forward;
|
||||
playerForward.y = 0f;
|
||||
playerForward.Normalize();
|
||||
|
||||
float dot = Vector3.Dot(playerForward, _lightForwardDirection);
|
||||
float angleDegrees = Mathf.Acos(Mathf.Clamp(dot, -1f, 1f)) * Mathf.Rad2Deg;
|
||||
if (playerForward.sqrMagnitude > 0f)
|
||||
playerForward.Normalize();
|
||||
|
||||
float darknessT = Mathf.Clamp01(
|
||||
(angleDegrees - _turningBackAngle) / (180f - _turningBackAngle)
|
||||
);
|
||||
float dot =
|
||||
Vector3.Dot(
|
||||
playerForward,
|
||||
_lightForwardDirection
|
||||
);
|
||||
|
||||
float lerpDelta = Time.deltaTime * _darknessLerpSpeed;
|
||||
float angleDegrees =
|
||||
Mathf.Acos(
|
||||
Mathf.Clamp(dot, -1f, 1f)
|
||||
) * Mathf.Rad2Deg;
|
||||
|
||||
float darknessT =
|
||||
Mathf.Clamp01(
|
||||
(angleDegrees - _turningBackAngle) /
|
||||
Mathf.Max(
|
||||
180f - _turningBackAngle,
|
||||
0.01f
|
||||
)
|
||||
);
|
||||
|
||||
float lerpDelta =
|
||||
Time.deltaTime * _darknessLerpSpeed;
|
||||
|
||||
if (_vignette != null)
|
||||
_vignette.intensity.value = Mathf.Lerp(_vignette.intensity.value, _maxVignetteIntensity * darknessT, lerpDelta);
|
||||
{
|
||||
_vignette.intensity.value =
|
||||
Mathf.Lerp(
|
||||
_vignette.intensity.value,
|
||||
_maxVignetteIntensity * darknessT,
|
||||
lerpDelta
|
||||
);
|
||||
}
|
||||
|
||||
if (_colorAdjustments != null)
|
||||
_colorAdjustments.postExposure.value = Mathf.Lerp(_colorAdjustments.postExposure.value, _maxExposureReduction * darknessT, lerpDelta);
|
||||
{
|
||||
_colorAdjustments.postExposure.value =
|
||||
Mathf.Lerp(
|
||||
_colorAdjustments.postExposure.value,
|
||||
_maxExposureReduction * darknessT,
|
||||
lerpDelta
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Public API
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Total distance the player has walked along the corridor direction (unbounded).</summary>
|
||||
public float DistanceTravelled => _distanceTravelled;
|
||||
/// <summary>
|
||||
/// Total distance travelled along the corridor direction.
|
||||
/// </summary>
|
||||
public float DistanceTravelled =>
|
||||
_distanceTravelled;
|
||||
|
||||
/// <summary>
|
||||
/// Normalized visual progression.
|
||||
///
|
||||
/// 0 = start of the corridor.
|
||||
/// 1 = progression distance has been reached.
|
||||
///
|
||||
/// This value is clamped to [0,1] even though DistanceTravelled
|
||||
/// can continue increasing indefinitely.
|
||||
/// </summary>
|
||||
public float Progression =>
|
||||
Mathf.Clamp01(
|
||||
_distanceTravelled /
|
||||
Mathf.Max(_progressionDistance, 0.01f)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Resets corridor progression and the receding exit.
|
||||
/// </summary>
|
||||
public void ResetProgression()
|
||||
{
|
||||
_distanceTravelled = 0f;
|
||||
_lastPlayerPosition = PlayerController.Instance.transform.position;
|
||||
_distanceTravelled = 0f;
|
||||
|
||||
if (PlayerController.Instance != null)
|
||||
{
|
||||
_lastPlayerPosition =
|
||||
PlayerController.Instance.transform.position;
|
||||
}
|
||||
|
||||
if (_exit != null)
|
||||
_exit.position = _exitRestPosition;
|
||||
{
|
||||
_exit.position =
|
||||
_exitRestPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the corridor forward direction.
|
||||
/// </summary>
|
||||
public void SetLightDirection(Vector3 worldDirection)
|
||||
{
|
||||
_lightForwardDirection = worldDirection;
|
||||
_lightForwardDirection.y = 0f;
|
||||
_lightForwardDirection.Normalize();
|
||||
worldDirection.y = 0f;
|
||||
|
||||
if (worldDirection.sqrMagnitude <= 0f)
|
||||
return;
|
||||
|
||||
_lightForwardDirection =
|
||||
worldDirection.normalized;
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Editor
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawRay(transform.position + Vector3.up, _lightForwardDirection * 4f);
|
||||
|
||||
Gizmos.DrawRay(
|
||||
transform.position + Vector3.up,
|
||||
_lightForwardDirection * 4f
|
||||
);
|
||||
|
||||
if (_exit != null)
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireSphere(_exit.position, 0.5f);
|
||||
|
||||
Gizmos.DrawWireSphere(
|
||||
_exit.position,
|
||||
0.5f
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<Solution>
|
||||
<Project Path="AK.Wwise.Unity.MonoBehaviour.csproj" />
|
||||
<Project Path="OctoCore.Runtime.csproj" />
|
||||
<Project Path="Assembly-CSharp-Editor.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.API.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.MonoBehaviour.Editor.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.API.Editor.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.ProjectDatabase.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.Timeline.csproj" />
|
||||
<Project Path="Assembly-CSharp.csproj" />
|
||||
<Project Path="OctoCore.Editor.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.API.WwiseTypes.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.OpenXR.csproj" />
|
||||
<Project Path="MeshBakerEditor.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.Logging.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.Timeline.Editor.csproj" />
|
||||
<Project Path="MeshBakerCore.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.AddressableInstaller.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.Utilities.csproj" />
|
||||
<Project Path="Ak.Wwise.Api.WAAPI.csproj" />
|
||||
<Project Path="AK.Wwise.Unity.AddressableUpdater.csproj" />
|
||||
</Solution>
|
||||
Reference in New Issue
Block a user