Basic Interaction System in C++

Introduction

In this first part I will show you a C++ implementation of an usable actor that can be activated (open/close door or pickup object) and has hooks for start/end mouse-over (eg. for highlighting of the object) at the end of the tutorial I will demonstrate a blueprint example of how this system can be utilized plus some examples of how I used this system for my own prototypes. I intend to extend this into a series which includes an inventory system and some of the other demonstrated use-cases.

Depending on your C++ knowledge the tutorial is best followed using the C++ FPSTemplate. Starting from a blank project or other template requires some basic knowledge on class (.cpp/.h) files as template code is omitted from the code snippets in this tutorial!

Project source is available on GitHub!

Update #1: A Blueprint implementation of this system is now available here!

Update #2: UsableActor functions now return bool instead of void to force Unreal to recognize it as functions instead of Events. This is required to support overriding functions in child blueprints. (07-Aug-14)

Update #3: Added code to Use_Implementation to fix support for networked games. Make sure you enable “replicates” property in your Blueprint too. (07-Aug-14)

Last Updated For: 4.7.x

Why not Blueprint?

This system could be implemented in Blueprint with about the same effort, I’ve done so as an experiment. I do however prefer to implement core functionality and framework code in C++ to create a strong foundation and use Blueprint for content driven behavior. For example I would implement door response/movement in Blueprint, but the abstract logic would be created in C++ – in fact that is exactly what we will do in this tutorial, create the basic foundation that could be used to control a door!

Update: A Blueprint implementation of this system is now available here! I still recommend people with a C++ project back-end to follow this C++ tutorial instead.

The Concept

The character performs a ray trace each tick that find the currently looked-at actor that derives from our “UsableActor”. The first and last frame we look at the actor we trigger events that can be implemented to for example highlight the selection. Finally, the “E”-key triggers the OnUsed event that should trigger some kind of response in Blueprint, like adding it to the players inventory.

UsableActor

The base class for all types of use/activate actors. I’ve used this single class for both a lever that controls a door and in-world objects that can be picked up by the player and placed in the inventory.

UsableActor.h

#pragma once

#include "UsableActor.generated.h"

UCLASS()
class AUsableActor : public AStaticMeshActor
{
	GENERATED_UCLASS_BODY()

	UFUNCTION(BlueprintImplementableEvent)
	bool OnUsed(ACharacter* character);

	UFUNCTION(BlueprintImplementableEvent)
	bool StartFocusItem();

	UFUNCTION(BlueprintImplementableEvent)
	bool EndFocusItem();
};

UsableActor.cpp

#include "YourProject.h"
#include "UsableActor.h"

AUsableActor::AUsableActor(const class FObjectInitializer& PCIP)
: Super(PCIP)
{

}

Character

The character class handles the ray-trace to select the actor in view. The Tick function triggers Start/End focus events to be handled by the UsableActor in Blueprint (we will come back to this) and finally the Use function will trigger the pickup/activate/destroy or whatever you decide to implement in your blueprint.

I’ve omitted certain code areas that exist in the FPSTemplate for readability. This “Use” implementation supports multiplayer although the blueprints derived from UsableActor must still manually handle replication as with any actor in Unreal.

Replace “AInvSystemCharacter” with your own character class name after pasting the code snippets.

*Character.h

	
        virtual void Tick(float DeltaSeconds) OVERRIDE;

protected:
	// APawn interface
	virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) OVERRIDE;
	// End of APawn interface

	/** Get actor derived from UsableActor currently looked at by the player */
	class AUsableActor* GetUsableInView();

	/* Max distance to use/focus on actors. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
	float MaxUseDistance;

	/* True only in first frame when focused on new usable actor. */
	bool bHasNewFocus;

	/* Actor derived from UsableActor currently in center-view. */
	AUsableActor* FocusedUsableActor;

public:

	/** Use the actor currently in view (if derived from UsableActor) */
	UFUNCTION(BlueprintCallable, WithValidation, Server, Reliable, Category = PlayerAbility)
	virtual void Use();

