feat: add gameplay enhancements and visual improvements
Backend: - Add weather system with 6 weather types and transition probabilities - Add day/night cycle (dawn, day, dusk, night) with phase modifiers - Add mood system for agents (happy, neutral, sad, anxious) - Add new commands: heal, talk, encourage, revive - Add agent social interaction system with relationships - Add casual mode with auto-revive and reduced decay rates Frontend (Web): - Add world state display (weather, time of day) - Add mood bar to agent cards - Add new action buttons for heal, encourage, talk, revive - Handle new event types from server Unity Client: - Add EnvironmentManager with dynamic sky gradient and island scene - Add WeatherEffects with rain, sun rays, fog, and heat particles - Add SceneBootstrap for automatic visual system initialization - Improve AgentVisual with better character sprites and animations - Add breathing and bobbing idle animations - Add character shadows - Improve UI panels with rounded corners and borders - Improve SpeechBubble with rounded corners and proper tail - Add support for all new server events and commands 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
627
unity-client/Assets/Scripts/Visual/EnvironmentManager.cs
Normal file
627
unity-client/Assets/Scripts/Visual/EnvironmentManager.cs
Normal file
@@ -0,0 +1,627 @@
|
||||
using UnityEngine;
|
||||
using TheIsland.Core;
|
||||
using TheIsland.Network;
|
||||
using TheIsland.Models;
|
||||
|
||||
namespace TheIsland.Visual
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the island environment visuals including sky, ground, water, and lighting.
|
||||
/// Creates a beautiful dynamic background that responds to time of day and weather.
|
||||
/// </summary>
|
||||
public class EnvironmentManager : MonoBehaviour
|
||||
{
|
||||
#region Singleton
|
||||
private static EnvironmentManager _instance;
|
||||
public static EnvironmentManager Instance => _instance;
|
||||
#endregion
|
||||
|
||||
#region Sky Colors by Time of Day
|
||||
[Header("Dawn Colors")]
|
||||
[SerializeField] private Color dawnSkyTop = new Color(0.98f, 0.65f, 0.45f);
|
||||
[SerializeField] private Color dawnSkyBottom = new Color(1f, 0.85f, 0.6f);
|
||||
[SerializeField] private Color dawnAmbient = new Color(1f, 0.8f, 0.6f);
|
||||
|
||||
[Header("Day Colors")]
|
||||
[SerializeField] private Color daySkyTop = new Color(0.4f, 0.7f, 1f);
|
||||
[SerializeField] private Color daySkyBottom = new Color(0.7f, 0.9f, 1f);
|
||||
[SerializeField] private Color dayAmbient = new Color(1f, 1f, 0.95f);
|
||||
|
||||
[Header("Dusk Colors")]
|
||||
[SerializeField] private Color duskSkyTop = new Color(0.3f, 0.2f, 0.5f);
|
||||
[SerializeField] private Color duskSkyBottom = new Color(1f, 0.5f, 0.3f);
|
||||
[SerializeField] private Color duskAmbient = new Color(1f, 0.6f, 0.4f);
|
||||
|
||||
[Header("Night Colors")]
|
||||
[SerializeField] private Color nightSkyTop = new Color(0.05f, 0.05f, 0.15f);
|
||||
[SerializeField] private Color nightSkyBottom = new Color(0.1f, 0.15f, 0.3f);
|
||||
[SerializeField] private Color nightAmbient = new Color(0.3f, 0.35f, 0.5f);
|
||||
#endregion
|
||||
|
||||
#region Weather Modifiers
|
||||
[Header("Weather Color Modifiers")]
|
||||
[SerializeField] private Color cloudyTint = new Color(0.7f, 0.7f, 0.75f);
|
||||
[SerializeField] private Color rainyTint = new Color(0.5f, 0.55f, 0.6f);
|
||||
[SerializeField] private Color stormyTint = new Color(0.35f, 0.35f, 0.4f);
|
||||
[SerializeField] private Color foggyTint = new Color(0.8f, 0.8f, 0.85f);
|
||||
[SerializeField] private Color hotTint = new Color(1.1f, 0.95f, 0.85f);
|
||||
#endregion
|
||||
|
||||
#region Ground & Water
|
||||
[Header("Ground Settings")]
|
||||
[SerializeField] private Color sandColor = new Color(0.95f, 0.87f, 0.7f);
|
||||
[SerializeField] private Color sandDarkColor = new Color(0.8f, 0.7f, 0.5f);
|
||||
|
||||
[Header("Water Settings")]
|
||||
[SerializeField] private Color waterShallowColor = new Color(0.3f, 0.8f, 0.9f, 0.8f);
|
||||
[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;
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
private Camera _mainCamera;
|
||||
private Material _skyMaterial;
|
||||
private GameObject _groundPlane;
|
||||
private GameObject _waterPlane;
|
||||
private Material _groundMaterial;
|
||||
private Material _waterMaterial;
|
||||
private Light _mainLight;
|
||||
|
||||
// Current state
|
||||
private string _currentTimeOfDay = "day";
|
||||
private string _currentWeather = "Sunny";
|
||||
private float _transitionProgress = 1f;
|
||||
private Color _targetSkyTop, _targetSkyBottom;
|
||||
private Color _currentSkyTop, _currentSkyBottom;
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
private void Awake()
|
||||
{
|
||||
if (_instance != null && _instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
_instance = this;
|
||||
|
||||
_mainCamera = Camera.main;
|
||||
CreateEnvironment();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Subscribe to network events
|
||||
var network = NetworkManager.Instance;
|
||||
if (network != null)
|
||||
{
|
||||
network.OnPhaseChange += HandlePhaseChange;
|
||||
network.OnWeatherChange += HandleWeatherChange;
|
||||
network.OnTick += HandleTick;
|
||||
}
|
||||
|
||||
// Set initial sky
|
||||
UpdateSkyColors();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Smooth sky transition
|
||||
if (_transitionProgress < 1f)
|
||||
{
|
||||
_transitionProgress += Time.deltaTime * 0.5f;
|
||||
_currentSkyTop = Color.Lerp(_currentSkyTop, _targetSkyTop, _transitionProgress);
|
||||
_currentSkyBottom = Color.Lerp(_currentSkyBottom, _targetSkyBottom, _transitionProgress);
|
||||
UpdateSkyMaterial();
|
||||
}
|
||||
|
||||
// Animate water
|
||||
AnimateWater();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
var network = NetworkManager.Instance;
|
||||
if (network != null)
|
||||
{
|
||||
network.OnPhaseChange -= HandlePhaseChange;
|
||||
network.OnWeatherChange -= HandleWeatherChange;
|
||||
network.OnTick -= HandleTick;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Environment Creation
|
||||
private void CreateEnvironment()
|
||||
{
|
||||
CreateSky();
|
||||
CreateGround();
|
||||
CreateWater();
|
||||
CreateLighting();
|
||||
CreateDecorations();
|
||||
}
|
||||
|
||||
private void CreateSky()
|
||||
{
|
||||
// Create a gradient sky using a camera background shader
|
||||
_skyMaterial = new Material(Shader.Find("Unlit/Color"));
|
||||
|
||||
// Create sky quad that fills the background
|
||||
var skyObj = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||
skyObj.name = "SkyBackground";
|
||||
skyObj.transform.SetParent(transform);
|
||||
skyObj.transform.position = new Vector3(0, 5, 20);
|
||||
skyObj.transform.localScale = new Vector3(60, 30, 1);
|
||||
|
||||
// Remove collider
|
||||
Destroy(skyObj.GetComponent<Collider>());
|
||||
|
||||
// Create gradient material
|
||||
_skyMaterial = CreateGradientMaterial();
|
||||
skyObj.GetComponent<Renderer>().material = _skyMaterial;
|
||||
skyObj.GetComponent<Renderer>().sortingOrder = -100;
|
||||
|
||||
// Set initial colors
|
||||
_currentSkyTop = daySkyTop;
|
||||
_currentSkyBottom = daySkyBottom;
|
||||
_targetSkyTop = daySkyTop;
|
||||
_targetSkyBottom = daySkyBottom;
|
||||
UpdateSkyMaterial();
|
||||
}
|
||||
|
||||
private Material CreateGradientMaterial()
|
||||
{
|
||||
// Create a simple shader for vertical gradient
|
||||
string shaderCode = @"
|
||||
Shader ""Custom/SkyGradient"" {
|
||||
Properties {
|
||||
_TopColor (""Top Color"", Color) = (0.4, 0.7, 1, 1)
|
||||
_BottomColor (""Bottom Color"", Color) = (0.7, 0.9, 1, 1)
|
||||
}
|
||||
SubShader {
|
||||
Tags { ""Queue""=""Background"" ""RenderType""=""Opaque"" }
|
||||
Pass {
|
||||
ZWrite Off
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include ""UnityCG.cginc""
|
||||
|
||||
fixed4 _TopColor;
|
||||
fixed4 _BottomColor;
|
||||
|
||||
struct v2f {
|
||||
float4 pos : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
v2f vert(appdata_base v) {
|
||||
v2f o;
|
||||
o.pos = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = v.texcoord;
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target {
|
||||
return lerp(_BottomColor, _TopColor, i.uv.y);
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
// Since we can't create shaders at runtime easily, use a texture-based approach
|
||||
return CreateGradientTextureMaterial();
|
||||
}
|
||||
|
||||
private Material CreateGradientTextureMaterial()
|
||||
{
|
||||
// Create gradient texture
|
||||
Texture2D gradientTex = new Texture2D(1, 256);
|
||||
gradientTex.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
for (int y = 0; y < 256; y++)
|
||||
{
|
||||
float t = y / 255f;
|
||||
Color color = Color.Lerp(_currentSkyBottom, _currentSkyTop, t);
|
||||
gradientTex.SetPixel(0, y, color);
|
||||
}
|
||||
gradientTex.Apply();
|
||||
|
||||
Material mat = new Material(Shader.Find("Unlit/Texture"));
|
||||
mat.mainTexture = gradientTex;
|
||||
return mat;
|
||||
}
|
||||
|
||||
private void UpdateSkyMaterial()
|
||||
{
|
||||
if (_skyMaterial == null || _skyMaterial.mainTexture == null) return;
|
||||
|
||||
Texture2D tex = (Texture2D)_skyMaterial.mainTexture;
|
||||
for (int y = 0; y < 256; y++)
|
||||
{
|
||||
float t = y / 255f;
|
||||
Color color = Color.Lerp(_currentSkyBottom, _currentSkyTop, t);
|
||||
tex.SetPixel(0, y, color);
|
||||
}
|
||||
tex.Apply();
|
||||
}
|
||||
|
||||
private void CreateGround()
|
||||
{
|
||||
// Create sandy beach ground
|
||||
_groundPlane = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||
_groundPlane.name = "GroundPlane";
|
||||
_groundPlane.transform.SetParent(transform);
|
||||
_groundPlane.transform.position = new Vector3(0, -0.5f, 5);
|
||||
_groundPlane.transform.rotation = Quaternion.Euler(90, 0, 0);
|
||||
_groundPlane.transform.localScale = new Vector3(40, 20, 1);
|
||||
|
||||
// Create sand texture
|
||||
_groundMaterial = new Material(Shader.Find("Unlit/Texture"));
|
||||
_groundMaterial.mainTexture = CreateSandTexture();
|
||||
_groundPlane.GetComponent<Renderer>().material = _groundMaterial;
|
||||
_groundPlane.GetComponent<Renderer>().sortingOrder = -50;
|
||||
|
||||
// Remove collider (we don't need physics)
|
||||
Destroy(_groundPlane.GetComponent<Collider>());
|
||||
}
|
||||
|
||||
private Texture2D CreateSandTexture()
|
||||
{
|
||||
int size = 128;
|
||||
Texture2D tex = new Texture2D(size, size);
|
||||
tex.filterMode = FilterMode.Bilinear;
|
||||
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
// Create sandy noise pattern
|
||||
float noise = Mathf.PerlinNoise(x * 0.1f, y * 0.1f) * 0.3f;
|
||||
float detail = Mathf.PerlinNoise(x * 0.3f, y * 0.3f) * 0.1f;
|
||||
|
||||
Color baseColor = Color.Lerp(sandDarkColor, sandColor, 0.5f + noise + detail);
|
||||
|
||||
// Add some sparkle/grain
|
||||
if (Random.value > 0.95f)
|
||||
{
|
||||
baseColor = Color.Lerp(baseColor, Color.white, 0.3f);
|
||||
}
|
||||
|
||||
tex.SetPixel(x, y, baseColor);
|
||||
}
|
||||
}
|
||||
tex.Apply();
|
||||
return tex;
|
||||
}
|
||||
|
||||
private void CreateWater()
|
||||
{
|
||||
// Create water plane at the horizon
|
||||
_waterPlane = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||
_waterPlane.name = "WaterPlane";
|
||||
_waterPlane.transform.SetParent(transform);
|
||||
_waterPlane.transform.position = new Vector3(0, -0.3f, 12);
|
||||
_waterPlane.transform.rotation = Quaternion.Euler(90, 0, 0);
|
||||
_waterPlane.transform.localScale = new Vector3(60, 15, 1);
|
||||
|
||||
// Create water material
|
||||
_waterMaterial = new Material(Shader.Find("Unlit/Transparent"));
|
||||
_waterMaterial.mainTexture = CreateWaterTexture();
|
||||
_waterPlane.GetComponent<Renderer>().material = _waterMaterial;
|
||||
_waterPlane.GetComponent<Renderer>().sortingOrder = -40;
|
||||
|
||||
Destroy(_waterPlane.GetComponent<Collider>());
|
||||
}
|
||||
|
||||
private Texture2D CreateWaterTexture()
|
||||
{
|
||||
int size = 128;
|
||||
Texture2D tex = new Texture2D(size, size);
|
||||
tex.filterMode = FilterMode.Bilinear;
|
||||
tex.wrapMode = TextureWrapMode.Repeat;
|
||||
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
float t = (float)y / size;
|
||||
Color baseColor = Color.Lerp(waterShallowColor, waterDeepColor, t);
|
||||
|
||||
// Add wave highlights
|
||||
float wave = Mathf.Sin(x * 0.2f + y * 0.1f) * 0.5f + 0.5f;
|
||||
baseColor = Color.Lerp(baseColor, Color.white, wave * 0.1f);
|
||||
|
||||
tex.SetPixel(x, y, baseColor);
|
||||
}
|
||||
}
|
||||
tex.Apply();
|
||||
return tex;
|
||||
}
|
||||
|
||||
private void AnimateWater()
|
||||
{
|
||||
if (_waterMaterial == null) return;
|
||||
|
||||
// Simple UV scrolling for wave effect
|
||||
float offset = Time.time * waveSpeed * 0.1f;
|
||||
_waterMaterial.mainTextureOffset = new Vector2(offset, offset * 0.5f);
|
||||
}
|
||||
|
||||
private void CreateLighting()
|
||||
{
|
||||
// Find or create main directional light
|
||||
_mainLight = FindFirstObjectByType<Light>();
|
||||
if (_mainLight == null)
|
||||
{
|
||||
var lightObj = new GameObject("MainLight");
|
||||
lightObj.transform.SetParent(transform);
|
||||
_mainLight = lightObj.AddComponent<Light>();
|
||||
_mainLight.type = LightType.Directional;
|
||||
}
|
||||
|
||||
_mainLight.transform.rotation = Quaternion.Euler(50, -30, 0);
|
||||
_mainLight.intensity = 1f;
|
||||
_mainLight.color = dayAmbient;
|
||||
|
||||
// Set ambient light
|
||||
RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Flat;
|
||||
RenderSettings.ambientLight = dayAmbient;
|
||||
}
|
||||
|
||||
private void CreateDecorations()
|
||||
{
|
||||
// Create palm tree silhouettes
|
||||
CreatePalmTree(new Vector3(-8, 0, 8), 2.5f);
|
||||
CreatePalmTree(new Vector3(-10, 0, 10), 3f);
|
||||
CreatePalmTree(new Vector3(9, 0, 7), 2.2f);
|
||||
CreatePalmTree(new Vector3(11, 0, 9), 2.8f);
|
||||
|
||||
// Create rocks
|
||||
CreateRock(new Vector3(-5, 0, 4), 0.5f);
|
||||
CreateRock(new Vector3(6, 0, 5), 0.7f);
|
||||
CreateRock(new Vector3(-7, 0, 6), 0.4f);
|
||||
}
|
||||
|
||||
private void CreatePalmTree(Vector3 position, float scale)
|
||||
{
|
||||
var treeObj = new GameObject("PalmTree");
|
||||
treeObj.transform.SetParent(transform);
|
||||
treeObj.transform.position = position;
|
||||
|
||||
// Create trunk (stretched capsule-ish shape using sprite)
|
||||
var trunkSprite = new GameObject("Trunk");
|
||||
trunkSprite.transform.SetParent(treeObj.transform);
|
||||
trunkSprite.transform.localPosition = new Vector3(0, scale * 0.5f, 0);
|
||||
|
||||
var trunkRenderer = trunkSprite.AddComponent<SpriteRenderer>();
|
||||
trunkRenderer.sprite = CreateTreeSprite();
|
||||
trunkRenderer.sortingOrder = -20;
|
||||
trunkSprite.transform.localScale = new Vector3(scale * 0.5f, scale, 1);
|
||||
}
|
||||
|
||||
private Sprite CreateTreeSprite()
|
||||
{
|
||||
int width = 64;
|
||||
int height = 128;
|
||||
Texture2D tex = new Texture2D(width, height);
|
||||
|
||||
Color trunk = new Color(0.4f, 0.25f, 0.15f);
|
||||
Color trunkDark = new Color(0.3f, 0.18f, 0.1f);
|
||||
Color leaf = new Color(0.2f, 0.5f, 0.2f);
|
||||
Color leafBright = new Color(0.3f, 0.65f, 0.25f);
|
||||
|
||||
// Clear
|
||||
Color[] pixels = new Color[width * height];
|
||||
for (int i = 0; i < pixels.Length; i++) pixels[i] = Color.clear;
|
||||
|
||||
// Draw trunk
|
||||
int trunkWidth = 8;
|
||||
int trunkStart = width / 2 - trunkWidth / 2;
|
||||
for (int y = 0; y < height * 0.6f; y++)
|
||||
{
|
||||
for (int x = trunkStart; x < trunkStart + trunkWidth; x++)
|
||||
{
|
||||
float noise = Mathf.PerlinNoise(x * 0.2f, y * 0.1f);
|
||||
pixels[y * width + x] = Color.Lerp(trunkDark, trunk, noise);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw palm fronds
|
||||
DrawPalmFronds(pixels, width, height, leaf, leafBright);
|
||||
|
||||
tex.SetPixels(pixels);
|
||||
tex.Apply();
|
||||
tex.filterMode = FilterMode.Point;
|
||||
|
||||
return Sprite.Create(tex, new Rect(0, 0, width, height), new Vector2(0.5f, 0));
|
||||
}
|
||||
|
||||
private void DrawPalmFronds(Color[] pixels, int width, int height, Color leaf, Color leafBright)
|
||||
{
|
||||
Vector2 center = new Vector2(width / 2, height * 0.65f);
|
||||
|
||||
// Draw several fronds
|
||||
float[] angles = { -60, -30, 0, 30, 60, -80, 80 };
|
||||
foreach (float angle in angles)
|
||||
{
|
||||
DrawFrond(pixels, width, height, center, angle, leaf, leafBright);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawFrond(Color[] pixels, int width, int height, Vector2 start, float angle, Color leaf, Color leafBright)
|
||||
{
|
||||
float rad = angle * Mathf.Deg2Rad;
|
||||
int length = 35;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
float t = i / (float)length;
|
||||
float droop = t * t * 15; // Fronds droop more at the end
|
||||
|
||||
int x = (int)(start.x + Mathf.Sin(rad) * i);
|
||||
int y = (int)(start.y + Mathf.Cos(rad) * i - droop);
|
||||
|
||||
// Draw thick frond
|
||||
for (int dx = -2; dx <= 2; dx++)
|
||||
{
|
||||
for (int dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
int px = x + dx;
|
||||
int py = y + dy;
|
||||
if (px >= 0 && px < width && py >= 0 && py < height)
|
||||
{
|
||||
float brightness = Mathf.PerlinNoise(px * 0.1f, py * 0.1f);
|
||||
pixels[py * width + px] = Color.Lerp(leaf, leafBright, brightness);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateRock(Vector3 position, float scale)
|
||||
{
|
||||
var rockObj = new GameObject("Rock");
|
||||
rockObj.transform.SetParent(transform);
|
||||
rockObj.transform.position = position;
|
||||
|
||||
var rockRenderer = rockObj.AddComponent<SpriteRenderer>();
|
||||
rockRenderer.sprite = CreateRockSprite();
|
||||
rockRenderer.sortingOrder = -15;
|
||||
rockObj.transform.localScale = Vector3.one * scale;
|
||||
}
|
||||
|
||||
private Sprite CreateRockSprite()
|
||||
{
|
||||
int size = 32;
|
||||
Texture2D tex = new Texture2D(size, size);
|
||||
|
||||
Color rockDark = new Color(0.3f, 0.3f, 0.35f);
|
||||
Color rockLight = new Color(0.5f, 0.5f, 0.55f);
|
||||
|
||||
Color[] pixels = new Color[size * size];
|
||||
for (int i = 0; i < pixels.Length; i++) pixels[i] = Color.clear;
|
||||
|
||||
// Draw rock shape
|
||||
Vector2 center = new Vector2(size / 2, size / 3);
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
float dx = (x - center.x) / (size * 0.4f);
|
||||
float dy = (y - center.y) / (size * 0.3f);
|
||||
float dist = dx * dx + dy * dy;
|
||||
|
||||
if (dist < 1 && y < size * 0.7f)
|
||||
{
|
||||
float noise = Mathf.PerlinNoise(x * 0.2f, y * 0.2f);
|
||||
pixels[y * size + x] = Color.Lerp(rockDark, rockLight, noise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tex.SetPixels(pixels);
|
||||
tex.Apply();
|
||||
tex.filterMode = FilterMode.Point;
|
||||
|
||||
return Sprite.Create(tex, new Rect(0, 0, size, size), new Vector2(0.5f, 0));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
private void HandlePhaseChange(PhaseChangeData data)
|
||||
{
|
||||
_currentTimeOfDay = data.new_phase;
|
||||
UpdateSkyColors();
|
||||
}
|
||||
|
||||
private void HandleWeatherChange(WeatherChangeData data)
|
||||
{
|
||||
_currentWeather = data.new_weather;
|
||||
UpdateSkyColors();
|
||||
}
|
||||
|
||||
private void HandleTick(TickData data)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(data.time_of_day) && data.time_of_day != _currentTimeOfDay)
|
||||
{
|
||||
_currentTimeOfDay = data.time_of_day;
|
||||
UpdateSkyColors();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(data.weather) && data.weather != _currentWeather)
|
||||
{
|
||||
_currentWeather = data.weather;
|
||||
UpdateSkyColors();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSkyColors()
|
||||
{
|
||||
// Get base colors for time of day
|
||||
Color baseTop, baseBottom, ambient;
|
||||
|
||||
switch (_currentTimeOfDay)
|
||||
{
|
||||
case "dawn":
|
||||
baseTop = dawnSkyTop;
|
||||
baseBottom = dawnSkyBottom;
|
||||
ambient = dawnAmbient;
|
||||
break;
|
||||
case "dusk":
|
||||
baseTop = duskSkyTop;
|
||||
baseBottom = duskSkyBottom;
|
||||
ambient = duskAmbient;
|
||||
break;
|
||||
case "night":
|
||||
baseTop = nightSkyTop;
|
||||
baseBottom = nightSkyBottom;
|
||||
ambient = nightAmbient;
|
||||
break;
|
||||
default: // day
|
||||
baseTop = daySkyTop;
|
||||
baseBottom = daySkyBottom;
|
||||
ambient = dayAmbient;
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply weather tint
|
||||
Color weatherTint = Color.white;
|
||||
switch (_currentWeather)
|
||||
{
|
||||
case "Cloudy": weatherTint = cloudyTint; break;
|
||||
case "Rainy": weatherTint = rainyTint; break;
|
||||
case "Stormy": weatherTint = stormyTint; break;
|
||||
case "Foggy": weatherTint = foggyTint; break;
|
||||
case "Hot": weatherTint = hotTint; break;
|
||||
}
|
||||
|
||||
_targetSkyTop = baseTop * weatherTint;
|
||||
_targetSkyBottom = baseBottom * weatherTint;
|
||||
_transitionProgress = 0f;
|
||||
|
||||
// Update lighting
|
||||
if (_mainLight != null)
|
||||
{
|
||||
_mainLight.color = ambient * weatherTint;
|
||||
_mainLight.intensity = _currentTimeOfDay == "night" ? 0.3f : 1f;
|
||||
}
|
||||
|
||||
RenderSettings.ambientLight = ambient * weatherTint * 0.8f;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
/// <summary>
|
||||
/// Force update the environment to specific conditions.
|
||||
/// </summary>
|
||||
public void SetEnvironment(string timeOfDay, string weather)
|
||||
{
|
||||
_currentTimeOfDay = timeOfDay;
|
||||
_currentWeather = weather;
|
||||
UpdateSkyColors();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6aa9102a04d7544619ec0187e065eda9
|
||||
95
unity-client/Assets/Scripts/Visual/SceneBootstrap.cs
Normal file
95
unity-client/Assets/Scripts/Visual/SceneBootstrap.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace TheIsland.Visual
|
||||
{
|
||||
/// <summary>
|
||||
/// Bootstraps the scene with all visual components.
|
||||
/// Attach this to an empty GameObject in your scene to automatically
|
||||
/// create the environment, weather effects, and other visual systems.
|
||||
/// </summary>
|
||||
public class SceneBootstrap : MonoBehaviour
|
||||
{
|
||||
[Header("Auto-Create Components")]
|
||||
[SerializeField] private bool createEnvironment = true;
|
||||
[SerializeField] private bool createWeatherEffects = true;
|
||||
|
||||
[Header("Camera Settings")]
|
||||
[SerializeField] private bool configureCamera = true;
|
||||
[SerializeField] private Vector3 cameraPosition = new Vector3(0, 3, -8);
|
||||
[SerializeField] private Vector3 cameraRotation = new Vector3(15, 0, 0);
|
||||
[SerializeField] private float cameraFieldOfView = 60f;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Configure camera
|
||||
if (configureCamera)
|
||||
{
|
||||
ConfigureMainCamera();
|
||||
}
|
||||
|
||||
// Create environment
|
||||
if (createEnvironment && EnvironmentManager.Instance == null)
|
||||
{
|
||||
CreateEnvironmentManager();
|
||||
}
|
||||
|
||||
// Create weather effects
|
||||
if (createWeatherEffects && WeatherEffects.Instance == null)
|
||||
{
|
||||
CreateWeatherEffects();
|
||||
}
|
||||
|
||||
Debug.Log("[SceneBootstrap] Visual systems initialized");
|
||||
}
|
||||
|
||||
private void ConfigureMainCamera()
|
||||
{
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera == null)
|
||||
{
|
||||
var camObj = new GameObject("Main Camera");
|
||||
mainCamera = camObj.AddComponent<Camera>();
|
||||
camObj.AddComponent<AudioListener>();
|
||||
camObj.tag = "MainCamera";
|
||||
}
|
||||
|
||||
mainCamera.transform.position = cameraPosition;
|
||||
mainCamera.transform.rotation = Quaternion.Euler(cameraRotation);
|
||||
mainCamera.fieldOfView = cameraFieldOfView;
|
||||
mainCamera.clearFlags = CameraClearFlags.SolidColor;
|
||||
mainCamera.backgroundColor = new Color(0.4f, 0.6f, 0.9f); // Fallback sky color
|
||||
|
||||
Debug.Log("[SceneBootstrap] Camera configured");
|
||||
}
|
||||
|
||||
private void CreateEnvironmentManager()
|
||||
{
|
||||
var envObj = new GameObject("EnvironmentManager");
|
||||
envObj.AddComponent<EnvironmentManager>();
|
||||
Debug.Log("[SceneBootstrap] EnvironmentManager created");
|
||||
}
|
||||
|
||||
private void CreateWeatherEffects()
|
||||
{
|
||||
var weatherObj = new GameObject("WeatherEffects");
|
||||
weatherObj.AddComponent<WeatherEffects>();
|
||||
Debug.Log("[SceneBootstrap] WeatherEffects created");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this to manually refresh all visual systems.
|
||||
/// </summary>
|
||||
public void RefreshVisuals()
|
||||
{
|
||||
if (EnvironmentManager.Instance != null)
|
||||
{
|
||||
EnvironmentManager.Instance.SetEnvironment("day", "Sunny");
|
||||
}
|
||||
|
||||
if (WeatherEffects.Instance != null)
|
||||
{
|
||||
WeatherEffects.Instance.SetWeather("Sunny");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35dc7c6201b284023b5ab113ffab8add
|
||||
473
unity-client/Assets/Scripts/Visual/WeatherEffects.cs
Normal file
473
unity-client/Assets/Scripts/Visual/WeatherEffects.cs
Normal file
@@ -0,0 +1,473 @@
|
||||
using UnityEngine;
|
||||
using TheIsland.Network;
|
||||
using TheIsland.Models;
|
||||
|
||||
namespace TheIsland.Visual
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates and manages weather particle effects.
|
||||
/// Responds to weather changes from the server.
|
||||
/// </summary>
|
||||
public class WeatherEffects : MonoBehaviour
|
||||
{
|
||||
#region Singleton
|
||||
private static WeatherEffects _instance;
|
||||
public static WeatherEffects Instance => _instance;
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
[Header("Rain Settings")]
|
||||
[SerializeField] private int rainParticleCount = 500;
|
||||
[SerializeField] private int stormParticleCount = 1000;
|
||||
[SerializeField] private Color rainColor = new Color(0.7f, 0.8f, 0.9f, 0.6f);
|
||||
|
||||
[Header("Sun Settings")]
|
||||
[SerializeField] private int sunRayCount = 50;
|
||||
[SerializeField] private Color sunRayColor = new Color(1f, 0.95f, 0.8f, 0.3f);
|
||||
|
||||
[Header("Fog Settings")]
|
||||
[SerializeField] private Color fogColor = new Color(0.85f, 0.85f, 0.9f, 0.5f);
|
||||
|
||||
[Header("Hot Weather Settings")]
|
||||
[SerializeField] private int heatWaveCount = 30;
|
||||
[SerializeField] private Color heatColor = new Color(1f, 0.9f, 0.7f, 0.2f);
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
private ParticleSystem _rainSystem;
|
||||
private ParticleSystem _sunRaySystem;
|
||||
private ParticleSystem _fogSystem;
|
||||
private ParticleSystem _heatSystem;
|
||||
private ParticleSystem _cloudSystem;
|
||||
|
||||
private string _currentWeather = "Sunny";
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
private void Awake()
|
||||
{
|
||||
if (_instance != null && _instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
_instance = this;
|
||||
|
||||
CreateAllEffects();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
var network = NetworkManager.Instance;
|
||||
if (network != null)
|
||||
{
|
||||
network.OnWeatherChange += HandleWeatherChange;
|
||||
network.OnTick += HandleTick;
|
||||
}
|
||||
|
||||
// Start with sunny weather
|
||||
SetWeather("Sunny");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
var network = NetworkManager.Instance;
|
||||
if (network != null)
|
||||
{
|
||||
network.OnWeatherChange -= HandleWeatherChange;
|
||||
network.OnTick -= HandleTick;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Effect Creation
|
||||
private void CreateAllEffects()
|
||||
{
|
||||
CreateRainEffect();
|
||||
CreateSunRayEffect();
|
||||
CreateFogEffect();
|
||||
CreateHeatEffect();
|
||||
CreateCloudEffect();
|
||||
}
|
||||
|
||||
private void CreateRainEffect()
|
||||
{
|
||||
var rainObj = new GameObject("RainEffect");
|
||||
rainObj.transform.SetParent(transform);
|
||||
rainObj.transform.position = new Vector3(0, 10, 5);
|
||||
|
||||
_rainSystem = rainObj.AddComponent<ParticleSystem>();
|
||||
var main = _rainSystem.main;
|
||||
main.maxParticles = stormParticleCount;
|
||||
main.startLifetime = 1.5f;
|
||||
main.startSpeed = 15f;
|
||||
main.startSize = 0.05f;
|
||||
main.startColor = rainColor;
|
||||
main.simulationSpace = ParticleSystemSimulationSpace.World;
|
||||
main.gravityModifier = 1.5f;
|
||||
|
||||
var emission = _rainSystem.emission;
|
||||
emission.rateOverTime = rainParticleCount;
|
||||
|
||||
var shape = _rainSystem.shape;
|
||||
shape.shapeType = ParticleSystemShapeType.Box;
|
||||
shape.scale = new Vector3(25, 0.1f, 15);
|
||||
|
||||
// Renderer settings
|
||||
var renderer = rainObj.GetComponent<ParticleSystemRenderer>();
|
||||
renderer.material = CreateParticleMaterial(rainColor);
|
||||
renderer.sortingOrder = 50;
|
||||
|
||||
// Start stopped
|
||||
_rainSystem.Stop();
|
||||
}
|
||||
|
||||
private void CreateSunRayEffect()
|
||||
{
|
||||
var sunObj = new GameObject("SunRayEffect");
|
||||
sunObj.transform.SetParent(transform);
|
||||
sunObj.transform.position = new Vector3(5, 8, 10);
|
||||
sunObj.transform.rotation = Quaternion.Euler(45, -30, 0);
|
||||
|
||||
_sunRaySystem = sunObj.AddComponent<ParticleSystem>();
|
||||
var main = _sunRaySystem.main;
|
||||
main.maxParticles = sunRayCount;
|
||||
main.startLifetime = 3f;
|
||||
main.startSpeed = 0.5f;
|
||||
main.startSize = new ParticleSystem.MinMaxCurve(0.5f, 2f);
|
||||
main.startColor = sunRayColor;
|
||||
main.simulationSpace = ParticleSystemSimulationSpace.World;
|
||||
|
||||
var emission = _sunRaySystem.emission;
|
||||
emission.rateOverTime = 10;
|
||||
|
||||
var shape = _sunRaySystem.shape;
|
||||
shape.shapeType = ParticleSystemShapeType.Cone;
|
||||
shape.angle = 15;
|
||||
shape.radius = 3;
|
||||
|
||||
var colorOverLifetime = _sunRaySystem.colorOverLifetime;
|
||||
colorOverLifetime.enabled = true;
|
||||
Gradient gradient = new Gradient();
|
||||
gradient.SetKeys(
|
||||
new GradientColorKey[] { new GradientColorKey(Color.white, 0), new GradientColorKey(Color.white, 1) },
|
||||
new GradientAlphaKey[] { new GradientAlphaKey(0, 0), new GradientAlphaKey(0.3f, 0.3f), new GradientAlphaKey(0, 1) }
|
||||
);
|
||||
colorOverLifetime.color = gradient;
|
||||
|
||||
var sizeOverLifetime = _sunRaySystem.sizeOverLifetime;
|
||||
sizeOverLifetime.enabled = true;
|
||||
sizeOverLifetime.size = new ParticleSystem.MinMaxCurve(1f, new AnimationCurve(
|
||||
new Keyframe(0, 0.5f), new Keyframe(0.5f, 1f), new Keyframe(1, 1.5f)));
|
||||
|
||||
var renderer = sunObj.GetComponent<ParticleSystemRenderer>();
|
||||
renderer.material = CreateSunRayMaterial();
|
||||
renderer.sortingOrder = 40;
|
||||
|
||||
_sunRaySystem.Stop();
|
||||
}
|
||||
|
||||
private void CreateFogEffect()
|
||||
{
|
||||
var fogObj = new GameObject("FogEffect");
|
||||
fogObj.transform.SetParent(transform);
|
||||
fogObj.transform.position = new Vector3(0, 1, 5);
|
||||
|
||||
_fogSystem = fogObj.AddComponent<ParticleSystem>();
|
||||
var main = _fogSystem.main;
|
||||
main.maxParticles = 100;
|
||||
main.startLifetime = 8f;
|
||||
main.startSpeed = 0.3f;
|
||||
main.startSize = new ParticleSystem.MinMaxCurve(3f, 6f);
|
||||
main.startColor = fogColor;
|
||||
main.simulationSpace = ParticleSystemSimulationSpace.World;
|
||||
|
||||
var emission = _fogSystem.emission;
|
||||
emission.rateOverTime = 5;
|
||||
|
||||
var shape = _fogSystem.shape;
|
||||
shape.shapeType = ParticleSystemShapeType.Box;
|
||||
shape.scale = new Vector3(30, 2, 15);
|
||||
|
||||
var velocityOverLifetime = _fogSystem.velocityOverLifetime;
|
||||
velocityOverLifetime.enabled = true;
|
||||
velocityOverLifetime.x = new ParticleSystem.MinMaxCurve(-0.2f, 0.2f);
|
||||
velocityOverLifetime.y = new ParticleSystem.MinMaxCurve(0.05f, 0.1f);
|
||||
|
||||
var colorOverLifetime = _fogSystem.colorOverLifetime;
|
||||
colorOverLifetime.enabled = true;
|
||||
Gradient gradient = new Gradient();
|
||||
gradient.SetKeys(
|
||||
new GradientColorKey[] { new GradientColorKey(Color.white, 0), new GradientColorKey(Color.white, 1) },
|
||||
new GradientAlphaKey[] { new GradientAlphaKey(0, 0), new GradientAlphaKey(0.5f, 0.3f), new GradientAlphaKey(0.5f, 0.7f), new GradientAlphaKey(0, 1) }
|
||||
);
|
||||
colorOverLifetime.color = gradient;
|
||||
|
||||
var renderer = fogObj.GetComponent<ParticleSystemRenderer>();
|
||||
renderer.material = CreateFogMaterial();
|
||||
renderer.sortingOrder = 30;
|
||||
|
||||
_fogSystem.Stop();
|
||||
}
|
||||
|
||||
private void CreateHeatEffect()
|
||||
{
|
||||
var heatObj = new GameObject("HeatEffect");
|
||||
heatObj.transform.SetParent(transform);
|
||||
heatObj.transform.position = new Vector3(0, 0, 5);
|
||||
|
||||
_heatSystem = heatObj.AddComponent<ParticleSystem>();
|
||||
var main = _heatSystem.main;
|
||||
main.maxParticles = heatWaveCount;
|
||||
main.startLifetime = 4f;
|
||||
main.startSpeed = 0.8f;
|
||||
main.startSize = new ParticleSystem.MinMaxCurve(1f, 3f);
|
||||
main.startColor = heatColor;
|
||||
main.simulationSpace = ParticleSystemSimulationSpace.World;
|
||||
|
||||
var emission = _heatSystem.emission;
|
||||
emission.rateOverTime = 8;
|
||||
|
||||
var shape = _heatSystem.shape;
|
||||
shape.shapeType = ParticleSystemShapeType.Box;
|
||||
shape.scale = new Vector3(20, 0.1f, 10);
|
||||
|
||||
var velocityOverLifetime = _heatSystem.velocityOverLifetime;
|
||||
velocityOverLifetime.enabled = true;
|
||||
velocityOverLifetime.y = 1f;
|
||||
velocityOverLifetime.x = new ParticleSystem.MinMaxCurve(-0.3f, 0.3f);
|
||||
|
||||
var colorOverLifetime = _heatSystem.colorOverLifetime;
|
||||
colorOverLifetime.enabled = true;
|
||||
Gradient gradient = new Gradient();
|
||||
gradient.SetKeys(
|
||||
new GradientColorKey[] { new GradientColorKey(heatColor, 0), new GradientColorKey(heatColor, 1) },
|
||||
new GradientAlphaKey[] { new GradientAlphaKey(0, 0), new GradientAlphaKey(0.2f, 0.3f), new GradientAlphaKey(0, 1) }
|
||||
);
|
||||
colorOverLifetime.color = gradient;
|
||||
|
||||
var renderer = heatObj.GetComponent<ParticleSystemRenderer>();
|
||||
renderer.material = CreateHeatMaterial();
|
||||
renderer.sortingOrder = 35;
|
||||
|
||||
_heatSystem.Stop();
|
||||
}
|
||||
|
||||
private void CreateCloudEffect()
|
||||
{
|
||||
var cloudObj = new GameObject("CloudEffect");
|
||||
cloudObj.transform.SetParent(transform);
|
||||
cloudObj.transform.position = new Vector3(0, 8, 15);
|
||||
|
||||
_cloudSystem = cloudObj.AddComponent<ParticleSystem>();
|
||||
var main = _cloudSystem.main;
|
||||
main.maxParticles = 30;
|
||||
main.startLifetime = 20f;
|
||||
main.startSpeed = 0.2f;
|
||||
main.startSize = new ParticleSystem.MinMaxCurve(5f, 10f);
|
||||
main.startColor = new Color(1, 1, 1, 0.7f);
|
||||
main.simulationSpace = ParticleSystemSimulationSpace.World;
|
||||
|
||||
var emission = _cloudSystem.emission;
|
||||
emission.rateOverTime = 1;
|
||||
|
||||
var shape = _cloudSystem.shape;
|
||||
shape.shapeType = ParticleSystemShapeType.Box;
|
||||
shape.scale = new Vector3(40, 2, 5);
|
||||
|
||||
var velocityOverLifetime = _cloudSystem.velocityOverLifetime;
|
||||
velocityOverLifetime.enabled = true;
|
||||
velocityOverLifetime.x = 0.3f;
|
||||
|
||||
var renderer = cloudObj.GetComponent<ParticleSystemRenderer>();
|
||||
renderer.material = CreateCloudMaterial();
|
||||
renderer.sortingOrder = 25;
|
||||
|
||||
_cloudSystem.Stop();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Material Creation
|
||||
private Material CreateParticleMaterial(Color color)
|
||||
{
|
||||
Material mat = new Material(Shader.Find("Particles/Standard Unlit"));
|
||||
mat.SetColor("_Color", color);
|
||||
mat.SetFloat("_Mode", 2); // Fade mode
|
||||
|
||||
// Create simple white texture
|
||||
Texture2D tex = new Texture2D(8, 8);
|
||||
for (int i = 0; i < 64; i++) tex.SetPixel(i % 8, i / 8, Color.white);
|
||||
tex.Apply();
|
||||
mat.mainTexture = tex;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private Material CreateSunRayMaterial()
|
||||
{
|
||||
Material mat = new Material(Shader.Find("Particles/Standard Unlit"));
|
||||
mat.SetColor("_Color", sunRayColor);
|
||||
mat.SetFloat("_Mode", 1); // Additive
|
||||
|
||||
// Create soft gradient texture
|
||||
Texture2D tex = new Texture2D(32, 32);
|
||||
Vector2 center = new Vector2(16, 16);
|
||||
for (int y = 0; y < 32; y++)
|
||||
{
|
||||
for (int x = 0; x < 32; x++)
|
||||
{
|
||||
float dist = Vector2.Distance(new Vector2(x, y), center) / 16f;
|
||||
float alpha = Mathf.Clamp01(1 - dist);
|
||||
tex.SetPixel(x, y, new Color(1, 1, 1, alpha * alpha));
|
||||
}
|
||||
}
|
||||
tex.Apply();
|
||||
mat.mainTexture = tex;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private Material CreateFogMaterial()
|
||||
{
|
||||
Material mat = new Material(Shader.Find("Particles/Standard Unlit"));
|
||||
mat.SetColor("_Color", fogColor);
|
||||
mat.SetFloat("_Mode", 2); // Fade
|
||||
|
||||
// Create soft cloud texture
|
||||
Texture2D tex = new Texture2D(64, 64);
|
||||
for (int y = 0; y < 64; y++)
|
||||
{
|
||||
for (int x = 0; x < 64; x++)
|
||||
{
|
||||
float noise = Mathf.PerlinNoise(x * 0.1f, y * 0.1f);
|
||||
float dist = Vector2.Distance(new Vector2(x, y), new Vector2(32, 32)) / 32f;
|
||||
float alpha = Mathf.Clamp01((1 - dist) * noise);
|
||||
tex.SetPixel(x, y, new Color(1, 1, 1, alpha));
|
||||
}
|
||||
}
|
||||
tex.Apply();
|
||||
mat.mainTexture = tex;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private Material CreateHeatMaterial()
|
||||
{
|
||||
Material mat = new Material(Shader.Find("Particles/Standard Unlit"));
|
||||
mat.SetColor("_Color", heatColor);
|
||||
mat.SetFloat("_Mode", 1); // Additive
|
||||
|
||||
// Create wavy heat texture
|
||||
Texture2D tex = new Texture2D(32, 64);
|
||||
for (int y = 0; y < 64; y++)
|
||||
{
|
||||
for (int x = 0; x < 32; x++)
|
||||
{
|
||||
float wave = Mathf.Sin((x + y * 0.3f) * 0.3f) * 0.5f + 0.5f;
|
||||
float fade = 1 - Mathf.Abs(x - 16) / 16f;
|
||||
float alpha = wave * fade * (1 - y / 64f);
|
||||
tex.SetPixel(x, y, new Color(1, 1, 1, alpha * 0.3f));
|
||||
}
|
||||
}
|
||||
tex.Apply();
|
||||
mat.mainTexture = tex;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private Material CreateCloudMaterial()
|
||||
{
|
||||
Material mat = new Material(Shader.Find("Particles/Standard Unlit"));
|
||||
mat.SetColor("_Color", Color.white);
|
||||
mat.SetFloat("_Mode", 2); // Fade
|
||||
|
||||
// Create fluffy cloud texture
|
||||
Texture2D tex = new Texture2D(64, 64);
|
||||
for (int y = 0; y < 64; y++)
|
||||
{
|
||||
for (int x = 0; x < 64; x++)
|
||||
{
|
||||
float noise1 = Mathf.PerlinNoise(x * 0.08f, y * 0.08f);
|
||||
float noise2 = Mathf.PerlinNoise(x * 0.15f + 100, y * 0.15f + 100) * 0.5f;
|
||||
float dist = Vector2.Distance(new Vector2(x, y), new Vector2(32, 32)) / 32f;
|
||||
float alpha = Mathf.Clamp01((noise1 + noise2) * (1 - dist * dist));
|
||||
tex.SetPixel(x, y, new Color(1, 1, 1, alpha * 0.8f));
|
||||
}
|
||||
}
|
||||
tex.Apply();
|
||||
tex.filterMode = FilterMode.Bilinear;
|
||||
mat.mainTexture = tex;
|
||||
|
||||
return mat;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Weather Control
|
||||
private void HandleWeatherChange(WeatherChangeData data)
|
||||
{
|
||||
SetWeather(data.new_weather);
|
||||
}
|
||||
|
||||
private void HandleTick(TickData data)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(data.weather) && data.weather != _currentWeather)
|
||||
{
|
||||
SetWeather(data.weather);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetWeather(string weather)
|
||||
{
|
||||
_currentWeather = weather;
|
||||
|
||||
// Stop all effects first
|
||||
_rainSystem?.Stop();
|
||||
_sunRaySystem?.Stop();
|
||||
_fogSystem?.Stop();
|
||||
_heatSystem?.Stop();
|
||||
_cloudSystem?.Stop();
|
||||
|
||||
// Enable appropriate effects
|
||||
switch (weather)
|
||||
{
|
||||
case "Sunny":
|
||||
_sunRaySystem?.Play();
|
||||
break;
|
||||
|
||||
case "Cloudy":
|
||||
_cloudSystem?.Play();
|
||||
break;
|
||||
|
||||
case "Rainy":
|
||||
_rainSystem?.Play();
|
||||
var rainMain = _rainSystem.main;
|
||||
var rainEmission = _rainSystem.emission;
|
||||
rainEmission.rateOverTime = rainParticleCount;
|
||||
_cloudSystem?.Play();
|
||||
break;
|
||||
|
||||
case "Stormy":
|
||||
_rainSystem?.Play();
|
||||
var stormMain = _rainSystem.main;
|
||||
var stormEmission = _rainSystem.emission;
|
||||
stormEmission.rateOverTime = stormParticleCount;
|
||||
stormMain.startSpeed = 20f;
|
||||
_cloudSystem?.Play();
|
||||
// Could add lightning flashes here
|
||||
break;
|
||||
|
||||
case "Foggy":
|
||||
_fogSystem?.Play();
|
||||
break;
|
||||
|
||||
case "Hot":
|
||||
_heatSystem?.Play();
|
||||
_sunRaySystem?.Play();
|
||||
break;
|
||||
}
|
||||
|
||||
Debug.Log($"[WeatherEffects] Weather set to: {weather}");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d309e5fa265df414cba2779d11a0ed3c
|
||||
Reference in New Issue
Block a user