Переглянути джерело

[NetworkStateManagement] Make rendering scale.

Dominique Louis 3 тижнів тому
батько
коміт
6162d7e901

+ 3 - 22
NetworkStateManagement/Core/MessageDisplayComponent.cs

@@ -19,7 +19,6 @@ namespace NetworkStateManagement
     /// </summary>
     class MessageDisplayComponent : DrawableGameComponent, IMessageDisplay
     {
-
         SpriteBatch spriteBatch;
         SpriteFont font;
 
@@ -33,9 +32,7 @@ namespace NetworkStateManagement
         static readonly TimeSpan fadeInTime = TimeSpan.FromSeconds(0.25);
         static readonly TimeSpan showTime = TimeSpan.FromSeconds(5);
         static readonly TimeSpan fadeOutTime = TimeSpan.FromSeconds(0.5);
-
-
-
+        NetworkStateManagementGame game;
 
         /// <summary>
         /// Constructs a new message display component.
@@ -43,11 +40,11 @@ namespace NetworkStateManagement
         public MessageDisplayComponent(Game game)
             : base(game)
         {
+            this.game = game as NetworkStateManagementGame;
             // Register ourselves to implement the IMessageDisplay service.
             game.Services.AddService(typeof(IMessageDisplay), this);
         }
 
-
         /// <summary>
         /// Load graphics content for the message display.
         /// </summary>
@@ -58,10 +55,6 @@ namespace NetworkStateManagement
             font = Game.Content.Load<SpriteFont>("menufont");
         }
 
-
-
-
-
         /// <summary>
         /// Updates the message display component.
         /// </summary>
@@ -106,7 +99,6 @@ namespace NetworkStateManagement
             }
         }
 
-
         /// <summary>
         /// Draws the message display component.
         /// </summary>
@@ -120,7 +112,7 @@ namespace NetworkStateManagement
 
                 Vector2 position = new Vector2(GraphicsDevice.Viewport.Width - 100, 0);
 
-                spriteBatch.Begin();
+                spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, game.ScreenManager.GlobalTransformation);
 
                 // Draw each message in turn.
                 foreach (NotificationMessage message in messages)
@@ -166,10 +158,6 @@ namespace NetworkStateManagement
             }
         }
 
-
-
-
-
         /// <summary>
         /// Shows a new notification message.
         /// </summary>
@@ -185,10 +173,6 @@ namespace NetworkStateManagement
             }
         }
 
-
-
-
-
         /// <summary>
         /// Helper class stores the position and text of a single notification message.
         /// </summary>
@@ -198,7 +182,6 @@ namespace NetworkStateManagement
             public float Position;
             public TimeSpan Age;
 
-
             public NotificationMessage(string text, float position)
             {
                 Text = text;
@@ -206,7 +189,5 @@ namespace NetworkStateManagement
                 Age = TimeSpan.Zero;
             }
         }
-
-
     }
 }

+ 7 - 10
NetworkStateManagement/Core/NetworkStateManagementGame.cs

@@ -23,12 +23,9 @@ namespace NetworkStateManagement
 	/// </summary>
 	public class NetworkStateManagementGame : Game
 	{
-		const int screenWidth = 480;
-		const int screenHeight = 540;
-
 		GraphicsDeviceManager graphicsDeviceManager;
 		ScreenManager screenManager;
-
+		public ScreenManager ScreenManager { get => screenManager; set => screenManager = value; }
 
 		// By preloading any assets used by UI rendering, we avoid framerate glitches
 		// when they suddenly need to be loaded in the middle of a menu transition.
@@ -42,16 +39,16 @@ namespace NetworkStateManagement
 			"chat_mute",
 		};
 
-		/// <summary>
-		/// The main game constructor.
-		/// </summary>		
-		public NetworkStateManagementGame()
+        /// <summary>
+        /// The main game constructor.
+        /// </summary>		
+        public NetworkStateManagementGame()
 		{
 			Content.RootDirectory = "Content";
 
 			graphicsDeviceManager = new GraphicsDeviceManager(this);
-			graphicsDeviceManager.PreferredBackBufferWidth = screenWidth;
-			graphicsDeviceManager.PreferredBackBufferHeight = screenHeight;
+			graphicsDeviceManager.PreferredBackBufferWidth = ScreenManager.BASE_BUFFER_WIDTH;
+			graphicsDeviceManager.PreferredBackBufferHeight = ScreenManager.BASE_BUFFER_HEIGHT;
 
 			if (UIUtility.IsMobile)
 			{

+ 1 - 1
NetworkStateManagement/Core/Networking/LobbyScreen.cs

@@ -180,7 +180,7 @@ namespace NetworkStateManagement
 			else
 				position.X += transitionOffset * 512;
 
-			spriteBatch.Begin();
+			spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, ScreenManager.GlobalTransformation);
 
 			// Draw all the gamers in the session.
 			int gamerCount = 0;

+ 1 - 1
NetworkStateManagement/Core/Networking/NetworkBusyScreen.cs

@@ -173,7 +173,7 @@ namespace NetworkStateManagement
 			// Fade the popup alpha during transitions.
 			Color color = Color.White * TransitionAlpha;
 
-			spriteBatch.Begin();
+			spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, ScreenManager.GlobalTransformation);
 
 			// Draw the background rectangle.
 			spriteBatch.Draw(gradientTexture, backgroundRectangle, color);

+ 9 - 30
NetworkStateManagement/Core/ScreenManager/GameScreen.cs

@@ -34,8 +34,6 @@ namespace NetworkStateManagement
 	/// </summary>
 	public abstract class GameScreen
 	{
-
-
 		/// <summary>
 		/// Normally when one screen is brought up over the top of another,
 		/// the first screen will transition off to make room for the new
@@ -51,7 +49,6 @@ namespace NetworkStateManagement
 
 		bool isPopup = false;
 
-
 		/// <summary>
 		/// Indicates how long the screen takes to
 		/// transition on when it is activated.
@@ -64,7 +61,6 @@ namespace NetworkStateManagement
 
 		TimeSpan transitionOnTime = TimeSpan.Zero;
 
-
 		/// <summary>
 		/// Indicates how long the screen takes to
 		/// transition off when it is deactivated.
@@ -77,7 +73,6 @@ namespace NetworkStateManagement
 
 		TimeSpan transitionOffTime = TimeSpan.Zero;
 
-
 		/// <summary>
 		/// Gets the current position of the screen transition, ranging
 		/// from zero (fully active, no transition) to one (transitioned
@@ -91,7 +86,6 @@ namespace NetworkStateManagement
 
 		float transitionPosition = 1;
 
-
 		/// <summary>
 		/// Gets the current alpha of the screen transition, ranging
 		/// from 1 (fully active, no transition) to 0 (transitioned
@@ -102,7 +96,6 @@ namespace NetworkStateManagement
 			get { return 1f - TransitionPosition; }
 		}
 
-
 		/// <summary>
 		/// Gets the current screen transition state.
 		/// </summary>
@@ -114,7 +107,6 @@ namespace NetworkStateManagement
 
 		ScreenState screenState = ScreenState.TransitionOn;
 
-
 		/// <summary>
 		/// There are two possible reasons why a screen might be transitioning
 		/// off. It could be temporarily going away to make room for another
@@ -131,7 +123,6 @@ namespace NetworkStateManagement
 
 		bool isExiting = false;
 
-
 		/// <summary>
 		/// Checks whether this screen is active and can respond to user input.
 		/// </summary>
@@ -147,7 +138,6 @@ namespace NetworkStateManagement
 
 		bool otherScreenHasFocus;
 
-
 		/// <summary>
 		/// Gets the manager that this screen belongs to.
 		/// </summary>
@@ -159,7 +149,6 @@ namespace NetworkStateManagement
 
 		ScreenManager screenManager;
 
-
 		/// <summary>
 		/// Gets the index of the player who is currently controlling this screen,
 		/// or null if it is accepting input from any player. This is used to lock
@@ -176,7 +165,6 @@ namespace NetworkStateManagement
 
 		PlayerIndex? controllingPlayer;
 
-
 		/// <summary>
 		/// Gets the gestures the screen is interested in. Screens should be as specific
 		/// as possible with gestures to increase the accuracy of the gesture engine.
@@ -203,18 +191,14 @@ namespace NetworkStateManagement
 
 		GestureType enabledGestures = GestureType.None;
 
-
-
-
-
 		/// <summary>
 		/// Load graphics content for the screen.
 		/// </summary>
 		public virtual void LoadContent()
 		{
+			ScreenManager.ScalePresentationArea();
 		}
 
-
 		/// <summary>
 		/// Unload content for the screen.
 		/// </summary>
@@ -222,10 +206,6 @@ namespace NetworkStateManagement
 		{
 		}
 
-
-
-
-
 		/// <summary>
 		/// Allows the screen to run logic, such as updating the transition position.
 		/// Unlike HandleInput, this method is called regardless of whether the screen
@@ -275,8 +255,15 @@ namespace NetworkStateManagement
 					screenState = ScreenState.Active;
 				}
 			}
-		}
 
+			// Check if the back buffer size has changed (e.g., window resize).
+			if (ScreenManager.BackbufferHeight != ScreenManager.GraphicsDevice.PresentationParameters.BackBufferHeight
+				|| ScreenManager.BackbufferWidth != ScreenManager.GraphicsDevice.PresentationParameters.BackBufferWidth)
+			{
+				// Adjust the presentation area to match the new back buffer size.
+				ScreenManager.ScalePresentationArea();
+			}
+		}
 
 		/// <summary>
 		/// Helper for updating the screen transition position.
@@ -307,7 +294,6 @@ namespace NetworkStateManagement
 			return true;
 		}
 
-
 		/// <summary>
 		/// Allows the screen to handle user input. Unlike Update, this method
 		/// is only called when the screen is active, and not when some other
@@ -317,7 +303,6 @@ namespace NetworkStateManagement
 		{
 		}
 
-
 		/// <summary>
 		/// This is called when the screen should draw itself.
 		/// </summary>
@@ -325,10 +310,6 @@ namespace NetworkStateManagement
 		{
 		}
 
-
-
-
-
 		/// <summary>
 		/// Tells the screen to go away. Unlike ScreenManager.RemoveScreen, which
 		/// instantly kills the screen, this method respects the transition timings
@@ -347,7 +328,5 @@ namespace NetworkStateManagement
 				isExiting = true;
 			}
 		}
-
-
 	}
 }

+ 15 - 16
NetworkStateManagement/Core/ScreenManager/InputState.cs

@@ -20,7 +20,6 @@ namespace NetworkStateManagement
     /// </summary>
     public class InputState
     {
-
         public const int MaxInputs = 4;
 
         public readonly KeyboardState[] CurrentKeyboardStates;
@@ -35,14 +34,18 @@ namespace NetworkStateManagement
 
         public readonly List<GestureSample> Gestures = new List<GestureSample>();
 
-
-
+        Matrix inputTransformation;
+        readonly float baseBufferWidth;
+        readonly float baseBufferHeight;
 
         /// <summary>
         /// Constructs a new input state.
         /// </summary>
-        public InputState()
+        public InputState(float baseBufferWidth, float baseBufferHeight)
         {
+            this.baseBufferWidth = baseBufferWidth;
+            this.baseBufferHeight = baseBufferHeight;
+
             CurrentKeyboardStates = new KeyboardState[MaxInputs];
             CurrentGamePadStates = new GamePadState[MaxInputs];
 
@@ -52,10 +55,6 @@ namespace NetworkStateManagement
             GamePadWasConnected = new bool[MaxInputs];
         }
 
-
-
-
-
         /// <summary>
         /// Reads the latest state of the keyboard and gamepad.
         /// </summary>
@@ -86,7 +85,6 @@ namespace NetworkStateManagement
             }
         }
 
-
         /// <summary>
         /// Helper for checking if a key was newly pressed during this update. The
         /// controllingPlayer parameter specifies which player to read input for.
@@ -116,7 +114,6 @@ namespace NetworkStateManagement
             }
         }
 
-
         /// <summary>
         /// Helper for checking if a button was newly pressed during this update.
         /// The controllingPlayer parameter specifies which player to read input for.
@@ -146,7 +143,6 @@ namespace NetworkStateManagement
             }
         }
 
-
         /// <summary>
         /// Checks for a "menu select" input action.
         /// The controllingPlayer parameter specifies which player to read input for.
@@ -162,7 +158,6 @@ namespace NetworkStateManagement
                    IsNewButtonPress(Buttons.Start, controllingPlayer, out playerIndex);
         }
 
-
         /// <summary>
         /// Checks for a "menu cancel" input action.
         /// The controllingPlayer parameter specifies which player to read input for.
@@ -177,7 +172,6 @@ namespace NetworkStateManagement
                    IsNewButtonPress(Buttons.Back, controllingPlayer, out playerIndex);
         }
 
-
         /// <summary>
         /// Checks for a "menu up" input action.
         /// The controllingPlayer parameter specifies which player to read
@@ -192,7 +186,6 @@ namespace NetworkStateManagement
                    IsNewButtonPress(Buttons.LeftThumbstickUp, controllingPlayer, out playerIndex);
         }
 
-
         /// <summary>
         /// Checks for a "menu down" input action.
         /// The controllingPlayer parameter specifies which player to read
