虚幻4自定义webbrowser,解除帧率锁定

源码分析

Webrowser是虚幻官方的webUi插件,但插件本身默认被锁了24FPS,导致部分动画看起来很卡段不连贯

其他修改浏览器参数的需求也可参照此文章

在虚幻引擎的安装目录:\Engine\Plugins\Runtime\WebBrowserWidget\Source\WebBrowserWidget\Private下可以看到webbrowser的源码,但是下载的安装版是无法修改此源码的,即便修改也无法编译,所以如果要实现自定义的浏览器,那就有两种方法:

  1. 下载虚幻源码自编译
  2. 仿照其写一个浏览器控件并调用

显然第一点的代价很大,所以接下来采取第二个方法

通过源码可以得知,这个浏览器控件其实是对“SBrowser”的实例化,cpp文件90行左右有如下代码:

WebBrowserWidget = SNew(SWebBrowser)
            .InitialURL(InitialURL)
            .ShowControls(false)
            .SupportsTransparency(bSupportsTransparency)
            .OnUrlChanged(BIND_UOBJECT_DELEGATE(FOnTextChanged, HandleOnUrlChanged))
            .OnBeforePopup(BIND_UOBJECT_DELEGATE(FOnBeforePopupDelegate, HandleOnBeforePopup));

        return WebBrowserWidget.ToSharedRef();

查看SWebBrowser的实现(直接资源管理器搜索这个文件即可),可看到其有对各种参数的定义,其中就有帧率限制:

SLATE_BEGIN_ARGS(SWebBrowser)
        : _InitialURL(TEXT("https://www.google.com"))
        , _ShowControls(true)
        , _ShowAddressBar(false)
        , _ShowErrorMessage(true)
        , _SupportsTransparency(false)
        , _SupportsThumbMouseButtonNavigation(true)
        , _ShowInitialThrobber(true)
        , _BackgroundColor(255,255,255,255)
        , _BrowserFrameRate(24)
        , _PopupMenuMethod(TOptional<EPopupMethod>())
        , _ViewportSize(FVector2D::ZeroVector)
    { }
    //。。下面还有很多参数,可以自行查看

到这里思路就比较清晰了,如果使用自编译源码的话,直接把24改了就好了,但是如果不是自编译,就需要另外搞事情。

自定义浏览器widget

新建C++类,名字随意,这里以WebBrowser60为例,意为60FPS的浏览器。

CPP文件和H文件直接照搬WebBrowser,如果没有特殊需求的话。
在初始化SBrowser的位置加上一句对参数的设定就行,理论上参数是都能设置的

WebBrowserWidget = SNew(SWebBrowser)
            .InitialURL(InitialURL)
            .ShowControls(false)
            .SupportsTransparency(bSupportsTransparency)
            //重点在这里
            .BrowserFrameRate(60)
            //重点在这里
            .OnUrlChanged(BIND_UOBJECT_DELEGATE(FOnTextChanged, HandleOnUrlChanged))
            .OnBeforePopup(BIND_UOBJECT_DELEGATE(FOnBeforePopupDelegate, HandleOnBeforePopup));

下面是整个cpp和h的文件代码,可以直接copy

UWebBrowser60.cpp

// Copyright Epic Games, Inc. All Rights Reserved.

#include "WebBrowser60.h"
#include "SWebBrowser.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Text/STextBlock.h"
#include "Async/TaskGraphInterfaces.h"
#include "UObject/ConstructorHelpers.h"

#if WITH_EDITOR
#include "Materials/MaterialInterface.h"
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
#include "Materials/MaterialExpressionTextureSample.h"
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
#include "Materials/MaterialFunction.h"
#include "Factories/MaterialFactoryNew.h"
#include "AssetRegistryModule.h"
#include "PackageHelperFunctions.h"
#endif

#define LOCTEXT_NAMESPACE "WebBrowser"

/////////////////////////////////////////////////////
// UWebBrowser60

UWebBrowser60::UWebBrowser60(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
    bIsVariable = true;
}

void UWebBrowser60::LoadURL(FString NewURL)
{
    if (WebBrowserWidget.IsValid())
    {
        return WebBrowserWidget->LoadURL(NewURL);
    }
}

void UWebBrowser60::LoadString(FString Contents, FString DummyURL)
{
    if (WebBrowserWidget.IsValid())
    {
        return WebBrowserWidget->LoadString(Contents, DummyURL);
    }
}

void UWebBrowser60::ExecuteJavascript(const FString& ScriptText)
{
    if (WebBrowserWidget.IsValid())
    {
        return WebBrowserWidget->ExecuteJavascript(ScriptText);
    }
}

FText UWebBrowser60::GetTitleText() const
{
    if (WebBrowserWidget.IsValid())
    {
        return WebBrowserWidget->GetTitleText();
    }

    return FText::GetEmpty();
}

FString UWebBrowser60::GetUrl() const
{
    if (WebBrowserWidget.IsValid())
    {
        return WebBrowserWidget->GetUrl();
    }

    return FString();
}

void UWebBrowser60::ReleaseSlateResources(bool bReleaseChildren)
{
    Super::ReleaseSlateResources(bReleaseChildren);

    WebBrowserWidget.Reset();
}

TSharedRef<SWidget> UWebBrowser60::RebuildWidget()
{
    if (IsDesignTime())
    {
        return SNew(SBox)
            .HAlign(HAlign_Center)
            .VAlign(VAlign_Center)
            [
                SNew(STextBlock)
                .Text(LOCTEXT("Web Browser", "Web Browser"))
            ];
    }
    else
    {
        WebBrowserWidget = SNew(SWebBrowser)
            .InitialURL(InitialURL)
            .ShowControls(false)
            .SupportsTransparency(bSupportsTransparency)
            .BrowserFrameRate(60)
            .OnUrlChanged(BIND_UOBJECT_DELEGATE(FOnTextChanged, HandleOnUrlChanged))
            .OnBeforePopup(BIND_UOBJECT_DELEGATE(FOnBeforePopupDelegate, HandleOnBeforePopup));

        return WebBrowserWidget.ToSharedRef();
    }
}

void UWebBrowser60::SynchronizeProperties()
{
    Super::SynchronizeProperties();

    if (WebBrowserWidget.IsValid())
    {

    }
}

void UWebBrowser60::HandleOnUrlChanged(const FText& InText)
{
    OnUrlChanged.Broadcast(InText);
}

bool UWebBrowser60::HandleOnBeforePopup(FString URL, FString Frame)
{
    if (OnBeforePopup.IsBound())
    {
        if (IsInGameThread())
        {
            OnBeforePopup.Broadcast(URL, Frame);
        }
        else
        {
            // Retry on the GameThread.
            TWeakObjectPtr<UWebBrowser60> WeakThis = this;
            FFunctionGraphTask::CreateAndDispatchWhenReady([WeakThis, URL, Frame]()
                {
                    if (WeakThis.IsValid())
                    {
                        WeakThis->HandleOnBeforePopup(URL, Frame);
                    }
                }, TStatId(), nullptr, ENamedThreads::GameThread);
        }

        return true;
    }

    return false;
}

#if WITH_EDITOR

const FText UWebBrowser60::GetPaletteCategory()
{
    return LOCTEXT("Experimental", "Experimental");
}

#endif

/////////////////////////////////////////////////////

#undef LOCTEXT_NAMESPACE

UWebBrowser60.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once
/*MyWebBrowser.h*/

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Components/Widget.h"
#include "WebBrowser60.generated.h"

/**
 * 
 */
UCLASS()
class DIGITALTWINPLATFORM_API UWebBrowser60 : public UWidget
{
    GENERATED_UCLASS_BODY()

public:
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUrlChanged, const FText&, Text);
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnBeforePopup, FString, URL, FString, Frame);

    /**
     * Load the specified URL
     *
     * @param NewURL New URL to load
     */
    UFUNCTION(BlueprintCallable, Category = "Web Browser")
    void LoadURL(FString NewURL);

    /**
     * Load a string as data to create a web page
     *
     * @param Contents String to load
     * @param DummyURL Dummy URL for the page
     */
    UFUNCTION(BlueprintCallable, Category="Web Browser")
    void LoadString(FString Contents, FString DummyURL);

    /**
    * Executes a JavaScript string in the context of the web page
    *
    * @param ScriptText JavaScript string to execute
    */
    UFUNCTION(BlueprintCallable, Category = "Web Browser")
    void ExecuteJavascript(const FString& ScriptText);

    /**
     * Get the current title of the web page
     */
    UFUNCTION(BlueprintCallable, Category="Web Browser")
    FText GetTitleText() const;

    /**
    * Gets the currently loaded URL.
    *
    * @return The URL, or empty string if no document is loaded.
    */
    UFUNCTION(BlueprintCallable, Category = "Web Browser")
    FString GetUrl() const;

    /** Called when the Url changes. */
    UPROPERTY(BlueprintAssignable, Category = "Web Browser|Event")
    FOnUrlChanged OnUrlChanged;

    /** Called when a popup is about to spawn. */
    UPROPERTY(BlueprintAssignable, Category = "Web Browser|Event")
    FOnBeforePopup OnBeforePopup;

public:

    //~ Begin UWidget interface
    virtual void SynchronizeProperties() override;
    // End UWidget interface

    virtual void ReleaseSlateResources(bool bReleaseChildren) override;

#if WITH_EDITOR
    virtual const FText GetPaletteCategory() override;
#endif

protected:
    /** URL that the browser will initially navigate to. The URL should include the protocol, eg http:// */
    UPROPERTY(EditAnywhere, Category=Appearance)
    FString InitialURL;

    /** Should the browser window support transparency. */
    UPROPERTY(EditAnywhere, Category=Appearance)
    bool bSupportsTransparency;

protected:
    TSharedPtr<class SWebBrowser> WebBrowserWidget;

protected:
    // UWidget interface
    virtual TSharedRef<SWidget> RebuildWidget() override;
    // End of UWidget interface

    void HandleOnUrlChanged(const FText& Text);
    bool HandleOnBeforePopup(FString URL, FString Frame);

};