80 Commits

Author SHA1 Message Date
1b1d9dcdac feat: Paddle Data Send 2024-07-14 23:55:06 +03:00
2565617ff9 chore: Cleanup 2024-07-14 23:22:30 +03:00
01668c7be3 feat: Added Networked Inputs to GameObjects 2024-02-13 11:54:01 +03:00
cafbc55780 feat: Networked Keyboard Inputs Finished 2024-02-13 11:16:43 +03:00
ea3c4a2d2a refactor: Removed Network Code From Paddle 2024-02-13 11:15:27 +03:00
89b756a615 refactor: Nullable Keyboard Input Actions 2024-02-13 11:08:53 +03:00
6af84bcb6d feat: Networked Keyboard Inputs 2024-02-12 17:53:51 +03:00
3c1528d879 wip: Network 2024-02-12 16:35:24 +03:00
6f3e7b4ae5 wip: INetworkEntity & INetworkManager 2024-02-09 11:50:16 +03:00
86ef57fb62 refactor: Folder and Namespace Restructure of Network Code 2024-02-09 10:00:59 +03:00
6c326b1fc6 chore: Engine Update 2024-02-09 09:44:25 +03:00
b8b10de08a wip: INetworkBehaviour 2024-02-08 17:59:04 +03:00
edd2dd8511 fix: Network Problems on Ball Synchronization 2024-02-05 13:32:27 +03:00
70ac012a83 refactor: ContentLoaderCacher Action 2024-02-05 13:23:59 +03:00
d5a904fe8f feat: Ball Hit Sound Effect 2024-02-05 13:19:40 +03:00
b1582ab5c2 feat: IMonoGameContentLoader 2024-02-05 13:18:33 +03:00
8a0a0289f9 chore: Engine Update 2024-02-05 12:20:20 +03:00
776d029e7e refactor: Code Readability 2024-02-05 12:09:32 +03:00
5afb8b69bf refactor: Window Title According to Network State 2024-02-04 12:23:41 +03:00
65073ec1ca fix: Pong Manager not Being Able to Send Data Over Network 2024-02-04 12:23:25 +03:00
0d62e5c986 chore: .vscode Location 2024-02-04 12:22:17 +03:00
fe5a08840c feat: Test Network 2024-02-02 17:44:51 +03:00
1c7b1cb78a feat: Entity Messaging 2024-02-02 17:44:20 +03:00
78010c266e feat: NetworkExtensions for Vector2D Communication 2024-02-02 17:44:11 +03:00
ef8b04648c feat: NetworkClient & Server to IBehaviour 2024-02-02 17:44:00 +03:00
d4c57c0153 feat: Network Interfaces 2024-02-02 17:43:49 +03:00
0e198afeab fix: Invalid Namespace 2024-02-02 09:13:51 +03:00
de267a9d0f feat: NetworkServer 2024-02-01 23:38:39 +03:00
977a2abdd7 feat: NetworkClient 2024-02-01 23:38:35 +03:00
91e88bbff8 feat: Added LiteNetLib 2024-02-01 22:14:59 +03:00
257e414c2a feat: CameraController Rotated Movement 2024-02-01 18:50:06 +03:00
0cb8170530 chore: Engine Time Update 2024-02-01 18:47:27 +03:00
bbf71f0be6 chore: Engine Update 2024-02-01 15:37:16 +03:00
41a17219b6 chore: Engine Update 2024-02-01 15:35:40 +03:00
29e79cdf26 chore: Engine Update 2024-02-01 14:52:12 +03:00
b7a4fe9cb0 chore: Engine Update 2024-02-01 14:43:13 +03:00
da83802427 chore: Engine Update 2024-02-01 11:33:55 +03:00
364c383e08 chore: ICamera2D to Engine 2024-01-31 17:30:37 +03:00
1fd8060857 feat: Applied ICamera2D 2024-01-31 17:28:41 +03:00
35933dfd14 feat: ICamera2D 2024-01-31 17:23:24 +03:00
d66b119119 refactor: Constructor Placements 2024-01-31 14:40:14 +03:00
e7ee460323 feat: Random Ball Start Direction 2024-01-31 13:56:32 +03:00
e86cf71010 fix: Build Errors 2024-01-31 13:43:04 +03:00
a357f0fdcb refactor: Removed Unnecessary Graphics Folder 2024-01-31 12:00:14 +03:00
ff3202eda9 refactor: Renamed Behaviours 2024-01-31 11:52:22 +03:00
8929d3282f refactor: Removed Unused Behaviours 2024-01-31 11:50:38 +03:00
bc7925dbab refactor: Text Behaviour Font Constructor 2024-01-31 11:23:07 +03:00
80ee5d760a refactor: Unnecessary Declarations Removed 2024-01-31 11:20:14 +03:00
de2d5fb446 refactor: Removed Static GamePong Fields 2024-01-31 11:06:42 +03:00
753c0031cd feat: Upgraded Engine to BehaviourCachers 2024-01-31 10:09:41 +03:00
a734f52b38 refactor: Removed Unnecessary Camera Viewport Assignment 2024-01-30 17:31:25 +03:00
4cb9c7dc8d refactor: Shortened Code 2024-01-30 17:03:04 +03:00
fe4618abd3 fix: Warning Fix 2024-01-30 15:14:45 +03:00
283556d329 refactor: Game1 to GamePong 2024-01-30 15:14:09 +03:00
5ef3d491c3 refactor: Math.Pow to Math.Sqr 2024-01-30 15:09:04 +03:00
46f589d3aa feat: Camera Reset 2024-01-30 14:35:24 +03:00
b7d49e41c3 refactor: General 2024-01-30 14:11:58 +03:00
677fec7acc refactor: Pong Reset Key Press Moved Into PongScoreboard 2024-01-30 13:22:29 +03:00
a9c7b8b660 refactor: Camera Controller 2024-01-30 13:16:07 +03:00
958a75552f refactor: MonoGameCamera 2024-01-30 12:07:25 +03:00
3717701171 refactor: Engine.Core ICamera Removal 2024-01-30 12:03:31 +03:00
4e95a3a420 feat: Ball Bounce Depending on Side Distance 2024-01-29 17:58:42 +03:00
ad60412d5f feat: Ball Speed Up Over Time 2024-01-29 16:16:58 +03:00
0b2158cc7b fix: Ball Not Resetting Position On Reset 2024-01-28 16:25:53 +03:00
3580469a07 refactor: Removed Shape Center Circles 2024-01-28 16:09:06 +03:00
a7e8787579 style: Moved Play Area Boxes Further Out 2024-01-28 15:55:35 +03:00
45df32dabb feat: Implemented Basic Mechanics 2024-01-28 15:45:39 +03:00
b3af693678 chore: Engine Update 2024-01-28 15:45:15 +03:00
c87827d1ef fix: UbuntuMono Smooth Text Problems 2024-01-28 15:45:04 +03:00
c2d2e1fd52 feat: TextBehaviour 2024-01-28 15:44:33 +03:00
7ad2cc5912 refactor: MovementBallBehaviour 2024-01-28 15:44:20 +03:00
ca92b12833 feat: Score Boundary 2024-01-28 15:33:45 +03:00
fbe3087502 feat: PongScoreboard 2024-01-28 15:33:36 +03:00
ef347320fd feat: Circle & Shape Behaviours IsActive Check 2024-01-28 15:33:15 +03:00
1207d04d50 feat: Ubuntu Mono Font Added & Removed Old Unusued Sprites 2024-01-28 15:32:45 +03:00
26f11b5cc0 refactor: Circle Behaviour Constructors Improved Readability 2024-01-27 22:45:49 +03:00
481a49bb52 chore: Engine Update 2024-01-27 21:51:12 +03:00
ffe4fc044e feat: Basic Pong Setting 2024-01-27 21:50:28 +03:00
91b05c97c4 Merge branch 'physics' 2024-01-27 20:39:25 +03:00
11074b89d5 chore: Engine Update 2023-11-30 17:54:40 +03:00
52 changed files with 1440 additions and 789 deletions

