Compare commits
99 Commits
breaking-c
...
feat/netwo
Author | SHA1 | Date | |
---|---|---|---|
1b1d9dcdac | |||
2565617ff9 | |||
01668c7be3 | |||
cafbc55780 | |||
ea3c4a2d2a | |||
89b756a615 | |||
6af84bcb6d | |||
3c1528d879 | |||
6f3e7b4ae5 | |||
86ef57fb62 | |||
6c326b1fc6 | |||
b8b10de08a | |||
edd2dd8511 | |||
70ac012a83 | |||
d5a904fe8f | |||
b1582ab5c2 | |||
8a0a0289f9 | |||
776d029e7e | |||
5afb8b69bf | |||
65073ec1ca | |||
0d62e5c986 | |||
fe5a08840c | |||
1c7b1cb78a | |||
78010c266e | |||
ef8b04648c | |||
d4c57c0153 | |||
0e198afeab | |||
de267a9d0f | |||
977a2abdd7 | |||
91e88bbff8 | |||
257e414c2a | |||
0cb8170530 | |||
bbf71f0be6 | |||
41a17219b6 | |||
29e79cdf26 | |||
b7a4fe9cb0 | |||
da83802427 | |||
364c383e08 | |||
1fd8060857 | |||
35933dfd14 | |||
d66b119119 | |||
e7ee460323 | |||
e86cf71010 | |||
a357f0fdcb | |||
ff3202eda9 | |||
8929d3282f | |||
bc7925dbab | |||
80ee5d760a | |||
de2d5fb446 | |||
753c0031cd | |||
a734f52b38 | |||
4cb9c7dc8d | |||
fe4618abd3 | |||
283556d329 | |||
5ef3d491c3 | |||
46f589d3aa | |||
b7d49e41c3 | |||
677fec7acc | |||
a9c7b8b660 | |||
958a75552f | |||
3717701171 | |||
4e95a3a420 | |||
ad60412d5f | |||
0b2158cc7b | |||
3580469a07 | |||
a7e8787579 | |||
45df32dabb | |||
b3af693678 | |||
c87827d1ef | |||
c2d2e1fd52 | |||
7ad2cc5912 | |||
ca92b12833 | |||
fbe3087502 | |||
ef347320fd | |||
1207d04d50 | |||
26f11b5cc0 | |||
481a49bb52 | |||
ffe4fc044e | |||
91b05c97c4 | |||
2f500bef19 | |||
7ed6c1e050 | |||
bd9974f410 | |||
42b427f49b | |||
ef08eedc34 | |||
9eed074025 | |||
427ab5ed54 | |||
e252d8fbb2 | |||
80c0f4cdd6 | |||
91ad13628c | |||
76484017e1 | |||
faf30b8227 | |||
bf825fd961 | |||
6e6475f8bf | |||
5b484eb38c | |||
04653711e9 | |||
40903eacc3 | |||
a70a934b3d | |||
8ea76f91f6 | |||
11074b89d5 |
@@ -10,9 +10,9 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/bin/Debug/net8.0/${workspaceFolderBasename}.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"program": "${workspaceFolder}/Game/bin/Debug/net8.0/Game.dll",
|
||||
"args": ["server"],
|
||||
"cwd": "${workspaceFolder}/Game",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
5
Game/.vscode/tasks.json → .vscode/tasks.json
vendored
5
Game/.vscode/tasks.json → .vscode/tasks.json
vendored
@@ -9,7 +9,10 @@
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"label": "build"
|
||||
"label": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/Game"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
2
Engine
2
Engine
Submodule Engine updated: d08495afbb...2cf6135063
8
Game/Abstract/IContentLoader.cs
Normal file
8
Game/Abstract/IContentLoader.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public interface IMonoGameContentLoader
|
||||
{
|
||||
void LoadContent(ContentManager content);
|
||||
}
|
8
Game/Abstract/IDisplayableShape.cs
Normal file
8
Game/Abstract/IDisplayableShape.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Apos.Shapes;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public interface IDisplayableShape
|
||||
{
|
||||
void Draw(ShapeBatch shapeBatch);
|
||||
}
|
@@ -2,7 +2,7 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
public interface IDisplayable
|
||||
public interface IDisplayableSprite
|
||||
{
|
||||
public void Draw(SpriteBatch spriteBatch);
|
||||
}
|
120
Game/Behaviours/BallBehaviour.cs
Normal file
120
Game/Behaviours/BallBehaviour.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
|
||||
using LiteNetLib;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Network;
|
||||
using Syntriax.Engine.Network.Abstract;
|
||||
using Syntriax.Engine.Physics2D;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class BallBehaviour : NetworkBehaviour, IMonoGameContentLoader
|
||||
{
|
||||
public Vector2D StartDirection { get; private set; } = Vector2D.Zero;
|
||||
public float Speed { get; set; } = 500f;
|
||||
public float SpeedUpMultiplier { get; set; } = .0125f;
|
||||
|
||||
private readonly Random random = new();
|
||||
private IRigidBody2D rigidBody = null!;
|
||||
private INetworkCommunicator communicator = null!;
|
||||
private SoundEffect soundEffect = null!;
|
||||
|
||||
public void LoadContent(ContentManager content)
|
||||
{
|
||||
soundEffect = content.Load<SoundEffect>("Hit");
|
||||
}
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
{
|
||||
if (!BehaviourController.TryGetBehaviour(out IRigidBody2D? foundRigidBody))
|
||||
throw new Exception($"{nameof(IRigidBody2D)} is missing on {GameObject.Name}.");
|
||||
if (!BehaviourController.TryGetBehaviour(out ICollider2D? foundCollider))
|
||||
throw new Exception($"{nameof(ICollider2D)} is missing on {GameObject.Name}.");
|
||||
if (!GameObject.GameManager.TryFindBehaviour(out INetworkCommunicator? foundCommunicator))
|
||||
throw new Exception($"{nameof(INetworkCommunicator)} is missing on GameManager.");
|
||||
|
||||
foundCollider.OnCollisionDetected += OnCollisionDetected;
|
||||
|
||||
rigidBody = foundRigidBody;
|
||||
communicator = foundCommunicator;
|
||||
if (GameObject.GameManager.TryFindBehaviour(out PongManagerBehaviour? pongManager))
|
||||
{
|
||||
pongManager.OnReset += ResetBall;
|
||||
pongManager.OnScored += ResetBall;
|
||||
pongManager.OnFinished += DisableBall;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisableBall(PongManagerBehaviour pongManager)
|
||||
{
|
||||
BehaviourController.GameObject.Transform.Position = Vector2D.Zero;
|
||||
rigidBody.Velocity = Vector2D.Zero;
|
||||
}
|
||||
|
||||
private void ResetBall(PongManagerBehaviour pongManager)
|
||||
{
|
||||
StateEnable.Enabled = true;
|
||||
BehaviourController.GameObject.Transform.Position = Vector2D.Zero;
|
||||
rigidBody.Velocity = GetRandomDirection() * Speed;
|
||||
|
||||
SendBallData();
|
||||
}
|
||||
|
||||
private Vector2D GetRandomDirection()
|
||||
{
|
||||
const float AllowedRadians = 45f * Syntriax.Engine.Core.Math.DegreeToRadian;
|
||||
float rotation = (float)random.NextDouble() * 2f * AllowedRadians - AllowedRadians;
|
||||
bool isBackwards = (random.Next() % 2) == 1;
|
||||
return Vector2D.Right.Rotate(isBackwards ? rotation + Syntriax.Engine.Core.Math.PI : rotation);
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (rigidBody.Velocity.MagnitudeSquared <= 0.01f)
|
||||
return;
|
||||
|
||||
Vector2D speedUp = rigidBody.Velocity.Normalized * Time.DeltaTimeFrame;
|
||||
rigidBody.Velocity += speedUp * SpeedUpMultiplier;
|
||||
}
|
||||
|
||||
private void OnCollisionDetected(ICollider2D collider2D, CollisionDetectionInformation information)
|
||||
{
|
||||
if (Syntriax.Engine.Core.Math.Abs(information.Normal.Dot(Vector2D.Right)) > .25)
|
||||
rigidBody.Velocity = information.Left.Transform.Position.FromTo(information.Right.Transform.Position).Normalized * rigidBody.Velocity.Magnitude;
|
||||
else
|
||||
rigidBody.Velocity = rigidBody.Velocity.Reflect(information.Normal);
|
||||
|
||||
soundEffect.Play();
|
||||
|
||||
SendBallData();
|
||||
}
|
||||
|
||||
private void SendBallData()
|
||||
{
|
||||
if (communicator is not INetworkServer server)
|
||||
return;
|
||||
|
||||
LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetMessageWriter(this);
|
||||
dataWriter.Put(rigidBody.Transform.Position);
|
||||
dataWriter.Put(rigidBody.Velocity);
|
||||
server.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer)
|
||||
{
|
||||
rigidBody.Transform.Position = reader.GetVector2D();
|
||||
rigidBody.Velocity = reader.GetVector2D();
|
||||
}
|
||||
|
||||
public BallBehaviour() { }
|
||||
public BallBehaviour(Vector2D startDirection, float speed)
|
||||
{
|
||||
StartDirection = Vector2D.Normalize(startDirection);
|
||||
Speed = speed;
|
||||
}
|
||||
}
|
74
Game/Behaviours/CameraController.cs
Normal file
74
Game/Behaviours/CameraController.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Input;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class CameraController : BehaviourOverride
|
||||
{
|
||||
private MonoGameCamera2DBehaviour cameraBehaviour = null!;
|
||||
private IButtonInputs<Keys> buttonInputs = null!;
|
||||
|
||||
private float defaultZoomLevel = 1f;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
{
|
||||
if (BehaviourController.TryGetBehaviour(out MonoGameCamera2DBehaviour? foundCameraBehaviour))
|
||||
cameraBehaviour = foundCameraBehaviour;
|
||||
|
||||
cameraBehaviour ??= BehaviourController.AddBehaviour<MonoGameCamera2DBehaviour>();
|
||||
|
||||
if (BehaviourController.TryGetBehaviour(out IButtonInputs<Keys>? foundButtonInputs))
|
||||
buttonInputs = foundButtonInputs;
|
||||
|
||||
buttonInputs ??= BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
||||
buttonInputs.RegisterOnPress(Keys.F, SwitchToFullScreen);
|
||||
buttonInputs.RegisterOnPress(Keys.R, ResetCamera);
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (buttonInputs.IsPressed(Keys.U))
|
||||
cameraBehaviour.Zoom += Time.Elapsed.Nanoseconds * 0.00025f;
|
||||
if (buttonInputs.IsPressed(Keys.J))
|
||||
cameraBehaviour.Zoom -= Time.Elapsed.Nanoseconds * 0.00025f;
|
||||
|
||||
|
||||
if (buttonInputs.IsPressed(Keys.NumPad8)) cameraBehaviour.BehaviourController.GameObject.Transform.Position += Vector2D.Up.Rotate(Transform.Rotation * Math.DegreeToRadian) * Time.DeltaTimeFrame;
|
||||
if (buttonInputs.IsPressed(Keys.NumPad2)) cameraBehaviour.BehaviourController.GameObject.Transform.Position -= Vector2D.Up.Rotate(Transform.Rotation * Math.DegreeToRadian) * Time.DeltaTimeFrame;
|
||||
if (buttonInputs.IsPressed(Keys.NumPad6)) cameraBehaviour.BehaviourController.GameObject.Transform.Position += Vector2D.Right.Rotate(Transform.Rotation * Math.DegreeToRadian) * Time.DeltaTimeFrame;
|
||||
if (buttonInputs.IsPressed(Keys.NumPad4)) cameraBehaviour.BehaviourController.GameObject.Transform.Position -= Vector2D.Right.Rotate(Transform.Rotation * Math.DegreeToRadian) * Time.DeltaTimeFrame;
|
||||
|
||||
|
||||
if (buttonInputs.IsPressed(Keys.Q))
|
||||
cameraBehaviour.BehaviourController.GameObject.Transform.Rotation += Time.Elapsed.Nanoseconds * 0.0025f;
|
||||
if (buttonInputs.IsPressed(Keys.E))
|
||||
cameraBehaviour.BehaviourController.GameObject.Transform.Rotation -= Time.Elapsed.Nanoseconds * 0.0025f;
|
||||
}
|
||||
|
||||
private void SwitchToFullScreen(IButtonInputs<Keys> inputs, Keys keys)
|
||||
{
|
||||
if (cameraBehaviour.Graphics.IsFullScreen)
|
||||
return;
|
||||
|
||||
cameraBehaviour.Graphics.PreferMultiSampling = false;
|
||||
cameraBehaviour.Graphics.PreferredBackBufferWidth = cameraBehaviour.Graphics.GraphicsDevice.Adapter.CurrentDisplayMode.Width;
|
||||
cameraBehaviour.Graphics.PreferredBackBufferHeight = cameraBehaviour.Graphics.GraphicsDevice.Adapter.CurrentDisplayMode.Height;
|
||||
cameraBehaviour.Graphics.IsFullScreen = true;
|
||||
cameraBehaviour.Graphics.ApplyChanges();
|
||||
|
||||
float previousScreenSize = Math.Sqrt(Math.Sqr(cameraBehaviour.Viewport.Width) + Math.Sqr(cameraBehaviour.Viewport.Height));
|
||||
float currentScreenSize = Math.Sqrt(Math.Sqr(cameraBehaviour.Graphics.GraphicsDevice.Viewport.Width) + Math.Sqr(cameraBehaviour.Graphics.GraphicsDevice.Viewport.Height));
|
||||
defaultZoomLevel /= previousScreenSize / currentScreenSize;
|
||||
cameraBehaviour.Zoom /= previousScreenSize / currentScreenSize;
|
||||
cameraBehaviour.Viewport = cameraBehaviour.Graphics.GraphicsDevice.Viewport;
|
||||
}
|
||||
|
||||
private void ResetCamera(IButtonInputs<Keys> inputs, Keys keys)
|
||||
{
|
||||
cameraBehaviour.Zoom = defaultZoomLevel;
|
||||
Transform.Position = Vector2D.Zero;
|
||||
Transform.Rotation = 0f;
|
||||
}
|
||||
}
|
29
Game/Behaviours/CircleBehaviour.cs
Normal file
29
Game/Behaviours/CircleBehaviour.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
using Apos.Shapes;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class CircleBehaviour : Syntriax.Engine.Physics2D.Collider2DCircleBehaviour, IDisplayableShape
|
||||
{
|
||||
public Color Color { get; set; } = Color.White;
|
||||
public float Thickness { get; set; } = .5f;
|
||||
|
||||
public void Draw(ShapeBatch shapeBatch)
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
Recalculate();
|
||||
|
||||
shapeBatch.BorderCircle(CircleWorld.Center.ToDisplayVector2(), CircleWorld.Radius, Color);
|
||||
}
|
||||
|
||||
public CircleBehaviour(Circle circle) : base(circle) { }
|
||||
public CircleBehaviour(Circle circle, float thickness) : base(circle) { Thickness = thickness; }
|
||||
public CircleBehaviour(Circle circle, Color color) : base(circle) { Color = color; }
|
||||
public CircleBehaviour(Circle circle, Color color, float thickness) : base(circle) { Thickness = thickness; Color = color; }
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
@@ -10,8 +9,8 @@ namespace Pong.Behaviours;
|
||||
|
||||
public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys>
|
||||
{
|
||||
private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>> OnPressed = new(256);
|
||||
private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>> OnReleased = new(256);
|
||||
private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>?> OnPressed = new(256);
|
||||
private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>?> OnReleased = new(256);
|
||||
|
||||
private int cachePressedCurrentlyCount = 0;
|
||||
private readonly Keys[] cachePressedCurrently = new Keys[256];
|
||||
@@ -69,7 +68,7 @@ public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys>
|
||||
if (WasPressed(currentlyPressedKey))
|
||||
continue;
|
||||
|
||||
action.Invoke(this, currentlyPressedKey);
|
||||
action?.Invoke(this, currentlyPressedKey);
|
||||
}
|
||||
|
||||
for (int i = 0; i < cachePressedPreviouslyCount; i++)
|
||||
@@ -82,7 +81,7 @@ public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys>
|
||||
if (IsPressed(previouslyPressedKey))
|
||||
continue;
|
||||
|
||||
action.Invoke(this, previouslyPressedKey);
|
||||
action?.Invoke(this, previouslyPressedKey);
|
||||
}
|
||||
|
||||
Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount);
|
||||
|
102
Game/Behaviours/MonoGameCamera2DBehaviour.cs
Normal file
102
Game/Behaviours/MonoGameCamera2DBehaviour.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class MonoGameCamera2DBehaviour(GraphicsDeviceManager Graphics) : BehaviourOverride, ICamera2D
|
||||
{
|
||||
public System.Action<MonoGameCamera2DBehaviour>? OnMatrixTransformChanged { get; set; } = null;
|
||||
public System.Action<MonoGameCamera2DBehaviour>? OnViewportChanged { get; set; } = null;
|
||||
public System.Action<MonoGameCamera2DBehaviour>? OnZoomChanged { get; set; } = null;
|
||||
|
||||
private Matrix _matrixTransform = Matrix.Identity;
|
||||
|
||||
private Viewport _viewport = default;
|
||||
private float _zoom = 1f;
|
||||
|
||||
public GraphicsDeviceManager Graphics { get; } = Graphics;
|
||||
|
||||
public Matrix MatrixTransform
|
||||
{
|
||||
get => _matrixTransform;
|
||||
set
|
||||
{
|
||||
if (_matrixTransform == value)
|
||||
return;
|
||||
|
||||
_matrixTransform = value;
|
||||
OnMatrixTransformChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2D Position
|
||||
{
|
||||
get => Transform.Position;
|
||||
set => Transform.Position = value;
|
||||
}
|
||||
|
||||
public Viewport Viewport
|
||||
{
|
||||
get => _viewport;
|
||||
set
|
||||
{
|
||||
if (_viewport.Equals(value))
|
||||
return;
|
||||
|
||||
_viewport = value;
|
||||
OnViewportChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get => _zoom;
|
||||
set
|
||||
{
|
||||
float newValue = Math.Max(0.1f, value);
|
||||
|
||||
if (_zoom == newValue)
|
||||
return;
|
||||
|
||||
_zoom = newValue;
|
||||
OnZoomChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get => Transform.Rotation;
|
||||
set => Transform.Rotation = value;
|
||||
}
|
||||
|
||||
System.Action<IAssignableTransform>? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
|
||||
ITransform IAssignableTransform.Transform => GameObject.Transform;
|
||||
bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform);
|
||||
|
||||
// TODO This causes delay since OnPreDraw calls assuming this is called in in Update
|
||||
public Vector2D ScreenToWorldPosition(Vector2D screenPosition)
|
||||
{
|
||||
Vector2D worldPosition = Vector2.Transform(screenPosition.ToVector2(), Matrix.Invert(MatrixTransform)).ToVector2D();
|
||||
return worldPosition.Scale(EngineConverter.screenScale);
|
||||
}
|
||||
public Vector2D WorldToScreenPosition(Vector2D worldPosition)
|
||||
{
|
||||
Vector2D screenPosition = Vector2.Transform(worldPosition.ToVector2(), MatrixTransform).ToVector2D();
|
||||
return screenPosition.Scale(EngineConverter.screenScale);
|
||||
}
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
=> Viewport = Graphics.GraphicsDevice.Viewport;
|
||||
|
||||
protected override void OnPreDraw()
|
||||
{
|
||||
MatrixTransform =
|
||||
Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) *
|
||||
Matrix.CreateRotationZ(Rotation * Math.DegreeToRadian) *
|
||||
Matrix.CreateScale(Zoom) *
|
||||
Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f));
|
||||
}
|
||||
}
|
@@ -1,106 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class MonoGameCameraBehaviour : BehaviourOverride, ICamera
|
||||
{
|
||||
public Action<ICamera>? OnPositionChanged { get; set; } = null;
|
||||
public Action<ICamera>? OnMatrixTransformChanged { get; set; } = null;
|
||||
public Action<ICamera>? OnViewportChanged { get; set; } = null;
|
||||
public Action<ICamera>? OnRotationChanged { get; set; } = null;
|
||||
public Action<ICamera>? OnZoomChanged { get; set; } = null;
|
||||
|
||||
private Matrix _matrixTransform = Matrix.Identity;
|
||||
|
||||
private Viewport _viewport = default;
|
||||
|
||||
private float _zoom = 1f;
|
||||
|
||||
public Matrix MatrixTransform
|
||||
{
|
||||
get => _matrixTransform;
|
||||
set
|
||||
{
|
||||
if (_matrixTransform == value)
|
||||
return;
|
||||
|
||||
_matrixTransform = value;
|
||||
OnMatrixTransformChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2D Position
|
||||
{
|
||||
get => Transform.Position;
|
||||
set => Transform.Position = value;
|
||||
}
|
||||
|
||||
public Viewport Viewport
|
||||
{
|
||||
get => _viewport;
|
||||
set
|
||||
{
|
||||
if (_viewport.Equals(value))
|
||||
return;
|
||||
|
||||
_viewport = value;
|
||||
OnViewportChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get => _zoom;
|
||||
set
|
||||
{
|
||||
float newValue = value >= .1f ? value : .1f;
|
||||
|
||||
if (_zoom == newValue)
|
||||
return;
|
||||
|
||||
_zoom = newValue;
|
||||
OnZoomChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get => Transform.Rotation;
|
||||
set => Transform.Rotation = value;
|
||||
}
|
||||
public Action<IAssignableTransform>? OnTransformAssigned { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
ITransform IAssignableTransform.Transform => throw new NotImplementedException();
|
||||
|
||||
public void Update()
|
||||
{
|
||||
MatrixTransform =
|
||||
Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) *
|
||||
Matrix.CreateRotationZ(Rotation) *
|
||||
Matrix.CreateScale(Zoom) *
|
||||
Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f));
|
||||
}
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
Transform.OnRotationChanged += OnTransformRotationChanged;
|
||||
Transform.OnPositionChanged += OnTransformPositionChanged;
|
||||
}
|
||||
|
||||
protected override void OnFinalize()
|
||||
{
|
||||
Transform.OnRotationChanged -= OnTransformRotationChanged;
|
||||
Transform.OnPositionChanged -= OnTransformPositionChanged;
|
||||
}
|
||||
|
||||
private void OnTransformRotationChanged(ITransform _) => OnRotationChanged?.Invoke(this);
|
||||
private void OnTransformPositionChanged(ITransform _) => OnPositionChanged?.Invoke(this);
|
||||
|
||||
public bool Assign(ITransform transform) => GameObject.Assign(transform);
|
||||
}
|
@@ -1,50 +1,53 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Input;
|
||||
using Syntriax.Engine.Physics2D;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
public class MovementBallBehaviour(Vector2D StartDirection, float Speed) : BehaviourOverride
|
||||
|
||||
public class MovementBallBehaviour : BehaviourOverride
|
||||
{
|
||||
public Vector2D StartDirection { get; private set; } = Vector2D.Normalize(StartDirection);
|
||||
public float Speed { get; set; } = Speed;
|
||||
public Vector2D StartDirection { get; private set; } = Vector2D.Zero;
|
||||
public float Speed { get; set; } = 500f;
|
||||
public float SpeedUpMultiplier { get; set; } = .0125f;
|
||||
|
||||
private IRigidBody2D rigidBody = null!;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
{
|
||||
if (!BehaviourController.TryGetBehaviour(out IRigidBody2D? rigidBody))
|
||||
throw new Exception($"Where's my {nameof(IRigidBody2D)}????");
|
||||
if (!BehaviourController.TryGetBehaviour(out IRigidBody2D? foundRigidBody))
|
||||
throw new Exception($"{nameof(IRigidBody2D)} is missing on {GameObject.Name}.");
|
||||
if (!BehaviourController.TryGetBehaviour(out ICollider2D? foundCollider))
|
||||
throw new Exception($"{nameof(ICollider2D)} is missing on {GameObject.Name}.");
|
||||
|
||||
rigidBody.Velocity = StartDirection * Speed;
|
||||
foundRigidBody.Velocity = StartDirection * Speed;
|
||||
foundCollider.OnCollisionDetected += OnCollisionDetected;
|
||||
|
||||
rigidBody = foundRigidBody;
|
||||
}
|
||||
|
||||
// protected override void OnUpdate(GameTime time)
|
||||
// {
|
||||
// GameObject.Transform.Position += StartDirection * (time.ElapsedGameTime.Nanoseconds * .001f) * Speed;
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (rigidBody.Velocity.MagnitudeSquared <= 0.01f)
|
||||
return;
|
||||
|
||||
// float absY = MathF.Abs(GameObject.Transform.Position.Y);
|
||||
// float differenceY = absY - PlayAreaBehaviour.PlayArea.Y * 0.5f;
|
||||
// if (differenceY > 0f)
|
||||
// {
|
||||
// if (GameObject.Transform.Position.Y > 0f)
|
||||
// GameObject.Transform.Position -= Vector2.UnitY * differenceY * 2f;
|
||||
// else
|
||||
// GameObject.Transform.Position += Vector2.UnitY * differenceY * 2f;
|
||||
Vector2D speedUp = rigidBody.Velocity.Normalized * Time.DeltaTimeFrame;
|
||||
rigidBody.Velocity += speedUp * SpeedUpMultiplier;
|
||||
}
|
||||
|
||||
// StartDirection = new(StartDirection.X, -StartDirection.Y);
|
||||
// }
|
||||
private void OnCollisionDetected(ICollider2D collider2D, CollisionDetectionInformation information)
|
||||
{
|
||||
if (Syntriax.Engine.Core.Math.Abs(information.Normal.Dot(Vector2D.Right)) > .25)
|
||||
rigidBody.Velocity = information.Left.Transform.Position.FromTo(information.Right.Transform.Position).Normalized * rigidBody.Velocity.Magnitude;
|
||||
else
|
||||
rigidBody.Velocity = rigidBody.Velocity.Reflect(information.Normal);
|
||||
}
|
||||
|
||||
// float absX = MathF.Abs(GameObject.Transform.Position.X);
|
||||
// float differenceX = absX - PlayAreaBehaviour.PlayArea.X * 0.5f;
|
||||
// if (differenceX > 0f)
|
||||
// {
|
||||
// if (GameObject.Transform.Position.X > 0f)
|
||||
// GameObject.Transform.Position -= Vector2.UnitX * differenceX * 2f;
|
||||
// else
|
||||
// GameObject.Transform.Position += Vector2.UnitX * differenceX * 2f;
|
||||
|
||||
// StartDirection = new(-StartDirection.X, StartDirection.Y);
|
||||
// }
|
||||
// }
|
||||
public MovementBallBehaviour() { }
|
||||
public MovementBallBehaviour(Vector2D startDirection, float speed)
|
||||
{
|
||||
StartDirection = Vector2D.Normalize(startDirection);
|
||||
Speed = speed;
|
||||
}
|
||||
}
|
||||
|
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Input;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class MovementBoxBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : BehaviourOverride
|
||||
{
|
||||
private Keys Up { get; } = Up;
|
||||
private Keys Down { get; } = Down;
|
||||
public float High { get; } = High;
|
||||
public float Low { get; } = Low;
|
||||
public float Speed { get; set; } = Speed;
|
||||
|
||||
private bool isUpPressed = false;
|
||||
private bool isDownPressed = false;
|
||||
|
||||
private IButtonInputs<Keys> inputs = null!;
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (isUpPressed && isDownPressed)
|
||||
return;
|
||||
|
||||
if (isUpPressed)
|
||||
GameObject.Transform.Position = GameObject.Transform.Position + Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed;
|
||||
else if (isDownPressed)
|
||||
GameObject.Transform.Position = GameObject.Transform.Position + -Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed;
|
||||
|
||||
GameObject.Transform.Position = new Vector2D(GameObject.Transform.Position.X, MathF.Max(MathF.Min(GameObject.Transform.Position.Y, High), Low));
|
||||
}
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
{
|
||||
if (!BehaviourController.TryGetBehaviour<IButtonInputs<Keys>>(out var behaviourResult))
|
||||
throw new Exception($"{nameof(IButtonInputs<Keys>)} is missing on ${GameObject.Name}.");
|
||||
|
||||
inputs = behaviourResult;
|
||||
|
||||
inputs.RegisterOnPress(Up, OnUpPressed);
|
||||
inputs.RegisterOnRelease(Up, OnUpReleased);
|
||||
|
||||
inputs.RegisterOnPress(Down, OnDownPressed);
|
||||
inputs.RegisterOnRelease(Down, OnDownReleased);
|
||||
}
|
||||
|
||||
protected override void OnFinalize()
|
||||
{
|
||||
inputs.UnregisterOnPress(Up, OnUpPressed);
|
||||
inputs.UnregisterOnRelease(Up, OnUpReleased);
|
||||
|
||||
inputs.UnregisterOnPress(Down, OnDownPressed);
|
||||
inputs.UnregisterOnRelease(Down, OnDownReleased);
|
||||
}
|
||||
|
||||
private void OnUpPressed(IButtonInputs<Keys> inputs, Keys keys) => isUpPressed = true;
|
||||
private void OnUpReleased(IButtonInputs<Keys> inputs, Keys keys) => isUpPressed = false;
|
||||
private void OnDownPressed(IButtonInputs<Keys> inputs, Keys keys) => isDownPressed = true;
|
||||
private void OnDownReleased(IButtonInputs<Keys> inputs, Keys keys) => isDownPressed = false;
|
||||
}
|
154
Game/Behaviours/NetworkedKeyboardInputs.cs
Normal file
154
Game/Behaviours/NetworkedKeyboardInputs.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LiteNetLib;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Syntriax.Engine.Input;
|
||||
using Syntriax.Engine.Network;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class NetworkedKeyboardInputs : NetworkBehaviour, IButtonInputs<Keys>
|
||||
{
|
||||
private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>?> OnPressed = new(256);
|
||||
private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>?> OnReleased = new(256);
|
||||
|
||||
private int cachePressedCurrentlyCount = 0;
|
||||
private readonly Keys[] cachePressedCurrently = new Keys[256];
|
||||
|
||||
private int cachePressedPreviouslyCount = 0;
|
||||
private readonly Keys[] cachePressedPreviously = new Keys[256];
|
||||
|
||||
public void RegisterOnPress(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
|
||||
{
|
||||
if (OnPressed.TryGetValue(key, out var action))
|
||||
{
|
||||
action += callback;
|
||||
OnPressed.Remove(key);
|
||||
OnPressed.Add(key, action);
|
||||
return;
|
||||
}
|
||||
|
||||
OnPressed.Add(key, callback);
|
||||
}
|
||||
|
||||
public void UnregisterOnPress(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
|
||||
{
|
||||
if (OnPressed.TryGetValue(key, out var action))
|
||||
action -= callback;
|
||||
}
|
||||
|
||||
public void RegisterOnRelease(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
|
||||
{
|
||||
if (OnReleased.TryGetValue(key, out var action))
|
||||
{
|
||||
action += callback;
|
||||
OnReleased.Remove(key);
|
||||
OnReleased.Add(key, action);
|
||||
return;
|
||||
}
|
||||
|
||||
OnReleased.Add(key, callback);
|
||||
}
|
||||
|
||||
public void UnregisterOnRelease(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
|
||||
{
|
||||
if (OnReleased.TryGetValue(key, out var action))
|
||||
action -= callback;
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (!IsServer && !LocalAssigned)
|
||||
return;
|
||||
|
||||
KeyboardState keyboardState = Keyboard.GetState();
|
||||
keyboardState.GetPressedKeys(cachePressedCurrently);
|
||||
cachePressedCurrentlyCount = keyboardState.GetPressedKeyCount();
|
||||
|
||||
for (int i = 0; i < cachePressedCurrentlyCount; i++)
|
||||
{
|
||||
Keys currentlyPressedKey = cachePressedCurrently[i];
|
||||
|
||||
if (!OnPressed.TryGetValue(currentlyPressedKey, out var action))
|
||||
continue;
|
||||
|
||||
if (WasPressed(currentlyPressedKey))
|
||||
continue;
|
||||
|
||||
action?.Invoke(this, currentlyPressedKey);
|
||||
}
|
||||
|
||||
for (int i = 0; i < cachePressedPreviouslyCount; i++)
|
||||
{
|
||||
Keys previouslyPressedKey = cachePressedPreviously[i];
|
||||
|
||||
if (!OnReleased.TryGetValue(previouslyPressedKey, out var action))
|
||||
continue;
|
||||
|
||||
if (IsPressed(previouslyPressedKey))
|
||||
continue;
|
||||
|
||||
action?.Invoke(this, previouslyPressedKey);
|
||||
}
|
||||
|
||||
Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount);
|
||||
cachePressedPreviouslyCount = cachePressedCurrentlyCount;
|
||||
}
|
||||
|
||||
public bool IsPressed(Keys key)
|
||||
{
|
||||
for (int i = 0; i < cachePressedCurrentlyCount; i++)
|
||||
if (cachePressedCurrently[i] == key)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool WasPressed(Keys key)
|
||||
{
|
||||
for (int i = 0; i < cachePressedPreviouslyCount; i++)
|
||||
if (cachePressedPreviously[i] == key)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
base.OnInitialize();
|
||||
foreach (Keys key in Enum.GetValues(typeof(Keys)))
|
||||
{
|
||||
RegisterOnPress(key, (keys, keyVal) =>
|
||||
{
|
||||
if (!LocalAssigned && !IsServer)
|
||||
return;
|
||||
|
||||
var netDataWriter = NetworkCommunicator.GetMessageWriter(this);
|
||||
netDataWriter.Put(true);
|
||||
netDataWriter.Put((int)key);
|
||||
NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
|
||||
});
|
||||
RegisterOnRelease(key, (keys, keyVal) =>
|
||||
{
|
||||
if (!LocalAssigned && !IsServer)
|
||||
return;
|
||||
|
||||
var netDataWriter = NetworkCommunicator.GetMessageWriter(this);
|
||||
netDataWriter.Put(false);
|
||||
netDataWriter.Put((int)key);
|
||||
NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer)
|
||||
{
|
||||
bool isPressed = reader.GetBool();
|
||||
Keys key = (Keys)reader.GetInt();
|
||||
|
||||
if (isPressed)
|
||||
if (OnPressed.TryGetValue(key, out var action))
|
||||
action?.Invoke(this, key);
|
||||
if (!isPressed)
|
||||
if (OnReleased.TryGetValue(key, out var action))
|
||||
action?.Invoke(this, key);
|
||||
}
|
||||
}
|
101
Game/Behaviours/PaddleBehaviour.cs
Normal file
101
Game/Behaviours/PaddleBehaviour.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using LiteNetLib;
|
||||
using LiteNetLib.Utils;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Input;
|
||||
using Syntriax.Engine.Network;
|
||||
using Syntriax.Engine.Network.Abstract;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : BehaviourOverride
|
||||
{
|
||||
private Keys Up { get; } = Up;
|
||||
private Keys Down { get; } = Down;
|
||||
public float High { get; } = High;
|
||||
public float Low { get; } = Low;
|
||||
public float Speed { get; set; } = Speed;
|
||||
|
||||
private bool isUpPressed = false;
|
||||
private bool isDownPressed = false;
|
||||
|
||||
private IButtonInputs<Keys> inputs = null!;
|
||||
private INetworkCommunicator communicator = null!;
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (isUpPressed && isDownPressed)
|
||||
return;
|
||||
|
||||
if (isUpPressed)
|
||||
MovePaddle(Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed);
|
||||
else if (isDownPressed)
|
||||
MovePaddle(-Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed);
|
||||
}
|
||||
|
||||
private void MovePaddle(Vector2D vectorToAdd)
|
||||
{
|
||||
GameObject.Transform.Position += vectorToAdd;
|
||||
GameObject.Transform.Position = new Vector2D(GameObject.Transform.Position.X, MathF.Max(MathF.Min(GameObject.Transform.Position.Y, High), Low));
|
||||
}
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
{
|
||||
BehaviourController.TryGetBehaviour<IButtonInputs<Keys>>(out var behaviourResult);
|
||||
inputs = behaviourResult ?? throw new Exception($"{nameof(IButtonInputs<Keys>)} is missing on {GameObject.Name}.");
|
||||
|
||||
if (!GameObject.GameManager.TryFindBehaviour(out INetworkCommunicator? foundCommunicator))
|
||||
throw new Exception($"{nameof(INetworkCommunicator)} is missing on GameManager.");
|
||||
|
||||
inputs.RegisterOnPress(Up, OnUpPressed);
|
||||
inputs.RegisterOnRelease(Up, OnUpReleased);
|
||||
|
||||
inputs.RegisterOnPress(Down, OnDownPressed);
|
||||
inputs.RegisterOnRelease(Down, OnDownReleased);
|
||||
|
||||
communicator = foundCommunicator;
|
||||
communicator.RegisterEntityListener(this, OnDataReceived);
|
||||
}
|
||||
|
||||
protected override void OnFinalize()
|
||||
{
|
||||
inputs.UnregisterOnPress(Up, OnUpPressed);
|
||||
inputs.UnregisterOnRelease(Up, OnUpReleased);
|
||||
|
||||
inputs.UnregisterOnPress(Down, OnDownPressed);
|
||||
inputs.UnregisterOnRelease(Down, OnDownReleased);
|
||||
}
|
||||
|
||||
private void UpdateNetwork()
|
||||
{
|
||||
NetDataWriter netDataWriter = communicator.GetEntityWriter(this);
|
||||
netDataWriter.Put(isUpPressed);
|
||||
netDataWriter.Put(isDownPressed);
|
||||
netDataWriter.Put(Transform.Position);
|
||||
communicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
private void OnDataReceived(NetPacketReader reader, NetPeer peer)
|
||||
{
|
||||
if (communicator is INetworkServer server)
|
||||
{
|
||||
reader.Get(out isUpPressed);
|
||||
reader.Get(out isDownPressed);
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Get(out isUpPressed);
|
||||
reader.Get(out isDownPressed);
|
||||
Transform.Position = reader.GetVector2D();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUpPressed(IButtonInputs<Keys> inputs, Keys keys) { isUpPressed = true; UpdateNetwork(); }
|
||||
private void OnUpReleased(IButtonInputs<Keys> inputs, Keys keys) { isUpPressed = false; UpdateNetwork(); }
|
||||
private void OnDownPressed(IButtonInputs<Keys> inputs, Keys keys) { isDownPressed = true; UpdateNetwork(); }
|
||||
private void OnDownReleased(IButtonInputs<Keys> inputs, Keys keys) { isDownPressed = false; UpdateNetwork(); }
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class PlayAreaBehaviour : BehaviourOverride
|
||||
{
|
||||
public Action<PlayAreaBehaviour>? OnPlayAreaChanged { get; set; } = null;
|
||||
|
||||
private Vector2 _playArea = Vector2.Zero;
|
||||
|
||||
public Vector2 PlayArea
|
||||
{
|
||||
get => _playArea;
|
||||
set
|
||||
{
|
||||
if (_playArea == value)
|
||||
return;
|
||||
|
||||
_playArea = value;
|
||||
OnPlayAreaChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
104
Game/Behaviours/PongManagerBehaviour.cs
Normal file
104
Game/Behaviours/PongManagerBehaviour.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using LiteNetLib;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Network;
|
||||
using Syntriax.Engine.Network.Abstract;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class PongManagerBehaviour : NetworkBehaviour
|
||||
{
|
||||
public Action<PongManagerBehaviour>? OnReset { get; set; } = null;
|
||||
public Action<PongManagerBehaviour>? OnFinished { get; set; } = null;
|
||||
public Action<PongManagerBehaviour>? OnScoresUpdated { get; set; } = null;
|
||||
public Action<PongManagerBehaviour>? OnScored { get; set; } = null;
|
||||
|
||||
private INetworkCommunicator communicator = null!;
|
||||
|
||||
public int ScoreLeft { get; private set; } = 0;
|
||||
public int ScoreRight { get; private set; } = 0;
|
||||
public int ScoreSum => ScoreLeft + ScoreRight;
|
||||
|
||||
public int WinScore { get; } = 5;
|
||||
|
||||
public PongManagerBehaviour() => WinScore = 5;
|
||||
public PongManagerBehaviour(int winScore) => WinScore = winScore;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
{
|
||||
KeyboardInputsBehaviour? buttonInputs = null!;
|
||||
|
||||
if (!BehaviourController.TryGetBehaviour(out buttonInputs))
|
||||
buttonInputs = BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
||||
|
||||
if (!GameObject.GameManager.TryFindBehaviour(out INetworkCommunicator? foundCommunicator))
|
||||
throw new Exception($"{nameof(INetworkCommunicator)} is missing on GameManager.");
|
||||
|
||||
buttonInputs.RegisterOnRelease(Keys.Space, (_, _1) => Reset());
|
||||
|
||||
communicator = foundCommunicator;
|
||||
}
|
||||
|
||||
public void ScoreToLeft()
|
||||
{
|
||||
ScoreLeft++;
|
||||
OnScoresUpdated?.Invoke(this);
|
||||
OnScored?.Invoke(this);
|
||||
|
||||
SendData();
|
||||
|
||||
CheckFinish();
|
||||
}
|
||||
|
||||
public void ScoreToRight()
|
||||
{
|
||||
ScoreRight++;
|
||||
OnScoresUpdated?.Invoke(this);
|
||||
OnScored?.Invoke(this);
|
||||
|
||||
SendData();
|
||||
|
||||
CheckFinish();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ScoreLeft = ScoreRight = 0;
|
||||
OnScoresUpdated?.Invoke(this);
|
||||
OnReset?.Invoke(this);
|
||||
|
||||
SendData();
|
||||
}
|
||||
|
||||
private void CheckFinish()
|
||||
{
|
||||
int halfwayScore = (int)(WinScore * .5f);
|
||||
|
||||
if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore)
|
||||
OnFinished?.Invoke(this);
|
||||
}
|
||||
|
||||
private void SendData()
|
||||
{
|
||||
if (communicator is not INetworkServer server)
|
||||
return;
|
||||
|
||||
LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetMessageWriter(this);
|
||||
dataWriter.Put(ScoreLeft);
|
||||
dataWriter.Put(ScoreRight);
|
||||
server.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer)
|
||||
{
|
||||
ScoreLeft = reader.GetInt();
|
||||
ScoreRight = reader.GetInt();
|
||||
OnScoresUpdated?.Invoke(this);
|
||||
|
||||
CheckFinish();
|
||||
}
|
||||
}
|
48
Game/Behaviours/ShapeAABBBehaviour.cs
Normal file
48
Game/Behaviours/ShapeAABBBehaviour.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
using Apos.Shapes;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Input;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class ShapeAABBBehaviour : BehaviourOverride, IDisplayableShape
|
||||
{
|
||||
private IShapeCollider2D? shapeCollider = null;
|
||||
|
||||
public Color Color { get; set; } = Color.White;
|
||||
public float Thickness { get; set; } = .5f;
|
||||
public bool display = true;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
{
|
||||
BehaviourController.TryGetBehaviour(out shapeCollider);
|
||||
|
||||
if (BehaviourController.TryGetBehaviour(out IButtonInputs<Microsoft.Xna.Framework.Input.Keys>? keys))
|
||||
keys.RegisterOnPress(Microsoft.Xna.Framework.Input.Keys.D, (_1, _2) => display = !display);
|
||||
}
|
||||
|
||||
public void Draw(ShapeBatch shapeBatch)
|
||||
{
|
||||
if (!display)
|
||||
return;
|
||||
|
||||
if (shapeCollider is null)
|
||||
return;
|
||||
|
||||
AABB aabb = AABB.FromVectors(shapeCollider.ShapeWorld);
|
||||
|
||||
shapeBatch.BorderCircle(aabb.Center.ToDisplayVector2(), 7.5f, Color.Beige);
|
||||
|
||||
shapeBatch.DrawRectangle(aabb.Center.ApplyDisplayScale().Subtract(aabb.SizeHalf).ToVector2(), aabb.Size.ToVector2(), Color.Transparent, Color.Blue);
|
||||
}
|
||||
|
||||
public ShapeAABBBehaviour() { }
|
||||
public ShapeAABBBehaviour(float Thickness) { this.Thickness = Thickness; }
|
||||
public ShapeAABBBehaviour(Color color) { Color = color; }
|
||||
public ShapeAABBBehaviour(Color color, float Thickness) { this.Thickness = Thickness; Color = color; }
|
||||
}
|
33
Game/Behaviours/ShapeBehaviour.cs
Normal file
33
Game/Behaviours/ShapeBehaviour.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
using Apos.Shapes;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class ShapeBehaviour : Syntriax.Engine.Physics2D.Collider2DShapeBehaviour, IDisplayableShape
|
||||
{
|
||||
public Color Color { get; set; } = Color.White;
|
||||
public float Thickness { get; set; } = .5f;
|
||||
|
||||
public void Draw(ShapeBatch shapeBatch)
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
Recalculate();
|
||||
|
||||
int count = ShapeWorld.Vertices.Count;
|
||||
|
||||
for (int i = 0; i < count - 1; i++)
|
||||
shapeBatch.DrawLine(ShapeWorld[i].ToDisplayVector2(), ShapeWorld[i + 1].ToDisplayVector2(), Thickness, Color, Color);
|
||||
shapeBatch.DrawLine(ShapeWorld[0].ToDisplayVector2(), ShapeWorld[^1].ToDisplayVector2(), Thickness, Color, Color);
|
||||
}
|
||||
|
||||
public ShapeBehaviour(Shape shape) : base(shape) { }
|
||||
public ShapeBehaviour(Shape shape, float thickness) : base(shape) { Thickness = thickness; }
|
||||
public ShapeBehaviour(Shape shape, Color color) : base(shape) { Color = color; }
|
||||
public ShapeBehaviour(Shape shape, Color color, float thickness) : base(shape) { Thickness = thickness; Color = color; }
|
||||
}
|
25
Game/Behaviours/TextBehaviour.cs
Normal file
25
Game/Behaviours/TextBehaviour.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class TextBehaviour : BehaviourOverride, IDisplayableSprite
|
||||
{
|
||||
public SpriteFont? Font { get; set; } = null;
|
||||
public int Size { get; set; } = 16;
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!IsActive || Font is null)
|
||||
return;
|
||||
|
||||
spriteBatch.DrawString(Font, Text, Transform.Position.ToDisplayVector2(), Color.White, Transform.Rotation, Vector2.One * .5f, Transform.Scale.Magnitude, SpriteEffects.None, 0f);
|
||||
}
|
||||
|
||||
public TextBehaviour() { }
|
||||
public TextBehaviour(SpriteFont font) => Font = font;
|
||||
}
|
32
Game/Behaviours/TextScoreBehaviour.cs
Normal file
32
Game/Behaviours/TextScoreBehaviour.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class TextScoreBehaviour : TextBehaviour
|
||||
{
|
||||
public bool IsLeft { get; }
|
||||
|
||||
private PongManagerBehaviour? pongManager = null;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
{
|
||||
if (!GameObject.GameManager.TryFindBehaviour(out pongManager))
|
||||
return;
|
||||
|
||||
pongManager.OnScoresUpdated += UpdateScores;
|
||||
|
||||
UpdateScores(pongManager);
|
||||
}
|
||||
|
||||
private void UpdateScores(PongManagerBehaviour pongManager)
|
||||
{
|
||||
if (IsLeft)
|
||||
Text = pongManager.ScoreLeft.ToString();
|
||||
else
|
||||
Text = pongManager.ScoreRight.ToString();
|
||||
}
|
||||
|
||||
public TextScoreBehaviour(bool IsLeft) => this.IsLeft = IsLeft;
|
||||
public TextScoreBehaviour(bool IsLeft, SpriteFont font) : base(font) => this.IsLeft = IsLeft;
|
||||
}
|
19
Game/Behaviours/WallScoreBehaviour.cs
Normal file
19
Game/Behaviours/WallScoreBehaviour.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class WallScoreBehaviour(Action OnCollision) : BehaviourOverride
|
||||
{
|
||||
private Action OnCollision { get; } = OnCollision;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
{
|
||||
if (!BehaviourController.TryGetBehaviour(out ICollider2D? collider2D))
|
||||
return;
|
||||
|
||||
collider2D.OnCollisionDetected += (_, _1) => OnCollision?.Invoke();
|
||||
}
|
||||
}
|
@@ -13,51 +13,16 @@
|
||||
|
||||
#---------------------------------- Content ---------------------------------#
|
||||
|
||||
#begin Sprites/Circle.png
|
||||
/importer:TextureImporter
|
||||
/processor:TextureProcessor
|
||||
/processorParam:ColorKeyColor=255,0,255,255
|
||||
/processorParam:ColorKeyEnabled=True
|
||||
/processorParam:GenerateMipmaps=False
|
||||
/processorParam:PremultiplyAlpha=True
|
||||
/processorParam:ResizeToPowerOfTwo=False
|
||||
/processorParam:MakeSquare=False
|
||||
/processorParam:TextureFormat=Color
|
||||
/build:Sprites/Circle.png
|
||||
#begin Hit.wav
|
||||
/importer:WavImporter
|
||||
/processor:SoundEffectProcessor
|
||||
/processorParam:Quality=Best
|
||||
/build:Hit.wav
|
||||
|
||||
#begin Sprites/Circle.png
|
||||
/importer:TextureImporter
|
||||
/processor:TextureProcessor
|
||||
/processorParam:ColorKeyColor=255,0,255,255
|
||||
/processorParam:ColorKeyEnabled=True
|
||||
/processorParam:GenerateMipmaps=False
|
||||
#begin UbuntuMono.spritefont
|
||||
/importer:FontDescriptionImporter
|
||||
/processor:FontDescriptionProcessor
|
||||
/processorParam:PremultiplyAlpha=True
|
||||
/processorParam:ResizeToPowerOfTwo=False
|
||||
/processorParam:MakeSquare=False
|
||||
/processorParam:TextureFormat=Color
|
||||
/build:Sprites/Circle.png
|
||||
|
||||
#begin Sprites/Pixel.png
|
||||
/importer:TextureImporter
|
||||
/processor:TextureProcessor
|
||||
/processorParam:ColorKeyColor=255,0,255,255
|
||||
/processorParam:ColorKeyEnabled=True
|
||||
/processorParam:GenerateMipmaps=False
|
||||
/processorParam:PremultiplyAlpha=True
|
||||
/processorParam:ResizeToPowerOfTwo=False
|
||||
/processorParam:MakeSquare=False
|
||||
/processorParam:TextureFormat=Color
|
||||
/build:Sprites/Pixel.png
|
||||
|
||||
#begin Sprites/Pixel.png
|
||||
/importer:TextureImporter
|
||||
/processor:TextureProcessor
|
||||
/processorParam:ColorKeyColor=255,0,255,255
|
||||
/processorParam:ColorKeyEnabled=True
|
||||
/processorParam:GenerateMipmaps=False
|
||||
/processorParam:PremultiplyAlpha=True
|
||||
/processorParam:ResizeToPowerOfTwo=False
|
||||
/processorParam:MakeSquare=False
|
||||
/processorParam:TextureFormat=Color
|
||||
/build:Sprites/Pixel.png
|
||||
/processorParam:TextureFormat=Compressed
|
||||
/build:UbuntuMono.spritefont
|
||||
|
||||
|
BIN
Game/Content/Hit.wav
Normal file
BIN
Game/Content/Hit.wav
Normal file
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 546 B |
60
Game/Content/UbuntuMono.spritefont
Normal file
60
Game/Content/UbuntuMono.spritefont
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file contains an xml description of a font, and will be read by the XNA
|
||||
Framework Content Pipeline. Follow the comments to customize the appearance
|
||||
of the font in your game, and to change the characters which are available to draw
|
||||
with.
|
||||
-->
|
||||
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
|
||||
<Asset Type="Graphics:FontDescription">
|
||||
|
||||
<!--
|
||||
Modify this string to change the font that will be imported.
|
||||
-->
|
||||
<FontName>Ubuntu Mono</FontName>
|
||||
|
||||
<!--
|
||||
Size is a float value, measured in points. Modify this value to change
|
||||
the size of the font.
|
||||
-->
|
||||
<Size>144</Size>
|
||||
|
||||
<!--
|
||||
Spacing is a float value, measured in pixels. Modify this value to change
|
||||
the amount of spacing in between characters.
|
||||
-->
|
||||
<Spacing>0</Spacing>
|
||||
|
||||
<!--
|
||||
UseKerning controls the layout of the font. If this value is true, kerning information
|
||||
will be used when placing characters.
|
||||
-->
|
||||
<UseKerning>true</UseKerning>
|
||||
|
||||
<!--
|
||||
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
|
||||
and "Bold, Italic", and are case sensitive.
|
||||
-->
|
||||
<Style>Bold</Style>
|
||||
|
||||
<!--
|
||||
If you uncomment this line, the default character will be substituted if you draw
|
||||
or measure text that contains characters which were not included in the font.
|
||||
-->
|
||||
<!-- <DefaultCharacter>*</DefaultCharacter> -->
|
||||
|
||||
<!--
|
||||
CharacterRegions control what letters are available in the font. Every
|
||||
character from Start to End will be built and made available for drawing. The
|
||||
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
|
||||
character set. The characters are ordered according to the Unicode standard.
|
||||
See the documentation for more information.
|
||||
-->
|
||||
<CharacterRegions>
|
||||
<CharacterRegion>
|
||||
<Start> </Start>
|
||||
<End>~</End>
|
||||
</CharacterRegion>
|
||||
</CharacterRegions>
|
||||
</Asset>
|
||||
</XnaContent>
|
@@ -6,10 +6,18 @@ namespace Pong;
|
||||
|
||||
public static class EngineConverter
|
||||
{
|
||||
public readonly static Vector2D screenScale = Vector2D.Down + Vector2D.Right;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EngineTime ToEngineTime(this GameTime gameTime) => new(gameTime.TotalGameTime, gameTime.ElapsedGameTime);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2D ToVector2D(this Vector2 vector) => new(vector.X, vector.Y);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2D ToVector2D(this Point point) => new(point.X, point.Y);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 ToVector2(this Vector2D vector) => new(vector.X, vector.Y);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2 ToDisplayVector2(this Vector2D vector) => vector.Scale(screenScale).ToVector2();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2D ApplyDisplayScale(this Vector2D vector) => vector.Scale(screenScale);
|
||||
}
|
||||
|
@@ -20,12 +20,15 @@
|
||||
<EmbeddedResource Include="Icon.bmp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Apos.Shapes" Version="0.2.3" />
|
||||
<PackageReference Include="LiteNetLib" Version="1.2.0" />
|
||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
||||
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Engine\Engine.Core\Engine.Core.csproj" />
|
||||
<ProjectReference Include="..\Engine\Engine.Input\Engine.Input.csproj" />
|
||||
<ProjectReference Include="..\Engine\Engine.Physics2D\Engine.Physics2D.csproj" />
|
||||
</ItemGroup>
|
||||
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
|
||||
<Message Text="Restoring dotnet tools" Importance="High" />
|
||||
|
@@ -1,31 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.002.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Game", "Game.csproj", "{42644486-9F9E-4242-B6C4-AF31BBFA31D2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "..\Engine\Engine.Core\Engine.Core.csproj", "{EF1FE4A2-40DF-4967-8003-CF6D98010D02}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{42644486-9F9E-4242-B6C4-AF31BBFA31D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{42644486-9F9E-4242-B6C4-AF31BBFA31D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{42644486-9F9E-4242-B6C4-AF31BBFA31D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{42644486-9F9E-4242-B6C4-AF31BBFA31D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF1FE4A2-40DF-4967-8003-CF6D98010D02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF1FE4A2-40DF-4967-8003-CF6D98010D02}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF1FE4A2-40DF-4967-8003-CF6D98010D02}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EF1FE4A2-40DF-4967-8003-CF6D98010D02}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {AB3225EE-1621-439F-8F83-DF4515922FEF}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
244
Game/Game1.cs
244
Game/Game1.cs
@@ -1,244 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using Pong.Behaviours;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Graphics.TwoDimensional;
|
||||
using Syntriax.Engine.Physics2D;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Pong;
|
||||
|
||||
public class Game1 : Game
|
||||
{
|
||||
private GraphicsDeviceManager _graphics = null!;
|
||||
private PhysicsEngine2D engine;
|
||||
private SpriteBatch _spriteBatch = null!;
|
||||
public static GameManager gameManager = null!;
|
||||
public static Sprite spriteBox = null!;
|
||||
private MonoGameCameraBehaviour cameraBehaviour = null!;
|
||||
|
||||
|
||||
public Game1()
|
||||
{
|
||||
engine = new PhysicsEngine2D();
|
||||
|
||||
_graphics = new GraphicsDeviceManager(this)
|
||||
{
|
||||
PreferredBackBufferWidth = 1024,
|
||||
PreferredBackBufferHeight = 576
|
||||
};
|
||||
|
||||
Content.RootDirectory = "Content";
|
||||
IsMouseVisible = true;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
gameManager = new();
|
||||
|
||||
// TODO: Add your initialization logic here
|
||||
gameManager.Initialize();
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
protected override void LoadContent()
|
||||
{
|
||||
_spriteBatch = new SpriteBatch(GraphicsDevice);
|
||||
|
||||
spriteBox = new Sprite() { Texture2D = Content.Load<Texture2D>("Sprites/Pixel") };
|
||||
Sprite spriteBall = new Sprite() { Texture2D = Content.Load<Texture2D>("Sprites/Circle") };
|
||||
|
||||
IGameObject gameObjectCamera = gameManager.InstantiateGameObject<GameObject>();
|
||||
gameObjectCamera.Name = "Camera";
|
||||
gameObjectCamera.Transform.Position = Vector2D.Zero;
|
||||
|
||||
cameraBehaviour = gameObjectCamera.BehaviourController.AddBehaviour<MonoGameCameraBehaviour>();
|
||||
cameraBehaviour.Viewport = GraphicsDevice.Viewport;
|
||||
gameManager.Camera = cameraBehaviour;
|
||||
|
||||
GameObject gameObjectBall = gameManager.InstantiateGameObject<GameObject>();
|
||||
gameObjectBall.Name = "Ball";
|
||||
gameObjectBall.Transform.Position = Vector2D.Zero;
|
||||
gameObjectBall.Transform.Scale = new Vector2D(1f / 51.2f, 1f / 51.2f);
|
||||
engine.AddRigidBody(gameObjectBall.BehaviourController.AddBehaviour<RigidBody2D>());
|
||||
gameObjectBall.BehaviourController.AddBehaviour<MovementBallBehaviour>(new Vector2D(.1f, .1f), 500f);
|
||||
gameObjectBall.BehaviourController.AddBehaviour<DisplayableSpriteBehaviour>().Assign(spriteBall);
|
||||
gameObjectBall.BehaviourController.AddBehaviour<Collider2DAABBBehaviour>().AABBLocal = new AABB(-Vector2D.One * 512f * .5f, Vector2D.One * 512f * .5f);
|
||||
// gameObjectBall = gameManager.InstantiateGameObject<GameObject>();
|
||||
// gameObjectBall.Name = "Ball";
|
||||
// gameObjectBall.Transform.Position = Vector2D.UnitY * 30f;
|
||||
// gameObjectBall.Transform.Scale = new Vector2D(1f / 51.2f, 1f / 51.2f);
|
||||
// engine.AddRigidBody(gameObjectBall.BehaviourController.AddBehaviour<RigidBody2D>());
|
||||
// gameObjectBall.BehaviourController.AddBehaviour<MovementBallBehaviour>(new Vector2D(.1f, .01f), 500f);
|
||||
// gameObjectBall.BehaviourController.AddBehaviour<DisplayableSpriteBehaviour>().Assign(spriteBall);
|
||||
// gameObjectBall.BehaviourController.AddBehaviour<Collider2DAABBBehaviour>().AABBLocal = new AABB(-Vector2D.One * 512f * .5f, Vector2D.One * 512f * .5f);
|
||||
|
||||
// IGameObject gameObjectLeft = gameManager.InstantiateGameObject<GameObject>();
|
||||
// gameObjectLeft.Name = "Left";
|
||||
// gameObjectLeft.Transform.Position = new Vector2D(-452, 0f);
|
||||
// gameObjectLeft.Transform.Scale = new Vector2D(10f, 40f);
|
||||
// gameObjectLeft.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
||||
// gameObjectLeft.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
|
||||
// gameObjectLeft.BehaviourController.AddBehaviour<DisplayableSpriteBehaviour>().Assign(spriteBox);
|
||||
// gameObjectLeft.BehaviourController.AddBehaviour<Collider2DAABBBehaviour>().AABBLocal = new AABB(-Vector2D.One * .5f, Vector2D.One * .5f);
|
||||
// engine.AddRigidBody(gameObjectLeft.BehaviourController.AddBehaviour<RigidBody2D>());
|
||||
|
||||
// IGameObject gameObjectRight = gameManager.InstantiateGameObject<GameObject>();
|
||||
// gameObjectRight.Name = "Right";
|
||||
// gameObjectRight.Transform.Position = new Vector2D(452, 0f);
|
||||
// gameObjectRight.Transform.Scale = new Vector2D(10f, 40f);
|
||||
// gameObjectRight.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
||||
// gameObjectRight.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.Up, Keys.Down, 268f, -268f, 400f);
|
||||
// gameObjectRight.BehaviourController.AddBehaviour<DisplayableSpriteBehaviour>().Assign(spriteBox);
|
||||
// gameObjectRight.BehaviourController.AddBehaviour<Collider2DAABBBehaviour>().AABBLocal = new AABB(-Vector2D.One * .5f, Vector2D.One * .5f);
|
||||
// engine.AddRigidBody(gameObjectRight.BehaviourController.AddBehaviour<RigidBody2D>());
|
||||
|
||||
|
||||
IGameObject goPlayAreaTop = gameManager.InstantiateGameObject<GameObject>();
|
||||
goPlayAreaTop.Transform.Position = new Vector2D(0f, 288f + 20f);
|
||||
goPlayAreaTop.Transform.Scale = new Vector2D(10240f, 40f);
|
||||
goPlayAreaTop.BehaviourController.AddBehaviour<DisplayableSpriteBehaviour>().Assign(spriteBox);
|
||||
goPlayAreaTop.BehaviourController.AddBehaviour<Collider2DAABBBehaviour>().AABBLocal = new AABB(-Vector2D.One * .5f, Vector2D.One * .5f);
|
||||
engine.AddRigidBody(goPlayAreaTop.BehaviourController.AddBehaviour<RigidBody2D>());
|
||||
IGameObject goPlayAreaBottom = gameManager.InstantiateGameObject<GameObject>();
|
||||
goPlayAreaBottom.Transform.Position = new Vector2D(0f, -(288f + 20f));
|
||||
goPlayAreaBottom.Transform.Scale = new Vector2D(10240f, 40f);
|
||||
goPlayAreaBottom.BehaviourController.AddBehaviour<DisplayableSpriteBehaviour>().Assign(spriteBox);
|
||||
goPlayAreaBottom.BehaviourController.AddBehaviour<Collider2DAABBBehaviour>().AABBLocal = new AABB(-Vector2D.One * .5f, Vector2D.One * .5f);
|
||||
engine.AddRigidBody(goPlayAreaBottom.BehaviourController.AddBehaviour<RigidBody2D>());
|
||||
|
||||
IGameObject goPlayAreaRight = gameManager.InstantiateGameObject<GameObject>();
|
||||
goPlayAreaRight.Transform.Position = new Vector2D(512f + 20f, 0f);
|
||||
goPlayAreaRight.Transform.Scale = new Vector2D(40f, 5760f);
|
||||
goPlayAreaRight.BehaviourController.AddBehaviour<DisplayableSpriteBehaviour>().Assign(spriteBox);
|
||||
goPlayAreaRight.BehaviourController.AddBehaviour<Collider2DAABBBehaviour>().AABBLocal = new AABB(-Vector2D.One * .5f, Vector2D.One * .5f);
|
||||
engine.AddRigidBody(goPlayAreaRight.BehaviourController.AddBehaviour<RigidBody2D>());
|
||||
IGameObject goPlayAreaLeft = gameManager.InstantiateGameObject<GameObject>();
|
||||
goPlayAreaLeft.Transform.Position = new Vector2D(-(512f + 20f), 0f);
|
||||
goPlayAreaLeft.Transform.Scale = new Vector2D(40f, 5760f);
|
||||
goPlayAreaLeft.BehaviourController.AddBehaviour<DisplayableSpriteBehaviour>().Assign(spriteBox);
|
||||
goPlayAreaLeft.BehaviourController.AddBehaviour<Collider2DAABBBehaviour>().AABBLocal = new AABB(-Vector2D.One * .5f, Vector2D.One * .5f);
|
||||
engine.AddRigidBody(goPlayAreaLeft.BehaviourController.AddBehaviour<RigidBody2D>());
|
||||
|
||||
// IGameObject goPlayAreaCenter = gameManager.InstantiateGameObject<GameObject>();
|
||||
// goPlayAreaCenter.Transform.Position = new Vector2D(100f, 100f);
|
||||
// goPlayAreaCenter.Transform.Scale = new Vector2D(40f, 40f);
|
||||
// // goPlayAreaCenter.BehaviourController.AddBehaviour<DisplayableSpriteBehaviour>().Assign(spriteBox);
|
||||
// engine.AddRigidBody(goPlayAreaCenter.BehaviourController.AddBehaviour<RigidBody2D>());
|
||||
// TODO: use this.Content to load your game content here
|
||||
}
|
||||
|
||||
protected override void Update(GameTime gameTime)
|
||||
{
|
||||
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
|
||||
Exit();
|
||||
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.F))
|
||||
{
|
||||
if (_graphics.IsFullScreen)
|
||||
return;
|
||||
|
||||
_graphics.PreferMultiSampling = false;
|
||||
_graphics.PreferredBackBufferWidth = GraphicsDevice.Adapter.CurrentDisplayMode.Width;
|
||||
_graphics.PreferredBackBufferHeight = GraphicsDevice.Adapter.CurrentDisplayMode.Height;
|
||||
_graphics.IsFullScreen = true;
|
||||
_graphics.ApplyChanges();
|
||||
|
||||
float previousScreenSize = MathF.Sqrt(MathF.Pow(cameraBehaviour.Viewport.Width, 2f) + MathF.Pow(cameraBehaviour.Viewport.Height, 2f));
|
||||
float currentScreenSize = MathF.Sqrt(MathF.Pow(GraphicsDevice.Viewport.Width, 2f) + MathF.Pow(GraphicsDevice.Viewport.Height, 2f));
|
||||
cameraBehaviour.Zoom /= previousScreenSize / currentScreenSize;
|
||||
cameraBehaviour.Viewport = GraphicsDevice.Viewport;
|
||||
}
|
||||
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.U))
|
||||
cameraBehaviour.Zoom += gameTime.ElapsedGameTime.Nanoseconds * 0.00025f;
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.J))
|
||||
cameraBehaviour.Zoom -= gameTime.ElapsedGameTime.Nanoseconds * 0.00025f;
|
||||
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.Q))
|
||||
cameraBehaviour.BehaviourController.GameObject.Transform.Rotation += gameTime.ElapsedGameTime.Nanoseconds * 0.000025f;
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.E))
|
||||
cameraBehaviour.BehaviourController.GameObject.Transform.Rotation -= gameTime.ElapsedGameTime.Nanoseconds * 0.000025f;
|
||||
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.N))
|
||||
{
|
||||
seconds = 70f;
|
||||
while (physicsTimer + 0.01f < seconds)
|
||||
{
|
||||
physicsTimer += 0.01f;
|
||||
engine.Step(.01f);
|
||||
}
|
||||
}
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.M))
|
||||
{
|
||||
seconds = 0f;
|
||||
while (physicsTimer - 0.01f > seconds)
|
||||
{
|
||||
physicsTimer -= 0.01f;
|
||||
engine.Step(-.01f);
|
||||
}
|
||||
}
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.Space))
|
||||
{
|
||||
seconds += gameTime.ElapsedGameTime.Milliseconds * .005f;
|
||||
while (physicsTimer + 0.01f < seconds)
|
||||
{
|
||||
Console.WriteLine($"Physics Timer: {physicsTimer}");
|
||||
physicsTimer += 0.01f;
|
||||
engine.Step(.01f);
|
||||
}
|
||||
}
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.B))
|
||||
{
|
||||
seconds -= gameTime.ElapsedGameTime.Milliseconds * .005f;
|
||||
while (physicsTimer - 0.01f > seconds)
|
||||
{
|
||||
Console.WriteLine($"Physics Timer: {physicsTimer}");
|
||||
physicsTimer -= 0.01f;
|
||||
engine.Step(-.01f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// while (physicsTimer + 0.01f < gameTime.TotalGameTime.TotalMilliseconds * .001f)//seconds)
|
||||
// {
|
||||
// Console.WriteLine($"Physics Timer: {physicsTimer}");
|
||||
// physicsTimer += 0.01f;
|
||||
// engine.Step(.01f);
|
||||
// }
|
||||
gameManager.Update(gameTime.ToEngineTime());
|
||||
base.Update(gameTime);
|
||||
}
|
||||
static float physicsTimer = 0f;
|
||||
static float seconds = 0f;
|
||||
|
||||
protected override void Draw(GameTime gameTime)
|
||||
{
|
||||
GraphicsDevice.Clear(new Color() { R = 32, G = 32, B = 32 });
|
||||
|
||||
// gameManager.Camera.Position = gameObjectBall.Transform.Position;
|
||||
// Console.WriteLine($"Pos: {gameManager.Camera.Position}");
|
||||
|
||||
// TODO: Add your drawing code here
|
||||
gameManager.PreDraw();
|
||||
gameManager.Camera.Update();
|
||||
|
||||
_spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cameraBehaviour.MatrixTransform);
|
||||
foreach (IGameObject gameObject in gameManager)
|
||||
{
|
||||
if (!gameObject.BehaviourController.TryGetBehaviour(out IDisplayable? displayable))
|
||||
continue;
|
||||
|
||||
displayable.Draw(_spriteBatch);
|
||||
}
|
||||
_spriteBatch.End();
|
||||
|
||||
base.Draw(gameTime);
|
||||
}
|
||||
}
|
200
Game/GamePong.cs
Normal file
200
Game/GamePong.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using Apos.Shapes;
|
||||
|
||||
using Pong.Behaviours;
|
||||
using Syntriax.Engine.Network;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Pong;
|
||||
|
||||
public class GamePong : Game
|
||||
{
|
||||
private readonly GraphicsDeviceManager graphics = null!;
|
||||
private IPhysicsEngine2D physicsEngine = null!;
|
||||
private SpriteBatch spriteBatch = null!;
|
||||
private ShapeBatch shapeBatch = null!;
|
||||
|
||||
private GameManager gameManager = null!;
|
||||
private BehaviourCollector<IDisplayableSprite> displayableCollector = null!;
|
||||
private BehaviourCollector<IDisplayableShape> displayableShapeCollector = null!;
|
||||
private BehaviourCollector<IMonoGameContentLoader> monoGameContentLoaderCollector = null!;
|
||||
private MonoGameCamera2DBehaviour cameraBehaviour = null!;
|
||||
|
||||
private PongManagerBehaviour pongManager = null!;
|
||||
|
||||
private float physicsTimer = 0f;
|
||||
|
||||
public GamePong()
|
||||
{
|
||||
graphics = new GraphicsDeviceManager(this)
|
||||
{
|
||||
PreferredBackBufferWidth = 1024,
|
||||
PreferredBackBufferHeight = 576,
|
||||
GraphicsProfile = GraphicsProfile.HiDef
|
||||
};
|
||||
|
||||
Content.RootDirectory = "Content";
|
||||
IsMouseVisible = true;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
// TODO: Add your initialization logic here
|
||||
gameManager = new();
|
||||
displayableCollector = new(gameManager);
|
||||
displayableShapeCollector = new(gameManager);
|
||||
monoGameContentLoaderCollector = new(gameManager);
|
||||
physicsEngine = new PhysicsEngine2DCollector(gameManager) { IterationPerStep = 3 };
|
||||
|
||||
monoGameContentLoaderCollector.OnCollected += (_, cached) => cached.LoadContent(Content);
|
||||
|
||||
gameManager.Initialize();
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
protected override void LoadContent()
|
||||
{
|
||||
spriteBatch = new SpriteBatch(GraphicsDevice);
|
||||
shapeBatch = new ShapeBatch(GraphicsDevice, Content);
|
||||
SpriteFont spriteFont = Content.Load<SpriteFont>("UbuntuMono");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string[] commandLineArguments = Environment.GetCommandLineArgs();
|
||||
if (commandLineArguments.Length != 1)
|
||||
{
|
||||
if (commandLineArguments[1].Equals("server", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
gameManager.InstantiateGameObject().BehaviourController.AddBehaviour<NetworkServer>().Start(8888, 2);
|
||||
Window.Title = "Pong - Server";
|
||||
}
|
||||
else
|
||||
{
|
||||
Window.Title = $"Pong - Client -> {commandLineArguments[1]}";
|
||||
gameManager.InstantiateGameObject().BehaviourController.AddBehaviour<NetworkClient>().Connect(commandLineArguments[1], 8888);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gameManager.InstantiateGameObject().BehaviourController.AddBehaviour<NetworkClient>().Connect("127.0.0.1", 8888);
|
||||
Window.Title = $"Pong - Client -> 127.0.0.1";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IGameObject gameObjectCamera = gameManager.InstantiateGameObject().SetGameObject("Camera"); ;
|
||||
gameObjectCamera.BehaviourController.AddBehaviour<CameraController>();
|
||||
cameraBehaviour = gameObjectCamera.BehaviourController.AddBehaviour<MonoGameCamera2DBehaviour>(graphics);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IGameObject gameObjectPongManager = gameManager.InstantiateGameObject().SetGameObject("Pong Game Manager");
|
||||
pongManager = gameObjectPongManager.BehaviourController.AddBehaviour<PongManagerBehaviour>(5);
|
||||
pongManager.Id = "pongManager";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IGameObject gameObjectBall = gameManager.InstantiateGameObject().SetGameObject("Ball");
|
||||
gameObjectBall.Transform.SetTransform(position: new Vector2D(0, 0f), scale: new Vector2D(10f, 10f));
|
||||
|
||||
gameObjectBall.BehaviourController.AddBehaviour<CircleBehaviour>(new Circle(Vector2D.Zero, 1f));
|
||||
gameObjectBall.BehaviourController.AddBehaviour<BallBehaviour>().Id = "ball";
|
||||
gameObjectBall.BehaviourController.AddBehaviour<RigidBody2D>();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IGameObject gameObjectLeftPaddle = gameManager.InstantiateGameObject().SetGameObject("Left Paddle");
|
||||
gameObjectLeftPaddle.Transform.SetTransform(position: new Vector2D(-468f, 0f), scale: new Vector2D(15f, 60f));
|
||||
|
||||
gameObjectLeftPaddle.BehaviourController.AddBehaviour<NetworkedKeyboardInputs>().Id = "leftPaddleInput";
|
||||
gameObjectLeftPaddle.BehaviourController.AddBehaviour<PaddleBehaviour>(Keys.W, Keys.S, 228f, -228f, 400f).Id = "leftPaddle";
|
||||
gameObjectLeftPaddle.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
|
||||
gameObjectLeftPaddle.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||
|
||||
IGameObject gameObjectRightPaddle = gameManager.InstantiateGameObject().SetGameObject("Right Paddle");
|
||||
gameObjectRightPaddle.Transform.SetTransform(position: new Vector2D(468f, 0f), scale: new Vector2D(15f, 60f));
|
||||
gameObjectRightPaddle.BehaviourController.AddBehaviour<NetworkedKeyboardInputs>().Id = "rightPaddleInput";
|
||||
gameObjectRightPaddle.BehaviourController.AddBehaviour<PaddleBehaviour>(Keys.Up, Keys.Down, 228f, -228f, 400f).Id = "rightPaddle";
|
||||
gameObjectRightPaddle.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
|
||||
gameObjectRightPaddle.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IGameObject gameObjectWallTop = gameManager.InstantiateGameObject().SetGameObject("Wall Top");
|
||||
gameObjectWallTop.Transform.SetTransform(position: new Vector2D(0f, 308f), scale: new Vector2D(552f, 20f));
|
||||
gameObjectWallTop.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
|
||||
gameObjectWallTop.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||
|
||||
IGameObject gameObjectWallBottom = gameManager.InstantiateGameObject().SetGameObject("Wall Bottom");
|
||||
gameObjectWallBottom.Transform.SetTransform(position: new Vector2D(0f, -308f), scale: new Vector2D(552f, 20f));
|
||||
gameObjectWallBottom.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
|
||||
gameObjectWallBottom.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||
|
||||
IGameObject gameObjectWallRight = gameManager.InstantiateGameObject().SetGameObject("Wall Right");
|
||||
gameObjectWallRight.Transform.SetTransform(position: new Vector2D(532f, 0f), scale: new Vector2D(20f, 328f));
|
||||
gameObjectWallRight.BehaviourController.AddBehaviour<WallScoreBehaviour>((Action)pongManager.ScoreToLeft);
|
||||
gameObjectWallRight.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
|
||||
gameObjectWallRight.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||
|
||||
IGameObject gameObjectWallLeft = gameManager.InstantiateGameObject().SetGameObject("Wall Left");
|
||||
gameObjectWallLeft.Transform.SetTransform(position: new Vector2D(-532f, 0f), scale: new Vector2D(20f, 328f));
|
||||
gameObjectWallLeft.BehaviourController.AddBehaviour<WallScoreBehaviour>((Action)pongManager.ScoreToRight);
|
||||
gameObjectWallLeft.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
|
||||
gameObjectWallLeft.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IGameObject gameObjectLeftScoreText = gameManager.InstantiateGameObject().SetGameObject("Score Left");
|
||||
gameObjectLeftScoreText.Transform.SetTransform(position: new Vector2D(-250f, 250f), scale: Vector2D.One * .25f);
|
||||
gameObjectLeftScoreText.BehaviourController.AddBehaviour<TextScoreBehaviour>(true, spriteFont);
|
||||
|
||||
IGameObject gameObjectRightScoreText = gameManager.InstantiateGameObject().SetGameObject("Score Right");
|
||||
gameObjectRightScoreText.Transform.SetTransform(position: new Vector2D(250f, 250f), scale: Vector2D.One * .25f);
|
||||
gameObjectRightScoreText.BehaviourController.AddBehaviour<TextScoreBehaviour>(false, spriteFont);
|
||||
}
|
||||
|
||||
protected override void Update(GameTime gameTime)
|
||||
{
|
||||
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
|
||||
Exit();
|
||||
|
||||
gameManager.Update(gameTime.ToEngineTime());
|
||||
while (physicsTimer + 0.01f < gameTime.TotalGameTime.TotalMilliseconds * .001f)//seconds)
|
||||
{
|
||||
physicsTimer += 0.01f;
|
||||
physicsEngine.Step(.01f);
|
||||
}
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
protected override void Draw(GameTime gameTime)
|
||||
{
|
||||
GraphicsDevice.Clear(new Color() { R = 32, G = 32, B = 32 });
|
||||
|
||||
// TODO: Add your drawing code here
|
||||
gameManager.PreDraw();
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cameraBehaviour.MatrixTransform);
|
||||
foreach (var displayable in displayableCollector)
|
||||
displayable.Draw(spriteBatch);
|
||||
spriteBatch.End();
|
||||
|
||||
shapeBatch.Begin(cameraBehaviour.MatrixTransform);
|
||||
foreach (var displayableShape in displayableShapeCollector)
|
||||
displayableShape.Draw(shapeBatch);
|
||||
shapeBatch.End();
|
||||
|
||||
base.Draw(gameTime);
|
||||
}
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="ISprite"/> field.
|
||||
/// </summary>
|
||||
public interface IAssignableSprite : IAssignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback triggered when the <see cref="ISprite"/> value has has been assigned a new value.
|
||||
/// </summary>
|
||||
Action<IAssignableSprite>? OnSpriteAssigned { get; set; }
|
||||
|
||||
/// <inheritdoc cref="ISprite" />
|
||||
ISprite Sprite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Assign a value to the <see cref="ISprite"/> field of this object
|
||||
/// </summary>
|
||||
/// <param name="sprite">New <see cref="ISprite"/> to assign.</param>
|
||||
/// <returns>
|
||||
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
|
||||
/// </returns>
|
||||
bool Assign(ISprite sprite);
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Syntriax.Engine.Core.Abstract;
|
||||
|
||||
// TODO Probably gonna have to rethink this
|
||||
public interface ISprite
|
||||
{
|
||||
Action<ISprite>? OnTextureChanged { get; set; }
|
||||
|
||||
Texture2D Texture2D { get; set; }
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class Sprite : ISprite
|
||||
{
|
||||
public Action<ISprite>? OnTextureChanged { get; set; }
|
||||
|
||||
private Texture2D _texture = null!;
|
||||
|
||||
public Texture2D Texture2D
|
||||
{
|
||||
get => _texture;
|
||||
set
|
||||
{
|
||||
if (_texture == value)
|
||||
return;
|
||||
|
||||
_texture = value;
|
||||
OnTextureChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Graphics.TwoDimensional.Abstract;
|
||||
public interface IDisplayableSprite : IDisplayable, IAssignableSprite
|
||||
{
|
||||
Action<IDisplayableSprite>? OnSpriteEffectsChanged { get; set; }
|
||||
Action<IDisplayableSprite>? OnOriginChanged { get; set; }
|
||||
Action<IDisplayableSprite>? OnColorChanged { get; set; }
|
||||
Action<IDisplayableSprite>? OnDepthChanged { get; set; }
|
||||
|
||||
SpriteEffects SpriteEffects { get; set; }
|
||||
Vector2 Origin { get; set; }
|
||||
Color Color { get; set; }
|
||||
float Depth { get; set; }
|
||||
}
|
@@ -1,117 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Graphics.TwoDimensional.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Graphics.TwoDimensional;
|
||||
|
||||
public class DisplayableSpriteBehaviour : Behaviour, IDisplayableSprite, IAssignableSprite
|
||||
{
|
||||
public Action<IAssignableSprite>? OnSpriteAssigned { get; set; } = null;
|
||||
public Action<IDisplayableSprite>? OnSpriteEffectsChanged { get; set; } = null;
|
||||
public Action<IDisplayableSprite>? OnOriginChanged { get; set; } = null;
|
||||
public Action<IDisplayableSprite>? OnColorChanged { get; set; } = null;
|
||||
public Action<IDisplayableSprite>? OnDepthChanged { get; set; } = null;
|
||||
|
||||
private ISprite _sprite = null!;
|
||||
private Color _color = Color.White;
|
||||
private float _depth = 0f;
|
||||
private SpriteEffects _spriteEffects = SpriteEffects.None;
|
||||
private Vector2 _origin = Vector2.One * .5f;
|
||||
|
||||
|
||||
public ISprite Sprite => _sprite;
|
||||
|
||||
public SpriteEffects SpriteEffects
|
||||
{
|
||||
get => _spriteEffects;
|
||||
set
|
||||
{
|
||||
if (_spriteEffects == value)
|
||||
return;
|
||||
|
||||
_spriteEffects = value;
|
||||
OnSpriteEffectsChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 Origin
|
||||
{
|
||||
get => _origin;
|
||||
set
|
||||
{
|
||||
if (_origin == value)
|
||||
return;
|
||||
|
||||
_origin = value;
|
||||
OnOriginChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Color Color
|
||||
{
|
||||
get => _color;
|
||||
set
|
||||
{
|
||||
if (_color == value)
|
||||
return;
|
||||
|
||||
_color = value;
|
||||
OnColorChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public float Depth
|
||||
{
|
||||
get => _depth;
|
||||
set
|
||||
{
|
||||
if (_depth == value)
|
||||
return;
|
||||
|
||||
_depth = value;
|
||||
OnDepthChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!BehaviourController.GameObject.StateEnable.Enabled || !StateEnable.Enabled)
|
||||
return;
|
||||
|
||||
ITransform transform = BehaviourController.GameObject.Transform;
|
||||
Vector2D position = transform.Position;
|
||||
Vector2D scale = transform.Scale;
|
||||
|
||||
Rectangle rectangle = new((int)position.X, -(int)position.Y, (int)(Sprite.Texture2D.Width * scale.X), (int)(Sprite.Texture2D.Height * scale.Y));
|
||||
|
||||
spriteBatch.Draw(Sprite.Texture2D, rectangle, null, Color, transform.Rotation, new Vector2(Sprite.Texture2D.Width, Sprite.Texture2D.Height) * Origin, SpriteEffects, Depth);
|
||||
}
|
||||
|
||||
public bool Assign(ISprite sprite)
|
||||
{
|
||||
_sprite = sprite;
|
||||
OnSpriteAssigned?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public DisplayableSpriteBehaviour() => OnUnassigned += OnUnassign;
|
||||
public DisplayableSpriteBehaviour(
|
||||
Color? color = null,
|
||||
float? depth = null,
|
||||
SpriteEffects? spriteEffects = null,
|
||||
Vector2? origin = null)
|
||||
{
|
||||
OnUnassigned += OnUnassign;
|
||||
|
||||
_color = color ?? _color;
|
||||
_depth = depth ?? _depth;
|
||||
_spriteEffects = spriteEffects ?? _spriteEffects;
|
||||
_origin = origin ?? _origin;
|
||||
}
|
||||
private void OnUnassign(IAssignable assignable) => _sprite = null!;
|
||||
}
|
18
Game/Network/Abstract/INetworkBehaviour.cs
Normal file
18
Game/Network/Abstract/INetworkBehaviour.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Network.Abstract;
|
||||
|
||||
public interface INetworkBehaviour : IBehaviour
|
||||
{
|
||||
int NetworkId { get; }
|
||||
|
||||
bool LocalAssigned { get; }
|
||||
|
||||
bool IsServer { get; }
|
||||
bool IsClient { get; }
|
||||
|
||||
INetworkCommunicator NetworkCommunicator { get; }
|
||||
|
||||
public void RequestAssignment();
|
||||
public void ReleaseAssignment();
|
||||
}
|
6
Game/Network/Abstract/INetworkClient.cs
Normal file
6
Game/Network/Abstract/INetworkClient.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Syntriax.Engine.Network.Abstract;
|
||||
|
||||
public interface INetworkClient : INetworkCommunicator
|
||||
{
|
||||
void Connect(string address, int port, string? password = null);
|
||||
}
|
22
Game/Network/Abstract/INetworkCommunicator.cs
Normal file
22
Game/Network/Abstract/INetworkCommunicator.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
using LiteNetLib;
|
||||
using LiteNetLib.Utils;
|
||||
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Network.Abstract;
|
||||
|
||||
public interface INetworkCommunicator
|
||||
{
|
||||
EventBasedNetListener Listener { get; }
|
||||
NetManager Manager { get; }
|
||||
|
||||
void PollEvents();
|
||||
void Stop();
|
||||
|
||||
void RegisterEntityListener(IEntity entity, Action<NetPacketReader, NetPeer> onDataReceived);
|
||||
void UnregisterEntityListener(IEntity entity);
|
||||
NetDataWriter GetEntityWriter(IEntity entity);
|
||||
NetDataWriter GetMessageWriter(IEntity entity);
|
||||
}
|
11
Game/Network/Abstract/INetworkEntity.cs
Normal file
11
Game/Network/Abstract/INetworkEntity.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Network.Abstract;
|
||||
|
||||
internal interface INetworkEntity
|
||||
{
|
||||
Action<INetworkEntity, int> OnNetworkIdChanged { get; set; }
|
||||
int NetworkId { get; set; }
|
||||
|
||||
void SetNetworkId(int id);
|
||||
}
|
10
Game/Network/Abstract/INetworkManager.cs
Normal file
10
Game/Network/Abstract/INetworkManager.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Syntriax.Engine.Network.Abstract;
|
||||
|
||||
public interface INetworkManager
|
||||
{
|
||||
// Action<IGameObject>? OnNetworkGameObjectInstantiated { get; set; }
|
||||
|
||||
INetworkCommunicator NetworkCommunicator { get; }
|
||||
|
||||
// Task<T> Instantiate<T>(params object?[]? args) where T : class, IGameObject;
|
||||
}
|
10
Game/Network/Abstract/INetworkServer.cs
Normal file
10
Game/Network/Abstract/INetworkServer.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Syntriax.Engine.Network.Abstract;
|
||||
|
||||
public interface INetworkServer : INetworkCommunicator
|
||||
{
|
||||
string Password { get; }
|
||||
int MaxConnectionCount { get; }
|
||||
int Port { get; }
|
||||
|
||||
void Start(int port, int maxConnectionCount, string? password = null);
|
||||
}
|
81
Game/Network/NetworkBase.cs
Normal file
81
Game/Network/NetworkBase.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using LiteNetLib;
|
||||
using LiteNetLib.Utils;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Network.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Network;
|
||||
|
||||
public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator
|
||||
{
|
||||
public EventBasedNetListener Listener { get; private set; } = null!;
|
||||
public NetManager Manager { get; private set; } = null!;
|
||||
|
||||
public NetworkBase()
|
||||
{
|
||||
Priority = 10;
|
||||
|
||||
Listener = new EventBasedNetListener();
|
||||
Manager = new NetManager(Listener);
|
||||
|
||||
Listener.NetworkReceiveEvent += NetworkReceiveEvent;
|
||||
}
|
||||
|
||||
public void PollEvents() => Manager.PollEvents();
|
||||
public void Stop() => Manager.Stop();
|
||||
|
||||
protected override void OnUpdate() => PollEvents();
|
||||
protected override void OnFinalize() => Stop();
|
||||
|
||||
private readonly Dictionary<string, Action<NetPacketReader, NetPeer>> callbacks = new(32);
|
||||
|
||||
private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
|
||||
{
|
||||
if (callbacks.TryGetValue(reader.GetString(), out var action))
|
||||
action?.Invoke(reader, peer);
|
||||
|
||||
reader.Recycle();
|
||||
}
|
||||
|
||||
public void RegisterEntityListener(IEntity entity, Action<NetPacketReader, NetPeer> onDataReceived)
|
||||
{
|
||||
if (callbacks.ContainsKey(entity.Id))
|
||||
return;
|
||||
|
||||
callbacks.Add(entity.Id, onDataReceived);
|
||||
entity.OnIdChanged += OnEntityIdChanged;
|
||||
}
|
||||
|
||||
public void UnregisterEntityListener(IEntity entity)
|
||||
{
|
||||
if (!callbacks.Remove(entity.Id))
|
||||
return;
|
||||
|
||||
entity.OnIdChanged -= OnEntityIdChanged;
|
||||
}
|
||||
|
||||
public NetDataWriter GetMessageWriter(IEntity entity)
|
||||
{
|
||||
NetDataWriter netDataWriter = GetEntityWriter(entity);
|
||||
netDataWriter.Put((int)MessageType.Message);
|
||||
return netDataWriter;
|
||||
}
|
||||
|
||||
public NetDataWriter GetEntityWriter(IEntity entity)
|
||||
{
|
||||
NetDataWriter netDataWriter = new();
|
||||
netDataWriter.Put(entity.Id);
|
||||
return netDataWriter;
|
||||
}
|
||||
|
||||
private void OnEntityIdChanged(IEntity entity, string previousId)
|
||||
{
|
||||
var action = callbacks[previousId];
|
||||
callbacks.Remove(previousId);
|
||||
callbacks.Add(entity.Id, action);
|
||||
}
|
||||
}
|
99
Game/Network/NetworkBehaviour.cs
Normal file
99
Game/Network/NetworkBehaviour.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
|
||||
using LiteNetLib;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Network.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Network;
|
||||
|
||||
public abstract class NetworkBehaviour : BehaviourOverride, INetworkBehaviour
|
||||
{
|
||||
public int NetworkId { get; private set; } = 0;
|
||||
|
||||
public bool LocalAssigned { get; private set; } = false;
|
||||
|
||||
public bool IsServer { get; private set; } = false;
|
||||
public bool IsClient { get; private set; } = false;
|
||||
|
||||
public INetworkCommunicator NetworkCommunicator { get; private set; } = null!;
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
NetworkCommunicator = BehaviourController.GetBehaviourInParent<INetworkCommunicator>()
|
||||
?? GameObject.GameManager.FindBehaviour<INetworkCommunicator>()
|
||||
?? throw new Exception($"Could not find an {nameof(INetworkCommunicator)}.");
|
||||
|
||||
NetworkCommunicator.RegisterEntityListener(this, OnMessageReceivedInternal);
|
||||
|
||||
if (NetworkCommunicator is INetworkClient client)
|
||||
{
|
||||
IsClient = true;
|
||||
return;
|
||||
}
|
||||
|
||||
IsServer = true;
|
||||
LocalAssigned = true;
|
||||
}
|
||||
|
||||
public void ReleaseAssignment()
|
||||
{
|
||||
if (IsServer)
|
||||
return;
|
||||
|
||||
var netDataWriter = NetworkCommunicator.GetEntityWriter(this);
|
||||
netDataWriter.Put((int)MessageType.AssignmentRelease);
|
||||
NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
public void RequestAssignment()
|
||||
{
|
||||
if (IsServer)
|
||||
return;
|
||||
|
||||
var netDataWriter = NetworkCommunicator.GetEntityWriter(this);
|
||||
netDataWriter.Put((int)MessageType.AssignmentRequest);
|
||||
NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
protected abstract void OnMessageReceived(NetPacketReader reader, NetPeer peer);
|
||||
private void OnMessageReceivedInternal(NetPacketReader reader, NetPeer peer)
|
||||
{
|
||||
MessageType messageType = (MessageType)reader.GetInt();
|
||||
|
||||
if (IsServer)
|
||||
{
|
||||
switch (messageType)
|
||||
{
|
||||
case MessageType.Message: OnMessageReceived(reader, peer); break;
|
||||
case MessageType.AssignmentRequest:
|
||||
case MessageType.AssignmentRelease:
|
||||
var netDataWriter = NetworkCommunicator.GetEntityWriter(this);
|
||||
netDataWriter.Put((int)(messageType == MessageType.AssignmentRequest ? MessageType.Assigned : MessageType.Unassigned));
|
||||
peer.Send(netDataWriter, DeliveryMethod.ReliableOrdered);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (messageType)
|
||||
{
|
||||
case MessageType.Message: OnMessageReceived(reader, peer); break;
|
||||
case MessageType.Assigned: LocalAssigned = true; break;
|
||||
case MessageType.Unassigned: LocalAssigned = false; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum MessageType
|
||||
{
|
||||
Message,
|
||||
AssignmentRequest,
|
||||
AssignmentRelease,
|
||||
Assigned,
|
||||
Unassigned,
|
||||
}
|
12
Game/Network/NetworkClient.cs
Normal file
12
Game/Network/NetworkClient.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Syntriax.Engine.Network.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Network;
|
||||
|
||||
public class NetworkClient : NetworkBase, INetworkClient
|
||||
{
|
||||
public void Connect(string address, int port, string? password = null)
|
||||
{
|
||||
Manager.Start();
|
||||
Manager.Connect(address, port, password ?? string.Empty);
|
||||
}
|
||||
}
|
20
Game/Network/NetworkExtensions.cs
Normal file
20
Game/Network/NetworkExtensions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using LiteNetLib;
|
||||
using LiteNetLib.Utils;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Network;
|
||||
|
||||
public static class NetworkExtensions
|
||||
{
|
||||
public static Vector2D GetVector2D(this NetPacketReader reader)
|
||||
=> new(reader.GetFloat(), reader.GetFloat());
|
||||
public static void GetVector2D(this NetPacketReader reader, out Vector2D vector2D)
|
||||
=> vector2D = new(reader.GetFloat(), reader.GetFloat());
|
||||
|
||||
public static void Put(this NetDataWriter writer, Vector2D vector)
|
||||
{
|
||||
writer.Put(vector.X);
|
||||
writer.Put(vector.Y);
|
||||
}
|
||||
}
|
31
Game/Network/NetworkManager.cs
Normal file
31
Game/Network/NetworkManager.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using LiteNetLib;
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
using Syntriax.Engine.Network.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Network;
|
||||
|
||||
public class NetworkManager : NetworkBehaviour, INetworkManager
|
||||
{
|
||||
private BehaviourCollector<INetworkEntity> entities = null!;
|
||||
|
||||
private static int networkIdIndex = 0;
|
||||
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
base.OnInitialize();
|
||||
|
||||
((INetworkEntity)this).SetNetworkId(networkIdIndex++);
|
||||
|
||||
entities = new(GameObject.GameManager);
|
||||
foreach (var entity in entities)
|
||||
entity.SetNetworkId(networkIdIndex++);
|
||||
|
||||
entities.OnCollected += OnCollected;
|
||||
}
|
||||
|
||||
private void OnCollected(BehaviourCollector<INetworkEntity> collector, INetworkEntity entity)
|
||||
=> entity.SetNetworkId(networkIdIndex++);
|
||||
|
||||
protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer) { }
|
||||
}
|
30
Game/Network/NetworkServer.cs
Normal file
30
Game/Network/NetworkServer.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Syntriax.Engine.Network.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Network;
|
||||
|
||||
public class NetworkServer : NetworkBase, INetworkServer
|
||||
{
|
||||
public string Password { get; private set; } = string.Empty;
|
||||
public int MaxConnectionCount { get; private set; } = 0;
|
||||
public int Port { get; private set; } = 8888;
|
||||
|
||||
public NetworkServer() : base()
|
||||
{
|
||||
Listener.ConnectionRequestEvent += request =>
|
||||
{
|
||||
if (Manager.ConnectedPeersCount < MaxConnectionCount)
|
||||
request.AcceptIfKey(Password);
|
||||
else
|
||||
request.Reject();
|
||||
};
|
||||
}
|
||||
|
||||
public void Start(int port, int maxConnectionCount, string? password = null)
|
||||
{
|
||||
Password = password ?? string.Empty;
|
||||
MaxConnectionCount = maxConnectionCount;
|
||||
Port = port;
|
||||
|
||||
Manager.Start(port);
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
public interface ICollider2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
Action<ICollider2D, ICollider2D>? OnCollisionPreResolve { get; set; }
|
||||
|
||||
IRigidBody2D? RigidBody2D { get; }
|
||||
|
||||
IList<Vector2D> Vertices { get; }
|
||||
|
||||
bool CheckCollision(Vector2D point);
|
||||
void Recalculate();
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
public interface ICollisionResolver2D
|
||||
{
|
||||
void ResolveCollision(ICollider2D colliderA, ICollider2D colliderB);
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
public interface IPhysicsEngine2D
|
||||
{
|
||||
int IterationCount { get; set; }
|
||||
|
||||
void AddRigidBody(IRigidBody2D rigidBody);
|
||||
void RemoveRigidBody(IRigidBody2D rigidBody);
|
||||
|
||||
void Step(float deltaTime);
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
public interface IPhysicsMaterial2D
|
||||
{
|
||||
float Friction { get; }
|
||||
float Restitution { get; }
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
public interface IRigidBody2D : IBehaviour, IAssignableTransform
|
||||
{
|
||||
IPhysicsMaterial2D Material { get; set; }
|
||||
|
||||
Vector2D Velocity { get; set; }
|
||||
float AngularVelocity { get; set; }
|
||||
|
||||
float Mass { get; set; }
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class Collider2DAABBBehaviour : BehaviourOverride, ICollider2D
|
||||
{
|
||||
public AABB AABBLocal { get; set; } = null!;
|
||||
public AABB AABBWorld { get; private set; } = null!;
|
||||
|
||||
private IRigidBody2D? _rigidBody2D = null;
|
||||
private List<Vector2D> _vertices = new List<Vector2D>(4);
|
||||
|
||||
public IRigidBody2D? RigidBody2D
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_rigidBody2D is null)
|
||||
BehaviourController.TryGetBehaviour(out _rigidBody2D);
|
||||
|
||||
return _rigidBody2D;
|
||||
}
|
||||
}
|
||||
|
||||
public Action<ICollider2D, ICollider2D>? OnCollisionPreResolve { get; set; } = null;
|
||||
|
||||
public Action<IAssignableTransform>? OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
|
||||
ITransform IAssignableTransform.Transform => Transform;
|
||||
public bool Assign(ITransform transform) => GameObject.Assign(transform);
|
||||
|
||||
public IList<Vector2D> Vertices => _vertices;
|
||||
|
||||
public bool CheckCollision(Vector2D point)
|
||||
{
|
||||
return AABBWorld.Overlaps(point);
|
||||
}
|
||||
|
||||
public void Recalculate()
|
||||
{
|
||||
AABBWorld = new AABB(
|
||||
AABBLocal.LowerBoundary.Scale(Transform.Scale) + Transform.Position,
|
||||
AABBLocal.UpperBoundary.Scale(Transform.Scale) + Transform.Position
|
||||
);
|
||||
|
||||
Vertices.Clear();
|
||||
Vertices.Add(AABBWorld.LowerBoundary);
|
||||
Vertices.Add(new Vector2D(AABBWorld.LowerBoundary.X, AABBWorld.UpperBoundary.Y));
|
||||
Vertices.Add(AABBWorld.UpperBoundary);
|
||||
Vertices.Add(new Vector2D(AABBWorld.UpperBoundary.X, AABBWorld.LowerBoundary.Y));
|
||||
}
|
||||
public Collider2DAABBBehaviour(Vector2D lowerBoundary, Vector2D upperBoundary)
|
||||
{
|
||||
AABBLocal = new AABB(lowerBoundary, upperBoundary);
|
||||
AABBWorld = new AABB(lowerBoundary, upperBoundary);
|
||||
}
|
||||
|
||||
public Collider2DAABBBehaviour()
|
||||
{
|
||||
AABBLocal = new(Vector2D.Zero, Vector2D.Zero);
|
||||
AABBWorld = new(Vector2D.Zero, Vector2D.Zero);
|
||||
}
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public record CollisionInformation
|
||||
(
|
||||
Vector2 Normal,
|
||||
Vector2 ContactPosition
|
||||
);
|
@@ -1,75 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class PhysicsEngine2D : IPhysicsEngine2D
|
||||
{
|
||||
private List<IRigidBody2D> rigidBodies = new List<IRigidBody2D>(32);
|
||||
private List<ICollider2D> colliders = new List<ICollider2D>(64);
|
||||
|
||||
private int _iterationCount = 1;
|
||||
|
||||
|
||||
public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
|
||||
|
||||
public void AddRigidBody(IRigidBody2D rigidBody)
|
||||
{
|
||||
if (rigidBodies.Contains(rigidBody))
|
||||
return;
|
||||
|
||||
rigidBodies.Add(rigidBody);
|
||||
|
||||
foreach (var collider2D in rigidBody.BehaviourController.GetBehaviours<ICollider2D>())
|
||||
colliders.Add(collider2D);
|
||||
|
||||
rigidBody.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
|
||||
rigidBody.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
|
||||
}
|
||||
|
||||
public void RemoveRigidBody(IRigidBody2D rigidBody)
|
||||
{
|
||||
rigidBodies.Remove(rigidBody);
|
||||
}
|
||||
|
||||
public void Step(float deltaTime)
|
||||
{
|
||||
float intervalDeltaTime = deltaTime / IterationCount;
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationCount; iterationIndex++)
|
||||
{
|
||||
foreach (var rigidBody in rigidBodies)
|
||||
StepRigidBody(rigidBody, intervalDeltaTime);
|
||||
|
||||
foreach (var collider in colliders)
|
||||
collider.Recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
|
||||
{
|
||||
rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime;
|
||||
rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime;
|
||||
}
|
||||
|
||||
private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is not ICollider2D collider2D)
|
||||
return;
|
||||
|
||||
colliders.Add(collider2D);
|
||||
}
|
||||
|
||||
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is not ICollider2D collider2D)
|
||||
return;
|
||||
|
||||
colliders.Remove(collider2D);
|
||||
}
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public record PhysicsMaterial2D(float Friction, float Restitution) : IPhysicsMaterial2D { }
|
@@ -1,6 +0,0 @@
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public record PhysicsMaterial2DDefault : PhysicsMaterial2D
|
||||
{
|
||||
public PhysicsMaterial2DDefault() : base(.1f, .1f) { }
|
||||
}
|
@@ -1,118 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public static class PhysicsMath
|
||||
{
|
||||
public static Vector2D Scale(this Vector2D original, Vector2D scale)
|
||||
=> new Vector2D(original.X * scale.X, original.Y * scale.Y);
|
||||
|
||||
public static Triangle ToSuperTriangle(this IList<Vector2D> vertices)
|
||||
{
|
||||
float minX = float.MaxValue, minY = float.MaxValue;
|
||||
float maxX = float.MinValue, maxY = float.MinValue;
|
||||
|
||||
foreach (Vector2D point in vertices)
|
||||
{
|
||||
minX = MathF.Min(minX, point.X);
|
||||
minY = MathF.Min(minY, point.Y);
|
||||
maxX = MathF.Max(maxX, point.X);
|
||||
maxY = MathF.Max(maxY, point.Y);
|
||||
}
|
||||
|
||||
float dx = maxX - minX;
|
||||
float dy = maxY - minY;
|
||||
float deltaMax = MathF.Max(dx, dy);
|
||||
float midX = (minX + maxX) / 2;
|
||||
float midY = (minY + maxY) / 2;
|
||||
|
||||
Vector2D p1 = new Vector2D((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
Vector2D p2 = new Vector2D((float)midX, (float)midY + 20 * (float)deltaMax);
|
||||
Vector2D p3 = new Vector2D((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
|
||||
return new Triangle(p1, p2, p3);
|
||||
}
|
||||
|
||||
public static IList<Line> ToLines(this IList<Vector2D> vertices)
|
||||
{
|
||||
List<Line> lines = new List<Line>(vertices.Count - 1);
|
||||
ToLines(vertices, lines);
|
||||
return lines;
|
||||
}
|
||||
|
||||
public static void ToLines(this IList<Vector2D> vertices, IList<Line> lines)
|
||||
{
|
||||
lines.Clear();
|
||||
for (int i = 0; i < vertices.Count - 1; i++)
|
||||
lines.Add(new(vertices[i], vertices[i + 1]));
|
||||
lines.Add(new(vertices[^1], vertices[0]));
|
||||
}
|
||||
|
||||
public static bool LaysOn(this Vector2D point, Line line)
|
||||
=> line.Resolve(point.X).ApproximatelyEquals(point);
|
||||
|
||||
|
||||
// Given three collinear points p, q, r, the function checks if
|
||||
// point q lies on line segment 'pr'
|
||||
public static bool OnSegment(Vector2D p, Vector2D q, Vector2D r)
|
||||
{
|
||||
if (q.X <= MathF.Max(p.X, r.X) && q.X >= MathF.Min(p.X, r.X) &&
|
||||
q.Y <= MathF.Max(p.Y, r.Y) && q.Y >= MathF.Min(p.Y, r.Y))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// To find orientation of ordered triplet (p, q, r).
|
||||
// The function returns following values
|
||||
// 0 --> p, q and r are collinear
|
||||
// 1 --> Clockwise
|
||||
// 2 --> Counterclockwise
|
||||
public static int Orientation(Vector2D p, Vector2D q, Vector2D r)
|
||||
{
|
||||
// See https://www.geeksforgeeks.org/orientation-3-ordered-points/
|
||||
// for details of below formula.
|
||||
float val = (q.Y - p.Y) * (r.X - q.X) -
|
||||
(q.X - p.X) * (r.Y - q.Y);
|
||||
|
||||
if (val == 0) return 0; // collinear
|
||||
|
||||
return (val > 0) ? 1 : 2; // clock or counterclock wise
|
||||
}
|
||||
|
||||
public static float IntersectionParameterT(Vector2D p0, Vector2D p1, Vector2D q0, Vector2D q1)
|
||||
=> ((q0.X - p0.X) * (p1.Y - p0.Y) - (q0.Y - p0.Y) * (p1.X - p0.X)) /
|
||||
((q1.Y - q0.Y) * (p1.X - p0.X) - (q1.X - q0.X) * (p1.Y - p0.Y));
|
||||
|
||||
|
||||
public static bool ApproximatelyEquals(this float a, float b)
|
||||
=> ApproximatelyEquals(a, b, float.Epsilon);
|
||||
public static bool ApproximatelyEquals(this Vector2 a, Vector2 b)
|
||||
=> ApproximatelyEquals(a, b, float.Epsilon);
|
||||
public static bool ApproximatelyEquals(this Vector2 a, Vector2 b, float epsilon)
|
||||
=> ApproximatelyEquals(a.X, b.X, epsilon) && ApproximatelyEquals(a.Y, b.Y, epsilon);
|
||||
public static bool ApproximatelyEquals(this Vector2D a, Vector2D b)
|
||||
=> ApproximatelyEquals(a, b, float.Epsilon);
|
||||
public static bool ApproximatelyEquals(this Vector2D a, Vector2D b, float epsilon)
|
||||
=> ApproximatelyEquals(a.X, b.X, epsilon) && ApproximatelyEquals(a.Y, b.Y, epsilon);
|
||||
public static bool ApproximatelyEquals(this float a, float b, float epsilon)
|
||||
{
|
||||
if (a == b)
|
||||
return true;
|
||||
|
||||
const float floatNormal = (1 << 23) * float.Epsilon;
|
||||
float absA = MathF.Abs(a);
|
||||
float absB = MathF.Abs(b);
|
||||
float diff = MathF.Abs(a - b);
|
||||
|
||||
if (a == 0.0f || b == 0.0f || diff < floatNormal)
|
||||
return diff < (epsilon * floatNormal);
|
||||
|
||||
return diff / MathF.Min(absA + absB, float.MaxValue) < epsilon;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary)
|
||||
{
|
||||
public bool Overlaps(Vector2D point)
|
||||
=> point.X >= LowerBoundary.X && point.X <= UpperBoundary.X &&
|
||||
point.Y >= LowerBoundary.Y && point.Y <= UpperBoundary.Y;
|
||||
|
||||
public bool Overlaps(AABB other)
|
||||
=> LowerBoundary.X <= other.UpperBoundary.X && UpperBoundary.X >= other.LowerBoundary.X &&
|
||||
LowerBoundary.Y <= other.UpperBoundary.Y && UpperBoundary.Y >= other.LowerBoundary.Y;
|
||||
|
||||
public bool ApproximatelyEquals(AABB other)
|
||||
=> LowerBoundary.ApproximatelyEquals(other.LowerBoundary) && UpperBoundary.ApproximatelyEquals(other.UpperBoundary);
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Circle(Vector2D Position, float Radius)
|
||||
{
|
||||
public bool Intersects(Circle other)
|
||||
{
|
||||
float distanceSquared = (Position - other.Position).LengthSquared();
|
||||
float radiusSumSquared = Radius * Radius + other.Radius * other.Radius;
|
||||
|
||||
return distanceSquared < radiusSumSquared;
|
||||
}
|
||||
|
||||
public bool Overlaps(Vector2D point) => (Position - point).LengthSquared() <= Radius * Radius;
|
||||
public bool ApproximatelyEquals(Circle other)
|
||||
=> Position.ApproximatelyEquals(other.Position) && Radius.ApproximatelyEquals(other.Radius);
|
||||
}
|
@@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Line(Vector2D From, Vector2D To)
|
||||
{
|
||||
public Line Reversed => new(To, From);
|
||||
public Vector2D Direction => Vector2D.Normalize(To - From);
|
||||
public float Length => (From - To).Length();
|
||||
public float LengthSquared => (From - To).LengthSquared();
|
||||
|
||||
public LineEquation LineEquation
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector2D slopeVector = To - From;
|
||||
float slope = slopeVector.Y / slopeVector.X;
|
||||
|
||||
float yOffset = From.Y - (slope * From.X);
|
||||
|
||||
return new LineEquation(slope, yOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Intersects(Vector2D point)
|
||||
=> Resolve(point.X).ApproximatelyEquals(point);
|
||||
|
||||
public float GetT(Vector2D point)
|
||||
{
|
||||
float fromX = MathF.Abs(From.X);
|
||||
float toX = MathF.Abs(To.X);
|
||||
float pointX = MathF.Abs(point.X);
|
||||
|
||||
float min = MathF.Min(fromX, toX);
|
||||
float max = MathF.Max(fromX, toX) - min;
|
||||
|
||||
pointX -= min;
|
||||
|
||||
float t = pointX / max;
|
||||
|
||||
// FIXME
|
||||
// I don't even know, apparently whatever I wrote up there doesn't take into account of the direction of the line
|
||||
// Which... I can see how, but I am also not sure how I can make it take into account. Or actually I'm for some reason
|
||||
// too unmotivated to find a solution. Future me, find a better way if possible, please.
|
||||
if (!Lerp(t).ApproximatelyEquals(point))
|
||||
return 1f - t;
|
||||
return t;
|
||||
}
|
||||
|
||||
public bool Exist(List<Vector2D> vertices)
|
||||
{
|
||||
for (int i = 0; i < vertices.Count - 1; i++)
|
||||
{
|
||||
Vector2D vertexCurrent = vertices[i];
|
||||
Vector2D vertexNext = vertices[i];
|
||||
if (From == vertexCurrent && To == vertexNext) return true;
|
||||
if (From == vertexNext && To == vertexCurrent) return true;
|
||||
}
|
||||
|
||||
Vector2D vertexFirst = vertices[0];
|
||||
Vector2D vertexLast = vertices[^1];
|
||||
if (From == vertexFirst && To == vertexLast) return true;
|
||||
if (From == vertexLast && To == vertexFirst) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public float IntersectionParameterT(Line other)
|
||||
{
|
||||
float numerator = (From.X - other.From.X) * (other.From.Y - other.To.Y) - (From.Y - other.From.Y) * (other.From.X - other.To.X);
|
||||
float denominator = (From.X - To.X) * (other.From.Y - other.To.Y) - (From.Y - To.Y) * (other.From.X - other.To.X);
|
||||
|
||||
// Lines are parallel
|
||||
if (denominator == 0)
|
||||
return float.NaN;
|
||||
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
public Vector2D Lerp(float t)
|
||||
=> new Vector2D(
|
||||
From.X + (To.X - From.X) * t,
|
||||
From.Y + (To.Y - From.Y) * t
|
||||
);
|
||||
|
||||
public Vector2D Resolve(float x)
|
||||
=> new Vector2D(x, LineEquation.Resolve(x));
|
||||
|
||||
public Vector2D ClosestPointTo(Vector2D point)
|
||||
{
|
||||
// Convert edge points to vectors
|
||||
var edgeVector = new Vector2D(To.X - From.X, To.Y - From.Y);
|
||||
var pointVector = new Vector2D(point.X - From.X, point.Y - From.Y);
|
||||
|
||||
// Calculate the projection of pointVector onto edgeVector
|
||||
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
|
||||
|
||||
// Clamp t to the range [0, 1] to ensure the closest point is on the edge
|
||||
t = MathF.Max(0, MathF.Min(1, t));
|
||||
|
||||
// Calculate the closest point on the edge
|
||||
float closestX = From.X + t * edgeVector.X;
|
||||
float closestY = From.Y + t * edgeVector.Y;
|
||||
|
||||
return new Vector2D((float)closestX, (float)closestY);
|
||||
}
|
||||
|
||||
public Vector2D IntersectionPoint(Line other)
|
||||
=> Vector2D.Lerp(From, To, IntersectionParameterT(other));
|
||||
|
||||
public bool Intersects(Line other)
|
||||
{
|
||||
int o1 = PhysicsMath.Orientation(From, To, other.From);
|
||||
int o2 = PhysicsMath.Orientation(From, To, other.To);
|
||||
int o3 = PhysicsMath.Orientation(other.From, other.To, From);
|
||||
int o4 = PhysicsMath.Orientation(other.From, other.To, To);
|
||||
|
||||
if (o1 != o2 && o3 != o4)
|
||||
return true;
|
||||
|
||||
if (o1 == 0 && PhysicsMath.OnSegment(From, other.From, To)) return true;
|
||||
if (o2 == 0 && PhysicsMath.OnSegment(From, other.To, To)) return true;
|
||||
if (o3 == 0 && PhysicsMath.OnSegment(other.From, From, other.To)) return true;
|
||||
if (o4 == 0 && PhysicsMath.OnSegment(other.From, To, other.To)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Intersects(Line other, [NotNullWhen(returnValue: true)] out Vector2D? point)
|
||||
{
|
||||
point = null;
|
||||
|
||||
bool result = Intersects(other);
|
||||
|
||||
if (result)
|
||||
point = IntersectionPoint(other);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool ApproximatelyEquals(Line other)
|
||||
=> From.ApproximatelyEquals(other.From) && To.ApproximatelyEquals(other.To);
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record LineEquation(float Slope, float OffsetY)
|
||||
{
|
||||
public float Resolve(float x) => Slope * x + OffsetY; // y = mx + b
|
||||
public bool ApproximatelyEquals(LineEquation other)
|
||||
=> Slope.ApproximatelyEquals(other.Slope) && OffsetY.ApproximatelyEquals(other.OffsetY);
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public static class Math
|
||||
{
|
||||
public const float RadianToDegree = 57.29577866666166f;
|
||||
public const float DegreeToRadian = 0.01745329277777778f;
|
||||
|
||||
public static float Clamp(float value, float min, float max) => (value < min) ? min : (value > max) ? max : value;
|
||||
}
|
@@ -1,68 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Shape(IList<Vector2D> Vertices)
|
||||
{
|
||||
public Triangle SuperTriangle
|
||||
{
|
||||
get
|
||||
{
|
||||
float minX = float.MaxValue, minY = float.MaxValue;
|
||||
float maxX = float.MinValue, maxY = float.MinValue;
|
||||
|
||||
foreach (Vector2D point in Vertices)
|
||||
{
|
||||
minX = MathF.Min(minX, point.X);
|
||||
minY = MathF.Min(minY, point.Y);
|
||||
maxX = MathF.Max(maxX, point.X);
|
||||
maxY = MathF.Max(maxY, point.Y);
|
||||
}
|
||||
|
||||
float dx = maxX - minX;
|
||||
float dy = maxY - minY;
|
||||
float deltaMax = MathF.Max(dx, dy);
|
||||
float midX = (minX + maxX) / 2;
|
||||
float midY = (minY + maxY) / 2;
|
||||
|
||||
Vector2D p1 = new Vector2D((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
Vector2D p2 = new Vector2D((float)midX, (float)midY + 20 * (float)deltaMax);
|
||||
Vector2D p3 = new Vector2D((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax);
|
||||
|
||||
return new Triangle(p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Line> Lines
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Line> lines = new List<Line>(Vertices.Count - 1);
|
||||
GetLinesNonAlloc(lines);
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetLinesNonAlloc(IList<Line> lines)
|
||||
{
|
||||
lines.Clear();
|
||||
for (int i = 0; i < Vertices.Count - 1; i++)
|
||||
lines.Add(new(Vertices[i], Vertices[i + 1]));
|
||||
lines.Add(new(Vertices[^1], Vertices[0]));
|
||||
}
|
||||
|
||||
public bool ApproximatelyEquals(Shape other)
|
||||
{
|
||||
if (Vertices.Count != other.Vertices.Count)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < Vertices.Count; i++)
|
||||
if (!Vertices[i].ApproximatelyEquals(other.Vertices[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Triangle(Vector2D A, Vector2D B, Vector2D C)
|
||||
{
|
||||
public float Area => MathF.Abs((
|
||||
A.X * (B.Y - C.Y) +
|
||||
B.X * (C.Y - A.Y) +
|
||||
C.X * (A.Y - B.Y)
|
||||
) * .5f);
|
||||
|
||||
public Circle CircumCircle
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector2D midAB = (A + B) / 2;
|
||||
Vector2D midBC = (B + C) / 2;
|
||||
|
||||
float slopeAB = (B.Y - A.Y) / (B.X - A.X);
|
||||
float slopeBC = (C.Y - B.Y) / (C.X - B.X);
|
||||
|
||||
Vector2D center;
|
||||
if (MathF.Abs(slopeAB - slopeBC) > float.Epsilon)
|
||||
{
|
||||
float x = (slopeAB * slopeBC * (A.Y - C.Y) + slopeBC * (A.X + B.X) - slopeAB * (B.X + C.X)) / (2 * (slopeBC - slopeAB));
|
||||
float y = -(x - (A.X + B.X) / 2) / slopeAB + (A.Y + B.Y) / 2;
|
||||
center = new Vector2D(x, y);
|
||||
}
|
||||
else
|
||||
center = (midAB + midBC) * .5f;
|
||||
|
||||
return new(center, Vector2D.Distance(center, A));
|
||||
}
|
||||
}
|
||||
|
||||
public bool Overlaps(Vector2D point)
|
||||
{
|
||||
float originalTriangleArea = Area;
|
||||
|
||||
float pointTriangleArea1 = new Triangle(point, B, C).Area;
|
||||
float pointTriangleArea2 = new Triangle(A, point, C).Area;
|
||||
float pointTriangleArea3 = new Triangle(A, B, point).Area;
|
||||
|
||||
float pointTriangleAreasSum = pointTriangleArea1 + pointTriangleArea2 + pointTriangleArea3;
|
||||
|
||||
return originalTriangleArea.ApproximatelyEquals(pointTriangleAreasSum, float.Epsilon * 3f);
|
||||
}
|
||||
|
||||
public bool ApproximatelyEquals(Triangle other)
|
||||
=> A.ApproximatelyEquals(other.A) && B.ApproximatelyEquals(other.B) && C.ApproximatelyEquals(other.C);
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class RigidBody2D : BehaviourOverride, IRigidBody2D
|
||||
{
|
||||
public Action<IAssignableTransform>? OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
|
||||
|
||||
|
||||
public IPhysicsMaterial2D Material { get; set; } = new PhysicsMaterial2DDefault();
|
||||
|
||||
public Vector2D Velocity { get; set; } = Vector2D.Zero;
|
||||
public float AngularVelocity { get; set; } = 0f;
|
||||
public float Mass { get; set; } = 0f;
|
||||
|
||||
ITransform IAssignableTransform.Transform => Transform;
|
||||
|
||||
|
||||
public bool Assign(ITransform transform) => GameObject.Assign(transform);
|
||||
}
|
@@ -1,3 +1,3 @@
|
||||
|
||||
using var game = new Pong.Game1();
|
||||
using var game = new Pong.GamePong();
|
||||
game.Run();
|
||||
|
12
Pong.sln
12
Pong.sln
@@ -11,7 +11,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Game", "Game\Game.csproj",
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Input", "Engine\Engine.Input\Engine.Input.csproj", "{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Graphics", "Engine\Engine.Graphics\Engine.Graphics.csproj", "{7371D9AF-6F4D-4F05-8458-197C4EE66B01}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Physics2D", "Engine\Engine.Physics2D\Engine.Physics2D.csproj", "{0D97F83C-B043-48B1-B155-7354C4E84FC0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -34,14 +34,14 @@ Global
|
||||
{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7371D9AF-6F4D-4F05-8458-197C4EE66B01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7371D9AF-6F4D-4F05-8458-197C4EE66B01}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7371D9AF-6F4D-4F05-8458-197C4EE66B01}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7371D9AF-6F4D-4F05-8458-197C4EE66B01}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||
{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||
{7371D9AF-6F4D-4F05-8458-197C4EE66B01} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
Reference in New Issue
Block a user