.
This commit is contained in:
@@ -36,7 +36,7 @@ public class ConsumableComponent : Engine.Scene.Component.Component
|
||||
/// <summary>
|
||||
/// Controls the movement of the consumable object.
|
||||
/// </summary>
|
||||
private MovementController _movementController = null!;
|
||||
private MovementComponent _movementComponent = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConsumableComponent"/> class.
|
||||
@@ -57,9 +57,9 @@ public class ConsumableComponent : Engine.Scene.Component.Component
|
||||
_collider.OnCollision += OnCollision;
|
||||
_box2DRenderer.Texture = EngineUtil.AssetResourceManager.Load<Texture>(_consumable.Icon);
|
||||
|
||||
_movementController = GameObject.GetComponent<MovementController>()!;
|
||||
_movementComponent = GameObject.GetComponent<MovementComponent>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_movementController);
|
||||
ArgumentNullException.ThrowIfNull(_movementComponent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,7 +113,7 @@ public class ConsumableComponent : Engine.Scene.Component.Component
|
||||
direction.Normalize();
|
||||
}
|
||||
|
||||
_movementController.ApplyMovement(direction);
|
||||
_movementComponent.ApplyMovement(direction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -30,7 +30,7 @@ public class EnemyController : Engine.Scene.Component.Component
|
||||
/// <summary>
|
||||
/// Controls the movement of the enemy.
|
||||
/// </summary>
|
||||
private MovementController _movementController = null!;
|
||||
private MovementComponent _movementComponent = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the enemy's attack behavior.
|
||||
@@ -56,19 +56,19 @@ public class EnemyController : Engine.Scene.Component.Component
|
||||
_gameController = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<GameController>()!;
|
||||
HealthController = GameObject.GetComponent<HealthController>()!;
|
||||
_enemyView = GameObject.GetComponent<IView<EnemyData>>()!;
|
||||
_movementController = GameObject.GetComponent<MovementController>()!;
|
||||
_movementComponent = GameObject.GetComponent<MovementComponent>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_gameController);
|
||||
ArgumentNullException.ThrowIfNull(HealthController);
|
||||
ArgumentNullException.ThrowIfNull(_enemyView);
|
||||
ArgumentNullException.ThrowIfNull(_movementController);
|
||||
ArgumentNullException.ThrowIfNull(_movementComponent);
|
||||
|
||||
_attackBehavior = _enemyData.AttackBehaviorCreator.Create(this, _gameController.PlayerController.HealthController);
|
||||
|
||||
HealthController.SetMaxHealth(_enemyData.BaseHealth);
|
||||
HealthController.OnDeath += OnDeath;
|
||||
_enemyView.UpdateView(_enemyData);
|
||||
_movementController.Speed = _enemyData.BaseSpeed;
|
||||
_movementComponent.Speed = _enemyData.BaseSpeed;
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
@@ -92,7 +92,7 @@ public class EnemyController : Engine.Scene.Component.Component
|
||||
if (enemyPosition != nextPosition)
|
||||
{
|
||||
var direction = (nextPosition - enemyPosition).Normalized();
|
||||
_movementController.ApplyMovement(direction);
|
||||
_movementComponent.ApplyMovement(direction);
|
||||
}
|
||||
|
||||
_attackBehavior.Attack(parDeltaTime);
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace DoomDeathmatch.Component.MVC;
|
||||
/// <summary>
|
||||
/// Controls the movement logic for a game object, applying movement forces to the rigidbody.
|
||||
/// </summary>
|
||||
public class MovementController : Engine.Scene.Component.Component
|
||||
public class MovementComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The movement speed.
|
||||
@@ -1,7 +1,6 @@
|
||||
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.Util;
|
||||
@@ -71,6 +70,34 @@ public class PlayerController : Engine.Scene.Component.Component
|
||||
return;
|
||||
}
|
||||
|
||||
ExecuteWeaponSelection();
|
||||
|
||||
ExecuteWeaponShooting();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles weapon shooting.
|
||||
/// </summary>
|
||||
private void ExecuteWeaponShooting()
|
||||
{
|
||||
if (!_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Space))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WeaponController.TryShoot())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ShootSelectedWeapon();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles weapon selection.
|
||||
/// </summary>
|
||||
private void ExecuteWeaponSelection()
|
||||
{
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (KeyboardButtonCode.D1 + i > KeyboardButtonCode.D0)
|
||||
@@ -86,38 +113,20 @@ public class PlayerController : Engine.Scene.Component.Component
|
||||
WeaponController.SelectWeapon(i);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Space))
|
||||
{
|
||||
if (!WeaponController.TryShoot())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HandleShootingRaycast();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the shooting raycast for the player.
|
||||
/// Shoots the selected weapon.
|
||||
/// </summary>
|
||||
private void HandleShootingRaycast()
|
||||
private void ShootSelectedWeapon()
|
||||
{
|
||||
var position = Camera.GameObject.Transform.GetFullTranslation();
|
||||
var forward = (Camera.Forward - position).Normalized();
|
||||
var right = Vector3.Cross(forward, Vector3.UnitZ).Normalized();
|
||||
var up = Vector3.UnitZ;
|
||||
var groups = new HashSet<string> { "enemy", "wall" };
|
||||
|
||||
var collisionManager = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<CollisionManagerComponent>();
|
||||
|
||||
var offsets = WeaponController.WeaponData.ShootPattern.GetShootPattern(forward, Vector3.UnitZ, right);
|
||||
foreach (var offset in offsets)
|
||||
foreach (var result in WeaponController.RaycastWeaponShooting(position, forward, up, groups))
|
||||
{
|
||||
var direction = forward + offset;
|
||||
if (!collisionManager!.Raycast(position, direction, ["enemy", "wall"], out var result))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var enemyController = result.HitObject.GetComponent<EnemyController>();
|
||||
enemyController?.HealthController.TakeDamage(WeaponController.WeaponData.Damage);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using DoomDeathmatch.Script.Model;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using DoomDeathmatch.Script.Collision;
|
||||
using DoomDeathmatch.Script.Model;
|
||||
using DoomDeathmatch.Script.Model.Weapon;
|
||||
using Engine.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Weapon;
|
||||
|
||||
@@ -21,21 +25,25 @@ public class WeaponController : Engine.Scene.Component.Component
|
||||
/// <summary>
|
||||
/// The internal weapon model containing weapon-related data.
|
||||
/// </summary>
|
||||
private readonly WeaponModel _weaponModel = new();
|
||||
private readonly WeaponModel _weaponModel;
|
||||
|
||||
/// <summary>
|
||||
/// View responsible for displaying weapon information and animations.
|
||||
/// </summary>
|
||||
private IWeaponView _weaponView = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeaponController"/> class with an initial weapon.
|
||||
/// </summary>
|
||||
/// <param name="parInitialWeapon">The initial weapon.</param>
|
||||
public WeaponController(WeaponData parInitialWeapon)
|
||||
{
|
||||
_weaponModel = new WeaponModel(parInitialWeapon);
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_weaponView = GameObject.GetComponent<IWeaponView>()!;
|
||||
_weaponView.UpdateView(_weaponModel.SelectedWeapon);
|
||||
_weaponView.UpdateView(new AmmoData
|
||||
{
|
||||
Ammo = _weaponModel.SelectedWeapon.Ammo, MaxAmmo = _weaponModel.SelectedWeapon.MaxAmmo
|
||||
});
|
||||
|
||||
_weaponModel.OnWeaponSelected += WeaponSelected;
|
||||
WeaponSelected(null, _weaponModel.SelectedWeapon);
|
||||
@@ -129,6 +137,34 @@ public class WeaponController : Engine.Scene.Component.Component
|
||||
_weaponModel.SelectedWeaponIndex = parIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raycasts for the shooting pattern of the selected weapon.
|
||||
/// </summary>
|
||||
/// <param name="parPosition">The position of the player or entity.</param>
|
||||
/// <param name="parForward">The forward direction of the player or entity.</param>
|
||||
/// <param name="parUp">The up direction of the player or entity.</param>
|
||||
/// <param name="parGroups">The collider groups to consider for the raycast.</param>
|
||||
/// <returns>The raycast results.</returns>
|
||||
public IEnumerable<RaycastResult> RaycastWeaponShooting(Vector3 parPosition, Vector3 parForward, Vector3 parUp,
|
||||
HashSet<string> parGroups)
|
||||
{
|
||||
var right = Vector3.Cross(parForward, parUp).Normalized();
|
||||
|
||||
var collisionManager = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent<CollisionManagerComponent>();
|
||||
|
||||
var offsets = WeaponData.ShootPattern.GetShootPattern(parForward, parUp, right);
|
||||
foreach (var offset in offsets)
|
||||
{
|
||||
var direction = parForward + offset;
|
||||
if (!collisionManager!.Raycast(parPosition, direction, parGroups, out var result))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the view when the selected weapon changes.
|
||||
/// </summary>
|
||||
@@ -143,8 +179,13 @@ public class WeaponController : Engine.Scene.Component.Component
|
||||
|
||||
parNewWeapon.OnAmmoChanged += AmmoChanged;
|
||||
_weaponView.UpdateView(parNewWeapon);
|
||||
AmmoChanged(parNewWeapon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the ammo view when the selected weapon's ammo changes.
|
||||
/// </summary>
|
||||
/// <param name="parWeapon">The updated weapon.</param>
|
||||
private void AmmoChanged(WeaponData parWeapon)
|
||||
{
|
||||
var ammoData = new AmmoData { Ammo = parWeapon.Ammo, MaxAmmo = parWeapon.MaxAmmo };
|
||||
|
||||
@@ -31,7 +31,7 @@ public class SelectorComponent : Engine.Scene.Component.Component
|
||||
/// <summary>
|
||||
/// The key used to select the currently selected item.
|
||||
/// </summary>
|
||||
public KeyboardButtonCode SelectKey { get; set; } = KeyboardButtonCode.Space;
|
||||
public KeyboardButtonCode SelectKey { get; set; } = KeyboardButtonCode.Enter;
|
||||
|
||||
/// <summary>
|
||||
/// The input handler used to check for keyboard input.
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace DoomDeathmatch.Component.Util;
|
||||
public class PlayerMovementComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The speed at which the player rotates.
|
||||
/// The speed at which the player rotates in degrees per second.
|
||||
/// </summary>
|
||||
public float RotationSpeed { get; set; } = 110.0f;
|
||||
|
||||
@@ -23,13 +23,13 @@ public class PlayerMovementComponent : Engine.Scene.Component.Component
|
||||
/// <summary>
|
||||
/// Controls the movement logic of the player.
|
||||
/// </summary>
|
||||
private MovementController _movementController = null!;
|
||||
private MovementComponent _movementComponent = null!;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_movementController = GameObject.GetComponent<MovementController>()!;
|
||||
_movementComponent = GameObject.GetComponent<MovementComponent>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_movementController);
|
||||
ArgumentNullException.ThrowIfNull(_movementComponent);
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
@@ -72,7 +72,7 @@ public class PlayerMovementComponent : Engine.Scene.Component.Component
|
||||
movement.Normalize();
|
||||
movement = GameObject.Transform.Rotation * movement;
|
||||
|
||||
_movementController.ApplyMovement(movement);
|
||||
_movementComponent.ApplyMovement(movement);
|
||||
}
|
||||
|
||||
GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitZ, MathHelper.DegreesToRadians(rotation) *
|
||||
|
||||
@@ -64,6 +64,7 @@ public static class GameConstants
|
||||
|
||||
public const float PLAYER_BASE_HEALTH = 100;
|
||||
public const float PLAYER_BASE_SPEED = 10;
|
||||
public const float PLAYER_BASE_ROTATION_SPEED = 110;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -314,6 +314,10 @@ public static class PlayScene
|
||||
logoUi.Offset = new Vector2(0, 3f);
|
||||
parScene.AddChild(parentObject, logoObject);
|
||||
|
||||
var (pauseUiObject, pauseUi, _) = UiPrefabs.CreateTextUi(parScene, backgroundUiContainer,
|
||||
UiPrefabs.GetDoomFont(), "Пауза",
|
||||
parRenderLayer: RenderLayer.HUD, parScale: 1.5f);
|
||||
|
||||
var (backUiObject, backUi, _) = UiPrefabs.CreateTextUi(parScene, backgroundUiContainer,
|
||||
UiPrefabs.GetDoomFont(), "Назад",
|
||||
parRenderLayer: RenderLayer.HUD);
|
||||
@@ -328,7 +332,7 @@ public static class PlayScene
|
||||
var (stackObject, stack) = UiPrefabs.CreateStackUi(parScene,
|
||||
new StackComponent
|
||||
{
|
||||
Offset = new Vector2(0, -1f), Container = backgroundUiContainer, Children = { backUi, exitUi }
|
||||
Offset = new Vector2(0, -1f), Container = backgroundUiContainer, Children = { pauseUi, backUi, exitUi }
|
||||
});
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
@@ -337,6 +341,8 @@ public static class PlayScene
|
||||
|
||||
parScene.AddChild(parentObject, selectorObject);
|
||||
parScene.AddChild(parentObject, stackObject);
|
||||
|
||||
parScene.AddChild(stackObject, pauseUiObject);
|
||||
parScene.AddChild(stackObject, backUiObject);
|
||||
parScene.AddChild(stackObject, exitUiObject);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public static class ConsumablePrefab
|
||||
new RigidbodyComponent(),
|
||||
|
||||
new DragComponent { Drag = 10f, Multiplier = new Vector3(1, 1, 0) },
|
||||
new MovementController { Speed = 10f },
|
||||
new MovementComponent { Speed = 10f },
|
||||
collider
|
||||
]
|
||||
);
|
||||
|
||||
@@ -32,7 +32,7 @@ public static class EnemyPrefab
|
||||
new HealthController(parEnemyData.BaseHealth),
|
||||
new EnemyHealthView(enemyHealthTextRenderer),
|
||||
|
||||
new MovementController(),
|
||||
new MovementComponent(),
|
||||
new RigidbodyComponent(),
|
||||
new DragComponent { Drag = 5f, Multiplier = new Vector3(1, 1, 0) },
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using DoomDeathmatch.Component.Physics;
|
||||
using DoomDeathmatch.Component.Physics.Collision;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Script.Collision;
|
||||
using DoomDeathmatch.Script.Model.Weapon;
|
||||
using Engine.Scene;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
@@ -17,9 +18,6 @@ public static class PlayerPrefab
|
||||
var (perspectiveCameraObject, perspectiveCamera) = UiPrefabs.CreatePerspectiveCamera(parScene);
|
||||
perspectiveCameraObject.Transform.Translation.Z = 2;
|
||||
var playerObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||
new PlayerMovementComponent(),
|
||||
|
||||
new MovementController { Speed = GameConstants.PLAYER_BASE_SPEED },
|
||||
new RigidbodyComponent(),
|
||||
new DragComponent { Drag = 10f, Multiplier = new Vector3(1, 1, 0) },
|
||||
|
||||
@@ -31,9 +29,12 @@ public static class PlayerPrefab
|
||||
ExcludeColliderCollideGroups = { "player" }
|
||||
},
|
||||
|
||||
new MovementComponent { Speed = GameConstants.PLAYER_BASE_SPEED },
|
||||
new PlayerMovementComponent { RotationSpeed = GameConstants.PLAYER_BASE_ROTATION_SPEED },
|
||||
|
||||
new PlayerController(perspectiveCamera),
|
||||
|
||||
new WeaponController(),
|
||||
new WeaponController(WeaponData.Pistol),
|
||||
parWeaponView,
|
||||
|
||||
new HealthController(GameConstants.PLAYER_BASE_HEALTH),
|
||||
|
||||
@@ -43,12 +43,15 @@ public static class UiPrefabs
|
||||
public static (GameObject, UiContainerComponent, (GameObject, TextRenderer)) CreateTextUi(Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parContainer,
|
||||
Font parFont, string parText, Align parAlign = Align.Center,
|
||||
RenderLayer? parRenderLayer = null)
|
||||
RenderLayer? parRenderLayer = null, float parScale = 1)
|
||||
{
|
||||
var size = parFont.Measure(parText);
|
||||
var outerObject = new GameObject
|
||||
{
|
||||
Transform = { Size = new Vector3(size.X, size.Y, 1f), Translation = new Vector3(0, 0, -1) }
|
||||
Transform =
|
||||
{
|
||||
Size = new Vector3(size.X, size.Y, 1f), Translation = new Vector3(0, 0, -1), Scale = new Vector3(parScale)
|
||||
}
|
||||
};
|
||||
var uiComponent = new UiContainerComponent { Container = parContainer };
|
||||
outerObject.AddComponent(uiComponent);
|
||||
|
||||
@@ -48,10 +48,19 @@ public class WeaponModel
|
||||
/// <summary>
|
||||
/// The list of available weapons for the player or entity.
|
||||
/// </summary>
|
||||
private readonly List<WeaponData> _weapons = [WeaponData.Pistol];
|
||||
private readonly List<WeaponData> _weapons;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the currently selected weapon in the weapons list.
|
||||
/// </summary>
|
||||
private int _selectedWeaponIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeaponModel"/> class with an initial weapon.
|
||||
/// </summary>
|
||||
/// <param name="parInitialWeapon">The initial weapon.</param>
|
||||
public WeaponModel(WeaponData parInitialWeapon)
|
||||
{
|
||||
_weapons = [parInitialWeapon];
|
||||
}
|
||||
}
|
||||
@@ -81,36 +81,36 @@ float perceptualColorDistance(vec3 color1, vec3 color2) {
|
||||
return dot(delta, delta);
|
||||
}
|
||||
|
||||
int findMostPerceptuallyAccurateColor(vec3 color) {
|
||||
int bestMatchIndex = 0;
|
||||
float minDistance = perceptualColorDistance(color, ConsoleColorVec3[0]);
|
||||
|
||||
for (int i = 1; i < 16; i++) {
|
||||
float currentDistance = perceptualColorDistance(color, ConsoleColorVec3[i]);
|
||||
if (currentDistance < minDistance) {
|
||||
minDistance = currentDistance;
|
||||
bestMatchIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestMatchIndex;
|
||||
}
|
||||
|
||||
//int findMostPerceptuallyAccurateColor(vec3 color) {
|
||||
// int closestIndex = 0;
|
||||
// float minDistance = distance(color, ConsoleColorVec3[0]);
|
||||
// int bestMatchIndex = 0;
|
||||
// float minDistance = perceptualColorDistance(color, ConsoleColorVec3[0]);
|
||||
//
|
||||
// for (int i = 1; i < 16; i++) {
|
||||
// float dist = distance(color, ConsoleColorVec3[i]);
|
||||
// if (dist < minDistance) {
|
||||
// minDistance = dist;
|
||||
// closestIndex = i;
|
||||
// float currentDistance = perceptualColorDistance(color, ConsoleColorVec3[i]);
|
||||
// if (currentDistance < minDistance) {
|
||||
// minDistance = currentDistance;
|
||||
// bestMatchIndex = i;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return closestIndex;
|
||||
// return bestMatchIndex;
|
||||
//}
|
||||
|
||||
int findMostPerceptuallyAccurateColor(vec3 color) {
|
||||
int closestIndex = 0;
|
||||
float minDistance = distance(color, ConsoleColorVec3[0]);
|
||||
|
||||
for (int i = 1; i < 16; i++) {
|
||||
float dist = distance(color, ConsoleColorVec3[i]);
|
||||
if (dist < minDistance) {
|
||||
minDistance = dist;
|
||||
closestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return closestIndex;
|
||||
}
|
||||
|
||||
// Enhanced luminosity calculation considering human perception
|
||||
float calculatePerceptualLuminance(vec3 color) {
|
||||
// BT.709 luminance coefficients with slight adjustment
|
||||
|
||||
Reference in New Issue
Block a user