@@ -207,7 +200,6 @@ namespace NetworkStateManagement
                    IsNewButtonPress(Buttons.LeftThumbstickDown, controllingPlayer, out playerIndex);
         }
 
-
         /// <summary>
         /// Checks for a "pause the game" input action.
         /// The controllingPlayer parameter specifies which player to read
@@ -222,6 +214,13 @@ namespace NetworkStateManagement
                    IsNewButtonPress(Buttons.Start, controllingPlayer, out playerIndex);
         }
 
-
+        /// <summary>
+        /// Updates the matrix used to transform input coordinates.
+        /// </summary>
+        /// <param name="inputTransformation">The transformation matrix to apply.</param>
+        public void UpdateInputTransformation(Matrix inputTransformation)
+        {
+            this.inputTransformation = inputTransformation;
+        }
     }
 }

+ 77 - 5
NetworkStateManagement/Core/ScreenManager/ScreenManager.cs

@@ -23,11 +23,11 @@ namespace NetworkStateManagement
     /// </summary>
     public class ScreenManager : DrawableGameComponent
     {
-
         List<GameScreen> screens = new List<GameScreen>();
         List<GameScreen> screensToUpdate = new List<GameScreen>();
 
-        InputState input = new InputState();
+        InputState inputState = new InputState(BASE_BUFFER_WIDTH, BASE_BUFFER_HEIGHT);
+        public InputState InputState => inputState;
 
         SpriteBatch spriteBatch;
         SpriteFont font;
@@ -37,8 +37,24 @@ namespace NetworkStateManagement
 
         bool traceEnabled;
 
+        internal const int BASE_BUFFER_WIDTH = 800;
+        internal const int BASE_BUFFER_HEIGHT = 480;
+
+        private int backbufferWidth;
+        /// <summary>Gets or sets the current backbuffer width.</summary>
+        public int BackbufferWidth { get => backbufferWidth; set => backbufferWidth = value; }
+
+        private int backbufferHeight;
+        /// <summary>Gets or sets the current backbuffer height.</summary>
+        public int BackbufferHeight { get => backbufferHeight; set => backbufferHeight = value; }
 
+        private Vector2 baseScreenSize = new Vector2(BASE_BUFFER_WIDTH, BASE_BUFFER_HEIGHT);
+        /// <summary>Gets or sets the base screen size used for scaling calculations.</summary>
+        public Vector2 BaseScreenSize { get => baseScreenSize; set => baseScreenSize = value; }
 
+        private Matrix globalTransformation;
+        /// <summary>Gets or sets the global transformation matrix for scaling and positioning.</summary>
+        public Matrix GlobalTransformation { get => globalTransformation; set => globalTransformation = value; }
 
         /// <summary>
         /// A default SpriteBatch shared by all the screens. This saves
@@ -140,7 +156,7 @@ namespace NetworkStateManagement
         public override void Update(GameTime gameTime)
         {
             // Read the keyboard and gamepad.
-            input.Update();
+            inputState.Update();
 
             // Make a copy of the master screen list, to avoid confusion if
             // the process of updating one screen adds or removes others.
@@ -170,7 +186,7 @@ namespace NetworkStateManagement
                     // give it a chance to handle input.
                     if (!otherScreenHasFocus)
                     {
-                        screen.HandleInput(input);
+                        screen.HandleInput(inputState);
 
                         otherScreenHasFocus = true;
                     }
@@ -287,7 +303,7 @@ namespace NetworkStateManagement
         {
             Viewport viewport = GraphicsDevice.Viewport;
 
-            spriteBatch.Begin();
+            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, GlobalTransformation);
 
             spriteBatch.Draw(blankTexture,
                              new Rectangle(0, 0, viewport.Width, viewport.Height),
@@ -296,6 +312,62 @@ namespace NetworkStateManagement
             spriteBatch.End();
         }
 
+        /// <summary>
+        /// Scales the game presentation area to match the screen's aspect ratio.
+        /// </summary>
+        public void ScalePresentationArea()
+        {
+            // Validate parameters before calculation
+            if (GraphicsDevice == null || baseScreenSize.X <= 0 || baseScreenSize.Y <= 0)
+            {
+                throw new InvalidOperationException("Invalid graphics configuration");
+            }
+
+            // Fetch screen dimensions
+            backbufferWidth = GraphicsDevice.PresentationParameters.BackBufferWidth;
+            backbufferHeight = GraphicsDevice.PresentationParameters.BackBufferHeight;
+
+            // Prevent division by zero
+            if (backbufferHeight == 0 || baseScreenSize.Y == 0)
+            {
+                return;
+            }
+
+            // Calculate aspect ratios
+            float baseAspectRatio = baseScreenSize.X / baseScreenSize.Y;
+            float screenAspectRatio = backbufferWidth / (float)backbufferHeight;
+
+            // Determine uniform scaling factor
+            float scalingFactor;
+            float horizontalOffset = 0;
+            float verticalOffset = 0;
+
+            if (screenAspectRatio > baseAspectRatio)
+            {
+                // Wider screen: scale by height
+                scalingFactor = backbufferHeight / baseScreenSize.Y;
+
+                // Centre things horizontally.
+                horizontalOffset = (backbufferWidth - baseScreenSize.X * scalingFactor) / 2;
+            }
+            else
+            {
+                // Taller screen: scale by width
+                scalingFactor = backbufferWidth / baseScreenSize.X;
+
+                // Centre things vertically.
+                verticalOffset = (backbufferHeight - baseScreenSize.Y * scalingFactor) / 2;
+            }
+
+            // Update the transformation matrix
+            globalTransformation = Matrix.CreateScale(scalingFactor) *
+                                   Matrix.CreateTranslation(horizontalOffset, verticalOffset, 0);
+
+            // Update the inputTransformation with the Inverted globalTransformation
+            inputState.UpdateInputTransformation(Matrix.Invert(globalTransformation));
 
+            // Debug info
+            Debug.WriteLine($"Screen Size - Width[{backbufferWidth}] Height[{backbufferHeight}] ScalingFactor[{scalingFactor}]");
+        }
     }
 }

+ 1 - 1
NetworkStateManagement/Core/Screens/BackgroundScreen.cs

@@ -87,7 +87,7 @@ namespace NetworkStateManagement
             Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
             Rectangle fullscreen = new Rectangle(0, 0, viewport.Width, viewport.Height);
 
-            spriteBatch.Begin();
+            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, ScreenManager.GlobalTransformation);
 
             spriteBatch.Draw(backgroundTexture, fullscreen,
                              new Color(TransitionAlpha, TransitionAlpha, TransitionAlpha));

+ 1 - 1
NetworkStateManagement/Core/Screens/GameplayScreen.cs

@@ -237,7 +237,7 @@ namespace NetworkStateManagement
             // Our player and enemy are both actually just text strings.
             SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
 
-            spriteBatch.Begin();
+            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, ScreenManager.GlobalTransformation);
 
             spriteBatch.DrawString(gameFont, "// TODO", playerPosition, Color.Green);
 

+ 1 - 5
NetworkStateManagement/Core/Screens/LoadingScreen.cs

@@ -31,7 +31,6 @@ namespace NetworkStateManagement
 	/// </summary>
 	class LoadingScreen : GameScreen
 	{
-
 		bool loadingIsSlow;
 		bool otherScreensAreGone;
 		GameScreen[] screensToLoad;
@@ -43,9 +42,6 @@ namespace NetworkStateManagement
 		GameTime loadStartTime;
 		TimeSpan loadAnimationTimer;
 
-
-
-
 		/// <summary>
 		/// The constructor is private: loading screens should
 		/// be activated via the static Load method instead.
@@ -194,7 +190,7 @@ namespace NetworkStateManagement
 				message += new string('.', dotCount);
 
 				// Draw the text.
-				spriteBatch.Begin();
+				spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, ScreenManager.GlobalTransformation);
 				spriteBatch.DrawString(font, message, textPosition, color);
 				spriteBatch.End();
 			}

+ 1 - 1
NetworkStateManagement/Core/Screens/MenuScreen.cs

@@ -194,7 +194,7 @@ namespace NetworkStateManagement
             SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
             SpriteFont font = ScreenManager.Font;
 
-            spriteBatch.Begin();
+            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, ScreenManager.GlobalTransformation);
 
             // Draw each menu entry in turn.
             for (int i = 0; i < menuEntries.Count; i++)

+ 1 - 1
NetworkStateManagement/Core/Screens/MessageBoxScreen.cs

@@ -137,7 +137,7 @@ namespace NetworkStateManagement
             // Fade the popup alpha during transitions.
             Color color = Color.White * TransitionAlpha;
 
-            spriteBatch.Begin();
+            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, ScreenManager.GlobalTransformation);
 
             // Draw the background rectangle.
             spriteBatch.Draw(gradientTexture, backgroundRectangle, color);

+ 0 - 0
NetworkStateManagement/launch.json


+ 0 - 0
NetworkStateManagement/tasks.json