Funomena Blueprints and UE4 Style Guide
A mostly reasonable approach to Unreal Engine 4
Forked from the Allar UE4 Style Guide. Heavily inspired by the Airbnb Javascript Style Guide.
Linking To This Document
Every section of this style guide is numbered for both easy reference and easy linking. You can link to any section directly by simply append a hash tag and the section number to the end of http://ue4.style
For example, if you want to send someone to the first principle of this style guide you would append #0.1
, resulting in http://ue4.style#0.1.
Important Terminology
Cases
There are a few different ways you can name things. Here are some common casing types:
PascalCase
Capitalize every word and remove all spaces, e.g.
DesertEagle
,StyleGuide
,ASeriesOfWords
.camelCase
The first letter is always lowercase but every following word starts with uppercase, e.g.
desertEagle
,styleGuide
,aSeriesOfWords
.Snake_case
Words can arbitrarily start upper or lowercase but words are separated by an underscore, e.g.
desert_Eagle
,Style_Guide
,a_Series_of_Words
.
Variables / Properties
The words 'variable' and 'property' in most contexts are interchangable. If they are both used together in the same context however:
Property
Usually refers to a variable defined in a class. For example, if BP_Barrel
had a variable bExploded
, bExploded
may be referred to as a property of BP_Barrel
.
When in the context of a class, often used to imply accessing previously defined data.
Variable
Usually refers to a variable defined as a function argument or a local variable inside a function.
When in the context of a class, often used to convey discussion about its definition and what it will hold.
0. Principles
These principles have been adapted from idomatic.js style guide.
0.1 If your UE4 project already has a style guide, you should follow it.
If you are working on a project or with a team that has a pre-existing style guide, it should be respected. Any inconsistency between an existing style guide and this guide should defer to the existing.
Style guides should be living documents however and you should propose style guide changes to an existing style guide as well as this guide if you feel the change benefits all usages.
"Arguments over style are pointless. There should be a style guide, and you should follow it."
0.2 All structure, assets, and code in any Unreal Engine 4 project should look like a single person created it, no matter how many people contributed.
Moving from one project to another should not cause a re-learning of style and structure. Conforming to a style guide removes unneeded guesswork and ambiguities.
It also allows for more productive creation and maintenance as one does not need to think about style, simply follow instructions. This style guide is written with best practices in mind, meaning that by following this style guide you will also minimize hard to track issues.
0.3 Friends do not let friends have bad style.
If you see someone working either against a style guide or no style guide, try to correct them.
When working within a team or discussing within a community such as Unreal Slackers, it is far easier to help and to ask for help when people are consistent. Nobody likes to help untangle someone's Blueprint spaghetti or deal with assets with names they can't understand.
If you are helping someone who's work conforms to a different but consistent and sane style guide, you should be able to adapt to it. If they do not conform to any style guide, please direct them here.
0.4 A team without a style guide is no team of mine.
When joining an Unreal Engine 4 team one of your first questions should be "Do you have a style guide?". If the answer is no, you should be skeptical about their ability to work as a team.
0.5 Don't Break The Law
Gamemakin LLC is not a lawyer, but please don't introduce illegal actions and behavior to a project, including but not limited to:
- Don't distribute content you don't have the rights to distribute
- Don't infringe on someone else's copyrighted or trademark material
- Don't steal content
- Follow licensing restrictions on content, e.g. attribute when attributions are needed
Table of Contents
1. Asset Naming Conventions
Naming conventions should be treated as law. A project that conforms to a naming convention is able to have its assets managed, searched, parsed, and maintained with incredible ease.
Most things are prefixed with prefixes being generally an acronym of the asset type followed by an underscore.
BaseAssetName_Variant_Suffix
1.1 Base Asset Name - Please take a look at the document: Calvarium Style Guide
2. Folder and Asset Structures
2.1 No Global Assets
Often in code style guides it is written that you should not pollute the global namespace and this follows the same principle. When assets are allowed to exist outside of a project folder it often becomes much harder to enforce a strict structure layout as assets not in a folder encourages the bad behavior of not having to organize assets.
Every asset should have a purpose, otherwise it does not belong in a project. If an asset is an experimental test and shouldn't be used by the project it should be put in a Developer
folder.
2.3 Use Developers Folder For Local Testing
During a project's development, it is very common for team members to have a sort of 'sandbox' where they can experiment freely without risking the core project. Because this work may be ongoing, these team members may wish to put their assets on a project's source control server. Not all teams require use of Developer folders, but ones that do use them often run into a common problem with assets submitted to source control.
It is very easy for a team member to accidentally use assets that are not ready for use which will cause issues once those assets are removed. For example, an artist may be iterating on a modular set of static meshes and still working on getting their sizing and grid snapping correct. If a world builder sees these assets in the main project folder, they might use them all over a level not knowing they could be subject to incredible change and/or removal. This causes massive amounts of re-working by everyone on the team to resolve.
If these modular assets were placed in a Developer folder, the world builder should never of had a reason to use them and the whole issue would never happen. The Content Browser has specific View Options that will hide Developer folders (they are hidden by default) making it impossible to accidentally use Developer assets under normal use.
Once the assets are ready for use, an artist simply has to move the assets into the project specific folder and fix up redirectors. This is essentially 'promoting' the assets from experimental to production.
Assets
is redundant.
2.6.1 Creating a folder named All assets are assets.
Meshes
, Textures
, or Materials
is redundant.
2.6.2 Creating a folder named All asset names are named with their asset type in mind. These folders offer only redundant information and the use of these folders can easily be replaced with the robust and easy to use filtering system the Content Browser provides.
2.7 Very Large Asset Sets Get Their Own Folder Layout
This can be seen as a pseudo-exception to 2.6.
There are certain asset types that have a huge volume of related files where each asset has a unique purpose. The two most common are Animation and Audio assets. If you find yourself having 15+ of these assets that belong together, they should be together.
For example, animations that are shared across multiple characters should lay in Characters/Common/Animations
and may have sub-folders such as Locomotion
or Cinematic
.
This does not apply to assets like textures and materials. It is common for a
Rocks
folder to have a large amount of textures if there are a large amount of rocks, however these textures are generally only related to a few specific rocks and should be named appropriately. Even if these textures are part of a Material Library.
2.9 No Empty Folders
There simply shouldn't be any empty folders. They clutter the content browser.
If you find that the content browser has an empty folder you can't delete, you should perform the following:
- Be sure you're using source control.
- Immediately run Fix Up Redirectors on your project.
- Navigate to the folder on-disk and delete the assets inside.
- Close the editor.
- Make sure your source control state is in sync (i.e. if using Perforce, run a Reconcile Offline Work on your content directory)
- Open the editor. Confirm everything still works as expected. If it doesn't, revert, figure out what went wrong, and try again.
- Ensure the folder is now gone.
- Submit changes to source control.
3. Blueprints
This section will focus on Blueprint classes and their internals. When possible, style rules conform to Epic's Coding Standard.
Remember: Blueprinting badly bears blunders, beware! (Phrase by KorkuVeren)
Sections
3.1 Compiling
3.2 Variables
3.3 Functions
3.4 Graphs
3.1 Compiling
All blueprints should compile with zero warnings and zero errors. You should fix blueprint warnings and errors immediately as they can quickly cascade into very scary unexpected behavior.
Do not submit broken blueprints to source control. If you must store them on source control, shelve them instead.
Broken blueprints can cause problems that manifest in other ways, such as broken references, unexpected behavior, cooking failures, and frequent unneeded recompilation. A broken blueprint has the power to break your entire game.
3.2 Variables
The words variable
and property
may be used interchangably.
Sections
3.2.1 Naming
3.2.2 Editable
3.2.3 Categories
3.2.4 Access
3.2.5 Advanced
3.2.6 Transient
3.2.7 Config
3.2.1 Naming
3.2.1.1 Nouns
All non-boolean variable names must be clear, unambiguous, and descriptive nouns.
3.2.1.2 PascalCase
All non-boolean variables should be in the form of PascalCase.
3.2.1.2e Examples:
Score
Kills
TargetPlayer
Range
CrosshairColor
AbilityID
b
Prefix
3.2.1.3 Boolean All booleans should be named in PascalCase but prefixed with a lowercase b
.
Example: Use bDead
and bEvil
, not Dead
and Evil
.
UE4 Blueprint editors know not to include the b
in user-friendly displays of the variable.
3.2.1.4 Boolean Names
3.2.1.4.1 General And Independent State Information
All booleans should be named as descriptive adjectives when possible if representing general information. Do not include words that phrase the variable as a question, such as Is
. This is reserved for functions.
Example: Use bDead
and bHostile
not bIsDead
and bIsHostile
.
Try to not use verbs such as bRunning
. Verbs tend to lead to complex states.
3.2.1.4.2 Complex States
Do not to use booleans to represent complex and/or dependent states. This makes state adding and removing complex and no longer easily readable. Use an enumeration instead.
Example: When defining a weapon, do not use bReloading
and bEquipping
if a weapon can't be both reloading and equipping. Define an enumeration named EWeaponState
and use a variable with this type named WeaponState
instead. This makes it far easier to add new states to weapons.
Example: Do not use bRunning
if you also need bWalking
or bSprinting
. This should be defined as an enumeration with clearly defined state names.
3.2.1.5 Considered Context
All variable names must not be redundant with their context as all variable references in Blueprint will always have context.
3.2.1.5e Examples:
Consider a Blueprint called BP_PlayerCharacter
.
Bad
PlayerScore
PlayerKills
MyTargetPlayer
MyCharacterName
CharacterSkills
ChosenCharacterSkin
All of these variables are named redundantly. It is implied that the variable is representative of the BP_PlayerCharacter
it belongs to because it is BP_PlayerCharacter
that is defining these variables.
Good
Score
Kills
TargetPlayer
Name
Skills
Skin
3.2.1.6 Do Not Include Atomic Type Names
Atomic or primitive variables are variables that represent data in their simplest form, such as booleans, integers, floats, and enumerations.
Strings and vectors are considered atomic in terms of style when working with Blueprints, however they are technically not atomic.
While vectors consist of three floats, vectors are often able to be manipulated as a whole, same with rotators.
Do not consider Text variables as atomic, they are secretly hiding localization functionality. The atomic type of a string of characters is
String
, notText
.
Atomic variables should not have their type name in their name.
Example: Use Score
, Kills
, and Description
not ScoreFloat
, FloatKills
, DescriptionString
.
The only exception to this rule is when a variable represents 'a number of' something to be counted and when using a name without a variable type is not easy to read.
Example: A fence generator needs to generate X number of posts. Store X in NumPosts
or PostsCount
instead of Posts
as Posts
may potentially read as an Array of a variable type named Post
.
3.2.1.7 Do Include Non-Atomic Type Names
Non-atomic or complex variables are variables that represent data as a collection of atomic variables. Structs, Classes, Interfaces, and primitives with hidden behavior such as Text
and Name
all qualify under this rule.
While an Array of an atomic variable type is a list of variables, Arrays do not change the 'atomicness' of a variable type.
These variables should include their type name while still considering their context.
If a class owns an instance of a complex variable, i.e. if a BP_PlayerCharacter
owns a BP_Hat
, it should be stored as the variable type as without any name modifications.
Example: Use Hat
, Flag
, and Ability
not MyHat
, MyFlag
, and PlayerAbility
.
If a class does not own the value a complex variable represents, you should use a noun along with the variable type.
Example: If a BP_Turret
has the ability to target a BP_PlayerCharacter
, it should store its target as TargetPlayer
as when in the context of BP_Turret
it should be clear that it is a reference to another complex variable type that it does not own.
3.2.1.8 Arrays
Arrays follow the same naming rules as above, but should be named as a plural noun.
Example: Use Targets
, Hats
, and EnemyPlayers
, not TargetList
, HatArray
, EnemyPlayerArray
.
3.2.2 Editable Variables
All variables that are safe to change the value of in order to configure behavior of a blueprint should be marked as Editable
.
Conversely, all variables that are not safe to change or should not be exposed to designers should not be marked as editable, unless for engineering reasons the variable must be marked as Expose On Spawn
.
Do not arbitrarily mark variables as Editable
.
3.2.2.1 Tooltips
All Editable
variables, including those marked editable just so they can be marked as Expose On Spawn
, should have a description in their Tooltip
fields that explains how changing this value affects the behavior of the blueprint.
3.2.2.2 Slider And Value Ranges
All Editable
variables should make use of slider and value ranges if there is ever a value that a variable should not be set to.
Example: A blueprint that generates fence posts might have an editable variable named PostsCount
and a value of -1 would not make any sense. Use the range fields to mark 0 as a minimum.
If an editable variable is used in a Construction Script, it should have a reasonable Slider Range defined so that someone can not accidentally assign it a large value that could crash the editor.
A Value Range only needs to be defined if the bounds of a value are known. While a Slider Range prevents accidental large number inputs, an undefined Value Range allows a user to specify a value outside the Slider Range that may be considered 'dangerous' but still valid.
3.2.3 Categories
If a class has only a small number of variables, categories are not required.
If a class has a moderate amount of variables (5-10), all Editable
variables should have a non-default category assigned. A common category is Config
.
If a class has a large amount of variables, all Editable
variables should be categorized into sub-categories using the category Config
as the base category. Non-editable variables should be categorized into descriptive categories describing their usage.
You can define sub-categories by using the pipe character
|
, i.e.Config | Animations
.
Example: A weapon class set of variables might be organized as:
|-- Config
| |-- Animations
| |-- Effects
| |-- Audio
| |-- Recoil
| |-- Timings
|-- Animations
|-- State
|-- Visuals
3.2.5 Advanced Display
If a variable should be editable but often untouched, mark it as Advanced Display
. This makes the variable hidden unless the advanced display arrow is clicked.
To find the Advanced Display
option, it is listed as an advanced displayed variable in the variable details list.
3.2.8 Config Variables
Do not use the Config Variable
flag. This makes it harder for designers to control blueprint behavior. Config variables should only be used in C++ for rarely changed variables. Think of them as Advanced Advanced Display
variables.
3.3 Functions, Events, and Event Dispatchers
This section describes how you should author functions, events, and event dispatchers. Everything that applies to functions also applies to events, unless otherwise noted.
3.3.1 Function Naming
The naming of functions, events, and event dispatchers is critically important. Based on the name alone, certain assumptions can be made about functions. For example:
- Is it a pure function?
- Is it fetching state information?
- Is it a handler?
- Is it an RPC?
- What is its purpose?
These questions and more can all be answered when functions are named appropriately.
3.3.1.1 All Functions Should Be Verbs
All functions and events perform some form of action, whether its getting info, calculating data, or causing something to explode. Therefore, all functions should all start with verbs. They should be worded in the present tense whenever possible. They should also have some context as to what they are doing.
Good examples:
Fire
- Good example if in a Character / Weapon class, as it has context. Bad if in a Barrel / Grass / any ambiguous class.Jump
- Good example if in a Character class, otherwise, needs context.Explode
ReceiveMessage
SortPlayerArray
GetArmOffset
GetCoordinates
UpdateTransforms
EnableBigHeadMode
IsEnemy
- "Is" is a verb.
Bad examples:
Dead
- Is Dead? Will deaden?Rock
ProcessData
- Ambiguous, these words mean nothing.PlayerState
- Nouns are ambiguous.Color
- Verb with no context, or ambiguous noun.
3.3.1.3 Info Functions Returning Bool Should Ask Questions
When writing a function that does not change the state of or modify any object and is purely for getting information, state, or computing a yes/no value, it should ask a question. This should also follow the verb rule.
This is extremely important as if a question is not asked, it may be assumed that the function performs an action and is returning whether that action succeeded.
Good examples:
IsDead
IsOnFire
IsAlive
IsSpeaking
IsHavingAnExistentialCrisis
IsVisible
HasWeapon
- "Has" is a verb.WasCharging
- "Was" is past-tense of "be". Use "was" when referring to 'previous frame' or 'previous state'.CanReload
- "Can" is a verb.
Bad examples:
Fire
- Is on fire? Will fire? Do fire?OnFire
- Can be confused with event dispatcher for firing.Dead
- Is dead? Will deaden?Visibility
- Is visible? Set visibility? A description of flying conditions?
On
3.3.1.4 Event Handlers Should Generally Start With Event handlers should begin with On
and continue to follow the verb rule. The verb may move to the end however if past-tense reads better. Use your own discretion on this rule, but try to keep to it as closely as possible.
Collocations of the word On
are exempt from following the verb rule.
Handle
is not allowed. It is 'Unreal' to use On
instead of Handle
, while other frameworks may prefer to use Handle
instead of On
.
Good examples:
OnDeath
- Common collocation in gamesOnPickup
OnReceiveMessage
OnMessageRecieved
OnTargetChanged
OnClick
OnLeave
Bad examples:
OnData
OnTarget
HandleMessage
HandleDeath
3.3.3 No Function Should Have More Than 50 Nodes
Simply, no function should have more than 50 nodes. Any function this big should be broken down into smaller functions for readability and ease of maintenance.
The following nodes are not counted as they are deemed to not increase function complexity:
- Comment
- Route
- Cast
- Getting a Variable
- Breaking a Struct
- Function Entry
- Self
3.4 Blueprint Graphs
This section covers things that apply to all Blueprint graphs.
3.4.1 No Spaghetti
Wires should have clear beginnings and ends. You should never have to mentally untangle wires to make sense of a graph. Many of the following sections are dedicated to reducing spaghetti.
3.4.2 Align Wires Not Nodes
Always align wires, not nodes. You can't always control the size and pin location on a node, but you can always control the location of a node and thus control the wires. Straight wires provide clear linear flow. Wiggly wires wear wits wickedly. You can straighten wires by using the Straigten Connections command with BP nodes selected. Hotkey: Q
Good example: The tops of the nodes are staggered to keep a perfectly straight white exec line.
Bad Example: The tops of the nodes are aligned creating a wiggly white exec line.
Acceptable Example: Certain nodes might not cooperate no matter how you use the alignment tools. In this situation, try to minimize the wiggle by bringing the node in closer.
3.4.3 White Exec Lines Are Top Priority
If you ever have to decide between straightening a linear white exec line or straightening data lines of some kind, always straighten the white exec line.
3.4.4 Graphs Should Be Reasonably Commented
Blocks of nodes should be wrapped in comments that describe their higher-level behavior. While every function should be well named so that each individual node is easily readable and understandable, groups of nodes contributing to a purpose should have their purpose described in a comment block. If a function does not have many blocks of nodes and it's clear that the nodes are serving a direct purpose in the function's goal, then they do not need to be commented as the function name and description should suffice.
3.4.5 Graphs Should Handle Casting Errors Where Appropriate
If a function or event assumes that a cast always succeeds, it should appropriately report a failure in logic if the cast fails. This lets others know why something that is 'supposed to work' doesn't. A function should also attempt a graceful recover after a failed cast if its known that the reference being casted could ever fail to be casted.
This does not mean every cast node should have its failure handled. In many cases, especially events regarding things like collisions, it is expected that execution flow terminates on a failed cast quietly.
3.4.6 Graphs Should Not Have Any Dangling / Loose / Dead Nodes
All nodes in all blueprint graphs must have a purpose. You should not leave dangling blueprint nodes around that have no purpose or are not executed.
Contributors
License
Copyright (c) 2016 Gamemakin LLC
See LICENSE
Unreal Engine 4 Linter Plugin
An automated method of checking your project against this style guide is available for purchase at the Unreal Engine marketplace. This plugin's source code will eventually be free, but in order to use with UE4 without building the engine from source code, please use the marketplace version.
Amendments
We encourage you to fork this guide and change the rules to fit your team's style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts.