This commit is contained in:
2025-01-04 22:44:43 +03:00
parent ac00eb18a9
commit 27f6b11f8f
55 changed files with 2547 additions and 702 deletions

View File

@@ -10,8 +10,4 @@
<ProjectReference Include="..\Engine\Engine.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="src\Scene\Play\" />
</ItemGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 47 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
# Blender 4.2.3 LTS
# www.blender.org
o Plane
v -14.316629 -4.684887 0.000000
v 14.316629 -4.684887 0.000000
v -14.316629 4.684887 0.000000
v 14.316629 4.684887 0.000000
v 6.822107 -4.684887 0.000000
v 6.822107 4.684887 0.000000
v 14.316629 33.607601 0.000000
v 6.822107 33.607601 0.000000
v 14.316629 25.280016 0.000000
v 6.822107 25.280016 0.000000
v -6.847844 33.607601 0.000000
v -6.847844 25.280016 0.000000
v -25.254845 24.277735 0.000000
v -18.173466 20.114567 0.000000
v -26.336294 7.837210 0.000000
v -19.225933 10.395360 0.000000
v -14.316629 -4.684887 3.000000
v 14.316629 -4.684887 3.000000
v 14.316629 4.684887 3.000000
v 6.822107 -4.684887 3.000000
v 14.316629 33.607601 3.000000
v 6.822107 33.607601 3.000000
v 14.316629 25.280016 3.000000
v -6.847844 33.607601 3.000000
v -25.254845 24.277735 3.000000
v -26.336294 7.837210 3.000000
v -14.316629 4.684887 3.000000
v 6.822107 4.684887 3.000000
v 6.822107 25.280016 3.000000
v -6.847844 25.280016 3.000000
v -18.173466 20.114567 3.000000
v -19.225933 10.395360 3.000000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn 0.7214 0.6925 -0.0000
vn 0.9978 -0.0656 -0.0000
vn 0.4521 -0.8920 -0.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vn -0.7583 -0.6519 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.4150 0.9098 -0.0000
vn -0.9942 0.1077 -0.0000
vt 1.000000 0.000000
vt 0.738258 1.000000
vt 0.738258 0.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 1.000000
s 0
f 2/1/1 6/2/1 5/3/1
f 5/3/1 3/4/1 1/5/1
f 9/6/1 8/2/1 10/2/1
f 4/6/1 10/2/1 6/2/1
f 8/2/1 12/2/1 10/2/1
f 11/2/1 14/2/1 12/2/1
f 14/2/1 15/2/1 16/2/1
f 3/4/1 15/2/1 1/5/1
f 2/1/2 19/6/2 4/6/2
f 15/2/3 17/5/3 1/5/3
f 13/2/4 26/2/4 15/2/4
f 4/6/2 23/6/2 9/6/2
f 11/2/5 25/2/5 13/2/5
f 7/6/6 22/2/6 8/2/6
f 8/2/6 24/2/6 11/2/6
f 1/5/7 20/3/7 5/3/7
f 5/3/7 18/1/7 2/1/7
f 9/6/2 21/6/2 7/6/2
f 12/2/7 29/2/7 10/2/7
f 3/4/8 32/2/8 16/2/8
f 10/2/9 28/2/9 6/2/9
f 14/2/10 30/2/10 12/2/10
f 6/2/6 27/4/6 3/4/6
f 16/2/11 31/2/11 14/2/11
f 2/1/1 4/6/1 6/2/1
f 5/3/1 6/2/1 3/4/1
f 9/6/1 7/6/1 8/2/1
f 4/6/1 9/6/1 10/2/1
f 8/2/1 11/2/1 12/2/1
f 11/2/1 13/2/1 14/2/1
f 14/2/1 13/2/1 15/2/1
f 3/4/1 16/2/1 15/2/1
f 2/1/2 18/1/2 19/6/2
f 15/2/3 26/2/3 17/5/3
f 13/2/4 25/2/4 26/2/4
f 4/6/2 19/6/2 23/6/2
f 11/2/5 24/2/5 25/2/5
f 7/6/6 21/6/6 22/2/6
f 8/2/6 22/2/6 24/2/6
f 1/5/7 17/5/7 20/3/7
f 5/3/7 20/3/7 18/1/7
f 9/6/2 23/6/2 21/6/2
f 12/2/7 30/2/7 29/2/7
f 3/4/8 27/4/8 32/2/8
f 10/2/9 29/2/9 28/2/9
f 14/2/10 31/2/10 30/2/10
f 6/2/6 28/2/6 27/4/6
f 16/2/11 32/2/11 31/2/11

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,33 +0,0 @@
namespace DoomDeathmatch.Component;
public class HealthComponent : Engine.Scene.Component.Component
{
public float MaxHealth { get; set; } = 100;
public float Health { get; private set; } = 100;
public event Action<HealthComponent>? HealthChanged;
public event Action<HealthComponent>? Died;
public void TakeDamage(float parDamage)
{
Health -= parDamage;
if (Health <= 0)
{
Died?.Invoke(this);
}
else
{
HealthChanged?.Invoke(this);
}
}
public void Heal(float parHeal)
{
Health += parHeal;
if (Health > MaxHealth)
{
Health = MaxHealth;
}
HealthChanged?.Invoke(this);
}
}

View File

@@ -0,0 +1,31 @@
using DoomDeathmatch.Component.MVC.Model;
using DoomDeathmatch.Component.MVC.View;
namespace DoomDeathmatch.Component.MVC.Controller;
public class HealthController : Engine.Scene.Component.Component
{
private readonly HealthModel _healthModel = new(100);
private HealthView? _healthView;
public override void Awake()
{
_healthView = GameObject.GetComponent<HealthView>();
if (_healthView != null)
{
_healthView.UpdateView(_healthModel);
_healthModel.HealthChanged += _healthView.UpdateView;
}
}
public void TakeDamage(float parDamage)
{
_healthModel.Health -= parDamage;
}
public void Heal(float parHeal)
{
_healthModel.Health += parHeal;
}
}

View File

@@ -0,0 +1,45 @@
using DoomDeathmatch.Component.MVC.Model;
using Engine.Input;
namespace DoomDeathmatch.Component.MVC.Controller;
public class PlayerController : Engine.Scene.Component.Component
{
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
private HealthController _healthController = null!;
private WeaponController _weaponController = null!;
private ScoreController _scoreController = null!;
public override void Awake()
{
_healthController = GameObject.GetComponent<HealthController>()!;
_weaponController = GameObject.GetComponent<WeaponController>()!;
_scoreController = GameObject.GetComponent<ScoreController>()!;
ArgumentNullException.ThrowIfNull(_healthController);
ArgumentNullException.ThrowIfNull(_weaponController);
ArgumentNullException.ThrowIfNull(_scoreController);
}
public override void Update(double parDeltaTime)
{
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.C))
_weaponController.AddWeapon(WeaponData.Shotgun);
for (var i = 0; i < 10; i++)
{
if (KeyboardButtonCode.D1 + i > KeyboardButtonCode.D0)
break;
if (!_inputHandler.IsKeyJustPressed(KeyboardButtonCode.D1 + i))
continue;
_weaponController.SelectWeapon(i);
break;
}
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Space))
_weaponController.TryShoot();
}
}

View File

@@ -0,0 +1,22 @@
using DoomDeathmatch.Component.MVC.Model;
using DoomDeathmatch.Component.MVC.View;
namespace DoomDeathmatch.Component.MVC.Controller;
public class ScoreController : Engine.Scene.Component.Component
{
private readonly ScoreModel _scoreModel = new();
private ScoreView _scoreView = null!;
public override void Awake()
{
_scoreView = GameObject.GetComponent<ScoreView>()!;
_scoreView.UpdateView(_scoreModel);
_scoreModel.ScoreChanged += _scoreView.UpdateView;
}
public void AddScore(int parScore)
{
_scoreModel.Score += parScore;
}
}

View File

@@ -0,0 +1,74 @@
using DoomDeathmatch.Component.MVC.Model;
using DoomDeathmatch.Component.MVC.View;
namespace DoomDeathmatch.Component.MVC.Controller;
public class WeaponController : Engine.Scene.Component.Component
{
public event Action<WeaponData>? OnWeaponShot;
private readonly WeaponModel _weaponModel = new();
private WeaponView _weaponView = null!;
public override void Awake()
{
_weaponView = GameObject.GetComponent<WeaponView>()!;
_weaponView.UpdateView(_weaponModel.SelectedWeapon);
_weaponModel.OnWeaponSelected += WeaponSelected;
WeaponSelected(null, _weaponModel.SelectedWeapon);
}
public bool TryShoot()
{
if (_weaponModel.SelectedWeapon.Ammo <= 0)
return false;
_weaponModel.SelectedWeapon.Ammo--;
OnWeaponShot?.Invoke(_weaponModel.SelectedWeapon);
return true;
}
public void Reload()
{
_weaponModel.SelectedWeapon.Ammo = _weaponModel.SelectedWeapon.MaxAmmo;
}
public void AddWeapon(WeaponData parWeaponData)
{
if (_weaponModel.Weapons.Contains(parWeaponData))
return;
_weaponModel.Weapons.Add(parWeaponData);
}
public void RemoveWeapon(int parIndex)
{
if (parIndex <= 0 || parIndex >= _weaponModel.Weapons.Count)
return;
var newSelectedIndex = parIndex >= _weaponModel.SelectedWeaponIndex ? _weaponModel.SelectedWeaponIndex : 0;
_weaponModel.SelectedWeaponIndex = newSelectedIndex;
_weaponModel.Weapons.RemoveAt(parIndex);
}
public void SelectWeapon(int parIndex)
{
if (parIndex >= _weaponModel.Weapons.Count)
return;
_weaponModel.SelectedWeaponIndex = parIndex;
}
private void WeaponSelected(WeaponData? parOldWeapon, WeaponData parNewWeapon)
{
if (parOldWeapon != null)
parOldWeapon.OnAmmoChanged -= _weaponView.UpdateAmmoView;
parNewWeapon.OnAmmoChanged += _weaponView.UpdateAmmoView;
_weaponView.UpdateView(parNewWeapon);
}
}

View File

@@ -0,0 +1,35 @@
namespace DoomDeathmatch.Component.MVC.Model;
public class HealthModel
{
public float MaxHealth
{
get => _maxHealth;
set
{
_maxHealth = Math.Max(value, 1);
HealthChanged?.Invoke(this);
}
}
public float Health
{
get => _health;
set
{
_health = Math.Clamp(value, 0, MaxHealth);
HealthChanged?.Invoke(this);
}
}
public event Action<HealthModel>? HealthChanged;
private float _health;
private float _maxHealth;
public HealthModel(float parMaxHealth)
{
MaxHealth = parMaxHealth;
Health = parMaxHealth;
}
}

View File

@@ -0,0 +1,18 @@
namespace DoomDeathmatch.Component.MVC.Model;
public class ScoreModel
{
public event Action<ScoreModel>? ScoreChanged;
public int Score
{
get => _score;
set
{
_score = Math.Max(value, 0);
ScoreChanged?.Invoke(this);
}
}
private int _score;
}

View File

@@ -0,0 +1,54 @@
namespace DoomDeathmatch.Component.MVC.Model;
public class WeaponData
{
public static WeaponData Pistol =>
new(30) { Id = "pistol", Name = "Пистолет", Texture = "texture/pistol.png", Damage = 10 };
public static WeaponData Shotgun =>
new(10) { Id = "shotgun", Name = "Дробовик", Texture = "texture/shotgun.png", Damage = 50 };
public string Id { get; private init; } = "";
public string Name { get; private init; } = "";
public string Texture { get; private init; } = "";
public int Damage { get; private init; }
public int MaxAmmo { get; }
public int Ammo
{
get => _ammo;
set
{
if (value < 0)
value = 0;
if (value > MaxAmmo)
value = MaxAmmo;
if (_ammo == value)
return;
_ammo = value;
OnAmmoChanged?.Invoke(this);
}
}
public event Action<WeaponData>? OnAmmoChanged;
private int _ammo;
private WeaponData(int parMaxAmmo)
{
MaxAmmo = parMaxAmmo;
Ammo = MaxAmmo;
}
public override bool Equals(object? parObj)
{
return parObj is WeaponData weaponModel && Id == weaponModel.Id;
}
public override int GetHashCode()
{
return HashCode.Combine(Id);
}
}

View File

@@ -0,0 +1,29 @@
namespace DoomDeathmatch.Component.MVC.Model;
public class WeaponModel
{
public event Action<WeaponData, WeaponData>? OnWeaponSelected;
public IList<WeaponData> Weapons => _weapons;
public WeaponData SelectedWeapon => _weapons[_selectedWeaponIndex];
public int SelectedWeaponIndex
{
get => _selectedWeaponIndex;
set
{
value = Math.Clamp(value, 0, _weapons.Count - 1);
if (_selectedWeaponIndex == value)
return;
var oldSelectedWeapon = SelectedWeapon;
_selectedWeaponIndex = value;
OnWeaponSelected?.Invoke(oldSelectedWeapon, SelectedWeapon);
}
}
private readonly List<WeaponData> _weapons = [WeaponData.Pistol];
private int _selectedWeaponIndex = 0;
}

View File

@@ -0,0 +1,23 @@
using DoomDeathmatch.Component.MVC.Model;
using Engine.Scene.Component.BuiltIn.Renderer;
namespace DoomDeathmatch.Component.MVC.View;
public class HealthView : Engine.Scene.Component.Component
{
private readonly TextRenderer _healthTextRenderer;
public HealthView(TextRenderer parHealthTextRenderer)
{
_healthTextRenderer = parHealthTextRenderer;
}
public void UpdateView(HealthModel parHealthModel)
{
var percentage = parHealthModel.Health / parHealthModel.MaxHealth * 100;
if (parHealthModel.Health != 0)
percentage = Math.Max(1, percentage);
_healthTextRenderer.Text = $"Здоровье: {percentage:000}";
}
}

View File

@@ -0,0 +1,19 @@
using DoomDeathmatch.Component.MVC.Model;
using Engine.Scene.Component.BuiltIn.Renderer;
namespace DoomDeathmatch.Component.MVC.View;
public class ScoreView : Engine.Scene.Component.Component
{
private readonly TextRenderer _scoreTextRenderer;
public ScoreView(TextRenderer parScoreTextRenderer)
{
_scoreTextRenderer = parScoreTextRenderer;
}
public void UpdateView(ScoreModel parScoreModel)
{
_scoreTextRenderer.Text = $"Счет: {parScoreModel.Score:00000}";
}
}

View File

@@ -0,0 +1,31 @@
using DoomDeathmatch.Component.MVC.Model;
using Engine.Graphics.Texture;
using Engine.Scene.Component.BuiltIn.Renderer;
namespace DoomDeathmatch.Component.MVC.View;
public class WeaponView : Engine.Scene.Component.Component
{
private readonly TextRenderer _weaponName;
private readonly TextRenderer _weaponAmmo;
private readonly Box2DRenderer _weaponSprite;
public WeaponView(TextRenderer parWeaponName, TextRenderer parWeaponAmmo, Box2DRenderer parWeaponSprite)
{
_weaponName = parWeaponName;
_weaponAmmo = parWeaponAmmo;
_weaponSprite = parWeaponSprite;
}
public void UpdateView(WeaponData parWeaponData)
{
UpdateAmmoView(parWeaponData);
_weaponName.Text = $"Оружие: {parWeaponData.Name}";
_weaponSprite.Texture = Engine.Engine.Instance.AssetResourceManager.Load<Texture>(parWeaponData.Texture);
}
public void UpdateAmmoView(WeaponData parWeaponData)
{
_weaponAmmo.Text = $"Патроны: {parWeaponData.Ammo}/{parWeaponData.MaxAmmo}";
}
}

View File

@@ -67,7 +67,7 @@ public class SelectorComponent : Engine.Scene.Component.Component
var scale = transformMatrix.ExtractScale();
GameObject.Transform.Translation = translation;
GameObject.Transform.Translation.X -= scale.X / 2 + 0.05f;
GameObject.Transform.Translation.X -= scale.X / 2;
GameObject.Transform.Size.Y = scale.Y;
}

View File

@@ -1,20 +0,0 @@
using Engine.Input;
using Engine.Scene.Component.BuiltIn;
namespace DoomDeathmatch.Component.UI;
public class TestComponent : Engine.Scene.Component.Component
{
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
public Camera? Camera { get; set; }
public override void Update(double parDeltaTime)
{
if (Camera == null)
{
return;
}
GameObject.Transform.Size.Xy = 2 * Camera.ScreenToWorld(_inputHandler.MousePosition).Xy;
}
}

View File

@@ -27,9 +27,7 @@ public class TextAlignComponent : Engine.Scene.Component.Component
_cachedText = _textRenderer.Text;
var font = _textRenderer.Font;
var size = font.Measure(_textRenderer.Text);
var scale = GameObject.Transform.FullTransformMatrix.ExtractScale();
var offset = GetOffset(size) + new Vector2(0, font.Metadata.Metrics.LineHeight - font.Metadata.Metrics.Ascender) / 2;
offset *= scale.Xy;
var offset = GetOffset(size) + new Vector2(0, _textRenderer.Font.Metadata.Metrics.Descender / 4);
GameObject.Transform.Translation.Xy = offset;
}

View File

@@ -8,7 +8,7 @@ public class UiComponent : Engine.Scene.Component.Component
public UiContainerComponent? Container { get; set; }
public Anchor Anchor { get; set; } = Anchor.Center;
public Vector2 Offset { get; set; } = Vector2.Zero;
public Vector2 Center { get; set; } = Vector2.Zero;
public Anchor Center { get; set; } = Anchor.Center;
public event Action<UiComponent>? OnClick;
public event Action<UiComponent>? OnMouseOver;
@@ -22,17 +22,17 @@ public class UiComponent : Engine.Scene.Component.Component
return;
}
GameObject.Transform.Translation.Xy = GetAnchorPosition(Container.GameObject.Transform.Size.Xy) + Offset;
var size = GameObject.Transform.Size * GameObject.Transform.Scale;
GameObject.Transform.Translation.Xy = GetAnchorPosition(Container.GameObject.Transform.Size.Xy, Anchor) + Offset -
GetAnchorPosition(size.Xy, Center);
var transformMatrix = GameObject.Transform.FullTransformMatrix;
var actualSize = transformMatrix.ExtractScale();
var translation = transformMatrix.ExtractTranslation();
var scale = transformMatrix.ExtractScale();
var relativeMousePosition = Container.MousePosition.Xy -
(translation.Xy);
var relativeMousePosition = Container.MousePosition.Xy - translation.Xy;
var objectSize = scale.Xy;
if (Math.Abs(relativeMousePosition.X) <= objectSize.X / 2 && Math.Abs(relativeMousePosition.Y) <= objectSize.Y / 2)
if (Math.Abs(relativeMousePosition.X) <= actualSize.X / 2 && Math.Abs(relativeMousePosition.Y) <= actualSize.Y / 2)
{
OnMouseOver?.Invoke(this);
@@ -48,14 +48,14 @@ public class UiComponent : Engine.Scene.Component.Component
OnClick?.Invoke(this);
}
private Vector2 GetAnchorPosition(Vector2 parSize)
private static Vector2 GetAnchorPosition(Vector2 parSize, Anchor parAnchor)
{
return parSize * GetAnchorRatio();
return parSize * GetAnchorRatio(parAnchor);
}
private Vector2 GetAnchorRatio()
private static Vector2 GetAnchorRatio(Anchor parAnchor)
{
return Anchor switch
return parAnchor switch
{
Anchor.TopLeft => new Vector2(-0.5f, 0.5f),
Anchor.TopCenter => new Vector2(0, 0.5f),
@@ -66,7 +66,7 @@ public class UiComponent : Engine.Scene.Component.Component
Anchor.BottomLeft => new Vector2(-0.5f, -0.5f),
Anchor.BottomCenter => new Vector2(0, -0.5f),
Anchor.BottomRight => new Vector2(0.5f, -0.5f),
_ => throw new ArgumentOutOfRangeException(nameof(Anchor), Anchor, null)
_ => throw new ArgumentOutOfRangeException(nameof(parAnchor), parAnchor, null)
};
}
}

View File

@@ -1,7 +1,7 @@
using Engine.Scene.Component.BuiltIn;
using OpenTK.Mathematics;
namespace DoomDeathmatch.Component;
namespace DoomDeathmatch.Component.Util;
public class BillboardComponent : Engine.Scene.Component.Component
{

View File

@@ -1,7 +1,7 @@
using Engine.Input;
using OpenTK.Mathematics;
namespace DoomDeathmatch.Component;
namespace DoomDeathmatch.Component.Util;
public class ControllerComponent : Engine.Scene.Component.Component
{

View File

@@ -0,0 +1,25 @@
using Engine.Scene.Component.BuiltIn;
using OpenTK.Mathematics;
namespace DoomDeathmatch.Component.Util;
public class CopySizeComponent : Engine.Scene.Component.Component
{
public Vector3 Coefficient { get; set; } = Vector3.One;
public Transform? Target { get; set; }
public override void Update(double parDeltaTime)
{
if (Target == null)
return;
if (Coefficient.X != 0)
GameObject.Transform.Size.X = Target.Size.X * Coefficient.X;
if (Coefficient.Y != 0)
GameObject.Transform.Size.Y = Target.Size.Y * Coefficient.Y;
if (Coefficient.Z != 0)
GameObject.Transform.Size.Z = Target.Size.Z * Coefficient.Z;
}
}

View File

@@ -1,6 +1,6 @@
using OpenTK.Mathematics;
namespace DoomDeathmatch.Component;
namespace DoomDeathmatch.Component.Util;
public class DragComponent : Engine.Scene.Component.Component
{

View File

@@ -1,6 +1,6 @@
using OpenTK.Mathematics;
namespace DoomDeathmatch.Component;
namespace DoomDeathmatch.Component.Util;
public class GravityComponent : Engine.Scene.Component.Component
{

View File

@@ -1,6 +1,6 @@
using OpenTK.Mathematics;
namespace DoomDeathmatch.Component;
namespace DoomDeathmatch.Component.Util;
public class RigidbodyComponent : Engine.Scene.Component.Component
{

View File

@@ -1,7 +1,7 @@
using Engine.Input;
using OpenTK.Mathematics;
namespace DoomDeathmatch.Component;
namespace DoomDeathmatch.Component.Util;
public class RotateComponent : Engine.Scene.Component.Component
{

View File

@@ -21,70 +21,6 @@ public static class DoomDeathmatch
{
public static void Initialize(Engine.Engine parEngine)
{
parEngine.SceneManager.TransitionTo(MainScene.Create(parEngine));
parEngine.SceneManager.TransitionTo(() => MainScene.Create(parEngine));
}
// private static Scene MainScene(Engine.Engine parEngine)
// {
// var playerObject = new GameObject();
// playerObject.AddComponent<RigidbodyComponent>();
// playerObject.AddComponent(new ControllerComponent { Speed = 5f });
// playerObject.AddComponent(new DragComponent { Drag = 5f, Coefficient = new Vector3(1, 1, 0) });
// playerObject.AddComponent(new TestComponent());
//
// var cameraObject = new GameObject();
// cameraObject.Transform.Translation.Z = 2;
// cameraObject.AddComponent<PerspectiveCamera>();
//
// var testObject = new GameObject { Transform = { Translation = new Vector3(0, 6, 0), Rotation = Quaternion.FromAxisAngle(Vector3.UnitX, (float)Math.PI / 2) } };
// testObject.AddComponent(new Box2DRenderer { Color = new Vector4(1, 0, 0, 1) });
//
// var mesh = parEngine.AssetResourceManager.Load<Mesh>("model/untitled.obj");
// var texture = parEngine.AssetResourceManager.Load<Texture>("TestImage.png");
// var font = parEngine.AssetResourceManager.Load<Font>("font/test");
//
// var box2dRenderer = new GameObject
// {
// Transform = { Scale = new Vector3(1), Rotation = Quaternion.FromAxisAngle(Vector3.UnitX, (float)Math.PI / 2) }
// };
// // box2dRenderer.AddComponent(new MeshRenderer { Mesh = mesh, Albedo = texture });
// box2dRenderer.AddComponent(new TextRenderer { Font = font, Text = "A", RenderLayer = RenderLayer.HUD });
// // box2dRenderer.AddComponent(new BillboardComponent { Target = cameraObject.Transform });
//
// var xAxis = new GameObject();
// xAxis.Transform.Translation.X = 5;
// xAxis.Transform.Scale.X = 10;
// xAxis.AddComponent(new Box2DRenderer { Color = new Vector4(1, 0, 0, 1) });
//
// var yAxis = new GameObject();
// yAxis.Transform.Translation.Y = 5;
// yAxis.Transform.Scale.X = 10;
// yAxis.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitZ, (float)Math.PI / 2);
// yAxis.AddComponent(new Box2DRenderer { Color = new Vector4(0, 1, 0, 1) });
//
// var zAxis = new GameObject();
// zAxis.Transform.Translation.Z = 5;
// zAxis.Transform.Scale.Y = 10;
// zAxis.Transform.Scale.X = 10;
// zAxis.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitX, (float)Math.PI / 2);
// zAxis.AddComponent(new Box2DRenderer
// {
// Color = new Vector4(0, 0, 1, 1), Texture = parEngine.AssetResourceManager.Load<Texture>("test.jpeg")
// });
//
// var scene = new Scene();
// scene.Add(cameraObject);
// scene.AddChild(cameraObject, testObject);
// scene.Add(playerObject);
// scene.SetChild(playerObject, cameraObject);
//
// scene.Add(box2dRenderer);
// // scene.AddChild(box2dRenderer, testChild);
//
// scene.Add(xAxis);
// // scene.Add(yAxis);
// // scene.Add(zAxis);
//
// return scene;
// }
}

View File

@@ -0,0 +1,35 @@
using Engine.Scene;
using Engine.Scene.Component.BuiltIn;
namespace DoomDeathmatch;
public static class GameObjectUtil
{
public static GameObject CreateGameObject(Engine.Scene.Scene parScene,
List<Engine.Scene.Component.Component> parComponents)
{
var gameObject = new GameObject();
foreach (var component in parComponents)
{
gameObject.AddComponent(component);
}
parScene.Add(gameObject);
return gameObject;
}
public static GameObject CreateGameObject(Engine.Scene.Scene parScene, Transform parTransform,
List<Engine.Scene.Component.Component> parComponents)
{
var gameObject = new GameObject(parTransform);
foreach (var component in parComponents)
{
gameObject.AddComponent(component);
}
parScene.Add(gameObject);
return gameObject;
}
}

View File

@@ -16,36 +16,25 @@ public static class LeadersScene
var (cameraObject, camera) = UiUtil.CreateOrthographicCamera(scene);
var (uiContainerObject, uiContainer) = UiUtil.CreateBackgroundUi(scene, camera);
var (uiContainerObject, uiContainer) = UiUtil.CreateBackgroundUi(scene, new UiContainerComponent { Camera = camera });
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, scene, uiContainer);
logoUi.Offset = new Vector2(0, 3f);
var (backUiObject, backUi) = UiUtil.CreateTextUi(scene, uiContainer,
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(scene, uiContainer,
UiUtil.GetDoomFont(parEngine), "Назад");
backUi.OnClick += _ => parEngine.SceneManager.TransitionTo(MainScene.Create(parEngine));
backUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => MainScene.Create(parEngine));
var (stackObject, stack) = UiUtil.CreateStackUi(scene, uiContainer,
[backUi]);
stack.Offset = new Vector2(0, -1f);
stackObject.Transform.Size = new Vector3(1f, 6f, 1f);
var (stackObject, stack) = UiUtil.CreateStackUi(scene,
new StackComponent { Offset = new Vector2(0, -1f), Container = uiContainer, Children = { backUi } });
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
var selectorObject = new GameObject
{
Transform = { Translation = new Vector3(0, 0, -1), Size = new Vector3(0.5f, 1f, 1f) }
};
selectorObject.AddComponent(new SelectorComponent
{
Children = { backUi },
SelectKey = KeyboardButtonCode.Space,
NextKey = KeyboardButtonCode.Down,
PrevKey = KeyboardButtonCode.Up,
});
selectorObject.AddComponent(new Box2DRenderer { Color = new Vector4(1, 0, 0, 1) });
var (selectorObject, selector) = UiUtil.CreateSelectorUi(scene, new SelectorComponent { Children = { backUi } });
scene.AddChild(uiContainerObject, selectorObject);
scene.SetChild(uiContainerObject, logoObject);
scene.SetChild(uiContainerObject, stackObject);
scene.SetChild(stackObject, backUiObject);
scene.AddChild(uiContainerObject, logoObject);
scene.AddChild(uiContainerObject, stackObject);
scene.AddChild(stackObject, backUiObject);
return scene;
}

View File

@@ -1,11 +1,8 @@
using DoomDeathmatch.Component.UI;
using DoomDeathmatch.Scene.Leaders;
using DoomDeathmatch.Scene.Play;
using DoomDeathmatch.Scene.Rules;
using Engine.Asset.Font;
using Engine.Input;
using Engine.Scene;
using Engine.Scene.Component.BuiltIn;
using Engine.Scene.Component.BuiltIn.Renderer;
using OpenTK.Mathematics;
namespace DoomDeathmatch.Scene.Main;
@@ -18,60 +15,49 @@ public static class MainScene
var (cameraObject, camera) = UiUtil.CreateOrthographicCamera(scene);
AddMainUi(parEngine, scene, camera);
var (uiContainerObject, uiContainer) =
UiUtil.CreateBackgroundUi(scene, new UiContainerComponent { Camera = camera });
var (playUiObject, playUi, _) =
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(parEngine), "Играть");
var (leadersUiObject, leadersUi, _) =
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(parEngine), "Лидеры");
var (rulesUiObject, rulesUi, _) =
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(parEngine), "Правила");
var (exitUiObject, exitUi, _) =
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(parEngine), "Выход");
var (stackObject, stack) = UiUtil.CreateStackUi(scene,
new StackComponent
{
Offset = new Vector2(0, -1f), Container = uiContainer, Children = { playUi, leadersUi, rulesUi, exitUi }
});
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
playUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => PlayScene.Create(parEngine));
leadersUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => LeadersScene.Create(parEngine));
rulesUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => RulesScene.Create(parEngine));
exitUi.OnClick += _ => parEngine.Close();
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, scene, uiContainer);
logoUi.Offset = new Vector2(0, 3f);
var (selectorObject, selector) = UiUtil.CreateSelectorUi(scene,
new SelectorComponent { Children = { playUi, leadersUi, rulesUi, exitUi } });
scene.AddChild(uiContainerObject, selectorObject);
scene.AddChild(uiContainerObject, stackObject);
scene.AddChild(uiContainerObject, logoObject);
scene.AddChild(stackObject, playUiObject);
scene.AddChild(stackObject, leadersUiObject);
scene.AddChild(stackObject, rulesUiObject);
scene.AddChild(stackObject, exitUiObject);
return scene;
}
private static void AddMainUi(Engine.Engine parEngine, Engine.Scene.Scene parScene, Camera parCamera)
{
var (uiContainerObject, uiContainer) = UiUtil.CreateBackgroundUi(parScene, parCamera);
var (playUiObject, playUi) =
UiUtil.CreateTextUi(parScene, uiContainer, UiUtil.GetDoomFont(parEngine), "Играть");
var (leadersUiObject, leadersUi) =
UiUtil.CreateTextUi(parScene, uiContainer, UiUtil.GetDoomFont(parEngine), "Лидеры");
var (rulesUiObject, rulesUi) =
UiUtil.CreateTextUi(parScene, uiContainer, UiUtil.GetDoomFont(parEngine), "Правила");
var (exitUiObject, exitUi) =
UiUtil.CreateTextUi(parScene, uiContainer, UiUtil.GetDoomFont(parEngine), "Выход");
var (stackObject, stack) = UiUtil.CreateStackUi(parScene, uiContainer,
[playUi, leadersUi, rulesUi, exitUi]);
stack.Offset = new Vector2(0, -1f);
stackObject.Transform.Size = new Vector3(2f, 5f, 1f);
playUi.OnClick += _ => Console.WriteLine("Play");
leadersUi.OnClick += _ => parEngine.SceneManager.TransitionTo(LeadersScene.Create(parEngine));
rulesUi.OnClick += _ => parEngine.SceneManager.TransitionTo(RulesScene.Create(parEngine));
exitUi.OnClick += _ => parEngine.Close();
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, parScene, uiContainer);
var selectorObject = new GameObject
{
Transform = { Translation = new Vector3(0, 0, -1), Size = new Vector3(0.5f, 1f, 1f) }
};
selectorObject.AddComponent(new SelectorComponent
{
Children = { playUi, leadersUi, rulesUi, exitUi },
SelectKey = KeyboardButtonCode.Space,
NextKey = KeyboardButtonCode.Down,
PrevKey = KeyboardButtonCode.Up,
});
selectorObject.AddComponent(new Box2DRenderer { Color = new Vector4(1, 0, 0, 1) });
parScene.AddChild(uiContainerObject, selectorObject);
parScene.SetChild(uiContainerObject, stackObject);
parScene.SetChild(uiContainerObject, logoObject);
parScene.SetChild(stackObject, playUiObject);
parScene.SetChild(stackObject, leadersUiObject);
parScene.SetChild(stackObject, rulesUiObject);
parScene.SetChild(stackObject, exitUiObject);
}
}

View File

@@ -0,0 +1,136 @@
using DoomDeathmatch.Component;
using DoomDeathmatch.Component.MVC.Controller;
using DoomDeathmatch.Component.MVC.View;
using DoomDeathmatch.Component.UI;
using DoomDeathmatch.Component.Util;
using Engine.Asset.Mesh;
using Engine.Graphics.Pipeline;
using Engine.Graphics.Texture;
using Engine.Scene;
using Engine.Scene.Component.BuiltIn;
using Engine.Scene.Component.BuiltIn.Renderer;
using OpenTK.Mathematics;
namespace DoomDeathmatch.Scene.Play;
public static class PlayScene
{
public static Engine.Scene.Scene Create(Engine.Engine parEngine)
{
var scene = new Engine.Scene.Scene();
var (hudCameraObject, hudCamera) = UiUtil.CreateOrthographicCamera(scene, RenderLayer.HUD);
var (uiContainerObject, uiContainer) =
UiUtil.CreateContainerUi(scene, new UiContainerComponent { Camera = hudCamera });
var (bottomContainerObject, bottomContainer) =
UiUtil.CreateContainerUi(scene, new UiContainerComponent { Container = uiContainer });
bottomContainer.Anchor = Anchor.BottomCenter;
bottomContainer.Center = Anchor.BottomCenter;
bottomContainerObject.AddComponent(new Box2DRenderer
{
Color = new Vector4(1, 0, 0, 1), RenderLayer = RenderLayer.HUD
});
bottomContainerObject.AddComponent(new CopySizeComponent
{
Target = uiContainerObject.Transform, Coefficient = new Vector3(1, 0, 1)
});
bottomContainerObject.Transform.Size.Y = 1.5f;
scene.AddChild(uiContainerObject, bottomContainerObject);
var (gunObject, (gunUi, gunSprite)) = UiUtil.CreateSpriteUi(scene, bottomContainer,
parEngine.AssetResourceManager.Load<Texture>("texture/pistol.png"), RenderLayer.HUD);
gunObject.Transform.Scale = new Vector3(5);
gunUi.Anchor = Anchor.TopCenter;
gunUi.Center = Anchor.BottomCenter;
scene.AddChild(bottomContainerObject, gunObject);
var (healthObject, healthUi, (_, healthTextRenderer)) =
UiUtil.CreateTextUi(scene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Здоровье: 000",
TextAlignComponent.Align.Center,
RenderLayer.HUD);
healthObject.Transform.Scale = new Vector3(0.75f);
healthUi.Anchor = Anchor.CenterLeft;
healthUi.Center = Anchor.CenterLeft;
scene.AddChild(bottomContainerObject, healthObject);
var (ammoObject, ammoUi, (_, ammoTextRenderer)) =
UiUtil.CreateTextUi(scene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Патроны: 00/00",
TextAlignComponent.Align.Center,
RenderLayer.HUD);
ammoObject.Transform.Scale = new Vector3(0.75f);
ammoUi.Anchor = Anchor.TopRight;
ammoUi.Center = Anchor.TopRight;
scene.AddChild(bottomContainerObject, ammoObject);
var (weaponObject, weaponUi, (_, weaponTextRenderer)) =
UiUtil.CreateTextUi(scene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Оружие: ОРУЖИЕОР",
TextAlignComponent.Align.Center,
RenderLayer.HUD);
weaponObject.Transform.Scale = new Vector3(0.75f);
weaponUi.Anchor = Anchor.BottomRight;
weaponUi.Center = Anchor.BottomRight;
scene.AddChild(bottomContainerObject, weaponObject);
var (topContainerObject, topContainer) =
UiUtil.CreateContainerUi(scene, new UiContainerComponent { Container = uiContainer });
topContainer.Anchor = Anchor.TopCenter;
topContainer.Center = Anchor.TopCenter;
topContainerObject.AddComponent(
new Box2DRenderer { Color = new Vector4(1, 0, 0, 1), RenderLayer = RenderLayer.HUD });
topContainerObject.AddComponent(new CopySizeComponent
{
Target = uiContainerObject.Transform, Coefficient = new Vector3(1, 0, 1)
});
topContainerObject.Transform.Size.Y = 1f;
scene.AddChild(uiContainerObject, topContainerObject);
var (timerObject, timerUi, _) =
UiUtil.CreateTextUi(scene, topContainer, UiUtil.GetDoomFont(parEngine), "Время: 00:00",
parRenderLayer: RenderLayer.HUD);
timerUi.Anchor = Anchor.CenterLeft;
timerUi.Center = Anchor.CenterLeft;
scene.AddChild(topContainerObject, timerObject);
var (scoreObject, scoreUi, (_, scoreTextRenderer)) =
UiUtil.CreateTextUi(scene, topContainer, UiUtil.GetDoomFont(parEngine), "Счет: 00000",
parRenderLayer: RenderLayer.HUD);
scoreUi.Anchor = Anchor.CenterRight;
scoreUi.Center = Anchor.CenterRight;
scene.AddChild(topContainerObject, scoreObject);
var playerObject = GameObjectUtil.CreateGameObject(scene, [
new RigidbodyComponent(),
new ControllerComponent { Speed = 5f },
new DragComponent { Drag = 5f, Coefficient = new Vector3(1, 1, 0) },
new PlayerController(),
new WeaponController(),
new WeaponView(weaponTextRenderer, ammoTextRenderer, gunSprite),
new HealthController(),
new HealthView(healthTextRenderer),
new ScoreController(),
new ScoreView(scoreTextRenderer),
]);
var (perspectiveCameraObject, perspectiveCamera) = UiUtil.CreatePerspectiveCamera(scene);
perspectiveCameraObject.Transform.Translation.Z = 2;
var mapObject = GameObjectUtil.CreateGameObject(scene, [
new MeshRenderer { Mesh = parEngine.AssetResourceManager.Load<Mesh>("model/map.obj") },
]);
var impObject = GameObjectUtil.CreateGameObject(scene,
new Transform { Translation = new Vector3(0, 0, 1), Scale = new Vector3(1, 2, 1), }, [
new Box2DRenderer { Texture = parEngine.AssetResourceManager.Load<Texture>("texture/imp.png") },
new BillboardComponent { Target = perspectiveCameraObject.Transform }
]);
scene.AddChild(playerObject, perspectiveCameraObject);
return scene;
}
}

View File

@@ -1,9 +1,5 @@
using DoomDeathmatch.Component.UI;
using DoomDeathmatch.Scene.Main;
using Engine.Asset.Font;
using Engine.Input;
using Engine.Scene;
using Engine.Scene.Component.BuiltIn.Renderer;
using OpenTK.Mathematics;
namespace DoomDeathmatch.Scene.Rules;
@@ -16,42 +12,31 @@ public static class RulesScene
var (cameraObject, camera) = UiUtil.CreateOrthographicCamera(scene);
var (uiContainerObject, uiContainer) = UiUtil.CreateBackgroundUi(scene, camera);
var (uiContainerObject, uiContainer) = UiUtil.CreateBackgroundUi(scene, new UiContainerComponent { Camera = camera });
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, scene, uiContainer);
logoUi.Offset = new Vector2(0, 3f);
var (backUiObject, backUi) = UiUtil.CreateTextUi(scene, uiContainer,
parEngine.AssetResourceManager.Load<Font>("font/test"), "Назад");
backUi.OnClick += _ => parEngine.SceneManager.TransitionTo(MainScene.Create(parEngine));
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(scene, uiContainer,
UiUtil.GetDoomFont(parEngine), "Назад");
backUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => MainScene.Create(parEngine));
var (rulesObject, rulesUi) = UiUtil.CreateTextUi(scene, uiContainer,
parEngine.AssetResourceManager.Load<Font>("font/test"), "Правила");
var (rulesObject, rulesUi, _) = UiUtil.CreateTextUi(scene, uiContainer,
UiUtil.GetDoomFont(parEngine), "Правила");
var (stackObject, stack) = UiUtil.CreateStackUi(scene, uiContainer,
[rulesUi, backUi]);
stack.Offset = new Vector2(0, -1f);
stackObject.Transform.Size = new Vector3(1f, 6f, 1f);
var (stackObject, stack) = UiUtil.CreateStackUi(scene,
new StackComponent { Offset = new Vector2(0, -1f), Container = uiContainer, Children = { rulesUi, backUi } });
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
var selectorObject = new GameObject
{
Transform = { Translation = new Vector3(0, 0, -1), Size = new Vector3(0.5f, 1f, 1f) }
};
selectorObject.AddComponent(new SelectorComponent
{
Children = { backUi },
SelectKey = KeyboardButtonCode.Space,
NextKey = KeyboardButtonCode.Down,
PrevKey = KeyboardButtonCode.Up,
});
selectorObject.AddComponent(new Box2DRenderer { Color = new Vector4(1, 0, 0, 1) });
var (selectorObject, selector) = UiUtil.CreateSelectorUi(scene, new SelectorComponent { Children = { backUi } });
scene.AddChild(uiContainerObject, selectorObject);
scene.SetChild(uiContainerObject, logoObject);
scene.SetChild(uiContainerObject, stackObject);
scene.AddChild(uiContainerObject, logoObject);
scene.AddChild(uiContainerObject, stackObject);
scene.SetChild(stackObject, rulesObject);
scene.SetChild(stackObject, backUiObject);
scene.AddChild(stackObject, rulesObject);
scene.AddChild(stackObject, backUiObject);
return scene;
}

View File

@@ -1,6 +1,8 @@
using DoomDeathmatch.Component.UI;
using Engine.Asset.Font;
using Engine.Graphics.Pipeline;
using Engine.Graphics.Texture;
using Engine.Input;
using Engine.Scene;
using Engine.Scene.Component.BuiltIn;
using Engine.Scene.Component.BuiltIn.Renderer;
@@ -15,102 +17,154 @@ public static class UiUtil
return parEngine.AssetResourceManager.Load<Font>("font/doom");
}
public static (GameObject, OrthographicCamera) CreateOrthographicCamera(Engine.Scene.Scene parScene)
public static (GameObject, OrthographicCamera) CreateOrthographicCamera(Engine.Scene.Scene parScene,
RenderLayer? parRenderLayer = null)
{
var cameraObject = new GameObject();
var camera = new OrthographicCamera();
cameraObject.AddComponent(camera);
parScene.Add(cameraObject);
OrthographicCamera camera;
var cameraObject = GameObjectUtil.CreateGameObject(parScene, [
camera = new OrthographicCamera { RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT }
]);
return (cameraObject, camera);
}
public static (GameObject, UiComponent) CreateTextUi(Engine.Scene.Scene parScene, UiContainerComponent parContainer,
Font parFont, string parText)
public static (GameObject, PerspectiveCamera) CreatePerspectiveCamera(Engine.Scene.Scene parScene,
RenderLayer? parRenderLayer = null)
{
PerspectiveCamera camera;
var cameraObject = GameObjectUtil.CreateGameObject(parScene, [
camera = new PerspectiveCamera { RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT }
]);
return (cameraObject, camera);
}
public static (GameObject, UiContainerComponent, (GameObject, TextRenderer)) CreateTextUi(Engine.Scene.Scene parScene,
UiContainerComponent parContainer,
Font parFont, string parText, TextAlignComponent.Align parAlign = TextAlignComponent.Align.Center,
RenderLayer? parRenderLayer = null)
{
var size = parFont.Measure(parText);
var outerObject = new GameObject
{
Transform = { Size = new Vector3(size.X, size.Y, 1f), Translation = new Vector3(0, 0, -1) }
};
var uiComponent = new UiComponent { Container = parContainer, Anchor = Anchor.Center };
var uiComponent = new UiContainerComponent { Container = parContainer };
outerObject.AddComponent(uiComponent);
outerObject.AddComponent(new Box2DRenderer { Color = new Vector4(0, 0, 1, 1) });
outerObject.AddComponent(new Box2DRenderer
{
Color = new Vector4(0, 0, 1, 1), RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT
});
var innerObject = new GameObject { Transform = { Translation = new Vector3(0, 0, -1) } };
innerObject.AddComponent(new TextRenderer { Font = parFont, Text = parText });
innerObject.AddComponent(new TextAlignComponent { Alignment = TextAlignComponent.Align.Center });
// var innerUiComponent = new UiComponent { Container = uiComponent };
// innerObject.AddComponent(innerUiComponent);
var innerTextRenderer = new TextRenderer
{
Font = parFont, Text = parText, RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT
};
innerObject.AddComponent(innerTextRenderer);
innerObject.AddComponent(new TextAlignComponent { Alignment = parAlign });
parScene.Add(outerObject);
parScene.AddChild(outerObject, innerObject);
return (outerObject, uiComponent);
return (outerObject, uiComponent, (innerObject, innerTextRenderer));
}
public static (GameObject, UiContainerComponent) CreateBackgroundUi(Engine.Scene.Scene parScene, Camera parCamera)
public static (GameObject, UiContainerComponent) CreateContainerUi(Engine.Scene.Scene parScene,
UiContainerComponent parUiContainerComponent)
{
var uiContainerObject = new GameObject();
var uiContainer = new UiContainerComponent { Camera = parCamera };
uiContainerObject.AddComponent(uiContainer);
uiContainerObject.AddComponent(new Box2DRenderer { Color = new Vector4(0.1f, 0.1f, 0.1f, 1) });
uiContainerObject.AddComponent(parUiContainerComponent);
parScene.Add(uiContainerObject);
return (uiContainerObject, parUiContainerComponent);
}
public static (GameObject, UiContainerComponent) CreateBackgroundUi(Engine.Scene.Scene parScene,
UiContainerComponent parUiContainerComponent, Vector4? parColor = null,
RenderLayer? parRenderLayer = null)
{
var (uiContainerObject, uiContainer) = CreateContainerUi(parScene, parUiContainerComponent);
uiContainerObject.AddComponent(new Box2DRenderer
{
Color = parColor ?? new Vector4(0.1f, 0.1f, 0.1f, 1), RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT
});
return (uiContainerObject, uiContainer);
}
public static (GameObject, UiComponent) CreateLogoUi(Engine.Engine parEngine, Engine.Scene.Scene parScene,
UiContainerComponent parContainer)
UiContainerComponent parContainer, RenderLayer? parRenderLayer = null)
{
var logoObject = new GameObject
{
Transform =
UiComponent uiComponent;
var logoObject = GameObjectUtil.CreateGameObject(parScene,
new Transform
{
Translation = new Vector3(0, 0, -10), Scale = new Vector3(3), Size = new Vector3(1.6385869565f, 1f, 1f)
}
};
logoObject.AddComponent(new Box2DRenderer
}, [
new Box2DRenderer
{
Texture = parEngine.AssetResourceManager.Load<Texture>("texture/doom_logo.png")
});
var uiComponent = new UiComponent { Container = null, Anchor = Anchor.Center };
logoObject.AddComponent(new UiComponent
{
Container = parContainer, Anchor = Anchor.Center, Offset = new Vector2(0, 3f)
});
parScene.Add(logoObject);
Texture = parEngine.AssetResourceManager.Load<Texture>("texture/doom_logo.png"),
RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT
},
uiComponent = new UiComponent { Container = parContainer, Anchor = Anchor.Center }
]
);
return (logoObject, uiComponent);
}
public static (GameObject, StackComponent) CreateStackUi(Engine.Scene.Scene parScene,
UiContainerComponent parContainer, List<UiComponent> parChildren, Orientation parOrientation = Orientation.Vertical)
public static (GameObject, (UiComponent, Box2DRenderer)) CreateSpriteUi(Engine.Scene.Scene parScene,
UiContainerComponent parContainer,
Texture? parTexture, RenderLayer? parRenderLayer = null)
{
var stack = new StackComponent
var spriteObject = new GameObject();
var spriteComponent = new Box2DRenderer
{
Container = parContainer,
Anchor = Anchor.Center
Texture = parTexture, RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT
};
spriteObject.AddComponent(spriteComponent);
var uiComponent = new UiContainerComponent { Container = parContainer };
spriteObject.AddComponent(uiComponent);
stack.Children.AddRange(parChildren);
foreach (var child in parChildren)
{
child.Container = stack;
return (spriteObject, (uiComponent, spriteComponent));
}
var stackObject = new GameObject
public static (GameObject, StackComponent) CreateStackUi(Engine.Scene.Scene parScene,
StackComponent parStackComponent, RenderLayer? parRenderLayer = null)
{
Transform = { Translation = new Vector3(0, 0, -1) }
};
stackObject.AddComponent(stack);
stackObject.AddComponent(new Box2DRenderer { Color = new Vector4(1, 0, 0, 1) });
foreach (var child in parStackComponent.Children)
child.Container = parStackComponent;
var stackObject = new GameObject { Transform = { Translation = new Vector3(0, 0, -1) } };
stackObject.AddComponent(parStackComponent);
// stackObject.AddComponent(new Box2DRenderer { Color = new Vector4(1, 0, 0, 1), RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT });
parScene.Add(stackObject);
return (stackObject, stack);
return (stackObject, parStackComponent);
}
public static (GameObject, SelectorComponent) CreateSelectorUi(Engine.Scene.Scene parScene,
SelectorComponent parSelectorComponent, RenderLayer? parRenderLayer = null)
{
var selectorObject = new GameObject { Transform = { Translation = new Vector3(0, 0, -1) } };
selectorObject.AddComponent(parSelectorComponent);
var innerSelectorObject = new GameObject
{
Transform = { Translation = new Vector3(-0.5f, 0, 0), Size = new Vector3(0.25f, 0.5f, 1f) }
};
innerSelectorObject.AddComponent(new Box2DRenderer
{
Color = new Vector4(1, 1, 1, 1), RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT
});
parScene.Add(selectorObject);
parScene.AddChild(selectorObject, innerSelectorObject);
return (selectorObject, parSelectorComponent);
}
}

View File

@@ -4,6 +4,7 @@ using Engine.Asset;
using Engine.Asset.Font;
using Engine.Asset.Mesh;
using Engine.Graphics;
using Engine.Graphics.Pipeline;
using Engine.Graphics.Pixel;
using Engine.Graphics.Shader;
using Engine.Graphics.Texture;
@@ -155,27 +156,32 @@ public sealed class Engine
Debug.RenderDocStartFrame();
#endif
Renderer.StartFrame();
Renderer.RunScheduledActions();
var projection = Matrix4.Identity;
var view = Matrix4.Identity;
lock (_sceneLock)
if (Monitor.TryEnter(_sceneLock))
{
var camera = SceneManager.CurrentScene?.MainCamera;
if (camera != null)
Renderer.StartFrame();
var matrices = new Dictionary<RenderLayer, (Matrix4, Matrix4)>();
{
var cameras = SceneManager.CurrentScene?.Cameras;
if (cameras != null)
{
foreach (var (renderLayer, camera) in cameras)
{
camera.ScreenSize = new Vector2i(Renderer.ViewportWidth, Renderer.ViewportHeight);
projection = camera.Projection;
view = camera.View;
matrices[renderLayer] = (camera.Projection, camera.View);
}
}
SceneManager.Render();
}
Monitor.Exit(_sceneLock);
Renderer.EndFrame(projection, view);
Renderer.EndFrame(matrices);
Presenter!.Present(Renderer.RenderTexture);
Presenter!.Render();
}
#if DEBUG
Debug.RenderDocEndFrame();

View File

@@ -10,11 +10,11 @@ namespace Engine.Graphics;
public class GenericRenderer : IRenderer
{
public QuadRenderer QuadRenderer => _quadRenderer ??= new QuadRenderer(_engine, 1024 * 8);
public AnyMeshRenderer AnyMeshRenderer => _globalMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
public AnyMeshRenderer AnyMeshRenderer => _anyMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
public TextRenderer TextRenderer => _textRenderer ??= new TextRenderer(_engine, 1024 * 8);
private QuadRenderer? _quadRenderer;
private AnyMeshRenderer? _globalMeshRenderer;
private AnyMeshRenderer? _anyMeshRenderer;
private TextRenderer? _textRenderer;
private readonly Engine _engine;
@@ -47,14 +47,23 @@ public class GenericRenderer : IRenderer
GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
QuadRenderer.Render(parProjectionMatrix, parViewMatrix);
QuadRenderer.Reset();
if (_anyMeshRenderer != null)
{
_anyMeshRenderer.Render(parProjectionMatrix, parViewMatrix);
_anyMeshRenderer.Reset();
}
AnyMeshRenderer.Render(parProjectionMatrix, parViewMatrix);
AnyMeshRenderer.Reset();
if (_quadRenderer != null)
{
_quadRenderer.Render(parProjectionMatrix, parViewMatrix);
_quadRenderer.Reset();
}
TextRenderer.Render(parProjectionMatrix, parViewMatrix);
TextRenderer.Reset();
if (_textRenderer != null)
{
_textRenderer.Render(parProjectionMatrix, parViewMatrix);
_textRenderer.Reset();
}
_framebuffer.Unbind();

View File

@@ -74,8 +74,8 @@ public class Renderer
// GL.Enable(EnableCap.FramebufferSrgb);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
GL.Viewport(0, 0, parWidth, parHeight);
}
@@ -118,21 +118,22 @@ public class Renderer
{
EnsureRenderThread();
RunScheduledActions();
foreach (var renderer in _renderers.Values)
{
renderer.StartFrame();
}
}
internal void EndFrame(in Matrix4 parViewMatrix, in Matrix4 parProjectionMatrix)
internal void EndFrame(Dictionary<RenderLayer, (Matrix4, Matrix4)> parMatrices)
{
EnsureRenderThread();
foreach (var renderer in _renderers.Values)
foreach (var (renderLayer, renderer) in _renderers)
{
renderer.EndFrame(in parViewMatrix, in parProjectionMatrix);
if (!parMatrices.TryGetValue(renderLayer, out var matrices))
renderer.EndFrame(Matrix4.Identity, Matrix4.Identity);
else
renderer.EndFrame(in matrices.Item1, in matrices.Item2);
}
_framebuffer.Bind();
@@ -143,8 +144,11 @@ public class Renderer
GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
foreach (var renderer in _renderers.Values)
foreach (var (renderLayer, renderer) in _renderers)
{
if (!parMatrices.ContainsKey(renderLayer))
continue;
QuadRenderer.Commit(Matrix4.CreateScale(2f, -2f, 1f), Vector4.One, renderer._framebuffer.TextureInternal);
QuadRenderer.Render(Matrix4.Identity, Matrix4.Identity);
QuadRenderer.Reset();
@@ -167,7 +171,7 @@ public class Renderer
}
}
private void RunScheduledActions()
internal void RunScheduledActions()
{
while (_scheduleActions.TryDequeue(out var action))
{

View File

@@ -29,6 +29,17 @@ public enum KeyboardButtonCode
Y,
Z,
D1,
D2,
D3,
D4,
D5,
D6,
D7,
D8,
D9,
D0,
Ctrl,
Alt,
Shift,

View File

@@ -103,6 +103,16 @@ public class WindowInputHandler(Window parWindow) : IInputHandler
KeyboardButtonCode.End => Keys.End,
KeyboardButtonCode.PageUp => Keys.PageUp,
KeyboardButtonCode.PageDown => Keys.PageDown,
KeyboardButtonCode.D0 => Keys.D0,
KeyboardButtonCode.D1 => Keys.D1,
KeyboardButtonCode.D2 => Keys.D2,
KeyboardButtonCode.D3 => Keys.D3,
KeyboardButtonCode.D4 => Keys.D4,
KeyboardButtonCode.D5 => Keys.D5,
KeyboardButtonCode.D6 => Keys.D6,
KeyboardButtonCode.D7 => Keys.D7,
KeyboardButtonCode.D8 => Keys.D8,
KeyboardButtonCode.D9 => Keys.D9,
_ => throw new ArgumentOutOfRangeException(nameof(parKeyboardButtonCode), parKeyboardButtonCode, null)
};
}

View File

@@ -19,7 +19,10 @@ public class FontLoader : IResourceLoader
var atlasPath = Path.Combine(parPath, "atlas.png");
using var atlasStream = parStreamProvider.GetStream(atlasPath);
var atlasTexture = ImageLoader.Load(atlasStream).ToStaticTexture();
var atlasImage = ImageLoader.Load(atlasStream);
// TODO: We should not be using engine instance here
var atlasTexture = Engine.Instance.Renderer.Schedule(() => atlasImage.ToStaticTexture()).Result;
return new Font(atlasTexture, metadata);
}

View File

@@ -7,6 +7,7 @@ public class TextureLoader : IResourceLoader
using var stream = parStreamProvider.GetStream(parPath);
var image = ImageLoader.Load(stream);
return image.ToStaticTexture();
// TODO: We should not be using engine instance here
return Engine.Instance.Renderer.Schedule(() => image.ToStaticTexture()).Result;
}
}

View File

@@ -1,4 +1,5 @@
using Engine.Graphics.Camera;
using Engine.Graphics.Pipeline;
using OpenTK.Mathematics;
using Serilog;
@@ -13,6 +14,8 @@ public abstract class Camera(
public float NearPlane { get; set; } = parNearPlane;
public float FarPlane { get; set; } = parFarPlane;
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
private Vector2i _screenSize = new(1, 1);
public abstract Matrix4 View { get; }

View File

@@ -25,6 +25,14 @@ public abstract class Component : IUpdate, IRender
public virtual void Destroy()
{
}
public virtual void Enable()
{
}
public virtual void Disable()
{
}
}
public static class ComponentTypeExtensions

View File

@@ -7,6 +7,17 @@ namespace Engine.Scene;
public sealed class GameObject : IUpdate, IRender
{
public Guid Id { get; } = Guid.NewGuid();
public bool IsEnabled
{
get => IsSelfEnabled && IsParentEnabled;
set => IsSelfEnabled = value;
}
private bool IsSelfEnabled { get; set; } = true;
private bool _prevIsSelfEnabled = true;
private bool IsParentEnabled => Scene?.Hierarchy.GetParent(this)?.IsEnabled ?? true;
public Transform Transform { get; }
internal Scene? Scene { get; set; }
@@ -50,6 +61,30 @@ public sealed class GameObject : IUpdate, IRender
public void Update(double parDeltaTime)
{
if (!IsEnabled)
{
if (!_prevIsSelfEnabled)
return;
foreach (var component in _components)
{
component.Disable();
}
_prevIsSelfEnabled = false;
return;
}
if (!_prevIsSelfEnabled)
{
foreach (var component in _components)
{
component.Enable();
}
}
_prevIsSelfEnabled = true;
foreach (var component in _components)
{
component.Update(parDeltaTime);
@@ -58,6 +93,9 @@ public sealed class GameObject : IUpdate, IRender
public void Render()
{
if (!IsEnabled)
return;
foreach (var component in _components)
{
component.Render();
@@ -113,7 +151,7 @@ public sealed class GameObject : IUpdate, IRender
parComponent.GameObject = this;
_components.Add(parComponent);
_addedComponentTypes.Add(typeof(T).GetComponentBaseType());
_addedComponentTypes.Add(parComponent.GetType().GetComponentBaseType());
});
}

View File

@@ -32,7 +32,7 @@ public class Hierarchy<T>
{
if (_parentLookup.ContainsKey(parObj))
{
throw new ArgumentException("Object is already added to hierarchy");
return;
}
_childrenLookup.Add(parObj, new List<T>());

View File

@@ -1,4 +1,5 @@
using Engine.Graphics.Camera;
using Engine.Graphics.Pipeline;
using Engine.Scene.Component.BuiltIn;
namespace Engine.Scene;
@@ -6,7 +7,8 @@ namespace Engine.Scene;
public class Scene : IUpdate, IRender
{
public bool IsPlaying { get; private set; }
public ICamera? MainCamera { get; private set; }
public IReadOnlyDictionary<RenderLayer, ICamera> Cameras => _cameras;
private readonly Dictionary<RenderLayer, ICamera> _cameras = new();
internal Hierarchy<GameObject> Hierarchy { get; } = new();
@@ -21,14 +23,25 @@ public class Scene : IUpdate, IRender
ProcessChanges();
MainCamera = FindFirstComponent<Camera>();
var allCameras = FindAllComponents<Camera>();
foreach (var camera in allCameras)
{
_cameras.Add(camera.RenderLayer, camera);
}
IsPlaying = true;
}
public List<T> FindAllComponents<T>() where T : Component.Component
{
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>())
.Where(parComponent => parComponent != null).ToList()!;
}
public T? FindFirstComponent<T>() where T : Component.Component
{
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>()).FirstOrDefault(parComponent => parComponent != null);
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>())
.FirstOrDefault(parComponent => parComponent != null);
}
public void Update(double parDeltaTime)
@@ -87,15 +100,10 @@ public class Scene : IUpdate, IRender
});
}
public void SetChild(GameObject parParent, GameObject parChild)
{
Hierarchy.AddChild(parParent, parChild);
}
public void AddChild(GameObject parParent, GameObject parChild)
{
Add(parChild);
SetChild(parParent, parChild);
Hierarchy.AddChild(parParent, parChild);
}
public void Remove(GameObject parGameObject)

View File

@@ -5,9 +5,10 @@ public class SceneManager : IUpdate, IRender
public Scene? CurrentScene => _currentScene;
private Scene? _currentScene;
private Scene? _nextScene;
// private Scene? _nextScene;
private Func<Scene>? _nextScene;
public void TransitionTo(Scene parScene)
public void TransitionTo(Func<Scene>? parScene)
{
_nextScene = parScene;
}
@@ -17,7 +18,7 @@ public class SceneManager : IUpdate, IRender
if (_nextScene != null)
{
_currentScene?.Exit();
_currentScene = _nextScene;
_currentScene = _nextScene();
_nextScene = null;
_currentScene.Enter();
}

62
Engine/src/Util/Timer.cs Normal file
View File

@@ -0,0 +1,62 @@
namespace Engine.Util;
public class Timer
{
public event Action? OnFinished;
public event Action<double>? OnUpdate;
public double TotalTime
{
get => _totalTime;
set
{
if (value <= 0)
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
_totalTime = value;
CurrentTime = value;
}
}
public double CurrentTime
{
get => _currentTime;
set
{
if (value < 0)
value = 0;
if (value > TotalTime)
value = TotalTime;
_currentTime = value;
OnUpdate?.Invoke(value);
if (IsFinished)
OnFinished?.Invoke();
}
}
public bool IsFinished => _currentTime <= 0;
private double _totalTime;
private double _currentTime;
public Timer(double parTotalTime)
{
if (parTotalTime <= 0)
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parTotalTime);
_totalTime = parTotalTime;
_currentTime = parTotalTime;
}
public void Update(double parDeltaTime)
{
CurrentTime -= parDeltaTime;
}
public void Reset()
{
CurrentTime = TotalTime;
}
}

View File

@@ -169,16 +169,16 @@ public class HierarchyTests
Assert.That(_hierarchy.GetChildren(obj).Count(), Is.EqualTo(0));
}
[Test]
public void AddDuplicateObjectThrows()
{
var obj = new object();
_hierarchy.Add(obj);
_hierarchy.Add(obj);
Assert.Throws<ArgumentException>(() => _hierarchy.ProcessChanges());
}
// [Test]
// public void AddDuplicateObjectThrows()
// {
// var obj = new object();
//
// _hierarchy.Add(obj);
// _hierarchy.Add(obj);
//
// Assert.Throws<ArgumentException>(() => _hierarchy.ProcessChanges());
// }
[Test]
public void AddChild_ParentNotInHierarchyThrows()

View File

@@ -99,6 +99,16 @@ public class ConsoleInputHandler : IInputHandler
KeyboardButtonCode.End => 0x23,
KeyboardButtonCode.PageUp => 0x21,
KeyboardButtonCode.PageDown => 0x22,
KeyboardButtonCode.D0 => 0x30,
KeyboardButtonCode.D1 => 0x31,
KeyboardButtonCode.D2 => 0x32,
KeyboardButtonCode.D3 => 0x33,
KeyboardButtonCode.D4 => 0x34,
KeyboardButtonCode.D5 => 0x35,
KeyboardButtonCode.D6 => 0x36,
KeyboardButtonCode.D7 => 0x37,
KeyboardButtonCode.D8 => 0x38,
KeyboardButtonCode.D9 => 0x39,
_ => throw new ArgumentOutOfRangeException(nameof(parKeyboardButtonCode), parKeyboardButtonCode, null)
};
}

View File

@@ -163,6 +163,16 @@ public class WpfInputHandler : IInputHandler
Key.Insert => (int)KeyboardButtonCode.Insert,
Key.Home => (int)KeyboardButtonCode.Home,
Key.End => (int)KeyboardButtonCode.End,
Key.D0 => (int)KeyboardButtonCode.D0,
Key.D1 => (int)KeyboardButtonCode.D1,
Key.D2 => (int)KeyboardButtonCode.D2,
Key.D3 => (int)KeyboardButtonCode.D3,
Key.D4 => (int)KeyboardButtonCode.D4,
Key.D5 => (int)KeyboardButtonCode.D5,
Key.D6 => (int)KeyboardButtonCode.D6,
Key.D7 => (int)KeyboardButtonCode.D7,
Key.D8 => (int)KeyboardButtonCode.D8,
Key.D9 => (int)KeyboardButtonCode.D9,
_ => -1
};