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 : BehaviourOverride, 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("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; communicator.RegisterEntityListener(this, OnDataReceived); if (GameObject.GameManager.TryFindBehaviour(out PongManagerBehaviour? pongManager)) { pongManager.OnReset += ResetBall; pongManager.OnScored += ResetBall; pongManager.OnFinished += DisableBall; } } private void OnDataReceived(NetPacketReader reader) { rigidBody.Transform.Position = reader.GetVector2D(); rigidBody.Velocity = reader.GetVector2D(); } 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.GetEntityWriter(this); dataWriter.Put(rigidBody.Transform.Position); dataWriter.Put(rigidBody.Velocity); server.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered); } public BallBehaviour() { } public BallBehaviour(Vector2D startDirection, float speed) { StartDirection = Vector2D.Normalize(startDirection); Speed = speed; } }