UE4 Very Long Game Load Times

There are a couple schools of thought on optimization. With any rule in programming, use at your own risk, for your own situation. The one I generally follow is make it work correctly now, optimize it later. Optimizing is time consuming and it locks in an implementation. If there are design changes to be made, it can make it that much harder to change and thus put resistance on the designer to making the change. If it’s loose at first, it can be changed, or thrown out with less attachment.

This doesn’t mean don’t care about any performance, still follow best practices upfront. Two big area’s in UE4 are how meshes are instanced and how much processing is done in a tick. I won’t go into those now, this post is specifically on long load times. I see it on AnswerHub a lot, and I’ve run into it myself. In my next UE4 project I will probably amend my philosophy and spend more attention up front to some of the load times, knowing now how out of control it can get.

Luckily, UE4 provides plenty of ways to improve load times.

The problem: I double click in Steam for my game to play. This is after Steam itself took a long time to make sure it was updated. A little while goes by and then my startup movies play. I could have lived with the time before the movies started. But after the last one, the screen is just black, for a long time. On my desktop it was around 1 minute, longer on my laptop.

It’s not good when you wonder if your game is going to load or if it has crashed. I wasn’t sure where to begin so just kept putting the task off. Last week I went to the Austin Game Convention and showed Lemons Must Die to a few people. I brought it by the Epic booth and at the end asked Zac Parrish if he had any thoughts on it. He thought it was probably loading too many assets and that was the problem that they saw with a lot of Indie Studio’s. He went on to say that any reference is completely loaded, and that casting is a reference.

Between my Game Instance and Player controller basically the entire game was referenced and loaded. Interfaces were recommended as a way to fix it, but he had a kind of sad face when he said it.

I have another rule with optimizing. I only optimize what I have actually tested, not by my intuition. Basically, need to run a profiler or actual tests before and after I optimize something. Intuition can waste a lot of time optimizing things that don’t give that much gain. I broke my rule here.

I went through my main game classes: GameInstance, GameState, PlayerCharacter, PlayerController, BaseProjectile, and all the UI Widgets, and removed every single cast replacing it with an Interface Message. I kept it simple and just made a Blueprint Interface for each class. It took about 5 hours to go through everything, and in the middle of it I was worried that it would never end.

Just doing that brought my load time, between the last startup movie and the Menu opening playable from over 1 minute to less than 2 seconds. The reason that this worked so dramatically for my game is that Between the PlayerController and the GameInstance classes virtually everything in the game was referenced via casting, but they didn’t have hard references. The GameInstance has Dictionaries ( Maps ) with many classes and associated id’s but that doesn’t seem to cause an issue, only references which casts are.

Here is a link describing how to hunt down load time problems through profiling, which is the analytical way to go about it: debugging-and-optimizing-memory

Share Button

Boss Fight Weekend

It’s been more than a year since I last wrote a post on here. That doesn’t mean I haven’t been developing in UE4, the opposite really. Trying to find the time to write never happened, so I making some time. It’s not a tutorial though. I don’t think tutorials help people become better developers, they are needed as a baseline to learn a complex API, Framework but I’m more interested in the theory of approaches to solving problems and more general information than specific tutorials.
img_8790
Some backstory on the why it’s Boss Fight weekend.

My last blog post was on a Utility AI system. I had an Indie Game in development called I Can Has Lazer. Yes, that’s past tense, I had. It was a a side project of my and one artist. We both ended up getting contract work on porting Hawken to consoles, so we had day jobs, and then this port with Hawken’s crazy in depth UI to make it work on consoles. There went all my time last winter. I worked about 65 hours a week plus commute times to my day job. There was no time left over for the Cat game. As well, the Cat Game never became fun. We had lots of cool tech things in it. I especially liked the world select screen.

But, the game just wasn’t fun. Earlier in 2015 I did contract work making a proof of concept on a UE4 game. The idea of it to me was crazy. It honestly was fun to play in the first couple weeks of development. The idea was that you were a Lemon in a deep grieving process of the loss of your fiancee or wife. But, really you maybe weren’t a Lemon, but a man, grief stricken and having a seriously bad intoxicated weekend bender and this was all a complex hallucination in your head. Funding wasn’t in place that I could just work on the game and complete it. But, I saw that this could really be fun and also didn’t want to see the Designer waste his money on only a proof of concept. So I committed to complete the development on it for royalties. At this point working on the game has pretty much consumed my entire life outside of my day job. We are scheduled to launch February 1st 2017. Our initial launch date was July of 2016, so there’s been serious slippage. The amount of work in completing a Video Game, even a small one is incredible.

