Utility System C++ Architecture

I had a couple considerations and constraints in making this system. I had just launched the Steam Greenlight campaign before I made the decision to redo the AI completely. The Cats just weren’t expressive enough for me and adding new things into the Behavior Tree was causing bugs in the existing branches. It’s probably a great system, but it is not well documented and you are just expected to know how it works to use it well. So, it’s probably great for the people who made it.

This system may be horrible to someone else, not optimized or any of a number of other things, but that doesn’t matter. Because, I am the only programmer who has to use it and if it allows me to add many behaviors to the Cats so they are playing interesting animations representing there’s moods and perform actions in the world on their own volition I’m happy with it.

It is a decision making system. It needs to have these features.

1. A straight forward way of adding new Actions
2. Needs to be able to choose from a large variety of animations based on moods
3. Needs to interact with items dynamically, if they are added to the world, or taken away so that it’s flexible
4. It needs to be quick to implement.

The core of the Utility System is a Manager that picks the best Action to perform. It basically goes through each Action and gets a score for it. Each Action has a list of considerations that contribute to it’s score. All the scores are normalized between 0 and 1.

One thing to think about is when an Action can be performed on multiple Actors in the level. Such as Chase Mouse. If there are 3 mice in the level, should the system decide which mouse to chase after it’s decided that’s what it’s going to do?

I decided that didn’t make since. So, run the entire Action List for each Actor in the Level that it can interact with. That way when it picks an Action such as Chase Mouse, it also has the Mouse to Chase along with it.

One thing that was a little tricky to get right at first was the Editor.

UtilityEditor

I wanted the simplest way possible to implement it. So I added a Class specifier to the UtilitySystem pointer that the AIController holds so that it could be specified directly in the Editor.

UPROPERTY(EditAnywhere, Instanced, Category = "AI")
UIAUtilitySystem* UtilitySystem;

Here is the complete header for the Utility System as it is now. I used Structs to give it a Data Only type feel. It is more procedural because of that. The UIAUtilitySystem is tightly coupled with the AI Controller to do the processing. This should be easily extendable for different controllers.

// Copyright 2013-2014 Cat Sniper LLC. All Rights Reserved.

#pragma once

#include "Object.h"
#include "IAUtilitySystem.generated.h"

class ACatAIController;

UENUM(BlueprintType)
enum class FunctionTypeEnum : uint8
{
FTE_Linear UMETA(DisplayName = "Linear"),
FTE_Quadratic UMETA(DisplayName = "Quadratic"),
FTE_Log UMETA(DisplayName = "Log")
};

UENUM(BlueprintType)
enum class FConsiderationEnum : uint8
{
CE_Distance UMETA(DisplayName = "Distance"),
CE_Los UMETA(DisplayName = "Line Of Sight"),
CE_AttractionTo UMETA(DisplayName = "Attraction To"),
CE_EnergyLevel UMETA(DisplayName = "Energy Level"),
CE_HungerLevel UMETA(DisplayName = "Hunger Level")

};

USTRUCT(Blueprintable)
struct FAxisParameter
{
GENERATED_USTRUCT_BODY()

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
FunctionTypeEnum CurveType;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
int32 M;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
int32 K;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
int32 B;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
int32 C;
};

/**
*
*/
USTRUCT(Blueprintable)
struct CATSNIPER_API FIAAxis
{
GENERATED_USTRUCT_BODY()

UPROPERTY(EditAnywhere, Category = "AI")
FAxisParameter Parameters;

UPROPERTY(EditAnywhere, Category = "AI")
FConsiderationEnum Consideration;
};

UENUM(BlueprintType)
enum class FActionTypeEnum : uint8
{
FAT_ChaseLaser UMETA(DisplayName = "Chase Laser"),
FAT_ChaseMouse UMETA(DisplayName = "Chase Mouse"),
FAT_MeowAt UMETA(DisplayName = "Meow At"),
FAT_EatFood UMETA(DisplayName = "Eat Food"),
FAT_PlayYarn UMETA(DisplayName = "Play Yarn"),
FAT_Sleep UMETA(DisplayName = "Sleep"),
FAT_WakeUp UMETA(DisplayName = "Wake up")
};

USTRUCT(Blueprintable)
struct FIAAction
{
GENERATED_USTRUCT_BODY()

int num;

FIAAction()
{
num = 0;
num = AxisList.Num();
if (num > 0)
{

int debug = num;
}
};

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
FActionTypeEnum ActionType;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
TArray AxisList;
};

USTRUCT(Blueprintable)
struct FIAActionScore
{
GENERATED_USTRUCT_BODY()

FActionTypeEnum ActionType;
float score;
};

/**
* Utility System
*/
UCLASS(Blueprintable, EditInlineNew)
class CATSNIPER_API UIAUtilitySystem : public UObject
{
GENERATED_BODY()

public:
void Init(ACatAIController* controller);

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
TArray ActionList;

UFUNCTION(BlueprintCallable, Category = "AI")
FIAActionScore EvaluateNextActor(AActor* actor);

private:
ACatAIController* MyController;

float GetAxisValue(FAxisParameter params, FConsiderationEnum consider, AActor* actor);

bool IsInitialized;
};

Because this is Enums and Structs everything can be Added in the Editor without having to have a custom constructor that builds up the lists from a description. The enum lists are not complete, this is just what I put in as a first pass as I’m making the system. In the next post I’ll show how the processing is done.

Share Button
Doing Good Better: How Effective Altruism Can Help You Make a Difference
by William MacAskill (Author), Sean Pratt (Narrator), LLC Gildan Media (Publisher) Doing Good Better: How Effective Altruism Can Help You Make a Difference(78) Buy new: $20.99 $17.95
by

Infinite Axis Utility System

So, I’m making a Cat and Lazer pointer game in UE4. I’ve redone the AI system now several times, I’m on my 4th iteration.

I had been using the built in Behavior Tree system in UE4, but I kept having issues with it. Basically I would get a behavior to work and move on to another part of the game, then when I’d come back to the AI a couple months later my cat wouldn’t move, or no longer looked at the laser etc.

I give up. I don’t know if I’m using it in a way that wasn’t intended or changes are made to the system that break my functionality when I update. Either way, it’s better for me if I just do it myself.

What is an Infinite Axis Utility System?

Here is a screenshot of the GUI Editor for the System:

UtilitySystemEditor

In a nutshell it is a system that returns an Action to perform with the highest value. It’s bascially a list of Actions and each Action has a list of “Axis”. Dave Mark did a talk on it at GDC a couple years ago and that’s where I heard about it. I think he also mentioned a Cat and Laser pointer game in the same talk actually which was the initial seed idea of Lol Cats I Can Has Lazer, though our game went it’s own design direction.

Here is a link to the talks:

http://intrinsicalgorithm.com/IAonAI/2013/02/both-my-gdc-lectures-on-utility-theory-free-on-gdc-vault/

Those videos go into the nuts and bolts of the system. The book Behavioral Mathmatics for Game AI is also a good source and I recommend reading it if you intend to implement a Utility System. It really gets into the design aspect of how to model behaviors as math functions.

Here is a link to a GDC talk of an evolved version of the Utility System used in an MMO:

http://www.gdcvault.com/play/1021848/Building-a-Better-Centaur-AI

After watching that I decided to incorporate some of the ideas into my system. My first couple IAUS ( Infinite Axis Utility System ) were not optimized at all. They were dynamic classes with functions. I wanted to describe my system in a more Data Centric way, and then have the System itself hold the functions that work on the data.

I choose this because it was easier to describe a system of data in the UE4 Editor than classes. Having TARRAY’s of structs with enums and floats as variables is very easy. With classes it’s not so easy.

Share Button
by
FakeGoDaddyPage

Fake GoDaddy Domain Change Notification Phishing Attempt

This morning I checked my email and there was a message from GoDaddy informing me there had been a name change to to one of my domains. It was the new one I created for an the app I started blogging about with the Google Compute Engine.

Something didn’t look quite right about it, but I clicked the link anyway. It went to a login form, but my user name is a long number that I’d have to look up to be able to type in, and, there was not enough stuff on the page. So, I googled it and came to several blog posts about Fake DNS change notifications. Apparently new Domains are being targeted.

FakeGoDaddyPage

I hadn’t had my morning cup of coffee yet, so was probably just lucky I didn’t enter my username and password because then someone would have full access to my account.

Here is what the real page should look like. Basically there wasn’t enough stuff on the fake one. What worries me though about it is if the creator was a little more skilled and had added more content to the page I may have been fooled.

RealGoDaddyPage

Here is what the email I received looked like. I changed the actual phishing link address to “seriesoflettersandnumbers”, this is what would probably link the info I put into the form with my email address. I’m putting this post up for awareness.

I don’t have that domain ownership blocked on WhoIs, so it is trivial to get my email address for it.

Dear Valued GoDaddy Customer MICHAEL PURVIS.

This notification is generated automatically as a service to you.

We have received a request that the name servers be changed for the following domain name(s):

luciddreamapp.com

If you are monitoring this name with Domain Backorders, the above change is also displayed in the Monitoring and Backordering section of your Account Manager.

Use the link below:
https://sso.godaddy.com/mains.aspx?idp=”seriesoflettersandnumbers”

Sincerely,
GoDaddy Domain Backorders team.
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –

Copyright (c) 1999-2015 GoDaddy.com, LLC. All rights reserved.

I called GoDaddy and reported the phishing attempt, gave them the url to look at and they confirmed there was nothing out of the ordinary with my account. It’s interesting that the phone number on the fake page is GoDaddy’s actual phone number. It makes me think that it’s possible whoever did this, is doing an experiment because they could have gone a few steps further to make it more effective.

Share Button
by

How to set up Git on Google Compute Engine

This is a beginner level tutorial for setting up Git on a Google Compute Engine Virtual Machine.

When I develop an application I do it in an iterative process. The first peace I did for the Nodejs server on GCE was in my last blog setup nodejs on gce which leaves off with being able to browse to the server from an external browser.

The next needed steps that I intend are to add Postgress for the Database, the Express framework to handle the HTTP and I haven’t decided on Aurelia or PhoneGap yet for the front end framework. But, it would be horrible to setup everything and then lose it. There are 2 steps that will aid in this.

1. Is to create version control so all the files live on Git.
2. The second is making a bash script that sets up and installs all the packages so I could easily get started from scratch in a fresh Ubuntu install, that file would also live in the Git repo.

This post will detail setting up Git. It’s really the same on any Ubuntu server, mine just happens to be on a Google virtual machine.

All there is to it:

sudo apt-get install git

The next step is to enter the username and password. Since this is on a Virtual Machine I don’t want to use my personal Git Account. I’ll make a new one for the project. You can get an free account if you don’t mind it being public. Sign up at, github.com

Now, It’s great if your developing your app and you push your changes up to GitHub so other collaborators can get them, and fetch changes others made and pull them down. But, that doesn’t make as much sense when it’s a webserver. It would be a pain to need to log onto the virtual machine hosting the webserver and pull down the changes. Setting up Automatic Deployment is the answer.

Here’s a link to instructions for it:

If you want to work on the VM like it was your local dev environment, especially at the start that’s fine also.

Set up your user name next.

git config --global user.name "Your Name"
git config --global user.email youremail@example.com
Share Button
Doing Good Better: How Effective Altruism Can Help You Make a Difference
by William MacAskill (Author), Sean Pratt (Narrator), LLC Gildan Media (Publisher) Doing Good Better: How Effective Altruism Can Help You Make a Difference(78) Buy new: $20.99 $17.95
by

How to create Node HTTP server on Google Cloud Platform

This is a quick tutorial, beginner level, to get a nodejs serving to the web as fast as possible. The result will be that you can navigate to an ip:port on the web and get a http response.

Google Cloud Platform: https://cloud.google.com/compute/

Click Free Trial and sign up, it does require a credit card.

In the Google Developers Console, select compute Engine and click New instance.

Give it a Name.

On Boot Disk, click Change and select Ubuntu 14.04 LTS

Check the box for allow HTTP traffic.

There is a Networking tab, there is an option for creating a new static IP address. You can promote an ephemeral IP to static at a later time.

Create the instance and at the far right click SSH under Connect to open a console.

In the console:

~$ sudo apt-get update

~$ curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash -

~$ sudo apt-get install -y nodejs

These instructions came from: NodeSource

Test that node is installed, type node. You should see a >. Type console.log(‘Hi’); You should see Hi. Then Ctrl+C to exit node, you’ll have to do it again after prompting.

Create a directory.

~$ mkdir myapp

Change to it.

~$ cd myapp

If linux and command line is new to you, a nice shortcut to know is that when typing a filename, if you press tab it will auto complete once it has enough letters to differentiate from other files.

I’m not a fan of editing files in the console, but vim is the answer to do it. You’ll need to search for some Vim tutorials, but I’ll give the needed commands for this simple tutorial.

To create a new file.

~$ vim hello.js

Now, you’ll be in the vim console. You can’t just edit from here as you’ll be in command mode to start.

Press i, now you should be in insert mode and can edit.

Type this in:

var http = require('http');
var server = http.createServer(function(req, res){
        res.writeHead(200);
        res.end('Hello Kitty');
});
server.listen(8080);

To run it:

~$ node hello.js

It will just sit there. Go to your Google Developers Console and click the SSH under connect for a new console and type:

~$ curl localhost:8080

You should see your message there, Hello Kitty

Now, this is almost there, but if you can’t navigate to this from the web yet. You need to enable the port for a tcp connection. To do this, in Google Developers Console select Firewall rules, it’s under Networking.

Click, New firewall rule.

Name it.

Under Source IP ranges, 0.0.0.0/0

Under Allowed protocols, tcp:8080

I put http-server under target tags, but it’s optional.

Now, if all went well you can navigate to the external IP and port to receive the http request and see the message in your browser. The external IP is to listed on the VM instances page of the Compute Engine. It’s to the left of the SSH button you’ve been clicking to open a console.

In a browser enter your external ip number followed by :8080 and Hello Kitty should show on the page.

That’s it for this tutorial. If you followed it your in a place now that you can start adding real features to your server. I’ll do more in this series to add to it.

Share Button
The Gifts of Imperfection: Let Go of Who You Think You're Supposed to Be and Embrace Who You Are
by Brené Brown (Author), Lauren Fortgang (Narrator), Audible Studios (Publisher) The Gifts of Imperfection: Let Go of Who You Think You're Supposed to Be and Embrace Who You Are(2979) Buy new: $19.95 $17.95 45 used & new from $17.95
by

How to make a Spider in UE4 Part 6

In this post add convex transitions to the spider. Basically up to this point the spider can navigate inside, or concave corners but can’t go around outside ones. The code to do this ended up being minimal.

 

I added another socket on the spider, to get the normal of the convex surface from a trace from a socket in front of the body.

ConvexCheck

 

In the tick, check to see if there is a surface under the front legs, if there’s not the get the normal of the surface that is convex to the current surface it’s on.

 

            //Check if convex corner
            if (!GetWorld()->LineTraceSingle(HitData, DirectionSocketLocation, FLSocketLoc, ECollisionChannel::ECC_Pawn, TraceParams) && 
                !GetWorld()->LineTraceSingle(HitData2, DirectionSocketLocation, FRSocketLoc, ECollisionChannel::ECC_Pawn, TraceParams))
            {
                HitNormal = HitData.ImpactNormal;
                HitNormal2 = HitData2.ImpactNormal;

                if (!HitData.GetActor() && !HitData2.GetActor())
                {
                    DidTransition = TransitionToWall(true);
                }
            }
            //Point directly in front
            else if (GetWorld()->LineTraceSingle(HitData, BellySocketLoc, FrontSocketLoc, ECollisionChannel::ECC_Pawn, TraceParams))
            {
                HitNormal = HitData.ImpactNormal;
                if (HitData.GetActor())
                {
                    DidTransition = TransitionToWall(false);
                }
            }

To transition to the new surface, need to know how to do the trace as it’s different depending on which way.

bool ASpiderPawn::TransitionToWall(bool IsConvex)
{
    bool retval = false;

    //Grab a ref to the world here
    UWorld* world = GetWorld();

    FHitResult hitResultTrace;
    FCollisionQueryParams queryParams;
    FCollisionObjectQueryParams objQueryParams;

    queryParams.AddIgnoredActor(this);

    FVector Start, End;

    FVector under;

    FVector newUp;
    FVector newForward;
    FVector newRight;

    if (!IsConvex)
    {
        Start = BellySocketLoc;
        End = FrontSocketLoc;
    }
    else
    {
        Start = FrontSocketLoc;
        End = ConvexCheckLoc;
    }

    //Trace to get the surface normal in front of actor
    if (world->LineTraceSingle(hitResultTrace, Start, End, queryParams, objQueryParams))
    {
        under = hitResultTrace.ImpactPoint;
        newUp = hitResultTrace.ImpactNormal;

        //Some math to get the new Axis
        FVector currentRightVect = GetActorRightVector();
        newForward = FVector::CrossProduct(currentRightVect, newUp);
        newRight = FVector::CrossProduct(newUp, newForward);

        //Build the new transform!
        FTransform newTransform(newForward, newRight, newUp, under);
        SetActorTransform(newTransform);
        retval = true;
    }
 
    return retval;
} 

The transitions are pretty jerky at this point. In the final version the idea is to play an animation that handles it. But I’m going to save that for later. The next post will feature adding in jumping, so the spider can jump up onto a wall, or down onto the floor, rotating correctly during the jump.

Share Button
by

How to make a Spider in UE4 Part 5

Here’s a video to show where the Spider currently is at.

As can be seen, there are some parts that are working well, such as the spider can walk on the walls, rotate on any surface, transition to other surfaces etc.

There’s a lot that isn’t working though. There are no collisions and the movement has a slide to it. It doesn’t correctly transition around outside edges.

I put the code, and project up on GitHub. github.com/eibonscroll/UE4SpiderAI

There was a problem with rotating the pawn on other surfaces. Basically, you can’t just set the rotation. Setting the transform does work.

void ASpiderPawn::RotateRight()
{
    FTransform TheTransform = this->GetTransform();

    FRotator rot = FRotator(0, rotationSpeed, 0);

    TheTransform.ConcatenateRotation(rot.Quaternion());
    TheTransform.NormalizeRotation();
    this->SetActorTransform(TheTransform);
}

This works on any surface angle correctly.

I use sockets on the Mesh to get the locations for the traces. The locations are updated each tick. Because there are a number of traces being done, can update locations of the sockets once per tick. This function creates a new transform. Previously I was trying to just set the rotation. Searching on the UE4 AnswerHub and the UE4 forums I went through all the posts I could find on wall walking, there were several people having trouble with strange angles when trying to rotate the Pawn onto different surfaces.

bool ASpiderPawn::TransitionToWall()
{
    bool retval = false;

    //Grab a ref to the world here
    UWorld* world = GetWorld();

    FHitResult hitResultTrace;
    FCollisionQueryParams queryParams;
    FCollisionObjectQueryParams objQueryParams;

    queryParams.AddIgnoredActor(this);

    FVector under;

    FVector newUp;
    FVector newForward;
    FVector newRight;

    //Trace to get the surface normal in front of actor
    if (world->LineTraceSingle(hitResultTrace, BellySocketLoc, FrontSocketLoc, queryParams, objQueryParams))
    {
            under = hitResultTrace.ImpactPoint;
            newUp = hitResultTrace.ImpactNormal;

            //Some math to get the new Axis
            FVector currentRightVect = GetActorRightVector();
            newForward = FVector::CrossProduct(currentRightVect, newUp);
            newRight = FVector::CrossProduct(newUp, newForward);

            //Build the new transform!
            FTransform newTransform(newForward, newRight, newUp, under);
            SetActorTransform(newTransform);
            retval = true;
    }

    return retval;
}

In the next post will work on getting the spider to walk around outside edges.

