.
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 108 KiB |
BIN
DoomDeathmatch/asset/texture/health_pack.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 28 KiB |
BIN
DoomDeathmatch/asset/texture/pistol/fire1.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
DoomDeathmatch/asset/texture/pistol/fire2.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
DoomDeathmatch/asset/texture/pistol/fire3.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
DoomDeathmatch/asset/texture/pistol/fire4.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
BIN
DoomDeathmatch/asset/texture/shotgun/fire1.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
DoomDeathmatch/asset/texture/shotgun/fire2.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
78
DoomDeathmatch/src/Component/ConsumableComponent.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using DoomDeathmatch.Component.MVC;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using DoomDeathmatch.Script.Consumable;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component;
|
||||
|
||||
public class ConsumableComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
private readonly IConsumable _consumable;
|
||||
private readonly AABBColliderComponent _collider;
|
||||
private readonly Box2DRenderer _box2DRenderer;
|
||||
|
||||
private readonly Random _random = new();
|
||||
|
||||
private MovementController _movementController = null!;
|
||||
|
||||
public ConsumableComponent(IConsumable parConsumable, AABBColliderComponent parCollider,
|
||||
Box2DRenderer parBox2DRenderer)
|
||||
{
|
||||
_consumable = parConsumable;
|
||||
_collider = parCollider;
|
||||
_box2DRenderer = parBox2DRenderer;
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_collider.OnCollision += OnCollision;
|
||||
_box2DRenderer.Texture = EngineUtil.AssetResourceManager.Load<Texture>(_consumable.Icon);
|
||||
|
||||
_movementController = GameObject.GetComponent<MovementController>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_movementController);
|
||||
}
|
||||
|
||||
private void OnCollision(AABBColliderComponent parCollider)
|
||||
{
|
||||
if (parCollider.ColliderGroups.Contains("player"))
|
||||
TryConsume(parCollider);
|
||||
else if (parCollider.ColliderGroups.Contains("consumable"))
|
||||
MoveAway(parCollider);
|
||||
}
|
||||
|
||||
private void TryConsume(AABBColliderComponent parCollider)
|
||||
{
|
||||
var playerController = parCollider.GameObject.GetComponent<PlayerController>();
|
||||
if (playerController == null)
|
||||
return;
|
||||
|
||||
_consumable.Consume(playerController);
|
||||
|
||||
EngineUtil.SceneManager.CurrentScene!.Remove(GameObject);
|
||||
}
|
||||
|
||||
private void MoveAway(AABBColliderComponent parCollider)
|
||||
{
|
||||
var direction = _collider.GameObject.Transform.GetFullTranslation() -
|
||||
parCollider.GameObject.Transform.GetFullTranslation();
|
||||
|
||||
if (direction.LengthSquared <= float.Epsilon)
|
||||
direction = GetRandomDirection();
|
||||
else
|
||||
direction.Normalize();
|
||||
|
||||
_movementController.ApplyMovement(direction);
|
||||
}
|
||||
|
||||
private Vector3 GetRandomDirection()
|
||||
{
|
||||
var x = _random.NextSingle() * 2 - 1;
|
||||
var y = _random.NextSingle() * 2 - 1;
|
||||
|
||||
return new Vector3(x, y, 0).Normalized();
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using Engine.Input;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
public class GameController : Engine.Scene.Component.Component
|
||||
{
|
||||
public bool IsPaused { get; set; } = false;
|
||||
|
||||
public PlayerController PlayerController => _playerController;
|
||||
public ScoreController ScoreController => _scoreController;
|
||||
|
||||
private ScoreController _scoreController = null!;
|
||||
private TimerController _timerController = null!;
|
||||
private PlayerController _playerController = null!;
|
||||
|
||||
private readonly MenuControllerComponent _menuController;
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
|
||||
public GameController(MenuControllerComponent parMenuController)
|
||||
{
|
||||
_menuController = parMenuController;
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_scoreController = GameObject.GetComponent<ScoreController>()!;
|
||||
_timerController = GameObject.GetComponent<TimerController>()!;
|
||||
_playerController = GameObject.Scene!.FindFirstComponent<PlayerController>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_scoreController);
|
||||
ArgumentNullException.ThrowIfNull(_timerController);
|
||||
ArgumentNullException.ThrowIfNull(_playerController);
|
||||
}
|
||||
|
||||
public void Unpause()
|
||||
{
|
||||
GameObject.Scene!.TimeScale = 1.0f;
|
||||
IsPaused = false;
|
||||
_menuController.SelectMenuItem("play");
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
GameObject.Scene!.TimeScale = 0.0f;
|
||||
IsPaused = true;
|
||||
_menuController.SelectMenuItem("escape");
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Escape))
|
||||
{
|
||||
if (IsPaused)
|
||||
{
|
||||
Unpause();
|
||||
}
|
||||
else
|
||||
{
|
||||
Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Script.Model.Enemy;
|
||||
using DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
namespace DoomDeathmatch.Component.MVC.Enemy;
|
||||
|
||||
public class EnemyController : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -24,7 +25,7 @@ public class EnemyController : Engine.Scene.Component.Component
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_gameController = GameObject.Scene!.FindFirstComponent<GameController>()!;
|
||||
_gameController = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<GameController>()!;
|
||||
_healthController = GameObject.GetComponent<HealthController>()!;
|
||||
_enemyView = GameObject.GetComponent<EnemyView>()!;
|
||||
_movementController = GameObject.GetComponent<MovementController>()!;
|
||||
@@ -69,7 +70,7 @@ public class EnemyController : Engine.Scene.Component.Component
|
||||
|
||||
private void OnDeath()
|
||||
{
|
||||
GameObject.Scene!.Remove(GameObject);
|
||||
EngineUtil.SceneManager.CurrentScene!.Remove(GameObject);
|
||||
_gameController.ScoreController.AddScore(_enemyData.BaseScore);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
using DoomDeathmatch.Script.Model.Enemy;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.View;
|
||||
namespace DoomDeathmatch.Component.MVC.Enemy;
|
||||
|
||||
public class EnemyView : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -15,6 +16,6 @@ public class EnemyView : Engine.Scene.Component.Component
|
||||
|
||||
public void UpdateView(EnemyData parEnemyData)
|
||||
{
|
||||
_box2DRenderer.Texture = Engine.Engine.Instance.AssetResourceManager.Load<Texture>(parEnemyData.Texture);
|
||||
_box2DRenderer.Texture = EngineUtil.AssetResourceManager.Load<Texture>(parEnemyData.Texture);
|
||||
}
|
||||
}
|
||||
77
DoomDeathmatch/src/Component/MVC/GameController.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using DoomDeathmatch.Component.MVC.Score;
|
||||
using DoomDeathmatch.Component.MVC.Timer;
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Scene.GameOver;
|
||||
using DoomDeathmatch.Scene.Play;
|
||||
using Engine.Input;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC;
|
||||
|
||||
public class GameController : Engine.Scene.Component.Component
|
||||
{
|
||||
public bool IsPaused { get; private set; } = false;
|
||||
public bool IsGameOver { get; private set; } = false;
|
||||
|
||||
public PlayerController PlayerController { get; private set; } = null!;
|
||||
public ScoreController ScoreController { get; private set; } = null!;
|
||||
|
||||
private TimerController _timerController = null!;
|
||||
|
||||
private readonly MenuControllerComponent _menuController;
|
||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||
|
||||
public GameController(MenuControllerComponent parMenuController)
|
||||
{
|
||||
_menuController = parMenuController;
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
ScoreController = GameObject.GetComponent<ScoreController>()!;
|
||||
PlayerController = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<PlayerController>()!;
|
||||
_timerController = GameObject.GetComponent<TimerController>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(ScoreController);
|
||||
ArgumentNullException.ThrowIfNull(_timerController);
|
||||
ArgumentNullException.ThrowIfNull(PlayerController);
|
||||
|
||||
PlayerController.OnDeath += GameOver;
|
||||
_timerController.OnFinished += GameOver;
|
||||
}
|
||||
|
||||
public void Unpause()
|
||||
{
|
||||
EngineUtil.SceneManager.CurrentScene!.TimeScale = 1.0f;
|
||||
IsPaused = false;
|
||||
_menuController.SelectMenuItem("play");
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
EngineUtil.SceneManager.CurrentScene!.TimeScale = 0.0f;
|
||||
IsPaused = true;
|
||||
_menuController.SelectMenuItem("escape");
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Escape))
|
||||
{
|
||||
if (IsPaused)
|
||||
{
|
||||
Unpause();
|
||||
}
|
||||
else
|
||||
{
|
||||
Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GameOver()
|
||||
{
|
||||
IsGameOver = true;
|
||||
EngineUtil.SceneManager.TransitionTo(() => GameOverScene.Create(ScoreController.Score));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Script.Model;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.View;
|
||||
namespace DoomDeathmatch.Component.MVC.Health;
|
||||
|
||||
public class EnemyHealthView : HealthView
|
||||
{
|
||||
@@ -1,11 +1,11 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using DoomDeathmatch.Script.Model;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
namespace DoomDeathmatch.Component.MVC.Health;
|
||||
|
||||
public class HealthController : Engine.Scene.Component.Component
|
||||
{
|
||||
public event Action? OnDeath;
|
||||
public bool IsAlive => _healthModel.Health > 0;
|
||||
|
||||
private readonly HealthModel _healthModel;
|
||||
private HealthView? _healthView;
|
||||
@@ -45,7 +45,7 @@ public class HealthController : Engine.Scene.Component.Component
|
||||
|
||||
private void OnHealthChanged(HealthModel parHealthModel)
|
||||
{
|
||||
if (parHealthModel.Health <= 0)
|
||||
if (!IsAlive)
|
||||
{
|
||||
OnDeath?.Invoke();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Script.Model;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.View;
|
||||
namespace DoomDeathmatch.Component.MVC.Health;
|
||||
|
||||
public class HealthView : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Component.Physics;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
namespace DoomDeathmatch.Component.MVC;
|
||||
|
||||
public class MovementController : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -21,6 +21,6 @@ public class MovementController : Engine.Scene.Component.Component
|
||||
|
||||
public void ApplyMovement(Vector3 parDirection)
|
||||
{
|
||||
_rigidbody.AddForce(_dragComponent.Drag * Speed * parDirection);
|
||||
_rigidbody.AddForce(_dragComponent.Drag * Speed * parDirection.Normalized());
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,24 @@
|
||||
using DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
using DoomDeathmatch.Component.MVC.Weapon;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using Engine.Input;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
namespace DoomDeathmatch.Component.MVC;
|
||||
|
||||
public class PlayerController : Engine.Scene.Component.Component
|
||||
{
|
||||
public bool IsPaused { get; set; } = false;
|
||||
public event Action? OnDeath;
|
||||
public bool IsAlive => _healthController.IsAlive;
|
||||
|
||||
public HealthController HealthController => _healthController;
|
||||
public WeaponController WeaponController => _weaponController;
|
||||
public PerspectiveCamera Camera => _camera;
|
||||
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||
|
||||
private HealthController _healthController = null!;
|
||||
private WeaponController _weaponController = null!;
|
||||
@@ -33,16 +36,15 @@ public class PlayerController : Engine.Scene.Component.Component
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_healthController);
|
||||
ArgumentNullException.ThrowIfNull(_weaponController);
|
||||
|
||||
_healthController.OnDeath += () => OnDeath?.Invoke();
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
if (IsPaused)
|
||||
if (!IsAlive || parDeltaTime == 0)
|
||||
return;
|
||||
|
||||
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.C))
|
||||
_weaponController.AddWeapon(WeaponData.Shotgun);
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (KeyboardButtonCode.D1 + i > KeyboardButtonCode.D0)
|
||||
@@ -64,7 +66,7 @@ public class PlayerController : Engine.Scene.Component.Component
|
||||
var forward = (_camera.Forward - position).Normalized();
|
||||
var right = Vector3.Cross(forward, Vector3.UnitZ).Normalized();
|
||||
|
||||
var collisionManager = GameObject.Scene!.FindFirstComponent<CollisionManager>();
|
||||
var collisionManager = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<CollisionManagerComponent>();
|
||||
|
||||
var offsets = _weaponController.WeaponData.ShootPattern.GetShootPattern(forward, Vector3.UnitZ, right);
|
||||
foreach (var offset in offsets)
|
||||
@@ -73,22 +75,6 @@ public class PlayerController : Engine.Scene.Component.Component
|
||||
if (!collisionManager!.Raycast(position, direction, ["enemy", "wall"], out var result))
|
||||
continue;
|
||||
|
||||
var hitDisplayObject = GameObjectUtil.CreateGameObject(GameObject.Scene,
|
||||
new Transform { Translation = result.HitPoint, Size = new Vector3(0.1f) },
|
||||
[
|
||||
new Box2DRenderer { Color = new Vector4(0, 0, 1, 1), RenderLayer = RenderLayer.DEFAULT },
|
||||
new BillboardComponent { Target = _camera.GameObject.Transform }
|
||||
]
|
||||
);
|
||||
|
||||
var hitNormalDisplayObject = GameObjectUtil.CreateGameObject(GameObject.Scene,
|
||||
new Transform { Translation = result.HitPoint + result.Normal, Size = new Vector3(0.1f) },
|
||||
[
|
||||
new Box2DRenderer { Color = new Vector4(1, 0, 1, 1), RenderLayer = RenderLayer.DEFAULT },
|
||||
new BillboardComponent { Target = _camera.GameObject.Transform }
|
||||
]
|
||||
);
|
||||
|
||||
var enemyController = result.HitObject.GetComponent<EnemyController>();
|
||||
enemyController?.HealthController.TakeDamage(_weaponController.WeaponData.Damage);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using DoomDeathmatch.Script.Model;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
namespace DoomDeathmatch.Component.MVC.Score;
|
||||
|
||||
public class ScoreController : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Script.Model;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.View;
|
||||
namespace DoomDeathmatch.Component.MVC.Score;
|
||||
|
||||
public class ScoreView : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -1,11 +1,9 @@
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using Engine.Util;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
namespace DoomDeathmatch.Component.MVC.Timer;
|
||||
|
||||
public class TimerController : Engine.Scene.Component.Component
|
||||
{
|
||||
public bool IsPaused { get; set; } = false;
|
||||
public event Action? OnFinished;
|
||||
|
||||
private readonly TickableTimer _tickableTimer = new(60 + 10);
|
||||
@@ -22,9 +20,6 @@ public class TimerController : Engine.Scene.Component.Component
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
if (IsPaused)
|
||||
return;
|
||||
|
||||
_tickableTimer.Update(parDeltaTime);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.View;
|
||||
namespace DoomDeathmatch.Component.MVC.Timer;
|
||||
|
||||
public class TimerView : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -1,32 +0,0 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
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}";
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using DoomDeathmatch.Script.Model;
|
||||
using DoomDeathmatch.Script.Model.Weapon;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
namespace DoomDeathmatch.Component.MVC.Weapon;
|
||||
|
||||
public class WeaponController : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -26,9 +25,10 @@ public class WeaponController : Engine.Scene.Component.Component
|
||||
if (_weaponModel.SelectedWeapon.Ammo <= 0)
|
||||
return false;
|
||||
|
||||
// _weaponModel.SelectedWeapon.Ammo--;
|
||||
_weaponModel.SelectedWeapon.Ammo--;
|
||||
|
||||
OnWeaponShot?.Invoke(_weaponModel.SelectedWeapon);
|
||||
_weaponView.Fire();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -46,6 +46,14 @@ public class WeaponController : Engine.Scene.Component.Component
|
||||
_weaponModel.Weapons.Add(parWeaponData);
|
||||
}
|
||||
|
||||
public void AddOrMergeWeapon(WeaponData parWeaponData)
|
||||
{
|
||||
if (!_weaponModel.Weapons.Contains(parWeaponData))
|
||||
_weaponModel.Weapons.Add(parWeaponData);
|
||||
else
|
||||
_weaponModel.Weapons.First(parData => parData.Id == parWeaponData.Id).Ammo += parWeaponData.Ammo;
|
||||
}
|
||||
|
||||
public void RemoveWeapon(int parIndex)
|
||||
{
|
||||
if (parIndex <= 0 || parIndex >= _weaponModel.Weapons.Count)
|
||||
@@ -65,6 +73,11 @@ public class WeaponController : Engine.Scene.Component.Component
|
||||
_weaponModel.SelectedWeaponIndex = parIndex;
|
||||
}
|
||||
|
||||
// public WeaponData? FindWeapon(string parId)
|
||||
// {
|
||||
// return _weaponModel.Weapons.FirstOrDefault(parData => parData.Id == parId);
|
||||
// }
|
||||
|
||||
private void WeaponSelected(WeaponData? parOldWeapon, WeaponData parNewWeapon)
|
||||
{
|
||||
if (parOldWeapon != null)
|
||||
71
DoomDeathmatch/src/Component/MVC/Weapon/WeaponView.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using DoomDeathmatch.Script;
|
||||
using DoomDeathmatch.Script.Model.Weapon;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Weapon;
|
||||
|
||||
public class WeaponView : Engine.Scene.Component.Component
|
||||
{
|
||||
private readonly TextRenderer _weaponName;
|
||||
private readonly TextRenderer _weaponAmmo;
|
||||
private readonly Box2DRenderer _weaponSprite;
|
||||
|
||||
private AnimationPlayer<Texture>? _weaponFireAnimation;
|
||||
private Texture? _idleTexture;
|
||||
|
||||
public WeaponView(TextRenderer parWeaponName, TextRenderer parWeaponAmmo, Box2DRenderer parWeaponSprite)
|
||||
{
|
||||
_weaponName = parWeaponName;
|
||||
_weaponAmmo = parWeaponAmmo;
|
||||
_weaponSprite = parWeaponSprite;
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
_weaponFireAnimation?.Update(parDeltaTime);
|
||||
}
|
||||
|
||||
public void UpdateView(WeaponData parWeaponData)
|
||||
{
|
||||
UpdateAmmoView(parWeaponData);
|
||||
_weaponName.Text = $"Оружие: {parWeaponData.Name}";
|
||||
|
||||
_idleTexture = EngineUtil.AssetResourceManager.Load<Texture>(parWeaponData.IdleTexture);
|
||||
_weaponSprite.Texture = _idleTexture;
|
||||
|
||||
_weaponFireAnimation?.Reset();
|
||||
if (_weaponFireAnimation != null)
|
||||
{
|
||||
_weaponFireAnimation.OnFrameChanged -= OnAnimationFrame;
|
||||
_weaponFireAnimation.OnFinish -= OnAnimationFinish;
|
||||
}
|
||||
|
||||
var frames = parWeaponData.FireAnimation.Select(EngineUtil.AssetResourceManager.Load<Texture>).ToList();
|
||||
|
||||
_weaponFireAnimation = new AnimationPlayer<Texture>(parWeaponData.FireAnimationDuration) { Frames = frames };
|
||||
_weaponFireAnimation.OnFrameChanged += OnAnimationFrame;
|
||||
_weaponFireAnimation.OnFinish += OnAnimationFinish;
|
||||
}
|
||||
|
||||
public void UpdateAmmoView(WeaponData parWeaponData)
|
||||
{
|
||||
_weaponAmmo.Text = $"Патроны: {parWeaponData.Ammo}/{parWeaponData.MaxAmmo}";
|
||||
}
|
||||
|
||||
public void Fire()
|
||||
{
|
||||
_weaponFireAnimation?.Start();
|
||||
}
|
||||
|
||||
private void OnAnimationFrame(Texture parFrameTexture)
|
||||
{
|
||||
_weaponSprite.Texture = parFrameTexture;
|
||||
}
|
||||
|
||||
private void OnAnimationFinish()
|
||||
{
|
||||
_weaponSprite.Texture = _idleTexture;
|
||||
}
|
||||
}
|
||||
11
DoomDeathmatch/src/Component/ObjectSpawnerComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using DoomDeathmatch.Script;
|
||||
|
||||
namespace DoomDeathmatch.Component;
|
||||
|
||||
public class ObjectSpawnerComponent(ObjectSpawner parObjectSpawner) : Engine.Scene.Component.Component
|
||||
{
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
parObjectSpawner.Update(parDeltaTime);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using OpenTK.Mathematics;
|
||||
using DoomDeathmatch.Script.Collision;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
namespace DoomDeathmatch.Component.Physics.Collision;
|
||||
|
||||
public class AABBColliderComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
namespace DoomDeathmatch.Component.Physics.Collision;
|
||||
|
||||
public class ColliderForceFieldComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -20,9 +20,9 @@ public class ColliderForceFieldComponent : Engine.Scene.Component.Component
|
||||
|
||||
var normal = _collider.Collider.GetCollisionNormal(parCollider.Collider);
|
||||
var speedAlongNormal = Vector3.Dot(rigidbody.Velocity, normal);
|
||||
if (speedAlongNormal >= 0)
|
||||
if (speedAlongNormal > 0)
|
||||
return;
|
||||
|
||||
rigidbody.AddVelocity(-normal * speedAlongNormal);
|
||||
rigidbody.AddVelocity(-normal * (speedAlongNormal * 1.75f));
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using DoomDeathmatch.Script.Collision;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
namespace DoomDeathmatch.Component.Physics.Collision;
|
||||
|
||||
public class CollisionManager : Engine.Scene.Component.Component
|
||||
public class CollisionManagerComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
private List<AABBColliderComponent> _colliders = [];
|
||||
|
||||
public override void PreUpdate()
|
||||
public override void PreUpdate(double parDeltaTime)
|
||||
{
|
||||
_colliders = GameObject.Scene!.FindAllComponents<AABBColliderComponent>();
|
||||
_colliders = EngineUtil.SceneManager.CurrentScene!.FindAllComponents<AABBColliderComponent>();
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
@@ -23,9 +25,9 @@ public class CollisionManager : Engine.Scene.Component.Component
|
||||
var colliderB = _colliders[j];
|
||||
|
||||
var canCollideAB = colliderA.ExcludeColliderCollideGroups.Count == 0 ||
|
||||
!colliderA.ExcludeColliderCollideGroups.IsSubsetOf(colliderB.ColliderGroups);
|
||||
!colliderB.ColliderGroups.Overlaps(colliderA.ExcludeColliderCollideGroups);
|
||||
var canCollideBA = colliderB.ExcludeColliderCollideGroups.Count == 0 ||
|
||||
!colliderB.ExcludeColliderCollideGroups.IsSubsetOf(colliderA.ColliderGroups);
|
||||
!colliderA.ColliderGroups.Overlaps(colliderB.ExcludeColliderCollideGroups);
|
||||
|
||||
if (!canCollideAB && !canCollideBA)
|
||||
continue;
|
||||
@@ -1,11 +1,11 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
namespace DoomDeathmatch.Component.Physics;
|
||||
|
||||
public class DragComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
public float Drag { get; set; } = 1f;
|
||||
public Vector3 Coefficient { get; set; } = Vector3.One;
|
||||
public Vector3 Multiplier { get; set; } = Vector3.One;
|
||||
|
||||
private RigidbodyComponent _rigidbody = null!;
|
||||
|
||||
@@ -18,6 +18,6 @@ public class DragComponent : Engine.Scene.Component.Component
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
_rigidbody.AddForce(-Drag * (_rigidbody.Velocity * Coefficient));
|
||||
_rigidbody.AddForce(-Drag * (_rigidbody.Velocity * Multiplier));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
namespace DoomDeathmatch.Component.Physics;
|
||||
|
||||
public class GravityComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
namespace DoomDeathmatch.Component.Physics;
|
||||
|
||||
public class RigidbodyComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
@@ -34,6 +34,7 @@ public class RigidbodyComponent : Engine.Scene.Component.Component
|
||||
Acceleration = Force / Mass;
|
||||
Velocity += Acceleration * (float)parDeltaTime;
|
||||
GameObject.Transform.Translation += Velocity * (float)parDeltaTime;
|
||||
|
||||
Force = Vector3.Zero;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Engine.Input;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.UI;
|
||||
|
||||
@@ -11,7 +12,7 @@ public class SelectorComponent : Engine.Scene.Component.Component
|
||||
|
||||
public event Action<UiComponent>? OnSelect;
|
||||
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||
|
||||
private readonly List<UiComponent> _children = [];
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Engine.Scene;
|
||||
using DoomDeathmatch.Script.UI;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.UI;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using DoomDeathmatch.Script.UI;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.UI;
|
||||
@@ -42,11 +43,4 @@ public class TextAlignComponent : Engine.Scene.Component.Component
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(Alignment), Alignment, null)
|
||||
};
|
||||
}
|
||||
|
||||
public enum Align
|
||||
{
|
||||
Left,
|
||||
Center,
|
||||
Right
|
||||
}
|
||||
}
|
||||
80
DoomDeathmatch/src/Component/UI/TextInputComponent.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Engine.Input;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.UI;
|
||||
|
||||
public class TextInputComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
public event Action<string>? OnInput;
|
||||
|
||||
public bool IsActive { get; set; } = false;
|
||||
public string Input { get; private set; } = "";
|
||||
|
||||
public float InputDelay
|
||||
{
|
||||
get => _inputDelay;
|
||||
set
|
||||
{
|
||||
_inputDelay = value;
|
||||
_inputTimer.TotalTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||
|
||||
private readonly KeyboardButtonCode[] _acceptedKeys =
|
||||
KeyboardButtonCodeHelper.GetAllPrintableKeys().Append(KeyboardButtonCode.Backspace).ToArray();
|
||||
|
||||
private readonly TickableTimer _inputTimer;
|
||||
private float _inputDelay = 0;
|
||||
private readonly HashSet<KeyboardButtonCode> _lastKeys = [];
|
||||
|
||||
public TextInputComponent(float parInputDelay = 0.2f)
|
||||
{
|
||||
_inputDelay = parInputDelay;
|
||||
_inputTimer = new TickableTimer(_inputDelay) { CurrentTime = 0 };
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
_inputTimer.Update(parDeltaTime);
|
||||
|
||||
foreach (var key in _acceptedKeys)
|
||||
{
|
||||
if (_lastKeys.Contains(key) && !_inputTimer.IsFinished)
|
||||
continue;
|
||||
|
||||
if (_inputHandler.IsKeyPressed(key))
|
||||
{
|
||||
var invoke = false;
|
||||
if (key == KeyboardButtonCode.Backspace)
|
||||
{
|
||||
if (Input.Length > 0)
|
||||
{
|
||||
Input = Input[..^1];
|
||||
invoke = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Input += key.GetChar();
|
||||
invoke = true;
|
||||
}
|
||||
|
||||
if (invoke)
|
||||
OnInput?.Invoke(Input);
|
||||
|
||||
_lastKeys.Add(key);
|
||||
|
||||
_inputTimer.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastKeys.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using Engine.Input;
|
||||
using DoomDeathmatch.Script.UI;
|
||||
using Engine.Input;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
using Math = System.Math;
|
||||
|
||||
namespace DoomDeathmatch.Component.UI;
|
||||
|
||||
@@ -13,7 +16,7 @@ public class UiComponent : Engine.Scene.Component.Component
|
||||
public event Action<UiComponent>? OnClick;
|
||||
public event Action<UiComponent>? OnMouseOver;
|
||||
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using Engine.Input;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.UI;
|
||||
|
||||
public class UiContainerComponent : UiComponent
|
||||
{
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||
|
||||
public Camera? Camera { get; set; }
|
||||
public Vector3 MousePosition { get; private set; }
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class CopySizeComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
public Vector3 Coefficient { get; set; } = Vector3.One;
|
||||
public Vector3 Multiplier { get; set; } = Vector3.One;
|
||||
public Transform? Target { get; set; }
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
@@ -13,13 +13,13 @@ public class CopySizeComponent : Engine.Scene.Component.Component
|
||||
if (Target == null)
|
||||
return;
|
||||
|
||||
if (Coefficient.X != 0)
|
||||
GameObject.Transform.Size.X = Target.Size.X * Coefficient.X;
|
||||
if (Multiplier.X != 0)
|
||||
GameObject.Transform.Size.X = Target.Size.X * Multiplier.X;
|
||||
|
||||
if (Coefficient.Y != 0)
|
||||
GameObject.Transform.Size.Y = Target.Size.Y * Coefficient.Y;
|
||||
if (Multiplier.Y != 0)
|
||||
GameObject.Transform.Size.Y = Target.Size.Y * Multiplier.Y;
|
||||
|
||||
if (Coefficient.Z != 0)
|
||||
GameObject.Transform.Size.Z = Target.Size.Z * Coefficient.Z;
|
||||
if (Multiplier.Z != 0)
|
||||
GameObject.Transform.Size.Z = Target.Size.Z * Multiplier.Z;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
@@ -21,6 +22,6 @@ public class FireballComponent : Engine.Scene.Component.Component
|
||||
var healthController = parCollider.GameObject.GetComponent<HealthController>();
|
||||
healthController?.TakeDamage(Damage);
|
||||
|
||||
GameObject.Scene!.Remove(GameObject);
|
||||
EngineUtil.SceneManager.CurrentScene!.Remove(GameObject);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC;
|
||||
using Engine.Input;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class ControllerComponent : Engine.Scene.Component.Component
|
||||
public class PlayerMovementComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
public float RotationSpeed { get; set; } = 70.0f;
|
||||
public float RotationSpeed { get; set; } = 110.0f;
|
||||
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
private readonly IInputHandler _inputHandler = EngineUtil.InputHandler;
|
||||
private MovementController _movementController = null!;
|
||||
|
||||
public override void Awake()
|
||||
@@ -1,4 +1,5 @@
|
||||
using DoomDeathmatch.Scene;
|
||||
using DoomDeathmatch.Scene.Main;
|
||||
|
||||
namespace DoomDeathmatch;
|
||||
|
||||
@@ -6,6 +7,6 @@ public static class DoomDeathmatch
|
||||
{
|
||||
public static void Initialize(Engine.Engine parEngine)
|
||||
{
|
||||
parEngine.SceneManager.TransitionTo(() => MainScene.Create(parEngine));
|
||||
parEngine.SceneManager.TransitionTo(() => MainScene.Create());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch;
|
||||
@@ -67,78 +66,6 @@ public static class GameObjectUtil
|
||||
]
|
||||
);
|
||||
|
||||
// var color = new Vector4(1, 0, 0, 0.5f);
|
||||
// var size = parCollider.Collider.Size;
|
||||
//
|
||||
// var frontPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform
|
||||
// {
|
||||
// Translation = new Vector3(0, -size.Y / 2, 0),
|
||||
// Size = new Vector3(size.X, size.Z, 0),
|
||||
// Rotation = Quaternion.FromAxisAngle(Vector3.UnitX, MathF.PI / 2)
|
||||
// },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var backPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform
|
||||
// {
|
||||
// Translation = new Vector3(0, size.Y / 2, 0),
|
||||
// Size = new Vector3(size.X, size.Z, 0),
|
||||
// Rotation = Quaternion.FromAxisAngle(Vector3.UnitX, -MathF.PI / 2)
|
||||
// },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var leftPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform
|
||||
// {
|
||||
// Translation = new Vector3(-size.X / 2, 0, 0),
|
||||
// Size = new Vector3(size.Z, size.Y, 0),
|
||||
// Rotation = Quaternion.FromAxisAngle(Vector3.UnitY, MathF.PI / 2)
|
||||
// },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var rightPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform
|
||||
// {
|
||||
// Translation = new Vector3(size.X / 2, 0, 0),
|
||||
// Size = new Vector3(size.Z, size.Y, 0),
|
||||
// Rotation = Quaternion.FromAxisAngle(Vector3.UnitY, -MathF.PI / 2)
|
||||
// },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var topPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform { Translation = new Vector3(0, 0, size.Z / 2), Size = new Vector3(size.X, size.Y, 0) },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var bottomPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform { Translation = new Vector3(0, 0, -size.Z / 2), Size = new Vector3(size.X, size.Y, 0) },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// parScene.AddChild(forceFieldObject, frontPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, backPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, leftPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, rightPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, topPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, bottomPlaneObject);
|
||||
|
||||
return forceFieldObject;
|
||||
}
|
||||
}
|
||||
71
DoomDeathmatch/src/Scene/GameOver/GameOverScene.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Scene.Main;
|
||||
using DoomDeathmatch.Script.Score;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using Engine.Input;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene.GameOver;
|
||||
|
||||
public static class GameOverScene
|
||||
{
|
||||
public static Engine.Scene.Scene Create(int parScore)
|
||||
{
|
||||
var scene = new Engine.Scene.Scene();
|
||||
|
||||
var (cameraObject, camera) = UiUtil.CreateOrthographicCamera(scene);
|
||||
|
||||
var (uiContainerObject, uiContainer) =
|
||||
UiUtil.CreateBackgroundUi(scene, new UiContainerComponent { Camera = camera });
|
||||
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(scene, uiContainer);
|
||||
logoUi.Offset = new Vector2(0, 3f);
|
||||
scene.AddChild(uiContainerObject, logoObject);
|
||||
|
||||
var (resultUiObject, resultUi, _) =
|
||||
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(), $"Ваш результат: {parScore}");
|
||||
|
||||
var (nameUiObject, nameUi, (_, nameTextRenderer)) =
|
||||
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(), "Имя: ");
|
||||
|
||||
var nameInputComponent = new TextInputComponent { IsActive = true };
|
||||
nameInputComponent.OnInput += parName =>
|
||||
{
|
||||
nameTextRenderer.Text = $"Имя: {parName}";
|
||||
};
|
||||
nameUiObject.AddComponent(nameInputComponent);
|
||||
|
||||
var (nextUiObject, nextUi, _) =
|
||||
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(), "Далее");
|
||||
nextUi.OnClick += _ =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(nameInputComponent.Input))
|
||||
return;
|
||||
|
||||
SubmitScore(nameInputComponent.Input, parScore);
|
||||
EngineUtil.SceneManager.TransitionTo(MainScene.Create);
|
||||
};
|
||||
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(scene,
|
||||
new SelectorComponent { Children = { nextUi }, SelectKey = KeyboardButtonCode.Enter }, RenderLayer.HUD);
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(scene,
|
||||
new StackComponent { Offset = new Vector2(0, -1f), Container = uiContainer, Children = { resultUi, nextUi } });
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
scene.AddChild(stackObject, resultUiObject);
|
||||
scene.AddChild(stackObject, nameUiObject);
|
||||
scene.AddChild(stackObject, nextUiObject);
|
||||
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
private static void SubmitScore(string parUsername, int parScore)
|
||||
{
|
||||
var table = ScoreTable.LoadOrCreate(Path.Combine(EngineUtil.DataFolder, "leaders.json"));
|
||||
table.Rows.Add(new ScoreRow { Name = parUsername, Score = parScore });
|
||||
ScoreTable.Save(table, Path.Combine(EngineUtil.DataFolder, "leaders.json"));
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,21 @@
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using System.Text.Json;
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Scene.Play;
|
||||
using DoomDeathmatch.Script.Score;
|
||||
using DoomDeathmatch.Script.UI;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene;
|
||||
namespace DoomDeathmatch.Scene.Main;
|
||||
|
||||
public static class MainScene
|
||||
{
|
||||
public static Engine.Scene.Scene Create(Engine.Engine parEngine)
|
||||
public static Engine.Scene.Scene Create()
|
||||
{
|
||||
var scene = new Engine.Scene.Scene();
|
||||
|
||||
@@ -16,7 +24,7 @@ public static class MainScene
|
||||
var (uiContainerObject, uiContainer) =
|
||||
UiUtil.CreateBackgroundUi(scene, new UiContainerComponent { Camera = camera });
|
||||
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, scene, uiContainer);
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(scene, uiContainer);
|
||||
logoUi.Offset = new Vector2(0, 3f);
|
||||
scene.AddChild(uiContainerObject, logoObject);
|
||||
|
||||
@@ -25,14 +33,14 @@ public static class MainScene
|
||||
menuController
|
||||
]);
|
||||
|
||||
var mainMenu = CreateMainMenu(parEngine, scene, uiContainer, menuController);
|
||||
var mainMenu = CreateMainMenu(scene, uiContainer, menuController);
|
||||
menuController.AddMenuItem("main", mainMenu);
|
||||
|
||||
var leadersMenu = CreateLeadersMenu(parEngine, scene, uiContainer, menuController);
|
||||
var leadersMenu = CreateLeadersMenu(scene, uiContainer, menuController);
|
||||
leadersMenu.IsEnabled = false;
|
||||
menuController.AddMenuItem("leaders", leadersMenu);
|
||||
|
||||
var rulesMenu = CreateRulesMenu(parEngine, scene, uiContainer, menuController);
|
||||
var rulesMenu = CreateRulesMenu(scene, uiContainer, menuController);
|
||||
rulesMenu.IsEnabled = false;
|
||||
menuController.AddMenuItem("rules", rulesMenu);
|
||||
|
||||
@@ -43,22 +51,22 @@ public static class MainScene
|
||||
return scene;
|
||||
}
|
||||
|
||||
private static GameObject CreateMainMenu(Engine.Engine parEngine, Engine.Scene.Scene parScene,
|
||||
private static GameObject CreateMainMenu(Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
var parentObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
|
||||
var (playUiObject, playUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(parEngine), "Играть");
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(), "Играть");
|
||||
|
||||
var (leadersUiObject, leadersUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(parEngine), "Лидеры");
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(), "Лидеры");
|
||||
|
||||
var (rulesUiObject, rulesUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(parEngine), "Правила");
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(), "Правила");
|
||||
|
||||
var (exitUiObject, exitUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(parEngine), "Выход");
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(), "Выход");
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
||||
new StackComponent
|
||||
@@ -67,10 +75,10 @@ public static class MainScene
|
||||
});
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
playUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => PlayScene.Create(parEngine));
|
||||
playUi.OnClick += _ => EngineUtil.SceneManager.TransitionTo(PlayScene.Create);
|
||||
leadersUi.OnClick += _ => parMenuController.SelectMenuItem("leaders");
|
||||
rulesUi.OnClick += _ => parMenuController.SelectMenuItem("rules");
|
||||
exitUi.OnClick += _ => parEngine.Close();
|
||||
exitUi.OnClick += _ => EngineUtil.Close();
|
||||
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(parScene,
|
||||
new SelectorComponent { Children = { playUi, leadersUi, rulesUi, exitUi } });
|
||||
@@ -86,19 +94,28 @@ public static class MainScene
|
||||
return parentObject;
|
||||
}
|
||||
|
||||
private static GameObject CreateLeadersMenu(Engine.Engine parEngine, Engine.Scene.Scene parScene,
|
||||
private static GameObject CreateLeadersMenu(Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
var parentObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
|
||||
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(parScene, parUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Назад");
|
||||
UiUtil.GetDoomFont(), "Назад");
|
||||
backUi.OnClick += _ => parMenuController.SelectMenuItem("main");
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
||||
new StackComponent { Offset = new Vector2(0, -1f), Container = parUiContainer, Children = { backUi } });
|
||||
new StackComponent { Offset = new Vector2(0, -1.5f), Container = parUiContainer, Children = { } });
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
var leadersTable = CreateLeadersTable(parScene, parUiContainer, EngineUtil.DataFolder);
|
||||
foreach (var (rowObject, rowUi) in leadersTable)
|
||||
{
|
||||
stack.Children.Add(rowUi);
|
||||
parScene.AddChild(stackObject, rowObject);
|
||||
}
|
||||
|
||||
stack.Children.Add(backUi);
|
||||
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(parScene, new SelectorComponent { Children = { backUi } });
|
||||
|
||||
parScene.AddChild(parentObject, selectorObject);
|
||||
@@ -108,17 +125,62 @@ public static class MainScene
|
||||
return parentObject;
|
||||
}
|
||||
|
||||
private static GameObject CreateRulesMenu(Engine.Engine parEngine, Engine.Scene.Scene parScene,
|
||||
private static List<(GameObject, UiComponent)> CreateLeadersTable(Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, string parDataFolder)
|
||||
{
|
||||
var leadersTableList =
|
||||
new List<(GameObject, UiComponent)> { CreateLeadersRow(parScene, parUiContainer, "Имя", "Очки") };
|
||||
|
||||
var leadersTable = ScoreTable.LoadOrCreate(Path.Combine(parDataFolder, "leaders.json"));
|
||||
|
||||
var rows = 0;
|
||||
foreach (var row in leadersTable.Rows)
|
||||
{
|
||||
leadersTableList.Add(CreateLeadersRow(parScene, parUiContainer, row.Name, row.Score.ToString()));
|
||||
if (++rows >= 5)
|
||||
break;
|
||||
}
|
||||
|
||||
return leadersTableList;
|
||||
}
|
||||
|
||||
private static (GameObject, UiComponent) CreateLeadersRow(Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, string parName, string parScore)
|
||||
{
|
||||
var uiComponent = new UiContainerComponent { Container = parUiContainer };
|
||||
|
||||
var rowObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
new Transform { Size = new Vector3(5, 8, 1) }, [
|
||||
uiComponent
|
||||
]);
|
||||
|
||||
var (nameUiObject, nameUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, uiComponent, UiUtil.GetDoomFont(), parName);
|
||||
nameUi.Anchor = Anchor.CenterLeft;
|
||||
nameUi.Center = Anchor.Center;
|
||||
|
||||
var (scoreUiObject, scoreUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, uiComponent, UiUtil.GetDoomFont(), parScore);
|
||||
scoreUi.Anchor = Anchor.CenterRight;
|
||||
scoreUi.Center = Anchor.Center;
|
||||
|
||||
parScene.AddChild(rowObject, nameUiObject);
|
||||
parScene.AddChild(rowObject, scoreUiObject);
|
||||
|
||||
return (rowObject, uiComponent);
|
||||
}
|
||||
|
||||
private static GameObject CreateRulesMenu(Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
var parentObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
|
||||
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(parScene, parUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Назад");
|
||||
UiUtil.GetDoomFont(), "Назад");
|
||||
backUi.OnClick += _ => parMenuController.SelectMenuItem("main");
|
||||
|
||||
var (rulesObject, rulesUi, _) = UiUtil.CreateTextUi(parScene, parUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Правила");
|
||||
UiUtil.GetDoomFont(), "Правила");
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
||||
new StackComponent { Offset = new Vector2(0, -1f), Container = parUiContainer, Children = { rulesUi, backUi } });
|
||||
@@ -1,21 +1,35 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using DoomDeathmatch.Component;
|
||||
using DoomDeathmatch.Component.MVC;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
using DoomDeathmatch.Component.MVC.Score;
|
||||
using DoomDeathmatch.Component.MVC.Timer;
|
||||
using DoomDeathmatch.Component.MVC.Weapon;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
using DoomDeathmatch.Scene.Main;
|
||||
using DoomDeathmatch.Scene.Play.Prefab;
|
||||
using DoomDeathmatch.Script;
|
||||
using DoomDeathmatch.Script.Collision;
|
||||
using DoomDeathmatch.Script.Condition;
|
||||
using DoomDeathmatch.Script.Consumable;
|
||||
using DoomDeathmatch.Script.Model.Enemy;
|
||||
using DoomDeathmatch.Script.Model.Weapon;
|
||||
using DoomDeathmatch.Script.Provider;
|
||||
using DoomDeathmatch.Script.UI;
|
||||
using Engine.Asset.Mesh;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene.Play;
|
||||
|
||||
public static class PlayScene
|
||||
{
|
||||
public static Engine.Scene.Scene Create(Engine.Engine parEngine)
|
||||
public static Engine.Scene.Scene Create()
|
||||
{
|
||||
var scene = new Engine.Scene.Scene();
|
||||
|
||||
@@ -29,41 +43,17 @@ public static class PlayScene
|
||||
]);
|
||||
|
||||
var (playUiObject, (playWeaponView, playHealthView, playScoreView, plaTimerView)) =
|
||||
CreateGameUi(parEngine, scene, uiContainer, menuController);
|
||||
CreateGameUi(scene, uiContainer, menuController);
|
||||
menuController.AddMenuItem("play", playUiObject);
|
||||
scene.AddChild(uiContainerObject, playUiObject);
|
||||
|
||||
var escapeUiObject = CreateEscapeMenu(parEngine, scene, uiContainer, menuController);
|
||||
var escapeUiObject = CreateEscapeMenu(scene, uiContainer, menuController);
|
||||
escapeUiObject.IsEnabled = false;
|
||||
menuController.AddMenuItem("escape", escapeUiObject);
|
||||
scene.AddChild(uiContainerObject, escapeUiObject);
|
||||
|
||||
var (perspectiveCameraObject, perspectiveCamera) = UiUtil.CreatePerspectiveCamera(scene);
|
||||
perspectiveCameraObject.Transform.Translation.Z = 2;
|
||||
var playerObject = GameObjectUtil.CreateGameObject(scene, [
|
||||
new ControllerComponent(),
|
||||
PlayerPrefab.Create(scene, playWeaponView, playHealthView);
|
||||
|
||||
new MovementController { Speed = 10f },
|
||||
new RigidbodyComponent(),
|
||||
new DragComponent { Drag = 10f, Coefficient = new Vector3(1, 1, 0) },
|
||||
|
||||
new AABBColliderComponent
|
||||
{
|
||||
Collider = new AABBCollider { Size = new Vector3(1, 1, 2) },
|
||||
Offset = new Vector3(0, 0f, 1f),
|
||||
ColliderGroups = { "player" },
|
||||
ExcludeColliderCollideGroups = { "player" }
|
||||
},
|
||||
|
||||
new PlayerController(perspectiveCamera),
|
||||
|
||||
new WeaponController(),
|
||||
playWeaponView,
|
||||
|
||||
new HealthController(),
|
||||
playHealthView,
|
||||
]);
|
||||
playerObject.Transform.Translation.X = -3;
|
||||
var gameControllerObject = GameObjectUtil.CreateGameObject(scene, [
|
||||
new GameController(menuController),
|
||||
|
||||
@@ -73,38 +63,100 @@ public static class PlayScene
|
||||
new ScoreController(),
|
||||
playScoreView,
|
||||
|
||||
new CollisionManager(),
|
||||
new CollisionManagerComponent(),
|
||||
]);
|
||||
|
||||
scene.AddChild(playerObject, perspectiveCameraObject);
|
||||
|
||||
var mapObject = LoadMap(parEngine, scene, "default");
|
||||
var impObject = EnemyObject.CreateEnemy(parEngine, scene, EnemyData.Imp);
|
||||
var mapObject = LoadMap(scene, gameControllerObject, "default");
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
private static GameObject LoadMap(Engine.Engine parEngine, Engine.Scene.Scene parScene, string parMapName)
|
||||
private static GameObject LoadMap(Engine.Scene.Scene parScene, GameObject parGameControllerObject, string parMapName)
|
||||
{
|
||||
var mapObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||
new MeshRenderer
|
||||
{
|
||||
Mesh = parEngine.AssetResourceManager.Load<Mesh>($"map/{parMapName}/mesh.obj"),
|
||||
Albedo = parEngine.AssetResourceManager.Load<Texture>($"map/{parMapName}/texture.png")
|
||||
Mesh = EngineUtil.AssetResourceManager.Load<Mesh>($"map/{parMapName}/mesh.obj"),
|
||||
Albedo = EngineUtil.AssetResourceManager.Load<Texture>($"map/{parMapName}/texture.png")
|
||||
}
|
||||
]);
|
||||
|
||||
var colliders = parEngine.AssetResourceManager.Load<Mesh>($"map/{parMapName}/colliders.obj");
|
||||
var colliders = EngineUtil.AssetResourceManager.Load<Mesh>($"map/{parMapName}/colliders.obj");
|
||||
var collidersObject = MeshToColliders(parScene, colliders);
|
||||
|
||||
var monsterSpawnPoints = MeshToSpawnPoints(
|
||||
parEngine.AssetResourceManager.Load<Mesh>($"map/{parMapName}/monster_spawners.obj"));
|
||||
EngineUtil.AssetResourceManager.Load<Mesh>($"map/{parMapName}/monster_spawners.obj"));
|
||||
|
||||
var valuableSpawnPoints = MeshToSpawnPoints(
|
||||
parEngine.AssetResourceManager.Load<Mesh>($"map/{parMapName}/valuable_spawners.obj"));
|
||||
EngineUtil.AssetResourceManager.Load<Mesh>($"map/{parMapName}/valuable_spawners.obj"));
|
||||
|
||||
parScene.AddChild(mapObject, collidersObject);
|
||||
|
||||
var monsterVector3ValueProvider =
|
||||
new RandomListValueProvider<Vector3>(monsterSpawnPoints.Select(ConstValueProvider<Vector3>.Create));
|
||||
|
||||
var monsterSpawnerObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||
new ObjectSpawnerComponent(
|
||||
new ObjectSpawner(new WeightedRandomValueProvider<GameObject>(
|
||||
new List<(int, IValueProvider<GameObject>)>
|
||||
{
|
||||
(6, GeneratorValueProvider<GameObject>.Create(() =>
|
||||
EnemyPrefab.Create(EngineUtil.SceneManager.CurrentScene!, EnemyData.Demon))),
|
||||
(4,
|
||||
GeneratorValueProvider<GameObject>.Create(() =>
|
||||
EnemyPrefab.Create(EngineUtil.SceneManager.CurrentScene!, EnemyData.Imp)))
|
||||
}),
|
||||
monsterVector3ValueProvider,
|
||||
new TickableTimerCondition(2f)))
|
||||
]
|
||||
);
|
||||
|
||||
parScene.AddChild(parGameControllerObject, monsterSpawnerObject);
|
||||
|
||||
var consumableVector3ValueProvider =
|
||||
new RandomListValueProvider<Vector3>(valuableSpawnPoints.Select(ConstValueProvider<Vector3>.Create));
|
||||
|
||||
var consumableSpawnerObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||
new ObjectSpawnerComponent(
|
||||
new ObjectSpawner(new WeightedRandomValueProvider<GameObject>(
|
||||
new List<(int, IValueProvider<GameObject>)>
|
||||
{
|
||||
(2, GeneratorValueProvider<GameObject>.Create(() =>
|
||||
{
|
||||
var gameController = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<GameController>()!;
|
||||
|
||||
return ConsumablePrefab.Create(EngineUtil.SceneManager.CurrentScene,
|
||||
new HealthPackConsumable(75),
|
||||
gameController.PlayerController.Camera.GameObject.Transform
|
||||
);
|
||||
})),
|
||||
(4, GeneratorValueProvider<GameObject>.Create(() =>
|
||||
{
|
||||
var gameController = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<GameController>()!;
|
||||
|
||||
return ConsumablePrefab.Create(EngineUtil.SceneManager.CurrentScene,
|
||||
new WeaponConsumable(WeaponData.Pistol),
|
||||
gameController.PlayerController.Camera.GameObject.Transform
|
||||
);
|
||||
})),
|
||||
(3, GeneratorValueProvider<GameObject>.Create(() =>
|
||||
{
|
||||
var gameController = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<GameController>()!;
|
||||
|
||||
return ConsumablePrefab.Create(EngineUtil.SceneManager.CurrentScene,
|
||||
new WeaponConsumable(WeaponData.Shotgun),
|
||||
gameController.PlayerController.Camera.GameObject.Transform
|
||||
);
|
||||
}))
|
||||
}),
|
||||
consumableVector3ValueProvider,
|
||||
new TickableTimerCondition(5f)))
|
||||
]
|
||||
);
|
||||
|
||||
parScene.AddChild(parGameControllerObject, consumableSpawnerObject);
|
||||
|
||||
return mapObject;
|
||||
}
|
||||
|
||||
@@ -150,7 +202,7 @@ public static class PlayScene
|
||||
return allCollidersObject;
|
||||
}
|
||||
|
||||
private static (GameObject, (WeaponView, HealthView, ScoreView, TimerView)) CreateGameUi(Engine.Engine parEngine,
|
||||
private static (GameObject, (WeaponView, HealthView, ScoreView, TimerView)) CreateGameUi(
|
||||
Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
@@ -166,7 +218,7 @@ public static class PlayScene
|
||||
});
|
||||
bottomContainerObject.AddComponent(new CopySizeComponent
|
||||
{
|
||||
Target = parUiContainer.GameObject.Transform, Coefficient = new Vector3(1, 0, 1)
|
||||
Target = parUiContainer.GameObject.Transform, Multiplier = new Vector3(1, 0, 1)
|
||||
});
|
||||
bottomContainerObject.Transform.Size.Y = 1.5f;
|
||||
parScene.AddChild(parentObject, bottomContainerObject);
|
||||
@@ -180,8 +232,8 @@ public static class PlayScene
|
||||
parScene.AddChild(bottomContainerObject, gunObject);
|
||||
|
||||
var (healthObject, healthUi, (_, healthTextRenderer)) =
|
||||
UiUtil.CreateTextUi(parScene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Здоровье: 000",
|
||||
TextAlignComponent.Align.Center,
|
||||
UiUtil.CreateTextUi(parScene, bottomContainer, UiUtil.GetDoomFont(), "Здоровье: 000",
|
||||
Align.Center,
|
||||
RenderLayer.HUD);
|
||||
healthObject.Transform.Scale = new Vector3(0.75f);
|
||||
healthUi.Anchor = Anchor.CenterLeft;
|
||||
@@ -189,8 +241,8 @@ public static class PlayScene
|
||||
parScene.AddChild(bottomContainerObject, healthObject);
|
||||
|
||||
var (ammoObject, ammoUi, (_, ammoTextRenderer)) =
|
||||
UiUtil.CreateTextUi(parScene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Патроны: 00/00",
|
||||
TextAlignComponent.Align.Center,
|
||||
UiUtil.CreateTextUi(parScene, bottomContainer, UiUtil.GetDoomFont(), "Патроны: 00/00",
|
||||
Align.Center,
|
||||
RenderLayer.HUD);
|
||||
ammoObject.Transform.Scale = new Vector3(0.75f);
|
||||
ammoUi.Anchor = Anchor.TopRight;
|
||||
@@ -198,8 +250,8 @@ public static class PlayScene
|
||||
parScene.AddChild(bottomContainerObject, ammoObject);
|
||||
|
||||
var (weaponObject, weaponUi, (_, weaponTextRenderer)) =
|
||||
UiUtil.CreateTextUi(parScene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Оружие: ОРУЖИЕОР",
|
||||
TextAlignComponent.Align.Center,
|
||||
UiUtil.CreateTextUi(parScene, bottomContainer, UiUtil.GetDoomFont(), "Оружие: ОРУЖИЕОР",
|
||||
Align.Center,
|
||||
RenderLayer.HUD);
|
||||
weaponObject.Transform.Scale = new Vector3(0.75f);
|
||||
weaponUi.Anchor = Anchor.BottomRight;
|
||||
@@ -214,20 +266,20 @@ public static class PlayScene
|
||||
new Box2DRenderer { Color = new Vector4(1, 0, 0, 1), RenderLayer = RenderLayer.HUD });
|
||||
topContainerObject.AddComponent(new CopySizeComponent
|
||||
{
|
||||
Target = parUiContainer.GameObject.Transform, Coefficient = new Vector3(1, 0, 1)
|
||||
Target = parUiContainer.GameObject.Transform, Multiplier = new Vector3(1, 0, 1)
|
||||
});
|
||||
topContainerObject.Transform.Size.Y = 1f;
|
||||
parScene.AddChild(parentObject, topContainerObject);
|
||||
|
||||
var (timerObject, timerUi, (_, timerTextRenderer)) =
|
||||
UiUtil.CreateTextUi(parScene, topContainer, UiUtil.GetDoomFont(parEngine), "Время: 00:00",
|
||||
UiUtil.CreateTextUi(parScene, topContainer, UiUtil.GetDoomFont(), "Время: 00:00",
|
||||
parRenderLayer: RenderLayer.HUD);
|
||||
timerUi.Anchor = Anchor.CenterLeft;
|
||||
timerUi.Center = Anchor.CenterLeft;
|
||||
parScene.AddChild(topContainerObject, timerObject);
|
||||
|
||||
var (scoreObject, scoreUi, (_, scoreTextRenderer)) =
|
||||
UiUtil.CreateTextUi(parScene, topContainer, UiUtil.GetDoomFont(parEngine), "Счет: 00000",
|
||||
UiUtil.CreateTextUi(parScene, topContainer, UiUtil.GetDoomFont(), "Счет: 00000",
|
||||
parRenderLayer: RenderLayer.HUD);
|
||||
scoreUi.Anchor = Anchor.CenterRight;
|
||||
scoreUi.Center = Anchor.CenterRight;
|
||||
@@ -241,7 +293,7 @@ public static class PlayScene
|
||||
return (parentObject, (weaponView, healthView, scoreView, timerView));
|
||||
}
|
||||
|
||||
private static GameObject CreateEscapeMenu(Engine.Engine parEngine, Engine.Scene.Scene parScene,
|
||||
private static GameObject CreateEscapeMenu(Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
var parentObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
@@ -251,20 +303,20 @@ public static class PlayScene
|
||||
backgroundObject.AddComponent(new CopySizeComponent { Target = parUiContainer.GameObject.Transform });
|
||||
parScene.AddChild(parentObject, backgroundObject);
|
||||
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, parScene, backgroundUiContainer, RenderLayer.HUD);
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parScene, backgroundUiContainer, RenderLayer.HUD);
|
||||
logoUi.Offset = new Vector2(0, 3f);
|
||||
parScene.AddChild(parentObject, logoObject);
|
||||
|
||||
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(parScene, backgroundUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Назад",
|
||||
UiUtil.GetDoomFont(), "Назад",
|
||||
parRenderLayer: RenderLayer.HUD);
|
||||
backUi.OnClick += parUiComponent =>
|
||||
parUiComponent.GameObject.Scene!.FindFirstComponent<GameController>()!.Unpause();
|
||||
backUi.OnClick += _ =>
|
||||
EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<GameController>()!.Unpause();
|
||||
|
||||
var (exitUiObject, exitUi, _) = UiUtil.CreateTextUi(parScene, backgroundUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Выход",
|
||||
UiUtil.GetDoomFont(), "Выход",
|
||||
parRenderLayer: RenderLayer.HUD);
|
||||
exitUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => MainScene.Create(parEngine));
|
||||
exitUi.OnClick += _ => EngineUtil.SceneManager.TransitionTo(MainScene.Create);
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
||||
new StackComponent
|
||||
|
||||
51
DoomDeathmatch/src/Scene/Play/Prefab/ConsumablePrefab.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using DoomDeathmatch.Component;
|
||||
using DoomDeathmatch.Component.MVC;
|
||||
using DoomDeathmatch.Component.Physics;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Script.Collision;
|
||||
using DoomDeathmatch.Script.Consumable;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene.Play.Prefab;
|
||||
|
||||
public static class ConsumablePrefab
|
||||
{
|
||||
public static GameObject Create(Engine.Scene.Scene parScene,
|
||||
IConsumable parConsumable, Transform? parBillboardTarget = null)
|
||||
{
|
||||
var box2DRenderer = new Box2DRenderer();
|
||||
var collider = new AABBColliderComponent
|
||||
{
|
||||
Collider = new AABBCollider { Size = new Vector3(1f) },
|
||||
Offset = new Vector3(0, 0f, 0.5f),
|
||||
ColliderGroups = { "consumable" },
|
||||
ExcludeColliderCollideGroups = { "enemy" }
|
||||
};
|
||||
|
||||
var consumableObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
[
|
||||
new ConsumableComponent(parConsumable, collider, box2DRenderer),
|
||||
new RigidbodyComponent(),
|
||||
|
||||
new DragComponent { Drag = 10f, Multiplier = new Vector3(1, 1, 0) },
|
||||
new MovementController { Speed = 10f },
|
||||
collider
|
||||
]
|
||||
);
|
||||
|
||||
var innerObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
new Transform { Translation = new Vector3(0, 0f, 0.5f), Size = new Vector3(1f) }, [
|
||||
box2DRenderer,
|
||||
new BillboardComponent { Target = parBillboardTarget }
|
||||
]
|
||||
);
|
||||
|
||||
parScene.AddChild(consumableObject, innerObject);
|
||||
|
||||
return consumableObject;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +1,74 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using DoomDeathmatch.Component.MVC;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
using DoomDeathmatch.Component.Physics;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
using DoomDeathmatch.Script.Collision;
|
||||
using DoomDeathmatch.Script.Model.Enemy;
|
||||
using DoomDeathmatch.Script.UI;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene.Play;
|
||||
namespace DoomDeathmatch.Scene.Play.Prefab;
|
||||
|
||||
public static class EnemyObject
|
||||
public static class EnemyPrefab
|
||||
{
|
||||
public static GameObject CreateEnemy(Engine.Engine parEngine, Engine.Scene.Scene parScene, EnemyData parEnemyData)
|
||||
public static GameObject Create(Engine.Scene.Scene parScene, EnemyData parEnemyData)
|
||||
{
|
||||
var enemyHealthTextRenderer = new TextRenderer { Font = UiUtil.GetDoomFont(parEngine), Text = "Здоровье: 000" };
|
||||
var displayBox2DRenderer = new Box2DRenderer();
|
||||
var enemyHealthTextRenderer = new TextRenderer { Font = UiUtil.GetDoomFont(), Text = "Здоровье: 000" };
|
||||
var enemyBox2DRenderer = new Box2DRenderer();
|
||||
|
||||
var demonObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
var enemyObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
[
|
||||
new EnemyController(parEnemyData),
|
||||
new EnemyView(displayBox2DRenderer),
|
||||
new EnemyView(enemyBox2DRenderer),
|
||||
|
||||
new HealthController(parEnemyData.BaseHealth),
|
||||
new EnemyHealthView(enemyHealthTextRenderer),
|
||||
|
||||
new MovementController(),
|
||||
new RigidbodyComponent(),
|
||||
new DragComponent { Drag = 5f, Coefficient = new Vector3(1, 1, 0) },
|
||||
new DragComponent { Drag = 5f, Multiplier = new Vector3(1, 1, 0) },
|
||||
|
||||
new AABBColliderComponent
|
||||
{
|
||||
Collider = new AABBCollider { Size = new Vector3(1, 1, 2) },
|
||||
Offset = new Vector3(0, 0f, 1f),
|
||||
ColliderGroups = { "enemy" },
|
||||
ExcludeColliderCollideGroups = { "enemy" }
|
||||
ExcludeColliderCollideGroups = { "fireball" }
|
||||
},
|
||||
new ColliderForceFieldComponent()
|
||||
]);
|
||||
var demonHealthContainer = GameObjectUtil.CreateGameObject(parScene,
|
||||
var enemyHealthContainer = GameObjectUtil.CreateGameObject(parScene,
|
||||
new Transform { Translation = new Vector3(0, 1.25f, 0f), Scale = new Vector3(0.2f) }
|
||||
);
|
||||
|
||||
var demonHealthObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||
var enemyHealthObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||
enemyHealthTextRenderer,
|
||||
new TextAlignComponent { Alignment = TextAlignComponent.Align.Center }
|
||||
new TextAlignComponent { Alignment = Align.Center }
|
||||
]
|
||||
);
|
||||
var demonVisualObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
var enemyVisualObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
new Transform { Translation = new Vector3(0, 0f, 1f), Size = new Vector3(1, 2, 1) }, [
|
||||
displayBox2DRenderer,
|
||||
enemyBox2DRenderer,
|
||||
new BillboardComponent(),
|
||||
]
|
||||
);
|
||||
|
||||
parScene.AddChild(demonObject, demonVisualObject);
|
||||
parScene.AddChild(demonVisualObject, demonHealthContainer);
|
||||
parScene.AddChild(demonHealthContainer, demonHealthObject);
|
||||
parScene.AddChild(enemyObject, enemyVisualObject);
|
||||
parScene.AddChild(enemyVisualObject, enemyHealthContainer);
|
||||
parScene.AddChild(enemyHealthContainer, enemyHealthObject);
|
||||
|
||||
return demonObject;
|
||||
return enemyObject;
|
||||
}
|
||||
|
||||
public static GameObject CreateImpFireball(Engine.Scene.Scene parScene, Vector3 parPosition,
|
||||
public static GameObject CreateFireball(Engine.Scene.Scene parScene, Vector3 parPosition,
|
||||
Vector3 parVelocity, float parDamage, Transform? parBillboardTarget = null)
|
||||
{
|
||||
var rigidbodyComponent = new RigidbodyComponent();
|
||||
@@ -72,15 +78,14 @@ public static class EnemyObject
|
||||
new Transform { Translation = parPosition, Size = new Vector3(0.5f) },
|
||||
[
|
||||
rigidbodyComponent,
|
||||
new Box2DRenderer
|
||||
{
|
||||
Texture = Engine.Engine.Instance.AssetResourceManager.Load<Texture>("texture/fireball.png")
|
||||
},
|
||||
new Box2DRenderer { Texture = EngineUtil.AssetResourceManager.Load<Texture>("texture/fireball.png") },
|
||||
new BillboardComponent { Target = parBillboardTarget },
|
||||
new FireballComponent { Damage = parDamage },
|
||||
new AABBColliderComponent
|
||||
{
|
||||
Collider = new AABBCollider { Size = new Vector3(0.25f) }, ExcludeColliderCollideGroups = { "enemy" }
|
||||
Collider = new AABBCollider { Size = new Vector3(0.25f) },
|
||||
ColliderGroups = { "fireball" },
|
||||
ExcludeColliderCollideGroups = { "enemy", "fireball", "consumable" }
|
||||
}
|
||||
]
|
||||
);
|
||||
47
DoomDeathmatch/src/Scene/Play/Prefab/PlayerPrefab.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using DoomDeathmatch.Component.MVC;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
using DoomDeathmatch.Component.MVC.Weapon;
|
||||
using DoomDeathmatch.Component.Physics;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Script.Collision;
|
||||
using Engine.Scene;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene.Play.Prefab;
|
||||
|
||||
public static class PlayerPrefab
|
||||
{
|
||||
public static GameObject Create(Engine.Scene.Scene parScene, WeaponView parWeaponView, HealthView parHealthView)
|
||||
{
|
||||
var (perspectiveCameraObject, perspectiveCamera) = UiUtil.CreatePerspectiveCamera(parScene);
|
||||
perspectiveCameraObject.Transform.Translation.Z = 2;
|
||||
var playerObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||
new PlayerMovementComponent(),
|
||||
|
||||
new MovementController { Speed = 10f },
|
||||
new RigidbodyComponent(),
|
||||
new DragComponent { Drag = 10f, Multiplier = new Vector3(1, 1, 0) },
|
||||
|
||||
new AABBColliderComponent
|
||||
{
|
||||
Collider = new AABBCollider { Size = new Vector3(1, 1, 2) },
|
||||
Offset = new Vector3(0, 0f, 1f),
|
||||
ColliderGroups = { "player" },
|
||||
ExcludeColliderCollideGroups = { "player" }
|
||||
},
|
||||
|
||||
new PlayerController(perspectiveCamera),
|
||||
|
||||
new WeaponController(),
|
||||
parWeaponView,
|
||||
|
||||
new HealthController(),
|
||||
parHealthView,
|
||||
]);
|
||||
|
||||
parScene.AddChild(playerObject, perspectiveCameraObject);
|
||||
|
||||
return playerObject;
|
||||
}
|
||||
}
|
||||
50
DoomDeathmatch/src/Script/AnimationPlayer.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Engine.Scene;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Script;
|
||||
|
||||
public class AnimationPlayer<T>(float parInterval) : IUpdate
|
||||
{
|
||||
public List<T> Frames { get; init; } = [];
|
||||
public bool IsPlaying { get; private set; } = false;
|
||||
public int NextFrame { get; private set; } = 0;
|
||||
|
||||
public event Action<T>? OnFrameChanged;
|
||||
public event Action? OnFinish;
|
||||
|
||||
private readonly TickableTimer _timer = new(parInterval);
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Reset();
|
||||
IsPlaying = true;
|
||||
}
|
||||
|
||||
public void Update(double parDeltaTime)
|
||||
{
|
||||
if (!IsPlaying)
|
||||
return;
|
||||
|
||||
_timer.Update(parDeltaTime);
|
||||
|
||||
if (_timer.CurrentTime < _timer.TotalTime * (1.0f - 1.0f / Frames.Count * NextFrame))
|
||||
{
|
||||
OnFrameChanged?.Invoke(Frames[NextFrame]);
|
||||
NextFrame++;
|
||||
}
|
||||
|
||||
if (_timer.IsFinished)
|
||||
{
|
||||
OnFinish?.Invoke();
|
||||
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_timer.Reset();
|
||||
NextFrame = 0;
|
||||
IsPlaying = false;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
namespace DoomDeathmatch.Script.Collision;
|
||||
|
||||
public class AABBCollider
|
||||
{
|
||||
@@ -42,12 +42,12 @@ public class AABBCollider
|
||||
var sign = Math.Sign(diff.Y);
|
||||
normal.Y = sign == 0 ? 1 : sign;
|
||||
}
|
||||
else
|
||||
else if (penZ < penX && penZ < penY)
|
||||
{
|
||||
var sign = Math.Sign(diff.Z);
|
||||
normal.Z = sign == 0 ? 1 : sign;
|
||||
}
|
||||
|
||||
return normal.Normalized();
|
||||
return normal;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Engine.Scene;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
namespace DoomDeathmatch.Script.Collision;
|
||||
|
||||
public class RaycastResult
|
||||
{
|
||||
12
DoomDeathmatch/src/Script/Condition/ICondition.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Engine.Scene;
|
||||
|
||||
namespace DoomDeathmatch.Script.Condition;
|
||||
|
||||
public interface ICondition : IUpdate
|
||||
{
|
||||
public event Action? OnTrue;
|
||||
|
||||
public bool IsTrue { get; }
|
||||
|
||||
public void Reset();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Script.Condition;
|
||||
|
||||
public class TickableTimerCondition : ICondition
|
||||
{
|
||||
public event Action? OnTrue;
|
||||
public bool IsTrue => _timer.IsFinished;
|
||||
|
||||
private readonly TickableTimer _timer;
|
||||
|
||||
public TickableTimerCondition(float parInterval)
|
||||
{
|
||||
_timer = new TickableTimer(parInterval);
|
||||
_timer.OnFinished += () => OnTrue?.Invoke();
|
||||
}
|
||||
|
||||
public void Update(double parDeltaTime)
|
||||
{
|
||||
_timer.Update(parDeltaTime);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_timer.Reset();
|
||||
}
|
||||
}
|
||||
13
DoomDeathmatch/src/Script/Consumable/HealthPackConsumable.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using DoomDeathmatch.Component.MVC;
|
||||
|
||||
namespace DoomDeathmatch.Script.Consumable;
|
||||
|
||||
public class HealthPackConsumable(float parHealth) : IConsumable
|
||||
{
|
||||
public string Icon => "texture/health_pack.png";
|
||||
|
||||
public void Consume(PlayerController parPlayerController)
|
||||
{
|
||||
parPlayerController.HealthController.Heal(parHealth);
|
||||
}
|
||||
}
|
||||
10
DoomDeathmatch/src/Script/Consumable/IConsumable.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using DoomDeathmatch.Component.MVC;
|
||||
|
||||
namespace DoomDeathmatch.Script.Consumable;
|
||||
|
||||
public interface IConsumable
|
||||
{
|
||||
public string Icon { get; }
|
||||
|
||||
public void Consume(PlayerController parPlayerController);
|
||||
}
|
||||
14
DoomDeathmatch/src/Script/Consumable/WeaponConsumable.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using DoomDeathmatch.Component.MVC;
|
||||
using DoomDeathmatch.Script.Model.Weapon;
|
||||
|
||||
namespace DoomDeathmatch.Script.Consumable;
|
||||
|
||||
public class WeaponConsumable(WeaponData parWeaponData) : IConsumable
|
||||
{
|
||||
public string Icon => parWeaponData.IdleTexture;
|
||||
|
||||
public void Consume(PlayerController parPlayerController)
|
||||
{
|
||||
parPlayerController.WeaponController.AddOrMergeWeapon(parWeaponData);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
|
||||
public abstract class AttackBehavior(EnemyController parEnemyController, HealthController parHealthController)
|
||||
{
|
||||
@@ -1,6 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
|
||||
public class CloseContinuousAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
@@ -1,7 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using Engine.Util;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
|
||||
public class CloseCooldownAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
@@ -1,6 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
|
||||
public class CompositeAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
@@ -1,7 +1,8 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
|
||||
public abstract class CooldownAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
@@ -1,6 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
|
||||
public class FuncAttackBehaviorCreator(Func<EnemyController, HealthController, AttackBehavior> parFunc)
|
||||
: IAttackBehaviorCreator
|
||||
@@ -1,6 +1,7 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
|
||||
public interface IAttackBehaviorCreator
|
||||
{
|
||||
@@ -1,7 +1,9 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Health;
|
||||
using Engine.Scene;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
|
||||
public class ObjectSpawnAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
@@ -19,7 +21,7 @@ public class ObjectSpawnAttackBehavior(
|
||||
protected override bool ActivateAttack()
|
||||
{
|
||||
var enemyObject = parObjectSpawnFunc(_enemyController, _healthController);
|
||||
_enemyController.GameObject.Scene!.Add(enemyObject);
|
||||
EngineUtil.SceneManager.CurrentScene!.Add(enemyObject);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy.Movement;
|
||||
using DoomDeathmatch.Scene.Play;
|
||||
using DoomDeathmatch.Scene.Play.Prefab;
|
||||
using DoomDeathmatch.Script.Model.Enemy.Attack;
|
||||
using DoomDeathmatch.Script.Model.Enemy.Movement;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy;
|
||||
|
||||
public class EnemyData
|
||||
{
|
||||
@@ -45,7 +46,7 @@ public class EnemyData
|
||||
(parHealthController.GameObject.Transform.Translation -
|
||||
parEnemyController.GameObject.Transform.Translation).Normalized();
|
||||
|
||||
var fireballObject = EnemyObject.CreateImpFireball(parEnemyController.GameObject.Scene!,
|
||||
var fireballObject = EnemyPrefab.CreateFireball(EngineUtil.SceneManager.CurrentScene!,
|
||||
parEnemyController.GameObject.Transform.Translation + new Vector3(0, 0f, 1.75f),
|
||||
direction * 25,
|
||||
35,
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Movement;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Movement;
|
||||
|
||||
public class FollowPlayerMovementBehavior(float parRadius) : IMovementBehavior
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Movement;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Movement;
|
||||
|
||||
public interface IMovementBehavior
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Movement;
|
||||
namespace DoomDeathmatch.Script.Model.Enemy.Movement;
|
||||
|
||||
public class StandingMovementBehavior : IMovementBehavior
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace DoomDeathmatch.Component.MVC.Model;
|
||||
namespace DoomDeathmatch.Script.Model;
|
||||
|
||||
public class HealthModel
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace DoomDeathmatch.Component.MVC.Model;
|
||||
namespace DoomDeathmatch.Script.Model;
|
||||
|
||||
public class ScoreModel
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
namespace DoomDeathmatch.Script.Model.Weapon;
|
||||
|
||||
public interface IShootPattern
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
namespace DoomDeathmatch.Script.Model.Weapon;
|
||||
|
||||
public class LineShootPattern : IShootPattern
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
namespace DoomDeathmatch.Script.Model.Weapon;
|
||||
|
||||
public class RandomFlatSpreadShootPattern(float parAngle, uint parCount) : IShootPattern
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
namespace DoomDeathmatch.Script.Model.Weapon;
|
||||
|
||||
public class WeaponData
|
||||
{
|
||||
@@ -9,7 +9,12 @@ public class WeaponData
|
||||
{
|
||||
Id = "pistol",
|
||||
Name = "Пистолет",
|
||||
Texture = "texture/pistol.png",
|
||||
IdleTexture = "texture/pistol/idle.png",
|
||||
FireAnimationDuration = 0.25f,
|
||||
FireAnimation =
|
||||
{
|
||||
"texture/pistol/fire1.png", "texture/pistol/fire2.png", "texture/pistol/fire3.png", "texture/pistol/fire4.png",
|
||||
},
|
||||
Damage = 30,
|
||||
ShootPattern = new LineShootPattern()
|
||||
};
|
||||
@@ -19,14 +24,18 @@ public class WeaponData
|
||||
{
|
||||
Id = "shotgun",
|
||||
Name = "Дробовик",
|
||||
Texture = "texture/shotgun.png",
|
||||
IdleTexture = "texture/shotgun/idle.png",
|
||||
FireAnimationDuration = 0.1f,
|
||||
FireAnimation = { "texture/shotgun/fire1.png", "texture/shotgun/fire2.png" },
|
||||
Damage = 5,
|
||||
ShootPattern = new RandomFlatSpreadShootPattern(MathHelper.DegreesToRadians(20), 40)
|
||||
ShootPattern = new RandomFlatSpreadShootPattern(MathHelper.DegreesToRadians(10), 40)
|
||||
};
|
||||
|
||||
public string Id { get; private init; } = "";
|
||||
public string Name { get; private init; } = "";
|
||||
public string Texture { get; private init; } = "";
|
||||
public string IdleTexture { get; private init; } = "";
|
||||
public float FireAnimationDuration { get; private init; } = 0;
|
||||
public List<string> FireAnimation { get; private init; } = [];
|
||||
public int Damage { get; private init; }
|
||||
public int MaxAmmo { get; }
|
||||
public IShootPattern ShootPattern { get; private init; }
|
||||
@@ -1,6 +1,6 @@
|
||||
using DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
using DoomDeathmatch.Script.Model.Weapon;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model;
|
||||
namespace DoomDeathmatch.Script.Model;
|
||||
|
||||
public class WeaponModel
|
||||
{
|
||||
40
DoomDeathmatch/src/Script/ObjectSpawner.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using DoomDeathmatch.Script.Condition;
|
||||
using DoomDeathmatch.Script.Provider;
|
||||
using Engine.Scene;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Script;
|
||||
|
||||
public class ObjectSpawner : IUpdate
|
||||
{
|
||||
public event Action<GameObject>? OnSpawned;
|
||||
|
||||
private readonly IValueProvider<GameObject> _gameObjectProvider;
|
||||
private readonly IValueProvider<Vector3> _positionProvider;
|
||||
private readonly ICondition _condition;
|
||||
|
||||
public ObjectSpawner(IValueProvider<GameObject> parGameObjectProvider, IValueProvider<Vector3> parPositionProvider,
|
||||
ICondition parCondition)
|
||||
{
|
||||
_gameObjectProvider = parGameObjectProvider;
|
||||
_positionProvider = parPositionProvider;
|
||||
_condition = parCondition;
|
||||
|
||||
_condition.OnTrue += Spawn;
|
||||
}
|
||||
|
||||
public void Update(double parDeltaTime)
|
||||
{
|
||||
_condition.Update(parDeltaTime);
|
||||
}
|
||||
|
||||
private void Spawn()
|
||||
{
|
||||
var gameObject = _gameObjectProvider.GetValue();
|
||||
var position = _positionProvider.GetValue();
|
||||
gameObject.Transform.Translation = position;
|
||||
|
||||
OnSpawned?.Invoke(gameObject);
|
||||
_condition.Reset();
|
||||
}
|
||||
}
|
||||
9
DoomDeathmatch/src/Script/Provider/ConstValueProvider.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace DoomDeathmatch.Script.Provider;
|
||||
|
||||
public class ConstValueProvider<T>(T parValue) : IValueProvider<T>
|
||||
{
|
||||
public T GetValue() => parValue;
|
||||
|
||||
public static ConstValueProvider<T> Create(T parValue) => new(parValue);
|
||||
public static implicit operator ConstValueProvider<T>(T parValue) => new(parValue);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace DoomDeathmatch.Script.Provider;
|
||||
|
||||
public class GeneratorValueProvider<T>(Func<T> parGenerator) : IValueProvider<T>
|
||||
{
|
||||
public T GetValue() => parGenerator();
|
||||
|
||||
public static GeneratorValueProvider<T> Create(Func<T> parGenerator) => new(parGenerator);
|
||||
public static implicit operator GeneratorValueProvider<T>(Func<T> parGenerator) => new(parGenerator);
|
||||
}
|
||||
6
DoomDeathmatch/src/Script/Provider/IValueProvider.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DoomDeathmatch.Script.Provider;
|
||||
|
||||
public interface IValueProvider<out T>
|
||||
{
|
||||
public T GetValue();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace DoomDeathmatch.Script.Provider;
|
||||
|
||||
public class RandomListValueProvider<T> : IValueProvider<T>
|
||||
{
|
||||
private readonly List<IValueProvider<T>> _providers = [];
|
||||
private readonly Random _random = new();
|
||||
|
||||
public RandomListValueProvider(IEnumerable<IValueProvider<T>> parProviders)
|
||||
{
|
||||
foreach (var provider in parProviders)
|
||||
{
|
||||
_providers.Add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
public T GetValue()
|
||||
{
|
||||
return _providers[_random.Next(_providers.Count)].GetValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace DoomDeathmatch.Script.Provider;
|
||||
|
||||
public class WeightedRandomValueProvider<T> : IValueProvider<T>
|
||||
{
|
||||
private readonly List<(int, IValueProvider<T>)> _providers = [];
|
||||
private readonly Random _random = new();
|
||||
private readonly int _totalWeight = 0;
|
||||
|
||||
public WeightedRandomValueProvider(IEnumerable<(int, IValueProvider<T>)> parProviders)
|
||||
{
|
||||
foreach (var (weight, provider) in parProviders)
|
||||
{
|
||||
_providers.Add((weight, provider));
|
||||
_totalWeight += weight;
|
||||
}
|
||||
|
||||
if (_totalWeight <= 0)
|
||||
throw new InvalidOperationException($"{nameof(WeightedRandomValueProvider<T>)} is empty");
|
||||
}
|
||||
|
||||
public T GetValue()
|
||||
{
|
||||
var random = _random.Next(_totalWeight);
|
||||
|
||||
foreach (var (weight, provider) in _providers)
|
||||
{
|
||||
if (random < weight)
|
||||
return provider.GetValue();
|
||||
|
||||
random -= weight;
|
||||
}
|
||||
|
||||
return default!; // unreachable
|
||||
}
|
||||
}
|
||||
16
DoomDeathmatch/src/Script/Score/ScoreRow.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace DoomDeathmatch.Script.Score;
|
||||
|
||||
[Serializable]
|
||||
[JsonSerializable(typeof(ScoreRow))]
|
||||
public class ScoreRow
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
[JsonInclude]
|
||||
public string Name { get; init; } = "";
|
||||
|
||||
[JsonPropertyName("score")]
|
||||
[JsonInclude]
|
||||
public int Score { get; init; }
|
||||
}
|
||||
71
DoomDeathmatch/src/Script/Score/ScoreTable.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace DoomDeathmatch.Script.Score;
|
||||
|
||||
[Serializable]
|
||||
[JsonSerializable(typeof(ScoreTable))]
|
||||
public class ScoreTable
|
||||
{
|
||||
private static readonly JsonSerializerOptions OPTIONS = new() { Converters = { new ScoreTableJsonConverter() } };
|
||||
|
||||
public List<ScoreRow> Rows { get; } = new();
|
||||
|
||||
public static ScoreTable LoadOrCreate(string parPath)
|
||||
{
|
||||
ScoreTable? table = null;
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = File.OpenRead(parPath);
|
||||
table = JsonSerializer.Deserialize<ScoreTable>(stream, OPTIONS);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (table != null)
|
||||
return table;
|
||||
|
||||
table = new ScoreTable();
|
||||
ScoreTable.Save(table, parPath);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
public static void Save(ScoreTable parTable, string parPath)
|
||||
{
|
||||
using var stream = File.Create(parPath);
|
||||
JsonSerializer.Serialize(stream, parTable, OPTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
public class ScoreTableJsonConverter : JsonConverter<ScoreTable>
|
||||
{
|
||||
public override ScoreTable? Read(
|
||||
ref Utf8JsonReader parReader,
|
||||
Type parTypeToConvert,
|
||||
JsonSerializerOptions? parOptions
|
||||
)
|
||||
{
|
||||
var rows = JsonSerializer.Deserialize<ScoreRow[]>(ref parReader, parOptions);
|
||||
if (rows == null)
|
||||
return null;
|
||||
|
||||
var scoreTable = new ScoreTable();
|
||||
foreach (var row in rows)
|
||||
{
|
||||
scoreTable.Rows.Add(row);
|
||||
}
|
||||
|
||||
scoreTable.Rows.Sort((parX, parY) => parY.Score.CompareTo(parX.Score));
|
||||
|
||||
return scoreTable;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter parWriter, ScoreTable parValue, JsonSerializerOptions parOptions)
|
||||
{
|
||||
JsonSerializer.Serialize(parWriter, parValue.Rows, parOptions);
|
||||
}
|
||||
}
|
||||
8
DoomDeathmatch/src/Script/UI/Align.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace DoomDeathmatch.Script.UI;
|
||||
|
||||
public enum Align
|
||||
{
|
||||
Left,
|
||||
Center,
|
||||
Right
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace DoomDeathmatch.Component.UI;
|
||||
namespace DoomDeathmatch.Script.UI;
|
||||
|
||||
public enum Anchor
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace DoomDeathmatch.Component.UI;
|
||||
namespace DoomDeathmatch.Script.UI;
|
||||
|
||||
public enum Orientation
|
||||
{
|
||||
@@ -1,20 +1,21 @@
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Script.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;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch;
|
||||
|
||||
public static class UiUtil
|
||||
{
|
||||
public static Font GetDoomFont(Engine.Engine parEngine)
|
||||
public static Font GetDoomFont()
|
||||
{
|
||||
return parEngine.AssetResourceManager.Load<Font>("font/doom");
|
||||
return EngineUtil.AssetResourceManager.Load<Font>("font/doom");
|
||||
}
|
||||
|
||||
public static (GameObject, OrthographicCamera) CreateOrthographicCamera(Engine.Scene.Scene parScene,
|
||||
@@ -41,7 +42,7 @@ public static class UiUtil
|
||||
|
||||
public static (GameObject, UiContainerComponent, (GameObject, TextRenderer)) CreateTextUi(Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parContainer,
|
||||
Font parFont, string parText, TextAlignComponent.Align parAlign = TextAlignComponent.Align.Center,
|
||||
Font parFont, string parText, Align parAlign = Align.Center,
|
||||
RenderLayer? parRenderLayer = null)
|
||||
{
|
||||
var size = parFont.Measure(parText);
|
||||
@@ -96,7 +97,7 @@ public static class UiUtil
|
||||
return (uiContainerObject, uiContainer);
|
||||
}
|
||||
|
||||
public static (GameObject, UiComponent) CreateLogoUi(Engine.Engine parEngine, Engine.Scene.Scene parScene,
|
||||
public static (GameObject, UiComponent) CreateLogoUi(Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parContainer, RenderLayer? parRenderLayer = null)
|
||||
{
|
||||
UiComponent uiComponent;
|
||||
@@ -107,7 +108,7 @@ public static class UiUtil
|
||||
}, [
|
||||
new Box2DRenderer
|
||||
{
|
||||
Texture = parEngine.AssetResourceManager.Load<Texture>("texture/doom_logo.png"),
|
||||
Texture = EngineUtil.AssetResourceManager.Load<Texture>("texture/doom_logo.png"),
|
||||
RenderLayer = parRenderLayer ?? RenderLayer.DEFAULT
|
||||
},
|
||||
uiComponent = new UiComponent { Container = parContainer, Anchor = Anchor.Center }
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Engine;
|
||||
|
||||
public sealed class Engine
|
||||
{
|
||||
public static Engine Instance { get; private set; } = null!;
|
||||
internal static Engine Instance { get; private set; } = null!;
|
||||
|
||||
public Renderer Renderer { get; }
|
||||
public SceneManager SceneManager { get; } = new();
|
||||
@@ -61,12 +61,16 @@ public sealed class Engine
|
||||
|
||||
internal ResourceManager EngineResourceManager => _engineResourceManager;
|
||||
public IResourceManager AssetResourceManager => _assetResourceManager;
|
||||
|
||||
public string DataFolder { get; }
|
||||
|
||||
private readonly ResourceManager _engineResourceManager;
|
||||
private readonly ResourceManager _assetResourceManager;
|
||||
|
||||
private Thread? _updateThread;
|
||||
|
||||
public Engine(int parWidth, int parHeight, bool parHeadless, string parTitle, string parAssetFolder,
|
||||
string parDataFolder,
|
||||
ILogger parLogger)
|
||||
{
|
||||
if (Instance != null)
|
||||
@@ -95,6 +99,10 @@ public sealed class Engine
|
||||
|
||||
_logger.Information("Created asset resource manager in {AssetFolder}", parAssetFolder);
|
||||
|
||||
DataFolder = parDataFolder;
|
||||
|
||||
_logger.Information("Using data folder {DataFolder}", parDataFolder);
|
||||
|
||||
Renderer = new Renderer(this, parWidth, parHeight, settings);
|
||||
Window = new Window(this, Renderer.NativeWindow, parHeadless);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ public sealed class EngineBuilder
|
||||
private bool _headless;
|
||||
private int _width = 1;
|
||||
private int _height = 1;
|
||||
private string _assetFolder = "./";
|
||||
private string _assetFolder = "./asset";
|
||||
private string _dataFolder = "./data";
|
||||
|
||||
private Func<Engine, IPresenter>? _presenterFunc;
|
||||
private Func<Engine, IInputHandler>? _inputHandlerFunc;
|
||||
@@ -58,6 +59,12 @@ public sealed class EngineBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
public EngineBuilder DataFolder(string parDataFolder)
|
||||
{
|
||||
_dataFolder = parDataFolder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EngineBuilder Presenter(Func<Engine, IPresenter> parPresenterFunc)
|
||||
{
|
||||
_presenterFunc = parPresenterFunc;
|
||||
@@ -101,7 +108,7 @@ public sealed class EngineBuilder
|
||||
public Engine Build()
|
||||
{
|
||||
var logger = BuildLogger();
|
||||
var engine = new Engine(_width, _height, _headless, _title, _assetFolder, logger);
|
||||
var engine = new Engine(_width, _height, _headless, _title, _assetFolder, _dataFolder, logger);
|
||||
|
||||
var presenter = _presenterFunc?.Invoke(engine);
|
||||
if (presenter != null)
|
||||
|
||||
@@ -24,7 +24,7 @@ public class RenderLayer : IComparable<RenderLayer>
|
||||
|
||||
public int CompareTo(RenderLayer? parOther)
|
||||
{
|
||||
return _order.CompareTo(parOther?._order);
|
||||
return parOther == null ? 1 : _order.CompareTo(parOther._order);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
|
||||
@@ -62,4 +62,61 @@ public enum KeyboardButtonCode
|
||||
PageDown,
|
||||
|
||||
TotalCount = PageDown + 1
|
||||
}
|
||||
|
||||
public static class KeyboardButtonCodeHelper
|
||||
{
|
||||
public static List<KeyboardButtonCode> GetAllPrintableKeys() =>
|
||||
Enum.GetValues<KeyboardButtonCode>().Where(parX => parX.IsPrintableKey()).ToList();
|
||||
|
||||
public static bool IsPrintableKey(this KeyboardButtonCode parKey) =>
|
||||
parKey is >= KeyboardButtonCode.A and <= KeyboardButtonCode.Z
|
||||
or >= KeyboardButtonCode.D1 and <= KeyboardButtonCode.D0 or KeyboardButtonCode.Space;
|
||||
|
||||
public static char GetChar(this KeyboardButtonCode parKey)
|
||||
{
|
||||
return parKey switch
|
||||
{
|
||||
KeyboardButtonCode.A => 'A',
|
||||
KeyboardButtonCode.B => 'B',
|
||||
KeyboardButtonCode.C => 'C',
|
||||
KeyboardButtonCode.D => 'D',
|
||||
KeyboardButtonCode.E => 'E',
|
||||
KeyboardButtonCode.F => 'F',
|
||||
KeyboardButtonCode.G => 'G',
|
||||
KeyboardButtonCode.H => 'H',
|
||||
KeyboardButtonCode.I => 'I',
|
||||
KeyboardButtonCode.J => 'J',
|
||||
KeyboardButtonCode.K => 'K',
|
||||
KeyboardButtonCode.L => 'L',
|
||||
KeyboardButtonCode.M => 'M',
|
||||
KeyboardButtonCode.N => 'N',
|
||||
KeyboardButtonCode.O => 'O',
|
||||
KeyboardButtonCode.P => 'P',
|
||||
KeyboardButtonCode.Q => 'Q',
|
||||
KeyboardButtonCode.R => 'R',
|
||||
KeyboardButtonCode.S => 'S',
|
||||
KeyboardButtonCode.T => 'T',
|
||||
KeyboardButtonCode.U => 'U',
|
||||
KeyboardButtonCode.V => 'V',
|
||||
KeyboardButtonCode.W => 'W',
|
||||
KeyboardButtonCode.X => 'X',
|
||||
KeyboardButtonCode.Y => 'Y',
|
||||
KeyboardButtonCode.Z => 'Z',
|
||||
|
||||
KeyboardButtonCode.D1 => '1',
|
||||
KeyboardButtonCode.D2 => '2',
|
||||
KeyboardButtonCode.D3 => '3',
|
||||
KeyboardButtonCode.D4 => '4',
|
||||
KeyboardButtonCode.D5 => '5',
|
||||
KeyboardButtonCode.D6 => '6',
|
||||
KeyboardButtonCode.D7 => '7',
|
||||
KeyboardButtonCode.D8 => '8',
|
||||
KeyboardButtonCode.D9 => '9',
|
||||
KeyboardButtonCode.D0 => '0',
|
||||
|
||||
KeyboardButtonCode.Space => ' ',
|
||||
_ => '\0'
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ public abstract class Component : IUpdate, IRender
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void PreUpdate()
|
||||
public virtual void PreUpdate(double parDeltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public sealed class GameObject : IUpdate, IRender
|
||||
|
||||
public Transform Transform { get; }
|
||||
|
||||
public Scene? Scene { get; set; }
|
||||
internal Scene? Scene { get; set; }
|
||||
|
||||
private readonly Queue<Action> _componentActions = new();
|
||||
|
||||
@@ -47,14 +47,14 @@ public sealed class GameObject : IUpdate, IRender
|
||||
Transform = GetComponent<Transform>()!;
|
||||
}
|
||||
|
||||
public void PreUpdate()
|
||||
public void PreUpdate(double parDeltaTime)
|
||||
{
|
||||
ProcessAddedComponents();
|
||||
ProcessRemovedComponents();
|
||||
|
||||
foreach (var component in _components)
|
||||
{
|
||||
component.PreUpdate();
|
||||
component.PreUpdate(parDeltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@ public class Scene : IUpdate, IRender
|
||||
return Hierarchy.Objects
|
||||
.Where(parGameObject => !parOnlyEnabled || parGameObject.IsEnabled)
|
||||
.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||
.Where(parComponent => parComponent != null).ToList()!;
|
||||
.Where(parComponent => parComponent != null)
|
||||
.Distinct()
|
||||
.ToList()!;
|
||||
}
|
||||
|
||||
public T? FindFirstComponent<T>() where T : Component.Component
|
||||
@@ -61,7 +63,7 @@ public class Scene : IUpdate, IRender
|
||||
|
||||
foreach (var gameObject in hierarchyObjects)
|
||||
{
|
||||
gameObject.PreUpdate();
|
||||
gameObject.PreUpdate(parDeltaTime * TimeScale);
|
||||
}
|
||||
|
||||
foreach (var gameObject in hierarchyObjects)
|
||||
|
||||
24
Engine/src/Util/EngineUtil.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Engine.Input;
|
||||
using Engine.Resource;
|
||||
using Engine.Scene;
|
||||
|
||||
namespace Engine.Util;
|
||||
|
||||
public static class EngineUtil
|
||||
{
|
||||
public static IInputHandler InputHandler => Engine.Instance.InputHandler!;
|
||||
public static SceneManager SceneManager => Engine.Instance.SceneManager;
|
||||
public static IResourceManager AssetResourceManager => Engine.Instance.AssetResourceManager;
|
||||
public static string DataFolder => Engine.Instance.DataFolder;
|
||||
|
||||
public static void CreateObject(GameObject parGameObject)
|
||||
{
|
||||
var scene = Engine.Instance.SceneManager.CurrentScene!;
|
||||
scene.Add(parGameObject);
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
Engine.Instance.Close();
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,9 @@ public class TickableTimer
|
||||
if (value > TotalTime)
|
||||
value = TotalTime;
|
||||
|
||||
if (value == _currentTime)
|
||||
return;
|
||||
|
||||
_currentTime = value;
|
||||
OnUpdate?.Invoke(value);
|
||||
|
||||
|
||||