last hope
This commit is contained in:
@@ -12,6 +12,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresenterNative", "Presente
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoomDeathmatch", "DoomDeathmatch\DoomDeathmatch.csproj", "{4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoomDeathmatch", "DoomDeathmatch\DoomDeathmatch.csproj", "{4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtil", "TestUtil\TestUtil.csproj", "{923DEAFC-55C6-426A-8EE8-D74142FDC342}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameTests", "GameTests\GameTests.csproj", "{C2CE10BB-DB8C-4283-BC22-88CDA8B54DEB}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -42,5 +46,13 @@ Global
|
|||||||
{4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{4FA5E1F8-B647-4764-9147-B6F5A4E9D1D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{923DEAFC-55C6-426A-8EE8-D74142FDC342}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{923DEAFC-55C6-426A-8EE8-D74142FDC342}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{923DEAFC-55C6-426A-8EE8-D74142FDC342}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{923DEAFC-55C6-426A-8EE8-D74142FDC342}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C2CE10BB-DB8C-4283-BC22-88CDA8B54DEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C2CE10BB-DB8C-4283-BC22-88CDA8B54DEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C2CE10BB-DB8C-4283-BC22-88CDA8B54DEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C2CE10BB-DB8C-4283-BC22-88CDA8B54DEB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -6,6 +6,14 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<DocumentationFile></DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<DocumentationFile></DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Engine\Engine.csproj"/>
|
<ProjectReference Include="..\Engine\Engine.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ public class HealthController : Engine.Scene.Component.Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsAlive => _healthModel.Health > 0;
|
public bool IsAlive => _healthModel.Health > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current health value of the entity.
|
||||||
|
/// </summary>
|
||||||
|
public float Health => _healthModel.Health;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The health model containing the current and maximum health.
|
/// The health model containing the current and maximum health.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class HealthView : Engine.Scene.Component.Component, IView<HealthModel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual void UpdateView(HealthModel parHealthModel)
|
public void UpdateView(HealthModel parHealthModel)
|
||||||
{
|
{
|
||||||
var percentage = parHealthModel.Health / parHealthModel.MaxHealth * 100;
|
var percentage = parHealthModel.Health / parHealthModel.MaxHealth * 100;
|
||||||
if (parHealthModel.Health != 0)
|
if (parHealthModel.Health != 0)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class MovementComponent : Engine.Scene.Component.Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The drag component for the game object.
|
/// The drag component for the game object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private DragComponent _dragComponent = null!;
|
private DragComponent? _dragComponent;
|
||||||
|
|
||||||
public override void Awake()
|
public override void Awake()
|
||||||
{
|
{
|
||||||
@@ -34,7 +34,6 @@ public class MovementComponent : Engine.Scene.Component.Component
|
|||||||
_dragComponent = GameObject.GetComponent<DragComponent>()!;
|
_dragComponent = GameObject.GetComponent<DragComponent>()!;
|
||||||
|
|
||||||
ArgumentNullException.ThrowIfNull(_rigidbody);
|
ArgumentNullException.ThrowIfNull(_rigidbody);
|
||||||
ArgumentNullException.ThrowIfNull(_dragComponent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -43,7 +42,7 @@ public class MovementComponent : Engine.Scene.Component.Component
|
|||||||
/// <param name="parDirection">The direction of movement.</param>
|
/// <param name="parDirection">The direction of movement.</param>
|
||||||
public void ApplyMovement(Vector3 parDirection)
|
public void ApplyMovement(Vector3 parDirection)
|
||||||
{
|
{
|
||||||
_rigidbody.Force += _dragComponent.Drag * Speed * parDirection.Normalized();
|
_rigidbody.Force += (_dragComponent?.Drag ?? 1) * Speed * parDirection.Normalized();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -53,6 +52,6 @@ public class MovementComponent : Engine.Scene.Component.Component
|
|||||||
public void ApplyRotation(Vector3 parAxis)
|
public void ApplyRotation(Vector3 parAxis)
|
||||||
{
|
{
|
||||||
var radiansPerSecond = MathHelper.DegreesToRadians(RotationSpeed);
|
var radiansPerSecond = MathHelper.DegreesToRadians(RotationSpeed);
|
||||||
_rigidbody.Torque += _dragComponent.RotationalDrag * radiansPerSecond * parAxis.Normalized();
|
_rigidbody.Torque += (_dragComponent?.RotationalDrag ?? 1) * radiansPerSecond * parAxis.Normalized();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ public class WeaponController : Engine.Scene.Component.Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// View responsible for displaying weapon information and animations.
|
/// View responsible for displaying weapon information and animations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IWeaponView _weaponView = null!;
|
private IWeaponView? _weaponView;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WeaponController"/> class with an initial weapon.
|
/// Initializes a new instance of the <see cref="WeaponController"/> class with an initial weapon.
|
||||||
@@ -43,7 +43,7 @@ public class WeaponController : Engine.Scene.Component.Component
|
|||||||
|
|
||||||
public override void Awake()
|
public override void Awake()
|
||||||
{
|
{
|
||||||
_weaponView = GameObject.GetComponent<IWeaponView>()!;
|
_weaponView = GameObject.GetComponent<IWeaponView>();
|
||||||
|
|
||||||
_weaponModel.OnWeaponSelected += WeaponSelected;
|
_weaponModel.OnWeaponSelected += WeaponSelected;
|
||||||
WeaponSelected(null, _weaponModel.SelectedWeapon);
|
WeaponSelected(null, _weaponModel.SelectedWeapon);
|
||||||
@@ -63,7 +63,7 @@ public class WeaponController : Engine.Scene.Component.Component
|
|||||||
_weaponModel.SelectedWeapon.Ammo--;
|
_weaponModel.SelectedWeapon.Ammo--;
|
||||||
|
|
||||||
OnWeaponShot?.Invoke(_weaponModel.SelectedWeapon);
|
OnWeaponShot?.Invoke(_weaponModel.SelectedWeapon);
|
||||||
_weaponView.PlayFireAnimation();
|
_weaponView?.PlayFireAnimation();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ public class WeaponController : Engine.Scene.Component.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
parNewWeapon.OnAmmoChanged += AmmoChanged;
|
parNewWeapon.OnAmmoChanged += AmmoChanged;
|
||||||
_weaponView.UpdateView(parNewWeapon);
|
_weaponView?.UpdateView(parNewWeapon);
|
||||||
AmmoChanged(parNewWeapon);
|
AmmoChanged(parNewWeapon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +190,6 @@ public class WeaponController : Engine.Scene.Component.Component
|
|||||||
{
|
{
|
||||||
var ammoData = new AmmoData { Ammo = parWeapon.Ammo, MaxAmmo = parWeapon.MaxAmmo };
|
var ammoData = new AmmoData { Ammo = parWeapon.Ammo, MaxAmmo = parWeapon.MaxAmmo };
|
||||||
|
|
||||||
_weaponView.UpdateView(ammoData);
|
_weaponView?.UpdateView(ammoData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,16 +154,9 @@ public class RigidbodyComponent : Engine.Scene.Component.Component
|
|||||||
_angularAcceleration = Torque / MomentOfInertia;
|
_angularAcceleration = Torque / MomentOfInertia;
|
||||||
AngularVelocity += _angularAcceleration * (float)parDeltaTime;
|
AngularVelocity += _angularAcceleration * (float)parDeltaTime;
|
||||||
|
|
||||||
// Update rotation using quaternion math
|
GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitX, AngularVelocity.X * (float)parDeltaTime);
|
||||||
var rotation = GameObject.Transform.Rotation;
|
GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitY, AngularVelocity.Y * (float)parDeltaTime);
|
||||||
var angularVelocityQuat = new Quaternion(AngularVelocity, 0.0f);
|
GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitZ, AngularVelocity.Z * (float)parDeltaTime);
|
||||||
|
|
||||||
// Quaternion rotation integration: Δq = 0.5 * angularVelocityQuat * rotation
|
|
||||||
var deltaRotation = 0.5f * angularVelocityQuat * rotation;
|
|
||||||
rotation += deltaRotation * (float)parDeltaTime;
|
|
||||||
rotation.Normalize(); // Ensure the quaternion remains normalized
|
|
||||||
|
|
||||||
GameObject.Transform.Rotation = rotation;
|
|
||||||
|
|
||||||
Torque = Vector3.Zero;
|
Torque = Vector3.Zero;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ public class TextAlignComponent : Engine.Scene.Component.Component
|
|||||||
{
|
{
|
||||||
return Alignment switch
|
return Alignment switch
|
||||||
{
|
{
|
||||||
Align.Left => new Vector2(0, -parSize.Y / 2),
|
Align.Left => new Vector2(0, parSize.Y / 2 - _textRenderer.Font.Metadata.Metrics.Ascender),
|
||||||
Align.Center => new Vector2(-parSize.X / 2, -parSize.Y / 2),
|
Align.Center => new Vector2(-parSize.X / 2, parSize.Y / 2 - _textRenderer.Font.Metadata.Metrics.Ascender),
|
||||||
Align.Right => new Vector2(-parSize.X, -parSize.Y / 2),
|
Align.Right => new Vector2(-parSize.X, parSize.Y / 2 - _textRenderer.Font.Metadata.Metrics.Ascender),
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(Alignment), Alignment, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(Alignment), Alignment, null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,9 +180,14 @@ public static class MainScene
|
|||||||
var (rulesObject, rulesUi, _) = UiPrefabs.CreateTextUi(parScene, parUiContainer,
|
var (rulesObject, rulesUi, _) = UiPrefabs.CreateTextUi(parScene, parUiContainer,
|
||||||
UiPrefabs.GetDoomFont(), "Правила");
|
UiPrefabs.GetDoomFont(), "Правила");
|
||||||
|
|
||||||
|
var (actualRulesObject, actualRulesUi, _) = UiPrefabs.CreateTextUi(parScene, parUiContainer,
|
||||||
|
UiPrefabs.GetDoomFont(), "Игрок управляет персонажем, который может передвигаться, собирать предметы и использовать оружие.\nВ игре два вида оружия: пистолет и дробовик, каждое с уникальными характеристиками и ограничением по боеприпасам.\nПротивники делятся на демонов, которые преследуют игрока и наносят ближний урон, и импом,\nкоторые атакуют издалека и создают огненные шары.\nНа уровне случайно появляются предметы, такие как оружие и аптечки, которые восполняют здоровье или боеприпасы.\nЗа уничтожение врагов игрок получает очки, отображаемые в счетчике.\nЛучшие результаты сохраняются в таблице рекордов.");
|
||||||
|
|
||||||
|
actualRulesObject.Transform.Scale.Xy = new Vector2(0.5f);
|
||||||
|
|
||||||
var (stackObject, stack) = UiPrefabs.CreateStackUi(parScene,
|
var (stackObject, stack) = UiPrefabs.CreateStackUi(parScene,
|
||||||
new StackComponent { Offset = new Vector2(0, -1f), Container = parUiContainer, Children = { rulesUi, backUi } });
|
new StackComponent { Offset = new Vector2(0, -1.5f), Container = parUiContainer, Children = { rulesUi, actualRulesUi, backUi } });
|
||||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
stackObject.Transform.Size.Xy = new Vector2(1f, 9f);
|
||||||
|
|
||||||
var (selectorObject, selector) = UiPrefabs.CreateSelectorUi(parScene, new SelectorComponent { Children = { backUi } });
|
var (selectorObject, selector) = UiPrefabs.CreateSelectorUi(parScene, new SelectorComponent { Children = { backUi } });
|
||||||
|
|
||||||
@@ -191,6 +196,7 @@ public static class MainScene
|
|||||||
parScene.AddChild(parentObject, stackObject);
|
parScene.AddChild(parentObject, stackObject);
|
||||||
|
|
||||||
parScene.AddChild(stackObject, rulesObject);
|
parScene.AddChild(stackObject, rulesObject);
|
||||||
|
parScene.AddChild(stackObject, actualRulesObject);
|
||||||
parScene.AddChild(stackObject, backUiObject);
|
parScene.AddChild(stackObject, backUiObject);
|
||||||
|
|
||||||
return parentObject;
|
return parentObject;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public static class PlayerPrefab
|
|||||||
perspectiveCameraObject.Transform.Translation.Z = 2;
|
perspectiveCameraObject.Transform.Translation.Z = 2;
|
||||||
var playerObject = GameObjectUtil.CreateGameObject(parScene, [
|
var playerObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||||
new RigidbodyComponent(),
|
new RigidbodyComponent(),
|
||||||
new DragComponent { Drag = 10f, RotationalDrag = 10f, },
|
new DragComponent { Drag = 10f, RotationalDrag = 20f, },
|
||||||
|
|
||||||
new AABBColliderComponent
|
new AABBColliderComponent
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ public static class UiPrefabs
|
|||||||
|
|
||||||
public static (GameObject, UiContainerComponent, (GameObject, TextRenderer)) CreateTextUi(Engine.Scene.Scene parScene,
|
public static (GameObject, UiContainerComponent, (GameObject, TextRenderer)) CreateTextUi(Engine.Scene.Scene parScene,
|
||||||
UiContainerComponent parContainer,
|
UiContainerComponent parContainer,
|
||||||
Font parFont, string parText, Align parAlign = Align.Center,
|
Font? parFont, string parText, Align parAlign = Align.Center,
|
||||||
RenderLayer? parRenderLayer = null, float parScale = 1)
|
RenderLayer? parRenderLayer = null, float parScale = 1)
|
||||||
{
|
{
|
||||||
var size = parFont.Measure(parText);
|
var size = parFont?.Measure(parText) ?? Vector2.Zero;
|
||||||
var outerObject = new GameObject
|
var outerObject = new GameObject
|
||||||
{
|
{
|
||||||
Transform =
|
Transform =
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="TestUtil"/>
|
||||||
<InternalsVisibleTo Include="EngineTests"/>
|
<InternalsVisibleTo Include="EngineTests"/>
|
||||||
<InternalsVisibleTo Include="PresenterConsole"/>
|
<InternalsVisibleTo Include="PresenterConsole"/>
|
||||||
<InternalsVisibleTo Include="PresenterWpf"/>
|
<InternalsVisibleTo Include="PresenterWpf"/>
|
||||||
|
|||||||
41
Engine/src/Context/EngineContext.cs
Normal file
41
Engine/src/Context/EngineContext.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using Engine.Graphics;
|
||||||
|
using Engine.Input;
|
||||||
|
using Engine.Resource;
|
||||||
|
using Engine.Scene;
|
||||||
|
|
||||||
|
namespace Engine.Context;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A context for the engine, providing access to the engine's services.
|
||||||
|
/// </summary>
|
||||||
|
public class EngineContext : IContext
|
||||||
|
{
|
||||||
|
public IInputHandler InputHandler => _engine.InputHandler!;
|
||||||
|
|
||||||
|
public IResourceManager AssetResourceManager => _engine.AssetResourceManager;
|
||||||
|
|
||||||
|
public ISceneManager SceneManager => _engine.SceneManager;
|
||||||
|
|
||||||
|
public IRenderer Renderer => _engine.Renderer;
|
||||||
|
|
||||||
|
public string DataFolder => _engine.DataFolder;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The engine instance associated with this context.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Engine _engine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="EngineContext"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parEngine">The engine instance to use for this context.</param>
|
||||||
|
public EngineContext(Engine parEngine)
|
||||||
|
{
|
||||||
|
_engine = parEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
_engine.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Engine/src/Context/IContext.cs
Normal file
42
Engine/src/Context/IContext.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using Engine.Graphics;
|
||||||
|
using Engine.Input;
|
||||||
|
using Engine.Resource;
|
||||||
|
using Engine.Scene;
|
||||||
|
|
||||||
|
namespace Engine.Context;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an interface for the engine's context, providing access to the engine's services.
|
||||||
|
/// </summary>
|
||||||
|
public interface IContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The input handler for the engine.
|
||||||
|
/// </summary>
|
||||||
|
public IInputHandler InputHandler { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The resource manager for the engine.
|
||||||
|
/// </summary>
|
||||||
|
public IResourceManager AssetResourceManager { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The scene manager for the engine.
|
||||||
|
/// </summary>
|
||||||
|
public ISceneManager SceneManager { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The renderer for the engine.
|
||||||
|
/// </summary>
|
||||||
|
public IRenderer Renderer { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The data folder for the engine.
|
||||||
|
/// </summary>
|
||||||
|
public string DataFolder { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes the engine, shutting down any running systems and freeing resources.
|
||||||
|
/// </summary>
|
||||||
|
public void Close();
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using System.Text;
|
|||||||
using Engine.Asset;
|
using Engine.Asset;
|
||||||
using Engine.Asset.Font;
|
using Engine.Asset.Font;
|
||||||
using Engine.Asset.Mesh;
|
using Engine.Asset.Mesh;
|
||||||
|
using Engine.Context;
|
||||||
using Engine.Graphics;
|
using Engine.Graphics;
|
||||||
using Engine.Graphics.Pipeline;
|
using Engine.Graphics.Pipeline;
|
||||||
using Engine.Graphics.Pixel;
|
using Engine.Graphics.Pixel;
|
||||||
@@ -12,6 +13,7 @@ using Engine.Input;
|
|||||||
using Engine.Resource;
|
using Engine.Resource;
|
||||||
using Engine.Resource.Loader;
|
using Engine.Resource.Loader;
|
||||||
using Engine.Scene;
|
using Engine.Scene;
|
||||||
|
using Engine.Util;
|
||||||
using OpenTK.Mathematics;
|
using OpenTK.Mathematics;
|
||||||
using OpenTK.Windowing.Common;
|
using OpenTK.Windowing.Common;
|
||||||
using OpenTK.Windowing.Desktop;
|
using OpenTK.Windowing.Desktop;
|
||||||
@@ -33,7 +35,7 @@ public sealed class Engine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scene manager for managing and updating scenes.
|
/// The scene manager for managing and updating scenes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SceneManager SceneManager { get; } = new();
|
public ISceneManager SceneManager => _sceneManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The resource manager responsible for asset management.
|
/// The resource manager responsible for asset management.
|
||||||
@@ -87,6 +89,11 @@ public sealed class Engine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal Window Window { get; }
|
internal Window Window { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The scene manager for managing and updating scenes.
|
||||||
|
/// </summary>
|
||||||
|
private readonly SceneManager _sceneManager = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The logger instance used by the engine.
|
/// The logger instance used by the engine.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -154,6 +161,8 @@ public sealed class Engine
|
|||||||
|
|
||||||
Renderer = new Renderer(this, parWidth, parHeight, settings);
|
Renderer = new Renderer(this, parWidth, parHeight, settings);
|
||||||
Window = new Window(this, Renderer.NativeWindow, parHeadless);
|
Window = new Window(this, Renderer.NativeWindow, parHeadless);
|
||||||
|
|
||||||
|
EngineUtil.SetContext(new EngineContext(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -250,7 +259,7 @@ public sealed class Engine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneManager.Render();
|
_sceneManager.Render();
|
||||||
}
|
}
|
||||||
Monitor.Exit(_sceneLock);
|
Monitor.Exit(_sceneLock);
|
||||||
|
|
||||||
@@ -283,7 +292,7 @@ public sealed class Engine
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SceneManager.Update(deltaTime);
|
_sceneManager.Update(deltaTime);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,22 +10,22 @@ namespace Engine.Graphics;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A generic renderer that supports rendering quads, meshes, and text.
|
/// A generic renderer that supports rendering quads, meshes, and text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GenericRenderer : IRenderer
|
public class GenericRenderer : IGenericRenderer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides functionality to render quads.
|
/// Provides functionality to render quads.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QuadRenderer QuadRenderer => _quadRenderer ??= new QuadRenderer(_engine, 1024 * 8);
|
public IQuadRenderer QuadRenderer => _quadRenderer ??= new QuadRenderer(_engine, 1024 * 8);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides functionality to render any type of mesh.
|
/// Provides functionality to render any type of mesh.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AnyMeshRenderer AnyMeshRenderer => _anyMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
|
public IAnyMeshRenderer AnyMeshRenderer => _anyMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides functionality to render text.
|
/// Provides functionality to render text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TextRenderer TextRenderer => _textRenderer ??= new TextRenderer(_engine, 1024 * 8);
|
public ITextRenderer TextRenderer => _textRenderer ??= new TextRenderer(_engine, 1024 * 8);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The framebuffer used for rendering.
|
/// The framebuffer used for rendering.
|
||||||
@@ -73,13 +73,19 @@ public class GenericRenderer : IRenderer
|
|||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <summary>
|
||||||
|
/// Prepares the renderer for a new frame.
|
||||||
|
/// </summary>
|
||||||
public void StartFrame()
|
public void StartFrame()
|
||||||
{
|
{
|
||||||
_frameStarted = true;
|
_frameStarted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <summary>
|
||||||
|
/// Finalizes the rendering pipeline for the current frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parProjectionMatrix">The projection matrix to use for rendering.</param>
|
||||||
|
/// <param name="parViewMatrix">The view matrix to use for rendering.</param>
|
||||||
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
||||||
{
|
{
|
||||||
if (!_frameStarted)
|
if (!_frameStarted)
|
||||||
@@ -115,7 +121,11 @@ public class GenericRenderer : IRenderer
|
|||||||
_frameStarted = false;
|
_frameStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <summary>
|
||||||
|
/// Resizes the renderer to accommodate changes in viewport dimensions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The new width of the viewport.</param>
|
||||||
|
/// <param name="parHeight">The new height of the viewport.</param>
|
||||||
public void Resize(int parWidth, int parHeight)
|
public void Resize(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
_framebuffer.Resize(parWidth, parHeight);
|
_framebuffer.Resize(parWidth, parHeight);
|
||||||
|
|||||||
26
Engine/src/Graphics/IGenericRenderer.cs
Normal file
26
Engine/src/Graphics/IGenericRenderer.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Engine.Graphics.Render.Mesh;
|
||||||
|
using Engine.Graphics.Render.Quad;
|
||||||
|
using Engine.Graphics.Render.Text;
|
||||||
|
|
||||||
|
namespace Engine.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface defining the essential functionality for a renderer.
|
||||||
|
/// </summary>
|
||||||
|
public interface IGenericRenderer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality to render quads.
|
||||||
|
/// </summary>
|
||||||
|
public IQuadRenderer QuadRenderer { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality to render any type of mesh.
|
||||||
|
/// </summary>
|
||||||
|
public IAnyMeshRenderer AnyMeshRenderer { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality to render text.
|
||||||
|
/// </summary>
|
||||||
|
public ITextRenderer TextRenderer { get; }
|
||||||
|
}
|
||||||
@@ -1,28 +1,14 @@
|
|||||||
using OpenTK.Mathematics;
|
using Engine.Graphics.Pipeline;
|
||||||
|
|
||||||
namespace Engine.Graphics;
|
namespace Engine.Graphics;
|
||||||
|
|
||||||
/// <summary>
|
public interface IRenderer
|
||||||
/// Interface defining the essential functionality for a renderer.
|
|
||||||
/// </summary>
|
|
||||||
internal interface IRenderer
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prepares the renderer for a new frame.
|
/// Retrieves the renderer for the specified render layer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void StartFrame();
|
/// <param name="parRenderLayer">The render layer to retrieve.</param>
|
||||||
|
/// <returns>The <see cref="GenericRenderer"/> for the specified render layer.</returns>
|
||||||
/// <summary>
|
/// <exception cref="InvalidOperationException">Thrown if the render layer does not exist.</exception>
|
||||||
/// Finalizes the rendering pipeline for the current frame.
|
public IGenericRenderer this[RenderLayer parRenderLayer] { get; }
|
||||||
/// </summary>
|
|
||||||
/// <param name="parProjectionMatrix">The projection matrix to use for rendering.</param>
|
|
||||||
/// <param name="parViewMatrix">The view matrix to use for rendering.</param>
|
|
||||||
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resizes the renderer to accommodate changes in viewport dimensions.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parWidth">The new width of the viewport.</param>
|
|
||||||
/// <param name="parHeight">The new height of the viewport.</param>
|
|
||||||
public void Resize(int parWidth, int parHeight);
|
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ namespace Engine.Graphics.Render.Mesh;
|
|||||||
/// A renderer class that manages multiple meshes and delegates rendering to individual mesh renderers.
|
/// A renderer class that manages multiple meshes and delegates rendering to individual mesh renderers.
|
||||||
/// Handles batching of mesh instances and ensures that only the necessary mesh renderers are created.
|
/// Handles batching of mesh instances and ensures that only the necessary mesh renderers are created.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AnyMeshRenderer
|
public class AnyMeshRenderer : IAnyMeshRenderer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A dictionary that maps each mesh to its corresponding <see cref="MeshRenderer"/>.
|
/// A dictionary that maps each mesh to its corresponding <see cref="MeshRenderer"/>.
|
||||||
@@ -40,14 +40,8 @@ public class AnyMeshRenderer
|
|||||||
_program = parEngine.EngineResourceManager.Load<Program>("shader/mesh");
|
_program = parEngine.EngineResourceManager.Load<Program>("shader/mesh");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Commits an instance of a mesh to the renderer, adding it to the render queue with the specified model matrix and optional texture.
|
public void Commit(Asset.Mesh.Mesh parMesh, in Matrix4 parModelMatrix, Texture.Texture? parAlbedo = null)
|
||||||
/// If the mesh is not already being tracked, a new <see cref="MeshRenderer"/> will be created for it.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parMesh">The mesh to render.</param>
|
|
||||||
/// <param name="parModelMatrix">The model transformation matrix to apply to the mesh.</param>
|
|
||||||
/// <param name="parAlbedo">An optional texture to apply to the mesh. If null, no texture is applied.</param>
|
|
||||||
public void Commit(Asset.Mesh.Mesh parMesh, Matrix4 parModelMatrix, Texture.Texture? parAlbedo = null)
|
|
||||||
{
|
{
|
||||||
if (_meshRenderers.TryGetValue(parMesh, out var meshRenderer))
|
if (_meshRenderers.TryGetValue(parMesh, out var meshRenderer))
|
||||||
{
|
{
|
||||||
|
|||||||
18
Engine/src/Graphics/Render/Mesh/IAnyMeshRenderer.cs
Normal file
18
Engine/src/Graphics/Render/Mesh/IAnyMeshRenderer.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Engine.Graphics.Render.Mesh;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an interface for a renderer that can render any type of mesh.
|
||||||
|
/// </summary>
|
||||||
|
public interface IAnyMeshRenderer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Commits an instance of a mesh to the renderer, adding it to the render queue with the specified model matrix and optional texture.
|
||||||
|
/// If the mesh is not already being tracked, a new <see cref="MeshRenderer"/> will be created for it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parMesh">The mesh to render.</param>
|
||||||
|
/// <param name="parModelMatrix">The model transformation matrix to apply to the mesh.</param>
|
||||||
|
/// <param name="parAlbedo">An optional texture to apply to the mesh. If null, no texture is applied.</param>
|
||||||
|
public void Commit(Asset.Mesh.Mesh parMesh, in Matrix4 parModelMatrix, Texture.Texture? parAlbedo = null);
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ public class MeshRenderer : InstancedRenderer<Asset.Mesh.Mesh.Vertex, MeshInstan
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parModelMatrix">The model transformation matrix for this instance.</param>
|
/// <param name="parModelMatrix">The model transformation matrix for this instance.</param>
|
||||||
/// <param name="parTexture">An optional texture to apply to the mesh. If null, no texture is applied.</param>
|
/// <param name="parTexture">An optional texture to apply to the mesh. If null, no texture is applied.</param>
|
||||||
public void Commit(Matrix4 parModelMatrix, Texture.Texture? parTexture = null)
|
public void Commit(in Matrix4 parModelMatrix, Texture.Texture? parTexture = null)
|
||||||
{
|
{
|
||||||
if (_queuedInstanceCount >= _instanceCount)
|
if (_queuedInstanceCount >= _instanceCount)
|
||||||
{
|
{
|
||||||
|
|||||||
17
Engine/src/Graphics/Render/Quad/IQuadRenderer.cs
Normal file
17
Engine/src/Graphics/Render/Quad/IQuadRenderer.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Engine.Graphics.Render.Quad;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an interface for a renderer that can render quads.
|
||||||
|
/// </summary>
|
||||||
|
public interface IQuadRenderer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Commits an instance to the renderer, adding it to the queue with the specified model matrix, color, and optional texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parModelMatrix">The model transformation matrix for this instance.</param>
|
||||||
|
/// <param name="parColor">The color to apply to this instance.</param>
|
||||||
|
/// <param name="parTexture">An optional texture to apply to the quad. If null, no texture is applied.</param>
|
||||||
|
public void Commit(in Matrix4 parModelMatrix, in Vector4 parColor, Texture.Texture? parTexture = null);
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace Engine.Graphics.Render.Quad;
|
|||||||
/// A renderer class for rendering quadrilaterals (quads) using instancing.
|
/// A renderer class for rendering quadrilaterals (quads) using instancing.
|
||||||
/// Supports dynamic texture binding and manages the state for rendering multiple instances of quads.
|
/// Supports dynamic texture binding and manages the state for rendering multiple instances of quads.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVertex>
|
public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVertex>, IQuadRenderer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps textures to texture units with a limit of 16 texture units.
|
/// Maps textures to texture units with a limit of 16 texture units.
|
||||||
@@ -47,12 +47,7 @@ public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVert
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Commits an instance to the renderer, adding it to the queue with the specified model matrix, color, and optional texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parModelMatrix">The model transformation matrix for this instance.</param>
|
|
||||||
/// <param name="parColor">The color to apply to this instance.</param>
|
|
||||||
/// <param name="parTexture">An optional texture to apply to the quad. If null, no texture is applied.</param>
|
|
||||||
public void Commit(in Matrix4 parModelMatrix, in Vector4 parColor, Texture.Texture? parTexture = null)
|
public void Commit(in Matrix4 parModelMatrix, in Vector4 parColor, Texture.Texture? parTexture = null)
|
||||||
{
|
{
|
||||||
if (_queuedInstanceCount >= _instanceCount)
|
if (_queuedInstanceCount >= _instanceCount)
|
||||||
|
|||||||
19
Engine/src/Graphics/Render/Text/ITextRenderer.cs
Normal file
19
Engine/src/Graphics/Render/Text/ITextRenderer.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Engine.Asset.Font;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Engine.Graphics.Render.Text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an interface for a renderer that can render text.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITextRenderer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Commits a string of text to the renderer, creating the necessary glyphs and adding them to the render queue.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parFont">The font to use for rendering the text.</param>
|
||||||
|
/// <param name="parText">The text string to render.</param>
|
||||||
|
/// <param name="parColor">The color to apply to the text.</param>
|
||||||
|
/// <param name="parModelMatrix">The model transformation matrix to apply to the text.</param>
|
||||||
|
public void Commit(Font parFont, string parText, in Vector4 parColor, in Matrix4 parModelMatrix);
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ namespace Engine.Graphics.Render.Text;
|
|||||||
/// A renderer class for rendering text using glyphs from a font atlas.
|
/// A renderer class for rendering text using glyphs from a font atlas.
|
||||||
/// Handles dynamic font rendering with support for textures.
|
/// Handles dynamic font rendering with support for textures.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TextRenderer
|
public class TextRenderer : ITextRenderer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The shader program used for rendering the text.
|
/// The shader program used for rendering the text.
|
||||||
@@ -80,14 +80,8 @@ public class TextRenderer
|
|||||||
_vertexArray.BindVertexBuffer(_glyphVertexBuffer);
|
_vertexArray.BindVertexBuffer(_glyphVertexBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Commits a string of text to the renderer, creating the necessary glyphs and adding them to the render queue.
|
public void Commit(Font parFont, string parText, in Vector4 parColor, in Matrix4 parModelMatrix)
|
||||||
/// </summary>
|
|
||||||
/// <param name="parFont">The font to use for rendering the text.</param>
|
|
||||||
/// <param name="parText">The text string to render.</param>
|
|
||||||
/// <param name="parColor">The color to apply to the text.</param>
|
|
||||||
/// <param name="parModelMatrix">The model transformation matrix to apply to the text.</param>
|
|
||||||
public void Commit(Font parFont, string parText, Vector4 parColor, in Matrix4 parModelMatrix)
|
|
||||||
{
|
{
|
||||||
if (_queuedCharacterCount >= _characterCount)
|
if (_queuedCharacterCount >= _characterCount)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Engine.Graphics;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles the rendering pipeline, manages render layers, and provides tools for rendering graphics in the engine.
|
/// Handles the rendering pipeline, manages render layers, and provides tools for rendering graphics in the engine.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Renderer
|
public class Renderer : IRenderer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The width of the viewport.
|
/// The width of the viewport.
|
||||||
@@ -89,13 +89,8 @@ public class Renderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Retrieves the renderer for the specified render layer.
|
public IGenericRenderer this[RenderLayer parRenderLayer]
|
||||||
/// </summary>
|
|
||||||
/// <param name="parRenderLayer">The render layer to retrieve.</param>
|
|
||||||
/// <returns>The <see cref="GenericRenderer"/> for the specified render layer.</returns>
|
|
||||||
/// <exception cref="InvalidOperationException">Thrown if the render layer does not exist.</exception>
|
|
||||||
public GenericRenderer this[RenderLayer parRenderLayer]
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Engine.Graphics.Pipeline;
|
using Engine.Graphics.Pipeline;
|
||||||
using Engine.Graphics.Texture;
|
using Engine.Graphics.Texture;
|
||||||
|
using Engine.Util;
|
||||||
using OpenTK.Mathematics;
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
||||||
@@ -32,7 +33,7 @@ public class Box2DRenderer : Component
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Render()
|
public override void Render()
|
||||||
{
|
{
|
||||||
Engine.Instance.Renderer[RenderLayer].QuadRenderer
|
EngineUtil.Renderer[RenderLayer].QuadRenderer
|
||||||
.Commit(GameObject.Transform.FullTransformMatrix, Color, Texture);
|
.Commit(GameObject.Transform.FullTransformMatrix, Color, Texture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Engine.Asset.Mesh;
|
using Engine.Asset.Mesh;
|
||||||
using Engine.Graphics.Pipeline;
|
using Engine.Graphics.Pipeline;
|
||||||
using Engine.Graphics.Texture;
|
using Engine.Graphics.Texture;
|
||||||
|
using Engine.Util;
|
||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ public class MeshRenderer : Component
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Render()
|
public override void Render()
|
||||||
{
|
{
|
||||||
Engine.Instance.Renderer[RenderLayer].AnyMeshRenderer
|
EngineUtil.Renderer[RenderLayer].AnyMeshRenderer
|
||||||
.Commit(Mesh, GameObject.Transform.FullTransformMatrix, Albedo);
|
.Commit(Mesh, GameObject.Transform.FullTransformMatrix, Albedo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Engine.Asset.Font;
|
using Engine.Asset.Font;
|
||||||
using Engine.Graphics.Pipeline;
|
using Engine.Graphics.Pipeline;
|
||||||
|
using Engine.Util;
|
||||||
using OpenTK.Mathematics;
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
||||||
@@ -12,7 +13,7 @@ public class TextRenderer : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The font used for rendering the text.
|
/// The font used for rendering the text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Font Font { get; set; } = null!;
|
public Font? Font { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The color of the text.
|
/// The color of the text.
|
||||||
@@ -37,12 +38,12 @@ public class TextRenderer : Component
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Render()
|
public override void Render()
|
||||||
{
|
{
|
||||||
if (Text == null)
|
if (Text == null || Font == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine.Instance.Renderer[RenderLayer].TextRenderer
|
EngineUtil.Renderer[RenderLayer].TextRenderer
|
||||||
.Commit(Font, Text, Color, GameObject.Transform.FullTransformMatrix);
|
.Commit(Font, Text, Color, GameObject.Transform.FullTransformMatrix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
18
Engine/src/Scene/ISceneManager.cs
Normal file
18
Engine/src/Scene/ISceneManager.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace Engine.Scene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an interface for a scene manager that handles the current scene and scene transitions.
|
||||||
|
/// </summary>
|
||||||
|
public interface ISceneManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The current scene, or null if no scene is active.
|
||||||
|
/// </summary>
|
||||||
|
public Scene? CurrentScene { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transitions to the specified scene.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parScene">The generator function for the scene to transition to.</param>
|
||||||
|
public void TransitionTo(Func<Scene>? parScene);
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ public class Scene : IUpdate, IRender
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time scale for updating the scene. A value of 1.0 means normal speed.
|
/// The time scale for updating the scene. A value of 1.0 means normal speed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float TimeScale { get; set; } = 1.0f;
|
public double TimeScale { get; set; } = 1.0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A hierarchy of game objects in the scene.
|
/// A hierarchy of game objects in the scene.
|
||||||
@@ -206,7 +206,7 @@ public class Scene : IUpdate, IRender
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes changes in the hierarchy and scene actions.
|
/// Processes changes in the hierarchy and scene actions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void ProcessChanges()
|
internal void ProcessChanges()
|
||||||
{
|
{
|
||||||
Hierarchy.ProcessChanges();
|
Hierarchy.ProcessChanges();
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manages the current scene in the game, handles scene transitions, and facilitates updating and rendering the current scene.
|
/// Manages the current scene in the game, handles scene transitions, and facilitates updating and rendering the current scene.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SceneManager : IUpdate, IRender
|
public class SceneManager : ISceneManager, IUpdate, IRender
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current scene being managed by the scene manager.
|
/// The current scene being managed by the scene manager.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Engine.Input;
|
using Engine.Context;
|
||||||
|
using Engine.Graphics;
|
||||||
|
using Engine.Input;
|
||||||
using Engine.Resource;
|
using Engine.Resource;
|
||||||
using Engine.Scene;
|
using Engine.Scene;
|
||||||
|
|
||||||
@@ -13,38 +15,47 @@ public static class EngineUtil
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The engine's input handler, which processes user input.
|
/// The engine's input handler, which processes user input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IInputHandler InputHandler => Engine.Instance.InputHandler!;
|
public static IInputHandler InputHandler => CONTEXT_INSTANCE.InputHandler;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The engine's scene manager, which handles the current scene and scene transitions.
|
/// The engine's scene manager, which handles the current scene and scene transitions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static SceneManager SceneManager => Engine.Instance.SceneManager;
|
public static ISceneManager SceneManager => CONTEXT_INSTANCE.SceneManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The engine's asset resource manager, which handles loading and caching of assets.
|
/// The engine's asset resource manager, which handles loading and caching of assets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IResourceManager AssetResourceManager => Engine.Instance.AssetResourceManager;
|
public static IResourceManager AssetResourceManager => CONTEXT_INSTANCE.AssetResourceManager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The engine's renderer, which handles rendering operations.
|
||||||
|
/// </summary>
|
||||||
|
public static IRenderer Renderer => CONTEXT_INSTANCE.Renderer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The engine's data folder, which contains assets and other data files.
|
/// The engine's data folder, which contains assets and other data files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string DataFolder => Engine.Instance.DataFolder;
|
public static string DataFolder => CONTEXT_INSTANCE.DataFolder;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a game object and adds it to the current scene.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parGameObject">The game object to be added to the scene.</param>
|
|
||||||
public static void CreateObject(GameObject parGameObject)
|
|
||||||
{
|
|
||||||
var scene = Engine.Instance.SceneManager.CurrentScene!;
|
|
||||||
scene.Add(parGameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Closes the engine, shutting down any running systems and freeing resources.
|
/// Closes the engine, shutting down any running systems and freeing resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void Close()
|
public static void Close()
|
||||||
{
|
{
|
||||||
Engine.Instance.Close();
|
CONTEXT_INSTANCE.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The engine's context, which provides access to the engine's services.
|
||||||
|
/// </summary>
|
||||||
|
private static IContext CONTEXT_INSTANCE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the engine's context, allowing access to the engine's services.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parContext">The context to use for the engine.</param>
|
||||||
|
internal static void SetContext(IContext parContext)
|
||||||
|
{
|
||||||
|
CONTEXT_INSTANCE = parContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,10 +23,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Engine\Engine.csproj"/>
|
<ProjectReference Include="..\Engine\Engine.csproj"/>
|
||||||
</ItemGroup>
|
<ProjectReference Include="..\TestUtil\TestUtil.csproj" />
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="src\"/>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -68,4 +68,220 @@ public class GameObjectTests
|
|||||||
|
|
||||||
Assert.Throws<ArgumentException>(() => _gameObject.ProcessChanges());
|
Assert.Throws<ArgumentException>(() => _gameObject.ProcessChanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldSetGameObjectForAddedComponent()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(testComponent.GameObject, Is.EqualTo(_gameObject));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldCallAwakeOnAddedComponentOnPreUpdate()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
var wasAwakeCalled = false;
|
||||||
|
testComponent.OnAwake += () => wasAwakeCalled = true;
|
||||||
|
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.PreUpdate(0);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(wasAwakeCalled, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldCallStartOnAddedComponentOnPreUpdate()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
var wasStartCalled = false;
|
||||||
|
testComponent.OnStart += () => wasStartCalled = true;
|
||||||
|
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.PreUpdate(0);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(wasStartCalled, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldCallPreUpdateOnAddedComponentOnPreUpdate()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
var wasPreUpdateCalled = false;
|
||||||
|
testComponent.OnPreUpdate += _ => wasPreUpdateCalled = true;
|
||||||
|
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.PreUpdate(0);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(wasPreUpdateCalled, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldCallUpdateOnAddedComponentOnUpdate()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
var wasUpdateCalled = false;
|
||||||
|
testComponent.OnUpdate += _ => wasUpdateCalled = true;
|
||||||
|
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.Update(0);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(wasUpdateCalled, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldCallPostUpdateOnAddedComponentOnPostUpdate()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
var wasPostUpdateCalled = false;
|
||||||
|
testComponent.OnPostUpdate += _ => wasPostUpdateCalled = true;
|
||||||
|
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.PostUpdate(0);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(wasPostUpdateCalled, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldCallRenderOnAddedComponentOnRender()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
var wasRenderCalled = false;
|
||||||
|
testComponent.OnRender += () => wasRenderCalled = true;
|
||||||
|
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.Render();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(wasRenderCalled, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldCallDestroyOnAddedComponentOnDestroy()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
var wasDestroyCalled = false;
|
||||||
|
testComponent.OnDestroy += () => wasDestroyCalled = true;
|
||||||
|
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.Destroy();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(wasDestroyCalled, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldCallDisableOnAddedComponentOnDisable()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
var wasDisableCalled = false;
|
||||||
|
testComponent.OnDisable += () => wasDisableCalled = true;
|
||||||
|
|
||||||
|
_gameObject.IsEnabled = false;
|
||||||
|
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.Update(0);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(wasDisableCalled, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddComponent_ShouldCallEnableOnAddedComponentOnEnable()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
var wasEnableCalled = false;
|
||||||
|
testComponent.OnEnable += () => wasEnableCalled = true;
|
||||||
|
|
||||||
|
_gameObject.IsEnabled = false;
|
||||||
|
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.Update(0);
|
||||||
|
|
||||||
|
_gameObject.IsEnabled = true;
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
_gameObject.Update(0);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(wasEnableCalled, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RemoveComponent_ShouldThrowIfComponentIsTransform()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentException>(() => _gameObject.RemoveComponent<Transform>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RemoveComponent_ShouldThrowIfComponentDoesNotExist()
|
||||||
|
{
|
||||||
|
_gameObject.RemoveComponent<TestComponent>();
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(() => _gameObject.ProcessChanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetComponent_ShouldReturnComponentIfExists()
|
||||||
|
{
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
_gameObject.AddComponent(testComponent);
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
|
||||||
|
var component = _gameObject.GetComponent<TestComponent>();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(component, Is.EqualTo(testComponent));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetComponent_ShouldReturnNullIfComponentDoesNotExist()
|
||||||
|
{
|
||||||
|
_gameObject.ProcessChanges();
|
||||||
|
|
||||||
|
var component = _gameObject.GetComponent<TestComponent>();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(component, Is.Null);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
199
EngineTests/src/Scene/SceneTests.cs
Normal file
199
EngineTests/src/Scene/SceneTests.cs
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
using Engine.Scene;
|
||||||
|
|
||||||
|
namespace EngineTests.Scene;
|
||||||
|
|
||||||
|
public class SceneTests
|
||||||
|
{
|
||||||
|
private Engine.Scene.Scene _scene;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_scene = new Engine.Scene.Scene();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Enter_ShouldSetSceneToPlaying()
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(_scene.IsPlaying, Is.False);
|
||||||
|
});
|
||||||
|
|
||||||
|
_scene.Enter();
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(_scene.IsPlaying, Is.True);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Exit_ShouldUnsetSceneToPlaying()
|
||||||
|
{
|
||||||
|
_scene.Enter();
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(_scene.IsPlaying, Is.True);
|
||||||
|
});
|
||||||
|
|
||||||
|
_scene.Exit();
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(_scene.IsPlaying, Is.False);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Update_ShouldRespectTimeScale()
|
||||||
|
{
|
||||||
|
const double INITIAL_DELTA_TIME = 1.0;
|
||||||
|
const double TIME_SCALE = 2.0;
|
||||||
|
|
||||||
|
var (gameObject, testComponent) = CreateTestGameObject();
|
||||||
|
var actualDeltaTime = 0.0;
|
||||||
|
testComponent.OnUpdate += parDeltaTime => actualDeltaTime = parDeltaTime;
|
||||||
|
|
||||||
|
_scene.Add(gameObject);
|
||||||
|
_scene.Enter();
|
||||||
|
|
||||||
|
_scene.TimeScale = TIME_SCALE;
|
||||||
|
_scene.Update(INITIAL_DELTA_TIME);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(actualDeltaTime, Is.EqualTo(INITIAL_DELTA_TIME * TIME_SCALE));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Add_ShouldSetSceneForAddedGameObject()
|
||||||
|
{
|
||||||
|
var (gameObject, _) = CreateTestGameObject();
|
||||||
|
_scene.Add(gameObject);
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(gameObject.Scene, Is.EqualTo(_scene));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Add_ShouldAddGameObjectToHierarchy()
|
||||||
|
{
|
||||||
|
var (gameObject, _) = CreateTestGameObject();
|
||||||
|
_scene.Add(gameObject);
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(_scene.Hierarchy.Objects, Contains.Item(gameObject));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Remove_ShouldUnsetSceneForRemovedGameObject()
|
||||||
|
{
|
||||||
|
var (gameObject, _) = CreateTestGameObject();
|
||||||
|
_scene.Add(gameObject);
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
_scene.Remove(gameObject);
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(gameObject.Scene, Is.Null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Remove_ShouldRemoveGameObjectFromHierarchy()
|
||||||
|
{
|
||||||
|
var (gameObject, _) = CreateTestGameObject();
|
||||||
|
_scene.Add(gameObject);
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
_scene.Remove(gameObject);
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(_scene.Hierarchy.Objects, Is.Not.Contains(gameObject));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void FindAllComponents_ShouldReturnAllComponentsCount1()
|
||||||
|
{
|
||||||
|
var (gameObject, testComponent) = CreateTestGameObject();
|
||||||
|
_scene.Add(gameObject);
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
var components = _scene.FindAllComponents<TestComponent>();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(components, Contains.Item(testComponent));
|
||||||
|
Assert.That(components, Has.Count.EqualTo(1));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void FindAllComponents_ShouldReturnAllComponentsCount2()
|
||||||
|
{
|
||||||
|
var (gameObject1, testComponent1) = CreateTestGameObject();
|
||||||
|
var (gameObject2, testComponent2) = CreateTestGameObject();
|
||||||
|
_scene.Add(gameObject1);
|
||||||
|
_scene.Add(gameObject2);
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
var components = _scene.FindAllComponents<TestComponent>();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(components, Contains.Item(testComponent1));
|
||||||
|
Assert.That(components, Contains.Item(testComponent2));
|
||||||
|
Assert.That(components, Has.Count.EqualTo(2));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void FindFirstComponent_ShouldReturnFirstComponent()
|
||||||
|
{
|
||||||
|
var (gameObject, testComponent) = CreateTestGameObject();
|
||||||
|
_scene.Add(gameObject);
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
var component = _scene.FindFirstComponent<TestComponent>();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(component, Is.EqualTo(testComponent));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void FindFirstComponent_ShouldReturnNullIfComponentDoesNotExist()
|
||||||
|
{
|
||||||
|
_scene.ProcessChanges();
|
||||||
|
|
||||||
|
var component = _scene.FindFirstComponent<TestComponent>();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(component, Is.Null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (GameObject, TestComponent) CreateTestGameObject()
|
||||||
|
{
|
||||||
|
var gameObject = new GameObject();
|
||||||
|
var testComponent = new TestComponent();
|
||||||
|
|
||||||
|
gameObject.AddComponent(testComponent);
|
||||||
|
gameObject.ProcessChanges();
|
||||||
|
|
||||||
|
return (gameObject, testComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
EngineTests/src/Scene/TestComponent.cs
Normal file
61
EngineTests/src/Scene/TestComponent.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using Engine.Scene.Component;
|
||||||
|
|
||||||
|
namespace EngineTests.Scene;
|
||||||
|
|
||||||
|
public class TestComponent : Component
|
||||||
|
{
|
||||||
|
public event Action? OnAwake;
|
||||||
|
public event Action? OnStart;
|
||||||
|
public event Action<double>? OnPreUpdate;
|
||||||
|
public event Action<double>? OnUpdate;
|
||||||
|
public event Action<double>? OnPostUpdate;
|
||||||
|
public event Action? OnRender;
|
||||||
|
public event Action? OnDestroy;
|
||||||
|
public event Action? OnEnable;
|
||||||
|
public event Action? OnDisable;
|
||||||
|
|
||||||
|
public override void Awake()
|
||||||
|
{
|
||||||
|
OnAwake?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Start()
|
||||||
|
{
|
||||||
|
OnStart?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PreUpdate(double parDeltaTime)
|
||||||
|
{
|
||||||
|
OnPreUpdate?.Invoke(parDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(double parDeltaTime)
|
||||||
|
{
|
||||||
|
OnUpdate?.Invoke(parDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostUpdate(double parDeltaTime)
|
||||||
|
{
|
||||||
|
OnPostUpdate?.Invoke(parDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Render()
|
||||||
|
{
|
||||||
|
OnRender?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Destroy()
|
||||||
|
{
|
||||||
|
OnDestroy?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Enable()
|
||||||
|
{
|
||||||
|
OnEnable?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disable()
|
||||||
|
{
|
||||||
|
OnDisable?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
30
GameTests/GameTests.csproj
Normal file
30
GameTests/GameTests.csproj
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
|
||||||
|
<PackageReference Include="NUnit" Version="3.14.0"/>
|
||||||
|
<PackageReference Include="NUnit.Analyzers" Version="3.9.0"/>
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="NUnit.Framework"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DoomDeathmatch\DoomDeathmatch.csproj" />
|
||||||
|
<ProjectReference Include="..\Engine\Engine.csproj" />
|
||||||
|
<ProjectReference Include="..\TestUtil\TestUtil.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
52
GameTests/src/HealthControllerTest.cs
Normal file
52
GameTests/src/HealthControllerTest.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using DoomDeathmatch;
|
||||||
|
using DoomDeathmatch.Component.MVC.Health;
|
||||||
|
using DoomDeathmatch.Component.MVC.Weapon;
|
||||||
|
|
||||||
|
namespace GameTests;
|
||||||
|
|
||||||
|
public class HealthControllerTest : SceneMockTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void HealthController_HealthShouldBeCappedToMaxHealthWhenHealed()
|
||||||
|
{
|
||||||
|
const float MAX_HEALTH = 100;
|
||||||
|
|
||||||
|
var healthController = new HealthController(MAX_HEALTH);
|
||||||
|
var gameObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
healthController
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(gameObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
healthController.Heal(MAX_HEALTH * 2);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(healthController.Health, Is.EqualTo(MAX_HEALTH));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void HealthController_HealthShouldBeCappedToZeroWhenHealedBelowZero()
|
||||||
|
{
|
||||||
|
const float MAX_HEALTH = 100;
|
||||||
|
|
||||||
|
var healthController = new HealthController(MAX_HEALTH);
|
||||||
|
var gameObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
healthController
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(gameObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
healthController.TakeDamage(MAX_HEALTH * 2);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(healthController.Health, Is.EqualTo(0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
154
GameTests/src/MovementComponentTest.cs
Normal file
154
GameTests/src/MovementComponentTest.cs
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
using DoomDeathmatch;
|
||||||
|
using DoomDeathmatch.Component.MVC;
|
||||||
|
using DoomDeathmatch.Component.Physics;
|
||||||
|
using DoomDeathmatch.Component.Util;
|
||||||
|
using DoomDeathmatch.Scene.Play.Prefab;
|
||||||
|
using Engine.Input;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace GameTests;
|
||||||
|
|
||||||
|
public class MovementComponentTest : SceneMockTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void PlayerMovement_ShouldMovePlayerLeftWithKeyboardA()
|
||||||
|
{
|
||||||
|
var playerMovementComponent = new PlayerMovementComponent();
|
||||||
|
var movementComponent = new MovementComponent();
|
||||||
|
var playerObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
new RigidbodyComponent(),
|
||||||
|
movementComponent,
|
||||||
|
playerMovementComponent
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(playerObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
InputHandler.PressedKeyboardButtons.Add(KeyboardButtonCode.A);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(playerObject.Transform.Translation.X, Is.EqualTo(-movementComponent.Speed));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PlayerMovement_ShouldMovePlayerRightWithKeyboardD()
|
||||||
|
{
|
||||||
|
var playerMovementComponent = new PlayerMovementComponent();
|
||||||
|
var movementComponent = new MovementComponent();
|
||||||
|
var playerObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
new RigidbodyComponent(),
|
||||||
|
movementComponent,
|
||||||
|
playerMovementComponent
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(playerObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
InputHandler.PressedKeyboardButtons.Add(KeyboardButtonCode.D);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(playerObject.Transform.Translation.X, Is.EqualTo(movementComponent.Speed));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PlayerMovement_ShouldMovePlayerUpWithKeyboardW()
|
||||||
|
{
|
||||||
|
var playerMovementComponent = new PlayerMovementComponent();
|
||||||
|
var movementComponent = new MovementComponent();
|
||||||
|
var playerObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
new RigidbodyComponent(),
|
||||||
|
movementComponent,
|
||||||
|
playerMovementComponent
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(playerObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
InputHandler.PressedKeyboardButtons.Add(KeyboardButtonCode.W);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(playerObject.Transform.Translation.Y, Is.EqualTo(movementComponent.Speed));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PlayerMovement_ShouldMovePlayerDownWithKeyboardS()
|
||||||
|
{
|
||||||
|
var playerMovementComponent = new PlayerMovementComponent();
|
||||||
|
var movementComponent = new MovementComponent();
|
||||||
|
var playerObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
new RigidbodyComponent(),
|
||||||
|
movementComponent,
|
||||||
|
playerMovementComponent
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(playerObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
InputHandler.PressedKeyboardButtons.Add(KeyboardButtonCode.S);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(playerObject.Transform.Translation.Y, Is.EqualTo(-movementComponent.Speed));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PlayerMovement_ShouldRotatePlayerRightWithKeyboardQ()
|
||||||
|
{
|
||||||
|
var playerMovementComponent = new PlayerMovementComponent();
|
||||||
|
var movementComponent = new MovementComponent();
|
||||||
|
var playerObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
new RigidbodyComponent(),
|
||||||
|
movementComponent,
|
||||||
|
playerMovementComponent
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(playerObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
InputHandler.PressedKeyboardButtons.Add(KeyboardButtonCode.Q);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Console.WriteLine(playerObject.Transform.Rotation.ToEulerAngles());
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(playerObject.Transform.Rotation.ToEulerAngles().Z,
|
||||||
|
Is.EqualTo(MathHelper.DegreesToRadians(movementComponent.RotationSpeed)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PlayerMovement_ShouldRotatePlayerRightWithKeyboardE()
|
||||||
|
{
|
||||||
|
var playerMovementComponent = new PlayerMovementComponent();
|
||||||
|
var movementComponent = new MovementComponent();
|
||||||
|
var playerObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
new RigidbodyComponent(),
|
||||||
|
movementComponent,
|
||||||
|
playerMovementComponent
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(playerObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
InputHandler.PressedKeyboardButtons.Add(KeyboardButtonCode.E);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(playerObject.Transform.Rotation.ToEulerAngles().Z,
|
||||||
|
Is.EqualTo(-MathHelper.DegreesToRadians(movementComponent.RotationSpeed)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
51
GameTests/src/SceneMockTest.cs
Normal file
51
GameTests/src/SceneMockTest.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Engine.Graphics;
|
||||||
|
using Engine.Graphics.Pipeline;
|
||||||
|
using Engine.Scene;
|
||||||
|
using TestUtil;
|
||||||
|
using TestUtil.Renderer;
|
||||||
|
|
||||||
|
namespace GameTests;
|
||||||
|
|
||||||
|
public abstract class SceneMockTest
|
||||||
|
{
|
||||||
|
public Scene Scene => _scene;
|
||||||
|
public MockContext Context => _context;
|
||||||
|
public MockInputHandler InputHandler => _inputHandler;
|
||||||
|
public MockResourceManager ResourceManager => _resourceManager;
|
||||||
|
public SceneManager SceneManager => _sceneManager;
|
||||||
|
public MockRenderer Renderer => _renderer;
|
||||||
|
|
||||||
|
private Scene _scene;
|
||||||
|
|
||||||
|
private MockContext _context;
|
||||||
|
private MockInputHandler _inputHandler;
|
||||||
|
private MockResourceManager _resourceManager;
|
||||||
|
private SceneManager _sceneManager;
|
||||||
|
private MockRenderer _renderer;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
_scene = new Scene();
|
||||||
|
|
||||||
|
_inputHandler = new MockInputHandler();
|
||||||
|
_resourceManager = new MockResourceManager();
|
||||||
|
_sceneManager = new SceneManager();
|
||||||
|
_renderer = new MockRenderer(new Dictionary<RenderLayer, IGenericRenderer>
|
||||||
|
{
|
||||||
|
[RenderLayer.DEFAULT] = new MockGenericRenderer(new MockQuadRenderer(), new MockAnyMeshRenderer(),
|
||||||
|
new MockTextRenderer()),
|
||||||
|
[RenderLayer.OVERLAY] = new MockGenericRenderer(new MockQuadRenderer(), new MockAnyMeshRenderer(),
|
||||||
|
new MockTextRenderer()),
|
||||||
|
[RenderLayer.HUD] = new MockGenericRenderer(new MockQuadRenderer(), new MockAnyMeshRenderer(),
|
||||||
|
new MockTextRenderer())
|
||||||
|
});
|
||||||
|
|
||||||
|
_context = new MockContext(_inputHandler, _resourceManager, _sceneManager, _renderer, "");
|
||||||
|
|
||||||
|
MockContext.SetMockContext(_context);
|
||||||
|
|
||||||
|
_sceneManager.TransitionTo(() => Scene);
|
||||||
|
_sceneManager.Update(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
104
GameTests/src/WeaponControllerTest.cs
Normal file
104
GameTests/src/WeaponControllerTest.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using DoomDeathmatch;
|
||||||
|
using DoomDeathmatch.Component.MVC.Weapon;
|
||||||
|
using DoomDeathmatch.Script.Model.Weapon;
|
||||||
|
|
||||||
|
namespace GameTests;
|
||||||
|
|
||||||
|
public class WeaponControllerTest : SceneMockTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void WeaponController_DefaultWeaponShouldBeSelected()
|
||||||
|
{
|
||||||
|
var weapon = WeaponData.Pistol;
|
||||||
|
var weaponController = new WeaponController(weapon);
|
||||||
|
var gameObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
weaponController
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(gameObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
weaponController.SelectWeapon(0);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(weaponController.WeaponData, Is.EqualTo(weapon));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WeaponController_CannotRemoveDefaultWeapon()
|
||||||
|
{
|
||||||
|
var weapon = WeaponData.Pistol;
|
||||||
|
var weaponController = new WeaponController(weapon);
|
||||||
|
var gameObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
weaponController
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(gameObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
weaponController.RemoveWeapon(0);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(weaponController.WeaponData, Is.EqualTo(weapon));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WeaponController_CannotSelectNonExistentWeapon()
|
||||||
|
{
|
||||||
|
var weapon = WeaponData.Pistol;
|
||||||
|
var weaponController = new WeaponController(weapon);
|
||||||
|
var gameObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
weaponController
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(gameObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
weaponController.SelectWeapon(1);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(weaponController.WeaponData, Is.EqualTo(weapon));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WeaponController_ShouldAddAmmoOnWeaponMerge()
|
||||||
|
{
|
||||||
|
const int INITIAL_AMMO1 = 10;
|
||||||
|
const int INITIAL_AMMO2 = 10;
|
||||||
|
|
||||||
|
var weapon1 = WeaponData.Pistol;
|
||||||
|
var weapon2 = WeaponData.Pistol;
|
||||||
|
weapon1.Ammo = INITIAL_AMMO1;
|
||||||
|
weapon2.Ammo = INITIAL_AMMO2;
|
||||||
|
|
||||||
|
var weaponController = new WeaponController(weapon1);
|
||||||
|
var gameObject = GameObjectUtil.CreateGameObject(Scene, [
|
||||||
|
weaponController
|
||||||
|
]);
|
||||||
|
|
||||||
|
Scene.Add(gameObject);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(weaponController.WeaponData.Ammo, Is.EqualTo(INITIAL_AMMO1));
|
||||||
|
});
|
||||||
|
|
||||||
|
weaponController.AddOrMergeWeapon(weapon2);
|
||||||
|
SceneManager.Update(1);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(weaponController.WeaponData.Ammo, Is.EqualTo(INITIAL_AMMO1 + INITIAL_AMMO2));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,36 +81,36 @@ float perceptualColorDistance(vec3 color1, vec3 color2) {
|
|||||||
return dot(delta, delta);
|
return dot(delta, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
//int findMostPerceptuallyAccurateColor(vec3 color) {
|
|
||||||
// int bestMatchIndex = 0;
|
|
||||||
// float minDistance = perceptualColorDistance(color, ConsoleColorVec3[0]);
|
|
||||||
//
|
|
||||||
// for (int i = 1; i < 16; i++) {
|
|
||||||
// float currentDistance = perceptualColorDistance(color, ConsoleColorVec3[i]);
|
|
||||||
// if (currentDistance < minDistance) {
|
|
||||||
// minDistance = currentDistance;
|
|
||||||
// bestMatchIndex = i;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return bestMatchIndex;
|
|
||||||
//}
|
|
||||||
|
|
||||||
int findMostPerceptuallyAccurateColor(vec3 color) {
|
int findMostPerceptuallyAccurateColor(vec3 color) {
|
||||||
int closestIndex = 0;
|
int bestMatchIndex = 0;
|
||||||
float minDistance = distance(color, ConsoleColorVec3[0]);
|
float minDistance = perceptualColorDistance(color, ConsoleColorVec3[0]);
|
||||||
|
|
||||||
for (int i = 1; i < 16; i++) {
|
for (int i = 1; i < 16; i++) {
|
||||||
float dist = distance(color, ConsoleColorVec3[i]);
|
float currentDistance = perceptualColorDistance(color, ConsoleColorVec3[i]);
|
||||||
if (dist < minDistance) {
|
if (currentDistance < minDistance) {
|
||||||
minDistance = dist;
|
minDistance = currentDistance;
|
||||||
closestIndex = i;
|
bestMatchIndex = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return closestIndex;
|
return bestMatchIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//int findMostPerceptuallyAccurateColor(vec3 color) {
|
||||||
|
// int closestIndex = 0;
|
||||||
|
// float minDistance = distance(color, ConsoleColorVec3[0]);
|
||||||
|
//
|
||||||
|
// for (int i = 1; i < 16; i++) {
|
||||||
|
// float dist = distance(color, ConsoleColorVec3[i]);
|
||||||
|
// if (dist < minDistance) {
|
||||||
|
// minDistance = dist;
|
||||||
|
// closestIndex = i;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return closestIndex;
|
||||||
|
//}
|
||||||
|
|
||||||
// Enhanced luminosity calculation considering human perception
|
// Enhanced luminosity calculation considering human perception
|
||||||
float calculatePerceptualLuminance(vec3 color) {
|
float calculatePerceptualLuminance(vec3 color) {
|
||||||
// BT.709 luminance coefficients with slight adjustment
|
// BT.709 luminance coefficients with slight adjustment
|
||||||
|
|||||||
13
TestUtil/TestUtil.csproj
Normal file
13
TestUtil/TestUtil.csproj
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Engine\Engine.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
40
TestUtil/src/MockContext.cs
Normal file
40
TestUtil/src/MockContext.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using Engine.Context;
|
||||||
|
using Engine.Graphics;
|
||||||
|
using Engine.Input;
|
||||||
|
using Engine.Resource;
|
||||||
|
using Engine.Scene;
|
||||||
|
using Engine.Util;
|
||||||
|
|
||||||
|
namespace TestUtil;
|
||||||
|
|
||||||
|
public class MockContext : IContext
|
||||||
|
{
|
||||||
|
public Action? OnClose;
|
||||||
|
|
||||||
|
public IInputHandler InputHandler { get; }
|
||||||
|
public IResourceManager AssetResourceManager { get; }
|
||||||
|
public ISceneManager SceneManager { get; }
|
||||||
|
public IRenderer Renderer { get; }
|
||||||
|
public string DataFolder { get; }
|
||||||
|
|
||||||
|
public MockContext(IInputHandler parInputHandler, IResourceManager parAssetResourceManager,
|
||||||
|
ISceneManager parSceneManager, IRenderer parRenderer,
|
||||||
|
string parDataFolder)
|
||||||
|
{
|
||||||
|
InputHandler = parInputHandler;
|
||||||
|
AssetResourceManager = parAssetResourceManager;
|
||||||
|
SceneManager = parSceneManager;
|
||||||
|
Renderer = parRenderer;
|
||||||
|
DataFolder = parDataFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetMockContext(MockContext parContext)
|
||||||
|
{
|
||||||
|
EngineUtil.SetContext(parContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
OnClose?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
46
TestUtil/src/MockInputHandler.cs
Normal file
46
TestUtil/src/MockInputHandler.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using Engine.Input;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace TestUtil;
|
||||||
|
|
||||||
|
public class MockInputHandler : IInputHandler
|
||||||
|
{
|
||||||
|
public CultureInfo CurrentInputLanguage { get; set; } = new(1033);
|
||||||
|
|
||||||
|
public Vector2 MousePosition { get; set; } = Vector2.Zero;
|
||||||
|
|
||||||
|
public ISet<KeyboardButtonCode> PressedKeyboardButtons => _pressedKeyboardButtons;
|
||||||
|
public ISet<KeyboardButtonCode> JustPressedKeyboardButtons => _justPressedKeyboardButtons;
|
||||||
|
public ISet<MouseButtonCode> PressedMouseButtons => _pressedMouseButtons;
|
||||||
|
public ISet<MouseButtonCode> JustPressedMouseButtons => _justPressedMouseButtons;
|
||||||
|
|
||||||
|
private readonly HashSet<KeyboardButtonCode> _pressedKeyboardButtons = [];
|
||||||
|
private readonly HashSet<KeyboardButtonCode> _justPressedKeyboardButtons = [];
|
||||||
|
private readonly HashSet<MouseButtonCode> _pressedMouseButtons = [];
|
||||||
|
private readonly HashSet<MouseButtonCode> _justPressedMouseButtons = [];
|
||||||
|
|
||||||
|
public void Update(double parDeltaTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
|
{
|
||||||
|
return _pressedKeyboardButtons.Contains(parKeyboardButtonCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
|
{
|
||||||
|
return _justPressedKeyboardButtons.Contains(parKeyboardButtonCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
|
||||||
|
{
|
||||||
|
return _pressedMouseButtons.Contains(parButtonCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
|
||||||
|
{
|
||||||
|
return _justPressedMouseButtons.Contains(parButtonCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
TestUtil/src/MockResourceManager.cs
Normal file
15
TestUtil/src/MockResourceManager.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Engine.Resource;
|
||||||
|
|
||||||
|
namespace TestUtil;
|
||||||
|
|
||||||
|
public class MockResourceManager : IResourceManager
|
||||||
|
{
|
||||||
|
public event Action<Type, string>? OnLoad;
|
||||||
|
|
||||||
|
public T Load<T>(string parPath) where T : class
|
||||||
|
{
|
||||||
|
OnLoad?.Invoke(typeof(T), parPath);
|
||||||
|
|
||||||
|
return null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
TestUtil/src/Renderer/MockAnyMeshRenderer.cs
Normal file
16
TestUtil/src/Renderer/MockAnyMeshRenderer.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Engine.Asset.Mesh;
|
||||||
|
using Engine.Graphics.Render.Mesh;
|
||||||
|
using Engine.Graphics.Texture;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace TestUtil.Renderer;
|
||||||
|
|
||||||
|
public class MockAnyMeshRenderer : IAnyMeshRenderer
|
||||||
|
{
|
||||||
|
public event Action<Mesh, Matrix4, Texture?>? OnCommit;
|
||||||
|
|
||||||
|
public void Commit(Mesh parMesh, in Matrix4 parModelMatrix, Texture? parAlbedo = null)
|
||||||
|
{
|
||||||
|
OnCommit?.Invoke(parMesh, parModelMatrix, parAlbedo);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
TestUtil/src/Renderer/MockGenericRenderer.cs
Normal file
21
TestUtil/src/Renderer/MockGenericRenderer.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Engine.Graphics;
|
||||||
|
using Engine.Graphics.Render.Mesh;
|
||||||
|
using Engine.Graphics.Render.Quad;
|
||||||
|
using Engine.Graphics.Render.Text;
|
||||||
|
|
||||||
|
namespace TestUtil.Renderer;
|
||||||
|
|
||||||
|
public class MockGenericRenderer : IGenericRenderer
|
||||||
|
{
|
||||||
|
public IQuadRenderer QuadRenderer { get; }
|
||||||
|
public IAnyMeshRenderer AnyMeshRenderer { get; }
|
||||||
|
public ITextRenderer TextRenderer { get; }
|
||||||
|
|
||||||
|
public MockGenericRenderer(IQuadRenderer parQuadRenderer, IAnyMeshRenderer parAnyMeshRenderer,
|
||||||
|
ITextRenderer parTextRenderer)
|
||||||
|
{
|
||||||
|
QuadRenderer = parQuadRenderer;
|
||||||
|
AnyMeshRenderer = parAnyMeshRenderer;
|
||||||
|
TextRenderer = parTextRenderer;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
TestUtil/src/Renderer/MockQuadRenderer.cs
Normal file
15
TestUtil/src/Renderer/MockQuadRenderer.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Engine.Graphics.Render.Quad;
|
||||||
|
using Engine.Graphics.Texture;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace TestUtil.Renderer;
|
||||||
|
|
||||||
|
public class MockQuadRenderer : IQuadRenderer
|
||||||
|
{
|
||||||
|
public event Action<Matrix4, Vector4, Texture?>? OnCommit;
|
||||||
|
|
||||||
|
public void Commit(in Matrix4 parModelMatrix, in Vector4 parColor, Texture? parTexture = null)
|
||||||
|
{
|
||||||
|
OnCommit?.Invoke(parModelMatrix, parColor, parTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
TestUtil/src/Renderer/MockRenderer.cs
Normal file
16
TestUtil/src/Renderer/MockRenderer.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Engine.Graphics;
|
||||||
|
using Engine.Graphics.Pipeline;
|
||||||
|
|
||||||
|
namespace TestUtil.Renderer;
|
||||||
|
|
||||||
|
public class MockRenderer : IRenderer
|
||||||
|
{
|
||||||
|
public IGenericRenderer this[RenderLayer parRenderLayer] => _renderers[parRenderLayer];
|
||||||
|
|
||||||
|
private readonly Dictionary<RenderLayer, IGenericRenderer> _renderers;
|
||||||
|
|
||||||
|
public MockRenderer(Dictionary<RenderLayer, IGenericRenderer> parRenderers)
|
||||||
|
{
|
||||||
|
_renderers = parRenderers;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
TestUtil/src/Renderer/MockTextRenderer.cs
Normal file
15
TestUtil/src/Renderer/MockTextRenderer.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Engine.Asset.Font;
|
||||||
|
using Engine.Graphics.Render.Text;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace TestUtil.Renderer;
|
||||||
|
|
||||||
|
public class MockTextRenderer : ITextRenderer
|
||||||
|
{
|
||||||
|
public event Action<Font, string, Vector4, Matrix4>? OnCommit;
|
||||||
|
|
||||||
|
public void Commit(Font parFont, string parText, in Vector4 parColor, in Matrix4 parModelMatrix)
|
||||||
|
{
|
||||||
|
OnCommit?.Invoke(parFont, parText, parColor, parModelMatrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user