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

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

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

問題所在緣由是當你把虛幻引擎的窗口做爲子窗口掛在Qt後,SWindow::GetPositionInScreen()中返回的座標是錯誤的。 當你單獨啓動虛幻引擎的窗口,這裏返回的是當前窗口左上角在屏幕中的位置。而當你把虛幻引擎掛在爲Qt子窗口後,SWindow::GetPositionInScreen()中的ScreenPosition的值會因爲WM_MOVE更新爲0. 我看了你的代碼中 SetParent後:api

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()的獲取方式進行修改。less

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

小貼士

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

已知的坑

一、由於啓動的exe進程並不是遊戲進程,因此經過QProcess的狀態來判斷Ue4是否啓動是不對的,推薦使用WINAPI來獲取對應線程。 二、能夠在項目設置中修改窗口顯示標題,能夠把討厭的(32-bit, PCD3D_SM5)去掉,強烈推薦使用窗口句柄查看工具,我是網上下了句柄精靈。(窗口標題後面都是有空格的) 三、使用嵌入方法回致使Ue4客戶區(不太肯定這個叫什麼)座標異常(固定在左上角),會致使Umg按鈕沒法點擊,最大化窗口後能夠點擊到一部分。使用原生的win32窗口項目嵌入也是會有這個問題的,若是有對winapi瞭解人麻煩告知我解決方案。ui

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

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

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

由於本人對WINAPI不熟因此只找了一些資料.net

打開一個外部程序,通常是經過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);
}
相關文章
相關標籤/搜索