diff --git a/unity-client/Assets/Editor/SetupVisuals.cs b/unity-client/Assets/Editor/SetupVisuals.cs new file mode 100644 index 0000000..e81ea0e --- /dev/null +++ b/unity-client/Assets/Editor/SetupVisuals.cs @@ -0,0 +1,74 @@ +using UnityEngine; +using UnityEditor; +using UnityEngine.Rendering.PostProcessing; + +public class SetupVisuals +{ + [MenuItem("TheIsland/Setup PostProcessing")] + public static void Setup() + { + // 1. Setup Camera + var camera = Camera.main; + if (camera == null) { Debug.LogError("No Main Camera!"); return; } + + var layer = camera.gameObject.GetComponent(); + if (layer == null) layer = camera.gameObject.AddComponent(); + + // Use Default layer + layer.volumeLayer = LayerMask.GetMask("Default"); + // Simple AA + layer.antialiasingMode = PostProcessLayer.Antialiasing.SubpixelMorphologicalAntialiasing; + + // 2. Create Global Volume + var volumeGo = GameObject.Find("GlobalVolume"); + if (volumeGo == null) + { + volumeGo = new GameObject("GlobalVolume"); + volumeGo.layer = LayerMask.NameToLayer("Default"); + } + + var volume = volumeGo.GetComponent(); + if (volume == null) volume = volumeGo.AddComponent(); + + volume.isGlobal = true; + + // 3. Create Profile if not exists + var profilePath = "Assets/GlobalProfile.asset"; + var profile = AssetDatabase.LoadAssetAtPath(profilePath); + if (profile == null) + { + profile = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(profile, profilePath); + } + volume.profile = profile; + + // 4. Clean existing settings + profile.settings.Clear(); + + // 5. Add Effects + // Bloom - Glow effect + var bloom = profile.AddSettings(); + bloom.enabled.value = true; + bloom.intensity.value = 3.0f; + bloom.threshold.value = 1.0f; + bloom.softKnee.value = 0.5f; + + // Color Grading - Better colors + var colorGrading = profile.AddSettings(); + colorGrading.enabled.value = true; + colorGrading.tonemapper.value = Tonemapper.ACES; + colorGrading.postExposure.value = 0.5f; // Slightly brighter + colorGrading.saturation.value = 20f; // More vibrant + colorGrading.contrast.value = 15f; // More pop + + // Vignette - Focus center + var vignette = profile.AddSettings(); + vignette.enabled.value = true; + vignette.intensity.value = 0.35f; + vignette.smoothness.value = 0.4f; + + EditorUtility.SetDirty(profile); + AssetDatabase.SaveAssets(); + Debug.Log("Visuals Setup Complete! Bloom, ColorGrading, and Vignette configured."); + } +} diff --git a/unity-client/Assets/Editor/SetupVisuals.cs.meta b/unity-client/Assets/Editor/SetupVisuals.cs.meta new file mode 100644 index 0000000..a23ca5e --- /dev/null +++ b/unity-client/Assets/Editor/SetupVisuals.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2f42deb3551fc40dfbe902fe1945f25b \ No newline at end of file diff --git a/unity-client/Assets/GlobalProfile.asset b/unity-client/Assets/GlobalProfile.asset new file mode 100644 index 0000000..ac06bb0 --- /dev/null +++ b/unity-client/Assets/GlobalProfile.asset @@ -0,0 +1,18 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8e6292b2c06870d4495f009f912b9600, type: 3} + m_Name: GlobalProfile + m_EditorClassIdentifier: Unity.Postprocessing.Runtime::UnityEngine.Rendering.PostProcessing.PostProcessProfile + settings: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} diff --git a/unity-client/Assets/GlobalProfile.asset.meta b/unity-client/Assets/GlobalProfile.asset.meta new file mode 100644 index 0000000..c4a5a0d --- /dev/null +++ b/unity-client/Assets/GlobalProfile.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5a9d5cd213c7f4bebb196509d67c68e3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity-client/Assets/Scripts/AgentVisual.cs b/unity-client/Assets/Scripts/AgentVisual.cs index 9a9e3c5..bf59772 100644 --- a/unity-client/Assets/Scripts/AgentVisual.cs +++ b/unity-client/Assets/Scripts/AgentVisual.cs @@ -95,29 +95,60 @@ namespace TheIsland.Visual { if (!IsAlive) return; - // Idle breathing animation + // Idle breathing animation (Squash and Stretch) _idleAnimTimer += Time.deltaTime; - _breathScale = 1f + Mathf.Sin(_idleAnimTimer * 2f) * 0.02f; + + // Breathing: Scale Y up, Scale X down (preserving volume) + float breath = Mathf.Sin(_idleAnimTimer * 3f) * 0.05f; + _breathScale = 1f + breath; + float antiBreath = 1f - (breath * 0.5f); // Squash X when stretching Y - // Gentle bobbing - _bobOffset = Mathf.Sin(_idleAnimTimer * 1.5f) * 0.05f; + // Bobbing: Move up and down + _bobOffset = Mathf.Sin(_idleAnimTimer * 2f) * 0.08f; if (_spriteRenderer != null && _originalSpriteScale != Vector3.zero) { - // Apply breathing scale + // Apply squash & stretch _spriteRenderer.transform.localScale = new Vector3( - _originalSpriteScale.x * _breathScale, + _originalSpriteScale.x * antiBreath, _originalSpriteScale.y * _breathScale, _originalSpriteScale.z ); - // Apply bobbing + // Apply bobbing position var pos = _spriteRenderer.transform.localPosition; pos.y = 1f + _bobOffset; _spriteRenderer.transform.localPosition = pos; } } + // Trigger a jump animation (to be called by events) + public void DoJump() + { + StartCoroutine(JumpRoutine()); + } + + private IEnumerator JumpRoutine() + { + float timer = 0; + float duration = 0.4f; + Vector3 startPos = _spriteRenderer.transform.localPosition; + + while (timer < duration) + { + timer += Time.deltaTime; + float t = timer / duration; + + // Parabolic jump height + float height = Mathf.Sin(t * Mathf.PI) * 0.5f; + + var pos = _spriteRenderer.transform.localPosition; + pos.y = startPos.y + height; + _spriteRenderer.transform.localPosition = pos; + yield return null; + } + } + private void OnMouseDown() { if (!IsAlive) @@ -604,7 +635,7 @@ namespace TheIsland.Visual var bg = panel.AddComponent(); bg.sprite = CreateRoundedRectSprite(32, 32, 8); bg.type = Image.Type.Sliced; - bg.color = new Color(0.1f, 0.12f, 0.18f, 0.85f); + bg.color = new Color(0.1f, 0.12f, 0.18f, 0.6f); // Lower alpha for glass effect // Add subtle border var borderObj = new GameObject("Border"); diff --git a/unity-client/Assets/Scripts/EventLog.cs b/unity-client/Assets/Scripts/EventLog.cs index 2d99dd3..c399a21 100644 --- a/unity-client/Assets/Scripts/EventLog.cs +++ b/unity-client/Assets/Scripts/EventLog.cs @@ -189,7 +189,7 @@ namespace TheIsland.UI panelRect.offsetMax = new Vector2(360, -80); var panelImg = _panel.AddComponent(); - panelImg.color = new Color(0.05f, 0.07f, 0.1f, 0.95f); + panelImg.color = new Color(0f, 0f, 0f, 0.0f); // 完全透明背景 // 标题 var header = new GameObject("Header"); @@ -201,7 +201,7 @@ namespace TheIsland.UI headerRect.sizeDelta = new Vector2(0, 28); headerRect.anchoredPosition = Vector2.zero; - header.AddComponent().color = new Color(0.12f, 0.15f, 0.2f); + header.AddComponent().color = new Color(0.12f, 0.15f, 0.2f, 0.8f); var titleObj = new GameObject("Title"); titleObj.transform.SetParent(header.transform, false); @@ -296,8 +296,8 @@ namespace TheIsland.UI entry.transform.SetParent(_content, false); entry.AddComponent().color = _entries.Count % 2 == 0 - ? new Color(0.08f, 0.1f, 0.13f, 0.9f) - : new Color(0.06f, 0.08f, 0.11f, 0.9f); + ? new Color(0f, 0f, 0f, 0.2f) + : new Color(0f, 0f, 0f, 0.1f); var le = entry.AddComponent(); le.minHeight = 36; diff --git a/unity-client/Assets/Scripts/GameManager.cs b/unity-client/Assets/Scripts/GameManager.cs index 9c4d71d..569a5d0 100644 --- a/unity-client/Assets/Scripts/GameManager.cs +++ b/unity-client/Assets/Scripts/GameManager.cs @@ -322,6 +322,7 @@ namespace TheIsland.Core if (_agentVisuals.TryGetValue(data.agent_id, out AgentVisual agentVisual)) { agentVisual.ShowSpeech(data.text); + agentVisual.DoJump(); // Add jump effect } // Check AgentUI (programmatic UI system) else if (_agentUIs.TryGetValue(data.agent_id, out AgentUI agentUI)) diff --git a/unity-client/Assets/Scripts/UIManager.cs b/unity-client/Assets/Scripts/UIManager.cs index 428960e..4259bf4 100644 --- a/unity-client/Assets/Scripts/UIManager.cs +++ b/unity-client/Assets/Scripts/UIManager.cs @@ -164,7 +164,7 @@ namespace TheIsland.UI topBar.offsetMax = new Vector2(-10, -10); var topBarImg = topBar.gameObject.AddComponent(); - topBarImg.color = new Color(0, 0, 0, 0.7f); + topBarImg.color = new Color(0, 0, 0, 0.0f); // 透明顶部栏 // Connection Status (Left) _connectionStatus = CreateText(topBar, "ConnectionStatus", "● Disconnected", @@ -205,7 +205,7 @@ namespace TheIsland.UI bottomBar.offsetMax = new Vector2(-10, 70); var bottomBarImg = bottomBar.gameObject.AddComponent(); - bottomBarImg.color = new Color(0, 0, 0, 0.7f); + bottomBarImg.color = new Color(0, 0, 0, 0.2f); // 低透明度底部栏 // Command Input var inputObj = new GameObject("CommandInput"); diff --git a/unity-client/Assets/Scripts/Visual/EnvironmentManager.cs b/unity-client/Assets/Scripts/Visual/EnvironmentManager.cs index e775839..3715432 100644 --- a/unity-client/Assets/Scripts/Visual/EnvironmentManager.cs +++ b/unity-client/Assets/Scripts/Visual/EnvironmentManager.cs @@ -57,6 +57,7 @@ namespace TheIsland.Visual [SerializeField] private Color waterDeepColor = new Color(0.1f, 0.4f, 0.6f, 0.9f); [SerializeField] private float waveSpeed = 0.5f; [SerializeField] private float waveAmplitude = 0.1f; + [SerializeField] private Material customWaterMaterial; // Custom shader support #endregion #region References @@ -116,8 +117,9 @@ namespace TheIsland.Visual UpdateSkyMaterial(); } - // Animate water - AnimateWater(); + // Animate environment (Water & Trees) + AnimateEnvironment(); + AnimateClouds(); } private void OnDestroy() @@ -140,6 +142,7 @@ namespace TheIsland.Visual CreateWater(); CreateLighting(); CreateDecorations(); + CreateClouds(); } private void CreateSky() @@ -308,9 +311,17 @@ namespace TheIsland.Visual _waterPlane.transform.localScale = new Vector3(60, 15, 1); // Create water material - _waterMaterial = new Material(Shader.Find("Unlit/Transparent")); - _waterMaterial.mainTexture = CreateWaterTexture(); - _waterPlane.GetComponent().material = _waterMaterial; + if (customWaterMaterial != null) + { + _waterMaterial = customWaterMaterial; + _waterPlane.GetComponent().material = _waterMaterial; + } + else + { + _waterMaterial = new Material(Shader.Find("Unlit/Transparent")); + _waterMaterial.mainTexture = CreateWaterTexture(); + _waterPlane.GetComponent().material = _waterMaterial; + } _waterPlane.GetComponent().sortingOrder = -40; Destroy(_waterPlane.GetComponent()); @@ -439,6 +450,29 @@ namespace TheIsland.Visual return Sprite.Create(tex, new Rect(0, 0, width, height), new Vector2(0.5f, 0)); } + private void AnimateEnvironment() + { + // Water animation + if (_waterMaterial != null) + { + float offset = Time.time * waveSpeed * 0.1f; + _waterMaterial.mainTextureOffset = new Vector2(offset, offset * 0.5f); + } + + // Tree swaying animation + // Find all palm tree objects (simple lookup by name since we created them) + // Ideally we'd cache these, but for this scale it's fine + foreach (Transform child in transform) + { + if (child.name == "PalmTree") + { + // Sway rotation + float sway = Mathf.Sin(Time.time * 1.5f + child.position.x) * 2.0f; + child.rotation = Quaternion.Euler(0, 0, sway); + } + } + } + private void DrawPalmFronds(Color[] pixels, int width, int height, Color leaf, Color leafBright) { Vector2 center = new Vector2(width / 2, height * 0.65f); @@ -612,6 +646,80 @@ namespace TheIsland.Visual } #endregion + private void CreateClouds() + { + for (int i = 0; i < 5; i++) + { + var cloud = new GameObject("Cloud"); + cloud.transform.SetParent(transform); + + // Random position in sky + float startX = Random.Range(-25f, 25f); + float startY = Random.Range(3f, 8f); + float depth = Random.Range(15f, 25f); + cloud.transform.position = new Vector3(startX, startY, depth); + + var renderer = cloud.AddComponent(); + renderer.sprite = CreateCloudSprite(); + renderer.sortingOrder = -90; // Behind everything but sky + + // Random size and opacity + float scale = Random.Range(3f, 6f); + cloud.transform.localScale = new Vector3(scale * 1.5f, scale, 1f); + renderer.color = new Color(1f, 1f, 1f, Random.Range(0.4f, 0.8f)); + } + } + + private Sprite CreateCloudSprite() + { + int size = 64; + Texture2D tex = new Texture2D(size, size); + Color[] pixels = new Color[size * size]; + + // Procedural fluffy cloud + Vector2 center = new Vector2(size/2, size/2); + for (int y = 0; y < size; y++) + { + for (int x = 0; x < size; x++) + { + float noise = Mathf.PerlinNoise(x * 0.15f, y * 0.15f); // Noise frequency + float dist = Vector2.Distance(new Vector2(x, y), center) / (size * 0.4f); + + // Soft circle with noise + float density = Mathf.Clamp01(1f - dist); + density *= (0.5f + noise * 0.5f); + // Threshold for fluffiness + density = Mathf.SmoothStep(0.2f, 0.8f, density); + + pixels[y * size + x] = new Color(1, 1, 1, density * density); + } + } + + tex.SetPixels(pixels); + tex.Apply(); + return Sprite.Create(tex, new Rect(0, 0, size, size), new Vector2(0.5f, 0.5f)); + } + + private void AnimateClouds() + { + // Move clouds slowly + foreach (Transform child in transform) + { + if (child.name == "Cloud") + { + Vector3 pos = child.transform.position; + // Wind speed depends on cloud distance for parallax + float speed = 0.5f + (25f - pos.z) * 0.05f; + pos.x += Time.deltaTime * speed; + + // Wrap around + if (pos.x > 30f) pos.x = -30f; + + child.transform.position = pos; + } + } + } + #region Public API /// /// Force update the environment to specific conditions. diff --git a/unity-client/Assets/Shaders.meta b/unity-client/Assets/Shaders.meta new file mode 100644 index 0000000..7a4eba5 --- /dev/null +++ b/unity-client/Assets/Shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 45d3ecb46bd4b4019847fcced069b50f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity-client/Assets/Shaders/CartoonWater.shader b/unity-client/Assets/Shaders/CartoonWater.shader new file mode 100644 index 0000000..38ec836 --- /dev/null +++ b/unity-client/Assets/Shaders/CartoonWater.shader @@ -0,0 +1,81 @@ +Shader "TheIsland/CartoonWater" +{ + Properties + { + _MainColor ("Water Color", Color) = (0.2, 0.5, 0.9, 0.8) + _FoamColor ("Foam Color", Color) = (1, 1, 1, 1) + _WaveSpeed ("Wave Speed", Range(0, 5)) = 1.0 + _WaveHeight ("Wave Height", Range(0, 1)) = 0.1 + _FoamAmount ("Foam Amount", Range(0, 1)) = 0.1 + } + SubShader + { + Tags { "RenderType"="Transparent" "Queue"="Transparent" } + LOD 100 + Blend SrcAlpha OneMinusSrcAlpha + ZWrite Off + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + + struct appdata + { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f + { + float2 uv : TEXCOORD0; + float4 vertex : SV_POSITION; + float3 worldPos : TEXCOORD1; + }; + + fixed4 _MainColor; + fixed4 _FoamColor; + float _WaveSpeed; + float _WaveHeight; + float _FoamAmount; + + v2f vert (appdata v) + { + v2f o; + // Simple vertex displacement wave + float wave = sin(_Time.y * _WaveSpeed + v.vertex.x * 2.0) * _WaveHeight; + v.vertex.y += wave; + + o.vertex = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; + return o; + } + + fixed4 frag (v2f i) : SV_Target + { + // Moving foam texture simulation using noise + float2 uv1 = i.uv + float2(_Time.x * 0.1, _Time.x * 0.05); + float noise = frac(sin(dot(uv1, float2(12.9898, 78.233))) * 43758.5453); + + // 1. Horizon/Wave Foam (Top) + float waveFoam = step(1.0 - _FoamAmount - (noise * 0.05), i.uv.y); + + // 2. Shoreline Foam (Bottom) + // Sine wave for "tide" effect + float tide = sin(_Time.y * 1.5) * 0.05; + float shoreThreshold = 0.05 + tide + (noise * 0.02); + float shoreFoam = step(i.uv.y, shoreThreshold); + + // Combine foam + float totalFoam = max(waveFoam, shoreFoam); + + fixed4 col = lerp(_MainColor, _FoamColor, totalFoam); + return col; + } + ENDCG + } + } +} diff --git a/unity-client/Assets/Shaders/CartoonWater.shader.meta b/unity-client/Assets/Shaders/CartoonWater.shader.meta new file mode 100644 index 0000000..cc63f70 --- /dev/null +++ b/unity-client/Assets/Shaders/CartoonWater.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f82a9056a602b4fcd9b86f52d2c43c9a +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity-client/Packages/manifest.json b/unity-client/Packages/manifest.json index 019c1e8..f3d93b7 100644 --- a/unity-client/Packages/manifest.json +++ b/unity-client/Packages/manifest.json @@ -43,6 +43,7 @@ "com.unity.modules.video": "1.0.0", "com.unity.modules.vr": "1.0.0", "com.unity.modules.wind": "1.0.0", - "com.unity.modules.xr": "1.0.0" + "com.unity.modules.xr": "1.0.0", + "com.unity.postprocessing": "3.4.0" } -} +} \ No newline at end of file diff --git a/unity-client/Packages/packages-lock.json b/unity-client/Packages/packages-lock.json index f01c824..68be8fb 100644 --- a/unity-client/Packages/packages-lock.json +++ b/unity-client/Packages/packages-lock.json @@ -276,6 +276,15 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.postprocessing": { + "version": "3.4.0", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + }, + "url": "https://packages.unity.com" + }, "com.unity.serialization": { "version": "3.1.3", "depth": 1, diff --git a/unity-client/ProjectSettings/ProjectSettings.asset b/unity-client/ProjectSettings/ProjectSettings.asset index aa45f78..acdfe44 100644 --- a/unity-client/ProjectSettings/ProjectSettings.asset +++ b/unity-client/ProjectSettings/ProjectSettings.asset @@ -682,7 +682,22 @@ PlayerSettings: webWasm2023: 0 webEnableSubmoduleStrippingCompatibility: 0 scriptingDefineSymbols: - Standalone: APP_UI_EDITOR_ONLY + Android: UNITY_POST_PROCESSING_STACK_V2 + EmbeddedLinux: UNITY_POST_PROCESSING_STACK_V2 + GameCoreScarlett: UNITY_POST_PROCESSING_STACK_V2 + GameCoreXboxOne: UNITY_POST_PROCESSING_STACK_V2 + Kepler: UNITY_POST_PROCESSING_STACK_V2 + LinuxHeadlessSimulation: UNITY_POST_PROCESSING_STACK_V2 + Nintendo Switch: UNITY_POST_PROCESSING_STACK_V2 + Nintendo Switch 2: UNITY_POST_PROCESSING_STACK_V2 + PS4: UNITY_POST_PROCESSING_STACK_V2 + PS5: UNITY_POST_PROCESSING_STACK_V2 + QNX: UNITY_POST_PROCESSING_STACK_V2 + Standalone: APP_UI_EDITOR_ONLY;UNITY_POST_PROCESSING_STACK_V2 + VisionOS: UNITY_POST_PROCESSING_STACK_V2 + WebGL: UNITY_POST_PROCESSING_STACK_V2 + XboxOne: UNITY_POST_PROCESSING_STACK_V2 + tvOS: UNITY_POST_PROCESSING_STACK_V2 additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: {}