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

Unreal Engine macOS切换窗口模式时崩溃无响应问题求助

UE项目macOS下指定显示器切换全屏/窗口崩溃无响应问题

开发Unreal Engine项目时,在macOS系统中切换选定显示器的全屏/窗口模式,应用会出现崩溃或无响应情况。Windows系统下运行正常,但macOS上操作后游戏窗口冻结,崩溃后仅能移动鼠标无法点击,只能强制退出。当前实现的ApplyFullscreenToMonitor()函数负责将全屏模式应用到指定显示器,逻辑为获取显示指标、根据显示器分辨率和位置调整窗口,代码如下:

void UEtrResolutionSetting::ApplyFullscreenToMonitor()
{
    // Get game user settings
    UGameUserSettings* GameUserSettings = GEngine->GetGameUserSettings();
    if (GameUserSettings)
    {
        FDisplayMetrics displayMetrics;
        FSlateApplication::Get().GetDisplayMetrics(displayMetrics);
        const int32 num = displayMetrics.MonitorInfo.Num();
        int32 chosenIndex = -1;
        int32 primaryIndex = 0;
        for (int32 i = 0; i < num; i++)
        {
            FMonitorInfo& info = displayMetrics.MonitorInfo[i];
            if (info.Name == SelectedMonitor.Name)
            {
                chosenIndex = i;
                break;
            }
            if (info.bIsPrimary) { primaryIndex = i; }
        }
        int32 finalIndex = (chosenIndex < 0) ? primaryIndex : chosenIndex;
        if (chosenIndex < 0 && !SelectedMonitor.Name.IsEmpty())
        {
            // NOTE: Log something here
        }
        const FMonitorInfo& info = displayMetrics.MonitorInfo[finalIndex];
        const FIntPoint resolution = GameUserSettings->GetScreenResolution();
        if (resolution.X > info.NativeWidth || resolution.Y > info.NativeHeight)
        {
            GameUserSettings->SetScreenResolution({ info.NativeWidth, info.NativeHeight });
        }
        GameUserSettings->SetWindowPosition(info.WorkArea.Left, info.WorkArea.Top);
        // Move the window to the selected monitor and resize
        if (GEngine && GEngine->GameViewport && GEngine->GameViewport->GetWindow())
        {
            TSharedPtr<SWindow> window = GEngine->GameViewport->GetWindow();
            EWindowMode::Type const WindowMode = GEngine->GetGameUserSettings()->GetFullscreenMode();
            window->MoveWindowTo(GameUserSettings->GetWindowPosition());
            window->Resize(GameUserSettings->GetScreenResolution());
            window->SetWindowMode(WindowMode);
            GameUserSettings->SetFullscreenMode(WindowMode);
            GameUserSettings->ApplyResolutionSettings(false);
        }
        else
        {
            mDelegateHandleViewportCreated = UGameViewportClient::OnViewportCreated().AddUObject(
                this, &UEtrResolutionSetting::ApplyFullscreenToMonitor);
        }
        if (mDelegateHandleViewportCreated.IsValid())
        {
            UGameViewportClient::OnViewportCreated().Remove(mDelegateHandleViewportCreated);
            mDelegateHandleViewportCreated.Reset();
        }
        // Log successful fullscreen change on the specific monitor
        UE_LOG(LogTemp, Log, TEXT("Game switched to fullscreen on monitor: %s with resolution %dx%d"), *SelectedMonitor.Name, resolution.X, resolution.Y);
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to get GameUserSettings"));
    }
}

问题根源

  • 委托逻辑错误:代码中在else分支添加OnViewportCreated委托后,立刻移除委托,导致委托永远不会触发,窗口未初始化时无法正确执行逻辑。
  • 窗口操作顺序不符合macOS机制:直接调用SWindow的移动、 resize、切换窗口模式方法,macOS下Slate窗口的全屏切换需要特定顺序,直接操作可能引发UI线程阻塞或死锁。
  • 重复状态变更:同时调用window->SetWindowMode和GameUserSettings->SetFullscreenMode,触发重复的窗口状态变更,引发冲突。

修复方案

  1. 修正委托逻辑:仅在窗口未初始化时添加委托,且在委托触发后再移除,确保窗口创建完成后执行全屏切换。
  2. 调整窗口操作流程:依赖GameUserSettings的ApplyResolutionSettings统一处理窗口状态,避免直接调用Slate窗口方法;macOS下需先切换到窗口模式,调整位置后再切换到全屏。
  3. 确保UI操作在主线程执行:使用延迟队列将窗口操作放到主线程执行,避免跨线程操作冲突。

修改后的代码

void UEtrResolutionSetting::ApplyFullscreenToMonitor()
{
    UGameUserSettings* GameUserSettings = GEngine->GetGameUserSettings();
    if (!GameUserSettings)
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to get GameUserSettings"));
        return;
    }

    FDisplayMetrics displayMetrics;
    FSlateApplication::Get().GetDisplayMetrics(displayMetrics);
    const int32 MonitorCount = displayMetrics.MonitorInfo.Num();
    int32 ChosenIndex = -1;
    int32 PrimaryIndex = 0;

    for (int32 i = 0; i < MonitorCount; i++)
    {
        FMonitorInfo& Info = displayMetrics.MonitorInfo[i];
        if (Info.Name == SelectedMonitor.Name)
        {
            ChosenIndex = i;
            break;
        }
        if (Info.bIsPrimary)
        {
            PrimaryIndex = i;
        }
    }

    const int32 FinalIndex = (ChosenIndex >= 0) ? ChosenIndex : PrimaryIndex;
    if (ChosenIndex < 0 && !SelectedMonitor.Name.IsEmpty())
    {
        UE_LOG(LogTemp, Warning, TEXT("Selected monitor %s not found, falling back to primary"), *SelectedMonitor.Name);
    }

    const FMonitorInfo& TargetMonitor = displayMetrics.MonitorInfo[FinalIndex];
    const FIntPoint CurrentResolution = GameUserSettings->GetScreenResolution();
    const FIntPoint TargetResolution = FIntPoint(TargetMonitor.NativeWidth, TargetMonitor.NativeHeight);

    // 限制分辨率不超过显示器原生尺寸
    if (CurrentResolution.X > TargetResolution.X || CurrentResolution.Y > TargetResolution.Y)
    {
        GameUserSettings->SetScreenResolution(TargetResolution);
    }
    GameUserSettings->SetWindowPosition(TargetMonitor.WorkArea.Left, TargetMonitor.WorkArea.Top);

    if (GEngine && GEngine->GameViewport && GEngine->GameViewport->GetWindow())
    {
        TSharedPtr<SWindow> GameWindow = GEngine->GameViewport->GetWindow();
        EWindowMode::Type TargetWindowMode = GameUserSettings->GetFullscreenMode();

        // macOS下需先切换到窗口模式再调整位置,避免全屏状态下移动窗口引发冲突
        if (TargetWindowMode == EWindowMode::Fullscreen && FPlatformProperties::IsMac())
        {
            // 延迟到主线程执行,避免UI线程阻塞
            FSlateApplication::Get().GetMainThreadWidget()->QueueDeferredAction([this, GameWindow, GameUserSettings, TargetWindowMode]()
            {
                GameWindow->SetWindowMode(EWindowMode::Windowed);
                GameWindow->MoveWindowTo(GameUserSettings->GetWindowPosition());
                GameUserSettings->SetFullscreenMode(TargetWindowMode);
                GameUserSettings->ApplyResolutionSettings(false);
            });
        }
        else
        {
            GameUserSettings->SetFullscreenMode(TargetWindowMode);
            GameUserSettings->ApplyResolutionSettings(false);
        }
    }
    else
    {
        // 仅当委托未添加时才注册,避免重复注册
        if (!mDelegateHandleViewportCreated.IsValid())
        {
            mDelegateHandleViewportCreated = UGameViewportClient::OnViewportCreated().AddUObject(this, &UEtrResolutionSetting::ApplyFullscreenToMonitor);
        }
        return;
    }

    // 移除已完成任务的委托
    if (mDelegateHandleViewportCreated.IsValid())
    {
        UGameViewportClient::OnViewportCreated().Remove(mDelegateHandleViewportCreated);
        mDelegateHandleViewportCreated.Reset();
    }

    UE_LOG(LogTemp, Log, TEXT("Game switched to %s on monitor: %s with resolution %dx%d"), 
        *UEnum::GetValueAsString(GameUserSettings->GetFullscreenMode()), 
        *SelectedMonitor.Name, 
        GameUserSettings->GetScreenResolution().X, 
        GameUserSettings->GetScreenResolution().Y);
}

额外注意事项

  • macOS的全屏模式分为原生全屏(EWindowMode::Fullscreen)和窗口全屏(EWindowMode::WindowedFullscreen),原生全屏会占用整个显示器,切换时需要系统级权限,可能引发兼容性问题,建议优先使用窗口全屏模式。
  • 测试时需关闭macOS的“自动隐藏菜单栏”和“自动隐藏程序坞”选项,避免窗口状态变更时系统UI干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 18:35:07