What is a Buildable Hologram?

Buildable Holograms control the logic of what happens when constructing buildings and where players can place them.

Each FGBuildable needs a hologram specified to be constructed. Most of the time, you can re-use an existing hologram class made by Coffee Stain, since they have a lot of the common use cases covered already. However, you may want to write your own logic for additional functionality, such as when you want to upgrade a building, snap to other buildings, or to snap something in the environment.

Writing your own Hologram logic requires writing some C++ code. This page assumes basic knowledge of C++.

Create your Own Hologram Class

First, you’ll have to create a class that extends FGBuildableHologram, or one of its subclasses, such as FGFactoryBuildingHologram (which implements Zooping) or FGSplineHologram (used for pipes and belts), if you want to re-use some of their functionality.

Most base-game hologram headers can be found in Source/FactoryGame/Public/Hologram/.

This docs page assumes your hologram class is called AMyModHologram.

Build Modes

Build Modes are the different options users can select from while constructing a building. Examples include the Noodle and Vertical build modes for pipes and hypertubes, and the default or zooping modes for walls and foundations.

Holograms define which modes are supported for a building through its implementation of GetSupportedBuildModes.

Here are some C++ headers relevant to Build Modes from FGHologram.h:

/**
* Get the build modes implemented for the hologram
* @param out_buildmodes	 Array with all supported build modes
*/
UFUNCTION( BlueprintNativeEvent, Category = "Hologram" )
void GetSupportedBuildModes( TArray< TSubclassOf<UFGHologramBuildModeDescriptor> >& out_buildmodes ) const;

UFUNCTION( BlueprintPure, Category = "Hologram" )
TSubclassOf<UFGHologramBuildModeDescriptor> GetCurrentBuildMode();

UFUNCTION( BlueprintCallable, Category = "Hologram" )
void SetBuildMode( TSubclassOf<UFGHologramBuildModeDescriptor> mode );

UFUNCTION( BlueprintCallable, Category = "Hologram" )
void CycleBuildMode( int32 deltaIndex );

UFUNCTION( BlueprintPure, Category = "Hologram" )
bool IsCurrentBuildMode( TSubclassOf<UFGHologramBuildModeDescriptor> buildMode ) const;

virtual void OnBuildModeChanged();

In the below example, we have our custom hologram add an additional build mode mNewBuildmode to the supported build modes it inherited from its superclass.

void AMyModHologram::GetSupportedBuildModes_Implementation(TArray<TSubclassOf<UFGHologramBuildModeDescriptor>>& out_buildmodes) const
{
	Super::GetSupportedBuildModes_Implementation(out_buildmodes);

	if(mNewBuildmode)
		out_buildmodes.AddUnique(mNewBuildmode);
}

Don’t add the same BuildMode class twice, and don’t add any invalid BuildModes! That’s why the example includes the null check before adding an item to out_buildmodes, and why AddUnique is used.

BuildModes are usually changed by the user pressing the keybinding when the buildgun is out (default R). However, we can also do this programmatically via SetBuildMode(MyBuildMode) or CycleBuildMode(1) (next) / CycleBuildMode(-1) (previous).

We may want to change our hologram’s behavior depending on which build mode is active. There are a few ways to handle this.

The first is to have the function IsCurrentBuildMode(MyBuildMode), which returns true if the current build mode is MyBuildMode. We can also get the active build mode via GetCurrentBuildMode() and compare it using equality checks or some other logic. In addition, we can also perform actions when the build mode has been changed.

For example, the below snippet uses OnBuildModeChanged() to set MyProperty in response to a changed build mode.

void AMyModHologram::OnBuildModeChanged()
{
    Super::OnBuildModeChanged();

    MyProperty = IsCurrentBuildMode(MyBuildMode);
}

If you want to re-use the build mode of an existing base-game buildable, it’s usually found as a field in that buildable’s hologram. In this case, your hologram should probably inherit from the class that owns that build mode. Otherwise, you can read the reference from the class' CDO at hologram BeginPlay/Construction.

Configuring a Buildable

Holograms also allow us to supply values or perform changes to buildings as they are built. This allows, for example, changing a mesh depending on the building’s location, rotating a component a bit, or setting references to a snapped building.

There are different phases of the that we can use depending on what we want to do to the buildable, and when we want the changes to take place.

Configure functions are called in the following order, and can thus override each others' steps. This list is adapted from comments in FGBuildableHologram.h

  • PreConfigureActor( buildable );

  • ConfigureActor( buildable );

  • ConfigureBuildEffect( buildable );

  • (Perform the actual spawning of the buildable actor in the world)

  • ConfigureComponents( buildable );

  • (BeginPlay called on the buildable)

Next, we’ll go into each phase in more detail.

PreConfigureActor

/**
 * Function to allow any pre-initialization on the actor before the configuration occurs. This is to allow for
 * final checks and to set properties as once were configuring its all const from there
 */
virtual void PreConfigureActor( class AFGBuildable* inBuildable );

In certain cases it may be necessary to check the properties again before the configuration of the actor starts. We can do that here.

ConfigureActor

/**
* Configure function: Configuring the actor created from the hologram when executed.
* @param inBuildable - The resulting buildable placed in the world that we are to configure before it's finished.
* @note DO NOT TOUCH COMPONENTS HERE as they'll be overwritten! Use ConfigureComponents for that
*/
virtual void ConfigureActor( class AFGBuildable* inBuildable ) const;

Configure Actor should only be used to set properties, not to create components or anything like that.

This is useful, for example, for moving properties from an upgraded actor to the new one if performing an Building Upgrade.

ConfigureBuildEffect

/** Configures the build effect for the constructed actor. */
void ConfigureBuildEffect( class AFGBuildable* inBuildable );

ConfigureComponents

/**
* Configure function: Configuring the actor component created from the hologram when executed.
* @param inBuildable - The resulting buildable placed in the world that we are to configure before it's finished.
* @note This is a good place to initialize snapped connections etc.
*/
virtual void ConfigureComponents( class AFGBuildable* inBuildable ) const;

Configure Components is a good place to, for example, change positions of components, or to replace a pipe connection with an upgraded actor.

Upgrading a Buildable

Holograms also allow implementing the upgrading of existing buildings. This is useful when you have multiple tiers of a building, and you want to upgrade them without having to dismantle the old one each time.

In the base game this is used by belts, for example.

Here are some C++ headers relevant to upgrading from FGHologram.h:

/** Get the target upgraded Actor */
virtual AActor* GetUpgradedActor() const override;

/** Do we allowed to Upgrade? */
virtual bool TryUpgrade(const FHitResult& hitResult) override;

private:
/** target upgraded Actor */
UPROPERTY(Transient)
AActor* mUpgradedActor = nullptr;

Let’s go into each of these in more detail.

mUpgradedActor

UPROPERTY(Transient)
AActor* mUpgradedActor = nullptr;

This field references the actor we are looking at when trying to upgrade. It’s the old building whose information we probably want to move to our new one.

GetUpgradedActor

/** Get the target upgraded Actor */
virtual AActor* GetUpgradedActor() const override;

You should return the Target actor here (in our example, mUpgradedActor).

TryUpgrade

/** Do we allowed to Upgrade? */
virtual bool TryUpgrade(const FHitResult& hitResult) override;

This function is called to check whether we are allowed to upgrade an actor. You should be sure to set mUpgradedActor from the hit result here, otherwise strange things can happen. You should also set the location of the hologram to that of the hit actor. Returning true means the upgrade is allowed.

Example Upgrade Hologram

A very basic example for the C++ part:

The base game logic for Upgrading actors will automatically handle belt, pipe, and power connections as long as the connection points use the same location and the same names.

However, inventories must be manually transferred, as well as fields like the selected recipe in machines.

For inventories, you can use for example NewBuildingInventory→CopyFromOtherComponent(OldBuildingInventory); in the ConfigureComponents step.

AActor* AMyModHologram::GetUpgradedActor() const
{
  // return the target actor to hide them ingame!
  return mUpgradedActor;
}

bool AMyModHologram::TryUpgrade(const FHitResult& hitResult)
{
  if(hitResult.GetActor())
  {
    const TSubclassOf<AActor> ActorClass = GetActorClass();

    // we check here that we don't try to upgrade the same Actor. the class should be different!
    if(hitResult.GetActor()->GetClass() != ActorClass)
    {
      // IMPORTANT we need to set the location from our hologram to the target Actor
      SetActorTransform(hitResult.GetActor()->GetActorTransform());

      // set the UpgradedActor and return true if it is valid (should be only make sure)
      mUpgradedActor = hitResult.GetActor();

      return mUpgradedActor != nullptr;
    }
  }

  // otherwise the UpgradedActor to nullptr
  mUpgradedActor = nullptr;
  return Super::TryUpgrade(hitResult);
}