Adding custom Surfaces and Collisions

Using custom surfaces and collisions is brilliant way to handle the game logic and code flowing. Designing collision and surface dynamic between actors from beginning of the project will save you a lot of headache later when your project grows bigger and bigger. More channels you have, more easily you control the flow of the code.


Starting up!

We begin by creating a C++ project based on First Person template. After that, we create a Collision channel from Project Settings -> Collisions. Click ”New Trace Channel”. Give it a name. We call it ”WeaponTrace” and set default as ”Block”

 

Next we need to add a line trace. We create a new key binding from Project Settings – Input. Give it a name. We call it Trace and Key is E

Dwelving inside the code

Next, we add header file to our Characters .cpp file so we can use the math functions

#include "Kismet/KismetMathLibrary.h"

After that is done, we are going to create a function that will do our line tracing. In character header file we create a function StartTrace();

void StartTrace();

Define it in .cpp and add some tracing logic

void AMyProjectCharacter::StartTrace()
{
// Hit Struct that will be filled if succesfull hit.
 FHitResult Hit;
 FVector TraceStart = FirstPersonCameraComponent->GetComponentLocation();
 FVector TraceEnd = (UKismetMathLibrary::GetForwardVector(FirstPersonCameraComponent->GetComponentRotation())) * 500 + TraceStart;
 FCollisionQueryParams QueryParams;
 QueryParams.AddIgnoredActor(this);
 
 if (GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Visibility, QueryParams))
 {
 // Debug our Line if hit was successfull
 UKismetSystemLibrary::DrawDebugLine(GetWorld(), TraceStart, TraceEnd, FColor::Yellow, 1, 1);
}
 }

Also, we need to add the call to our function after the key pressing. We do that inside the SetupPlayerInputComponent() of course :).

PlayerInputComponent->BindAction("Trace", IE_Pressed, this, &AMyProjectCharacter::StartTrace);

Now we can successfully trace forward from our camera by pressing the E button. Next, it is time to put our trace channel to test. Go ahead and open your projects MyProject.h file. It should be totally empty. This is very good place to add some definitions. We create definition to the first of the free slots. Each time you add your own collision channel, it will reserve one slot for you. So for now, the first free slot is ECC_GameTraceChannel1. For some reason, the counting may start from ECC_GameTraceChannel2. So keep that in mind if for some reason the collision isn’t working correctly.

#define COLLISION_WEAPON ECC_GameTraceChannel1
// #define COLLISION_WEAPON ECC_GameTraceChannel2

Next, we include MyProject.h header file so we can use our #Defines

#include "MyProject.h"

Let’s go back to our Trace function and change it a little. We Don’t use ECC_Visibility channel anymore, but we use the COLLISION_WEAPON channel. So we change our IF-statement a little bit.

 if (GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, COLLISION_WEAPON, QueryParams))

Now, we can verify again that collision still works by playing the game. Now, go ahead and click one of the cubes inside game. Go to it’s static mesh properties and into collision settings. You can see our WeaponTrace channel there. Set it to Ignore. Now when playing the game, we can verify that the tracing is indeed using our custom channel by trying to trace the cube we set to ignore the WeaponTrace. It’s simple as that!


Adding new Surface Type

Adding surface works almost identically with the collision channel. First we create our suface by going to Project settings -> Physics and and add our surfaces to surface types. Set SurfaceType1 to be ”MySurface”

Go and edit MyProject.h and add another #define. Notice that we set SurfaceType1 to be MySurface

#define MY_SURFACE SurfaceType1

Next, go to your content browser and create new Physical Material. Name it for example ”MyPhysMat”

Now, you may need to restart the editor so go ahead and do that. After restart, open your newly created physical material and set it to be ”MySurface”

Edit one of the Cubes inside level and set it’s physical material to your newly created one.

Now back to the characters .cpp file. We add one header file that can handle the Physical Material

void AMyProjectCharacter::StartTrace()
#include "PhysicalMaterials/PhysicalMaterial.h"

We edit our tracing function again.

void AWebsiteProjectCharacter::StartTrace()
{
  // Hit Struct that will be filled if succesfull hit.
  FHitResult Hit;
  FVector TraceStart = FirstPersonCameraComponent->GetComponentLocation();
  FVector TraceEnd = (UKismetMathLibrary::GetForwardVector(FirstPersonCameraComponent->GetComponentRotation())) * 500 + TraceStart;
  FCollisionQueryParams QueryParams;
  QueryParams.AddIgnoredActor(this);
  // We need to add this to return the surface we create later
  QueryParams.bReturnPhysicalMaterial = true;
  UE_LOG(LogTemp, Warning, TEXT("Tracing"));
	
  if (GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, COLLISION_WEAPON, QueryParams))
    {
       UE_LOG(LogTemp, Warning, TEXT("%s"), *Hit.GetActor()->GetName());
//     Get The Surface type we hit!
       EPhysicalSurface SurfaceType = UPhysicalMaterial::DetermineSurfaceType(Hit.PhysMaterial.Get());
//     Are we hitting the SurfaceType1 (aka. MY_SURFACE)		
       if (SurfaceType == MY_SURFACE)
       {
	 UE_LOG(LogTemp, Warning, TEXT("We hit our custom surface!"));
       }
       UKismetSystemLibrary::DrawDebugLine(GetWorld(), TraceStart, TraceEnd, FColor::Yellow, 1, 1);

    }

 }

That’s about it! Now you can successfully create your own tracing channels and surfaces and name them easily using the #define. Happy coding!

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *