You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MonoGame中RenderTarget与变换矩阵的Y轴偏移问题及适配优化

修复Y轴偏移问题及优化多分辨率适配方案

一、当前代码的核心问题

  • RenderTarget尺寸错误:你当前的RenderTarget是按实际屏幕分辨率创建的,违背了虚拟分辨率适配的初衷——应该固定以虚拟分辨率(1920×1080)创建RenderTarget,确保所有UI元素在统一的虚拟画布上绘制。
  • 缩放与偏移计算逻辑矛盾:你用scaleX同时缩放X/Y轴来保持宽高比,但计算偏移时错误混用scaleX和scaleY,且绘制RenderTarget时使用虚拟分辨率尺寸(1920×1080),而非缩放后的实际尺寸。

二、修复Y轴偏移的具体代码调整

1. 修正RenderTarget初始化

将RenderTarget固定为虚拟分辨率:

// 初始化代码修改
ScreenWidths = new int[] { 1920, 960, 1366, 1280, 1280, 1366 };
ScreenHeights = new int[] { 1080, 540, 768, 1024, 720, 680 };

// 固定用虚拟分辨率创建RenderTarget
RenderTarget = new RenderTarget2D(
    GraphicsDevice,
    1920,
    1080,
    false,
    GraphicsDevice.PresentationParameters.BackBufferFormat,
    DepthFormat.Depth24);

2. 修正Draw方法逻辑

重新计算缩放比例、偏移量,并修正RenderTarget的绘制参数:

protected override void Draw(GameTime gameTime)
{
    // 1. 绘制到虚拟分辨率的RenderTarget
    GraphicsDevice.SetRenderTarget(RenderTarget);
    GraphicsDevice.Clear(BackgroundColor);

    spriteBatch.Begin(); // 无需变换矩阵,RenderTarget本身就是虚拟画布
    foreach (var uiElement in UIElements)
    {
        uiElement.Draw(gameTime, spriteBatch);
    }
    spriteBatch.End();
    GraphicsDevice.SetRenderTarget(null);

    // 2. 计算适配到实际屏幕的参数
    int virtualWidth = 1920;
    int virtualHeight = 1080;
    int actualWidth = ScreenWidths[CurrentResolution];
    int actualHeight = ScreenHeights[CurrentResolution];

    // 取宽高比例的较小值,确保内容完整显示且无变形
    float scale = Math.Min((float)actualWidth / virtualWidth, (float)actualHeight / virtualHeight);
    // 计算居中偏移量
    int offsetX = (actualWidth - (int)(virtualWidth * scale)) / 2;
    int offsetY = (actualHeight - (int)(virtualHeight * scale)) / 2;
    // 最终绘制的矩形区域
    Rectangle destRect = new Rectangle(offsetX, offsetY, (int)(virtualWidth * scale), (int)(virtualHeight * scale));

    // 3. 将RenderTarget绘制到实际屏幕
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend,
                SamplerState.LinearClamp, DepthStencilState.Default,
                RasterizerState.CullNone);
    spriteBatch.Draw(RenderTarget, destRect, Color.White);
    spriteBatch.End();

    // 4. 修正鼠标坐标转换
    var mouseState = Mouse.GetState();
    MousePosition = new Vector2(
        (mouseState.X - offsetX) / scale,
        (mouseState.Y - offsetY) / scale
    );

    base.Draw(gameTime);
}

3. 分辨率切换后的RenderTarget重置

切换分辨率后需重新创建RenderTarget,避免GraphicsDevice参数变化导致异常:

// 切换分辨率按钮代码修改
if (Main.CurrentResolution >= 0 && Main.CurrentResolution < Main.ScreenWidths.Length - 1)              { 
    Main.CurrentResolution++;
    Main.graphics.PreferredBackBufferWidth = Main.ScreenWidths[Main.CurrentResolution];
    Main.graphics.PreferredBackBufferHeight = Main.ScreenHeights[Main.CurrentResolution];
    Main.graphics.ApplyChanges();

    // 销毁旧RenderTarget并重新创建
    Main.RenderTarget.Dispose();
    Main.RenderTarget = new RenderTarget2D(
        Main.graphics.GraphicsDevice,
        1920,
        1080,
        false,
        Main.graphics.GraphicsDevice.PresentationParameters.BackBufferFormat,
        DepthFormat.Depth24);
}

三、更优的多分辨率适配方案(无需RenderTarget)

若无需后期处理,可直接通过SpriteBatch的变换矩阵实现适配,省去RenderTarget的开销:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(BackgroundColor);

    int virtualWidth = 1920;
    int virtualHeight = 1080;
    int actualWidth = ScreenWidths[CurrentResolution];
    int actualHeight = ScreenHeights[CurrentResolution];

    float scale = Math.Min((float)actualWidth / virtualWidth, (float)actualHeight / virtualHeight);
    // 构建居中缩放的变换矩阵
    Matrix transformMatrix = Matrix.CreateTranslation(-virtualWidth / 2f, -virtualHeight / 2f, 0)
                           * Matrix.CreateScale(scale)
                           * Matrix.CreateTranslation(actualWidth / 2f, actualHeight / 2f, 0);

    spriteBatch.Begin(transformMatrix: transformMatrix);
    foreach (var uiElement in UIElements)
    {
        uiElement.Draw(gameTime, spriteBatch);
    }
    spriteBatch.End();

    // 鼠标坐标转换
    var mouseState = Mouse.GetState();
    MousePosition = Vector2.Transform(new Vector2(mouseState.X, mouseState.Y), Matrix.Invert(transformMatrix));

    base.Draw(gameTime);
}

该方案优势:

  • 减少内存占用与绘制开销
  • 逻辑简洁,直接通过矩阵变换完成适配
  • 同样保证UI元素无变形、居中显示

内容的提问来源于stack exchange,提问作者ExitThe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 18:40:32