對《將Unreal4打包后的工程嵌入到Qt或者桌面中》一文的補充


在上一文中本人嘗試將Ue4嵌入到Qt中,但依然有一些問題沒有去嘗試解決。今天因為幫助知乎專欄作者@大釗的關系,順便進行補完。

2018.7.18更新:
正好在參加杭州UnrealCircle的時候見到了EPIC上海的工程師李鋒,之后我通過郵件詢問了他這個問題,以下是他給我的回復:

問題所在原因是當你把虛幻引擎的窗口作為子窗口掛在Qt后,SWindow::GetPositionInScreen()中返回的坐標是錯誤的。
當你單獨啟動虛幻引擎的窗口,這里返回的是當前窗口左上角在屏幕中的位置。而當你把虛幻引擎掛在為Qt子窗口后,SWindow::GetPositionInScreen()中的ScreenPosition的值會由於WM_MOVE更新為0.
我看了你的代碼中 SetParent后:

MoveWindow(hwnWindow,rect.x(),rect.y(), rect.width(), rect.height(), true);
::SetWindowPos( hwnWindow, nullptr,rect.x(), rect.y(), rect.width(), rect.height(), SWP_NOZORDER | SWP_FRAMECHANGED| SWP_NOCOPYBITS );

但是這兩句執行后,最后系統還是會收到WM_MOVE消息進而把窗口的坐標設置為0. 說明Qt在后面某一時刻把你的窗口重新Move回(0,0). 這個需要你去Qt中查看了.
(其實這里你的MoveWindow和SetWindowPos 在后面都被覆蓋掉了,沒有生效。原因是當你單獨創建兩個Windows窗口,不適用Qt框架,你會看到子窗口相對於父窗口左上角的位置就是你MoveWindow中設置的 rect.x(),rect.y()的值,然而在你寫的Qt代碼運行后,子窗口>這里不管寫什么值,最終都是貼着父窗口Client Area左上角的)

總結原因是因為SetParent后,SWindow就無法接受到WM_MOVE消息了。突破思路有這么幾條,但限於本人技術的問題暫時不會去嘗試:
1.在Qt中手動發送WM_MOVE消息給Ue4窗口
2.在Qt中通過Socket之類的方法更新ScreenPosition的數值
3.修改UE4源代碼,將SWindow::GetPositionInScreen()的獲取方式進行修改。

2018.6.16更新:
1、更新了一下代碼,刪除了一些錯誤的東西

小貼士

Ue4項目設置中的Use Borderless Window
這個選項會讓Ue4窗口變成沒有外框與標題欄的狀態,這樣在嵌入Qt的時候就不要設置WindowStyle了,這樣看起來效果會更好(設置WindowStyle會讓窗口短暫顯現出來,這樣或許就不需要做靜默啟動了)
解除Ue4對鼠標的限制
Set Input Game And UI 來解除Ue4對鼠標限制
讓Ue4重新獲得焦點
BringWindowToTop (HWND);

已知的坑

1、因為啟動的exe進程並非游戲進程,所以通過QProcess的狀態來判斷Ue4是否啟動是不對的,推薦使用WINAPI來獲取對應線程。
2、可以在項目設置中修改窗口顯示標題,可以把討厭的(32-bit, PCD3D_SM5)去掉,強烈推薦使用窗口句柄查看工具,我是網上下了句柄精靈。(窗口標題后面都是有空格的)
3、使用嵌入方法回導致Ue4客戶區(不太確定這個叫什么)坐標異常(固定在左上角),會導致Umg按鈕無法點擊,最大化窗口后可以點擊到一部分。使用原生的win32窗口項目嵌入也是會有這個問題的,如果有對winapi了解人麻煩告知我解決方案。

本人嘗試了許多方法,都失敗了:
1、在Ue4中使用AdjustWindowRectEx
2、SetWindowPos與MoveWindow
3、在Ue4中手動調用Ue4包裝的MoveWindow函數
4、先移動Ue4窗口再嵌入Qt
5、向ue4發送Resize消息

目前的補救方案就是,在Ue4中計算鼠標坐標,再使用虛擬點擊來點擊按鈕。

關於靜默啟動外部程序與枚舉進程與窗體句柄

因為本人對WINAPI不熟所以只找了一些資料

打開一個外部程序,一般是通過ShellExecute/Ex或CreateProcess或WinExec等API。
至於隱藏主窗口以及控制窗口上的控件,這個根據不同的程序會有一定的復雜程度,首先是找到所謂的主窗口,然后再枚舉窗口上的子窗口,通過一系列API模擬點擊或讀取/設置文本內容等。
參考:http://bbs.csdn.net/topics/240049935

https://www.cnblogs.com/zjutlitao/p/3889900.html

代碼

這里我直接上代碼了,一看就懂。

#include "calculateandmove.h"
#include "ui_calculateandmove.h"
#include <QDebug>
#include <QtConcurrent>
#include <QThread>
#include <QWindow>
#include <QTimer>
CalculateAndMove::CalculateAndMove(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::CalculateAndMove),process(nullptr)
{
    ui->setupUi(this);
    //setAttribute(Qt::WA_NativeWindow, true);
}

CalculateAndMove::~CalculateAndMove()
{
    if(hwnWindow!=0)
        SendMessage(hwnWindow,WM_CLOSE,0,0);
    if(process){
        if(process->state()==QProcess::Running){
            process->terminate();
            process->waitForFinished(30000);
       }
       delete process;
   }
    delete ui;
}

void CalculateAndMove::on_insetUe4_clicked()
{
    startUe4();
}

void CalculateAndMove::on_deleteUe4_clicked()
{
    if(hwnWindow==0)
        return;
    SendMessage(hwnWindow,WM_CLOSE,0,0);
}

void CalculateAndMove::startUe4(){
    //啟動程序
    QString unreal4Path{"D:/QtProject/QtWithUnreal4/WindowsNoEditor/DemoGame.exe"};
    QStringList arguments;
    arguments << "-WINDOWED";
    process=new QProcess;
    process->start(unreal4Path,arguments);

    QtConcurrent::run([this]{
        while (true) {
            //通過窗口名稱取得窗口句柄
            //hwnWindow=FindWindow(NULL,L"DemoGame  ");
            hwnWindow=FindWindow(NULL,L"DemoGame (32-bit, PCD3D_SM5) ");
            qDebug()<<hwnWindow;
            if(hwnWindow!=0)
            {
                connect(this,SIGNAL(insetUe4Complete()),this,SLOT(insetUe4()));
                emit insetUe4Complete();
                break;
            }
        }
    });
}

void CalculateAndMove::insetUe4(){  
////----------------------------------------------
////方法一,進qt后需要調用解除客戶區鎖定函數(Ue4
//    ue4Window=QWindow::fromWinId(WId(hwnWindow));
//    ue4Window->setParent( this->windowHandle());
//    ue4Window->setGeometry(0,0,ui->label->width(),ui->label->height());
//    ue4Window->show();

 //----------------------------------------------
 //方法二,進qt后需要調用解除客戶區鎖定函數(Ue4
    QRect rect=ui->label->geometry();
    QPoint pos=ui->label->mapToGlobal(ui->label->pos());
    qDebug()<<"rect:"<<rect;
    qDebug()<<"worldPos:"<<pos;
    QRect worldRect={rect.x()+pos.x(),rect.y()+pos.y(),rect.width()+pos.x(),rect.height()+pos.y()};

    SetParent(hwnWindow,(HWND)QWidget::winId());

    //MoveWindow(hwnWindow,rect.x(),rect.y(), rect.width(), rect.height(), true);
    ::SetWindowPos( hwnWindow, nullptr,rect.x(), rect.y(), rect.width(), rect.height(), SWP_NOZORDER | SWP_FRAMECHANGED| SWP_NOCOPYBITS );

//----------------------------------------------


    LPRECT lprect;
    GetClientRect(hwnWindow,lprect);
    qDebug()<<(int)lprect->left<<(int)lprect->top<<(int)lprect->right<<(int)lprect->bottom;
    this->repaint();
}

void CalculateAndMove::on_pushButton_clicked()
{
        BringWindowToTop (hwnWindow);
        SetForegroundWindow(hwnWindow);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM