Category Archives: How to make a Spider AI in UE4

Series of posts that blog the making of a Spider in Unreal Engine 4

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 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
Memory Game Despicable Me Edition
by Hasbro Memory Game Despicable Me Edition(135) Buy new: $9.99 $5.99 45 used & new from $5.84
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
A Shade of Vampire 17: A Wind of Change
Bella Forrest A Shade of Vampire 17: A Wind of Change 21 days in the top 100 A Shade of Vampire 17: A Wind of Change(23) Download: $3.99
by

How to make a Spider in UE4 Part 2

SpiderHitNormal

 

Picking up from Part 1 where we left the Spider able to detect what surface is in front and under itself.

The next step is to get the surface angle of the detected object. The main reason is we want this Spider to be able to navigate any landscape and level geometry made in the Editor, and not just geometry that has been marked up with special tags for querying.

The FHitResult contains the ImpactNormal. This is the Normal of the surface, regardless of the angle the trace intersected at.

I added another trace directly down so that a comparison could be done. The above screenshot shows the results of the traces. The Floor shows a Z value of 1, and the Wall shows a Y of -1.

The walls will have a non zero value in X or Y, and the floor and presumably ceiling will have a non zero value in Z. Testing just against if an X or Y would equal a -1 or 1 would mean the spider would only think it could go up perfectly orthogonal walls. But, just testing if Not Zero found hits everywhere.

if (HitNormal.Y <= -0.02 || HitNormal.Y >= 0.02 || HitNormal.X <= -0.02 || HitNormal.X >= 0.02)

I picked up some “principals” of good practice that I try to adhere to when programming. One of them is not to use “Magic Variables”. In the above test 0,02 is a magic variable. I’ll change it out for a named one; maybe, “WallTestThreshold”.

 

Now that the Spider can tell when it can transfer to a wall, the next step is to actually transfer it.

ICanWallWalk

 

Share Button
by

How to make a Spider in UE4 Part 1

Wall walking, ceiling walking, going around corners, etc.

This will blog my progress through doing this small project. It will primarily be done in C++ and I won’t go into details of making the model or animations, but I will show how I set those up once they have been imported into the Engine.

 

SimpleCast

 

For the spider to be able to walk up walls, transfer onto the ceiling, or even around corners on walls it needs to be able to detect when it can.

I choose to create Sockets on the skeleton and then get there location in C++ to perform traces.

SpiderSockets

 

In C++, get the location and then run traces from the Socket above, to the separate feelers in front, down and diagonal.

Note: I need to find a better syntax highlighter for WP, this is WP-Code-Highlight. Recommendations are welcome. The right arrows are inconsistent.

 

// Tick animations before physics.
if (CharacterOwner->GetMesh())
{
const FVector Start = CharacterOwner->GetMesh()->GetSocketLocation("DetectionSocket");

const FVector FLSocketLocation = CharacterOwner->GetMesh()->GetSocketLocation("FL");
const FVector RLSocketLocation = CharacterOwner->GetMesh()->GetSocketLocation("FR");

FHitResult HitDataFL(ForceInit);
FHitResult HitDataRL(ForceInit);

if (Trace(CharacterOwner, Start, FLSocketLocation, HitDataFL, ECollisionChannel::ECC_Pawn, false, true))
{
   //Print out the name of the traced actor
   if (HitDataFL.GetActor())
   {
            print(HitDataFL.GetActor()->GetName());
    }
}

if (Trace(CharacterOwner, Start, RLSocketLocation, HitDataRL, ECollisionChannel::ECC_Pawn, false, true))
{
   //Print out the name of the traced actor
   if (HitDataRL.GetActor())
   {
       print(HitDataRL.GetActor()->GetName());
   }
}

 

This will provide the actor under each hit. This method alone would require a naming scheme to be implemented in all the levels to actually use it, so it is just for debugging. We will test the angle on hits to determine whether it’s a transition and what type.

It can be seen in the below picture, how the right feeler detects a hit, and the left does not.

SpiderFeelers

 

In the next picture, if you look at the printed comments you can see that the floor and wall are detected separately.

SpiderWallFloorDetection

Share Button
by