Introduction

Holograms are the foundation of any buildables that are built through the BuildGun. Consider the following questions:

  • Have you ever wondered how a Pump can only be built along a pipe?
  • Have you ever wondered how a splitter divides a conveyor belt when placed in the middle of the belt?
  • Have you ever wondered how conveyors, pipes and buildable in-place upgrades are executed?
  • Have you ever wondered how foundations snap together depending on where you point your buildgun?
  • the answer to all of the above (and many more) it's "Through holograms"!

    Holograms are the architects of our Buildables, we tell them what, where, and how buildables need to be handled. But let's get into the details...

    How does it work

    Let's start to describe the process:

    Definitions

    Build_Object : the Buildable class we are going to make (Ex: FGBuildableFactory, FGBuildableManufacturer, FGResourceExtractor)

    When we select the Build_Object that we wish to build into the world a hologram version of our Build_Object is cast into the world following our Mouse cursor (it takes all the components of our Build_Object and applies an Outline to it).

    Experimenting with some different holograms we can notice that the behaviour is not always the same:
  • Some holograms only Outline in blue/white when pointing at building of same type or some kind of attachments (pipes/conveyours attach only to other pipes/conveyours, to pipe/conveyour attachments or pipe/conveyour input/outputs)
  • Some holograms only Outline in blue/white when pointing at certain certain Build_Objects (Miners can only be placed on Resource Nodes)
  • Some holograms only Outline in blue/white when pointing at the ground or to the ceiling (foundations, different kind of light)

  • This is the first step that the hologram has to face: Where should i build?

    Yes, we do give the hologram a location already by pointing at it with our cursor but the hologram, being a particular stingy architect, wish to know the specifics: is any ground fine to be built one? Do i only build on top of something in particular? Do you want me to build it attached to something? Do you wish this building to replace something?

    For now at the initial talking with the architect (in the constructor) we tell him what he can interact with

    We do this by calling the mValidHitClasses (an array of all kind of Actors (TArray<TSubclassOf>) to which we can add a valid class the hologram can interact with

    AFGMyBuildableHologram::AFGMyBuildableHologram() : Super(){
    
       this->mValidHitClasses.Add(AFGMyBuildableHologram::StaticClass());
    
    }

    This will tell our architect which Build_Objects are valid for our checks (see below). Depending on the hologram class you are inheriting, the mValidHitClasses array may already have other classes already implemented as Valid.

    Now we proceed to give the our architect more specifics about our placement. This is done through a series of checks (that are processed while pointing your cursor). Here's a brief list of some of the functions that handle theese checks:

  • IsValidHitResult: the cursor uses a ray cast (a line starting from our camera toward the cursor) and returns the first thing it hits and that thing is returned through this function via its paramenter (a FHitResult variable). The function will only take into consideration the classess that we pointed out initially in our constructor (see code above). The FHitResult variable proves really useful as it stores the Actor we are pointing at and all its properties. What i usually do to tell if the target is valid is check the actor against the type of class i wish to be valid for construction:
    bool AFGMyBuildableHologram::IsValidHitResult(const FHitResult& hit) const {
    
        //A pointer to the Actor we are looking at
        AActor* target = hit.GetActor();
    
        //We check if the target is null (if we are not looking at an actor that we filtered previously)
        if (target) {
    
            //Check if the actor we are looking at is actually the one of the class we wish to be valid
            if (target->IsA(AFGMyBuildableHologram::StaticClass())) {
    
                return true;
            }
        }
        return false;
    }

    When returning true, the architect will understand that what we are pointing at is a valid placement and will outline our building with a blue/white color. We can also expand our example if we wish to find a Build_Object of a specific class but with a specific component. This time would be useful to Cast the actor

    bool AFGMyBuildableHologram::IsValidHitResult(const FHitResult& hit) const {
    
        //A pointer to the Actor we are looking at
        AActor* target = hit.GetActor();
    
        //We check if the target is null (if we are not looking at an actor that we filtered previously)
        if (target) {
    
            //Check if the actor we are looking at is actually the one of the class we wish to be valid
            if (target->IsA(AFGMyBuildableHologram::StaticClass())) {
    
                //Find all Factory Connection components from this actor and store the array in a variable
                auto components = target->GetComponentsByClass(UFGFactoryConnectionComponent::StaticClass());
                //Check if there is at least 1 Factory Connection Component attached to this Actor
                if(components.Num()>0){
                     return true;
                }
            }
        }
        return false;
    }


  • Most of the following function can be used as the one above. They are just used in different situations

  • TryUpgrade: it's used when checking a building that needs to be upgraded triggering the upgrade related functions.
  • TrySnapToActor: the class comments say it all "See if we can snap to the hit actor. Used for holograms snapping on top of ex resource nodes, other buildings like stackable poles and similar. If returning true, we assume location and snapping is applied, and no further location and rotation will be updated this frame by the build gun. * @return true if we can snap; false if not."

  • Let's say we are now decided to place our building, by pressing the left mouse button, the architect wants now to understand if the Build_Object is a SplineMesh of sort (like cable, conveyour belts, pipes, etc which have a starting placement and an end). For that we use the function DoMultiStepPlacement inside of which we can define the criteria when the last step is taken. Each time we return false in this function we tell the architect that its placement job isn't finished and that we wish to keep choosing a location for our Build_Object. Once we return true the placement step will be over.

    WIP

    Info

    Created: May 29, 2021, 12:33:50 AM
    Views: 219

    Author

    Drejn