diff --git a/backend/app/engine.py b/backend/app/engine.py index e9817a8..de05917 100644 --- a/backend/app/engine.py +++ b/backend/app/engine.py @@ -215,6 +215,35 @@ class GameEngine: # ========================================================================= # Day/Night cycle (Phase 2) # ========================================================================= + async def _process_weather(self) -> None: + """Process weather transitions.""" + with get_db_session() as db: + world = db.query(WorldState).first() + if not world: + return + + world.weather_duration += 1 + + # Should we transition? + if world.weather_duration >= random.randint(WEATHER_MIN_DURATION, WEATHER_MAX_DURATION): + old_weather = world.weather + transitions = WEATHER_TRANSITIONS.get(old_weather, {"Sunny": 1.0}) + + # Biased random choice + choices = list(transitions.keys()) + weights = list(transitions.values()) + new_weather = random.choices(choices, weights=weights, k=1)[0] + + if new_weather != old_weather: + world.weather = new_weather + world.weather_duration = 0 + + await self._broadcast_event(EventType.WEATHER_CHANGE, { + "old_weather": old_weather, + "new_weather": new_weather, + "message": f"The weather is changing to {new_weather}." + }) + async def _advance_time(self) -> Optional[dict]: """Advance time and return phase change info if applicable.""" with get_db_session() as db: diff --git a/unity-client/Assets/Scripts/AgentUI.cs b/unity-client/Assets/Scripts/AgentUI.cs index d4beae9..62d5302 100644 --- a/unity-client/Assets/Scripts/AgentUI.cs +++ b/unity-client/Assets/Scripts/AgentUI.cs @@ -151,13 +151,47 @@ namespace TheIsland.UI rect.sizeDelta = size; rect.anchoredPosition = Vector2.zero; - // Semi-transparent background + // Semi-transparent glass background var bg = panel.AddComponent(); - bg.color = new Color(0, 0, 0, 0.5f); + bg.color = new Color(0.1f, 0.12f, 0.18f, 0.6f); + + // Add border highlight + var border = new GameObject("Border"); + border.transform.SetParent(panel.transform); + var bRect = border.AddComponent(); + bRect.anchorMin = Vector2.zero; + bRect.anchorMax = Vector2.one; + bRect.offsetMin = new Vector2(-1, -1); + bRect.offsetMax = new Vector2(1, 1); + var bImg = border.AddComponent(); + bImg.color = new Color(1f, 1f, 1f, 0.1f); + border.transform.SetAsFirstSibling(); return panel; } + private float _currentHp; + private float _currentEnergy; + + private void Update() + { + if (_hpBarFill != null && _currentData != null) + { + _currentHp = Mathf.Lerp(_currentHp, _currentData.hp, Time.deltaTime * 5f); + float hpPercent = _currentHp / 100f; + _hpBarFill.rectTransform.anchorMax = new Vector2(hpPercent, 1); + _hpBarFill.color = Color.Lerp(hpLowColor, hpHighColor, hpPercent); + } + + if (_energyBarFill != null && _currentData != null) + { + _currentEnergy = Mathf.Lerp(_currentEnergy, _currentData.energy, Time.deltaTime * 5f); + float energyPercent = _currentEnergy / 100f; + _energyBarFill.rectTransform.anchorMax = new Vector2(energyPercent, 1); + _energyBarFill.color = Color.Lerp(energyLowColor, energyHighColor, energyPercent); + } + } + private TextMeshProUGUI CreateText(GameObject parent, string name, string text, float fontSize, Color color, FontStyles style = FontStyles.Normal) { @@ -296,25 +330,11 @@ namespace TheIsland.UI { _currentData = data; - // Update HP - float hpPercent = data.hp / 100f; - if (_hpBarFill != null) - { - _hpBarFill.rectTransform.anchorMax = new Vector2(hpPercent, 1); - _hpBarFill.color = Color.Lerp(hpLowColor, hpHighColor, hpPercent); - } if (_hpText != null) { _hpText.text = $"HP: {data.hp}"; } - // Update Energy - float energyPercent = data.energy / 100f; - if (_energyBarFill != null) - { - _energyBarFill.rectTransform.anchorMax = new Vector2(energyPercent, 1); - _energyBarFill.color = Color.Lerp(energyLowColor, energyHighColor, energyPercent); - } if (_energyText != null) { _energyText.text = $"Energy: {data.energy}"; diff --git a/unity-client/Assets/Scripts/AgentVisual.cs b/unity-client/Assets/Scripts/AgentVisual.cs index bfc3507..cff860e 100644 --- a/unity-client/Assets/Scripts/AgentVisual.cs +++ b/unity-client/Assets/Scripts/AgentVisual.cs @@ -82,6 +82,14 @@ namespace TheIsland.Visual private Vector3 _targetPosition; private bool _isMoving; private float _moveSpeed = 3f; + + // UI Smoothing (Phase 19) + private float _currentHpPercent; + private float _currentEnergyPercent; + private float _currentMoodPercent; + private float _targetHpPercent; + private float _targetEnergyPercent; + private float _targetMoodPercent; #endregion #region Properties @@ -157,6 +165,35 @@ namespace TheIsland.Visual pos.y = 1f + _bobOffset; _spriteRenderer.transform.localPosition = pos; } + + // Phase 19: Smooth UI Bar Transitions + UpdateSmoothBars(); + } + + private void UpdateSmoothBars() + { + float lerpSpeed = 5f * Time.deltaTime; + + if (_hpBarFill != null) + { + _currentHpPercent = Mathf.Lerp(_currentHpPercent, _targetHpPercent, lerpSpeed); + _hpBarFill.rectTransform.anchorMax = new Vector2(_currentHpPercent, 1); + _hpBarFill.color = Color.Lerp(hpLowColor, hpHighColor, _currentHpPercent); + } + + if (_energyBarFill != null) + { + _currentEnergyPercent = Mathf.Lerp(_currentEnergyPercent, _targetEnergyPercent, lerpSpeed); + _energyBarFill.rectTransform.anchorMax = new Vector2(_currentEnergyPercent, 1); + _energyBarFill.color = Color.Lerp(energyLowColor, energyHighColor, _currentEnergyPercent); + } + + if (_moodBarFill != null) + { + _currentMoodPercent = Mathf.Lerp(_currentMoodPercent, _targetMoodPercent, lerpSpeed); + _moodBarFill.rectTransform.anchorMax = new Vector2(_currentMoodPercent, 1); + _moodBarFill.color = GetMoodColor(_currentData?.mood_state ?? "neutral"); + } } // Trigger a jump animation (to be called by events) @@ -218,7 +255,10 @@ namespace TheIsland.Visual _currentData = data; gameObject.name = $"Agent_{data.id}_{data.name}"; - // Apply unique color based on agent ID + // Loading premium assets (Phase 19) + TryLoadPremiumSprite(data.id); + + // Apply unique color based on agent ID (as fallback/tint) ApplyAgentColor(data.id); // Set UI text @@ -229,6 +269,45 @@ namespace TheIsland.Visual Debug.Log($"[AgentVisual] Initialized: {data.name}"); } + private void TryLoadPremiumSprite(int id) + { + // Load the collection texture from Assets + // Note: In a real build, we'd use Resources.Load or Addressables. + // For this environment, we'll try to find it in the path or use a static reference. + // Since we can't easily use Resources.Load at runtime for arbitrary paths, + // we'll implement a simple runtime texture loader if needed, or assume it's assigned to a manager. + + // For now, let's assume the texture is assigned or loaded. + // I'll add a static reference to the collection texture in NetworkManager or AgentVisual. + + if (characterSprite != null) return; // Already has a sprite + + StartCoroutine(LoadSpriteCoroutine(id)); + } + + private IEnumerator LoadSpriteCoroutine(int id) + { + // This is a simplified runtime loader for the demonstration + string path = Application.dataPath + "/Sprites/Characters.png"; + if (!System.IO.File.Exists(path)) yield break; + + byte[] fileData = System.IO.File.ReadAllBytes(path); + Texture2D tex = new Texture2D(2, 2); + tex.LoadImage(fileData); + + // Slice the 1x3 collection (3 characters in a row) + int charIndex = id % 3; + float charWidth = tex.width / 3f; + Rect rect = new Rect(charIndex * charWidth, 0, charWidth, tex.height); + + characterSprite = Sprite.Create(tex, rect, new Vector2(0.5f, 0.5f), 100f); + if (_spriteRenderer != null) + { + _spriteRenderer.sprite = characterSprite; + _spriteRenderer.color = Color.white; + } + } + private void ApplyAgentColor(int agentId) { // Generate unique color per agent @@ -702,28 +781,23 @@ namespace TheIsland.Visual rect.anchoredPosition = Vector2.zero; var bg = panel.AddComponent(); - bg.sprite = CreateRoundedRectSprite(32, 32, 8); + bg.sprite = CreateRoundedRectSprite(32, 32, 12); bg.type = Image.Type.Sliced; - bg.color = new Color(0.1f, 0.12f, 0.18f, 0.6f); // Lower alpha for glass effect + bg.color = new Color(0.05f, 0.08f, 0.15f, 0.45f); // Darker, more transparent glass - // Add subtle border + // Add inner glow border (Phase 19) var borderObj = new GameObject("Border"); borderObj.transform.SetParent(panel.transform); - borderObj.transform.localPosition = Vector3.zero; - borderObj.transform.localRotation = Quaternion.identity; - borderObj.transform.localScale = Vector3.one; - var borderRect = borderObj.AddComponent(); borderRect.anchorMin = Vector2.zero; borderRect.anchorMax = Vector2.one; - borderRect.offsetMin = new Vector2(-2, -2); - borderRect.offsetMax = new Vector2(2, 2); - borderRect.SetAsFirstSibling(); - + borderRect.offsetMin = new Vector2(1, 1); + borderRect.offsetMax = new Vector2(-1, -1); + var borderImg = borderObj.AddComponent(); - borderImg.sprite = CreateRoundedRectSprite(32, 32, 8); + borderImg.sprite = CreateRoundedRectSprite(32, 32, 12); borderImg.type = Image.Type.Sliced; - borderImg.color = new Color(0.3f, 0.35f, 0.45f, 0.5f); + borderImg.color = new Color(1f, 1f, 1f, 0.15f); // Subtle highlight return panel; } @@ -918,38 +992,21 @@ namespace TheIsland.Visual { _currentData = data; - // Update HP bar - float hpPercent = data.hp / 100f; - if (_hpBarFill != null) - { - _hpBarFill.rectTransform.anchorMax = new Vector2(hpPercent, 1); - _hpBarFill.color = Color.Lerp(hpLowColor, hpHighColor, hpPercent); - } + // Set targets for smooth lerping (Phase 19) + _targetHpPercent = data.hp / 100f; + _targetEnergyPercent = data.energy / 100f; + _targetMoodPercent = data.mood / 100f; + if (_hpText != null) { _hpText.text = $"HP: {data.hp}"; } - // Update Energy bar - float energyPercent = data.energy / 100f; - if (_energyBarFill != null) - { - _energyBarFill.rectTransform.anchorMax = new Vector2(energyPercent, 1); - _energyBarFill.color = Color.Lerp(energyLowColor, energyHighColor, energyPercent); - } if (_energyText != null) { _energyText.text = $"Energy: {data.energy}"; } - // Update Mood bar - float moodPercent = data.mood / 100f; - if (_moodBarFill != null) - { - _moodBarFill.rectTransform.anchorMax = new Vector2(moodPercent, 1); - _moodBarFill.color = GetMoodColor(data.mood_state); - } - // Check for mood change (Visual Expression) if (_moodState != data.mood_state) { diff --git a/unity-client/Assets/Scripts/Visual/EnvironmentManager.cs b/unity-client/Assets/Scripts/Visual/EnvironmentManager.cs index 423cc70..8fa3fa5 100644 --- a/unity-client/Assets/Scripts/Visual/EnvironmentManager.cs +++ b/unity-client/Assets/Scripts/Visual/EnvironmentManager.cs @@ -86,6 +86,7 @@ namespace TheIsland.Visual } _instance = this; + LoadEnvironmentTexture(); _mainCamera = Camera.main; CreateEnvironment(); } @@ -103,6 +104,12 @@ namespace TheIsland.Visual // Set initial sky UpdateSkyColors(); + + // Phase 19: Add Visual Effects Manager + if (FindObjectOfType() == null) + { + new GameObject("VisualEffectsManager").AddComponent(); + } } private void Update() @@ -116,11 +123,58 @@ namespace TheIsland.Visual UpdateSkyMaterial(); } + // Phase 19: Cinematic Lighting + AnimateLighting(); + // Animate environment (Water & Trees) AnimateEnvironment(); AnimateClouds(); } + private void AnimateLighting() + { + if (_mainLight == null) return; + + // Simple 120s cycle for demonstration (30s per phase) + float cycleDuration = 120f; + float t = (Time.time % cycleDuration) / cycleDuration; + + // t: 0=Dawn, 0.25=Noon, 0.5=Dusk, 0.75=Midnight + float intensity = 1f; + Color lightColor = Color.white; + + if (t < 0.2f) // Dawn + { + float p = t / 0.2f; + intensity = Mathf.Lerp(0.5f, 1.2f, p); + lightColor = Color.Lerp(new Color(1f, 0.6f, 0.4f), Color.white, p); + } + else if (t < 0.5f) // Day + { + intensity = 1.2f; + lightColor = Color.white; + } + else if (t < 0.7f) // Dusk + { + float p = (t - 0.5f) / 0.2f; + intensity = Mathf.Lerp(1.2f, 0.4f, p); + lightColor = Color.Lerp(Color.white, new Color(1f, 0.4f, 0.2f), p); + } + else // Night + { + float p = (t - 0.7f) / 0.3f; + intensity = Mathf.Lerp(0.4f, 0.2f, p); + lightColor = new Color(0.4f, 0.5f, 1f); // Moonlight + } + + _mainLight.intensity = intensity; + _mainLight.color = lightColor; + + // Rotate sun + float sunAngle = t * 360f - 90f; + _mainLight.transform.rotation = Quaternion.Euler(sunAngle, -30f, 0); + } + private void OnDestroy() { var network = NetworkManager.Instance; @@ -338,11 +392,14 @@ namespace TheIsland.Visual for (int x = 0; x < size; x++) { float t = (float)y / size; - Color baseColor = Color.Lerp(waterShallowColor, waterDeepColor, t); + // Add some noise to the base color + float n = Mathf.PerlinNoise(x * 0.05f, y * 0.05f) * 0.1f; + Color baseColor = Color.Lerp(waterShallowColor, waterDeepColor, t + n); - // 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); + // Add caustic-like highlights + float wave1 = Mathf.Sin(x * 0.15f + y * 0.05f + Time.time * 0.2f) * 0.5f + 0.5f; + float wave2 = Mathf.Cos(x * 0.08f - y * 0.12f + Time.time * 0.15f) * 0.5f + 0.5f; + baseColor = Color.Lerp(baseColor, Color.white, (wave1 * wave2) * 0.15f); tex.SetPixel(x, y, baseColor); } @@ -356,8 +413,11 @@ namespace TheIsland.Visual 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); + float offset = Time.time * waveSpeed * 0.05f; + _waterMaterial.mainTextureOffset = new Vector2(offset, offset * 0.3f); + + // Periodically update texture for dynamic caustic effect (expensive but looks premium) + // Or just use the original UV scrolling if performance is an issue. } private void CreateLighting() @@ -414,8 +474,27 @@ namespace TheIsland.Visual trunkSprite.transform.localScale = new Vector3(scale * 0.5f, scale, 1); } + private Texture2D _envTexture; + + private void LoadEnvironmentTexture() + { + string path = Application.dataPath + "/Sprites/Environment.png"; + if (System.IO.File.Exists(path)) + { + byte[] data = System.IO.File.ReadAllBytes(path); + _envTexture = new Texture2D(2, 2); + _envTexture.LoadImage(data); + } + } + private Sprite CreateTreeSprite() { + if (_envTexture != null) + { + // Slice palm tree (Assuming it's in the top-left quadrant of the collection) + return Sprite.Create(_envTexture, new Rect(0, _envTexture.height / 2f, _envTexture.width / 2f, _envTexture.height / 2f), new Vector2(0.5f, 0f), 100f); + } + int width = 64; int height = 128; Texture2D tex = new Texture2D(width, height); @@ -530,6 +609,12 @@ namespace TheIsland.Visual private Sprite CreateRockSprite() { + if (_envTexture != null) + { + // Slice rock from Environment.png (Assuming bottom-right quadrant) + return Sprite.Create(_envTexture, new Rect(_envTexture.width / 2f, 0, _envTexture.width / 2f, _envTexture.height / 2f), new Vector2(0.5f, 0.5f), 100f); + } + int size = 32; Texture2D tex = new Texture2D(size, size); @@ -575,7 +660,16 @@ namespace TheIsland.Visual private void HandleWeatherChange(WeatherChangeData data) { _currentWeather = data.new_weather; - UpdateSkyColors(); + Debug.Log($"[EnvironmentManager] Weather changed to: {_currentWeather}"); + + // Notify VFX manager + if (VisualEffectsManager.Instance != null) + { + VisualEffectsManager.Instance.SetWeather(_currentWeather); + } + + // Adjust lighting based on weather + UpdateSkyColors(); // This will use the new weather in its logic } private void HandleTick(TickData data) diff --git a/unity-client/Assets/Scripts/Visual/VisualEffectsManager.cs b/unity-client/Assets/Scripts/Visual/VisualEffectsManager.cs new file mode 100644 index 0000000..fa3bd17 --- /dev/null +++ b/unity-client/Assets/Scripts/Visual/VisualEffectsManager.cs @@ -0,0 +1,243 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace TheIsland.Visual +{ + /// + /// Manages environmental visual effects like fireflies, stars, and weather (Phase 19 & 20). + /// + public class VisualEffectsManager : MonoBehaviour + { + public static VisualEffectsManager Instance { get; private set; } + + [Header("Fireflies")] + [SerializeField] private int fireflyCount = 20; + [SerializeField] private Color fireflyColor = new Color(1f, 1f, 0.5f, 0.8f); + [SerializeField] private Vector2 islandBounds = new Vector2(10, 10); + + [Header("Stars & Meteors")] + [SerializeField] private int starCount = 50; + [SerializeField] private Color starColor = Color.white; + [SerializeField] private float meteorInterval = 15f; + + [Header("Weather VFX")] + [SerializeField] private int rainDensity = 120; + private List _rainDrops = new List(); + private string _activeWeather = "Sunny"; + private Sprite _rainSprite; + + private List _fireflies = new List(); + private List _stars = new List(); + private float _meteorTimer; + + private void Awake() + { + Instance = this; + _rainSprite = CreateRainSprite(); + CreateFireflies(); + CreateStars(); + CreateRain(); + } + + public void SetWeather(string weather) + { + _activeWeather = weather; + Debug.Log($"[VFXManager] Setting weather visuals for: {weather}"); + + // Toggle systems + bool isRainy = weather == "Rainy" || weather == "Stormy"; + foreach (var drop in _rainDrops) drop.SetActive(isRainy); + + if (weather == "Stormy") StartCoroutine(LightningLoop()); + } + + private void Update() + { + UpdateAtmosphere(); + UpdateRain(); + } + + private void UpdateAtmosphere() + { + float t = (Time.time % 120f) / 120f; + bool isNight = t > 0.65f || t < 0.15f; + float starAlpha = isNight ? (t > 0.75f || t < 0.05f ? 1f : 0.5f) : 0f; + + foreach (var star in _stars) + { + var sprite = star.GetComponent(); + sprite.color = new Color(starColor.r, starColor.g, starColor.b, starAlpha * 0.8f); + } + + if (isNight) + { + _meteorTimer += Time.deltaTime; + if (_meteorTimer >= meteorInterval) + { + _meteorTimer = 0; + if (Random.value < 0.3f) StartCoroutine(ShootMeteor()); + } + } + } + + private void CreateFireflies() + { + for (int i = 0; i < fireflyCount; i++) + { + var firefly = new GameObject($"Firefly_{i}"); + firefly.transform.SetParent(transform); + float x = Random.Range(-islandBounds.x, islandBounds.x); + float z = Random.Range(-islandBounds.y, islandBounds.y); + firefly.transform.position = new Vector3(x, Random.Range(1f, 3f), z); + + var sprite = firefly.AddComponent(); + sprite.sprite = CreateGlowSprite(); + sprite.color = fireflyColor; + sprite.sortingOrder = 50; + firefly.AddComponent(); + _fireflies.Add(firefly); + StartCoroutine(AnimateFirefly(firefly)); + } + } + + private void CreateStars() + { + for (int i = 0; i < starCount; i++) + { + var star = new GameObject($"Star_{i}"); + star.transform.SetParent(transform); + float x = Random.Range(-25f, 25f); + float y = Random.Range(10f, 15f); + float z = Random.Range(-25f, 25f); + star.transform.position = new Vector3(x, y, z); + + var sprite = star.AddComponent(); + sprite.sprite = CreateGlowSprite(); + sprite.color = new Color(1, 1, 1, 0); + sprite.sortingOrder = -50; + star.AddComponent().ConfigureForUI(); + _stars.Add(star); + } + } + + private void CreateRain() + { + for (int i = 0; i < rainDensity; i++) + { + var drop = new GameObject($"Rain_{i}"); + drop.transform.SetParent(transform); + var sprite = drop.AddComponent(); + sprite.sprite = _rainSprite; + sprite.color = new Color(0.8f, 0.9f, 1f, 0.6f); + sprite.sortingOrder = 100; + ResetRainDrop(drop); + drop.SetActive(false); + _rainDrops.Add(drop); + } + } + + private void ResetRainDrop(GameObject drop) + { + float x = Random.Range(-20f, 20f); + float y = Random.Range(10f, 15f); + float z = Random.Range(-20f, 20f); + drop.transform.position = new Vector3(x, y, z); + } + + private void UpdateRain() + { + if (_activeWeather != "Rainy" && _activeWeather != "Stormy") return; + float speed = _activeWeather == "Stormy" ? 40f : 20f; + foreach (var drop in _rainDrops) + { + drop.transform.position += Vector3.down * speed * Time.deltaTime; + drop.transform.position += Vector3.left * speed * 0.2f * Time.deltaTime; + if (drop.transform.position.y < -2f) ResetRainDrop(drop); + } + } + + private IEnumerator LightningLoop() + { + while (_activeWeather == "Stormy") + { + yield return new WaitForSeconds(Random.Range(3f, 10f)); + var flash = new GameObject("LightningFlash"); + var img = flash.AddComponent(); + img.sprite = CreateGlowSprite(); + img.color = new Color(1, 1, 1, 0.8f); + flash.transform.position = new Vector3(0, 10, 0); + flash.transform.localScale = new Vector3(100, 100, 1); + yield return new WaitForSeconds(0.05f); + img.color = new Color(1, 1, 1, 0.3f); + yield return new WaitForSeconds(0.05f); + Destroy(flash); + } + } + + private IEnumerator ShootMeteor() + { + var meteor = new GameObject("Meteor"); + meteor.transform.SetParent(transform); + Vector3 startPos = new Vector3(Random.Range(-20, 20), 15, Random.Range(-20, 20)); + Vector3 direction = new Vector3(Random.Range(-10, 10), -5, Random.Range(-10, 10)).normalized; + meteor.transform.position = startPos; + var sprite = meteor.AddComponent(); + sprite.sprite = CreateGlowSprite(); + sprite.color = Color.white; + sprite.transform.localScale = new Vector3(0.5f, 0.1f, 1f); + float duration = 1.0f; + float elapsed = 0; + while (elapsed < duration) + { + elapsed += Time.deltaTime; + meteor.transform.position += direction * 30f * Time.deltaTime; + sprite.color = new Color(1, 1, 1, 1f - (elapsed / duration)); + yield return null; + } + Destroy(meteor); + } + + private Sprite CreateRainSprite() + { + Texture2D tex = new Texture2D(2, 8); + for (int y = 0; y < 8; y++) + for (int x = 0; x < 2; x++) tex.SetPixel(x, y, Color.white); + tex.Apply(); + return Sprite.Create(tex, new Rect(0, 0, 2, 8), new Vector2(0.5f, 0.5f)); + } + + private Sprite CreateGlowSprite() + { + int size = 32; + Texture2D tex = new Texture2D(size, size); + tex.filterMode = FilterMode.Bilinear; + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) { + float dist = Vector2.Distance(new Vector2(x, y), new Vector2(size/2f, size/2f)) / (size/2f); + float alpha = Mathf.Exp(-dist * 4f); + tex.SetPixel(x, y, new Color(1, 1, 1, alpha)); + } + tex.Apply(); + return Sprite.Create(tex, new Rect(0, 0, size, size), new Vector2(0.5f, 0.5f)); + } + + private IEnumerator AnimateFirefly(GameObject firefly) + { + Vector3 startPos = firefly.transform.position; + float seed = Random.value * 100f; + while (true) + { + float t = Time.time + seed; + float dx = (Mathf.PerlinNoise(t * 0.2f, 0) - 0.5f) * 2f; + float dy = (Mathf.PerlinNoise(0, t * 0.2f) - 0.5f) * 1f; + float dz = (Mathf.PerlinNoise(t * 0.1f, t * 0.1f) - 0.5f) * 2f; + firefly.transform.position = startPos + new Vector3(dx, dy, dz); + var sprite = firefly.GetComponent(); + float pulse = Mathf.PingPong(t, 1f) * 0.5f + 0.5f; + sprite.color = new Color(fireflyColor.r, fireflyColor.g, fireflyColor.b, pulse * fireflyColor.a); + yield return null; + } + } + } +} diff --git a/unity-client/Assets/Scripts/Visual/VisualEffectsManager.cs.meta b/unity-client/Assets/Scripts/Visual/VisualEffectsManager.cs.meta new file mode 100644 index 0000000..1bbae44 --- /dev/null +++ b/unity-client/Assets/Scripts/Visual/VisualEffectsManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 47dcba28f3d78421ca5bdf74a4365bed \ No newline at end of file diff --git a/unity-client/Assets/Sprites/Characters.png b/unity-client/Assets/Sprites/Characters.png new file mode 100644 index 0000000..6f3f1ce Binary files /dev/null and b/unity-client/Assets/Sprites/Characters.png differ diff --git a/unity-client/Assets/Sprites/Characters.png.meta b/unity-client/Assets/Sprites/Characters.png.meta new file mode 100644 index 0000000..f96bec0 --- /dev/null +++ b/unity-client/Assets/Sprites/Characters.png.meta @@ -0,0 +1,143 @@ +fileFormatVersion: 2 +guid: 2976a69bb8c114901b263e613a7b6006 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: iOS + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity-client/Assets/Sprites/Environment.png b/unity-client/Assets/Sprites/Environment.png new file mode 100644 index 0000000..cccaa35 Binary files /dev/null and b/unity-client/Assets/Sprites/Environment.png differ diff --git a/unity-client/Assets/Sprites/Environment.png.meta b/unity-client/Assets/Sprites/Environment.png.meta new file mode 100644 index 0000000..695b8dd --- /dev/null +++ b/unity-client/Assets/Sprites/Environment.png.meta @@ -0,0 +1,143 @@ +fileFormatVersion: 2 +guid: 8ea1b9777c2a54dea9d8f4fb46a00d03 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: iOS + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: