Kleptine / funomena-ue4-style-guide

An attempt to make Unreal Engine 4 projects more consistent

Home Page:http://ue4.style

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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."

Rebecca Murphey

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
  2. Directory Structure
  3. Blueprints
  4. Static Meshes
  5. Particle Systems
  6. Levels / Maps
  7. Textures

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.

1.1 Base Asset Name - BaseAssetName_Variant_Suffix

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.

2.6.1 Creating a folder named Assets is redundant. #

All assets are assets.

2.6.2 Creating a folder named Meshes, Textures, or Materials is redundant. #

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:

  1. Be sure you're using source control.
  2. Immediately run Fix Up Redirectors on your project.
  3. Navigate to the folder on-disk and delete the assets inside.
  4. Close the editor.
  5. Make sure your source control state is in sync (i.e. if using Perforce, run a Reconcile Offline Work on your content directory)
  6. Open the editor. Confirm everything still works as expected. If it doesn't, revert, figure out what went wrong, and try again.
  7. Ensure the folder is now gone.
  8. Submit changes to source control.

⬆ Back to Top

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

3.2.1.3 Boolean b Prefix #

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, not Text.

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:

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?

3.3.1.4 Event Handlers Should Generally Start With On #

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 games
  • OnPickup
  • 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. Aligned By Wires

Bad Example: The tops of the nodes are aligned creating a wiggly white exec line. Bad

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. Acceptable

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.

⬆ Back to Top

Contributors

License

Copyright (c) 2016 Gamemakin LLC

See LICENSE

⬆ Back to Top

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.

About

An attempt to make Unreal Engine 4 projects more consistent

http://ue4.style

License:MIT License