wip : Cleaning not uses & integration liminal spaces
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6f2032eaaa42144e84b60741df012ae
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: add3c88aa7631214caf2879b666bb32d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f13157381d6fb6040abab2d065cfc6e5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 998ca963664cdba409eb7e9bdb3d997a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class BoatMover : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Transform _target;
|
||||
[SerializeField] private float _speed = 2f;
|
||||
[SerializeField] private float _tiltAmount = 3f;
|
||||
[SerializeField] private float _tiltSpeed = 0.8f;
|
||||
|
||||
private Vector3 _startPosition;
|
||||
private Quaternion _startRotation;
|
||||
|
||||
void Start()
|
||||
{
|
||||
_startPosition = transform.position;
|
||||
_startRotation = transform.rotation;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (_target == null) return;
|
||||
|
||||
transform.position = Vector3.MoveTowards(transform.position, _target.position, _speed * Time.deltaTime);
|
||||
|
||||
float tilt = Mathf.Sin(Time.time * _tiltSpeed) * _tiltAmount;
|
||||
transform.rotation = _startRotation * Quaternion.Euler(tilt, 0, tilt * 0.5f);
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ad389ae7a3c264478f56add3bdc53f1
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
using OctoCore.Components;
|
||||
using UnityEngine;
|
||||
|
||||
public class BoatPlayerSync : MonoBehaviour
|
||||
{
|
||||
private Vector3 _lastPosition;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
_lastPosition = transform.position;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (OctoPlayer.Instance == null) return;
|
||||
|
||||
Vector3 delta = transform.position - _lastPosition;
|
||||
OctoPlayer.Instance.GetComponent<CharacterController>().Move(delta);
|
||||
|
||||
_lastPosition = transform.position;
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ff2be398108c8842bb01e0bb8f52f54
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf1554292afd92b4eb64a4621c48b6ba
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11ac527ae2550fe439450496a224f113
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#region Using statements
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Unheimlich.Core
|
||||
{
|
||||
public class FlagEnumAttribute : PropertyAttribute
|
||||
{
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41e19a4b75910c146a0b391bbc5f1ea8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 187485
|
||||
packageName: URP Stylized Water Shader - Proto Series
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/Bitgem/StylisedWater/URP/Scripts/Bitgem/Core/FlagEnumAttribute.cs
|
||||
uploadId: 412673
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68ba1b4fc29360446b280c7c9cdf24ec
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#region Using statements
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Unheimlich.Editor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(Core.FlagEnumAttribute))]
|
||||
public class EnumFlagsAttributeDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
|
||||
{
|
||||
_property.intValue = EditorGUI.MaskField(_position, _label, _property.intValue, _property.enumNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d359930cbabfcc7448e9f0e56039b644
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 187485
|
||||
packageName: URP Stylized Water Shader - Proto Series
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/Bitgem/StylisedWater/URP/Scripts/Bitgem/Editor/FlagEnumAttributeDrawer.cs
|
||||
uploadId: 412673
|
||||
+397
@@ -0,0 +1,397 @@
|
||||
#region Using statements
|
||||
|
||||
using Unheimlich.Core;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Unheimlich.VFX.StylisedWater
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[RequireComponent(typeof(MeshFilter))]
|
||||
public class WaterVolumeBase : MonoBehaviour
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const int MAX_TILES_X = 100;
|
||||
public const int MAX_TILES_Y = 50;
|
||||
public const int MAX_TILES_Z = 100;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Flag lists
|
||||
|
||||
[System.Flags]
|
||||
public enum TileFace : int
|
||||
{
|
||||
NegX = 1,
|
||||
PosX = 2,
|
||||
NegZ = 4,
|
||||
PosZ = 8
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private fields
|
||||
|
||||
protected bool isDirty = true;
|
||||
|
||||
private UnityEngine.Mesh mesh = null;
|
||||
private MeshFilter meshFilter = null;
|
||||
|
||||
private bool[,,] tiles = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public fields
|
||||
|
||||
[FlagEnum]
|
||||
public TileFace IncludeFaces = TileFace.NegX | TileFace.NegZ | TileFace.PosX | TileFace.PosZ;
|
||||
|
||||
[FlagEnum]
|
||||
public TileFace IncludeFoam = TileFace.NegX | TileFace.NegZ | TileFace.PosX | TileFace.PosZ;
|
||||
|
||||
[Range(0.1f, 100f)]
|
||||
public float TileSize = 1f;
|
||||
|
||||
public bool ShowDebug = true;
|
||||
public bool RealtimeUpdates = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
|
||||
private void ensureReferences()
|
||||
{
|
||||
// ensure a mesh filter
|
||||
if (meshFilter == null)
|
||||
{
|
||||
mesh = null;
|
||||
meshFilter = gameObject.GetComponent<MeshFilter>();
|
||||
if (meshFilter == null)
|
||||
{
|
||||
meshFilter = gameObject.AddComponent<MeshFilter>();
|
||||
}
|
||||
}
|
||||
|
||||
// ensure a mesh
|
||||
if (mesh == null)
|
||||
{
|
||||
mesh = meshFilter.sharedMesh;
|
||||
if (mesh == null || mesh.name != "WaterVolume-" + gameObject.GetInstanceID())
|
||||
{
|
||||
mesh = new UnityEngine.Mesh();
|
||||
mesh.name = "WaterVolume-" + gameObject.GetInstanceID();
|
||||
}
|
||||
}
|
||||
|
||||
// apply the mesh to the filter
|
||||
meshFilter.sharedMesh = mesh;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public methods
|
||||
|
||||
public float? GetHeight(Vector3 _position)
|
||||
{
|
||||
// convert the position to a tile
|
||||
var x = Mathf.FloorToInt((_position.x - transform.position.x + 0.5f) / TileSize);
|
||||
var z = Mathf.FloorToInt((_position.z - transform.position.z + 0.5f) / TileSize);
|
||||
|
||||
// check if out of bounds
|
||||
if (x < 0 || x >= MAX_TILES_X || z < 0 || z >= MAX_TILES_Z)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// find the highest active water block in the column
|
||||
// TODO : could be reworked to cater for gaps
|
||||
for (var y = MAX_TILES_Y - 1; y >= 0; y--)
|
||||
{
|
||||
if (tiles[x, y, z])
|
||||
{
|
||||
return transform.position.y + y * TileSize;
|
||||
}
|
||||
}
|
||||
|
||||
// no water in the column
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Rebuild()
|
||||
{
|
||||
// Debug.Log("rebuilding water volume \"" + gameObject.name + "\"");
|
||||
|
||||
// ensure references to components before trying to use them
|
||||
ensureReferences();
|
||||
|
||||
// delete any existing mesh
|
||||
mesh.Clear();
|
||||
|
||||
// allow any child class to generate the tiles to build from
|
||||
tiles = new bool[MAX_TILES_X, MAX_TILES_Y, MAX_TILES_Z];
|
||||
GenerateTiles(ref tiles);
|
||||
|
||||
// prepare buffers for the mesh data
|
||||
var vertices = new List<Vector3>();
|
||||
var normals = new List<Vector3>();
|
||||
var uvs = new List<Vector2>();
|
||||
var colors = new List<Color>();
|
||||
var indices = new List<int>();
|
||||
|
||||
// iterate the tiles
|
||||
for (var x = 0; x < MAX_TILES_X; x++)
|
||||
{
|
||||
for (var y = 0; y < MAX_TILES_Y; y++)
|
||||
{
|
||||
for (var z = 0; z < MAX_TILES_Z; z++)
|
||||
{
|
||||
// check there is water here
|
||||
if (!tiles[x, y, z])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// calculate tile position
|
||||
var x0 = x * TileSize - 0.5f;
|
||||
var x1 = x0 + TileSize;
|
||||
var y0 = y * TileSize - 0.5f;
|
||||
var y1 = y0 + TileSize;
|
||||
var z0 = z * TileSize - 0.5f;
|
||||
var z1 = z0 + TileSize;
|
||||
var ux0 = x0 + transform.position.x;
|
||||
var ux1 = x1 + transform.position.x;
|
||||
var uy0 = y0 + transform.position.y;
|
||||
var uy1 = y1 + transform.position.y;
|
||||
var uz0 = z0 + transform.position.z;
|
||||
var uz1 = z1 + transform.position.z;
|
||||
|
||||
// check for edges
|
||||
var negX = x == 0 || !tiles[x - 1, y, z];
|
||||
var posX = x == MAX_TILES_X - 1 || !tiles[x + 1, y, z];
|
||||
var negY = y == 0 || !tiles[x, y - 1, z];
|
||||
var posY = y == MAX_TILES_Y - 1 || !tiles[x, y + 1, z];
|
||||
var negZ = z == 0 || !tiles[x, y, z - 1];
|
||||
var posZ = z == MAX_TILES_Z - 1 || !tiles[x, y, z + 1];
|
||||
var negXnegZ = !negX && !negZ && x > 0 && z > 0 && !tiles[x - 1, y, z - 1];
|
||||
var negXposZ = !negX && !posZ && x > 0 && z < MAX_TILES_Z && !tiles[x - 1, y, z + 1];
|
||||
var posXposZ = !posX && !posZ && x < MAX_TILES_X && z < MAX_TILES_Z && !tiles[x + 1, y, z + 1];
|
||||
var posXnegZ = !posX && !negZ && x < MAX_TILES_X && z > 0 && !tiles[x + 1, y, z - 1];
|
||||
var faceNegX = negX && (IncludeFaces & TileFace.NegX) == TileFace.NegX;
|
||||
var facePosX = posX && (IncludeFaces & TileFace.PosX) == TileFace.PosX;
|
||||
var faceNegZ = negZ && (IncludeFaces & TileFace.NegZ) == TileFace.NegZ;
|
||||
var facePosZ = posZ && (IncludeFaces & TileFace.PosZ) == TileFace.PosZ;
|
||||
var foamNegX = negX && (IncludeFoam & TileFace.NegX) == TileFace.NegX;
|
||||
var foamPosX = posX && (IncludeFoam & TileFace.PosX) == TileFace.PosX;
|
||||
var foamNegZ = negZ && (IncludeFoam & TileFace.NegZ) == TileFace.NegZ;
|
||||
var foamPosZ = posZ && (IncludeFoam & TileFace.PosZ) == TileFace.PosZ;
|
||||
var foamNegXnegZ = negXnegZ && ((IncludeFoam & TileFace.NegX) == TileFace.NegX || (IncludeFoam & TileFace.NegZ) == TileFace.NegZ);
|
||||
var foamNegXposZ = negXposZ && ((IncludeFoam & TileFace.PosX) == TileFace.PosX || (IncludeFoam & TileFace.PosZ) == TileFace.PosZ);
|
||||
var foamPosXposZ = posXposZ && ((IncludeFoam & TileFace.NegZ) == TileFace.NegZ || (IncludeFoam & TileFace.PosZ) == TileFace.PosZ);
|
||||
var foamPosXnegZ = posXnegZ && ((IncludeFoam & TileFace.PosZ) == TileFace.PosZ || (IncludeFoam & TileFace.NegZ) == TileFace.NegZ);
|
||||
|
||||
// create the top face
|
||||
if (y == MAX_TILES_Y - 1 || !tiles[x, y + 1, z])
|
||||
{
|
||||
vertices.Add(new Vector3(x0, y1, z0));
|
||||
vertices.Add(new Vector3(x0, y1, z1));
|
||||
vertices.Add(new Vector3(x1, y1, z1));
|
||||
vertices.Add(new Vector3(x1, y1, z0));
|
||||
normals.Add(new Vector3(0, 1, 0));
|
||||
normals.Add(new Vector3(0, 1, 0));
|
||||
normals.Add(new Vector3(0, 1, 0));
|
||||
normals.Add(new Vector3(0, 1, 0));
|
||||
uvs.Add(new Vector2(ux0, uz0));
|
||||
uvs.Add(new Vector2(ux0, uz1));
|
||||
uvs.Add(new Vector2(ux1, uz1));
|
||||
uvs.Add(new Vector2(ux1, uz0));
|
||||
colors.Add(foamNegX || foamNegZ || foamNegXnegZ ? Color.red : Color.black);
|
||||
colors.Add(foamNegX || foamPosZ || foamNegXposZ ? Color.red : Color.black);
|
||||
colors.Add(foamPosX || foamPosZ || foamPosXposZ ? Color.red : Color.black);
|
||||
colors.Add(foamPosX || foamNegZ || foamPosXnegZ ? Color.red : Color.black);
|
||||
var v = vertices.Count - 4;
|
||||
if (foamNegX && foamPosZ || foamPosX && foamNegZ)
|
||||
{
|
||||
indices.Add(v + 1);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 3);
|
||||
indices.Add(v + 3);
|
||||
indices.Add(v);
|
||||
indices.Add(v + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
indices.Add(v);
|
||||
indices.Add(v + 1);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 3);
|
||||
indices.Add(v);
|
||||
}
|
||||
}
|
||||
|
||||
// create the side faces
|
||||
if (faceNegX)
|
||||
{
|
||||
vertices.Add(new Vector3(x0, y0, z1));
|
||||
vertices.Add(new Vector3(x0, y1, z1));
|
||||
vertices.Add(new Vector3(x0, y1, z0));
|
||||
vertices.Add(new Vector3(x0, y0, z0));
|
||||
normals.Add(new Vector3(-1, 0, 0));
|
||||
normals.Add(new Vector3(-1, 0, 0));
|
||||
normals.Add(new Vector3(-1, 0, 0));
|
||||
normals.Add(new Vector3(-1, 0, 0));
|
||||
uvs.Add(new Vector2(uz1, uy0));
|
||||
uvs.Add(new Vector2(uz1, uy1));
|
||||
uvs.Add(new Vector2(uz0, uy1));
|
||||
uvs.Add(new Vector2(uz0, uy0));
|
||||
colors.Add(Color.black);
|
||||
colors.Add(posY ? Color.red : Color.black);
|
||||
colors.Add(posY ? Color.red : Color.black);
|
||||
colors.Add(Color.black);
|
||||
var v = vertices.Count - 4;
|
||||
indices.Add(v);
|
||||
indices.Add(v + 1);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 3);
|
||||
indices.Add(v);
|
||||
}
|
||||
if (facePosX)
|
||||
{
|
||||
vertices.Add(new Vector3(x1, y0, z0));
|
||||
vertices.Add(new Vector3(x1, y1, z0));
|
||||
vertices.Add(new Vector3(x1, y1, z1));
|
||||
vertices.Add(new Vector3(x1, y0, z1));
|
||||
normals.Add(new Vector3(1, 0, 0));
|
||||
normals.Add(new Vector3(1, 0, 0));
|
||||
normals.Add(new Vector3(1, 0, 0));
|
||||
normals.Add(new Vector3(1, 0, 0));
|
||||
uvs.Add(new Vector2(uz0, uy0));
|
||||
uvs.Add(new Vector2(uz0, uy1));
|
||||
uvs.Add(new Vector2(uz1, uy1));
|
||||
uvs.Add(new Vector2(uz1, uy0));
|
||||
colors.Add(Color.black);
|
||||
colors.Add(posY ? Color.red : Color.black);
|
||||
colors.Add(posY ? Color.red : Color.black);
|
||||
colors.Add(Color.black);
|
||||
var v = vertices.Count - 4;
|
||||
indices.Add(v);
|
||||
indices.Add(v + 1);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 3);
|
||||
indices.Add(v);
|
||||
}
|
||||
if (faceNegZ)
|
||||
{
|
||||
vertices.Add(new Vector3(x0, y0, z0));
|
||||
vertices.Add(new Vector3(x0, y1, z0));
|
||||
vertices.Add(new Vector3(x1, y1, z0));
|
||||
vertices.Add(new Vector3(x1, y0, z0));
|
||||
normals.Add(new Vector3(0, 0, -1));
|
||||
normals.Add(new Vector3(0, 0, -1));
|
||||
normals.Add(new Vector3(0, 0, -1));
|
||||
normals.Add(new Vector3(0, 0, -1));
|
||||
uvs.Add(new Vector2(ux0, uy0));
|
||||
uvs.Add(new Vector2(ux0, uy1));
|
||||
uvs.Add(new Vector2(ux1, uy1));
|
||||
uvs.Add(new Vector2(ux1, uy0));
|
||||
colors.Add(Color.black);
|
||||
colors.Add(posY ? Color.red : Color.black);
|
||||
colors.Add(posY ? Color.red : Color.black);
|
||||
colors.Add(Color.black);
|
||||
var v = vertices.Count - 4;
|
||||
indices.Add(v);
|
||||
indices.Add(v + 1);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 3);
|
||||
indices.Add(v);
|
||||
}
|
||||
if (facePosZ)
|
||||
{
|
||||
vertices.Add(new Vector3(x1, y0, z1));
|
||||
vertices.Add(new Vector3(x1, y1, z1));
|
||||
vertices.Add(new Vector3(x0, y1, z1));
|
||||
vertices.Add(new Vector3(x0, y0, z1));
|
||||
normals.Add(new Vector3(0, 0, 1));
|
||||
normals.Add(new Vector3(0, 0, 1));
|
||||
normals.Add(new Vector3(0, 0, 1));
|
||||
normals.Add(new Vector3(0, 0, 1));
|
||||
uvs.Add(new Vector2(ux1, uy0));
|
||||
uvs.Add(new Vector2(ux1, uy1));
|
||||
uvs.Add(new Vector2(ux0, uy1));
|
||||
uvs.Add(new Vector2(ux0, uy0));
|
||||
colors.Add(Color.black);
|
||||
colors.Add(posY ? Color.red : Color.black);
|
||||
colors.Add(posY ? Color.red : Color.black);
|
||||
colors.Add(Color.black);
|
||||
var v = vertices.Count - 4;
|
||||
indices.Add(v);
|
||||
indices.Add(v + 1);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 2);
|
||||
indices.Add(v + 3);
|
||||
indices.Add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply the buffers
|
||||
mesh.SetVertices(vertices);
|
||||
mesh.SetNormals(normals);
|
||||
mesh.SetUVs(0, uvs);
|
||||
mesh.SetColors(colors);
|
||||
mesh.SetTriangles(indices, 0);
|
||||
|
||||
// update
|
||||
mesh.RecalculateBounds();
|
||||
//mesh.RecalculateNormals();
|
||||
mesh.RecalculateTangents();
|
||||
|
||||
meshFilter.sharedMesh = mesh;
|
||||
|
||||
// flag as clean
|
||||
isDirty = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual methods
|
||||
|
||||
protected virtual void GenerateTiles(ref bool[,,] _tiles) { }
|
||||
public virtual void Validate() { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region MonoBehaviour events
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
// keep tile size in a sensible range
|
||||
TileSize = Mathf.Clamp(TileSize, 0.1f, 100f);
|
||||
|
||||
// allow any child class to perform validation
|
||||
Validate();
|
||||
|
||||
// flag as needing rebuilding
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// rebuild if needed
|
||||
if (isDirty || (!Application.isPlaying && RealtimeUpdates))
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1912f68a566bec4429513ef3d402aaeb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 187485
|
||||
packageName: URP Stylized Water Shader - Proto Series
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/Bitgem/StylisedWater/URP/Scripts/Bitgem/VFX/StylisedWater/WaterVolumeBase.cs
|
||||
uploadId: 412673
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
#region Using statements
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Unheimlich.VFX.StylisedWater
|
||||
{
|
||||
[AddComponentMenu("Bitgem/Water Volume (Box)")]
|
||||
public class WaterVolumeBox : WaterVolumeBase
|
||||
{
|
||||
#region Public fields
|
||||
|
||||
public Vector3 Dimensions = Vector3.zero;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public methods
|
||||
|
||||
protected override void GenerateTiles(ref bool[,,] _tiles)
|
||||
{
|
||||
// calculate volume in tiles
|
||||
var maxX = Mathf.Clamp(Mathf.RoundToInt(Dimensions.x / TileSize), 1, MAX_TILES_X);
|
||||
var maxY = Mathf.Clamp(Mathf.RoundToInt(Dimensions.y / TileSize), 1, MAX_TILES_Y);
|
||||
var maxZ = Mathf.Clamp(Mathf.RoundToInt(Dimensions.z / TileSize), 1, MAX_TILES_Z);
|
||||
|
||||
// populate the tiles with a box volume
|
||||
for (var x = 0; x < maxX; x++)
|
||||
{
|
||||
for (var y = 0; y < maxY; y++)
|
||||
{
|
||||
for (var z = 0; z < maxZ; z++)
|
||||
{
|
||||
_tiles[x, y, z] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
// keep values sensible
|
||||
Dimensions.x = Mathf.Clamp(Dimensions.x, 1, MAX_TILES_X);
|
||||
Dimensions.y = Mathf.Clamp(Dimensions.y, 1, MAX_TILES_Y);
|
||||
Dimensions.z = Mathf.Clamp(Dimensions.z, 1, MAX_TILES_Z);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 427749d1fc2d12648a7acfc208590ea3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 187485
|
||||
packageName: URP Stylized Water Shader - Proto Series
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/Bitgem/StylisedWater/URP/Scripts/Bitgem/VFX/StylisedWater/WaterVolumeBox.cs
|
||||
uploadId: 412673
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
#region Using statements
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Unheimlich.VFX.StylisedWater
|
||||
{
|
||||
public class WaterVolumeHelper : MonoBehaviour
|
||||
{
|
||||
#region Private static fields
|
||||
|
||||
private static WaterVolumeHelper instance = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public fields
|
||||
|
||||
public WaterVolumeBase WaterVolume = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public static properties
|
||||
|
||||
public static WaterVolumeHelper Instance { get { return instance; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public methods
|
||||
|
||||
public float? GetHeight(Vector3 _position)
|
||||
{
|
||||
// ensure a water volume
|
||||
if (!WaterVolume)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
// ensure a material
|
||||
var renderer = WaterVolume.gameObject.GetComponent<MeshRenderer>();
|
||||
if (!renderer || !renderer.sharedMaterial)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
// replicate the shader logic, using parameters pulled from the specific material, to return the height at the specified position
|
||||
var waterHeight = WaterVolume.GetHeight(_position);
|
||||
if (!waterHeight.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var _WaveFrequency = renderer.sharedMaterial.GetFloat("_WaveFrequency");
|
||||
var _WaveScale = renderer.sharedMaterial.GetFloat("_WaveScale");
|
||||
var _WaveSpeed = renderer.sharedMaterial.GetFloat("_WaveSpeed");
|
||||
var time = Time.time * _WaveSpeed;
|
||||
var shaderOffset = (Mathf.Sin(_position.x * _WaveFrequency + time) + Mathf.Cos(_position.z * _WaveFrequency + time)) * _WaveScale;
|
||||
return waterHeight.Value + shaderOffset;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MonoBehaviour events
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf39edffa406b4d4c8db438c744f592a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 187485
|
||||
packageName: URP Stylized Water Shader - Proto Series
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/Bitgem/StylisedWater/URP/Scripts/Bitgem/VFX/StylisedWater/WaterVolumeHelper.cs
|
||||
uploadId: 412673
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
#region Using statements
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Unheimlich.VFX.StylisedWater
|
||||
{
|
||||
[AddComponentMenu("Bitgem/Water Volume (Transforms)")]
|
||||
public class WaterVolumeTransforms : WaterVolumeBase
|
||||
{
|
||||
#region MonoBehaviour events
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (!ShowDebug)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate the chldren
|
||||
for (var i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
// grab the local position/scale
|
||||
var pos = transform.GetChild(i).localPosition;
|
||||
var sca = transform.GetChild(i).localScale / TileSize;
|
||||
|
||||
// fix to the grid
|
||||
var x = Mathf.RoundToInt(pos.x / TileSize);
|
||||
var y = Mathf.RoundToInt(pos.y / TileSize);
|
||||
var z = Mathf.RoundToInt(pos.z / TileSize);
|
||||
|
||||
var drawPos = new Vector3(x, y, z) * TileSize;
|
||||
var drawSca = new Vector3(Mathf.RoundToInt(sca.x), Mathf.RoundToInt(sca.y), Mathf.RoundToInt(sca.z)) * TileSize;
|
||||
drawPos += drawSca / 2f;
|
||||
drawPos += transform.position;
|
||||
drawPos -= new Vector3(TileSize, TileSize, TileSize);
|
||||
|
||||
// render as wired volumes
|
||||
Gizmos.DrawWireCube(drawPos, drawSca);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTransformChildrenChanged()
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public methods
|
||||
|
||||
protected override void GenerateTiles(ref bool[,,] _tiles)
|
||||
{
|
||||
// iterate the chldren
|
||||
for (var i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
// grab the local position/scale
|
||||
var pos = transform.GetChild(i).localPosition;
|
||||
var sca = transform.GetChild(i).localScale / TileSize;
|
||||
|
||||
// fix to the grid
|
||||
var x = Mathf.RoundToInt(pos.x / TileSize);
|
||||
var y = Mathf.RoundToInt(pos.y / TileSize);
|
||||
var z = Mathf.RoundToInt(pos.z / TileSize);
|
||||
|
||||
// iterate the size of the transform
|
||||
for (var ix = x; ix < x + Mathf.RoundToInt(sca.x); ix++)
|
||||
{
|
||||
for (var iy = y; iy < y + Mathf.RoundToInt(sca.y); iy++)
|
||||
{
|
||||
for (var iz = z; iz < z + Mathf.RoundToInt(sca.z); iz++)
|
||||
{
|
||||
// validate
|
||||
if (ix < 0 || ix >= MAX_TILES_X || iy < 0 | iy >= MAX_TILES_Y || iz < 0 || iz >= MAX_TILES_Z)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// add the tile
|
||||
_tiles[ix, iy, iz] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d1bc949ada500e40a47e0e6f07b2d86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 187485
|
||||
packageName: URP Stylized Water Shader - Proto Series
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/Bitgem/StylisedWater/URP/Scripts/Bitgem/VFX/StylisedWater/WaterVolumeTransforms.cs
|
||||
uploadId: 412673
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#region Using statements
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Unheimlich.VFX.StylisedWater
|
||||
{
|
||||
public class WateverVolumeFloater : MonoBehaviour
|
||||
{
|
||||
#region Public fields
|
||||
|
||||
public WaterVolumeHelper WaterVolumeHelper = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region MonoBehaviour events
|
||||
|
||||
void Update()
|
||||
{
|
||||
var instance = WaterVolumeHelper ? WaterVolumeHelper : WaterVolumeHelper.Instance;
|
||||
if (!instance)
|
||||
{
|
||||
Debug.Log("No Instance !");
|
||||
return;
|
||||
}
|
||||
|
||||
transform.position = new Vector3(transform.position.x, instance.GetHeight(transform.position) ?? transform.position.y, transform.position.z);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36aa02d38bb10b548bc7b8879596b15c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 187485
|
||||
packageName: URP Stylized Water Shader - Proto Series
|
||||
packageVersion: 1.0
|
||||
assetPath: Assets/Bitgem/StylisedWater/URP/Scripts/Bitgem/VFX/StylisedWater/WateverVolumeFloater.cs
|
||||
uploadId: 412673
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 349e49a74428e134384bca6c39087939
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e4596c87b143aa43ab66f8e39ea85c9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
using UnityEngine;
|
||||
using Unheimlich.Features.Player;
|
||||
|
||||
namespace Unheimlich.World.HouseOfLeaves
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggers an extreme FOV zoom when the player reaches the end of the corridor.
|
||||
/// Attach to a trigger collider placed at the corridor exit.
|
||||
/// </summary>
|
||||
public class CorridorEndTrigger : MonoBehaviour
|
||||
{
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Serialized Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Header("Zoom")]
|
||||
[Tooltip("Target FOV when the player hits the trigger. Low value = extreme zoom.")]
|
||||
[SerializeField] private float _targetFov = 5f;
|
||||
|
||||
[Tooltip("Speed of the zoom lerp.")]
|
||||
[SerializeField] private float _zoomSpeed = 3f;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Private Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private bool _triggered;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Unity Messages
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (_triggered) return;
|
||||
if (other.GetComponent<OctoCore.Components.OctoPlayer>() == null) return;
|
||||
|
||||
_triggered = true;
|
||||
|
||||
PlayerController.Instance.CameraEffects.enabled = false;
|
||||
|
||||
var dolly = FindFirstObjectByType<DollyZoomEffect>();
|
||||
if (dolly) dolly.SetEnabled(false);
|
||||
|
||||
var hol = FindFirstObjectByType<HouseOfLeavesController>();
|
||||
if (hol) hol.enabled = false;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_triggered) return;
|
||||
if (PlayerController.Instance?.PlayerCamera == null) return;
|
||||
|
||||
PlayerController.Instance.PlayerCamera.fieldOfView = Mathf.Lerp(
|
||||
PlayerController.Instance.PlayerCamera.fieldOfView,
|
||||
_targetFov,
|
||||
Time.deltaTime * _zoomSpeed
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2138d5849f491f4e8ed3ccee1061493
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unheimlich.World.HouseOfLeaves
|
||||
{
|
||||
/// <summary>
|
||||
/// Physically moves the corridor walls inward based on the player's progression,
|
||||
/// creating a genuine sense of spatial compression without relying on FOV tricks.
|
||||
///
|
||||
/// Reads progression from <see cref="HouseOfLeavesController"/> via its public
|
||||
/// <see cref="HouseOfLeavesController.Progression"/> property.
|
||||
/// </summary>
|
||||
public class CorridorWallsController : MonoBehaviour
|
||||
{
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Serialized Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Header("References")]
|
||||
[Tooltip("The HouseOfLeavesController driving the progression value.")]
|
||||
[SerializeField] private HouseOfLeavesController _controller;
|
||||
|
||||
[Tooltip("Left wall plane.")]
|
||||
[SerializeField] private Transform _wallLeft;
|
||||
|
||||
[Tooltip("Right wall plane.")]
|
||||
[SerializeField] private Transform _wallRight;
|
||||
|
||||
[Tooltip("Ceiling plane.")]
|
||||
[SerializeField] private Transform _wallCeiling;
|
||||
|
||||
[Tooltip("Floor plane.")]
|
||||
[SerializeField] private Transform _wallFloor;
|
||||
|
||||
[Header("Compression")]
|
||||
[Tooltip("How far (meters) each side wall moves inward at full progression.")]
|
||||
[SerializeField] private float _maxSideInset = 2f;
|
||||
|
||||
[Tooltip("How far (meters) the ceiling moves down at full progression.")]
|
||||
[SerializeField] private float _maxCeilingInset = 1f;
|
||||
|
||||
[Tooltip("How far (meters) the floor moves up at full progression.")]
|
||||
[SerializeField] private float _maxFloorInset = 0f;
|
||||
|
||||
[Tooltip("Lerp speed applied to wall movement each frame.")]
|
||||
[SerializeField] private float _lerpSpeed = 2f;
|
||||
|
||||
[Tooltip("Animation curve mapping progression [0,1] to compression [0,1]. " +
|
||||
"Lets you tune when walls start closing in.")]
|
||||
[SerializeField] private AnimationCurve _compressionCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Private Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Vector3 _initialLeftPos;
|
||||
private Vector3 _initialRightPos;
|
||||
private Vector3 _initialCeilingPos;
|
||||
private Vector3 _initialFloorPos;
|
||||
|
||||
private bool _isInitialized;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Unity Messages
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_controller == null)
|
||||
{
|
||||
Debug.LogError("[CorridorWallsController] HouseOfLeavesController reference is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_wallLeft) _initialLeftPos = _wallLeft.position;
|
||||
if (_wallRight) _initialRightPos = _wallRight.position;
|
||||
if (_wallCeiling) _initialCeilingPos = _wallCeiling.position;
|
||||
if (_wallFloor) _initialFloorPos = _wallFloor.position;
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_isInitialized) return;
|
||||
|
||||
float t = _compressionCurve.Evaluate(_controller.Progression);
|
||||
float lerpDelta = Time.deltaTime * _lerpSpeed;
|
||||
|
||||
if (_wallLeft)
|
||||
{
|
||||
Vector3 target = _initialLeftPos + Vector3.right * _maxSideInset * t;
|
||||
_wallLeft.position = Vector3.Lerp(_wallLeft.position, target, lerpDelta);
|
||||
}
|
||||
|
||||
if (_wallRight)
|
||||
{
|
||||
Vector3 target = _initialRightPos + Vector3.left * _maxSideInset * t;
|
||||
_wallRight.position = Vector3.Lerp(_wallRight.position, target, lerpDelta);
|
||||
}
|
||||
|
||||
if (_wallCeiling)
|
||||
{
|
||||
Vector3 target = _initialCeilingPos + Vector3.down * _maxCeilingInset * t;
|
||||
_wallCeiling.position = Vector3.Lerp(_wallCeiling.position, target, lerpDelta);
|
||||
}
|
||||
|
||||
if (_wallFloor)
|
||||
{
|
||||
Vector3 target = _initialFloorPos + Vector3.up * _maxFloorInset * t;
|
||||
_wallFloor.position = Vector3.Lerp(_wallFloor.position, target, lerpDelta);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Editor
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!_isInitialized) return;
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
if (_wallLeft) Gizmos.DrawWireCube(_initialLeftPos + Vector3.right * _maxSideInset, Vector3.one * 0.3f);
|
||||
if (_wallRight) Gizmos.DrawWireCube(_initialRightPos + Vector3.left * _maxSideInset, Vector3.one * 0.3f);
|
||||
if (_wallCeiling) Gizmos.DrawWireCube(_initialCeilingPos + Vector3.down * _maxCeilingInset, Vector3.one * 0.3f);
|
||||
if (_wallFloor) Gizmos.DrawWireCube(_initialFloorPos + Vector3.up * _maxFloorInset, Vector3.one * 0.3f);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 502932957cfd6bc44856b113b9f4fa87
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using Unheimlich.Features.Player;
|
||||
|
||||
namespace Unheimlich.World.HouseOfLeaves
|
||||
{
|
||||
/// <summary>
|
||||
/// Orchestrates the core House of Leaves experience.
|
||||
/// Script Execution Order: set to -10 so SetBaseFovOverride is written
|
||||
/// before CameraEffects.Update consumes it in the same frame.
|
||||
/// </summary>
|
||||
public class HouseOfLeavesController : MonoBehaviour
|
||||
{
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Serialized Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Header("Corridor")]
|
||||
[Tooltip("Total length of the corridor (meters).")]
|
||||
[SerializeField] private float _corridorLength = 100f;
|
||||
|
||||
[Header("Speed Ramp")]
|
||||
[SerializeField] private float _initialWalkSpeed = 10f;
|
||||
[SerializeField] private float _maxWalkSpeed = 22f;
|
||||
|
||||
[Header("FOV Ramp")]
|
||||
[SerializeField] private float _initialFov = 70f;
|
||||
[SerializeField] private float _minFov = 45f;
|
||||
[SerializeField] private float _fovLerpSpeed = 2f;
|
||||
|
||||
[Header("Darkness")]
|
||||
[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;
|
||||
|
||||
[Header("Light Source")]
|
||||
[SerializeField] private Transform _lightSourceReference;
|
||||
|
||||
[Header("Fracture")]
|
||||
[SerializeField] private Material _fractureMaterial;
|
||||
[SerializeField] private AnimationCurve _fractureCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Private Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private OctoCore.Components.OctoPlayer _octoPlayer;
|
||||
private Camera _playerCamera;
|
||||
private CameraEffects _cameraEffects;
|
||||
|
||||
private FieldInfo _settingsFieldInfo;
|
||||
private FieldInfo _walkSpeedFieldInfo;
|
||||
private object _settingsInstance;
|
||||
|
||||
private Vignette _vignette;
|
||||
private ColorAdjustments _colorAdjustments;
|
||||
|
||||
private Vector3 _lastPlayerPosition;
|
||||
private float _distanceTravelled;
|
||||
private float _currentFov;
|
||||
private Vector3 _lightForwardDirection;
|
||||
private bool _isInitialized;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Unity Messages
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (!TryResolvePlayerReferences()) return;
|
||||
|
||||
InitializeSpeedReflection();
|
||||
InitializeFov();
|
||||
InitializeLightDirection();
|
||||
InitializePostProcess();
|
||||
|
||||
_lastPlayerPosition = PlayerController.Instance.transform.position;
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_isInitialized) return;
|
||||
|
||||
TrackDistance();
|
||||
|
||||
float t = Mathf.Clamp01(_distanceTravelled / _corridorLength);
|
||||
|
||||
TickSpeed(t);
|
||||
TickFov(t);
|
||||
TickDarkness();
|
||||
TickFracture(t);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Initialization
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private bool TryResolvePlayerReferences()
|
||||
{
|
||||
if (PlayerController.Instance == null)
|
||||
{
|
||||
Debug.LogError("[HouseOfLeavesController] PlayerController.Instance not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
_octoPlayer = PlayerController.Instance.Movement;
|
||||
_playerCamera = PlayerController.Instance.PlayerCamera;
|
||||
_cameraEffects = PlayerController.Instance.CameraEffects;
|
||||
|
||||
if (_octoPlayer == null || _playerCamera == null)
|
||||
{
|
||||
Debug.LogError("[HouseOfLeavesController] OctoPlayer or PlayerCamera could not be resolved.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cameraEffects == null)
|
||||
Debug.LogWarning("[HouseOfLeavesController] CameraEffects not found — FOV kick sync disabled.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void InitializeSpeedReflection()
|
||||
{
|
||||
System.Type playerType = typeof(OctoCore.Components.OctoPlayer);
|
||||
|
||||
_settingsFieldInfo = playerType.GetField(
|
||||
"_settings", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (_settingsFieldInfo == null)
|
||||
{
|
||||
Debug.LogError("[HouseOfLeavesController] Could not find '_settings' on OctoPlayer via reflection.");
|
||||
return;
|
||||
}
|
||||
|
||||
_settingsInstance = _settingsFieldInfo.GetValue(_octoPlayer);
|
||||
|
||||
_walkSpeedFieldInfo = _settingsInstance.GetType().GetField(
|
||||
"WalkSpeed", BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
if (_walkSpeedFieldInfo == null)
|
||||
Debug.LogError("[HouseOfLeavesController] Could not find 'WalkSpeed' on OctoPlayer.Settings via reflection.");
|
||||
}
|
||||
|
||||
private void InitializeFov()
|
||||
{
|
||||
_currentFov = _initialFov;
|
||||
_playerCamera.fieldOfView = _initialFov;
|
||||
}
|
||||
|
||||
private void InitializeLightDirection()
|
||||
{
|
||||
_lightForwardDirection = _lightSourceReference != null
|
||||
? _lightSourceReference.forward
|
||||
: PlayerController.Instance.transform.forward;
|
||||
|
||||
_lightForwardDirection.y = 0f;
|
||||
_lightForwardDirection.Normalize();
|
||||
}
|
||||
|
||||
private void InitializePostProcess()
|
||||
{
|
||||
if (_postProcessVolume == null)
|
||||
{
|
||||
Debug.LogWarning("[HouseOfLeavesController] No URP Volume assigned — darkness effect disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
_postProcessVolume.profile.TryGet(out _vignette);
|
||||
_postProcessVolume.profile.TryGet(out _colorAdjustments);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Distance Tracking
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void TrackDistance()
|
||||
{
|
||||
Vector3 currentPosition = PlayerController.Instance.transform.position;
|
||||
Vector3 delta = currentPosition - _lastPlayerPosition;
|
||||
delta.y = 0f;
|
||||
|
||||
_distanceTravelled += Vector3.Dot(delta, _lightForwardDirection);
|
||||
_distanceTravelled = Mathf.Clamp(_distanceTravelled, 0f, _corridorLength);
|
||||
|
||||
_lastPlayerPosition = currentPosition;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Tick Methods
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void TickSpeed(float t)
|
||||
{
|
||||
if (_walkSpeedFieldInfo == null || _settingsInstance == null) return;
|
||||
|
||||
float newSpeed = Mathf.Lerp(_initialWalkSpeed, _maxWalkSpeed, t);
|
||||
_walkSpeedFieldInfo.SetValue(_settingsInstance, newSpeed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lerps the FOV and submits it to CameraEffects as the base for this frame's
|
||||
/// sprint kick. CameraEffects.Update then does the authoritative fieldOfView write.
|
||||
/// This component must run before CameraEffects (Execution Order -10).
|
||||
/// </summary>
|
||||
private void TickFov(float t)
|
||||
{
|
||||
float targetFov = Mathf.Lerp(_initialFov, _minFov, t);
|
||||
_currentFov = Mathf.Lerp(_currentFov, targetFov, Time.deltaTime * _fovLerpSpeed);
|
||||
|
||||
// Submit to CameraEffects — it will write fieldOfView = _currentFov + kickOffset.
|
||||
// If CameraEffects is absent, write directly as fallback.
|
||||
if (_cameraEffects != null)
|
||||
_cameraEffects.SetBaseFovOverride(_currentFov);
|
||||
else
|
||||
_playerCamera.fieldOfView = _currentFov;
|
||||
}
|
||||
|
||||
private void TickDarkness()
|
||||
{
|
||||
if (_vignette == null && _colorAdjustments == null) return;
|
||||
|
||||
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;
|
||||
|
||||
float darknessT = Mathf.Clamp01(
|
||||
(angleDegrees - _turningBackAngle) / (180f - _turningBackAngle)
|
||||
);
|
||||
|
||||
float lerpDelta = Time.deltaTime * _darknessLerpSpeed;
|
||||
|
||||
if (_vignette != null)
|
||||
_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);
|
||||
}
|
||||
|
||||
private void TickFracture(float t)
|
||||
{
|
||||
float fractureT = _fractureCurve.Evaluate(t);
|
||||
_fractureMaterial?.SetFloat("_FractureAmount", fractureT);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Public API
|
||||
|
||||
/// <summary>Current corridor progression in [0, 1].</summary>
|
||||
public float Progression => _corridorLength > 0f
|
||||
? Mathf.Clamp01(_distanceTravelled / _corridorLength)
|
||||
: 0f;
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public void ResetProgression()
|
||||
{
|
||||
_distanceTravelled = 0f;
|
||||
_lastPlayerPosition = PlayerController.Instance.transform.position;
|
||||
}
|
||||
|
||||
public void SetLightDirection(Vector3 worldDirection)
|
||||
{
|
||||
_lightForwardDirection = worldDirection;
|
||||
_lightForwardDirection.y = 0f;
|
||||
_lightForwardDirection.Normalize();
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Editor
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawRay(transform.position + Vector3.up, _lightForwardDirection * 4f);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af6f7d258d38edf44a956a642c1f414d
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unheimlich.World.HouseOfLeaves
|
||||
{
|
||||
/// <summary>
|
||||
/// Animates the sand terrain rising from below the corridor floor
|
||||
/// based on the player's progression through the corridor.
|
||||
///
|
||||
/// The terrain starts buried underground and slowly emerges as the
|
||||
/// player advances, simulating sand flooding the corridor from below.
|
||||
/// </summary>
|
||||
public class SandTerrainController : MonoBehaviour
|
||||
{
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Serialized Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Header("References")]
|
||||
[Tooltip("The HouseOfLeavesController driving the progression value.")]
|
||||
[SerializeField] private HouseOfLeavesController _controller;
|
||||
|
||||
[Tooltip("The sand terrain Transform to animate.")]
|
||||
[SerializeField] private Transform _sandTerrain;
|
||||
|
||||
[Header("Rise Settings")]
|
||||
[Tooltip("Y position of the terrain at the start (buried underground).")]
|
||||
[SerializeField] private float _startY = -4f;
|
||||
|
||||
[Tooltip("Y position of the terrain at full progression (emerged).")]
|
||||
[SerializeField] private float _endY = 0.5f;
|
||||
|
||||
[Tooltip("Lerp speed applied to the vertical movement each frame.")]
|
||||
[SerializeField] private float _lerpSpeed = 2f;
|
||||
|
||||
[Tooltip("Animation curve mapping progression [0,1] to rise amount [0,1]. " +
|
||||
"Use this to delay the rise until mid-corridor for example.")]
|
||||
[SerializeField] private AnimationCurve _riseCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Unity Messages
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_controller == null)
|
||||
{
|
||||
Debug.LogError("[SandTerrainController] HouseOfLeavesController reference is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_sandTerrain == null)
|
||||
{
|
||||
Debug.LogError("[SandTerrainController] Sand terrain Transform is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize position immediately without lerp.
|
||||
Vector3 startPos = _sandTerrain.position;
|
||||
startPos.y = _startY;
|
||||
_sandTerrain.position = startPos;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_controller == null || _sandTerrain == null) return;
|
||||
|
||||
float t = _riseCurve.Evaluate(_controller.Progression);
|
||||
float targetY = Mathf.Lerp(_startY, _endY, t);
|
||||
|
||||
Vector3 pos = _sandTerrain.position;
|
||||
pos.y = Mathf.Lerp(pos.y, targetY, Time.deltaTime * _lerpSpeed);
|
||||
_sandTerrain.position = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb5cadc4360bf0d4a8b6d071a76cc0b7
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b4f4d1b773da0142ad768322d0420f7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b6f1184f24f5ad4ab849b0e96679499
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,254 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
public class ScarecrowFire : MonoBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
public class FireElement
|
||||
{
|
||||
public GameObject VFX;
|
||||
/// <summary>Original scale captured at initialization, used as lerp target.</summary>
|
||||
[HideInInspector] public Vector3 OriginalScale;
|
||||
}
|
||||
|
||||
[Header("Fire VFX Elements")]
|
||||
[SerializeField] private FireElement _fireBase;
|
||||
[SerializeField] private FireElement _fireTorso;
|
||||
[SerializeField] private FireElement _fireArmLeft;
|
||||
[SerializeField] private FireElement _fireArmLeftEnd;
|
||||
[SerializeField] private FireElement _fireArmRight;
|
||||
[SerializeField] private FireElement _fireArmRightEnd;
|
||||
[SerializeField] private FireElement _fireHead;
|
||||
[SerializeField] private FireElement _fireBig;
|
||||
|
||||
[Header("Propagation Timings")]
|
||||
[Tooltip("Duration of the scale lerp for each fire element (seconds).")]
|
||||
[SerializeField] private float _growDuration = 3f;
|
||||
|
||||
[Tooltip("How early the next stage starts before the current one finishes (0 = no overlap, 1 = instant).")]
|
||||
[Range(0f, 1f)]
|
||||
[SerializeField] private float _overlapFactor = 0.4f;
|
||||
|
||||
[Tooltip("Curve driving the scale growth — tweak directly in the Inspector.")]
|
||||
[SerializeField] private AnimationCurve _growCurve = new AnimationCurve(
|
||||
new Keyframe(0f, 0f, 0f, 0f),
|
||||
new Keyframe(0.3f, 0.1f, 0f, 2f),
|
||||
new Keyframe(0.7f, 0.8f, 2f, 0.5f),
|
||||
new Keyframe(1f, 1f, 0.2f, 0f)
|
||||
);
|
||||
|
||||
[Header("Post Process")]
|
||||
[Tooltip("Volume driving the heat haze effect — weight is driven up as fire spreads.")]
|
||||
[SerializeField] private Volume _heatVolume;
|
||||
|
||||
[Tooltip("Curve driving all heat effects over the full sequence duration.")]
|
||||
[SerializeField] private AnimationCurve _heatCurve = new AnimationCurve(
|
||||
new Keyframe(0f, 0f, 0f, 0.5f),
|
||||
new Keyframe(0.5f, 0.4f, 1f, 1f),
|
||||
new Keyframe(1f, 1f, 0.5f, 0f)
|
||||
);
|
||||
|
||||
[Header("Heat Shader")]
|
||||
[Tooltip("Material using the HeatHaze shader — _Weight property is driven from 0 to 1.")]
|
||||
[SerializeField] private Material _heatMaterial;
|
||||
|
||||
[Header("Light")]
|
||||
[Tooltip("LightFlicker component to drive — baseIntensity is ramped up with the fire.")]
|
||||
[SerializeField] private MonoLimboStudio.LightFlicker _lightFlicker;
|
||||
|
||||
[Tooltip("Final light intensity reached when the fire is fully ablaze.")]
|
||||
[SerializeField] private float _targetLightIntensity = 3f;
|
||||
|
||||
// Flag set when the last GrowFire completes — DriveHeatEffects waits on this
|
||||
private bool _fireSequenceComplete = false;
|
||||
private float _totalSequenceDuration = 0f;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
void Awake()
|
||||
{
|
||||
InitElement(_fireBase);
|
||||
InitElement(_fireTorso);
|
||||
InitElement(_fireArmLeft);
|
||||
InitElement(_fireArmLeftEnd);
|
||||
InitElement(_fireArmRight);
|
||||
InitElement(_fireArmRightEnd);
|
||||
InitElement(_fireHead);
|
||||
InitElement(_fireBig);
|
||||
|
||||
// Used as a time reference for _heatCurve mapping — doesn't need to be exact
|
||||
// since DriveHeatEffects now syncs to _fireSequenceComplete rather than this value
|
||||
float overlapDelay = _growDuration * (1f - _overlapFactor);
|
||||
_totalSequenceDuration = _growDuration + (7f * overlapDelay);
|
||||
|
||||
// Ensure everything starts at zero
|
||||
SetHeatShaderWeight(0f);
|
||||
|
||||
if (_lightFlicker)
|
||||
_lightFlicker.SetBaseIntensity(0f);
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
TriggerFire();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures the original Inspector scale, then sets the starting scale to
|
||||
/// (x * 0.2, y * 0.1, z * 0.2) so each element grows from near-nothing.
|
||||
/// </summary>
|
||||
private void InitElement(FireElement el)
|
||||
{
|
||||
if (!el.VFX) return;
|
||||
|
||||
el.OriginalScale = el.VFX.transform.localScale;
|
||||
el.VFX.transform.localScale = new Vector3(
|
||||
el.OriginalScale.x * 0.2f,
|
||||
el.OriginalScale.y * 0.1f,
|
||||
el.OriginalScale.z * 0.2f
|
||||
);
|
||||
el.VFX.SetActive(false);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Starts the full fire propagation sequence.</summary>
|
||||
public void TriggerFire()
|
||||
{
|
||||
_fireSequenceComplete = false;
|
||||
StartCoroutine(FireSequence());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Propagates fire bottom-up with overlapping stages for an organic feel.
|
||||
/// Sets _fireSequenceComplete when the last element finishes growing.
|
||||
/// </summary>
|
||||
private IEnumerator FireSequence()
|
||||
{
|
||||
float overlapDelay = _growDuration * (1f - _overlapFactor);
|
||||
|
||||
// Drive all heat effects independently — syncs via _fireSequenceComplete
|
||||
StartCoroutine(DriveHeatEffects());
|
||||
|
||||
// Feet
|
||||
StartCoroutine(GrowFire(_fireBase));
|
||||
yield return new WaitForSeconds(overlapDelay);
|
||||
|
||||
// Torso overlaps with feet still growing
|
||||
StartCoroutine(GrowFire(_fireTorso));
|
||||
yield return new WaitForSeconds(overlapDelay);
|
||||
|
||||
// Both arms ignite simultaneously, each propagating from shoulder to tip
|
||||
StartCoroutine(PropagateArm(_fireArmLeft, _fireArmLeftEnd, overlapDelay));
|
||||
StartCoroutine(PropagateArm(_fireArmRight, _fireArmRightEnd, overlapDelay));
|
||||
yield return new WaitForSeconds(overlapDelay * 2f);
|
||||
|
||||
// Head
|
||||
StartCoroutine(GrowFire(_fireHead));
|
||||
yield return new WaitForSeconds(overlapDelay);
|
||||
|
||||
// Full blaze — wait for it to fully complete before flagging done
|
||||
yield return StartCoroutine(GrowFire(_fireBig));
|
||||
|
||||
_fireSequenceComplete = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Propagates fire along one arm: shoulder ignites first,
|
||||
/// then tip starts after overlapDelay rather than waiting for shoulder to finish.
|
||||
/// </summary>
|
||||
private IEnumerator PropagateArm(FireElement shoulder, FireElement tip, float overlapDelay)
|
||||
{
|
||||
StartCoroutine(GrowFire(shoulder));
|
||||
yield return new WaitForSeconds(overlapDelay);
|
||||
yield return StartCoroutine(GrowFire(tip));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates a fire element and lerps its scale from near-zero to its original
|
||||
/// Inspector scale, driven by _growCurve for an organic progression.
|
||||
/// </summary>
|
||||
private IEnumerator GrowFire(FireElement el)
|
||||
{
|
||||
if (!el.VFX) yield break;
|
||||
|
||||
el.VFX.SetActive(true);
|
||||
|
||||
Vector3 startScale = el.VFX.transform.localScale;
|
||||
Vector3 targetScale = el.OriginalScale;
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < _growDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / _growDuration);
|
||||
float curveT = _growCurve.Evaluate(t);
|
||||
|
||||
el.VFX.transform.localScale = Vector3.Lerp(startScale, targetScale, curveT);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
el.VFX.transform.localScale = targetScale;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Drives all heat effects (post-process volume, heat shader, light intensity)
|
||||
/// using _heatCurve until _fireSequenceComplete is set, then snaps cleanly to 1.
|
||||
/// Decoupled from individual fire element timing for smoother blending.
|
||||
/// </summary>
|
||||
private IEnumerator DriveHeatEffects()
|
||||
{
|
||||
float elapsed = 0f;
|
||||
|
||||
while (!_fireSequenceComplete)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = elapsed / _totalSequenceDuration;
|
||||
float weight = _heatCurve.Evaluate(Mathf.Min(t, 0.99f));
|
||||
|
||||
if (_heatVolume)
|
||||
_heatVolume.weight = weight;
|
||||
|
||||
SetHeatShaderWeight(weight);
|
||||
|
||||
if (_lightFlicker)
|
||||
_lightFlicker.SetBaseIntensity(_targetLightIntensity * weight);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
float lerpElapsed = 0f;
|
||||
float lerpDuration = 8f;
|
||||
float startWeight = _heatCurve.Evaluate(0.99f);
|
||||
|
||||
while (lerpElapsed < lerpDuration)
|
||||
{
|
||||
lerpElapsed += Time.deltaTime;
|
||||
float weight = Mathf.Lerp(startWeight, 1f, lerpElapsed / lerpDuration);
|
||||
|
||||
if (_heatVolume) _heatVolume.weight = weight;
|
||||
SetHeatShaderWeight(weight);
|
||||
if (_lightFlicker) _lightFlicker.SetBaseIntensity(_targetLightIntensity * weight);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (_heatVolume) _heatVolume.weight = 1f;
|
||||
SetHeatShaderWeight(1f);
|
||||
if (_lightFlicker) _lightFlicker.SetBaseIntensity(_targetLightIntensity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drives the heat shader via a single normalized weight (0-1).
|
||||
/// All visual parameters are tweaked directly on the material in the Inspector.
|
||||
/// </summary>
|
||||
private void SetHeatShaderWeight(float weight)
|
||||
{
|
||||
if (!_heatMaterial) return;
|
||||
_heatMaterial.SetFloat("_Weight", weight);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fbd34556207d3134180ab200fc2ec560
|
||||
@@ -4,7 +4,6 @@ using Unheimlich.Core;
|
||||
using Unheimlich.Features.Player.Data;
|
||||
using Unheimlich.Features.Dialogue;
|
||||
using InputManager = Unheimlich.Core.Input.InputManager;
|
||||
using OctoCore.Components;
|
||||
|
||||
namespace Unheimlich.Features.Player
|
||||
{
|
||||
@@ -28,6 +27,11 @@ namespace Unheimlich.Features.Player
|
||||
private float _baseFov = -1f;
|
||||
private float _currentFovOffset = 0f;
|
||||
|
||||
// External FOV override — when >= 0, UpdateFovKick uses this as the base FOV
|
||||
// instead of the locally cached snapshot. Set by HouseOfLeavesController each frame.
|
||||
// Consumed once per frame — caller must re-submit every Update.
|
||||
private float _externalBaseFov = -1f;
|
||||
|
||||
public bool IsAimLocked { get; private set; }
|
||||
public bool HasActiveLookInput { get; private set; }
|
||||
public bool IsUsingGamepad { get; private set; }
|
||||
@@ -53,24 +57,40 @@ namespace Unheimlich.Features.Player
|
||||
ApplyAimAssistInternal(_pendingAimAssistTarget);
|
||||
}
|
||||
|
||||
// ── FOV Kick ─────────────────────────────────────────────────────────────
|
||||
// ── FOV Kick ──────────────────────────────────────────────────────────────
|
||||
|
||||
private void UpdateFovKick()
|
||||
{
|
||||
if (Settings == null || PlayerController.Instance?.PlayerCamera == null) return;
|
||||
|
||||
// Capture base FOV once (handles cases where FOV is changed externally before Start).
|
||||
if (_baseFov < 0f)
|
||||
_baseFov = PlayerController.Instance.PlayerCamera.fieldOfView;
|
||||
// Use the external override if submitted this frame, otherwise fall back
|
||||
// to the locally cached base FOV.
|
||||
float effectiveBase = _externalBaseFov >= 0f ? _externalBaseFov : _baseFov;
|
||||
|
||||
float targetOffset = PlayerController.Instance.IsRunning ? Settings.fovKickAmount : 0f;
|
||||
_currentFovOffset = Mathf.Lerp(_currentFovOffset, targetOffset, Time.deltaTime * Settings.fovKickSpeed);
|
||||
if (effectiveBase < 0f)
|
||||
{
|
||||
effectiveBase = PlayerController.Instance.PlayerCamera.fieldOfView;
|
||||
_baseFov = effectiveBase;
|
||||
}
|
||||
|
||||
PlayerController.Instance.PlayerCamera.fieldOfView = _baseFov + _currentFovOffset;
|
||||
float targetOffset = PlayerController.Instance.IsRunning ? Settings.fovKickAmount : 0f;
|
||||
_currentFovOffset = Mathf.Lerp(_currentFovOffset, targetOffset, Time.deltaTime * Settings.fovKickSpeed);
|
||||
|
||||
PlayerController.Instance.PlayerCamera.fieldOfView = effectiveBase + _currentFovOffset;
|
||||
|
||||
// Consume the override — must be re-submitted next frame by the caller.
|
||||
_externalBaseFov = -1f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the base FOV reference. Call this if the camera FOV is changed intentionally at runtime.
|
||||
/// Overrides the base FOV used by the sprint kick for this frame.
|
||||
/// Call every Update from HouseOfLeavesController — consumed automatically after use.
|
||||
/// Must be called before CameraEffects.Update runs (use Script Execution Order -10).
|
||||
/// </summary>
|
||||
public void SetBaseFovOverride(float fov) => _externalBaseFov = fov;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the locally cached base FOV to the camera's current fieldOfView.
|
||||
/// </summary>
|
||||
public void ResetBaseFov()
|
||||
{
|
||||
@@ -105,7 +125,7 @@ namespace Unheimlich.Features.Player
|
||||
|
||||
if (_shakeTimer <= 0f)
|
||||
{
|
||||
_shakeTimer = _shakeInterval * amplitude;
|
||||
_shakeTimer = _shakeInterval * amplitude;
|
||||
_shakeOriginalPos = PlayerController.Instance.CameraPivot.position;
|
||||
|
||||
float x = Random.Range(-1f, 1f) * amplitude;
|
||||
@@ -132,7 +152,7 @@ namespace Unheimlich.Features.Player
|
||||
public void ApplyAimAssist(Vector3 targetCenter)
|
||||
{
|
||||
_pendingAimAssistTarget = targetCenter;
|
||||
_hasAimAssistTarget = true;
|
||||
_hasAimAssistTarget = true;
|
||||
}
|
||||
|
||||
private void ApplyAimAssistInternal(Vector3 targetCenter)
|
||||
@@ -147,30 +167,27 @@ namespace Unheimlich.Features.Player
|
||||
IsAimLocked = false;
|
||||
|
||||
Vector3 directionToTarget = (targetCenter - PlayerController.Instance.CameraTransform.position).normalized;
|
||||
float angleError = Vector3.Angle(PlayerController.Instance.CameraTransform.forward, directionToTarget);
|
||||
float angleError = Vector3.Angle(PlayerController.Instance.CameraTransform.forward, directionToTarget);
|
||||
|
||||
if (IsAimLocked || angleError < Settings.aimAssistMaxAngle)
|
||||
{
|
||||
Vector3 targetDirection = directionToTarget;
|
||||
float targetYaw = Mathf.Atan2(targetDirection.x, targetDirection.z) * Mathf.Rad2Deg;
|
||||
float targetPitch = -Mathf.Asin(targetDirection.y) * Mathf.Rad2Deg;
|
||||
float targetYaw = Mathf.Atan2(targetDirection.x, targetDirection.z) * Mathf.Rad2Deg;
|
||||
float targetPitch = -Mathf.Asin(targetDirection.y) * Mathf.Rad2Deg;
|
||||
|
||||
float currentYaw = OctoPlayer.Instance.State.Yaw;
|
||||
float currentPitch = OctoPlayer.Instance.State.Pitch;
|
||||
float currentYaw = PlayerController.Instance.Movement.Yaw;
|
||||
float currentPitch = PlayerController.Instance.Movement.Pitch;
|
||||
|
||||
float newYaw = Mathf.LerpAngle(currentYaw, targetYaw, Settings.aimAssistStrength * Time.deltaTime);
|
||||
float newPitch = Mathf.Lerp(currentPitch, targetPitch, Settings.aimAssistStrength * Time.deltaTime);
|
||||
float newYaw = Mathf.LerpAngle(currentYaw, targetYaw, Settings.aimAssistStrength * Time.deltaTime);
|
||||
float newPitch = Mathf.Lerp(currentPitch, targetPitch, Settings.aimAssistStrength * Time.deltaTime);
|
||||
|
||||
OctoPlayer.Instance.SetYaw(newYaw);
|
||||
OctoPlayer.Instance.SetPitch(newPitch);
|
||||
PlayerController.Instance.Movement.Yaw = newYaw;
|
||||
PlayerController.Instance.Movement.Pitch = newPitch;
|
||||
}
|
||||
|
||||
_hasAimAssistTarget = false;
|
||||
}
|
||||
|
||||
public void LockAim(bool locked)
|
||||
{
|
||||
IsAimLocked = locked;
|
||||
}
|
||||
public void LockAim(bool locked) => IsAimLocked = locked;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using UnityEngine;
|
||||
using Unheimlich.Features.Player;
|
||||
|
||||
namespace Unheimlich.World.HouseOfLeaves
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulates a Dolly Zoom (Vertigo effect) by applying a speed-driven additive FOV offset
|
||||
/// on top of the base FOV already written by <see cref="HouseOfLeavesController"/> and
|
||||
/// <see cref="CameraEffects"/> earlier in the frame.
|
||||
///
|
||||
/// Runs in <c>LateUpdate</c> — after both <see cref="HouseOfLeavesController.Update"/> and
|
||||
/// <see cref="CameraEffects.Update"/> — so it always layers on top of the final written value.
|
||||
/// Subtracts the previous frame's offset before adding the new one to avoid compounding.
|
||||
///
|
||||
/// The faster the player moves, the more the FOV pinches inward,
|
||||
/// compressing the perceived corridor space and amplifying claustrophobia.
|
||||
/// </summary>
|
||||
public class DollyZoomEffect : MonoBehaviour
|
||||
{
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Serialized Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Header("Effect")]
|
||||
[Tooltip("Enable or disable the effect at runtime.")]
|
||||
[SerializeField] private bool _isEnabled = true;
|
||||
|
||||
[Tooltip("Maximum negative FOV offset at full speed (degrees). " +
|
||||
"Negative value pinches the FOV inward.")]
|
||||
[SerializeField] private float _maxFovOffset = -15f;
|
||||
|
||||
[Tooltip("Walk speed (km/h) at which the effect starts fading in.")]
|
||||
[SerializeField] private float _speedThreshold = 12f;
|
||||
|
||||
[Tooltip("Walk speed (km/h) at which the effect reaches its maximum.")]
|
||||
[SerializeField] private float _maxSpeedForEffect = 22f;
|
||||
|
||||
[Tooltip("Lerp speed for the FOV offset transitions.")]
|
||||
[SerializeField] private float _smoothSpeed = 3f;
|
||||
|
||||
[Header("Pulse (optional)")]
|
||||
[Tooltip("Adds a subtle sinusoidal oscillation to the FOV offset for extra unease.")]
|
||||
[SerializeField] private bool _enablePulse;
|
||||
|
||||
[Tooltip("Amplitude of the pulse oscillation (degrees).")]
|
||||
[SerializeField] private float _pulseAmplitude = 1.5f;
|
||||
|
||||
[Tooltip("Frequency of the pulse oscillation (Hz).")]
|
||||
[SerializeField] private float _pulseFrequency = 0.4f;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Private Fields
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Camera _playerCamera;
|
||||
private float _currentFovOffset;
|
||||
private bool _isInitialized;
|
||||
|
||||
private float _previousFovOffset;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Unity Messages
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (PlayerController.Instance?.PlayerCamera == null)
|
||||
{
|
||||
Debug.LogError("[DollyZoomEffect] PlayerCamera could not be resolved.");
|
||||
return;
|
||||
}
|
||||
|
||||
_playerCamera = PlayerController.Instance.PlayerCamera;
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
// LateUpdate ensures this runs after HouseOfLeavesController and CameraEffects
|
||||
// have both written their FOV values in Update.
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!_isInitialized || !_isEnabled) return;
|
||||
|
||||
TickDollyZoom();
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Tick
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Computes the speed-driven FOV delta and applies it additively on top of
|
||||
/// the current <c>fieldOfView</c>. Strips the previous frame's offset first
|
||||
/// to avoid compounding the delta across frames.
|
||||
/// </summary>
|
||||
// Dans DollyZoomEffect, stocker le FOV base séparément
|
||||
private void TickDollyZoom()
|
||||
{
|
||||
float speedKmh = GetHorizontalSpeedKmh();
|
||||
float speedT = Mathf.Clamp01(
|
||||
(speedKmh - _speedThreshold) / Mathf.Max(0.01f, _maxSpeedForEffect - _speedThreshold)
|
||||
);
|
||||
|
||||
float targetOffset = _maxFovOffset * speedT;
|
||||
if (_enablePulse && speedT > 0.01f)
|
||||
targetOffset += Mathf.Sin(Time.time * _pulseFrequency * Mathf.PI * 2f) * _pulseAmplitude * speedT;
|
||||
|
||||
_currentFovOffset = Mathf.Lerp(_currentFovOffset, targetOffset, Time.deltaTime * _smoothSpeed);
|
||||
|
||||
_playerCamera.fieldOfView += _currentFovOffset - _previousFovOffset;
|
||||
_previousFovOffset = _currentFovOffset;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Helpers
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private float GetHorizontalSpeedKmh()
|
||||
{
|
||||
CharacterController charController = PlayerController.Instance?.CharController;
|
||||
if (charController == null) return 0f;
|
||||
|
||||
Vector3 horizontalVelocity = charController.velocity;
|
||||
horizontalVelocity.y = 0f;
|
||||
|
||||
return horizontalVelocity.magnitude * 3.6f;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Public API
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public void SetEnabled(bool value) => _isEnabled = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 156759bdbd15e4a498d2d415ec86892b
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db15fe44adc17944db9edf5e968629f4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,115 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace MonoLimboStudio
|
||||
{
|
||||
public class LightFlicker : MonoBehaviour
|
||||
{
|
||||
[Header("Light Settings")]
|
||||
public Light targetLight;
|
||||
|
||||
[Tooltip("Base intensity set externally (e.g. by ScarecrowFire). Flicker oscillates around this value.")]
|
||||
public float baseIntensity = 1f;
|
||||
|
||||
[Tooltip("Flicker range applied relative to baseIntensity (e.g. 0.3 = ±30%).")]
|
||||
[Range(0f, 1f)]
|
||||
public float flickerRange = 0.3f;
|
||||
|
||||
[Header("Perlin Flicker (smooth)")]
|
||||
public float flickerSpeed = 5f;
|
||||
|
||||
[Header("Glitch Flicker (bursts)")]
|
||||
[Range(0f, 1f)] public float glitchChance = 0.15f;
|
||||
public float glitchMinDuration = 0.02f;
|
||||
public float glitchMaxDuration = 0.12f;
|
||||
|
||||
[Header("Material Settings")]
|
||||
public Renderer targetRenderer;
|
||||
public string opacityProperty = "_Opacity";
|
||||
[Range(0f, 1f)] public float minOpacity = 0.2f;
|
||||
[Range(0f, 1f)] public float maxOpacity = 1.0f;
|
||||
|
||||
private Material _materialInstance;
|
||||
private float _randomOffset;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
void Start()
|
||||
{
|
||||
_randomOffset = Random.Range(0f, 1000f);
|
||||
|
||||
if (targetRenderer != null)
|
||||
{
|
||||
_materialInstance = targetRenderer.material;
|
||||
|
||||
if (!_materialInstance.HasProperty(opacityProperty))
|
||||
Debug.LogWarning($"Property '{opacityProperty}' not found on material.");
|
||||
}
|
||||
|
||||
StartCoroutine(FlickerRoutine());
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Sets the base intensity that the flicker oscillates around.
|
||||
/// Called each frame by ScarecrowFire as the fire grows.
|
||||
/// </summary>
|
||||
public void SetBaseIntensity(float value)
|
||||
{
|
||||
baseIntensity = value;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
private IEnumerator FlickerRoutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
float perlinValue = Mathf.PerlinNoise(Time.time * flickerSpeed + _randomOffset, 0f);
|
||||
ApplyFlicker(perlinValue);
|
||||
|
||||
if (Random.value < glitchChance)
|
||||
yield return StartCoroutine(GlitchBurst());
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator GlitchBurst()
|
||||
{
|
||||
float duration = Random.Range(glitchMinDuration, glitchMaxDuration);
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
ApplyFlicker(Random.value);
|
||||
elapsed += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies flicker as an offset around baseIntensity rather than
|
||||
/// absolute min/max, so external intensity control stays intact.
|
||||
/// </summary>
|
||||
private void ApplyFlicker(float t)
|
||||
{
|
||||
if (targetLight != null)
|
||||
{
|
||||
float offset = Mathf.Lerp(-flickerRange, flickerRange, t);
|
||||
targetLight.intensity = Mathf.Max(0f, baseIntensity + offset);
|
||||
}
|
||||
|
||||
if (_materialInstance != null && _materialInstance.HasProperty(opacityProperty))
|
||||
_materialInstance.SetFloat(opacityProperty, Mathf.Lerp(minOpacity, maxOpacity, t));
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (_materialInstance != null)
|
||||
Destroy(_materialInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1df1fd6452d396e4699ee7d8406130a2
|
||||
Reference in New Issue
Block a user