Share Button
Pandemic Board Game
by Z-Man Games Pandemic Board Game(1587) Buy new: $39.99 $24.60 144 used & new from $18.74
by

How to make a Spider in UE4 Part 4

Before getting the Spider to climb walls, need to to just move on the floor to start. I’ll start as simple as possible and then iterate on it as things are needed, such as keying the right animation to play based on the movement.

This may end up being helpful for someone looking to start their movement code from scratch. The reason I am putting the control code in the level blueprint is only for test. The goal of this pawn will be AI Controlled but need it will be easier to test it with key presses.

In the project settings, input, create an action mapping for the P button (Arbitrarily chosen). A public MoveForward function in the Pawn code will be called from it.

LevelBPMove

I set the speed value to a small number, 0.5, as it will be applied each tick. When P is pressed it opens a gate so that the MoveForward function is called each tick, on releasing P the gate closes.

Here is the C++ function on the Pawn.

 

In the header:

UFUNCTION(BlueprintCallable, Category = "Spider Move")
void MoveForward();

In the .cpp

void ASpiderPawn::MoveForward()
{
    FVector loc = GetActorLocation();
    FVector forward = GetActorForwardVector();
    FVector newLocation = loc + (forward * speed);
    SetActorLocation(newLocation);
}

 

Share Button
The Power of Habit: Why We Do What We Do in Life and Business
by Charles Duhigg (Author), Mike Chamberlain (Narrator), Random House Audio (Publisher) The Power of Habit: Why We Do What We Do in Life and Business(3734) Buy new: $28.00 $23.95 29 used & new from $23.95
by

How to make a Spider in UE4 Part 3

In this 3rd installment of the make a Spider blog. In the last part I left off with some problems. Some things worked great and are keepers, but the movement simply wasn’t working right. The spider could detect any wall, but once it rotated up onto it, it’s orientation was wrong. I had used the Character class as the base, which comes with a collision capsule component which always want’s to be upright.

This already wasn’t working out for the spider, a rectangle is a much better collision shape but extending the Character the capsule is hardwired into it. While I was able to get the spider to walk up the wall, making it rotate on the correct axis was different for each wall it was on, and that was because of things hardwired into the Character.

The best option going forward, instead of trying to hack the Character is to extend farther up the chain, from Pawn directly. The Pawn doesn’t have any existing movement code to fight.

Even though this is going to be an AI controlled spider, for developing the movement it’s a lot easier for it to take it’s control from key presses. Saving the path-finding problems of walls and ceiling to deal with later, after the spider can move on those surfaces well.

So, I started a new UE4 project from the launcher. A BaseCode C++ projectile.

PawnSpider

I made a new C++ Class extending Pawn and named it SpiderPawn. Then made a new Blueprint extending from SpiderPawn, naming it SPiderPawnBP, made a scene component, named it root. Then added a SkeletalMesh component. I dropped my Spider FBX, mesh, Idle, and Walk files into the project, imported them, then set the Skeletal Mesh to the FBX I had just imported as a SkeletalMesh.

So now there is a reasonably blank starting point to work from. Because the trace system from the previous posts was working well I will add that into the new Pawn.

There is a design decision for the movement that needs to be made. The last version of the spider had problems because it was trying to do to much.

To start with just 2 movement controls, walk forward and rotate. Once things are working can always add to it.

I will make the functions to move public to start, and in the level blueprint handle the key press’s and call the functions on a reference to the Spider in the level. Once things are working, will restrict there access and call them from a AIController which is possessing it.

Share Button
by

UE4 C++ How to make Component with Billboard SubComponent

UE4 Blueprints makes it very easy to drag and drop objects onto each other, but sometimes you want everything already set up.

I’ve been working on a procedural level generation system, which is designer assisted. So blocks that will be assembled to build a level need to be marked up by the designer with locations to spawn items and other things.

Doing this in Blueprints is very easy, make a new Blueprint extending Actor, then press AddComponent, pick BillBoardComponent, set it’s Sprite in it’s details panel and you are done. But, it’s a Blueprint itself which means code that runs it will have to go looking for blueprint instances. Additionally, it gets messier when the Blueprint extends Blueprint Component, and then these need to be added to each block.