Here’s a link to the game’s site: www.lemxns.com

I first heard about Triage when I worked as a Level Designer at The 3DO Company in 2001. The metaphor is for game features but based on how Military casualties are handled. I don’t know if it’s actually how casualties were treated in Viatnam but this is the story I was given.

Wounded soldiers in the hospital tent, and there are too many compared to doctors and nurses so this is how they categorized them to save maximum lives. Divide them into 3 catagories. This is where it get’s it’s name. Triage.

First category: These guys ( or gals ), are going to die no matter what is done to them. No matter how much time, attention, surgery etc. that they get, they are going to die.

Second category: These soldiers will live no matter what is, or is not done to them.

Third category: These soldiers could go either way. If they get time and attention they will probably live, but if they don’t they will probably die.

There is a point in game development where you just have to finish the game. We were at a point where all the major game features were complete and fixed every single bug and called that Alpha. But, the game wasn’t really a game yet. It was just a tech demo of game features that while they worked and were fun, it definitely wasn’t a game.

We got a booth in IGN Middle East, which was awesome because there were only 4 Indie Games at the event so we knew we’d get a lot of exposure, to the people that attended at least. We used that as a deadline to make a playable demo. It was the first time the cut scenes and level progression was really in place. The event went great and we got feedback through surveys on the game. We asked very specific A /B type questions. Nothing open ended such as. Did you like the controls? Instead would ask did you prefer the Keyboard and mouse or the Gamepad? From the feedback we addressed all the issue’s in the next couple weeks, but them it looked like we were going right back into a cycle of continuous improvement of what we allready had but not completing the game.

img_8788

We also missed every one of our previous deadlines. The demo for IGN Middle East was the first one we actually succeeded in completing on time.

So we set an aggressive schedule to complete the game and are applying Triage Techniques to game features. A lot of our game is procedural generated so art assets just need to get added to data lists, but some of our levels, especially the Boss Fights are designer sculpted and work in conjunction with the particular Boss. The Bosses’s also need all their animations setup to work along with their AI and any other scripted elements for their fight.

We gave ourselves 2 weeks to complete our first 2 worlds. We somehow added in an entire Boss fight and made the deadline. In fairness, we had been working on these for more than a year so it was really just a wrap up. We are in the 3rd world phase which our deadline is in 3 days from my writing this. It needs a Boss, the Art was complete for this. There are 2 more worlds which we allotted 2 weeks each too. If we stick to our schedule the game will be content complete December 31st. At which point we need to add in the achievements and make the builds for Mac and Linux all work on Steam.

If we end up having to Triage too many features, we may have to again slip our publish date. It is very important to us that we put out a complete game. If we call it early access it is just because we don’t have a team of testers and anticipate there being some bugs we need to fix. We will not publish it with the idea of still needing to add to it.

So, the Boss Fight weekend.

Oh, one more thing. John Romero talked about one of the principals they used at Id was never to leave a bug. As soon as they found one, they would fix it right there. We had a large list prior to Alpha, but took care of all of them. We have been keeping it zero bug basically on a weekly basis since Alpha. Zero known bugs, but I address them as soon as one is seen.

Last night instead of going to the comic book store and playing Friday Night Magic I fixed bugs, and did the technical setup of the Boss so that over the weekend I had a clear path to create the fight scenario. We allready had 2 Boss fights. The first is a swarm of zombies and the 2nd is a giant creature that charges. This fight is stationary, with a multi headed creature.

One important feature to a Boss fight is. The player should know they are in a Boss fight. We can change the music, but that’s not quite enough, so we wanted to change the HUD as well. Our character, Mr. Lemon has a juice squeezer that empties over time and as he’s damaged so we made one to represent the Boss. That should also give the player some indecation on how much more they need to damage the Boss since they do have a lot of health.

kingpulp

That is what the Boss looks like in the Editor after I set up his basic classes. Below is what the HUD looks like in the Designer now. It shows all the elements, we make invisible uneeded elements, such as the lives and lemon slices and reveal them as the game state requires. So, the Boss health will be in the lower right corner and we will reveal it on Boss levels.

hud-designer

I was given a stack of animations but it’s basically my job to make it into an interactive fight. The Boss is stationary and most of it’s attacks are close in, it has a breath attach for example. So I was thinking, the player will just want to stand far away and shoot it long distance, what’s to force him to get close. I think waves of zombies that spawn in at the edges of the map might help a bit, but spike traps that come up from the floor in a pattern will also entice the player to get in range of the close in attacks.

I’ll show progress on the Boss fight in my next post. I intend to post at least once a week from now until game launch, but hopefully I can do more. I’ve learned so much about the Steam Greenlight process, Marketing, and tiny sized team development this year and would like to share that information with other Indie Game Devs. I don’t forsee myself doing another tutorial anytime soon though, it will be more like a dev dairy for now.

Share Button

Utility System C++ Architecture

I had a couple considerations and constraints in making this system. I had just launched the Steam Greenlight campaign before I made the decision to redo the AI completely. The Cats just weren’t expressive enough for me and adding new things into the Behavior Tree was causing bugs in the existing branches. It’s probably a great system, but it is not well documented and you are just expected to know how it works to use it well. So, it’s probably great for the people who made it.

This system may be horrible to someone else, not optimized or any of a number of other things, but that doesn’t matter. Because, I am the only programmer who has to use it and if it allows me to add many behaviors to the Cats so they are playing interesting animations representing there’s moods and perform actions in the world on their own volition I’m happy with it.

It is a decision making system. It needs to have these features.

1. A straight forward way of adding new Actions
2. Needs to be able to choose from a large variety of animations based on moods
3. Needs to interact with items dynamically, if they are added to the world, or taken away so that it’s flexible
4. It needs to be quick to implement.

The core of the Utility System is a Manager that picks the best Action to perform. It basically goes through each Action and gets a score for it. Each Action has a list of considerations that contribute to it’s score. All the scores are normalized between 0 and 1.

One thing to think about is when an Action can be performed on multiple Actors in the level. Such as Chase Mouse. If there are 3 mice in the level, should the system decide which mouse to chase after it’s decided that’s what it’s going to do?

I decided that didn’t make since. So, run the entire Action List for each Actor in the Level that it can interact with. That way when it picks an Action such as Chase Mouse, it also has the Mouse to Chase along with it.

One thing that was a little tricky to get right at first was the Editor.

UtilityEditor

I wanted the simplest way possible to implement it. So I added a Class specifier to the UtilitySystem pointer that the AIController holds so that it could be specified directly in the Editor.

UPROPERTY(EditAnywhere, Instanced, Category = "AI")
UIAUtilitySystem* UtilitySystem;

Here is the complete header for the Utility System as it is now. I used Structs to give it a Data Only type feel. It is more procedural because of that. The UIAUtilitySystem is tightly coupled with the AI Controller to do the processing. This should be easily extendable for different controllers.

// Copyright 2013-2014 Cat Sniper LLC. All Rights Reserved.

#pragma once

#include "Object.h"
#include "IAUtilitySystem.generated.h"

class ACatAIController;

UENUM(BlueprintType)
enum class FunctionTypeEnum : uint8
{
FTE_Linear UMETA(DisplayName = "Linear"),
FTE_Quadratic UMETA(DisplayName = "Quadratic"),
FTE_Log UMETA(DisplayName = "Log")
};

UENUM(BlueprintType)
enum class FConsiderationEnum : uint8
{
CE_Distance UMETA(DisplayName = "Distance"),
CE_Los UMETA(DisplayName = "Line Of Sight"),
CE_AttractionTo UMETA(DisplayName = "Attraction To"),
CE_EnergyLevel UMETA(DisplayName = "Energy Level"),
CE_HungerLevel UMETA(DisplayName = "Hunger Level")

};

USTRUCT(Blueprintable)
struct FAxisParameter
{
GENERATED_USTRUCT_BODY()

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
FunctionTypeEnum CurveType;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
int32 M;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
int32 K;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
int32 B;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
int32 C;
};

/**
*
*/
USTRUCT(Blueprintable)
struct CATSNIPER_API FIAAxis
{
GENERATED_USTRUCT_BODY()

UPROPERTY(EditAnywhere, Category = "AI")
FAxisParameter Parameters;

UPROPERTY(EditAnywhere, Category = "AI")
FConsiderationEnum Consideration;
};

UENUM(BlueprintType)
enum class FActionTypeEnum : uint8
{
FAT_ChaseLaser UMETA(DisplayName = "Chase Laser"),
FAT_ChaseMouse UMETA(DisplayName = "Chase Mouse"),
FAT_MeowAt UMETA(DisplayName = "Meow At"),
FAT_EatFood UMETA(DisplayName = "Eat Food"),
FAT_PlayYarn UMETA(DisplayName = "Play Yarn"),
FAT_Sleep UMETA(DisplayName = "Sleep"),
FAT_WakeUp UMETA(DisplayName = "Wake up")
};

USTRUCT(Blueprintable)
struct FIAAction
{
GENERATED_USTRUCT_BODY()

int num;

FIAAction()
{
num = 0;
num = AxisList.Num();
if (num > 0)
{

int debug = num;
}
};

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
FActionTypeEnum ActionType;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
TArray AxisList;
};

USTRUCT(Blueprintable)
struct FIAActionScore
{
GENERATED_USTRUCT_BODY()

FActionTypeEnum ActionType;
float score;
};

/**
* Utility System
*/
UCLASS(Blueprintable, EditInlineNew)
class CATSNIPER_API UIAUtilitySystem : public UObject
{
GENERATED_BODY()

public:
void Init(ACatAIController* controller);

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
TArray ActionList;

UFUNCTION(BlueprintCallable, Category = "AI")
FIAActionScore EvaluateNextActor(AActor* actor);

private:
ACatAIController* MyController;

float GetAxisValue(FAxisParameter params, FConsiderationEnum consider, AActor* actor);

bool IsInitialized;
};

Because this is Enums and Structs everything can be Added in the Editor without having to have a custom constructor that builds up the lists from a description. The enum lists are not complete, this is just what I put in as a first pass as I’m making the system. In the next post I’ll show how the processing is done.

Share Button

Infinite Axis Utility System

So, I’m making a Cat and Lazer pointer game in UE4. I’ve redone the AI system now several times, I’m on my 4th iteration.

I had been using the built in Behavior Tree system in UE4, but I kept having issues with it. Basically I would get a behavior to work and move on to another part of the game, then when I’d come back to the AI a couple months later my cat wouldn’t move, or no longer looked at the laser etc.

I give up. I don’t know if I’m using it in a way that wasn’t intended or changes are made to the system that break my functionality when I update. Either way, it’s better for me if I just do it myself.

What is an Infinite Axis Utility System?

Here is a screenshot of the GUI Editor for the System:

UtilitySystemEditor

In a nutshell it is a system that returns an Action to perform with the highest value. It’s bascially a list of Actions and each Action has a list of “Axis”. Dave Mark did a talk on it at GDC a couple years ago and that’s where I heard about it. I think he also mentioned a Cat and Laser pointer game in the same talk actually which was the initial seed idea of Lol Cats I Can Has Lazer, though our game went it’s own design direction.

Here is a link to the talks:

http://intrinsicalgorithm.com/IAonAI/2013/02/both-my-gdc-lectures-on-utility-theory-free-on-gdc-vault/

Those videos go into the nuts and bolts of the system. The book Behavioral Mathmatics for Game AI is also a good source and I recommend reading it if you intend to implement a Utility System. It really gets into the design aspect of how to model behaviors as math functions.

Here is a link to a GDC talk of an evolved version of the Utility System used in an MMO:

http://www.gdcvault.com/play/1021848/Building-a-Better-Centaur-AI

After watching that I decided to incorporate some of the ideas into my system. My first couple IAUS ( Infinite Axis Utility System ) were not optimized at all. They were dynamic classes with functions. I wanted to describe my system in a more Data Centric way, and then have the System itself hold the functions that work on the data.

