My entire codebase is structured this way, but I’m facing a major issue with UI scaling.
When I use _screenScaleMatrix, both the UI and text become blurry. However, if I remove it, the UI appears too small on higher-resolution screens. Additionally, when I resize the window, the UI stretches and fills the entire screen, which is not the intended behavior.
I’ve been struggling to solve this problem for almost a week and haven’t found a proper solution yet.
// c_main
public class Main : Game
{
private readonly GraphicsDeviceManager _graphicsDeviceManager;
private SpriteBatch _spriteBatch;
private DebugConsole _debugConsole;
private DebugUI _debugUI;
public static int _resolutionWidth = 1280; // desired width
public static int _resolutionHeight = 720; // desired height
public static int _virtualWidth = 1280; // width we render
public static int _virtualHeight = 720; // height we render
private bool _isResizing;
private Matrix _screenScaleMatrix;
private Viewport _viewport;
// constructor f_main
public Main()
{
Globals.Main = this;
_graphicsDeviceManager = new GraphicsDeviceManager(this)
{
PreferredBackBufferWidth = _resolutionWidth,
PreferredBackBufferHeight = _resolutionHeight
};
_graphicsDeviceManager.ApplyChanges();
Content.RootDirectory = "Content";
IsMouseVisible = true;
Window.Title = Config.GameWindowTitle;
Window.AllowUserResizing = true;
Window.ClientSizeChanged += OnClientSizeChanged;
Globals.SceneManager = new();
}
// initialize f_main
protected override void Initialize()
{
UpdateScreenScaleMatrix();
// globals
Globals.GraphicsDevice = GraphicsDevice;
Globals.GraphicsDeviceManager = _graphicsDeviceManager;
Globals.ContentManager = Content;
Globals.InputManager = new InputManager();
Globals.AudioManager = new AudioManager();
_debugUI = new DebugUI();
SaveSystem.Initialize();
SaveSystem.LoadValues();
base.Initialize();
}
// load content f_main
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
Globals.SpriteBatch = _spriteBatch;
if (Config.IsDebugMode)
_debugConsole = new DebugConsole();
Globals.MainFontS = Content.Load<SpriteFont>(AssetPath.Font.MainS);
Globals.MainFontM = Content.Load<SpriteFont>(AssetPath.Font.MainM);
Globals.MainFontL = Content.Load<SpriteFont>(AssetPath.Font.MainL);
Globals.SceneManager.InitTransitions();
Globals.SceneManager.PushScene(new GameplayScene());
NotificationUI.Initialize();
}
// update f_main
protected override void Update(GameTime gameTime)
{
Globals.InputManager.Update();
Globals.SceneManager.Update(gameTime);
Globals.AudioManager.Update();
NotificationUI.Update(gameTime);
_debugConsole?.Update();
if (Config.ShowDebugUI)
_debugUI?.Update(gameTime);
base.Update(gameTime);
}
// draw f_main
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
GraphicsDevice.Viewport = _viewport;
_spriteBatch.Begin(samplerState: SamplerState.PointClamp, transformMatrix: _screenScaleMatrix);
Globals.SceneManager.Draw(gameTime);
_spriteBatch.End();
_spriteBatch.Begin(samplerState: SamplerState.PointClamp);
NotificationUI.Draw();
_debugConsole?.Draw();
_debugUI?.Draw(gameTime);
_spriteBatch.End();
base.Draw(gameTime);
}
// unload content f_main
protected override void UnloadContent()
{
Globals.AudioManager.Dispose();
Globals.ContentManager.Dispose();
AssetManager.Dispose();
base.UnloadContent();
}
// update screen scale matrix f_main
public void UpdateScreenScaleMatrix()
{
// determine the size of actual screen
float screenWidth = GraphicsDevice.PresentationParameters.BackBufferWidth;
float screenHeight = GraphicsDevice.PresentationParameters.BackBufferHeight;
// calculate the virtual resolution based on the current screen resolution compared to our intended resolution
if (screenWidth / _resolutionWidth > screenHeight / _resolutionHeight)
{
float aspect = screenHeight / _resolutionHeight;
_virtualWidth = (int)(aspect * _resolutionWidth);
_virtualHeight = (int)screenHeight;
}
else
{
float aspect = screenWidth / _resolutionWidth;
_virtualWidth = (int)screenWidth;
_virtualHeight = (int)(aspect * _resolutionHeight);
}
_screenScaleMatrix = Matrix.CreateScale(_virtualWidth / (float)_resolutionWidth);
_viewport = new Viewport
{
X = (int)(screenWidth / 2 - _virtualWidth / 2),
Y = (int)(screenHeight / 2 - _virtualHeight / 2),
Width = _virtualWidth,
Height = _virtualHeight,
MinDepth = 0,
MaxDepth = 1
};
}
// screen on client size changed f_main
private void OnClientSizeChanged(object sender, EventArgs e)
{
if (!_isResizing && Window.ClientBounds.Width > 0 && Window.ClientBounds.Height > 0)
{
_isResizing = true;
UpdateScreenScaleMatrix();
_isResizing = false;
}
}
}