feat(unity): enhance visual effects and animations
- Add cloud system with procedural sprites and parallax movement - Add tree swaying animation for palm trees - Improve agent breathing with squash & stretch animation - Add jump animation routine for agent reactions - Add custom CartoonWater shader support - Add SetupVisuals editor tool and GlobalProfile asset - Lower speech bubble alpha for glass effect 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
74
unity-client/Assets/Editor/SetupVisuals.cs
Normal file
74
unity-client/Assets/Editor/SetupVisuals.cs
Normal file
@@ -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<PostProcessLayer>();
|
||||||
|
if (layer == null) layer = camera.gameObject.AddComponent<PostProcessLayer>();
|
||||||
|
|
||||||
|
// 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<PostProcessVolume>();
|
||||||
|
if (volume == null) volume = volumeGo.AddComponent<PostProcessVolume>();
|
||||||
|
|
||||||
|
volume.isGlobal = true;
|
||||||
|
|
||||||
|
// 3. Create Profile if not exists
|
||||||
|
var profilePath = "Assets/GlobalProfile.asset";
|
||||||
|
var profile = AssetDatabase.LoadAssetAtPath<PostProcessProfile>(profilePath);
|
||||||
|
if (profile == null)
|
||||||
|
{
|
||||||
|
profile = ScriptableObject.CreateInstance<PostProcessProfile>();
|
||||||
|
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>();
|
||||||
|
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>();
|
||||||
|
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>();
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
2
unity-client/Assets/Editor/SetupVisuals.cs.meta
Normal file
2
unity-client/Assets/Editor/SetupVisuals.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2f42deb3551fc40dfbe902fe1945f25b
|
||||||
18
unity-client/Assets/GlobalProfile.asset
Normal file
18
unity-client/Assets/GlobalProfile.asset
Normal file
@@ -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}
|
||||||
8
unity-client/Assets/GlobalProfile.asset.meta
Normal file
8
unity-client/Assets/GlobalProfile.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5a9d5cd213c7f4bebb196509d67c68e3
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -95,29 +95,60 @@ namespace TheIsland.Visual
|
|||||||
{
|
{
|
||||||
if (!IsAlive) return;
|
if (!IsAlive) return;
|
||||||
|
|
||||||
// Idle breathing animation
|
// Idle breathing animation (Squash and Stretch)
|
||||||
_idleAnimTimer += Time.deltaTime;
|
_idleAnimTimer += Time.deltaTime;
|
||||||
_breathScale = 1f + Mathf.Sin(_idleAnimTimer * 2f) * 0.02f;
|
|
||||||
|
|
||||||
// Gentle bobbing
|
// Breathing: Scale Y up, Scale X down (preserving volume)
|
||||||
_bobOffset = Mathf.Sin(_idleAnimTimer * 1.5f) * 0.05f;
|
float breath = Mathf.Sin(_idleAnimTimer * 3f) * 0.05f;
|
||||||
|
_breathScale = 1f + breath;
|
||||||
|
float antiBreath = 1f - (breath * 0.5f); // Squash X when stretching Y
|
||||||
|
|
||||||
|
// Bobbing: Move up and down
|
||||||
|
_bobOffset = Mathf.Sin(_idleAnimTimer * 2f) * 0.08f;
|
||||||
|
|
||||||
if (_spriteRenderer != null && _originalSpriteScale != Vector3.zero)
|
if (_spriteRenderer != null && _originalSpriteScale != Vector3.zero)
|
||||||
{
|
{
|
||||||
// Apply breathing scale
|
// Apply squash & stretch
|
||||||
_spriteRenderer.transform.localScale = new Vector3(
|
_spriteRenderer.transform.localScale = new Vector3(
|
||||||
_originalSpriteScale.x * _breathScale,
|
_originalSpriteScale.x * antiBreath,
|
||||||
_originalSpriteScale.y * _breathScale,
|
_originalSpriteScale.y * _breathScale,
|
||||||
_originalSpriteScale.z
|
_originalSpriteScale.z
|
||||||
);
|
);
|
||||||
|
|
||||||
// Apply bobbing
|
// Apply bobbing position
|
||||||
var pos = _spriteRenderer.transform.localPosition;
|
var pos = _spriteRenderer.transform.localPosition;
|
||||||
pos.y = 1f + _bobOffset;
|
pos.y = 1f + _bobOffset;
|
||||||
_spriteRenderer.transform.localPosition = pos;
|
_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()
|
private void OnMouseDown()
|
||||||
{
|
{
|
||||||
if (!IsAlive)
|
if (!IsAlive)
|
||||||
@@ -604,7 +635,7 @@ namespace TheIsland.Visual
|
|||||||
var bg = panel.AddComponent<Image>();
|
var bg = panel.AddComponent<Image>();
|
||||||
bg.sprite = CreateRoundedRectSprite(32, 32, 8);
|
bg.sprite = CreateRoundedRectSprite(32, 32, 8);
|
||||||
bg.type = Image.Type.Sliced;
|
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
|
// Add subtle border
|
||||||
var borderObj = new GameObject("Border");
|
var borderObj = new GameObject("Border");
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ namespace TheIsland.UI
|
|||||||
panelRect.offsetMax = new Vector2(360, -80);
|
panelRect.offsetMax = new Vector2(360, -80);
|
||||||
|
|
||||||
var panelImg = _panel.AddComponent<Image>();
|
var panelImg = _panel.AddComponent<Image>();
|
||||||
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");
|
var header = new GameObject("Header");
|
||||||
@@ -201,7 +201,7 @@ namespace TheIsland.UI
|
|||||||
headerRect.sizeDelta = new Vector2(0, 28);
|
headerRect.sizeDelta = new Vector2(0, 28);
|
||||||
headerRect.anchoredPosition = Vector2.zero;
|
headerRect.anchoredPosition = Vector2.zero;
|
||||||
|
|
||||||
header.AddComponent<Image>().color = new Color(0.12f, 0.15f, 0.2f);
|
header.AddComponent<Image>().color = new Color(0.12f, 0.15f, 0.2f, 0.8f);
|
||||||
|
|
||||||
var titleObj = new GameObject("Title");
|
var titleObj = new GameObject("Title");
|
||||||
titleObj.transform.SetParent(header.transform, false);
|
titleObj.transform.SetParent(header.transform, false);
|
||||||
@@ -296,8 +296,8 @@ namespace TheIsland.UI
|
|||||||
entry.transform.SetParent(_content, false);
|
entry.transform.SetParent(_content, false);
|
||||||
|
|
||||||
entry.AddComponent<Image>().color = _entries.Count % 2 == 0
|
entry.AddComponent<Image>().color = _entries.Count % 2 == 0
|
||||||
? new Color(0.08f, 0.1f, 0.13f, 0.9f)
|
? new Color(0f, 0f, 0f, 0.2f)
|
||||||
: new Color(0.06f, 0.08f, 0.11f, 0.9f);
|
: new Color(0f, 0f, 0f, 0.1f);
|
||||||
|
|
||||||
var le = entry.AddComponent<LayoutElement>();
|
var le = entry.AddComponent<LayoutElement>();
|
||||||
le.minHeight = 36;
|
le.minHeight = 36;
|
||||||
|
|||||||
@@ -322,6 +322,7 @@ namespace TheIsland.Core
|
|||||||
if (_agentVisuals.TryGetValue(data.agent_id, out AgentVisual agentVisual))
|
if (_agentVisuals.TryGetValue(data.agent_id, out AgentVisual agentVisual))
|
||||||
{
|
{
|
||||||
agentVisual.ShowSpeech(data.text);
|
agentVisual.ShowSpeech(data.text);
|
||||||
|
agentVisual.DoJump(); // Add jump effect
|
||||||
}
|
}
|
||||||
// Check AgentUI (programmatic UI system)
|
// Check AgentUI (programmatic UI system)
|
||||||
else if (_agentUIs.TryGetValue(data.agent_id, out AgentUI agentUI))
|
else if (_agentUIs.TryGetValue(data.agent_id, out AgentUI agentUI))
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ namespace TheIsland.UI
|
|||||||
topBar.offsetMax = new Vector2(-10, -10);
|
topBar.offsetMax = new Vector2(-10, -10);
|
||||||
|
|
||||||
var topBarImg = topBar.gameObject.AddComponent<Image>();
|
var topBarImg = topBar.gameObject.AddComponent<Image>();
|
||||||
topBarImg.color = new Color(0, 0, 0, 0.7f);
|
topBarImg.color = new Color(0, 0, 0, 0.0f); // 透明顶部栏
|
||||||
|
|
||||||
// Connection Status (Left)
|
// Connection Status (Left)
|
||||||
_connectionStatus = CreateText(topBar, "ConnectionStatus", "● Disconnected",
|
_connectionStatus = CreateText(topBar, "ConnectionStatus", "● Disconnected",
|
||||||
@@ -205,7 +205,7 @@ namespace TheIsland.UI
|
|||||||
bottomBar.offsetMax = new Vector2(-10, 70);
|
bottomBar.offsetMax = new Vector2(-10, 70);
|
||||||
|
|
||||||
var bottomBarImg = bottomBar.gameObject.AddComponent<Image>();
|
var bottomBarImg = bottomBar.gameObject.AddComponent<Image>();
|
||||||
bottomBarImg.color = new Color(0, 0, 0, 0.7f);
|
bottomBarImg.color = new Color(0, 0, 0, 0.2f); // 低透明度底部栏
|
||||||
|
|
||||||
// Command Input
|
// Command Input
|
||||||
var inputObj = new GameObject("CommandInput");
|
var inputObj = new GameObject("CommandInput");
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ namespace TheIsland.Visual
|
|||||||
[SerializeField] private Color waterDeepColor = new Color(0.1f, 0.4f, 0.6f, 0.9f);
|
[SerializeField] private Color waterDeepColor = new Color(0.1f, 0.4f, 0.6f, 0.9f);
|
||||||
[SerializeField] private float waveSpeed = 0.5f;
|
[SerializeField] private float waveSpeed = 0.5f;
|
||||||
[SerializeField] private float waveAmplitude = 0.1f;
|
[SerializeField] private float waveAmplitude = 0.1f;
|
||||||
|
[SerializeField] private Material customWaterMaterial; // Custom shader support
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region References
|
#region References
|
||||||
@@ -116,8 +117,9 @@ namespace TheIsland.Visual
|
|||||||
UpdateSkyMaterial();
|
UpdateSkyMaterial();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animate water
|
// Animate environment (Water & Trees)
|
||||||
AnimateWater();
|
AnimateEnvironment();
|
||||||
|
AnimateClouds();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
@@ -140,6 +142,7 @@ namespace TheIsland.Visual
|
|||||||
CreateWater();
|
CreateWater();
|
||||||
CreateLighting();
|
CreateLighting();
|
||||||
CreateDecorations();
|
CreateDecorations();
|
||||||
|
CreateClouds();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateSky()
|
private void CreateSky()
|
||||||
@@ -308,9 +311,17 @@ namespace TheIsland.Visual
|
|||||||
_waterPlane.transform.localScale = new Vector3(60, 15, 1);
|
_waterPlane.transform.localScale = new Vector3(60, 15, 1);
|
||||||
|
|
||||||
// Create water material
|
// Create water material
|
||||||
_waterMaterial = new Material(Shader.Find("Unlit/Transparent"));
|
if (customWaterMaterial != null)
|
||||||
_waterMaterial.mainTexture = CreateWaterTexture();
|
{
|
||||||
_waterPlane.GetComponent<Renderer>().material = _waterMaterial;
|
_waterMaterial = customWaterMaterial;
|
||||||
|
_waterPlane.GetComponent<Renderer>().material = _waterMaterial;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_waterMaterial = new Material(Shader.Find("Unlit/Transparent"));
|
||||||
|
_waterMaterial.mainTexture = CreateWaterTexture();
|
||||||
|
_waterPlane.GetComponent<Renderer>().material = _waterMaterial;
|
||||||
|
}
|
||||||
_waterPlane.GetComponent<Renderer>().sortingOrder = -40;
|
_waterPlane.GetComponent<Renderer>().sortingOrder = -40;
|
||||||
|
|
||||||
Destroy(_waterPlane.GetComponent<Collider>());
|
Destroy(_waterPlane.GetComponent<Collider>());
|
||||||
@@ -439,6 +450,29 @@ namespace TheIsland.Visual
|
|||||||
return Sprite.Create(tex, new Rect(0, 0, width, height), new Vector2(0.5f, 0));
|
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)
|
private void DrawPalmFronds(Color[] pixels, int width, int height, Color leaf, Color leafBright)
|
||||||
{
|
{
|
||||||
Vector2 center = new Vector2(width / 2, height * 0.65f);
|
Vector2 center = new Vector2(width / 2, height * 0.65f);
|
||||||
@@ -612,6 +646,80 @@ namespace TheIsland.Visual
|
|||||||
}
|
}
|
||||||
#endregion
|
#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<SpriteRenderer>();
|
||||||
|
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
|
#region Public API
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Force update the environment to specific conditions.
|
/// Force update the environment to specific conditions.
|
||||||
|
|||||||
8
unity-client/Assets/Shaders.meta
Normal file
8
unity-client/Assets/Shaders.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 45d3ecb46bd4b4019847fcced069b50f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
81
unity-client/Assets/Shaders/CartoonWater.shader
Normal file
81
unity-client/Assets/Shaders/CartoonWater.shader
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
unity-client/Assets/Shaders/CartoonWater.shader.meta
Normal file
9
unity-client/Assets/Shaders/CartoonWater.shader.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f82a9056a602b4fcd9b86f52d2c43c9a
|
||||||
|
ShaderImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
defaultTextures: []
|
||||||
|
nonModifiableTextures: []
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
"com.unity.modules.video": "1.0.0",
|
"com.unity.modules.video": "1.0.0",
|
||||||
"com.unity.modules.vr": "1.0.0",
|
"com.unity.modules.vr": "1.0.0",
|
||||||
"com.unity.modules.wind": "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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,6 +276,15 @@
|
|||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"url": "https://packages.unity.com"
|
"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": {
|
"com.unity.serialization": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"depth": 1,
|
"depth": 1,
|
||||||
|
|||||||
@@ -682,7 +682,22 @@ PlayerSettings:
|
|||||||
webWasm2023: 0
|
webWasm2023: 0
|
||||||
webEnableSubmoduleStrippingCompatibility: 0
|
webEnableSubmoduleStrippingCompatibility: 0
|
||||||
scriptingDefineSymbols:
|
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: {}
|
additionalCompilerArguments: {}
|
||||||
platformArchitecture: {}
|
platformArchitecture: {}
|
||||||
scriptingBackend: {}
|
scriptingBackend: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user