[UnrealEngine4] 在game模式下如何禁止在windows下拉window的border來調整大小

posted in: C/C++程式設計, UnrealEngine | 0

2023 6月 Update:剛剛試4.27,現在只要在ProjectSetting把Allow Window Resize拿掉就行了 引擎版本:4.9,以下內容已經有點舊,僅供參考 最近在看各別解析度對應的方法,發現unreal engine 4裡面已經有DPI Scale來很好處理這一塊了。但其實這個機制其實也並不完美,因為玩家能夠亂拉視窗的大小,隨便亂拉的結果則會有些UI會跑到看不見,尤其是當視窗大小被拉成不是預期的狀態時,整個UI會變成很奇怪,這對於一個好的遊戲體驗是沒辦法接受的。 在我的預期上調整短邊應該要能夠scale位置,而調整長邊去scale大小。但這個DPI Scale機制最終其實也只能選擇直的或橫的來做處理。雖然有一個custom可以實作自己的類別,看起來似乎有那麼一點戲可以做到想做的效果,但實在有點麻煩,而且實作出來應該也只是對值的customize而已,而不是根據動作(上面說的位置、大小),所以我開始在尋找禁止玩家去亂拉window大小的方法,最終目標是只在遊戲中提供幾個按鈕讓玩家選擇可用的解析度大小。這種做法也比較符合一般遊戲使用的機制,不用去煩惱那幾近無限的解析度適配的問題,只要我們提供的選項的aspect ratio是一致的,然後再根據我們的選項去把那個DPI Scale curve拉好,基本上就不會有什麼大問題。 為了這個問題我研究了好幾天,發現在void FWindowsWindow::Initialize這個function下面雖然有傳入一個FGenericWindowDefinition definition來指定這個視窗的樣式,但該參數並沒有開放給引擎的使用者進行調整,因為它在更上一層的FSlateApplication::MakeWindow被宣告成local的變數再傳進來的。那麼到底該怎麼去調整這視窗的樣式呢? 看來引擎並沒有一個generic的api來做這件事,因此我們能做的就是針對各個平台拿出NativeWindow再進行對應的實作,以下的程式碼可以達成這件事: 在遊戲跑起來之後呼叫一次這個方法之後,總算能讓玩家不能去亂拉window的大小啦。 當然在其他平台,如mac或linux,則必須要再survey相關的實作才行(如果有需要的話)。

如何使用C++動態載入UMG與Slot基本概念

posted in: UnrealEngine | 0

不囉唆,直接先上CODE: https://gist.github.com/dorgonman/cbfe159a83ba07f1c66e   可以看到在header檔裡面宣告了一個WidgetClass跟一個WidgetInstance。 其中尋找WidgetClass的動作必須放在constructor裡面,另外: ConstructorHelpers::FClassFinder<UGameWidget> PutNameHere(TEXT(“/Game/MyUMG”)); 這邊裡面的路徑指的是我們在editor做好放在Content資料夾的Widget Blueprint(名字是MyUMG.uasset 的那個),這裡不用寫完整路徑的原因是FClassFinder內部會自動加入相關的suffix,如下圖: 接下來在BeginPlay裡面做的事情就是用這個Class把WidgetInstance建立出來,並加入一個UImage進到這個Widget裡面去。 在這裡必須要注意的是並不是所有的Widget都有AddChild這個方法,所以我們才需要先拿出RootCanvasPanel之後才能把image加進去。 什麼是Slot?從名稱來看,它就是用來讓別人把東西掛上去的『洞』,只要我們的child掛上某個parent的洞之後,就獲得了該parent所提供的調整界面,如下圖: 在code裡面我們canvasPanelSlot->SetPosition(FVector2D(100, 100)),其實就是調整editor裡面的Position X跟Position Y。值得注意的是Slot裡面的參數都是相對於parent的,而不是絕對的。 下面再給出一個掛到Button下面之後能夠調整的slot參數: 這邊的Horizontal Alignment跟Vertical Alignment指的就是我們的Image在button中要對齊的是那個位置。 另外要注意的是UCanvasPanelSlot* canvasPanelSlot = (UCanvasPanelSlot*)image->Slot;這行必須要在AddChild之後,因為如果是放在之前的話,因為Slot還沒掛上parent,所以會得到空指標。

[Machine Learning] Learning Coordination strategies using Reinforcement Learning

posted in: algorithm | 0

最近圍棋AI似乎還蠻熱門的,因此這裡將幾年前學生時期做的資料開放出來 : ) 這份投影片主要是研究標題中提到的論文,並對相關的文獻資料進行補充。 主要談論的主題是在圍棋上Reinforcement Learning的應用 雖然研究的論文年代有點老了,但相關的概念及基礎應該沒有改變太多。

[筆記] The CutHill-McKee Algorithm

posted in: algorithm | 0

http://ciprian-zavoianu.blogspot.tw/…/project-bandwidth-red… 這篇講的好清楚,總結一下: 1.把所有的node丟進去做BFS,看那個在最前面都可以。 2.把expanded node丟進fringeList(使用priority queue,根據degree排序),繼續BFS未完成的流程。 3.把expanded的node丟進closed array,若這個array的size等於所有的node就完成了,否則則繼續step 1中丟進來的起始node重開BFS。 4.把closed array做reverse完再用這個label去重建graph就行啦。   boost的graph內有相關的實作: http://www.boost.org/doc/libs/1_60_0/libs/graph/doc/sparse_matrix_ordering.html

結合boost.log跟Unreal Engine 4的log系統

最近在研究怎麼把boost跟UE4做結合使用,目前已經成功的將一些好用的boost library應用在專案上了,就先來整理一下相關該注意的技術事項吧。 首先我嘗試導入的是boost的log機制,雖然UE4本身已經有LOG系統了,但稍微survey了一下,看起來其實還是不太完整,而且log寫起來其實還蠻費勁的;如果能把boost.log導進來的話,不僅輸出log的時候會更方便,而且又能夠利用boost.log本身上提供的許多強大的功能。 其實boost.log還蠻容易跟其他引擎做結合的,我們只需要寫一個backend,並將其所綁定的sink加入到boost.log系統裡面就行了。而這個backend要做的事情也很簡單,它只需要負責把傳進來的訊息再原封不動的傳給UE4原本的log系統。聽起來是不是很簡單?先讓我們來看一下下面這段code: https://gist.github.com/dorgonman/fdab8f1be4a11a4a4fbc 把sink加入到log系統的方式如下: typedef boost::log::sinks::synchronous_sink<HorizonLogBasicFormatedBackend> HorizonLogSink; boost::log::core::get()->add_sink(sink); 紅色的部份是我們自己寫的backend,其實作如下: https://gist.github.com/dorgonman/3f56b82a8116bddb3425 這個backend留有一個接口: inline void setLogMessageImplemen(HorizonLogImpFunc_t&& impl); 目的就是留一個log的實作方式給引擎方去設定,我們需要做的就是從UE4丟一個std::function到backend就行了。設定方式如下: https://gist.github.com/dorgonman/0db4138c2cee95b51582 然後由於UE4中的check macro跟boost中的有衝突,因此在include boost 相關的header之前需先做下面這件事: #pragma push_macro(“check”) #undef check //include your boost header here #pragma pop_macro(“check”) 接下來我們就可以很方便的使用boost所提供的log機制了: https://gist.github.com/dorgonman/575f2e4a3c78621bcff8 只是目前的機制只能用在64位元的版本中,不知道為什麼當運行起32位元的時候總是會在下面這行code crash: if (_Myostr.rdbuf() … Continued

C++ Game Project for Unreal Engine 4.8 part4:Adjust Camera Ratio, Landscape and Portrait

posted in: UnrealEngine | 0

預設的Camera的視角其實是以Landscape來呈現,如果我們想要調整成Protrait的話該怎麼做呢?很簡單,我們只要調整Camera中的Aspect Ratio這個設定即可。我們可以看到這個值預設是1.777778,代表寬與高的比例是16:9。只要我們將這個比值反轉改設成0.5625,即9:16,我們就可以得到Portrait的視角。 有人問0.5625怎麼得到的?把9除上16就可以了。 只是這時候我們的Ortho Width裡面值的意義也同樣的跟height對調,因此我們這裡要選擇一個能夠符合9:16的寬高比。常用的比值如下: 我裡我們挑選720×1280,並將ortho Width設成720。 接著把camera的位置設成(-(720/2), -1440, (1280/2)),y值-1440(隨意即可),x跟z值則是我們挑選寬高的一半。記得camera的rotation.z設成90度。接著把我們的sprite位置設成(0, 0, 0),我們就完成了Portrait模式的設置。 從上圖我們可以看到,我們sprite的中心點是(0, 0, 0)並出現在視窗的左下角。 在調完Camera的視角之後,接下來我們可以調整我們的Widow size。首先找到Editor Preferences=>Level Editor=>Play中,把New Window Size跟Standalone Window Size設成我們想要的size,這裡先選擇使用iphone 4的size(640×960),因為可以用來模擬如果在手機上玩的感覺。

C++ Game Project for Unreal Engine 4.8 part3:Create and Customized Camera Actor from C++ code

posted in: UnrealEngine | 0

在上一篇我們討論了如何在editor上建立CameraActor,並在C++中找到該actor的instance。這篇將會反過來討論怎麼樣在C++上進行客制化,並將調整完的CameraActor加入到world中。首先我們先建立我們自己的C++類別並繼承自CameraActor: 記得要選Show All Classes,不然會找不到。 在建完我們的MainCameraActor之後,我們必須對裡面的CameraComponent進行設定。   AMainCameraActor::AMainCameraActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { GetCameraComponent()->ProjectionMode = ECameraProjectionMode::Orthographic; GetCameraComponent()->OrthoWidth = 2048.0f; }   跟上一篇不同的是,這裡我們將ProjectionMode調整成Orthographic,並將寬度設為2048。調完之後,我們就可以直接把C++ class拉進我們的editor中,並命名為MainCamera: 從上圖中可以看到在我們的MainCamera的instance底下有一個CameraComponent。上面程式碼中的GetCameraComponent()取到的就是這個物件。在引擎中通常會把Actor中可以隨時抽換組合的功能寫成一個Component,可以很方便我們進行功能的復用及重組。在我們的CameraActor中,預設有一個CameraComponent,主要用來描述我們CameraActor的viewpoint以及各種cmera相關的設定,例:projection type、field of view……。Actor本身並不會有transform相關資訊( location、rotation,、scale),這些資訊是保存在RootComponent中。以下是ACameraActor的建構子內容:   ACameraActor::ACameraActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { // Setup camera defaults CameraComponent = … Continued

C++ Game Project for Unreal Engine 4.8 part2:2D Camera

posted in: UnrealEngine | 0

當我們按下play按鈕的時候會發現,其實玩家還是在3D world中進行操控,當玩家按下旋轉或移動時,camera會進行對應的動作。那麼我們該怎麼固定Camera的視角呢? 為了解決這個問題,我們所需思考的,便是想辦法將一個我們固定好的camera設給玩家所使用的view port,並禁止其改變這個camera的位置。實際上我們該怎麼辦到這件事?由於在UE中對於不同關卡的管理是用『map』的概念來進行操作,因此我們必須想辦法在map中找到我們的PlayerController,並將這個被我們固定住的camera設給他。 map,其實就是一個擺設好關卡內容的場景再加上GameMode中所描述的game rule所組合而成。由於我們剛剛建立了一個MainMap取代了預設的template map,因此可以預見的是現在這個map的game mode會是空的。雖然在引擎中大部份的參數如果沒有設定的話,通常都會有引擎的預設值,但這裡我們希望將GameMode設回HopeGameMode這個類別。要在哪裡才能找到這個設定呢?首先先在右上角找到目前的map按右鍵,選取WorldSettings: 然後在下面找到GameMode,將GameMode Override設成HopeGameMode: 這時候我們會發現下面幾個參數都是灰色的:Default Pawn Class、HUD Class、Player Controller Class、Game State Class。當我們按下Play按鈕時,會發現在右上角的World Outliner中引擎自動生成的instaance包含了這幾個類別: 由於PlayerController負責用來接收Human Player的輸入並轉發給對應的Actor,因此為了達到固定視角的目的,因此我們必須使用我們自己的PlayerController,並設定這個CameraActor不該接受Human Player操控。只是由於我們的GameMode是C++ Class,因此要更改上面那四個預設的類別也只能在C++中操作。當然,若是使用BluePrint,我們就會發現在editor中就可以直接選擇了。 這裡我們討論的是C++,因此首先我們要做的,便是先建立一個C++ PlayerController,並在HopeGameMode的建構子中指定。在這裡,我們將其命名為GamePlayerController。   AHopeGameMode::AHopeGameMode(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {   PlayerControllerClass = AGamePlayerController::StaticClass(); }   有了PlayerController,接下來我們要在editor中建立一個CameraActor,並調整到正確的位置上。在這裡提供一個技巧,可以很快速的就將Camera的位置調好。首先,在右上角的World Outliner中對剛剛建好的CemeraActor按右鍵: 我們可以看到有三個選項: … Continued

C++ Game Project for Unreal Engine 4.8 part1:Hello World

posted in: UnrealEngine | 0

  寫在前面:   這系列的文章基本上預期要做一個簡單的2D遊戲,但也有可能會談到一些3D的東西。總之,內容基本上還未定案,但可以保證的是我會儘量的使用C++來實作所有的功能。 對於想要入門UE4的同學們,我想,我們可以一起加油來研究這個強大的引擎! 對於引擎使用的版本,基本上在寫文章的當下,若有新版本我應該都會去進行更新。   本文開始: 在建立完專案之後,首先我們會進到預設的Map: 但其實這個map是引擎底層所提供的template,之所以Editor會在一開始就載入這個頁面,是因為在Project Settings->Maps & Modes預設做了下面這個設定: 路徑是指向Engine底下的:Epic Games\4.8\Engine\Content\Maps\Templates 為了讓環境乾淨一點,因此我們選擇File->New Level,並建立一個新的Empty Level,並命名為MainMap。路徑存放在我們遊戲Content的Maps目錄底下: 從上面的內容我們應該可以看出來,UE對於『/Engine』跟『/Game』這二個路徑有做了一些處理,前者指向Engine底下的Content目錄,後者專向我們專案底下的Content目錄。 接著我們將Project Settings->Maps & Modes換成我們剛剛新建立的MainMap。關掉引擎,並雙擊Hope.uproject之後,我會就會發現Editor預設載入的Map變成剛剛我們所指定的了。 接著,讓我們試著加入一張2D sprite進到地圖看看吧! 首先在Content底下建立一個ArtWorks目錄,並將我們的圖檔拉進引擎裡面,就會自動建立對應的texture。由於引擎沒辦法直接吃texture,因此我們必須先對該texture建立Sprite才能開始使用。對該圖檔案右鍵,我們會發現有一個Create Sprite的選項: 點擊之後,我們就發現引擎在同目錄底下幫我們建立了一個Sprite: 左邊c1的type是texture,右邊的type是Sprite,而c1_Sprite會參考到c1這個texture。接著直接把Sprite拖到我們的map裡面去: 視角變成這樣子,但要做2D遊戲的話,用Perspective其實不好看,因此我們改用Orthographic模式會比較好看一點: 選擇之後變成這樣: 嗯,這樣在編輯2D遊戲的時候就比較符合直覺了。

[讀書筆記]Universal References in C++11 part 7: Small String Optimize(SSO) 與 move

什麼是SSO?用了這麼久的C++,還是第一次聽到這個名詞。其實,這個是標準庫對std::string所進行的一種優化:對於長度小的字串使用預先分配的stack storage(通常是16 Byte),長字串就根據長度動態的new出free storage。──我們知道,stack storage的速度比free storage還要快出許多。 上一篇我們有談到,若是一個類別裡面沒有使用到free storage的話,那麼其實move就等同於從一個stack storage複制到另一個stack storage。當std::string滿足SSO啟動條件的時候,就是一個典型的例子。但其實我們並不需要擔心太多這個優化問題,因為這邊的成本小到我們可以忽視掉。 不過,多知道一些東西說不定什麼時候能夠派上用場,因此還是讓我們來看看這個優化的細節到底是怎麼進行的吧! 首先,建議先閱讀下面二篇文章: http://stackoverflow.com/questions/10315041/meaning-of-acronym-sso-in-the-context-of-stdstring https://akrzemi1.wordpress.com/2014/04/14/common-optimizations/ 本篇文章接下來將會探討VC++的std::string的實作。 首先,先讓我們看看VC++裡面的SSO的宣告:   enum {    // length of internal buffer, [1, 16] _BUF_SIZE = 16 / sizeof(value_type) < 1 ? 1 : 16 / sizeof(value_type) };   … Continued