Update README.md to include project description, developer documentation links, and license information.
CI / build-test (push) Successful in 1m6s
CI / build-test (push) Successful in 1m6s
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
using Friflo.Engine.ECS;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MrGameEng.Assets;
|
||||
using MrGameEng.Audio;
|
||||
using MrGameEng.Core;
|
||||
using MrGameEng.Graphics;
|
||||
using MrGameEng.Input;
|
||||
|
||||
namespace MrGameEng.Sample.Scenes;
|
||||
|
||||
/// <summary>
|
||||
/// Интерактивная демо-сцена: ассеты через сгенерированные хендлы, анимация, слои
|
||||
/// (мир + Y-sort + screen-space HUD), камера с зумом/поворотом, ввод, звук и музыка.
|
||||
/// WASD — игрок, колесо — зум, Q/E — поворот, Space — звук, P — пауза, M — музыка, Tab — стресс-сцена.
|
||||
/// </summary>
|
||||
public sealed class MainScene : Scene
|
||||
{
|
||||
private const int DecorCount = 1500;
|
||||
private static readonly RectF WorldBounds = new(-2000f, -2000f, 4000f, 4000f);
|
||||
|
||||
protected override void OnLoad()
|
||||
{
|
||||
var assets = Context.Services.GetOrDefault<AssetManager>() ?? Context.UseAssets();
|
||||
var audio = Context.Services.GetOrDefault<AudioManager>() ?? Context.UseAudio();
|
||||
var input = this.UseInput();
|
||||
var actions = SampleInput.CreateActions(input);
|
||||
this.UseSpriteAnimation();
|
||||
|
||||
var renderer = this.UseRenderer2D(new Renderer2DOptions { VirtualResolution = new Point(1280, 720) });
|
||||
SampleLayers.EnsureRegistered(renderer);
|
||||
|
||||
var playerTexture = assets.Load(GameAssets.Textures.Player);
|
||||
var shapesTexture = assets.Load(GameAssets.Textures.Shapes);
|
||||
var beep = assets.Load(GameAssets.Sounds.Beep);
|
||||
|
||||
var shapeRegions = new[]
|
||||
{
|
||||
new Texture2DRegion(shapesTexture, new Rectangle(0, 0, 32, 32)),
|
||||
new Texture2DRegion(shapesTexture, new Rectangle(32, 0, 32, 32)),
|
||||
new Texture2DRegion(shapesTexture, new Rectangle(0, 32, 32, 32)),
|
||||
new Texture2DRegion(shapesTexture, new Rectangle(32, 32, 32, 32)),
|
||||
};
|
||||
|
||||
// Декорации по всему миру: уезжаешь камерой — попадают под culling (см. заголовок окна).
|
||||
var random = new Random(42);
|
||||
for (var i = 0; i < DecorCount; i++)
|
||||
{
|
||||
var sprite = new Sprite(shapeRegions[random.Next(shapeRegions.Length)]);
|
||||
sprite.CenterOrigin();
|
||||
sprite.Color = Color.White * 0.35f;
|
||||
Store.CreateEntity(
|
||||
new Transform2D(
|
||||
new Vector2(
|
||||
random.NextSingle() * WorldBounds.Width + WorldBounds.Left,
|
||||
random.NextSingle() * WorldBounds.Height + WorldBounds.Top),
|
||||
rotation: random.NextSingle() * MathF.Tau,
|
||||
scale: new Vector2(0.5f + random.NextSingle())),
|
||||
sprite);
|
||||
}
|
||||
|
||||
// Бродячие "существа" на Y-sort слое: кто ниже на экране — тот ближе.
|
||||
for (var i = 0; i < 200; i++)
|
||||
{
|
||||
var sprite = new Sprite(shapeRegions[random.Next(shapeRegions.Length)], SampleLayers.Actors);
|
||||
sprite.CenterOrigin();
|
||||
var angle = random.NextSingle() * MathF.Tau;
|
||||
Store.CreateEntity(
|
||||
new Transform2D(new Vector2(random.Next(-600, 600), random.Next(-400, 400))),
|
||||
sprite,
|
||||
new Velocity { Value = new Vector2(MathF.Cos(angle), MathF.Sin(angle)) * (40f + random.Next(80)) });
|
||||
}
|
||||
|
||||
// Игрок: анимированный спрайт (2 кадра из атласа player.png), Y-sort слой.
|
||||
var playerSprite = new Sprite(new Texture2DRegion(playerTexture, new Rectangle(0, 0, 32, 32)), SampleLayers.Actors);
|
||||
playerSprite.CenterOrigin();
|
||||
var blink = new SpriteAnimationClip(
|
||||
[
|
||||
new Texture2DRegion(playerTexture, new Rectangle(0, 0, 32, 32)),
|
||||
new Texture2DRegion(playerTexture, new Rectangle(32, 0, 32, 32)),
|
||||
], framesPerSecond: 3f);
|
||||
|
||||
var player = Store.CreateEntity(
|
||||
new Transform2D(Vector2.Zero, scale: new Vector2(2f)),
|
||||
playerSprite,
|
||||
new SpriteAnimator(blink));
|
||||
|
||||
var camera = Store.CreateEntity(new Camera(Vector2.Zero, zoom: 1f, bounds: WorldBounds));
|
||||
|
||||
// HUD: золотой квадрат в углу на screen-space слое — не двигается с камерой.
|
||||
var hud = new Sprite(shapeRegions[3], SampleLayers.Ui);
|
||||
Store.CreateEntity(new Transform2D(new Vector2(16f, 16f)), hud);
|
||||
|
||||
UpdateSystems.Add(new PlayerControlSystem(player, actions, audio, beep));
|
||||
UpdateSystems.Add(new BounceSystem(WorldBounds));
|
||||
UpdateSystems.Add(new CameraControlSystem(camera, player, input));
|
||||
UpdateSystems.Add(new SceneHotkeysSystem(Context, actions, () => new StressScene()));
|
||||
UpdateSystems.Add(new TitleStatsSystem(Context, renderer, "Main"));
|
||||
|
||||
var music = audio.Music;
|
||||
if (!music.IsPlaying)
|
||||
{
|
||||
music.Volume = 0.4f;
|
||||
music.Play(assets.Load(GameAssets.Music.Theme));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using Friflo.Engine.ECS;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MrGameEng.Assets;
|
||||
using MrGameEng.Core;
|
||||
using MrGameEng.Graphics;
|
||||
using MrGameEng.Input;
|
||||
|
||||
namespace MrGameEng.Sample.Scenes;
|
||||
|
||||
/// <summary>
|
||||
/// Стресс-сцена: 100 000 спрайтов скачут в мире 4000×4000. Колесо — зум (чем дальше,
|
||||
/// тем больше спрайтов в кадре), Tab — обратно в основную сцену. Цель: 60 FPS.
|
||||
/// </summary>
|
||||
public sealed class StressScene : Scene
|
||||
{
|
||||
private const int SpriteCount = 100_000;
|
||||
private static readonly RectF WorldBounds = new(-2000f, -2000f, 4000f, 4000f);
|
||||
|
||||
protected override void OnLoad()
|
||||
{
|
||||
var assets = Context.Services.Get<AssetManager>();
|
||||
var input = this.UseInput();
|
||||
var actions = SampleInput.CreateActions(input);
|
||||
|
||||
var renderer = this.UseRenderer2D();
|
||||
SampleLayers.EnsureRegistered(renderer);
|
||||
|
||||
var shapesTexture = assets.Load(GameAssets.Textures.Shapes);
|
||||
var regions = new[]
|
||||
{
|
||||
new Texture2DRegion(shapesTexture, new Rectangle(0, 0, 32, 32)),
|
||||
new Texture2DRegion(shapesTexture, new Rectangle(32, 0, 32, 32)),
|
||||
new Texture2DRegion(shapesTexture, new Rectangle(0, 32, 32, 32)),
|
||||
new Texture2DRegion(shapesTexture, new Rectangle(32, 32, 32, 32)),
|
||||
};
|
||||
|
||||
var random = new Random(7);
|
||||
for (var i = 0; i < SpriteCount; i++)
|
||||
{
|
||||
var sprite = new Sprite(regions[random.Next(regions.Length)]);
|
||||
sprite.CenterOrigin();
|
||||
var angle = random.NextSingle() * MathF.Tau;
|
||||
Store.CreateEntity(
|
||||
new Transform2D(
|
||||
new Vector2(
|
||||
random.NextSingle() * WorldBounds.Width + WorldBounds.Left,
|
||||
random.NextSingle() * WorldBounds.Height + WorldBounds.Top),
|
||||
scale: new Vector2(0.4f + random.NextSingle() * 0.6f)),
|
||||
sprite,
|
||||
new Velocity { Value = new Vector2(MathF.Cos(angle), MathF.Sin(angle)) * (30f + random.Next(150)) });
|
||||
}
|
||||
|
||||
var camera = Store.CreateEntity(new Camera(Vector2.Zero, zoom: 0.3f));
|
||||
|
||||
UpdateSystems.Add(new BounceSystem(WorldBounds));
|
||||
UpdateSystems.Add(new StressCameraSystem(camera, input));
|
||||
UpdateSystems.Add(new SceneHotkeysSystem(Context, actions, () => new MainScene()));
|
||||
UpdateSystems.Add(new TitleStatsSystem(Context, renderer, $"Stress {SpriteCount:N0}"));
|
||||
}
|
||||
|
||||
/// <summary>Только зум колесом — чтобы регулировать число видимых спрайтов.</summary>
|
||||
private sealed class StressCameraSystem(Entity cameraEntity, InputManager input)
|
||||
: Friflo.Engine.ECS.Systems.BaseSystem
|
||||
{
|
||||
protected override void OnUpdateGroup()
|
||||
{
|
||||
ref var camera = ref cameraEntity.GetComponent<Camera>();
|
||||
camera.Zoom = Math.Clamp(camera.Zoom * (1f + input.WheelDelta * 0.001f), 0.05f, 5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user