重新建文件夾開始構建UtopiaEngine(1)

序言

在苦等了半年多以後,我終於開始了嚮往已久的實時NPR遊戲引擎項目——Utopia Engine,這半年多一直爲了構建這個引擎在作不少準備:多線程、動態連接庫、腳本引擎、當即渲染GUI……通通吃了一遍(就差彙編沒學了,話說這學期要開這門課來着,結果老師都已經翹課四周了(╯‵□′)╯︵┻━┻)。因而,等不及的我開始了Utopia Engine的構建項目(彙編的知識就一邊作一邊學吧)要趕在畢設前作完這個引擎工做量仍是有些大的。固然,畢竟遊戲引擎脫離不了計算機圖形,因此,本博客裏的計算機圖形學相關內容也不會中止更新,敬請期待!ios

編寫此文的目的也很簡單:就是爲了作一個記錄。在之後的構建過程當中防止出現錯誤而沒法找到曾經埋下的隱患,而且同時也是學習相關技術的一個筆記,固然,我會盡可能把這個系列寫的通俗易懂,可讓看到這個博客同時也想要構建本身的遊戲引擎的各位遵循着本系列作出本身的遊戲引擎,是個有點偏向於教程的開發日誌。可是並非所謂的「0基礎速成班」,至少各位是要有較爲足夠的C++語言開發經驗以及計算機圖形學的基礎知識。若是二者都沒有,那麼各位在啃本人寫的這些文章時恐怕就要有些費勁了。git

1.項目設計

雖說我對這個引擎渲染目標的定位是實時NPR(也就是實時非真實渲染),但其實效果更偏向於NPR裏的「Toon Shading」,即卡通渲染。畢竟也是被新海誠導演製做的電影系列所驚豔到,因此但願經過實時渲染將這「雖然很假,但假的漂亮」的畫面表現出來。不過目前因爲本人的技術能力太過於生草以致於若是直接進行NPR的渲染實現不知道會作出什麼屑做來,而且目前現存的成功NPR技術實例也並非徹底在引擎層面上去實現的,由於美術風格但是破壞渲染統一性的最大緣由,著名的如萬代南夢宮的《究極風暴》以及《罪惡裝備X》幾乎都是從貼圖層面去實現真實的「假」細節(這可累壞了很多美工和TA),因此本人目前並不打算在Utopia Engine上實現實時NPR渲染,着重實現目前較爲流行的PBR(基於物理渲染)技術,也就是真實渲染領域,要求不高,只要能經過本引擎作出一個能夠跑的中小體量的3D遊戲便可,這也就是目前本階段的目標。接下來的事情就交給之後的我吧(拜託了,另外一個我!)github

2.初始化工做

2.1 項目構建

首先,聲明一下,我這裏的工做環境是微軟的Visual Studio 2017 Community(之後均簡稱VS2017),使用的圖形api若是不出意外的話應該就是OpenGL,WinSDK是 10.0.17763.0,不過,因爲WinSDK所形成的編譯失敗或者是其餘調試中的問題並不常見,因此這裏能夠忽略。windows

那麼接下來開始構建項目,如何在VS2017裏建立一個完整的C++空項目與解決方案而且爲它建立本地與遠程git代碼庫就不用我過多贅述了,相信看到這裏的你們都明白。不過咱們接下來並不會在項目裏建立咱們的第一個代碼文件而且開始莽代碼。由於VS2017自動建立的生成目錄、中間目錄、項目目錄文件結構並不怎麼符合咱們的需求,只要記住項目名便可。設計模式

接下來要介紹一位熟悉的陌生人:premake工具,說它熟悉是由於想必各位都應該據說過CMake,這個premake和CMake的做用同樣,都是用來作跨平臺項目構建的工具。說它陌生是由於這個名字是真的陌生(至少我周圍圈子的同窗都沒聽過),使用這個工具的一個最主要緣由是它不用在外網下載MinGW( ̄へ ̄),並且體量極小,一個Lua腳本就能夠完成全部配置而且生成你想要的平臺版本,缺點就是沒有GUI,你得面對CMD無盡的黑暗(這裏強力推薦微軟的Windows Terminal,有了它你甚至能夠將你的CMD裝扮成初號機的顯示屏)。不過操做很簡單,並且官方超詳細的Wiki也足以彌補這些缺陷。api

若是你沒有premake工具的話,請前往premake的GitHub項目主頁下載對應操做系統的release壓縮包。解壓後你會獲得一個premake5.exe可執行文件,將這個文件放入你的解決方案根目錄裏,接下來再在你的根目錄裏新建一個Lua腳本文件(文件後綴名是 「.lua」,文件名必須爲premake5),在裏面敲入以下Lua腳本代碼:多線程

workspace "UtopiaEngine"        -- 解決方案名稱(填入你本身的引擎解決方案名稱)
 architecture "x64"             -- 對應運行平臺,若是你想兼容更老的32位系統,請再加x86選項

 configurations {               -- 配置類型:Debug,Release,Dist
  "Debug",
  "Release",
  "Dist"
 }

-- 全局變量:描述輸出文件路徑,由於不管是中間目錄仍是輸出目錄都有一部分相同的,因此將它們提取出來
-- 和VS2017同樣,路徑定義均可以用特殊格式轉義符來表示,具體轉義符表示意義請參閱premake工具的GitHub wiki,裏面有詳細解釋,這裏不過多說明
outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"

project "UtopiaEngine"          -- 引擎項目名,不要說你連項目和解決方案的區別都不知道哦
 location "UtopiaEngine"        -- 源文件所在目錄名,這裏建議和項目名保持一致
 kind "SharedLib"               -- 項目生成類型,這裏選擇SharedLib,在Windows平臺上就是dll文件
 language "C++"                 -- 項目使用語言類型

 targetdir ("bin/" .. outputdir .. "/%{prj.name}")      -- 項目成品輸出目錄,Lua中使用「..」做爲字符串與變量之間的鏈接符
 objdir ("bin-int/" .. outputdir .. "/%{prj.name}")     -- 項目中間目錄

 files {                        -- 項目包含的文件類型,假如說你但願使用VS自帶的.def文件代替__declspec(dllexport),那麼請加上.def的聲明
  "%{prj.name}/src/**.h",
  "%{prj.name}/src/**.cpp"
 }

 includedirs {                  -- 項目使用的第三方庫的include目錄,spdlog會在後面說明
  "%{prj.name}/vendor/spdlog/include"
 }

 filter "system:windows"        -- Lua裏的條件判斷語句,開始和停止邊界以縮進爲準,這裏的意思是若目標系統爲Windows則執行下列操做
  cppdialect "C++17"            -- 項目所遵循的語言標準
  staticruntime "On"            -- 靜態運行時
  systemversion "10.0.17763.0"  -- 系統版本號,也就是WinSDK的版本號,若是不知道你本身的,能夠填latest
  
  defines {                     -- 項目的全局宏定義,後面會解釋兩個宏定義的意義
   "UTOPIA_PLATFORM_WINDOWS",
   "UTOPIA_BUILD_DLL",
  }

  postbuildcommands {           -- 須要premake在生成項目時執行的命令
   ("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")
  }

 filter "configurations:Debug"  -- debug配置相關設置,下同
  defines "UTOPIA_DEBUG"
  symbols "On"

 filter "configurations:Release"
  defines "UTOPIA_RELEASE"
  optimize "On"

 filter "configurations:Dist"
  defines "UTOPIA_DIST"
  optimize "On"


project "Sandbox"               -- 接下來是引擎編輯器的項目配置,名字什麼均可以,大體與Utopia Engine項目配置一致
 location "Sandbox"
 kind "ConsoleApp"
 language "C++"

 targetdir ("bin/" .. outputdir .. "/%{prj.name}")
 objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

 files {
  "%{prj.name}/src/**.h",
  "%{prj.name}/src/**.cpp"
 }

 includedirs {
  "UtopiaEngine/vendor/spdlog/include",
  "UtopiaEngine/src/UtopiaEngine"
 }

 links {
  "UtopiaEngine"
 }

 filter "system:windows"
  cppdialect "C++17"
  staticruntime "On"
  systemversion "latest"
  
  defines {
   "UTOPIA_PLATFORM_WINDOWS"
  }

 filter "configurations:Debug"
  defines "UTOPIA_DEBUG"
  symbols "On"

 filter "configurations:Release"
  defines "UTOPIA_RELEASE"
  optimize "On"

 filter "configurations:Dist"
  defines "UTOPIA_DIST"
  optimize "On"

接下來調用cmd命令行鍵入「premake5.exe vs2017」命令,其中若是你使用的IDE是VS2019,那麼就填「vs2019」便可,回車後運行,運行結果以下:
(圖1)編輯器

運行結果

在繼續以前先解釋幾個事情:函數

  1. 關於爲何將引擎編輯器和引擎核心分離出來,這一點相信你們若是使用虛幻4引擎寫過項目的話應該是深有體會的:本身項目的解決方案裏一般在本身的項目以外也會包含一個名爲UE4的項目(話說才發現Utopia Engine縮寫居然和虛幻同樣),而這個項目就是引擎核心,是獨立掛載於你的項目上的(ps:貌似UE4編輯器的代碼就在覈內心面,是要經過手動調用方法來略過生成編輯器的),再直白一點說就是你確定不但願你的遊戲裏還塞着一個編輯器,佔用存儲空間不說,也會消耗必定的計算資源。因此分離開是頗有必要的。工具

  2. Lua腳本中的postbuildcommands選項的解釋:
    {COPY} %{cfg.buildtarget.relpath}:copy命令,若是是使用VS2017默認的生成目錄,那麼引擎核心dll文件是與編輯器可執行程序分開存儲在兩個不一樣的目錄裏,若是要是進行debug(尤爲是針對引擎核心的debug),那麼必須在每次debug以前先編譯,而後手動將dll文件放入編輯器的生成目錄裏,麻煩。因此使用copy命令指示系統在每次生成完dll文件後會自動複製一份放在編輯器的生成目錄裏。

  3. 目前我只寫了關於Windows平臺的命令腳本,爲的是讓項目的目錄結構符合開發習慣而且減小無謂的工做量,因此這個premake腳本文件我並無寫MacOS以及Linux的生成命令,並且目前徹底沒有必要去在這兩個平臺上去調試代碼(人窮,買不起Mac)。若是各位有須要,能夠去premake的項目主頁看看官方文檔。

2.2 隱藏程序入口點

在引擎項目構建以前,我並無想好究竟該爲這個引擎配置一個怎樣的腳本引擎。因而,我目前的想法是使用純C++做爲引擎的遊戲開發腳本語言(與其說腳本倒不如說直接上源碼,誒嘿!),就和UE4同樣。不過,既然如此,就會很考驗設計模式的基礎了,即面向接口,由於遊戲開發者可不想由於閱讀你的源碼而浪費大量的時間成本,而咱們能作的就是儘可能爲他們提供通俗易懂而且功能實在的接口讓他們去調用。

既然各位能夠看到這裏而且信得過我,那麼我也相信各位已經有了一些圖形API的調用基礎(再不濟你也應該拿Java的awt作太小遊戲吧)。咱們知道,通常圖形程序無非就是由三個部分組成:即初始化、渲染循環以及釋放資源。這三個部分咱們能夠將它封裝在一個名爲Application的類裏(這裏只是打個比方),而後用三個方法將這三個部分依次描述,再而後在main裏面進行調用,這是目前最基礎的作法。或許有人會說:「啊呀,你這不符合單一職責原則」。那你也能夠封裝成三個類,每一個類只負責一個部分。可是遊戲開發者(尤爲是我的開發者)才無論你什麼亂七八糟的「七大原則、二十多種模式」的條條框框,他們只想專一於遊戲的實現,這時候若是讓他們去本身實現main函數,恐怕就沒人在再用你的引擎了(我就是這麼過來的)。因此咱們得隱藏程序入口點。

廢話說了那麼多,開始幹正事。在你引擎的編輯器項目裏新建一個cpp文件,在引擎核內心新建兩個h文件,爲了名稱好記,暫且就叫作Engine.h(引擎頭文件)、EntryPoint.h(入口點頭文件)以及Application.cpp(編輯器源文件)。固然,名稱只是打個比方,你能夠按照你本身喜歡的名稱來,可是你得記住相應的名稱的文件做用是什麼,畢竟咱們還會在之後再用到它們,咱們會在這三個文件中分別寫入以下內容:

EnntryPoint.h

int main(int argc, char** argv)
{
    // 這裏是引擎初始化代碼

    // 這裏是引擎渲染循環代碼

    // 這裏是引擎運行結束前釋放資源而且terminate的代碼

    std::cout<<"Utah Teapot";
    return 0;

}

Engine.h

#include <iostream>
#include "EntryPoint.h"

Application.cpp

#include <Engine.h>

運行結果以下:(其實只要看第一行便可,由於下面是我後來加的log系統,若是是你的項目,應該會成功輸出第一行的文字,log系統在以後的文章中我會講到)
(圖2)

運行結果

看起來貌似很簡單,對沒錯,就這幾行代碼,咱們完成了最重要的一步:入口隱藏。咱們將入口隱藏在了引擎核心dll文件裏,使得遊戲開發人員沒必要將過多的精力放在main的實現上。這樣看起來便有點樣子了,不是麼?

2.3 肯定引擎內核規則

其實我也不知道該怎麼稱呼,總之就是想要將咱們之後在引擎內核定義的對象、靜態方法、函數等一系列的東西調用在遊戲邏輯中的時候,可不止是一句簡簡單單的#include就能夠解決問題,畢竟引擎內核是一串二進制代碼構成的dll文件,即動態連接庫。微軟專門爲動態連接庫提供了兩個語句:dllimport以及dllexport,調用方法以下:

// 假如說我在dll裏的某個頭文件裏定義了這麼一個函數,而且是對外提供調用的
__declspec(dllexport) void DllFunc()
{
    // Blablablablablablabla...
}

// 若是我要在另外一個依賴此dll的項目裏使用它,那麼我必須在這個項目裏聲明
__declspec(dllexport) void DllFunc();

可是它太麻煩了,試想,若是每個對外開放的函數都這麼寫,冗餘的代碼量增長不說,關鍵是在面對跨平臺構建項目時,XCode可不認你的dllimport和dllexport,畢竟蘋果的MacOS有着本身的一套SharedLib體系,這時候再去一個個改又會增長無謂的工做量。因此咱們要經過某些手段來下降移植難度。

還記得在命令腳本里出現的兩句全局宏定義嗎:UTOPIA_PLATFORM_WINDOWS 以及 UTOPIA_BUILD_DLL 。接下來就是使用這些宏定義的時候了,咱們能夠爲引擎核心創造一個專門用來進行SharedLib生成指令的頭文件(文件名「Core.h」),代碼以下:

#pragma once
#ifdef UTOPIA_PLATFORM_WINDOWS                          // 平臺識別宏
    #ifdef UTOPIA_BUILD_DLL                             // 判斷是不是Dll內定義
        #define ENGINE_API __declspec(dllexport)        // 若是是,則dllexport
    #else                                               
        #define ENGINE_API __declspec(dllimport)        // 不然,dllimport
    #endif                                              
#else
    #error engine is only build on windows at now!      //錯誤信息
#endif

先解釋幾個問題:

  1. 我只是定義了適用於Windows平臺的宏定義,其餘平臺你們能夠根據編譯器的要求來設定不一樣的分支。

  2. 全局宏定義以及其餘的宏定義的名稱任君所愛,可是在定義完後請必定要記牢,之後還有用。

接下來讓它被包含在Engine.h中。那麼,在你的遊戲項目中只要引入Engine.h而且在咱們的之後書寫中爲類和函數定義前加上你所定義的 ENGINE_API,那麼在遊戲項目加載編譯時,編譯器會自動根據宏定義去判斷到底是使用import仍是export而不用你在源文件中進行聲明。

3.結束語

其實本引擎的結構在很大程度上與油管大佬Cherno的榛子引擎相像,由於我就是看着Cherno大佬的課程一步步來的,可是YouTube的聽譯字幕質量着實不咋樣,致使我看的時候甚至一天只能看兩個教學視頻,B站也有好心的UP主搬運過來但因爲是全英文的因此也勸退了很多人。我記得在虛幻引擎的官方Q&A裏面有這樣一句話:「代碼是有版權的,但知識是無價的」。因此,若是說你的閱讀能力不錯,那能夠跟着我一塊來學習,我替你把該踩的雷先踩過,這樣你也就輕鬆多了,對我來講也是在積攢開發知識的過程,何樂而不爲?可是也不要抱太大但願哦,說不定哪天就斷更了,誒嘿!o( ̄▽ ̄)ブ

固然了,由於完成時間倉促以及本人技術力太過生草,確定文中還有許許多多的問題,歡迎各位大佬們指正(若是隻是想無腦噴的話,請出門左拐WB),以上。

相關文章
相關標籤/搜索