Jump to content
  • Advertisement
  • 01/23/18 04:04 PM

    Designing Player World Interaction in Unreal Engine 4

    Engines and Middleware

    Martin H Hollstein

    Originally posted on Troll Purse development blog.

    Unreal Engine 4 is an awesome game engine and the Editor is just as good. There are a lot of built in tools for a game (especially shooters) and some excellent tutorials out there for it. So, here is one more. Today the topic to discuss is different methods to program player world interaction in Unreal Engine 4 in C++. While the context is specific to UE4, it can also easily translate to any game with a similar architecture.

    Interaction via Overlaps

    By and far, the most common tutorials for player-world interaction is to use Trigger Volumes or Trigger Actors. This makes sense, it is a decoupled way to set up interaction and leverages most of the work using classes already provided by the engine. Here is a simple example where the overlap code is used to interact with the player:

    Header

    // Fill out your copyright notice in the Description page of Project Settings.
    
    #pragma once
    
    #include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "InteractiveActor.generated.h"
    
    UCLASS()
    class GAME_API InteractiveActor : public AActor
    {
    	GENERATED_BODY()
    
    public:
    	// Sets default values for this actor's properties
    	InteractiveActor();
    
        virtual void BeginPlay() override;
    
    protected:
    	UFUNCTION()
    	virtual void OnInteractionTriggerBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
    
    	UFUNCTION()
    	virtual void OnInteractionTriggerEndOverlap(UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
    
        UFUNCTION()
        virtual void OnPlayerInputActionReceived();
    
    	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Interaction)
    	class UBoxComponent* InteractionTrigger;
    }

    This is a small header file for a simple base Actor class that can handle overlap events and a single input action. From here, one can start building up the various entities within a game that will respond to player input. For this to work, the player pawn or character will have to overlap with the InteractionTrigger component. This will then put the InteractiveActor into the input stack for that specific player. The player will then trigger the input action (via a keyboard key press for example), and then the code in OnPlayerInputActionReceived will execute. Here is a layout of the executing code.

    Source

    // Fill out your copyright notice in the Description page of Project Settings.
    
    #include "InteractiveActor.h"
    #include "Components/BoxComponent.h"
    
    // Sets default values
    AInteractiveActor::AInteractiveActor()
    {
    	PrimaryActorTick.bCanEverTick = true;
    
    	RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
    	RootComponent->SetMobility(EComponentMobility::Static);
    
    	InteractionTrigger = CreateDefaultSubobject<UBoxComponent>(TEXT("Interaction Trigger"));
    	InteractionTrigger->InitBoxExtent(FVector(128, 128, 128));
    	InteractionTrigger->SetMobility(EComponentMobility::Static);
    	InteractionTrigger->OnComponentBeginOverlap.AddUniqueDynamic(this, &ABTPEquipment::OnInteractionProxyBeginOverlap);
    	InteractionTrigger->OnComponentEndOverlap.AddUniqueDynamic(this, &ABTPEquipment::OnInteractionProxyEndOverlap);
    
    	InteractionTrigger->SetupAttachment(RootComponent);
    }
    
    void AInteractiveActor::BeginPlay()
    {
        if(InputComponent == nullptr)
        {
            InputComponent = ConstructObject<UInputComponent>(UInputComponent::StaticClass(), this, "Input Component");
            InputComponent->bBlockInput = bBlockInput;
        }
    
        InputComponent->BindAction("Interact", EInputEvent::IE_Pressed, this, &AInteractiveActor::OnPlayerInputActionReceived);
    }
    
    void AInteractiveActor::OnPlayerInputActionReceived()
    {
        //this is where logic for the actor when it receives input will be execute. You could add something as simple as a log message to test it out.
    }
    
    void AInteractiveActor::OnInteractionProxyBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
    {
    	if (OtherActor)
    	{
    		AController* Controller = OtherActor->GetController();
            if(Controller)
            {
                APlayerController* PC = Cast<APlayerController>(Controller);
                if(PC)
                {
                    EnableInput(PC);
                }
            }
    	}
    }
    
    void AInteractiveActor::OnInteractionProxyEndOverlap(UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
    {
    	if (OtherActor)
    	{
    		AController* Controller = OtherActor->GetController();
            if(Controller)
            {
                APlayerController* PC = Cast<APlayerController>(Controller);
                if(PC)
                {
                    DisableInput(PC);
                }
            }
    	}
    }

     

    Pros and Cons

    The positives of the collision volume approach is the ease at which the code is implemented and the strong decoupling from the rest of the game logic. The negatives to this approach is that interaction becomes broad when considering the game space as well as the introduction to a new interactive volume for each interactive within the scene.

    Interaction via Raytrace

    Another popular method is to use the look at viewpoint of the player to ray trace for any interactive world items for the player to interact with. This method usually relies on inheritance for handling player interaction within the interactive object class. This method eliminates the need for another collision volume for item usage and allows for more precise interaction targeting.

    Source

    AInteractiveActor.h

    // Fill out your copyright notice in the Description page of Project Settings.
    
    #pragma once
    
    #include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "InteractiveActor.generated.h"
    
    UCLASS()
    class GAME_API AInteractiveActor : public AActor
    {
    	GENERATED_BODY()
    
    public:
        virtual OnReceiveInteraction(class APlayerController* PC);
    }

     

    AMyPlayerController.h

    // Fill out your copyright notice in the Description page of Project Settings.
    
    #pragma once
    
    #include "CoreMinimal.h"
    #include "GameFramework/PlayerController.h"
    #include "AMyPlayerController.generated.h"
    
    UCLASS()
    class GAME_API AMyPlayerController : public APlayerController
    {
    	GENERATED_BODY()
    
        AMyPlayerController();
    
    public:
        virtual void SetupInputComponent() override;
    
        float MaxRayTraceDistance;
    
    private:
        AInteractiveActor* GetInteractiveByCast();
    
        void OnCastInput();
    }

     

    These header files define the functions minimally needed to setup raycast interaction. Also note that there are two files here as two classes would need modification to support input. This is more work that the first method shown that uses trigger volumes. However, all input binding is now constrained to the single ACharacter class or - if you designed it differently - the APlayerController class. Here, the latter was used.

    The logic flow is straight forward. A player can point the center of the screen towards an object (Ideally a HUD crosshair aids in the coordination) and press the desired input button bound to Interact. From here, the function OnCastInput() is executed. It will invoke GetInteractiveByCast() returning either the first camera ray cast collision or nullptr if there are no collisions. Finally, the AInteractiveActor::OnReceiveInteraction(APlayerController*)  function is invoked. That final function is where inherited classes will implement interaction specific code.

    The simple execution of the code is as follows in the class definitions.

    AInteractiveActor.cpp

    void AInteractiveActor::OnReceiveInteraction(APlayerController* PC)
    {
        //nothing in the base class (unless there is logic ALL interactive actors will execute, such as cosmetics (i.e. sounds, particle effects, etc.))
    }

     

    AMyPlayerController.cpp

    AMyPlayerController::AMyPlayerController()
    {
        MaxRayTraceDistance = 1000.0f;
    }
    
    AMyPlayerController::SetupInputComponent()
    {
        Super::SetupInputComponent();
        InputComponent->BindAction("Interact", EInputEvent::IE_Pressed, this, &AInteractiveActor::OnCastInput);
    }
    
    void AMyPlayerController::OnCastInput()
    {
        AInteractiveActor* Interactive = GetInteractiveByCast();
        if(Interactive != nullptr)
        {
            Interactive->OnReceiveInteraction(this);
        }
        else
        {
            return;
        }
    }
    
    AInteractiveActor* AMyPlayerController::GetInteractiveByCast()
    {
        FVector CameraLocation;
    	FRotator CameraRotation;
    
    	GetPlayerViewPoint(CameraLocation, CameraRotation);
    	FVector TraceEnd = CameraLocation + (CameraRotation.Vector() * MaxRayTraceDistance);
    
    	FCollisionQueryParams TraceParams(TEXT("RayTrace"), true, GetPawn());
    	TraceParams.bTraceAsyncScene = true;
    
    	FHitResult Hit(ForceInit);
    	GetWorld()->LineTraceSingleByChannel(Hit, CameraLocation, TraceEnd, ECC_Visibility, TraceParams);
    
        AActor* HitActor = Hit.GetActor();
        if(HitActor != nullptr)
        {
            return Cast<AInteractiveActor>(HitActor);
        }
    	else
        {
            return nullptr;
        }
    }

     

    Pros and Cons

    One pro for this method is the control of input stays in the player controller and implementation of input actions is still owned by the Actor that receives the input. Some cons are that the interaction can be fired as many times as a player clicks and does not repeatedly detect interactive state without a refactor using a Tick function override.

    Conclusion

    There are many methods to player-world interaction within a game world. In regards to creating Actors within Unreal Engine 4 that allow for player interaction, two of these potential methods are collision volume overlaps and ray tracing from the player controller. There are several other methods discussed out there that could also be used. Hopefully, the two implementations presented help you decide on how to go about player-world interaction within your game. Cheers!

     

     

    Originally posted on Troll Purse development blog.



      Report Article


    User Feedback


    There is a typo in class declaration should be class AInteractiveActor instead of class InteractiveActor. Also identation is broken (mix of spaces and tabs).

    Edited by RootKiller

    Share this comment


    Link to comment
    Share on other sites
    On 1/23/2018 at 12:20 PM, RootKiller said:

    There is a typo in class declaration should be class AInteractiveActor instead of class InteractiveActor. Also identation is broken (mix of spaces and tabs).

    Thanks, I will fix that up in time. Any thoughts on the content other than that? Was this useful?

    Share this comment


    Link to comment
    Share on other sites

    Thanks for the post.

    As a none programmer I never find the time to develop new code like this, so having someone post there work and explain it is great for me to read.

    Share this comment


    Link to comment
    Share on other sites


    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

  • Advertisement
  • Game Developer Survey

    completed-task.png

    We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a $15 incentive for your time and insights. Click here to start!

    Take me to the survey!

  • Advertisement
  • Latest Featured Articles

  • Featured Blogs

  • Advertisement
  • Popular Now

  • Similar Content

    • By Harry Loretz
      Hello everyone, 

      I appreciate any help i can get. So i recently started getting this message listed bellow in visual studio when i would compile any game i have for UE4.23.0. 

      1>EXEC : [1/28] error : Unable to create child process
      I have no idea how to fix this, and also i really don't want to have to set all the classes again in a new project.

      Second thing is that i can not move my Character on the Sever when testing multiplayer after adding "Role = ROLE_Authority" to sections in an AI Tracker bot class that i created as a part of Tom Loomans Course on Udemy. I am not following Tom Loomans course 100%, Do i need to add sever implementation to my character because i am using different movement code then what Tom uses? I Am happy to share some screen shots if needed. 
       
      Please help, thankyou for your time.
    • By intenscia
      mod.io is an cross platform mod service created by the team behind ModDB.com and IndieDB.com. It can be integrated in-game using the  REST API, C/C++ SDK or engine plugins (if available) Unity and Unreal Engine are ready to use with other engine plugins in development.
      Features include:
      Platform agnostic (support 1 click mod installs on Steam, Epic Games Store, Discord, GOG, itch.io and even consoles in the future) Clientless (mod.io has no other dependencies and works behind the scenes in your game) Embeddable web app UI, so you can integrate your mod community anywhere Powerful search, filtering and tagging of mods Moderation and reporting systems built-in Steps to getting mod.io integrated:
      Add your game to our test environment or production Read our API documentation for an overview of how mod.io works Choose an Engine Plugin, API or SDK to integrate mod.io into your game and mod making tools Ready to launch? Add your game to our production environment then let's discuss promoting your release Need help? Our team is available on Discord to assist and our getting started guide has more information for you  
       
      Benefits of using mod.io:
      mod.io offers the same core functionality as Steamworks Workshop (1 click mod installs in-game), plus mod hosting, moderation and all of the critical pieces needed. Where we differ is our approach to modding and the flexibility a REST API offers. For example: 
      Our API is not dependent on a client or SDK, allowing you to run mod.io in many places such as your homepage and launchers Designing a good mod browsing UI is hard, our plugins ship with a UI built in to save you a lot of effort and help your mods stand out We don’t apply rules globally, so if you want to enable patronage, sales or other experimental features, reach out to discuss Our platform is built by the super experienced ModDB.com team and is continually improving for your benefit Your community can consume the mod.io API to build modding fan sites or discord bots if they want Large studios and publishers:
      A private white label option is available to license, if you want a fully featured mod-platform that you can control and host in-house. Contact us to discuss.
      Find out more:
      Visit mod.io | About us | Add your game | Chat on Discord
      These screenshots are from our Unity plugin:




    • By phil67rpg
      I have a very simple question, I am trying to rotate some vertex's around an arbitrary axis. basically I want to use glRotatef and glTranslatef to rotate a space ship I have drawn on the screen. here is my code of my  ship. what it does do is rotate around the origin when I  use the arrow keys left and right.
      void drawShip() { glPushMatrix(); glColor3f(255.0f, 0.0f, 0.0f); glTranslatef(-50.0f, 0.0f, 0.0f); glRotatef(rotateship, 0.0f, 0.0f, 1.0f); glBegin(GL_LINE_LOOP); glVertex3f(50.0f, 0.0f, 0.0f); glVertex3f(45.0f, -5.0f, 0.0f); glVertex3f(50.0f, 10.0f, 0.0f); glVertex3f(55.0f, -5.0f, 0.0f); glEnd(); glTranslatef(50.0f, 0.0f, 0.0f); glPopMatrix(); }  
    • By G-Dot
      Hello everyone! I've decided to implement a destructible enemies system.
       
      Description:
      When bullet hit enemy in specific part of his body(arm fo example) armour, which covers that part of body, will fall off. Enemies in my game are robots so this means that when shooting them certain plates of their armour will fall off. All enemies will have a different amount of armour plates
       
      My solution: 
      The only solution I came up with is to make an actor with a static mesh and attach it to the bones of enemy's skeletal mesh. When bullet hit that actor it detaches and fly away with add impulse node.

       
      Question: 
      Maybe there is a better solution, which I'm missing and it's more efficient. 
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!