.
This commit is contained in:
@@ -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
101
DoomDeathmatch/asset/model/map.obj
Normal file
101
DoomDeathmatch/asset/model/map.obj
Normal 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
|
||||
BIN
DoomDeathmatch/asset/texture/demon.png
Normal file
BIN
DoomDeathmatch/asset/texture/demon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
BIN
DoomDeathmatch/asset/texture/imp.png
Normal file
BIN
DoomDeathmatch/asset/texture/imp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
BIN
DoomDeathmatch/asset/texture/pistol.png
Normal file
BIN
DoomDeathmatch/asset/texture/pistol.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
DoomDeathmatch/asset/texture/shotgun.png
Normal file
BIN
DoomDeathmatch/asset/texture/shotgun.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
35
DoomDeathmatch/src/Component/MVC/Model/HealthModel.cs
Normal file
35
DoomDeathmatch/src/Component/MVC/Model/HealthModel.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
18
DoomDeathmatch/src/Component/MVC/Model/ScoreModel.cs
Normal file
18
DoomDeathmatch/src/Component/MVC/Model/ScoreModel.cs
Normal 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;
|
||||
}
|
||||
54
DoomDeathmatch/src/Component/MVC/Model/WeaponData.cs
Normal file
54
DoomDeathmatch/src/Component/MVC/Model/WeaponData.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
29
DoomDeathmatch/src/Component/MVC/Model/WeaponModel.cs
Normal file
29
DoomDeathmatch/src/Component/MVC/Model/WeaponModel.cs
Normal 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;
|
||||
}
|
||||
23
DoomDeathmatch/src/Component/MVC/View/HealthView.cs
Normal file
23
DoomDeathmatch/src/Component/MVC/View/HealthView.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
19
DoomDeathmatch/src/Component/MVC/View/ScoreView.cs
Normal file
19
DoomDeathmatch/src/Component/MVC/View/ScoreView.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
31
DoomDeathmatch/src/Component/MVC/View/WeaponView.cs
Normal file
31
DoomDeathmatch/src/Component/MVC/View/WeaponView.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using Engine.Input;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component;
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class ControllerComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
25
DoomDeathmatch/src/Component/Util/CopySizeComponent.cs
Normal file
25
DoomDeathmatch/src/Component/Util/CopySizeComponent.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component;
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class DragComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component;
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class GravityComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component;
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class RigidbodyComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using Engine.Input;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component;
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class RotateComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -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;
|
||||
// }
|
||||
}
|
||||
35
DoomDeathmatch/src/GameObjectUtil.cs
Normal file
35
DoomDeathmatch/src/GameObjectUtil.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
136
DoomDeathmatch/src/Scene/Play/PlayScene.cs
Normal file
136
DoomDeathmatch/src/Scene/Play/PlayScene.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
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);
|
||||
}, [
|
||||
new Box2DRenderer
|
||||
{
|
||||
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
|
||||
{
|
||||
Transform = { Translation = new Vector3(0, 0, -1) }
|
||||
};
|
||||
stackObject.AddComponent(stack);
|
||||
stackObject.AddComponent(new Box2DRenderer { Color = new Vector4(1, 0, 0, 1) });
|
||||
public static (GameObject, StackComponent) CreateStackUi(Engine.Scene.Scene parScene,
|
||||
StackComponent parStackComponent, RenderLayer? parRenderLayer = null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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,28 +156,33 @@ 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)>();
|
||||
{
|
||||
camera.ScreenSize = new Vector2i(Renderer.ViewportWidth, Renderer.ViewportHeight);
|
||||
projection = camera.Projection;
|
||||
view = camera.View;
|
||||
var cameras = SceneManager.CurrentScene?.Cameras;
|
||||
if (cameras != null)
|
||||
{
|
||||
foreach (var (renderLayer, camera) in cameras)
|
||||
{
|
||||
camera.ScreenSize = new Vector2i(Renderer.ViewportWidth, Renderer.ViewportHeight);
|
||||
matrices[renderLayer] = (camera.Projection, camera.View);
|
||||
}
|
||||
}
|
||||
|
||||
SceneManager.Render();
|
||||
}
|
||||
Monitor.Exit(_sceneLock);
|
||||
|
||||
SceneManager.Render();
|
||||
Renderer.EndFrame(matrices);
|
||||
|
||||
Presenter!.Present(Renderer.RenderTexture);
|
||||
Presenter!.Render();
|
||||
}
|
||||
|
||||
Renderer.EndFrame(projection, view);
|
||||
|
||||
Presenter!.Present(Renderer.RenderTexture);
|
||||
Presenter!.Render();
|
||||
|
||||
#if DEBUG
|
||||
Debug.RenderDocEndFrame();
|
||||
#endif
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -29,6 +29,17 @@ public enum KeyboardButtonCode
|
||||
Y,
|
||||
Z,
|
||||
|
||||
D1,
|
||||
D2,
|
||||
D3,
|
||||
D4,
|
||||
D5,
|
||||
D6,
|
||||
D7,
|
||||
D8,
|
||||
D9,
|
||||
D0,
|
||||
|
||||
Ctrl,
|
||||
Alt,
|
||||
Shift,
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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>());
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
62
Engine/src/Util/Timer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user