I choose this because it was easier to describe a system of data in the UE4 Editor than classes. Having TARRAY’s of structs with enums and floats as variables is very easy. With classes it’s not so easy.

Share Button

Fake GoDaddy Domain Change Notification Phishing Attempt

This morning I checked my email and there was a message from GoDaddy informing me there had been a name change to to one of my domains. It was the new one I created for an the app I started blogging about with the Google Compute Engine.

Something didn’t look quite right about it, but I clicked the link anyway. It went to a login form, but my user name is a long number that I’d have to look up to be able to type in, and, there was not enough stuff on the page. So, I googled it and came to several blog posts about Fake DNS change notifications. Apparently new Domains are being targeted.

FakeGoDaddyPage

I hadn’t had my morning cup of coffee yet, so was probably just lucky I didn’t enter my username and password because then someone would have full access to my account.

Here is what the real page should look like. Basically there wasn’t enough stuff on the fake one. What worries me though about it is if the creator was a little more skilled and had added more content to the page I may have been fooled.

RealGoDaddyPage

Here is what the email I received looked like. I changed the actual phishing link address to “seriesoflettersandnumbers”, this is what would probably link the info I put into the form with my email address. I’m putting this post up for awareness.

I don’t have that domain ownership blocked on WhoIs, so it is trivial to get my email address for it.

Dear Valued GoDaddy Customer MICHAEL PURVIS.

This notification is generated automatically as a service to you.

We have received a request that the name servers be changed for the following domain name(s):

luciddreamapp.com

If you are monitoring this name with Domain Backorders, the above change is also displayed in the Monitoring and Backordering section of your Account Manager.

Use the link below:
https://sso.godaddy.com/mains.aspx?idp=”seriesoflettersandnumbers”

Sincerely,
GoDaddy Domain Backorders team.
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –

Copyright (c) 1999-2015 GoDaddy.com, LLC. All rights reserved.

I called GoDaddy and reported the phishing attempt, gave them the url to look at and they confirmed there was nothing out of the ordinary with my account. It’s interesting that the phone number on the fake page is GoDaddy’s actual phone number. It makes me think that it’s possible whoever did this, is doing an experiment because they could have gone a few steps further to make it more effective.

Share Button

How to set up Git on Google Compute Engine

This is a beginner level tutorial for setting up Git on a Google Compute Engine Virtual Machine.

When I develop an application I do it in an iterative process. The first peace I did for the Nodejs server on GCE was in my last blog setup nodejs on gce which leaves off with being able to browse to the server from an external browser.

The next needed steps that I intend are to add Postgress for the Database, the Express framework to handle the HTTP and I haven’t decided on Aurelia or PhoneGap yet for the front end framework. But, it would be horrible to setup everything and then lose it. There are 2 steps that will aid in this.

1. Is to create version control so all the files live on Git.
2. The second is making a bash script that sets up and installs all the packages so I could easily get started from scratch in a fresh Ubuntu install, that file would also live in the Git repo.

This post will detail setting up Git. It’s really the same on any Ubuntu server, mine just happens to be on a Google virtual machine.

All there is to it:

sudo apt-get install git

The next step is to enter the username and password. Since this is on a Virtual Machine I don’t want to use my personal Git Account. I’ll make a new one for the project. You can get an free account if you don’t mind it being public. Sign up at, github.com

Now, It’s great if your developing your app and you push your changes up to GitHub so other collaborators can get them, and fetch changes others made and pull them down. But, that doesn’t make as much sense when it’s a webserver. It would be a pain to need to log onto the virtual machine hosting the webserver and pull down the changes. Setting up Automatic Deployment is the answer.

Here’s a link to instructions for it:

If you want to work on the VM like it was your local dev environment, especially at the start that’s fine also.

Set up your user name next.

git config --global user.name "Your Name"
git config --global user.email youremail@example.com
Share Button

How to create Node HTTP server on Google Cloud Platform

This is a quick tutorial, beginner level, to get a nodejs serving to the web as fast as possible. The result will be that you can navigate to an ip:port on the web and get a http response.

Google Cloud Platform: https://cloud.google.com/compute/

Click Free Trial and sign up, it does require a credit card.

In the Google Developers Console, select compute Engine and click New instance.

Give it a Name.

On Boot Disk, click Change and select Ubuntu 14.04 LTS

Check the box for allow HTTP traffic.

There is a Networking tab, there is an option for creating a new static IP address. You can promote an ephemeral IP to static at a later time.

Create the instance and at the far right click SSH under Connect to open a console.

In the console:

~$ sudo apt-get update

~$ curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash -

~$ sudo apt-get install -y nodejs

These instructions came from: NodeSource

Test that node is installed, type node. You should see a >. Type console.log(‘Hi’); You should see Hi. Then Ctrl+C to exit node, you’ll have to do it again after prompting.

Create a directory.

~$ mkdir myapp

Change to it.

~$ cd myapp

If linux and command line is new to you, a nice shortcut to know is that when typing a filename, if you press tab it will auto complete once it has enough letters to differentiate from other files.

I’m not a fan of editing files in the console, but vim is the answer to do it. You’ll need to search for some Vim tutorials, but I’ll give the needed commands for this simple tutorial.

To create a new file.

~$ vim hello.js

Now, you’ll be in the vim console. You can’t just edit from here as you’ll be in command mode to start.

Press i, now you should be in insert mode and can edit.

Type this in:

var http = require('http');
var server = http.createServer(function(req, res){
        res.writeHead(200);
        res.end('Hello Kitty');
});
server.listen(8080);

To run it:

~$ node hello.js

It will just sit there. Go to your Google Developers Console and click the SSH under connect for a new console and type:

~$ curl localhost:8080

You should see your message there, Hello Kitty

Now, this is almost there, but if you can’t navigate to this from the web yet. You need to enable the port for a tcp connection. To do this, in Google Developers Console select Firewall rules, it’s under Networking.

Click, New firewall rule.

Name it.

Under Source IP ranges, 0.0.0.0/0

Under Allowed protocols, tcp:8080

I put http-server under target tags, but it’s optional.

Now, if all went well you can navigate to the external IP and port to receive the http request and see the message in your browser. The external IP is to listed on the VM instances page of the Compute Engine. It’s to the left of the SSH button you’ve been clicking to open a console.

In a browser enter your external ip number followed by :8080 and Hello Kitty should show on the page.

That’s it for this tutorial. If you followed it your in a place now that you can start adding real features to your server. I’ll do more in this series to add to it.

Share Button

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

How to make a Spider in UE4 Part 5

Here’s a video to show where the Spider currently is at.

As can be seen, there are some parts that are working well, such as the spider can walk on the walls, rotate on any surface, transition to other surfaces etc.

There’s a lot that isn’t working though. There are no collisions and the movement has a slide to it. It doesn’t correctly transition around outside edges.

I put the code, and project up on GitHub. github.com/eibonscroll/UE4SpiderAI

There was a problem with rotating the pawn on other surfaces. Basically, you can’t just set the rotation. Setting the transform does work.

void ASpiderPawn::RotateRight()
{
    FTransform TheTransform = this->GetTransform();

    FRotator rot = FRotator(0, rotationSpeed, 0);

    TheTransform.ConcatenateRotation(rot.Quaternion());
    TheTransform.NormalizeRotation();
    this->SetActorTransform(TheTransform);
}

This works on any surface angle correctly.

I use sockets on the Mesh to get the locations for the traces. The locations are updated each tick. Because there are a number of traces being done, can update locations of the sockets once per tick. This function creates a new transform. Previously I was trying to just set the rotation. Searching on the UE4 AnswerHub and the UE4 forums I went through all the posts I could find on wall walking, there were several people having trouble with strange angles when trying to rotate the Pawn onto different surfaces.

bool ASpiderPawn::TransitionToWall()
{
    bool retval = false;

    //Grab a ref to the world here
    UWorld* world = GetWorld();

    FHitResult hitResultTrace;
    FCollisionQueryParams queryParams;
    FCollisionObjectQueryParams objQueryParams;

    queryParams.AddIgnoredActor(this);

    FVector under;

    FVector newUp;
    FVector newForward;
    FVector newRight;

    //Trace to get the surface normal in front of actor
    if (world->LineTraceSingle(hitResultTrace, BellySocketLoc, FrontSocketLoc, 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;
}

In the next post will work on getting the spider to walk around outside edges.

Share Button

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