*Character.cpp

#include "UsableActor.h"

AInvSystemCharacter::AInvSystemCharacter(const class FObjectInitializer& PCIP)
	: Super(PCIP)
{
	/* the default properties from FPStemplate */

	MaxUseDistance = 800;
	bHasNewFocus = true;
}

/*
	Performs raytrace to find closest looked-at UsableActor.
*/
AUsableActor* AInvSystemCharacter::GetUsableInView()
{
	FVector camLoc;
	FRotator camRot;

	if (Controller == NULL)
		return NULL;

	Controller->GetPlayerViewPoint(camLoc, camRot);
	const FVector start_trace = camLoc;
	const FVector direction = camRot.Vector();
	const FVector end_trace = start_trace + (direction * MaxUseDistance);

	FCollisionQueryParams TraceParams(FName(TEXT("")), true, this);
	TraceParams.bTraceAsyncScene = true;
	TraceParams.bReturnPhysicalMaterial = false;
	TraceParams.bTraceComplex = true;

	FHitResult Hit(ForceInit);
	GetWorld()->LineTraceSingle(Hit, start_trace, end_trace, COLLISION_PROJECTILE, TraceParams);

	return Cast<AUsableActor>(Hit.GetActor());
}

/*
	Update actor currently being looked at by player.
*/
void AInvSystemCharacter::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	if (Controller && Controller->IsLocalController())
	{
		AUsableActor* usable = GetUsableInView();

		// End Focus
		if (FocusedUsableActor != usable)
		{
			if (FocusedUsableActor)
			{
				FocusedUsableActor->EndFocusItem();
			}

			bHasNewFocus = true;
		}

		// Assign new Focus
		FocusedUsableActor = usable;

		// Start Focus.
		if (usable)
		{
			if (bHasNewFocus)
			{
				usable->StartFocusItem();
				bHasNewFocus = false;
			}
		}
	}
}

/*
	Runs on Server. Perform "OnUsed" on currently viewed UsableActor if implemented.
*/
void AInvSystemCharacter::Use_Implementation()
{
	AUsableActor* usable = GetUsableInView();
	if (usable)
	{
		usable->OnUsed(this);
	}
}

bool AInvSystemCharacter::Use_Validate()
{
	// No special server-side validation performed.
	return true;
}


//////////////////////////////////////////////////////////////////////////
// Input

void AInvSystemCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	/* all other input mappings here  */

	InputComponent->BindAction("Use", IE_Pressed, this, &AInvSystemCharacter::Use);
}

Note: I used COLLISION_PROJECTILE collision channel for the ray cast, you might want to update this to a special trace channel depending on your project in the future.

COLLISION_PROJECTILE is defined in the “MyProject.h” in your C++ code project if you started of with the FPSTemplate C++. This is what my “InvSystem.h” looks like:

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

#ifndef __INVSYSTEM_H__
#define __INVSYSTEM_H__

#include "Engine.h"
#include "EngineKismetLibraryClasses.h"

#define COLLISION_PROJECTILE	ECC_GameTraceChannel1

#endif

Setup your character input

It is important to bind “Use” to a key, this can be done through the Project Settings in Unreal. To open this window go to Edit > Project Settings…

Project Settings

Closing Words

If you completed the tutorial you have the basic framework to build your own blueprint interactions (eg. door, lever, pickup item, etc.) For a practical use go to part two of the tutorial series where I use this system to create a basic inventory & pickup system.

Basic Example

I’ve created a basic example of the above system in action, the blueprint sets “Render in CustomDepth” to true/false this is then used by a post-process material to create the outline effect.

The C++ code supports multiplayer, for this to work with your own Blueprint make sure you toggle “Replicates” in the Defaults-tab of your Blueprint. For the sample below that is all for it to work in a networked session, for more complex implementations like inventory systems etc. you may need to replicate additional variables and/or functions which is outside of this tutorial scope.

EvenGraph of UsableActor derived Blueprint:

To implement the functions you must right-click them in the Variables view of the Blueprint and click “Implement Function”

cpp_usableactor_updated01 cpp_usableactor_updated02 cpp_usableactor_updated03

ue4_tutorial_usableactor02

Additional Video Examples



Please feel free to leave some feedback or ask questions.

Continue to part two where we use this tutorial to build a basic inventory system!

Source is available on GitHub!

51 Responses

  1. Hi Tom,
    Thanks for sharing all the knowledge! Ive got a question which is a bit off from the ‘usable actor’ tutorial, since I decided to use an actor component. I’m having trouble getting the replication right, since I’m also using it on my player characters.

    Making it client functions, it skips the interact function (=multicast delegate) because it can’t find a valid owner on the player getting interacted with. And when using server functions it works out for the player characters, however can’t return a ref to the local player who’s interacting to add a widget on interact.

    Is there any ‘straight forward’ way you know of on how to get this to work?

    So I have both ‘normal’ actors and player characters i want to use the component on. And on interact I want to add a widget to the player screen who is interacting.

    (btw I read you worked at Guerrilla Games at some point? what year? We might have been colleagues for a short period)

    thanks for your time
    cheers
    Frank

    • What we did for routing RPCs was to use a component on the pawn/controller that will interact with the objects. Since otherwise you get your error with no channel available (when attempting to call client functions on actors in the world with no ‘owner’)

      So when a pawn interacts with the object, we pass in that pawn into the OnInteract() so that the code can check the pawn and look for the interaction component and call a Client/Server function as needed. I hope that makes sense.

      I was at Guerrilla around 2012-2015 I believe.

  2. Ive seen a few systems like this that instead use a useable interface instead of a base useable actor class. Which method do you think is better? It seems like the do the same thing but the syntax is a bit easier with a base class. Would like to know your thoughts.

    • These days I use an ActorComponent (or SceneComponent if you want to customize interaction point) instead of both options you mentioned.

      It’s very easy to access when needed and lets you avoid a single base class that may not always be desired. I generally don’t like interfaces at all (implementation and navigation in BP is lacking) so I only use them when necessary.

      I should probably do a fresh tutorial on that, this tutorial originated all the way back from the initial launch of Unreal 4.0

  3. Hello,Tom
    How to make OutLine in the same post processing show different colors, you Git”Tutorials” project “Map” in no ah.

  4. 4.12
    UsableCppCharacter.cpp
    61 line LineTraceSingle -> LineTraceSingleByChannel

    TutorialsProjectile.h, TutorialsProjectile.cpp
    void OnHit(AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
    ->
    void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)

    • The node to set the custom depth needs to be called on the static mesh component and not the actor (which is represented by the “self”) node.

  5. I’ve tried to implement this system to use for usable characters. Instead of AACtor base class I had ACharacter. But when i do cast to AUsableCharacter class it always fails, even though traced object’s class – child of AUsableCharacter

    • You need to make sure the collision works on the character. The channel we use for the trace may be ignored by your character. So double check your collision setup in your pawn. Dealing with channels can sometimes get a little tricky as there is a lot of filtering options available.

      – Tom

  6. Hi Tom,
    Great Tutorials, however I have a question regarding the pitch input (LookUp) for the CPP usable character class.

    The Default Pawn Class ‘UsableCppCharacter’ doesn’t seem to allow pitch input, however if I switch the Default Pawn Class to the ‘BP_UsableCppCharacter’ the pitch works as expected.

    I’m using the 4.9.2 Engine (with the Oculus Library Installed, but not it’s not currently connected).

    I’m wondering if the CPP LookUp input with the 4.9.2 engine worsk for you, and if it does, if you have any VR libraries installed.

    • I’ve not noticed any problems with lookup in C++ and I do have VR libraries installed. Been running 4.9 and currently on 4.10, neither have shown this issue.

  7. Hey Tom! Great tutorials they are much appreciated.

    I am having a problem where each client will see the highlight created by the others.

    I followed your tutorial pretty closely and made only 2 minor tweaks. I did not implement the use functions in both the character and actor. I just used the StartFocusItem, and EndFocusItem. Secondly, I implemented the SetRenderCustomDepth() in C++ rather than in blueprints (I did try blueprints and had the sample problem). Any suggestions?

    • I can only guess, but if clients see this than you might be ray-tracing for every pawn, not just the IsLocallyControlled() pawn. Meaning that whenever any client looks at an object, the others see the highlight just the same since 1 PC runs the code for every character he sees on screen including himself.

      – Tom

  8. Hi Tom,
    In the video, it shows interactions with weapons (I assume they are skeletal meshes) and cups (probably static meshes). However the implementation shown here is using AStaticMeshActor as a base class, how did you do it?

    I was thinking about using an interface for the functions, but since I want to have a UBoxComponent to for the interaction hitbox, I though it would be better to separate into several classes and use simple inheritance or use multiple-inheritance as long as I can avoid the “diamond of death”.

    • Hi Siveon,

      The weapons are actually static meshes, I re-imported them as static mesh 🙂

      Using an interface and/or a component is definitely a good approach of more abstract implementation that you can re-use in just about anything.

      – Tom

  9. heya im using 4.6 and managed to get the start and end focus events to work, but no matter what i try get onused to work is giving me greive.

    the code in the example just throws errors and any changes i have done that get it to run, result in it not doing anything.

    any help or tips would be greatly appreciated.

    Regards SM

  10. Tom could you pls let me know why this is giving me an error ?

    GetWorld()->LineTraceSingle(Hit, start_trace, end_trace, FColor(255, 255, 255), false, 1);

    specificly in the ->

    Sasys “no instance of overloaded function etc… “

    • Hi, make sure the function parameters match. As for example from the tutorial:

      GetWorld()->LineTraceSingle(Hit, start_trace, end_trace, COLLISION_PROJECTILE, TraceParams);

      Recheck if the types you pass into the function are correct.

      – Tom

    • as Tom said when you get this kind of error it usually means there is already a function with specific parameters that you are trying to change to a different type. double check that you are doing exactly as the tutorial says.
      On a side note sometimes during version changes a function may be redefined in a parent class and now you are trying to override it.
      I would do a quick search for the LineTraceSingle and make sure that you are following its defined parameters,

      cheers, DJ

      p.s. as always thanks Tom

    • Hi Shark,

      The “EndFocusItem” is a function that exists in an actor of type UsableActor, so make sure your variable is of the correct type and drag a line from that variable onto your canvas – the “EndFocusItem” will only appear if you have this variable as context (unreal’s context sensitivity for available node types is very strict)

      Hope that helps

      – Tom

  11. Hello

    I really appreciate these tutorials very helpful in understanding the way that unreal works with C++.
    But i have hit a problem in this tutorial the code:

    if (FocusedUsableActor)
    {
    FocusedUsableActor->EndFocusItem();
    }

    // Assign new Focus

    if (bHasNewFocus)
    {
    usable->StartFocusItem();
    bHasNewFocus = false;
    }
    when i do this in my C++ code i get a compiler error with the FocusedUsableActor->EndFocusItem();
    and the usable->StartFocusItem();

    The error i receive is that the FocusedUsableActor and usable is a incomplete pointer.
    I am using 4.6.1

    Thanks Guys

    • Hi James

      This issue is likely caused because you missed an include “#include SUsableActor.h” in your Character.cpp

      Let me know if it helps!

      – Tom

  12. Hello,

    I was trying to use this inventory system, but I failed to make the blueprint implementable events look visible inside Unreal Editor.

    AdventureCharacter.h: http://pastebin.com/bt0jj7Nb
    AdventureCharacter.cpp: http://pastebin.com/7NSYhz1Q
    UsableActor.h: http://pastebin.com/n2T7nzAZ
    UsableActor.cpp: http://pastebin.com/PTKwg2vC

    I would greatly appreciate any help to overcome the problem. Otherwise, this is a great tutorial, really helpful with the UE4 specific programming. Cheers!

    • Hi Cinar,

      Sorry, I couldn’t open the pastebins. If you’re still having some issues with your character let me know!

      – Tom

  13. Thank you for this C++ tutorial this is really helpfuly with the project that i am working 😀

    keep up the good work

  14. I cant edit the statement above but I see there is replication for the USE but it is not working.
    Use is only working if you are the server. Clients are not able to Use the object.

    • Hi Emilio!

      I looked into the code and found two things that require an update.

      First set “Replicates” to true in the BP_PickupActor, category Replication.

      The next step is to update the following function:

      void AInvSystemCharacter::Use_Implementation()
      {
      AUsableActor* usable = GetUsableInView();
      if (usable)
      {
      usable->OnUsed(this);
      }
      }

      That should properly execute on the server, the focused item is null on the server-side, so for simplicity I added another line trace to find what we’re looking at.

      Hope that helps, I will update the original tutorial when I can.

  15. ok resolved the problem but of course the include is still necessary since the update to 4.2. And of course there is no replication of the USE. Thanks for this it will help everyone.

  16. So I am having a pointer issue which I am not sure how to resolve I can assume it maybe has to do with the newest version of the UE4 engine.

    Well at least one was the include “#include “EngineKismetLibraryClasses.h” has been changed to #include “Kismet/GameplayStatics.h”

    MyCharacter.cpp(89): error C2027: use of undefined type ‘AUsableActor’ which refers to

    if (FocusedUsableActor)
    {
    FocusedUsableActor->EndFocusItem();
    }

    MyCharacter.h(32) : see declaration of ‘AUsableActor’ which is referring to
    class AUsableActor* GetUsableInView();

    instead of
    /* Actor derived from UsableActor currently in center-view. */
    AUsableActor* FocusedUsableActor;

    any ideas?

  17. I too would love to see this done via BP. Your system flow is seriously clean and if I possessed even a shred of C++ experience, I would attempt this. However, I am trying to add an inventory system (of which yours looks to be the best build so far) to an existing 3rd Person BP Template. I would absolutely LOVE to see this tutorial done with BP morons like me in mind.

    • Hi Grabiel,

      Thanks! I have received a lot of feedback and requests to see this tutorial in Blueprint. I’ve bumped it on my todo list, although I can’t make any promises on when it will be out.

  18. Thank you very much!
    I’m new to working with Unreal Engine.
    Be so kind to help me with another problem.
    I open my character Blueprint -> I want to add a function “OnUsed” or “Event Start Focus Item” but they were not present.
    What is my error?
    ==================
    Character.h: http://pastebin.com/tYKciZQG
    Characher.cpp: http://pastebin.com/DYnpk3zn
    UsableActor.h: http://pastebin.com/r3kHL8f3
    UsableActor.cpp: http://pastebin.com/dxucygrZ
    Thanks in advance!

    • Hi!

      The “OnUsed” events etc. will exist in UsableActor derived blueprints, so not your character blueprint. What you must do is create a new Blueprint that has a parent class of “UsableActor” (select this instead of Actor etc when you are asked what class you want to use for your new blueprint) Now you should be able to find the “OnUsed” events etc. in your newly created blueprint.

      Once you have found those events look for a more advanced example in part 2 (where I call a function from UsableActor to your character)

    • Hi Vasya,

      Try adding the following line near the top of your MyProjectCharacter.h (I placed it right after GENERATED_UCLASS_BODY())

      virtual void Tick(float DeltaSeconds) OVERRIDE;

      I will update the tutorial with this new line!

  19. As nice as this tutorial is, it doesn’t correspondently relate to someone who has already developed a project and wanted to incorporate this. I currently am running off a strictly blueprinted project at the moment and can’t seem to find a way to place this code into my character.h/.cpp files without corrupting things.
    If you have another workaround, please let me know.

    • Hi. Thanks for your feedback. I will update the tutorial so it’s more clear for people starting of with blueprints template.

      • Don’t get me wrong friend, this tutorial is one of the best out there and I am eager to share it with the community! Thanks for the amazing work.

Leave a comment on this post!