Compare commits
2 Commits
edc476adfd
...
28a683b358
| Author | SHA1 | Date | |
|---|---|---|---|
| 28a683b358 | |||
| 90d4bed381 |
@@ -40011,6 +40011,68 @@ Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 6078297801174817557, guid: 66d4f7c006b77084da3929add6d06f24, type: 3}
|
||||
m_PrefabInstance: {fileID: 1002930408}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &1007206781
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1007206783}
|
||||
- component: {fileID: 1007206782}
|
||||
m_Layer: 0
|
||||
m_Name: AmbientMotion
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1007206782
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1007206781}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: c5eaa0f5b116ec34695d5e4646784462, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: ProjectM.Client::ProjectM.Client.AmbientMotionConfig
|
||||
Enabled: 1
|
||||
DriftEnabled: 1
|
||||
DriftRate: 28
|
||||
DriftSize: 0.09
|
||||
MeadowMoteColor: {r: 0.62, g: 0.85, b: 0.6, a: 0.35}
|
||||
AridDustColor: {r: 0.85, g: 0.68, b: 0.45, a: 0.28}
|
||||
CavernMoteColor: {r: 0.55, g: 0.65, b: 0.95, a: 0.33}
|
||||
BlightSporeColor: {r: 0.72, g: 0.5, b: 0.85, a: 0.33}
|
||||
AridWind: {x: 2.2, y: -0.15, z: 0.5}
|
||||
SwayEnabled: 1
|
||||
SwayRadius: 30
|
||||
SwayAmpDeg: 1.4
|
||||
SwayHz: 0.8
|
||||
RustleEnabled: 1
|
||||
RustleRadius: 1.7
|
||||
RustleAmpDeg: 7
|
||||
RustleHz: 9
|
||||
RustleDecay: 2.5
|
||||
--- !u!4 &1007206783
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1007206781}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1001 &1010024963
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -77186,6 +77248,7 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 2045829909}
|
||||
- component: {fileID: 2045829910}
|
||||
- component: {fileID: 2045829911}
|
||||
m_Layer: 0
|
||||
m_Name: CabinLight
|
||||
m_TagString: Untagged
|
||||
@@ -77273,6 +77336,35 @@ Light:
|
||||
m_LightUnit: 1
|
||||
m_LuxAtDistance: 1
|
||||
m_EnableSpotReflector: 1
|
||||
--- !u!114 &2045829911
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2045829908}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalLightData
|
||||
m_UsePipelineSettings: 1
|
||||
m_AdditionalLightsShadowResolutionTier: 2
|
||||
m_CustomShadowLayers: 0
|
||||
m_LightCookieSize: {x: 1, y: 1}
|
||||
m_LightCookieOffset: {x: 0, y: 0}
|
||||
m_SoftShadowQuality: 0
|
||||
m_RenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_ShadowRenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_Version: 4
|
||||
m_LightLayerMask: 1
|
||||
m_ShadowLayerMask: 1
|
||||
m_RenderingLayers: 1
|
||||
m_ShadowRenderingLayers: 1
|
||||
--- !u!1001 &2047680188
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -81293,3 +81385,4 @@ SceneRoots:
|
||||
- {fileID: 1851179016}
|
||||
- {fileID: 70737666}
|
||||
- {fileID: 1276835916}
|
||||
- {fileID: 1007206783}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Live-tunable knobs for <see cref="AmbientMotionSystem"/> — the Phase 1.5 "world alive" ambient layer
|
||||
/// (per-biome drift particles + idle foliage sway + walk-through rustle). Mirrors the
|
||||
/// <see cref="WorldAtmosphereConfig"/>/<see cref="DynamicLightConfig"/> bridge idiom: a MonoBehaviour in
|
||||
/// the gameplay scene with a static <see cref="Instance"/> the client presentation system reads (falling
|
||||
/// back to these same defaults when absent). Pure presentation — never touches the simulation.
|
||||
/// </summary>
|
||||
public sealed class AmbientMotionConfig : MonoBehaviour
|
||||
{
|
||||
public static AmbientMotionConfig Instance;
|
||||
|
||||
[Header("Master")]
|
||||
public bool Enabled = true;
|
||||
|
||||
[Header("Biome drift particles (emitter follows the camera; sim space stays world)")]
|
||||
public bool DriftEnabled = true;
|
||||
[Min(0f)] public float DriftRate = 28f;
|
||||
[Min(0.01f)] public float DriftSize = 0.09f;
|
||||
public Color MeadowMoteColor = new Color(0.62f, 0.85f, 0.60f, 0.35f);
|
||||
public Color AridDustColor = new Color(0.85f, 0.68f, 0.45f, 0.28f);
|
||||
public Color CavernMoteColor = new Color(0.55f, 0.65f, 0.95f, 0.33f);
|
||||
public Color BlightSporeColor = new Color(0.72f, 0.50f, 0.85f, 0.33f);
|
||||
public Vector3 AridWind = new Vector3(2.2f, -0.15f, 0.5f);
|
||||
|
||||
[Header("Idle foliage sway (flora prop roots within view of the camera)")]
|
||||
public bool SwayEnabled = true;
|
||||
[Min(1f)] public float SwayRadius = 30f;
|
||||
[Min(0f)] public float SwayAmpDeg = 1.4f;
|
||||
[Min(0.05f)] public float SwayHz = 0.8f;
|
||||
|
||||
[Header("Walk-through rustle (local player brushing flora)")]
|
||||
public bool RustleEnabled = true;
|
||||
[Min(0.2f)] public float RustleRadius = 1.7f;
|
||||
[Min(0f)] public float RustleAmpDeg = 7f;
|
||||
[Min(0.5f)] public float RustleHz = 9f;
|
||||
[Min(0.2f)] public float RustleDecay = 2.5f;
|
||||
|
||||
void OnEnable() { Instance = this; }
|
||||
void OnDisable() { if (Instance == this) Instance = null; }
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
static void ResetStatics() { Instance = null; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5eaa0f5b116ec34695d5e4646784462
|
||||
@@ -0,0 +1,254 @@
|
||||
using System.Collections.Generic;
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
using Unity.Transforms;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-only AMBIENT MOTION layer (Phase 1.5 "world alive" — the anti-static beat, zero netcode):
|
||||
/// - per-biome DRIFT particles (meadow motes / arid wind-blown dust / cavern motes / blight spores) from
|
||||
/// one world-space emitter that follows the camera; palette keys on camera-X region + the replicated
|
||||
/// per-room biome, mirroring <see cref="WorldAtmosphereSystem"/>'s switch
|
||||
/// - idle SWAY on the cosmetic flora prop roots near the camera (Game.unity classic layer; probed
|
||||
/// 2026-07-09: 212 flora roots, none static-batched, LOD children follow the root)
|
||||
/// - walk-through RUSTLE: flora brushed by the LOCAL player (the GhostOwnerIsLocal idiom) gets an
|
||||
/// energy pulse that shakes and decays
|
||||
/// Observe-only <see cref="SystemBase"/> in <see cref="PresentationSystemGroup"/>; wall-clock time is fine
|
||||
/// (presentation). Rotations write around a cached base so the layer is lossless when toggled off.
|
||||
/// Knobs live in <see cref="AmbientMotionConfig"/> (defaults when absent).
|
||||
/// </summary>
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
|
||||
[UpdateInGroup(typeof(PresentationSystemGroup))]
|
||||
public partial class AmbientMotionSystem : SystemBase
|
||||
{
|
||||
static readonly System.Text.RegularExpressions.Regex FloraName = new System.Text.RegularExpressions.Regex(
|
||||
"bush|grass|flower|fern|groundcover|ground_cover|wildflower|clover|reed|succulent|cactus",
|
||||
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
||||
|
||||
struct Flora
|
||||
{
|
||||
public Transform T;
|
||||
public Vector3 Pos;
|
||||
public Quaternion BaseRot;
|
||||
public float Phase;
|
||||
public float RustleEnergy;
|
||||
}
|
||||
|
||||
readonly List<Flora> _flora = new List<Flora>(256);
|
||||
bool _floraCached;
|
||||
string _sceneKey;
|
||||
Camera _cam;
|
||||
GameObject _root;
|
||||
ParticleSystem _drift;
|
||||
int _biomeKey = -1;
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
RestoreFlora();
|
||||
if (_root != null) Object.Destroy(_root);
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||||
if (scene.name != "Game") { RestoreFlora(); _floraCached = false; return; }
|
||||
if (_cam == null) _cam = Camera.main;
|
||||
if (_cam == null) return;
|
||||
|
||||
var cfg = AmbientMotionConfig.Instance;
|
||||
bool enabled = cfg == null || cfg.Enabled;
|
||||
if (!enabled)
|
||||
{
|
||||
RestoreFlora();
|
||||
if (_drift != null && _drift.isPlaying) _drift.Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_floraCached || _sceneKey != scene.path) CacheFlora(scene);
|
||||
|
||||
UpdateDrift(cfg);
|
||||
UpdateSwayAndRustle(cfg);
|
||||
}
|
||||
|
||||
// ---------- flora cache ----------
|
||||
|
||||
void CacheFlora(UnityEngine.SceneManagement.Scene scene)
|
||||
{
|
||||
RestoreFlora();
|
||||
_flora.Clear();
|
||||
foreach (var root in scene.GetRootGameObjects())
|
||||
{
|
||||
if (root.name != "BaseBiome" && !root.name.StartsWith("ExpeditionBiome")) continue;
|
||||
foreach (var t in root.GetComponentsInChildren<Transform>(false))
|
||||
{
|
||||
if (!FloraName.IsMatch(t.gameObject.name)) continue;
|
||||
if (t.gameObject.name.Contains("LOD")) continue; // prop roots only; LOD children follow
|
||||
if (t.GetComponent<LODGroup>() == null && t.GetComponent<MeshRenderer>() == null) continue;
|
||||
var p = t.position;
|
||||
_flora.Add(new Flora
|
||||
{
|
||||
T = t,
|
||||
Pos = p,
|
||||
BaseRot = t.localRotation,
|
||||
Phase = (p.x * 12.9898f + p.z * 78.233f) % 6.2831f,
|
||||
RustleEnergy = 0f,
|
||||
});
|
||||
}
|
||||
}
|
||||
_floraCached = true;
|
||||
_sceneKey = scene.path;
|
||||
}
|
||||
|
||||
void RestoreFlora()
|
||||
{
|
||||
for (int i = 0; i < _flora.Count; i++)
|
||||
if (_flora[i].T != null) _flora[i].T.localRotation = _flora[i].BaseRot;
|
||||
}
|
||||
|
||||
// ---------- biome drift particles ----------
|
||||
|
||||
void UpdateDrift(AmbientMotionConfig cfg)
|
||||
{
|
||||
bool driftOn = cfg == null || cfg.DriftEnabled;
|
||||
if (!driftOn)
|
||||
{
|
||||
if (_drift != null && _drift.isPlaying) _drift.Stop();
|
||||
return;
|
||||
}
|
||||
if (_root == null)
|
||||
{
|
||||
_root = new GameObject("~AmbientMotion");
|
||||
Object.DontDestroyOnLoad(_root);
|
||||
}
|
||||
if (_drift == null)
|
||||
{
|
||||
var mat = FeedbackFx.MakeParticleMaterial("AmbientDrift");
|
||||
_drift = FeedbackFx.MakeBurst(_root.transform, "Drift", mat, Color.white,
|
||||
0.09f, 0.15f, 9f, 600, gravity: 0f, shapeRadius: 1f, sizeTail: 0.9f);
|
||||
var main = _drift.main;
|
||||
main.simulationSpace = ParticleSystemSimulationSpace.World;
|
||||
main.startLifetime = 9f;
|
||||
var shape = _drift.shape;
|
||||
shape.enabled = true;
|
||||
shape.shapeType = ParticleSystemShapeType.Box;
|
||||
shape.scale = new Vector3(36f, 7f, 24f);
|
||||
var noise = _drift.noise;
|
||||
noise.enabled = true;
|
||||
noise.strength = 0.45f;
|
||||
noise.frequency = 0.25f;
|
||||
var emission = _drift.emission;
|
||||
emission.enabled = true;
|
||||
}
|
||||
|
||||
// palette key: base hub vs the replicated per-room biome (WorldAtmosphereSystem's switch shape)
|
||||
int key = 0; // 0 = meadow/base
|
||||
if (_cam.transform.position.x > 500f)
|
||||
{
|
||||
key = 1; // arid default
|
||||
if (SystemAPI.TryGetSingleton<RunInfo>(out var ri) && ri.Lifecycle != RunLifecycle.Staging)
|
||||
{
|
||||
switch (ri.CurrentBiome)
|
||||
{
|
||||
case RoomBiomeId.Meadow: key = 0; break;
|
||||
case RoomBiomeId.Cavern: key = 2; break;
|
||||
case RoomBiomeId.Blight: key = 3; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key != _biomeKey)
|
||||
{
|
||||
_biomeKey = key;
|
||||
var main = _drift.main;
|
||||
main.startSize = cfg != null ? cfg.DriftSize : 0.09f;
|
||||
Color c;
|
||||
Vector3 wind;
|
||||
switch (key)
|
||||
{
|
||||
case 1: c = cfg != null ? cfg.AridDustColor : new Color(0.85f, 0.68f, 0.45f, 0.28f); wind = cfg != null ? cfg.AridWind : new Vector3(2.2f, -0.15f, 0.5f); break;
|
||||
case 2: c = cfg != null ? cfg.CavernMoteColor : new Color(0.55f, 0.65f, 0.95f, 0.33f); wind = new Vector3(0f, 0.18f, 0f); break;
|
||||
case 3: c = cfg != null ? cfg.BlightSporeColor : new Color(0.72f, 0.50f, 0.85f, 0.33f); wind = new Vector3(0f, 0.28f, 0f); break;
|
||||
default: c = cfg != null ? cfg.MeadowMoteColor : new Color(0.62f, 0.85f, 0.60f, 0.35f); wind = new Vector3(0f, 0.22f, 0f); break;
|
||||
}
|
||||
main.startColor = c;
|
||||
var vel = _drift.velocityOverLifetime;
|
||||
vel.enabled = true;
|
||||
vel.space = ParticleSystemSimulationSpace.World;
|
||||
vel.x = wind.x; vel.y = wind.y; vel.z = wind.z;
|
||||
var emission = _drift.emission;
|
||||
emission.rateOverTime = cfg != null ? cfg.DriftRate : 28f;
|
||||
}
|
||||
if (!_drift.isPlaying) _drift.Play();
|
||||
// follow the camera's ground focus so the volume always blankets the view
|
||||
var camPos = _cam.transform.position;
|
||||
_drift.transform.position = new Vector3(camPos.x, 3.5f, camPos.z);
|
||||
}
|
||||
|
||||
// ---------- sway + rustle ----------
|
||||
|
||||
void UpdateSwayAndRustle(AmbientMotionConfig cfg)
|
||||
{
|
||||
bool swayOn = cfg == null || cfg.SwayEnabled;
|
||||
bool rustleOn = cfg == null || cfg.RustleEnabled;
|
||||
if (!swayOn && !rustleOn) { RestoreFlora(); return; }
|
||||
|
||||
float swayRadius = cfg != null ? cfg.SwayRadius : 30f;
|
||||
float swayAmp = cfg != null ? cfg.SwayAmpDeg : 1.4f;
|
||||
float swayHz = cfg != null ? cfg.SwayHz : 0.8f;
|
||||
float rustleRadius = cfg != null ? cfg.RustleRadius : 1.7f;
|
||||
float rustleAmp = cfg != null ? cfg.RustleAmpDeg : 7f;
|
||||
float rustleHz = cfg != null ? cfg.RustleHz : 9f;
|
||||
float rustleDecay = cfg != null ? cfg.RustleDecay : 2.5f;
|
||||
|
||||
// local player position (rustle source); missing (menu/no ghost yet) -> far away
|
||||
Vector3 playerPos = new Vector3(1e9f, 0f, 1e9f);
|
||||
foreach (var lt in SystemAPI.Query<RefRO<LocalTransform>>().WithAll<GhostOwnerIsLocal, PlayerTag>())
|
||||
playerPos = new Vector3(lt.ValueRO.Position.x, 0f, lt.ValueRO.Position.z);
|
||||
|
||||
float t = UnityEngine.Time.time;
|
||||
float dt = UnityEngine.Time.deltaTime;
|
||||
var camPos = _cam.transform.position;
|
||||
float swayR2 = swayRadius * swayRadius;
|
||||
float rustleR2 = rustleRadius * rustleRadius;
|
||||
|
||||
for (int i = 0; i < _flora.Count; i++)
|
||||
{
|
||||
var f = _flora[i];
|
||||
if (f.T == null) continue;
|
||||
|
||||
float dxc = f.Pos.x - camPos.x, dzc = f.Pos.z - camPos.z;
|
||||
bool nearCam = dxc * dxc + dzc * dzc < swayR2;
|
||||
|
||||
if (rustleOn)
|
||||
{
|
||||
float dxp = f.Pos.x - playerPos.x, dzp = f.Pos.z - playerPos.z;
|
||||
if (dxp * dxp + dzp * dzp < rustleR2) f.RustleEnergy = 1f;
|
||||
else if (f.RustleEnergy > 0f) f.RustleEnergy = Mathf.Max(0f, f.RustleEnergy - rustleDecay * dt);
|
||||
}
|
||||
|
||||
if (!nearCam && f.RustleEnergy <= 0f)
|
||||
{
|
||||
_flora[i] = f;
|
||||
continue;
|
||||
}
|
||||
|
||||
float ax = 0f, az = 0f;
|
||||
if (swayOn && nearCam)
|
||||
{
|
||||
ax += swayAmp * Mathf.Sin(t * swayHz * 6.2831f + f.Phase);
|
||||
az += swayAmp * 0.8f * Mathf.Sin(t * swayHz * 5.5f + f.Phase * 1.7f);
|
||||
}
|
||||
if (f.RustleEnergy > 0f)
|
||||
{
|
||||
float e = f.RustleEnergy;
|
||||
ax += rustleAmp * e * Mathf.Sin(t * rustleHz * 6.2831f + f.Phase);
|
||||
az += rustleAmp * 0.7f * e * Mathf.Sin(t * rustleHz * 5.1f + f.Phase * 2.3f);
|
||||
}
|
||||
f.T.localRotation = f.BaseRot * Quaternion.Euler(ax, 0f, az);
|
||||
_flora[i] = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4a8e8f6d3c4d95428cf227b39f5fc30
|
||||
@@ -28,16 +28,16 @@ Material:
|
||||
- _Brightness: 1.1
|
||||
- _ColorLevels: 12
|
||||
- _Contrast: 1.05
|
||||
- _DepthEdgesEnabled: 0
|
||||
- _DepthEdgesEnabled: 1
|
||||
- _DepthThreshold: 0.08
|
||||
- _EdgeStrength: 1
|
||||
- _MasterEnabled: 1
|
||||
- _NormalEdgesEnabled: 0
|
||||
- _MasterEnabled: 0
|
||||
- _NormalEdgesEnabled: 1
|
||||
- _NormalThreshold: 1
|
||||
- _OutlineThickness: 1
|
||||
- _PixelHeight: 200
|
||||
- _PixelateEnabled: 0
|
||||
- _PosterizeEnabled: 0
|
||||
- _PixelHeight: 399.82455
|
||||
- _PixelateEnabled: 1
|
||||
- _PosterizeEnabled: 1
|
||||
- _Saturation: 1.35
|
||||
m_Colors:
|
||||
- _OutlineColorInner: {r: 0, g: 0, b: 0, a: 1}
|
||||
|
||||
@@ -128,10 +128,12 @@ attacks/animations/effects**. Art direction deliberately UNDECIDED — lighting
|
||||
|
||||
### Short-term wins (asset/scene/client layer)
|
||||
|
||||
3. **Lighting/atmosphere pass (direction-agnostic):** ambient way down, dynamic point lights on projectiles / portal /
|
||||
pickups / braziers / ability impacts, per-biome fog + color-grade tune. Build AS IF no pixel filter (it composes on
|
||||
top). Then the **art-look decision gate**: side-by-side screenshots pixel filter ON vs OFF over the lit scene →
|
||||
operator locks pixel-art-3D vs dark-Synty vs both. Every later visual choice hangs off this lock.
|
||||
3. ~~Lighting/atmosphere pass~~ **DONE (2026-07-09, `414f9ff62`)** — dark ambient + DynamicLightSystem
|
||||
(projectile/portal/node/impact-flash lights) + landmark mood lights + per-biome dark palettes; found+fixed
|
||||
the dead Linear-fog density knob. **ART LOOK LOCKED (operator, 2026-07-09): CLEAN DARK-SYNTY — pixel filter
|
||||
OFF** ("everything feels smoother & sharper without it"); the PixelOutline feature stays dev-toggleable (F3)
|
||||
with `_MasterEnabled=0` shipped. Operator is open to experimenting with OTHER fullscreen/shader effects
|
||||
later (sharpen/vignette/grade variants — backlog). Every later visual choice keys off clean dark-Synty.
|
||||
4. **Ambient motion layer (client-only, zero netcode):** foliage sway/rustle on walk-through, per-biome dust/embers/fog
|
||||
drift particles, critters that scatter, flags/banners. Biggest bang-per-effort for "not static".
|
||||
5. **Breakable clutter with drops:** Hades-style urns/crates/Blight growths seeded per room; shatter on hit → small
|
||||
|
||||
@@ -167,13 +167,35 @@ Dark-ambient dynamic-light-first look (roadmap 1.5 #3), built filter-agnostic:
|
||||
impact flashes observed), fog/ambient values confirmed live in Play, 466/466, console clean.
|
||||
- **Art-look gate material committed:** `Assets/Screenshots/artlook_{base,room}_pixel{ON,OFF}.png`.
|
||||
|
||||
## Part G — ART LOOK LOCKED + ambient motion layer shipped (`90d4bed38`)
|
||||
|
||||
**Operator locked the art look: CLEAN DARK-SYNTY — pixel filter OFF** ("everything feels smoother & sharper
|
||||
without it, even if mostly placebo"); open to OTHER shader/visual-tuning experiments later (backlog:
|
||||
sharpen/vignette/grade variants on the same fullscreen feature). `_MasterEnabled=0` saved on the material
|
||||
(F3 dev panel stays). Collisions signed off ("collisions are fine now").
|
||||
|
||||
**Ambient motion layer** (roadmap 1.5 #4, `AmbientMotionSystem` + config): per-biome drift particles from a
|
||||
camera-following world-space emitter (meadow motes / arid dust / cavern motes / blight spores — palette keyed
|
||||
like WorldAtmosphereSystem) · idle sway on ACTIVE flora prop roots near the camera · walk-through rustle
|
||||
(local-player proximity → decaying energy shake). Rotations write around a cached base → lossless toggle.
|
||||
Verified live: palettes exact on region cross, flora beside the player rocked (0,0)→(−4.0°,+5.0°), 466/466,
|
||||
console clean.
|
||||
|
||||
**Discoveries this round:**
|
||||
- **The BASE biome's flora is authored INACTIVE** (84 bush renderers etc. — the meadow hub has NO active
|
||||
sway targets and reads barren). Re-enabling a curated set is a cheap "world alive" win — operator call.
|
||||
- A mid-play session lost its netcode worlds while unfocused (the known unfocused-editor hazard —
|
||||
tick-batching warning then zero worlds; game keeps rendering as a zombie). Restart cured; not slice-related.
|
||||
- Unity 6.5: `Scene.handle` int conversions are deprecated-as-error (key caches on `scene.path`); bare
|
||||
`Time` in a `SystemBase` binds to the deprecated ECS `ComponentSystemBase.Time` (qualify `UnityEngine.Time`).
|
||||
|
||||
## Next-session intent
|
||||
|
||||
**OPERATOR GATE: lock the art look** from the artlook_* side-by-sides (pixel-art-3D vs clean dark-Synty vs
|
||||
keep-both-toggle) — every later visual choice hangs off this. Then Phase 1.5 continues: ambient-motion layer →
|
||||
breakable clutter → attack distinctness. Feel-lap items still pending operator eyes: refit walls/cover/85 prop
|
||||
colliders + solid craters call + atmosphere value taste (arid rooms read bright-tan — all knobs live) + the B5
|
||||
split-panels smoke.
|
||||
Phase 1.5 continues under the LOCKED clean dark-Synty look: **breakable clutter with drops** (Hades-style
|
||||
urns/growths — destructible-ghost + pickup patterns, netcode-cheap) → attack distinctness. Also queued:
|
||||
re-enable curated base flora (barren hub) · retune arid brightness to taste (all knobs live) · long-term 1.5
|
||||
items (hazards, destructible cover) need the adversarial design review first. Feel items for the operator's
|
||||
next lap: ambient motion + dark look in motion, craters-as-blocks call, B5 split-panels smoke.
|
||||
|
||||
Related: [[Iteration_2026-07_CoopHades]] · [[2026-07-07_Workflow_Consolidation]] ·
|
||||
[[2026-07-07_Expedition_Enemy_Stuck_Harvest_Feedback]]
|
||||
|
||||
Reference in New Issue
Block a user