View File

@@ -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

View File

@@ -9,7 +9,10 @@
"kind": "build",
"isDefault": true
},
"label": "build"
"label": "build",
"options": {
"cwd": "${workspaceFolder}/Game"
}
}
]
}

2
Engine

Submodule Engine updated: 6a104d8abd...2cf6135063

View File

@@ -0,0 +1,8 @@
using Microsoft.Xna.Framework.Content;
namespace Pong.Behaviours;
public interface IMonoGameContentLoader
{
void LoadContent(ContentManager content);
}

View File

@@ -1,6 +1,6 @@
using Apos.Shapes;
namespace Pong.Behaviours;
namespace Syntriax.Engine.Core.Abstract;
public interface IDisplayableShape
{

View File

@@ -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);
}

View 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;
}
}

View 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;
}
}

View File

@@ -2,22 +2,28 @@ 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 CircleBehaviour(Circle circle) { this.CircleLocal = circle; }
public CircleBehaviour(Circle circle, float Thickness) { this.CircleLocal = circle; this.Thickness = Thickness; }
public CircleBehaviour(Circle circle, Color color) { this.CircleLocal = circle; Color = color; }
public CircleBehaviour(Circle circle, Color color, float Thickness) { this.CircleLocal = circle; this.Thickness = Thickness; Color = color; }
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; }
}

