using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; namespace AlienAttack.MonoGame.Things.Stars; internal sealed class StarLayer { private readonly Random _rng; private readonly int _count; //private readonly float _speed; private readonly Vector2 _velocity; // pixels/sec private readonly float _minSize, _maxSize; private readonly float _minAlpha, _maxAlpha; private int _w, _h; private readonly Star[] _stars; public StarLayer(Random rng, int count, Vector2 velocityPxPerSec, float minSize, float maxSize, float minAlpha, float maxAlpha) { _rng = rng; _count = count; //_speed = speedPxPerSec; _velocity = velocityPxPerSec; _minSize = minSize; _maxSize = maxSize; _minAlpha = minAlpha; _maxAlpha = maxAlpha; _stars = new Star[_count]; } public void Initialize(int screenWidth, int screenHeight) { _w = screenWidth; _h = screenHeight; for (int i = 0; i < _stars.Length; i++) _stars[i] = CreateStar(randomY: true); } public void OnResize(int screenWidth, int screenHeight) { // Keep existing stars but clamp/wrap them into the new bounds. _w = screenWidth; _h = screenHeight; for (int i = 0; i < _stars.Length; i++) { var s = _stars[i]; s.Pos.X = Wrap(s.Pos.X, _w); s.Pos.Y = Wrap(s.Pos.Y, _h); _stars[i] = s; } } public void Update(float dt) { for (int i = 0; i < _stars.Length; i++) { var s = _stars[i]; // Move down (positive Y). Add slight horizontal drift if you want: // s.Pos.X += s.DriftX * dt; //s.Pos.Y += _speed * dt; s.Pos += _velocity * dt; // Wrap X continuously so diagonal drift never runs out of stars if (s.Pos.X < -s.Size) s.Pos.X = _w + s.Size; if (s.Pos.X > _w + s.Size) s.Pos.X = -s.Size; if (s.Pos.Y >= _h + s.Size) { // Respawn at top; keep it entering from above s = CreateStar(randomY: false); s.Pos.Y = -s.Size; } // Optional subtle twinkle: s.TwinkleT += dt * s.TwinkleSpeed; _stars[i] = s; } } public void Draw(SpriteBatch sb, Texture2D pixel) { for (int i = 0; i < _stars.Length; i++) { var s = _stars[i]; float twinkle = 1f; if (s.TwinkleSpeed > 0f) twinkle = 0.85f + 0.15f * (float)Math.Sin(s.TwinkleT); var color = Color.White * (s.Alpha * twinkle); // Draw as a scaled 1x1 pixel (super fast) sb.Draw(pixel, s.Pos, null, color, 0f, Vector2.Zero, s.Size, SpriteEffects.None, 0f); } } private Star CreateStar(bool randomY) { float x = (float)_rng.NextDouble() * _w; float y = randomY ? (float)_rng.NextDouble() * _h : 0f; float size = Lerp(_minSize, _maxSize, (float)_rng.NextDouble()); float alpha = Lerp(_minAlpha, _maxAlpha, (float)_rng.NextDouble()); // Twinkle: keep it more common on “near” layers by setting a nonzero speed range float twinkleSpeed = Lerp(0f, 6f, (float)_rng.NextDouble()) * 0.4f; // tweak or set 0 for none return new Star { Pos = new Vector2(x, y), Size = size, Alpha = alpha, TwinkleSpeed = twinkleSpeed, TwinkleT = (float)_rng.NextDouble() * MathF.PI * 2f }; } private static float Wrap(float v, float max) { if (max <= 0) return 0; v %= max; if (v < 0) v += max; return v; } private static float Lerp(float a, float b, float t) => a + (b - a) * t; }