FAQ: C++ Programming

FAQ includes content originally written for UDN.

Core:

Is there a plan to support text based assets that will make it human readable & mergeable?

  • This is being investigated by the development team but there are still some challenges to resolve and there is no target date for such a feature…

What are the best practices to decide if code should be in the project, a plugin vs modifying the engine?

  • You should always strive to have your code in your project or in plugins. This facilitates upgrades to newer engine releases.
  • Plugins promote modular development and sharing with other teams\projects.
  • We are happy to discuss API changes that will allow you to implement what you need in your plugin\project. Examples are adding getter\setters for properties, making a method virtual…
  • It is usually easiest if you modify the engine API and can submit a Pull Request on Github in that effect. Please make sure that your change will not break legacy code and will also not be detrimental to performance.

What are the recommended Best Practices for using Derived Data Cache functionality?

  • Q2, 2020: DDC works best today when all workstations at a site are configured to use the same shared backend and have a low-latency, high-bandwidth connection to that backend. With that setup, it makes sense to use one or more dedicated machines to continuously populate the cache either by cooking for every target platform or by running the DerivedDataCache commandlet to fill the cache. Populating the cache by cooking currently performs much better than running the DDC commandlet to fill the cache for console data. The commandlet is still the best for editor data.
  • If it is common for developers to have more than one workspace on their workstation, it is best to share a local cache among those workspaces by setting the environment variable UE-LocalDataCachePath.
  • If VMs are used for builds and automation, and they have sufficiently fast access to the shared cache folder, a significant amount of storage space can be saved by disabling the local cache for those VMs by setting the environment variable UE-LocalDataCachePath to None.
  • The shared cache backend performs very poorly over high-latency connections, as might be the case if developers have their workstations at home and the shared cache is accessed over VPN. In these cases, performance is generally better if the shared cache is disabled by setting UE-SharedDataCachePath to None. Version 4.26 introduces support for a shared cache that lives on the Amazon clouds. It is possible to integrate support in earlier releases by following the instructions here.
  • For more details: Derived Data Cache | Unreal Engine Documentation

Code:

Are UE4 Enum labels expected to be reorderable?

  • Enums are serialized as FNames so that when their values change they get resolved to the new values on load. Many enums are runtime-only. A few read from ini files and a few have indices that need other code to be simultaneously modified.

Best Practice for recompiling code, Gameplay/Editor-code (LiveLink, Hot reload or Recompile module from editor)?

  • Hot reload is not used much internally, as it is not stable with some of the ways in which our projects are set up. That said, some studios and independent developers have had success using it. LiveCoding has become a very successful workflow for many engineers internally. Unlike hotreload, it currently has no awareness of UE reflection markup, so you can’t modify uclass/property/function markups or introduce new properties and functions and have it reflected in the running editor instance. However, for standard c++ code behaviors it is generally quite effective.

Could you explain the effects of unchecking Enable Actor Tick in class defaults and of unchecking Can Blueprints Tick by Default in Project settings?

  • The tooltip for Can Blueprints Tick by Default should explain the different scenarios. If this setting is disabled, the actor tick settings in class defaults won’t be relevant.
    • Blueprints that derive from native C++ classes that have bCanEverTick=true will always be able to tick
    • Blueprints that derive from exactly AActor or UActorComponent will always be able to tick
    • Otherwise, they can tick as long as the parent doesn’t have meta=(ChildCannotTick) and either bCanBlueprintsTickByDefault is true or the parent has meta=(ChildCanTick)
  • Put another way, a blueprint will tick if all the following are true:
    • The native parent class does not have the ChildCannotTick metadata
    • Either Blueprints Tick by Default is true, or the native parent class has the ChildCanTick metadata
    • Either Start with Tick Enabled is checked on the actor in class defaults, or tick is explicitly enabled
    • The blueprint implements the Tick event

Modules and Plugins

Note: Please also have a look at our companion article Modules - Overview and Structure which gives an overview of what modules are and how to work with them.

How do I find out which module a class/header belongs to?

Each module will have a certain folder structure that you can easily recognize from the full file path, notably the Private and Public (and for older modules also the Classes) folders. These folders will always be located in the root directory of a module.

Example for the IAssetRegistry Interface:

D:\UE\Release-4.27\Engine\Source\Runtime\AssetRegistry\Public\IAssetRegistry.h

In this case you can see that the IAssetRegistry.h header is located inside a Public directory that is inside the AssetRegistry Folder. Thus you can infer that you need to add a dependency to the AssetRegistry module if you want to access this header in your application or plugin.

A module or Plugin is not yet loaded when it tries to access it?

The module might have the wrong loading phase for your use-case:

When trying to access a module early in the engine startup it might not be loaded yet. An earlier loading phase (e.g. when implementing splash screens) can help.

I’m getting Undefined References or “No such File or Directory” errors when using classes or symbols from another module or the engine code.

Possible reasons include:

  • Missing module dependency: Check if the class/symbol that is creating the error is in an engine module that is not specified in your public or private dependencies.
    If the class/symbol is used in the public part, make sure the dependency is specified in the PublicDependencyModuleNames.
  • MODULENAME_API macro might be missing on the class/symbol you are trying to use. If this happens in an engine plugin or module, please report to Epic.
  • Class/Symbol/Header is not part of the public folder. In this case the only way to access the classes is if they are properly exported and part of the public API.
    Some Engine classes will not export specific classes (like UAssetRegistryImpl) and only export an interface instead (IAssetRegistry)
  • An additional game module is not built:
    Make sure the game module is listed both in the .uproject files and in the correct .Target.cs or Editor.Target.cs

Gameplay Ability System:

What would be a reasonable way to interface the gameplay ability system with weapon states?

  • In Fortnite, when a weapon is equipped we check the weapon being unequipped and remove any abilities or Gameplay Effects that were granted by the weapon. Then we apply any Gameplay Effects for the current weapon (ex. a movement speed debuff for very heavy weapons) and grant the player abilities such as primary and secondary fire, reload, etc.
  • Our base weapon class has a pair of virtual functions called PressTrigger and ReleaseTrigger. These are used to support various weapon firing types (single shot, burst, auto, charged shots, etc). When the weapon is actually fired we activate either the primary or secondary fire ability on the instigating actor. Hits are determined by the weapon, or projectile, and then passed to the damage ability as targets. We override the functions related to ability costs to support using various types of ammunition when we fire. Because firing happens so often we don’t use gameplay cues for things like muzzle flash and hit FX. Instead these are all triggered from the base weapon class with weapon specific data to modify the visual and audio output.

What is the roadmap for the gameplay ability system?

  • The ability system was written with Paragon and Fortnite as our test cases. It is also used by other internal projects. You can ignore the warnings about it being experimental. We’re well past the stage of making breaking changes. We don’t have a set roadmap for the ability system. The most recent updates were improved documentation along with the ARPG sample and improvements to gameplay cues. I’d guess that the next significant area we’d improve is providing an example and better documentation for using it in a networked environment. There are still plenty of rough edges but so far none of them have been important enough to get to the top of our priority stack.

Is getting damage amounts on the client using gameplay cues the correct approach?

  • No, Gameplay Cues are not guaranteed to reach the clients and may be dropped if network traffic is heavy. You can use something like PostAttributeChanged to update the clients.

When should Gameplay Cues be used?

  • Gameplay Cues should only be used for non critical gameplay events or effects. They should be used for things where it’s okay if they don’t get replicated to other clients because they are not guaranteed and may be dropped depending on network traffic. You also need to be mindful about RPC limits when using Gameplay Cues. For example, we don’t use Gameplay Cues for weapon effects in Fortnite because they are too frequent and would create too much networking overhead.

Can you execute gameplay cues via the Cue Manager directly?

  • You can, but the biggest advantage of using gameplay cues is separation of the cue and its execution. You should use tags to trigger cues.

Is using inheritance in the Abilities System a good pattern in terms of reusability?

  • It’s similar to other code and depends on the game. We use a mix of inheritance and composition.

How to handle death in the Abilities System?

  • One approach is to use attribute set data to trigger abilities and apply tags. For example, you could have a health attribute that is used to determine if death should occur on PostGameplayEffectExecute.

How do you build generic relationships/systems using tags?

  • Use a combination of tag layout and tag queries. You should have someone own the tag structures at a high level to protect the tag layout.

What do you think about adding tags vs using Gameplay Effects?

  • Gameplay Effects generally modify attributes and add/remove tags. They handle replication and prediction/rollback.

Should we use Gameplay Effects to define states?

  • No, you should use tags to represent state and Gameplay Effects to modify attributes and add/remove tags.

Is there a good way to make reusable GameplayCues? For example, a GameplayCue that plays a ParticleEffect and a Sound. But each ability that plays the cue wants to play different effects. Do we need to make a new GameplayCue each time, or can we set something up to share code?

  • There’s not a built in way to do this. You could create a more general Gameplay Cue that considers subtags to allow some flexibility, but you’ll need to maintain a mapping of tags to Effects to play.

What is the best way to pass parameters to a GameplayCue?

  • You can derive from FGameplayCueParameters to pass in more parameters. This struct is intended to be extended for project specific needs.

What is the best way to replicate data generated on the server-side of an ability to the client-side of an ability?

  • Gameplay abilities aren’t generally replicated. You could have the client kick off an effect that triggers the server to initiate a separate gameplay effect(s) that would be triggered on the server and the client.

What is the best way to coordinate random number generation between a local client and server ability?

  • You could use the prediction key as a seed, but it’s just an incrementing int. Could also replicate GamePlayEventData, or override GameplayAbilitySpec.

How would you build a combo ability where local player input can advance the combo?

  • Two approaches to consider would be to either apply multiple tags with different durations to define the combo window or use notifications in the montage to tell us when to add or remove the tag that says we can do the next step of the combo.

What is the best way to implement root motion in an ability? Should we be using a montage that has root motion, AbilityTasks that use root motion, or something else?

  • Here’s a knowledge base article that includes some detailed info.
  • You can use animation root motion by playing montages through the AbilitySystem and it’ll be synchronized between client and server. This is typically how projects start and is a more familiar workflow to animators/gameplay people. Totally fine to do that.
  • If you want more gameplay programmer/design control over the root motion, they can trigger Root Motion Sources (basically custom root motion determined by C++ code) through the existing “ApplyRootMotion*” AbilityTask nodes and it’ll be handled for you. There’s several provided Root Motion Sources that are just about all the ones we used on Paragon except for a couple one-off special/custom ones.

How would you recommend setting up ability animations for a character that uses a FirstPerson mesh (arms only) on the local client and a ThirdPerson mesh (full body) everywhere else?

  • There are several ways this could be approached. Abilities could emit properties, and 2 different AnimGraphs can read from and trigger animations (1st person, and 3rd person). Or you could emit animation data that’s dynamically linked into AnimBPs. (it could be LinkedAnimBPs, or Montages). If you share the same skeleton, you could even emit a single Montage asset that contains 2 tracks: 1 for the first person anim, one for the third person anim.

When changing an ability’s input (FGameplayAbilitySpec::InputID) on the server and marking it as dirty through the AbilitySpec, it doesn’t replicate the input change down to the client if the ability is marked as a server only ability. How should this be handled?

  • We handle this type of case by granting and removing abilities when an item is equipped or unequipped. For slottable items we have some indirection so we tell slot X that the player has used whatever input is associated with it and slot X is responsible for passing that info to the equipped item.

What kind of data should I expect to be replicated about an enemy’s abilities. For example, if swinging a sword at an enemy and they have an “evasive” ability can I check a tag, or ability?

  • You can check the tag. PreGameplayEffectExecute() is a good place for that because it returns a bool that says, “Did this even work at all?”. So for evasion, you might want to have the whole thing fail right there.

When dealing damage, is the client simulating that and able to run with it and will the server correct them, or is it the case that you wait for the server to come back with the results?

  • The client will move ahead with results on gameplay effects. We have similar rollback there as we have with the abilities as far as (client)prediction goes. It’s linear and keeps track. The server will tell the client, “I’m up to number x” and the client will kill everything up to that point because the server will replicate everything down.

I noticed that clients never enter the PostGameplayEffectExecute() function, it only fires on the server. Is that expected?

  • If you are locally predicting an instant gameplay effect, we automatically make the duration infinite. A duration based event will go into the aggregator and will never finish executing on the client. The server will do the correct thing and execute it as an instant effect and replicate it down. That’s how we do the server rollback. Use PreAttributeChange() to update clients on locally predicted effects instead of PostGameplayEffectExecute(). Using a RepNotifiy is also okay.

What is the difference between executions and modifiers in the context of deductions?

  • They both do deductions. The difference is that executions have to have code to back them up. Generally modifiers are preferred, executions were added because the modifier framework couldn’t support Fortnite’s complex damage formula. Modifiers are preferred because it’s less code to maintain and it’s clear how they are applied.

What is the difference between DOREPLIFTIME and DOREPLIFETIME_CONDITION_NOTIFY and when should one use DOREPLIFETIME_CONDITION_NOTIFY with prediction?

  • DOREPLIFETIME will only replicate the value if it has changed since the last replication. DOREPLIFETIME_CONDITION_NOTIFY with a condition of COND_None will always replicate the value.

See more about C++ Programming on the Knowledge Base.

2 Likes