View File

@@ -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);

View 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));
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -1,13 +0,0 @@
using System;
using Microsoft.Xna.Framework.Input;
using Syntriax.Engine.Core;
namespace Pong.Behaviours;
public class MovementMouseBehaviour : BehaviourOverride
{
protected override void OnUpdate()
{
GameObject.Transform.Position = Mouse.GetState().Position.ToVector2D().ApplyDisplayScale();
}
}

View 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);
}
}

View 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(); }
}

View File

@@ -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);
}
}
}

View 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();
}
}

View File

@@ -1,25 +0,0 @@
using Syntriax.Engine.Core;
namespace Pong.Behaviours;
public class RotatableBehaviour : BehaviourOverride
{
private KeyboardInputsBehaviour? inputs = null;
protected override void OnFirstActiveFrame()
{
if (!BehaviourController.TryGetBehaviour(out inputs))
inputs = BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
}
protected override void OnUpdate()
{
if (inputs is null)
return;
if (inputs.IsPressed(Microsoft.Xna.Framework.Input.Keys.NumPad4))
Transform.Rotation += Time.Elapsed.Nanoseconds * 0.0025f;
if (inputs.IsPressed(Microsoft.Xna.Framework.Input.Keys.NumPad6))
Transform.Rotation -= Time.Elapsed.Nanoseconds * 0.0025f;
}
}

View File

@@ -3,6 +3,7 @@ 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;
@@ -13,11 +14,6 @@ public class ShapeAABBBehaviour : BehaviourOverride, IDisplayableShape
{
private IShapeCollider2D? shapeCollider = null;
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; }
public Color Color { get; set; } = Color.White;
public float Thickness { get; set; } = .5f;
public bool display = true;
@@ -44,4 +40,9 @@ public class ShapeAABBBehaviour : BehaviourOverride, IDisplayableShape
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; }
}

View File

@@ -2,28 +2,32 @@ 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 ShapeBehaviour(Shape Shape) { this.ShapeLocal = Shape; }
public ShapeBehaviour(Shape Shape, float Thickness) { this.ShapeLocal = Shape; this.Thickness = Thickness; }
public ShapeBehaviour(Shape Shape, Color color) { this.ShapeLocal = Shape; Color = color; }
public ShapeBehaviour(Shape Shape, Color color, float Thickness) { this.ShapeLocal = Shape; this.Thickness = Thickness; Color = color; }
public Color Color { get; set; } = Color.White;
public float Thickness { get; set; } = .5f;
public void Draw(ShapeBatch shapeBatch)
{
int count = ShapeWorld.Vertices.Count;
if (!IsActive)
return;
shapeBatch.BorderCircle(Transform.Position.ToDisplayVector2(), 5f, Color.DarkRed);
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; }
}

View 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;
}

View 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;
}

View 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();
}
}

View File

@@ -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

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

View 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>&#32;</Start>
<End>&#126;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

View File

@@ -21,6 +21,7 @@
</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>

View File

