feat: game start countdown added
This commit is contained in:
		@@ -1,11 +1,15 @@
 | 
			
		||||
using Microsoft.Xna.Framework.Audio;
 | 
			
		||||
using Microsoft.Xna.Framework.Content;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Integration.MonoGame;
 | 
			
		||||
using Syntriax.Engine.Network;
 | 
			
		||||
using Syntriax.Engine.Physics2D;
 | 
			
		||||
using Syntriax.Engine.Systems.Tween;
 | 
			
		||||
 | 
			
		||||
namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public class Ball : Behaviour2D, IFirstFrameUpdate, IPhysicsUpdate, INetworkEntity,
 | 
			
		||||
public class Ball : Behaviour2D, IFirstFrameUpdate, ILoadContent, IPhysicsUpdate, INetworkEntity,
 | 
			
		||||
    IPacketListenerClient<Ball.BallUpdatePacket>,
 | 
			
		||||
    IPacketListenerClient<Ball.BallResetPacket>
 | 
			
		||||
{
 | 
			
		||||
@@ -19,6 +23,8 @@ public class Ball : Behaviour2D, IFirstFrameUpdate, IPhysicsUpdate, INetworkEnti
 | 
			
		||||
    private INetworkCommunicatorServer? networkServer = null;
 | 
			
		||||
    private ITween? networkTween = null;
 | 
			
		||||
 | 
			
		||||
    private SoundEffect? bounceSoundEffect = null;
 | 
			
		||||
 | 
			
		||||
    public void FirstActiveFrame()
 | 
			
		||||
    {
 | 
			
		||||
        BehaviourController.GetRequiredBehaviour<ICollider2D>().OnCollisionDetected.AddListener(OnCollisionDetected);
 | 
			
		||||
@@ -28,6 +34,11 @@ public class Ball : Behaviour2D, IFirstFrameUpdate, IPhysicsUpdate, INetworkEnti
 | 
			
		||||
        networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void LoadContent(ContentManager content)
 | 
			
		||||
    {
 | 
			
		||||
        bounceSoundEffect = content.Load<SoundEffect>("Audio/Bounce");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void LaunchBall(Vector2D launchDirection)
 | 
			
		||||
    {
 | 
			
		||||
        ResetBall();
 | 
			
		||||
@@ -74,6 +85,9 @@ public class Ball : Behaviour2D, IFirstFrameUpdate, IPhysicsUpdate, INetworkEnti
 | 
			
		||||
        else
 | 
			
		||||
            Transform.Position = packet.Position;
 | 
			
		||||
 | 
			
		||||
        if (RigidBody.Velocity.MagnitudeSquared >= .01f)
 | 
			
		||||
            bounceSoundEffect?.Play();
 | 
			
		||||
 | 
			
		||||
        RigidBody.Velocity = packet.Velocity;
 | 
			
		||||
        physicsEngine2D.StepIndividual(RigidBody, sender.Ping);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										96
									
								
								Shared/Behaviours/PongGameStarter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								Shared/Behaviours/PongGameStarter.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
using Microsoft.Xna.Framework.Audio;
 | 
			
		||||
using Microsoft.Xna.Framework.Content;
 | 
			
		||||
using Microsoft.Xna.Framework.Input;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Core.Debug;
 | 
			
		||||
using Syntriax.Engine.Core.Exceptions;
 | 
			
		||||
using Syntriax.Engine.Integration.MonoGame;
 | 
			
		||||
using Syntriax.Engine.Network;
 | 
			
		||||
using Syntriax.Engine.Systems.Input;
 | 
			
		||||
using Syntriax.Engine.Systems.Time;
 | 
			
		||||
using Syntriax.Engine.Systems.Tween;
 | 
			
		||||
 | 
			
		||||
namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public class PongGameStarter : Behaviour, INetworkEntity, IFirstFrameUpdate,
 | 
			
		||||
    IPacketListenerServer<PongGameStarter.RequestStartPacket>,
 | 
			
		||||
    IPacketListenerClient<PongGameStarter.RequestStartPacket>
 | 
			
		||||
{
 | 
			
		||||
    private const float START_COUNTDOWN = 3f;
 | 
			
		||||
 | 
			
		||||
    private INetworkCommunicatorServer? networkServer = null;
 | 
			
		||||
    private INetworkCommunicatorClient? networkClient = null;
 | 
			
		||||
    private ITweenManager? tweenManager = null;
 | 
			
		||||
    private PongManager pongManager = null!;
 | 
			
		||||
 | 
			
		||||
    private ILogger? logger = null;
 | 
			
		||||
 | 
			
		||||
    private Label? label = null;
 | 
			
		||||
    private TickerTimer timer = null!;
 | 
			
		||||
 | 
			
		||||
    public void FirstActiveFrame()
 | 
			
		||||
    {
 | 
			
		||||
        IButtonInputs<Keys>? buttonInputs = Universe.FindBehaviour<IButtonInputs<Keys>>();
 | 
			
		||||
        buttonInputs?.RegisterOnRelease(Keys.Space, (_, _1) => networkClient?.SendToServer(new RequestStartPacket()));
 | 
			
		||||
 | 
			
		||||
        networkClient = Universe.FindBehaviour<INetworkCommunicatorClient>();
 | 
			
		||||
        networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>();
 | 
			
		||||
        tweenManager = Universe.FindBehaviour<ITweenManager>();
 | 
			
		||||
        pongManager = BehaviourController.GetRequiredBehaviourInParent<PongManager>();
 | 
			
		||||
        label = BehaviourController.GetRequiredBehaviour<Label>();
 | 
			
		||||
        logger = Universe.FindBehaviour<ILogger>();
 | 
			
		||||
 | 
			
		||||
        if (!BehaviourController.TryGetBehaviour(out timer!))
 | 
			
		||||
        {
 | 
			
		||||
            timer = BehaviourController.AddBehaviour<TickerTimer>();
 | 
			
		||||
            timer.OnStarted.AddListener(OnCountdownStart);
 | 
			
		||||
            timer.OnTick.AddListener(DisplayCountdown);
 | 
			
		||||
            timer.OnStopped.AddListener(StartPong);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnCountdownStart(IReadOnlyTimer sender)
 | 
			
		||||
    {
 | 
			
		||||
        pongManager.Reset();
 | 
			
		||||
        DisplayCountdown(timer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void DisplayCountdown(ITicker sender)
 | 
			
		||||
    {
 | 
			
		||||
        if (label != null)
 | 
			
		||||
        {
 | 
			
		||||
            label.Text = $"{START_COUNTDOWN - timer.TickCounter}";
 | 
			
		||||
            label.Color = new ColorRGBA(255, 255, 255, 255);
 | 
			
		||||
            if (tweenManager is not null)
 | 
			
		||||
                label.Color.TweenColor(tweenManager, 1f, new ColorRGBA(255, 255, 255, 0), (x) => label.Color = x);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void StartPong(IReadOnlyTimer sender)
 | 
			
		||||
    {
 | 
			
		||||
        pongManager.Start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void IPacketListenerServer<RequestStartPacket>.OnServerPacketArrived(IConnection sender, RequestStartPacket packet)
 | 
			
		||||
    {
 | 
			
		||||
        logger?.Log(this, $"{sender} requested start");
 | 
			
		||||
 | 
			
		||||
        if (pongManager.IsGameInProgress)
 | 
			
		||||
        {
 | 
			
		||||
            logger?.Log(this, $"The game is already in progress");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        timer.Start(START_COUNTDOWN);
 | 
			
		||||
        networkServer?.SendToAll(packet);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void IPacketListenerClient<RequestStartPacket>.OnClientPacketArrived(IConnection sender, RequestStartPacket packet)
 | 
			
		||||
    {
 | 
			
		||||
        logger?.Log(this, $"Server is starting the game");
 | 
			
		||||
        timer.Start(START_COUNTDOWN - sender.Ping);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class RequestStartPacket : INetworkPacket;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +1,12 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Xna.Framework.Input;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Core.Debug;
 | 
			
		||||
using Syntriax.Engine.Network;
 | 
			
		||||
using Syntriax.Engine.Systems.Input;
 | 
			
		||||
 | 
			
		||||
namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate,
 | 
			
		||||
    IPacketListenerServer<PongManager.RequestStartPacket>,
 | 
			
		||||
    IPacketListenerClient<PongManager.ScorePacket>
 | 
			
		||||
{
 | 
			
		||||
    public Action<PongManager>? OnReset { get; set; } = null;
 | 
			
		||||
@@ -18,9 +14,7 @@ public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate,
 | 
			
		||||
    public Action<PongManager>? OnScoreUpdated { get; set; } = null;
 | 
			
		||||
 | 
			
		||||
    private Random random = new();
 | 
			
		||||
    private Ball ball = null!;
 | 
			
		||||
 | 
			
		||||
    private INetworkCommunicatorClient? networkClient = null!;
 | 
			
		||||
    private INetworkCommunicatorServer? networkServer = null;
 | 
			
		||||
    private ILogger? logger = null;
 | 
			
		||||
 | 
			
		||||
@@ -29,17 +23,16 @@ public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate,
 | 
			
		||||
    public int ScoreSum => ScoreLeft + ScoreRight;
 | 
			
		||||
 | 
			
		||||
    public int WinScore { get; } = 5;
 | 
			
		||||
    public Ball Ball { get; private set; } = null!;
 | 
			
		||||
 | 
			
		||||
    public bool IsGameInProgress { get; private set; } = false;
 | 
			
		||||
 | 
			
		||||
    public PongManager() => WinScore = 5;
 | 
			
		||||
    public PongManager(int winScore) => WinScore = winScore;
 | 
			
		||||
 | 
			
		||||
    public void FirstActiveFrame()
 | 
			
		||||
    {
 | 
			
		||||
        IButtonInputs<Keys>? buttonInputs = Universe.FindBehaviour<IButtonInputs<Keys>>();
 | 
			
		||||
        buttonInputs?.RegisterOnRelease(Keys.Space, (_, _1) => networkClient?.SendToServer(new RequestStartPacket()));
 | 
			
		||||
 | 
			
		||||
        ball = Universe.FindRequiredBehaviour<Ball>();
 | 
			
		||||
        networkClient = Universe.FindBehaviour<INetworkCommunicatorClient>();
 | 
			
		||||
        Ball = Universe.FindRequiredBehaviour<Ball>();
 | 
			
		||||
        networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>();
 | 
			
		||||
        logger = Universe.FindBehaviour<ILogger>();
 | 
			
		||||
    }
 | 
			
		||||
@@ -60,18 +53,34 @@ public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate,
 | 
			
		||||
        PostScoreUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Start()
 | 
			
		||||
    {
 | 
			
		||||
        if (networkServer is null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (Ball.RigidBody.Velocity.MagnitudeSquared > 0.01f)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        Reset();
 | 
			
		||||
        IsGameInProgress = true;
 | 
			
		||||
        PostScoreUpdate();
 | 
			
		||||
        logger?.Log(this, $"Game started");
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Reset()
 | 
			
		||||
    {
 | 
			
		||||
        ScoreLeft = ScoreRight = 0;
 | 
			
		||||
        IsGameInProgress = false;
 | 
			
		||||
 | 
			
		||||
        PostScoreUpdate();
 | 
			
		||||
        Ball.ResetBall();
 | 
			
		||||
 | 
			
		||||
        OnReset?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void PostScoreUpdate()
 | 
			
		||||
    {
 | 
			
		||||
        ball.ResetBall();
 | 
			
		||||
        Ball.ResetBall();
 | 
			
		||||
 | 
			
		||||
        if (networkServer is not null)
 | 
			
		||||
        {
 | 
			
		||||
@@ -82,12 +91,13 @@ public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate,
 | 
			
		||||
        int halfwayScore = (int)(WinScore * .5f);
 | 
			
		||||
        if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore)
 | 
			
		||||
        {
 | 
			
		||||
            IsGameInProgress = false;
 | 
			
		||||
            OnFinished?.Invoke(this);
 | 
			
		||||
            logger?.Log(this, $"Game finished");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ball.LaunchBall(GetBallLaunchDirection());
 | 
			
		||||
        Ball.LaunchBall(GetBallLaunchDirection());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Vector2D GetBallLaunchDirection()
 | 
			
		||||
@@ -107,18 +117,6 @@ public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate,
 | 
			
		||||
        logger?.Log(this, $"Client score update packet arrived: {packet.Left} - {packet.Right}");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void IPacketListenerServer<RequestStartPacket>.OnServerPacketArrived(IConnection sender, RequestStartPacket packet)
 | 
			
		||||
    {
 | 
			
		||||
        logger?.Log(this, $"{sender} requested start");
 | 
			
		||||
        if (ball.RigidBody.Velocity.MagnitudeSquared > 0.01f)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Reset();
 | 
			
		||||
        ball.LaunchBall(GetBallLaunchDirection());
 | 
			
		||||
        logger?.Log(this, $"Game started");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class RequestStartPacket : INetworkPacket;
 | 
			
		||||
    private class ScorePacket : INetworkPacket
 | 
			
		||||
    {
 | 
			
		||||
        public int Left { get; set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,11 @@ public static class PongUniverse
 | 
			
		||||
        PongManager pongManager = universe.InstantiateUniverseObject().SetUniverseObject("Pong Game Manager")
 | 
			
		||||
            .BehaviourController.AddBehaviour<PongManager>(5);
 | 
			
		||||
 | 
			
		||||
        universe.InstantiateUniverseObject().SetUniverseObject("Pong Game Starter", parent: pongManager.UniverseObject)
 | 
			
		||||
            .BehaviourController.AddBehaviour<PongGameStarter>()
 | 
			
		||||
            .BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(-24, 250f), scale: Vector2D.One * .5f)
 | 
			
		||||
            .BehaviourController.AddBehaviour<Label>();
 | 
			
		||||
 | 
			
		||||
        ////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
        universe.InstantiateUniverseObject().SetUniverseObject("Ball")
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user