So, moving all this to C++.

The result will be that when the designer is modifying a Block they will press add component, and select the one they want directly, then position it.

BlockWithComponents

 

It actually is very simple, but because it wasn’t directly documented it took me a while to figure it out. Here’s the code.

This is the base header file:

#pragma once

#include "Components/ActorComponent.h"
#include "Components/SceneComponent.h"
#include "Components/BillboardComponent.h"
#include "MarkerComp.generated.h"
UCLASS(Blueprintable, ClassGroup = (Marker), meta = (BlueprintSpawnableComponent))
class MYGAME_API UMarkerComp : public USceneComponent
{
GENERATED_BODY()

protected:
//A UBillboardComponent to hold Icon sprite
class UBillboardComponent* BillboardComponent;
//Sprite for the Billboard Component
class UTexture2D* SpriteTexture;

public:
// Sets default values for this component's properties
UMarkerComp(const FObjectInitializer& ObjectInitializer);

// Begin ActorComponent interface
virtual void OnRegister() override;

// Called when the game starts
virtual void InitializeComponent() override;

// Called every frame
virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) override;
};

I didn’t actually choose to make the Billboards in it’s .cpp, but in it’s derived classes. I use multiple because each type of marker in my system has different variables that need to be set as well as functions that will be called as the level is being generated, so it is more than just a different sprite.

#include "MyGame.h"
#include "MarkerComp.h"


// Sets default values for this component's properties
UMarkerComp::UMarkerComp(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	bWantsInitializeComponent = true;
	PrimaryComponentTick.bCanEverTick = true;

	//IconPath = "/Game/Lemons/Procedural/Blocks/Blueprints/icons/AmmoMarker";
	bVisualizeComponent = true;
}


void UMarkerComp::OnRegister()
{
	// If we need to perform a call to AttachTo, do that now
	// At this point scene component still has no any state (rendering, physics),
	// so this call will just add this component to an AttachChildren array of a the Parent component
	AttachTo(AttachParent, AttachSocketName);
	Super::OnRegister();
}

Here is the extending header.

#pragma once

#include "Components/ActorComponent.h"
#include "MarkerComp.h"
#include "AmmoMarker.generated.h"

UCLASS(ClassGroup = (Lemon), meta = (BlueprintSpawnableComponent))
class MYGAME_API UAmmoMarker : public UMarkerComp
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	UAmmoMarker(const FObjectInitializer& ObjectInitializer);

	// Called when the game starts
	virtual void InitializeComponent() override;

	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
	
	
};

Here is the .cpp of an extending class that actually makes the Billboard Component.

#include "MyGame.h"
#include "AmmoMarker.h"


// Sets default values
UAmmoMarker::UAmmoMarker(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	bWantsInitializeComponent = true;
	PrimaryComponentTick.bCanEverTick = true;

	// Structure to hold one-time initialization
	struct FConstructorStatics
	{
		// A helper class object we use to find target UTexture2D object in resource package
		ConstructorHelpers::FObjectFinderOptional<UTexture2D> MarkerTextureObject;

		// Icon sprite category name
		FName ID_CatagoryName;

		// Icon sprite display name
		FText NAME_DisplayName;

		FConstructorStatics()
			// Use helper class object to find the texture
			// "/Engine/EditorResources/S_Note" is resource path
			: MarkerTextureObject(TEXT("/Game/Lemons/Procedural/Blocks/Blueprints/icons/AmmoMarker"))
			, ID_CatagoryName(TEXT("Marker"))
			, NAME_DisplayName(NSLOCTEXT("SpriteCategory", "AmmoMarker", "AmmoMarker"))
		{
		}
	};
	static FConstructorStatics ConstructorStatics;

	BillboardComponent = ObjectInitializer.CreateEditorOnlyDefaultSubobject<UBillboardComponent>(this, TEXT("Billboard"), true);

	SpriteTexture = ConstructorStatics.MarkerTextureObject.Get();
	BillboardComponent->Sprite = SpriteTexture;

	BillboardComponent->AttachTo(this);
}
Share Button
by