@@ -1,245 +0,0 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Pong.Behaviours;
using Apos.Shapes;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
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!;
private ShapeBatch _shapeBatch = 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,
GraphicsProfile = GraphicsProfile.HiDef
};
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);
_shapeBatch = new ShapeBatch(GraphicsDevice, Content);
// 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;
// IGameObject gameObjectCircle = gameManager.InstantiateGameObject<GameObject>();
// gameObjectCircle.Name = "Circle";
// gameObjectCircle.Transform.Position = new Vector2D(0f, -50f);
// gameObjectCircle.Transform.Scale = new Vector2D(25f, 25f);
// gameObjectCircle.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
// gameObjectCircle.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
// gameObjectCircle.BehaviourController.AddBehaviour<CircleBehaviour>(new Circle(Vector2D.Zero, 1f));
// engine.AddRigidBody(gameObjectCircle.BehaviourController.AddBehaviour<RigidBody2D>());
IGameObject gameObjectCircle2 = gameManager.InstantiateGameObject<GameObject>();
gameObjectCircle2.Name = "Circle2";
gameObjectCircle2.Transform.Position = new Vector2D(5f, 50f);
gameObjectCircle2.Transform.Scale = new Vector2D(25f, 25f);
gameObjectCircle2.BehaviourController.AddBehaviour<MovementMouseBehaviour>();
gameObjectCircle2.BehaviourController.AddBehaviour<CircleBehaviour>(new Circle(Vector2D.Zero, 1f));
engine.AddRigidBody(gameObjectCircle2.BehaviourController.AddBehaviour<RigidBody2D>());
IGameObject gameObjectDiamond = gameManager.InstantiateGameObject<GameObject>();
gameObjectDiamond.Name = "Diamond";
gameObjectDiamond.Transform.Position = new Vector2D(-150f, -150f);
gameObjectDiamond.Transform.Scale = new Vector2D(50f, 50f);
gameObjectDiamond.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
gameObjectDiamond.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
gameObjectDiamond.BehaviourController.AddBehaviour<RotatableBehaviour>();
gameObjectDiamond.BehaviourController.AddBehaviour<ShapeBehaviour>(new Shape([Vector2D.Up, Vector2D.One, Vector2D.Right, Vector2D.Down, -Vector2D.One, Vector2D.Left]));
gameObjectDiamond.BehaviourController.AddBehaviour<ShapeAABBBehaviour>();
engine.AddRigidBody(gameObjectDiamond.BehaviourController.AddBehaviour<RigidBody2D>());
IGameObject gameObjectBox = gameManager.InstantiateGameObject<GameObject>();
gameObjectBox.Name = "Box";
gameObjectBox.Transform.Position = new Vector2D(150f, -150f);
gameObjectBox.Transform.Scale = new Vector2D(100f, 100f);
gameObjectBox.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
gameObjectBox.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
gameObjectBox.BehaviourController.AddBehaviour<RotatableBehaviour>();
gameObjectBox.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Pentagon);
gameObjectBox.BehaviourController.AddBehaviour<ShapeAABBBehaviour>();
engine.AddRigidBody(gameObjectBox.BehaviourController.AddBehaviour<RigidBody2D>());
for (int i = 3; i < 10; i++)
{
IGameObject Test = gameManager.InstantiateGameObject<GameObject>();
Test.Name = i.ToString();
Test.Transform.Position = new Vector2D((i - 6) * 150, 0f);
Test.Transform.Scale = new Vector2D(75f, 75f);
Test.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
Test.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
Test.BehaviourController.AddBehaviour<RotatableBehaviour>();
Test.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.CreateNgon(i));
Test.BehaviourController.AddBehaviour<ShapeAABBBehaviour>();
RigidBody2D rigidBody = Test.BehaviourController.AddBehaviour<RigidBody2D>();
rigidBody.AngularVelocity = 90f;
engine.AddRigidBody(rigidBody);
}
// IGameObject gameObjectShape = gameManager.InstantiateGameObject<GameObject>();
// gameObjectShape.Name = "Shape";
// gameObjectShape.Transform.Position = new Vector2D(250f, 0f);
// gameObjectShape.Transform.Scale = new Vector2D(100f, 100f);
// gameObjectShape.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
// gameObjectShape.BehaviourController.AddBehaviour<MovementBoxBehaviour>(Keys.W, Keys.S, 268f, -268f, 400f);
// gameObjectShape.BehaviourController.AddBehaviour<RotatableBehaviour>();
// gameObjectShape.BehaviourController.AddBehaviour<ShapeBehaviour>(new Shape([Vector2D.Up, Vector2D.One, Vector2D.Right, Vector2D.Down, Vector2D.Zero, Vector2D.Left]));
// gameObjectShape.BehaviourController.AddBehaviour<ShapeAABBBehaviour>();
}
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)
foreach (var displayable in gameObject.BehaviourController.GetBehaviours<IDisplayable>())
displayable.Draw(_spriteBatch);
_spriteBatch.End();
_shapeBatch.Begin(cameraBehaviour.MatrixTransform);
foreach (IGameObject gameObject in gameManager)
foreach (var displayableShape in gameObject.BehaviourController.GetBehaviours<IDisplayableShape>())
displayableShape.Draw(_shapeBatch);
_shapeBatch.End();
base.Draw(gameTime);
}
}

200
Game/GamePong.cs Normal file
View 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);
}
}

View File

@@ -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);
}

View File

@@ -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; }
}

View File

@@ -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);
}
}
}

View File

@@ -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; }
}

View File

@@ -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!;
}

View 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();
}

View File

@@ -0,0 +1,6 @@
namespace Syntriax.Engine.Network.Abstract;
public interface INetworkClient : INetworkCommunicator
{
void Connect(string address, int port, string? password = null);
}

View 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);
}

View 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);
}

View 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;
}

View 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);
}

View 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);
}
}

View 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,
}

View 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);
}
}

View 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);
}
}

View 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) { }
}

View 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);
}
}

View File

@@ -1,3 +1,3 @@

using var game = new Pong.Game1();
using var game = new Pong.GamePong();
game.Run();