You don't need to use Visual Studio to write
game code for Unreal. You can generate a solution file, crack it
open, and just start writing code. That's the officially supported workflow,
after all, and it's a perfectly valid approach. In particular, it's great for beginners since
it's an easy way to get started. But I've found it also leads a lot of people
to miss out on an opportunity to form an understanding of how an Unreal
project fits together and how the build system works. And let's face it, the Visual Studio Experience
is not everybody's cup of tea. So in this video, I'm going to show you how
we can make an Unreal project from scratch, with just a text editor, and how we can build
and run that project from the command-line. My hope is that you'll come away from this
video with a solid, baseline understanding of what happens when you build your project,
so that down the line, when something goes wrong and you're staring at a wall of cryptic error
messages, you'll have a better idea of how to diagnose the problem and get moving
again. Just so we're all on the same page, we're
going to start from a clean installation of Windows. So we need to do some initial setup: this
means configuring the OS to suit our tastes, deleting all the garbage that comes preinstalled, installing whatever drivers and utilities
we need... and deleting even more garbage for good measure. Now, before we start talking about game engines
and compilers, there are two essential tools that every developer
needs: a text editor and a terminal. I'm going to install my favorites for Windows,
which are Sublime Text 3 and Cmder. Sublime Text is great because it doesn't get
in your way. It's highly customizable and packed with great
features, but it's also extremely lightweight and responsive. No matter how big your codebase is, Sublime
won't slow down and it won't hang. Cmder is a package that's built on top of
ConEmu to provide a better command-line experience on Windows. Once we've got it up and running, we can press
Ctrl+~ at any time to show or hide our terminal. Now we have our two most essential tools. We'll see them in action in a little bit. But first, let's install Unreal. There are two ways to set up an installation
of Unreal Engine: you can clone the complete Engine source from
GitHub and build it yourself, or you can install a pre-built version using
the Epic Games Launcher. If you want to be able to make changes to
the Engine itself, cloning and building from source is the way
to go. But the Launcher makes it easy to get up and
running, so that's what we'll use for now. We just need an Epic Games account, and then
we can install and run the launcher, go to Unreal Engine -> Library, and install
the latest version of Unreal. This download is about 12 gigs, and it'll
end up taking up about 35 gigs once installed. After the download finishes, it'll verify
everything, prompt us for permission to install prerequisites,
and then we're good to go. Since we're going to be writing game code
in C++, we need to install Visual Studio. Most people should just install the newest
version of Visual Studio that's recommended for their release of Unreal
and move on. But we aren't most people, so we're going
to pay attention to specific versions. And Visual Studio versions can be a bit confusing. Essentially, the Visual Studio IDE is its
own product, with its own sequential versioning scheme, and each major version is branded with a release
year. But then there are different languages, each
with their own runtimes and build tools, that you can use in conjunction with Visual
Studio. For C++ development, you use Microsoft Visual
C++ (MSVC) and it has its own versioning scheme separate
from the Visual Studio IDE. So we want to build our project with Visual
C++ 14.1, which means we want to install, at a minimum,
Visual Studio 2017. So how do we know which version we need? If we look up the release notes for the version
of Unreal that we're using, and we find the heading "IDE Version the Build
farm compiles against," we'll see what versions are officially supported. These may not be the latest versions, but
they're what Epic tested against for this release. It's not a hard requirement that we use these
exact versions, so if you already have newer versions installed
and everything works fine for you, you don't have to downgrade to an older version. But when you're working on a team, making
sure that everybody has the same version of the build tools is
a good way to avoid build problems. Since I'm setting up a new machine and I intend
to work with 4.25, I'm going to stick to these versions. So we'll want to find the installer for the
major product version of Visual Studio, which is 2017 in this case. The community edition is free and has everything
we need. The Visual Studio installer presents you with
a choice of workloads, which will install different sets of tools
based on the languages and platforms you'll be working with. For Unreal development, you need the "Desktop
development with C++" workload: this will install the traditional Visual C++
toolset, including the compiler, linker, and build
tools, the debugger, and the runtime and platform SDKs. It's also a good idea to grab the .NET workload
as well: a lot of Unreal's external tools are written
in C#, including the Unreal build system itself, so if you ever end up needing to build those
from source, the .NET tools will come in handy. The release notes mentioned a specific version
of the .NET Framework: we need this exact version of the targeting
pack in order to run any builds. We can find that version in the list of individual
components and make sure it's installed. We can also check to see how closely we're
hewing to our target version of Visual C++. According to the release notes, we want Visual
C++ version 14.16: we're at the same major and minor versions
here, so we should be good to go. We also need the Windows SDK, and the latest
version that this installer offers is older than our
target version. So we'll go with this one for now, but we'll
take the extra step of installing an updated Windows SDK once
this is done. Now we can start the install and let it run. When it's finished, we can boot up Visual
Studio. Great. As an extra step, I'm going to grab the latest
release of the Windows 10 SDK version that 4.25 was tested against. Note that Windows SDK releases are typically
pegged to Windows 10 OS Updates, so you may need to run Windows update before
installing a new SDK. And if you previously installed Visual Studio
without the required .NET targeting pack, you can download and install that separately
after the fact. So now we've got Unreal Engine installed, along with all the required tools to build
C++ projects from source. Let's take a look at the conventional, recommended
workflow for setting up a new project. When we first launch the engine, we'll be
prompted to create a new project. We can start from a template, but we'll just
make a blank project. We'll make sure we're using C++, we'll disable
starter content, and we'll just use the default name and path
for now. When we create the project, Unreal generates
a new project directory and populates it with a .uproject file as
well as Config, Content, and Source directories. It generates a Visual Studio solution for
us, and it begins building our project from source. When it's done, we end up with an editor DLL
for our project, and the editor then loads that module and
opens the project. This is a great way to get a new user up and
running: we just had to go through one straightforward,
user-friendly setup process, and now our project is fully up and running. Unreal Editor also has some integrated tools
for creating source files, which are similarly great for making the development
process discoverable to beginners. If we want to add a new class, we can choose
New C++ Class from the menu. Let's just make it an Actor class, and sure,
we'll just call it MyActor. Unreal will create the source files, update
our Visual Studio project, compile and reload the project DLL, and bring
Visual Studio to the foreground. So we've got some nice boilerplate here, with
handy comments so we'll never forget what "Tick" means, and with some kinda sloppy whitespace, and...
a... copyright notice, for some reason? And of course, we can build our project straight
from Visual Studio. By the way, at the top of the build output
here is where we can verify that Unreal's build system is using the expected
versions of the Visual C++ toolchain and the Windows SDK. So that's the officially recommended workflow. And it works! It's fine! It's meant to make this process accessible,
and it does a great job at that. But I want us to take a look behind the curtain to see how all these project files and build
processes relate to each other. So as an alternative to the beginner-friendly
approach, here's my new project setup workflow. First, let's get Sublime set up. Hitting Ctrl+Shift+P brings up the command
palette. We can run "Install Package Control" to get
a built-in package manager, which we can then use to install a few plugins. I recommend grabbing Project Manager and Switch
File Deluxe. I've created a package called Unreal Snippets
that I'd recommend as well: if you don't see it in Package Control, you
can run "Browse Packages" to open your package directory, then you can download the snippets from GitHub
and place them there. Now let's make a project. Hit Ctrl+Shift+P again, and use Project Manager
to add a new project. When it comes to naming your projects, my
advice is: don't overthink it. You don't need this name to be a part of your
public-facing brand, and it doesn't even need to describe the game
you're trying to make. I recommend using a single short codename. I'm just going to call my project "Dirk." Next we'll create a workspace directory: this will contain any Unreal projects we create,
along with any associated tools, scripts, assets, or other files. For me, this will be E:\hobby. We can use the command palette to add that
directory to our current project. So now let's make ourselves an Unreal project
from scratch. The first thing we need is a project directory. We need a .uproject file to define the basic
details about our project. We'll populate this file in a second. And we'll need a Source directory as well: this is where our project's C++ source code
lives. The project's codebase can be split into multiple
source modules. To start with, we'll just have a single module,
and we'll call it DirkCore, since it'll contain the core gameplay classes
for our Dirk project. So, back to the .uproject file. Since we've installed Unreal Snippets, we
can just type "uuproj" and hit Ctrl+Spacebar to populate the file. The one thing we need to fill in is the name
of our first source module, which we're calling DirkCore. Next we need to go to the root of our Source
directory, where we'll create a file called Dirk.Target.cs. We can add in the boilerplate here with "umt". This file is a target rules definition: it tells the Unreal Build System how to build
our project. The main thing we need to specify is which
source modules should be built for our target: so for right now, we'll add DirkCore to this
list. We also need another target rules file for
our Editor target. This'll be called DirkEditor.Target.cs, and
it's populated the same way, we just need to make sure the target type
is set to Editor. So the .uproject file tells Unreal Editor
that it can open this directory as an Unreal project using 4.25, and it additionally specifies that when the
Editor opens this project, it should find and load the compiled DLL for
the module called DirkCore. The .Target.cs file tells the Unreal Build
System that it can build all the necessary code for this project by building the DirkCore
module. So let's add the source for that module. It's a fairly common convention in Unreal
to split a module's Source into Public and Private subdirectories: Public contains the headers that need to be
visible to other modules, and Private contains implementation files
and any code that's internal to the module. We'll need one more C# file, this time in
the module directory itself: DirkCore.Build.cs. The snippet to use here is "umb". This is a module build rules file, and it
tells the Unreal Build System how to build this module for the target that it's currently
building. The main thing here is the list of other modules
that this module depends on: currently we have access to the core set of
Engine functionality defined in these three modules: Core, CoreUObject,
and Engine. If we look at the Engine's Source directory,
we can see that it's organized by module type. The Runtime modules represent all the first-party
engine code that can be built into your final game executable, leaving aside the editor and development tools. Within that directory, we can see all the
different runtime modules that make up the Engine. What our build rules file is essentially saying
is that DirkCore depends on these three modules: when we write code in DirkCore, we can use
any of the functionality that's defined in these modules. If we need to call functions from a different
module, then we'd need to add that module as a dependency. For example, if we were making a VR game and
we needed to access the HMD directly, we'd add the HeadMountedDisplay module to
our module's list of dependencies. So we've set up everything that the build
system needs: now we just need to write some code for it
to build. At the bare minimum, every module needs to
have a module definition, which is typically in a source file with the
same name as the module. We'll start by adding a public header called
DirkCore.h. To get the boilerplate, we can use "umh" here
- unreal-module-header. This defines the entry point for our module: Unreal will call StartupModule after loading
it, and it'll call ShutdownModule just before unloading it. Now we can make a corresponding DirkCore.cpp
file, and here we'll run "umcp" for unreal-module-cpp-primary. This registers our module implementation as
the primary module for our game. Each project needs exactly one module that's
designated as the primary game module. We'll also add a log category that's specific
to our module. It'll be internal to DirkCore, so we can add
Log.h and Log.cpp to our module's Private directory. "ulh" gives us the boilerplate for the header,
and "ulc" fills out the implementation. So now, from any .cpp file in our module,
we can include Log.h, and that'll let us add log output using the
"ull" snippet (unreal-log-line). This'll show up in the console output and
the log files for our game, and we can filter by our category (LogDirkCore)
to get output from this module specifically. So now that our primary module is defined,
we have a project that's ready to build from source. We've done all this without Visual Studio
or Unreal Editor: not because those are inherently bad tools, but just because this approach gives us a
chance to see what's happening under the hood. Unreal uses the Visual C++ compiler and linker, and it recommends Visual Studio as an officially
supported IDE, but it doesn't actually use Microsoft's build
system. Instead, Unreal has its own cross-platform
build system, which is why we need to write those C# files
to configure our target and its modules. When you generate Visual Studio project files, those solution and project files aren't actually
an essential part of the build: they're just a Visual-Studio-compatible frontend
for Unreal's build process. If we look at our Engine directory again,
under Build/BatchFiles, we can see a file called Build.bat. This basically just calls UnrealBuildTool.exe,
which invokes builds. When you build your project in Visual Studio,
it's essentially just running this Batch file. So let's leave Visual Studio out of the equation
and see how we can build our project directly. All we have to do is invoke Build.bat and
pass in a few things. We need to tell it which Target we want to
build, then we need to give it a platform and a build
configuration: our platform is going to be Win64 for editor
targets on Windows. For the build configuration, we'll use Development, which is sort of a middle ground between a
Debug build and a Shipping build. Then we just pass in the path to the .uproject
file, and we can kick off the build. Once the build gets going, we start getting
build artifacts in the Intermediate directory, starting with some files spit out by the build
system. Eventually, Unreal Header Tool parses our
source and spits out generated source files, then everything gets compiled to object code, and then finally our module is linked together
into a DLL that the editor can load. We can see that DLL in the Binaries directory: this file contains all the code we've written
in our DirkCore module, in a compiled binary form. Next to that is the corresponding PDB, or
symbol file: this essentially contains debugging information
so that symbols in the compiled binary version of our code can be traced back to the corresponding functions,
variables, and other identifiers in the source code. We also get a .modules file, which is just
a bit of metadata telling the editor which DLLs should be loaded for this module, and which Engine version those DLLs were compiled
against: this is why you'll get an error if you build
editor binaries on one version of Unreal, then try to open them with a different version. There's also a .target file, which is just
more metadata spit out by the build system. So now that we've built the one module that
our project needs to load, we can open the project in the editor. To do that, we just run UE4Editor.exe from
our engine installation, passing in the path to our .uproject file. Since this is our first time booting up the
project, we can see the Editor writing out some new
files in our project directory. Once the editor boots up, we're up and running. We can save a new map file in the Content
Browser, and let's make a quick adjustment just so
this map looks different from the template. Now if we look at our project settings, we
can generate a unique ID for the project, and we can also set our target hardware settings. We'll also change our default map to the one
we just saved. Let's look at our project directory now. We still have our .uproject file and our Source
directory, as well as the Binaries that we've built for
the project. We've now also got a Saved directory, which contains logs and other files created
at runtime, either by the game or the editor. We've got DerivedDataCache, where the editor
can cache data that it's built for imported assets, and we've got a Content directory, which maps
directly to our project's assets in the Content Browser. Finally there's the Config directory, where
our project configuration changes have been stored in .ini files. So that's the editor up and running. We've built editor binaries for our project, so we're able to load our project in a fully
playable and fully editable form using UE4Editor.exe. We can also launch a playable game instance
using editor binaries, without loading up the editor itself. To do that, we just pass in the -game flag
at launch. This is a fully playable instance of our game. We can fly around, we can use console commands,
and we can run "exit" when we're finished. So that's a quick way to test out the game
in a development setting: we're running from editor binaries, but that
means we need this full installation of Unreal Engine in order to run this build. If we want a standalone version of our project,
that's what our non-editor target is for. All we have to do is invoke the same build
process, passing in our project's Game target instead
of its Editor target. This'll run through the same build process,
but instead of linking our individual modules into DLL files, it'll link our project against all the required
Engine code and generate a single executable that runs our game, with all the editor-specific
functionality stripped out. You can see that our game executable is much
larger than our editor DLL, since it's a self-contained binary. But we can't run Dirk.exe just yet: first
we need to cook content. To make sense of the difference between cooked
and uncooked assets, let's use a texture file as an example. When we import an image file into the Editor,
we end up with a Texture2D asset, which is stored in our Content directory as
a .uasset file. This uncooked asset contains the original
uncompressed image data for our texture, along with all the flags and other property
values we've set after importing. In short, the uncooked asset is everything
the editor needs: it contains the original data in a complete
and nondestructive form. The final game, on the other hand, doesn't
need the uncompressed image data; it needs a compressed texture that it can
load straight into the GPU. This is the cooked form of the asset: it's
the same asset, but prebuilt for a specific platform, with
only the data that's necessary at runtime. So when we import this Targa, we get an uncooked
Texture2D asset that contains the original image data. When we cook the asset for Windows, we get a cooked asset file containing DXT-compressed
image data that the game can load at runtime. To cook data for our project, we can run the
command-line version of the editor, passing it our project file and telling it
to run the cook commandlet, with the target platform set to WindowsNoEditor. When that's done, we end up with a Saved/Cooked
directory: this is where our standalone game executable
will find all the assets it needs to load at runtime. So now we can run our game: this should be
functionally the same as running from editor binaries, except we're now using cooked data loaded
straight from disk: the engine isn't building any assets on demand
at runtime. We've still built in the Development configuration,
though, so we have access to all the same developer
tools. So, sure, we don't need to use Visual Studio
to build a project. That's cool and all, but having to invoke
this batch file with these specific arguments is kind of a pain in the ass. One quick fix is to make our own batch script
that invokes this build. If we just write this command into a file
called build.bat... then we can just enter "build" to build our
project. Batch files aren't really the state of the
art in build automation, but we're not doing anything fancy here, and
this approach works fine. We can make a few quick improvements, though. We can add "@echo off" to keep our commands
from being printed out. And then we can build these paths a little
more procedurally, if we want to. You might remember that "%~dp0" in a batch
script gives you the directory of the script file. You're probably not going to remember that
:~0,-1 will strip the trailing slash from that path, so you'll have to Google it like the rest
of us. But anyway, we can make the project name a
variable, then build up the path to our .uproject file, and then do some substitution in the command. We can do the same thing to construct the
path to Build.bat based on where our Engine version is installed. So now we have a script that calls Build.bat
for the Editor target of our project. And if we run build - it does the same thing. Cool. We can make a similar script called editor.bat
to run the editor. And instead of duplicating all these batch
variables, we'll just factor them into a file called
vars.bat. And then we can also build the path to UE4Editor.exe. So now editor.bat is largely the same as build.bat;
we just invoke a different command. With these two scripts, we can run "build"
to build our project, and we can run "editor" to launch the editor. Our editor script will also forward any additional
arguments to the editor on launch. What's great about using the command-line
is that we can easily chain multiple commands together. If we run "build && editor", that'll build,
and then if and only if the build succeeds, it'll launch the editor. This lets us jump back and forth between code
and editor really quickly. Let's add a new actor just to demonstrate
the workflow. We're going to add a new header file to our
DirkCore module and call it TestActor.h. The "uca" snippet will define an Actor class. We can use "upc" to add a component property: we'll make it a billboard component, which
just displays a sprite. We can use "upe" to add an editable property. This'll just be an arbitrary value so we have
something to test. Finally, we'll override BeginPlay, and we'll
declare a constructor. That's it for the header - now we can make
a corresponding .cpp file and include our header. I'm using Alt+O to switch between the header
and the source file. Let's grab these two functions and implement
them. We're going to construct a root SceneComponent
and a BillboardComponent, so let's add those to our engine includes. Then we'll use the ObjectInitializer to create
both components, with our billboard component attached to the
root. And then we can assign a default value to
our property. Finally, let's make the actor do something
at runtime. We're just going to have it write a line of
log output, so we'll include our module's private log header. Then in the body of BeginPlay, we can use
the "ull" snippet to add a log line. We'll write out the name of the actor, and
then the integer value of our property. So now that we're ready to test our changes,
here's our iteration loop: Ctrl+Tilde to bring up the console... Up arrow to retrieve the last command we ran... And Enter to run. Now, if there are no errors, the editor will
appear and we can test our changes. If the build fails, it'll halt instead. So here we have an error on line 16 of TestActor.cpp,
and if we go there... Hah, "sprote." ...we can fix our typo. And then same thing again: Ctrl+Tilde, Up,
Enter. This time, the build is good and we go straight
to the editor. We can place an instance of our actor, we can see that its Value property defaults
to 42, we can change the value on this instance, and if we run the game, we can see our line
in the log output. So that's my workflow. A lot of these choices come down to personal
preference. You don't have to use these exact tools if
something else works better for you. But I wanted to point out that there's more
than one way to do this part of the job, and more importantly, I thought it'd be a
good opportunity to look a bit deeper at how an Unreal project is structured. Before we wrap up with a quick review of the
build process, here are a few just workflow tips: I'd recommend setting up your Sublime project
with your project's Config and Source directories. If you hit Ctrl+P, you can quickly browse
to any file that's been included in the project. If we add UE4's Engine/Source/Runtime directory,
then we'll also have the Engine source at our fingertips. Since we've installed Switch File Deluxe,
we can use Alt+O to jump between header and source files. Highlighting a symbol and pressing F12 will
instantly jump to where that symbol is defined. The key distinction here is that where Visual
Studio tries to be smart, Sublime just tries to be fast. With Visual Studio, you're dealing with IntelliSense, which has to parse the entire Engine codebase
and run extensive static analysis, and then it has to reason about that codebase
while working around the fact that huge chunks of it rely on a custom code
generation process... and if you ask me, expecting it to be reliably
correct and responsive is just too much to ask. Back when I was using Visual Studio, I found
I couldn't rely on IntelliSense, and I'd end up just searching through the
API documentation all the time. But the code itself contains the exact same
documentation: you can cut out the middle-man and Ctrl+P
your way straight to an answer, faster than it takes for an IntelliSense completion
window to appear. This approach keeps you in the driver's seat. We can use Ctrl+Shift+F to search the entire
codebase to see how a symbol is used or referenced. And another good source of example code is
the Engine Plugins directory. If we limit our search there, we can find
usage examples that are set up in the same way our project code should be. I've found that if you treat the Engine source
as documentation in and of itself, and if you optimize your workflow for browsing
that source, you'll gain a much more intuitive understanding
of it over time. For example, if I can't remember exactly how
to update an actor's transform, I can just flip straight over to Actor.h and
find the function signature. Once I've done that a couple of times, I know
it by heart: I don't need to keep looking at documentation
or waiting on IntelliSense to tell me. So that concludes the opinion portion of our
program. Before we go, let's just briefly review what
we've accomplished here. We've installed Visual C++ as part of Visual
Studio, giving us a compiler and linker. We've also installed Unreal, giving us access
to all the Engine tools. Then we created a new project. If our project were Blueprint-only, then our
.uproject file wouldn't specify any additional modules, so we'd just be able to load it up in the
editor straightaway. However, when a project has its own source
modules, the editor needs to load the DLLs for those
modules in order to open the project. If we want to build those DLLs ourselves,
our project needs a source directory. Within the source directory, we can have one
or more modules, each with a Build.cs file along with its C++
source. The project itself needs a .Target.cs file
to tie all the modules together. When we build our project, UnrealBuildTool
looks at our target rules and figures out which modules it needs to
build from source. Then it goes about building those modules,
based on how their build rules are configured. First it invokes UnrealHeaderTool to parse
the headers and write out generated code. Then it runs the compiler for each translation
unit, giving us compiled object code for each .cpp file. Finally it links all of the module's code
together, resolving any cross-module references based
on the list of dependencies in the build rules. Once the build is done, we're left with the
editor binaries. Along with the .uproject file, this lets us
edit or play our project in the editor, using uncooked content. We can also use the editor, running from the
same binaries, to cook content. And once that's done, we're able to run our
standalone game. And that's it! Thanks for sticking with me here, and I hope
you learned something. If you know an Unreal developer who might
appreciate this video: please, give them a call, see how they're
doing, coming from a place of genuine curiosity and care, and then link them to the video. That would mean a lot to me. And if you, a decent human being, have any
questions for me, a decent human being: or if you'd like to discuss the points raised
in this video with other decent human beings like yourself... then please, feel free to comment below, like
a decent human being. Thanks for watching!
There arent a lot of UE C++ tutorials, thank you gor making this!
Ooo this seems interesting and useful to start learning how to use c++ in UE , thanks!
Very well made video, good job. I still prefer Visual studio, but i do agree that intellisense is slow in large code bases and sometimes even gives up and gives error notification about valid code. search function in engine takes ages. but on the other hand Visual studio has same great refactoring functions, rename all references, fill in switch with enum values and other features for non-UE4 development.
That was really good! Such an interesting and high quality video, I had to sub :)
Edit: One question though, when you install VS, if I remember correctly Unreal suggest to install the c++ game development framework not the desktop development one? Why is that, or am I misunderstanding something?
Excellent!
This is great, thank you for making it
Great video with high quality! I would love to see more of your work! Thanks for the video, man! I needed it!
Watching this video I got a very weird question: let alone the fact that it will be awfully unpractical and absolutely silly, could I make a project with Unreal in Windows and do the code part on Linux?
Linux is terrible to install Unreal Engine on, but Windows is horrid when it comes to working on code related projects. As I'm mostly developing, Linux has become my main OS, and I feel at home with all my IDEs installed and my compilers + toolchains ready without any hassle.
Even if that means doing cross-compiling and have a really rigorous and kind of restrictive workflow regarding making and testing, anyone think this is doable?