Download

Append properties to the 'Actor' class without modifying source code?

Hi all,

I’m starting to plan out my code base for a game I want to work on, and realising that a lot of my classes are going to require the same properties, such as Team ID, Heat/Image/Radar Signature, so on and so forth. I’ve created a child of the ‘Actor’ class called ‘BZGame_GameObject’ class, which would hold all of those values. The problem is, I also want all my Pawns and suchlike to have these properties as well. That way when I come to programming my weapons and various things later, I can simply cast to GameObjects rather than checking against lots of individual classes for the same variables. Here’s an example:

  • All of my Objects in the world will have a ‘Heat Signature’, a float variable that determines how much heat that object generates.
  • Missiles and other weapons will use that ‘Heat Signature’ to determine what object they should fly towards, and post process will use it for various material effects too.
  • Instead of casting or getting heat information from all of my different classes, I could just cast to the ‘GameObject’ master class that has these variables.

Another reason is that I want to avoid creating an identical copy of the ‘Pawn’ class just so that it derives from my master ‘GameObject’ class, it’d be a lot of extra work and if the Pawn class ever changes in code I would have to modify my code too.

So, any suggestions? I basically want to add some functionality and properties to the Actor class without modifying source code, so that I can still use the actor’s child classes.

You’re looking for interfaces. You should take a look at what Epic has done with Unreal Tournament in UTTeamInterface.

The solution they use is very simple, anything that needs a team implements the interface. Whenever GetTeamNum() is called on an object it returns a value from 0-255(uint8) defaulting to 255 for no team. You’re left to implement this however you need to in each class, but the public interface to that implementation is always the same so there is never a need to wonder how to check the team of an object.

Editing this to expand a bit more … So you have the concept of a GameObject class you want all of your gameplay actors to derive from. This cuts down on code duplication and lets you treat every gameplay actor the same way in some cases… But you also have a couple different Actor derived classes which you don’t want to modify, so inheritance is right out. Instead of inheriting behavior from a base class you might consider making GameObject a component of your gameplay actors. Every gameplay actor has a GameObject and every game object has a uniform interface that will provide you with the information relevant to every gameplay actor.



class GameObject 
{
public:
     GameObject(float heat, FName classifier);
     
     float GetHeat();
     FName GetClassifier();

private:
    float _heat;
    FName _classifier;
    

};




class AMyPawn : public APawn, public IGameObjectInterface
{
    GameObject go;

    virtual const GameObject * GetGameObject() 
    {
          return &go;
    }
    /*...] Pawn stuff*/
};




class AMyCharacter : public ACharacter, public IGameObjectInterface
{
    GameObject go;
    
    virtual const GameObject * GetGameObject() 
    {
          return &go;
    }
    
    /*...] Character stuff*/
};




/*UE Interface stuff*/
class IGameObjectInterface
{
     /* Amended according to Gigantoad's suggestion. */
public:
     virtual const GameObject* GetGameObject()  = 0;
};


If anyone has any critiques or suggestions for a better way, then please do chime in. I would never call myself any kind of expert, far far from it.

Hmm didn’t think of using a Component, that’s not a bad idea really, just grab it’s information from an attached Component.

I’m not familiar with Interfaces, though I did just download the UT source code to take a look at. I’ve mostly been looking at ShooterGame for implementations so far. Thanks for the example code too :slight_smile: If anybody else does have a suggestion please do chime in :slight_smile:

Far from an expert either, but C++ has a way of creating pure interface classes (classes that are 100% abstract and leave all implementation to deriving classes) by assigning 0 to functions:


class IErrorLog
{
    virtual bool OpenLog(const char *strFilename) = 0;
    virtual bool CloseLog() = 0;
 
    virtual bool WriteError(const char *strErrorMessage) = 0;
};

Example from here: http://www.learncpp.com/cpp-tutorial/126-pure-virtual-functions-abstract-base-classes-and-interface-classes/

So this is the closest to having actual interfaces of other languages and may be the right thing to do here.

I quite agree, that may be the best way according to best practices. There are examples of such in the UE codebase as well as some with very minimal implementation like the UTTeamInterface I referenced. Your example is more more predictable if the practice is adhered to. That’s important when writing code for maintainibility. I admit, I am a little bit of a lazy coder.

I’ve not looked too deeply into the way UE implements interfaces, but to make everything work with the various systems like blueprints it requires just a bit more. I believe that the UCLASS macro will not allow even such a mild form of multiple inheritance without using the UINTERFACE() macro… Outside of UObject classes there is of-course no restriction.

Here is an example of perhaps the simplest you can get, I think capturing the spirit of your example.



// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "SlateWidgetStyleContainerInterface.generated.h"


UINTERFACE()
class SLATECORE_API USlateWidgetStyleContainerInterface : public UInterface
{
	GENERATED_UINTERFACE_BODY()
};

class ISlateWidgetStyleContainerInterface
{
	GENERATED_IINTERFACE_BODY()

public:

	virtual const struct FSlateWidgetStyle* const GetStyle() const = 0;
};


Just one side note. You should not add any pure virtual functions directly to an UObject child, its because unreal will create the DefaultObject and needs to create at-least an instance for every class. Pure virtual classes are abstract classes and can not instanced :smiley:

Well at the moment, I’m getting a Link build error with Unresolved Externals, though I’m pretty sure I am using the right function and it’s not complaining just because a Virtual function has no Implementation.

I’ve pretty much copied the GetTeamNum function directly from UTTeamInterface.h, but perhaps I should not use the PURE_VIRTUAL macro?

BZGame_GameObjectInterface.h



#pragma once

#include "Object.h"
#include "BZGame_GameObjectInterface.generated.h"

UINTERFACE(MinimalAPI)
class UBZGame_GameObjectInterface : public UInterface
{
	GENERATED_UINTERFACE_BODY()
};

class BZGAME_API IBZGame_GameObjectInterface
{
	GENERATED_IINTERFACE_BODY()

	virtual bool IsNeutral() const
	{
		return true;
	}

	virtual uint8 GetTeamNum() const PURE_VIRTUAL(IBZGame_GameObjectInterface::GetTeamNum, return 255;);
};


BZGame_PowerUp.h





#pragma once

#include "GameFramework/Actor.h"
#include "BZGame_PowerUp.generated.h"

USTRUCT()
struct FPowerUpProperties
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditDefaultsOnly, Category = "PowerupClass")
	bool HoverInAir;

	UPROPERTY(EditDefaultsOnly, Category = "PowerupClass")
	float HoverAltitude;

	UPROPERTY(EditDefaultsOnly, Category = "PowerupClass")
	float GravityScale;

	// Defaults
	FPowerUpProperties()
	{
		HoverInAir = true;
		HoverAltitude = 16.0f;
		GravityScale = 0.2f;
	}
};


UCLASS()
class BZGAME_API ABZGame_PowerUp : public AActor, public IBZGame_GameObjectInterface
{
	GENERATED_UCLASS_BODY()

	UPROPERTY(VisibleDefaultsOnly, Category = "Crate")
	TSubobjectPtr<UStaticMeshComponent> CrateMesh;

	UPROPERTY(VisibleDefaultsOnly, Category = "Crate")
	TSubobjectPtr<UParticleSystemComponent> IdleParticle;

	UPROPERTY(EditDefaultsOnly, Category = "PowerUp")
	struct FPowerUpProperties PowerUpClass;

	UFUNCTION(BlueprintCallable, Category = "GameObjectClass")
	virtual uint8 GetTeamNum() const;

protected:

	/* Override Tick Function So We Can Do The Hovering */
	virtual void Tick(float DeltaSeconds);
};


BZGame_PowerUp.cpp



#include "BZGame.h"
#include "BZGame_PowerUp.h"


ABZGame_PowerUp::ABZGame_PowerUp(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{

}

void ABZGame_PowerUp::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);
}

uint8 ABZGame_PowerUp::GetTeamNum() const
{
	const IBZGame_GameObjectInterface* GameObjectInterface = InterfaceCast<IBZGame_GameObjectInterface>(this);
	if (GameObjectInterface != NULL)
	{
		return GameObjectInterface->GetTeamNum();
	}
	else
	{
		return 0;
	}
}


I’m starting to wonder if it’s more flexible for me to just create a BZGame_Pawn class which is identical to the regular Pawn class and base it on my GameObjectClass.

I’m not sure what the problem is with your code, sometimes those bugs are hard to spot. Doublecheck syntax… Could you post the exact error?

edit: redudant example

EDIT: sorry, mistook UBZGame as a typo :stuck_out_tongue: nevermind…

Also, your implementation should look like this


virtual uint8 GetTeamNum() const override;

uint8 GetTeamNum() const 
{
/*implementation */
}

Edit:

I’ve tested this interface and marking it blueprintcallable in the implementing class is fine.



#pragma once
#include "TestInterface.generated.h"

UINTERFACE(minimalapi)
class UTestInterface : public UInterface
{
		GENERATED_UINTERFACE_BODY()
};

class ROLLING_BP_API ITestInterface
{
	GENERATED_IINTERFACE_BODY()

	virtual bool IsNeutral() const
	{
		return true;
	}

	virtual uint8 GetTeamNum() const PURE_VIRTUAL(ITestInterface::GetTeamNum, return 255;);
};

#include "Rolling_BP_HitTest.h"
#include "TestInterface.h"

UTestInterface::UTestInterface(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
 
}




#pragma once

#include "GameFramework/PlayerController.h"
#include "TestInterface.h"
#include "MyPlayerController.generated.h"

UCLASS()
class ROLLING_BP_API AMyPlayerController : public APlayerController, public ITestInterface
{
	GENERATED_UCLASS_BODY()

	
	  UFUNCTION(BlueprintCallable, Category = "Test")
	  virtual uint8 GetTeamNum() const override;
};

#include "Rolling_BP.h"
#include "MyPlayerController.h"


AMyPlayerController::AMyPlayerController(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP)
{
}

uint8 AMyPlayerController::GetTeamNum() const
{
	return (uint8) 2;
}

@TheJamsh Unresolved Externals usually means that function is declared but is not defined. Check if linker gives you name of that UE and try to find its definition in your code.

It’s definitely not a virtual function issue atm, I’m pretty sure of that. Haven’t made any changes apart from trying to add this interface to something. I still can’t get it to compile without some kind of error no matter what I try. I’ve even followed Rama’s tutorial on the Wiki identically and I can’t get them to compile. Am I just missing an include somewhere or something?

EDIT: Scratch that, got it working. Re-wrote the whole **** thing.

It has made me wonder though… why do this when I could just create a struct of variables in my main game class, then give that struct to the sub-objects that I want to have those variables. There don’t really need to be any functions there, I guess. Or was I doing this for a reason… curse going on long breaks!

Okay so, now I need my interface to have a variable ‘TeamNumber’. When I call the ‘GetTeamNumber’ function, I want to be able to set that TeamNumber. In theory, I shouldn’t need a function that 'set’s the Team Number right? As I can just set the inherited ‘TeamNumber’ variable in my child classes that have the interface correct? In fact strictly speaking, I don’t really need a GetTeamNumber in this case surely, as I just want to ‘store’ a TeamNumber that I can get from anywhere?

I think… Any ideas what the best way is to go about this. What about something like ‘Health’ for example?

The reason for the “GetTeamNumber()” and the interface is really just to make it possible to InterfaceCast with your team interface and treat ANY object that is team aware the same way even if they have entirely different implementations of GetTeamNumber().

In UT Characters have teams, Controllers have teams, Teams have Teams, PlayerStart actors have teams Controllable rockets have teams, CarriedObjects have teams.

You could create a GetTeamNumber() method for each one, and forget about the interface but then you want to find out if the Flag is on the same team as the player that grabbed it and do something in both cases. Return Flag, Capture Flag etc.

bool OsSameTeam(MyCharacter character, MyFlag flag);
bool OsSameTeam(MyCharacter character, MyPlayerStart startpoint)
bool OnSameTeam(MyPlayerController controller, MyCharacter character)
bool OnSameTeam(MyPlayerController, MyRocket rocket)

or you could use the interface and have
bool OnSameTeam(Actor* actor1, Actor* actor2)