The Mini City - UE4 Waypoints and Editor Ticking
A quick weekend project where I put together a simple waypoint system with some proper in-editor route previewing
A couple of months ago I came across a pretty cool video about Building a Traffic System in Unity, and thought to myself it would be a fun little project to put one of these traffic systems together myself, but in UE4, and also make some changes to various system design choices I didn’t agree with.
Now, a mere three quarters of a year later I finally got around to giving it another watch and fulfilling my plan.
Editor Ticking in Unreal
I’ll start off with something not specific to a waypoint system but rather tools in general, which is getting actors to tick in in the editor while not playing. This is very trivial in Unity, and used a lot for drawing debug content for custom tools. It can also be utilized for more complex setups like small simulations or interactive content placement.
In UE4 it also used to be quite trivial, even in blueprint, through the PlacedEditorUtilityBase class. Unfortunately they broke its editor ticking capabilities in 4.19. Later they fixed this, but went on to deprecate the class and replace it with the new EditorUillityActor that sadly lost editor ticking along the way.
So after spending too much time down that rabbit hole I realized the best route is to make a new TickingEditorUillityActor class derived from EditorUtilityActor in which I simply state it should tick in editor using ShouldTickIfViewportOnly. To get it editor ticking from a blueprint actor however, I also have to declare my own BlueprintImplementableEvent that I’ve called EditorTick that I trigger from the regular tick function in code. This is because the default blueprint Tick event is simply not allowed to trigger in editor.
So the bottom part of my class header looks like this:
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "EditorTick", CallInEditor = "true"))
void ReceiveEditorTick(float DeltaTime);
virtual void Tick(float DeltaTime) override;
virtual bool ShouldTickIfViewportsOnly() const override;
And the bottom of the cpp file then looks like this:
void AEditorTickingActor::Tick(float DeltaTime)
bool AEditorTickingActor::ShouldTickIfViewportsOnly() const
Which lets me set up a proper way of displaying my waypoints without any hacky construction script shenanigans.
The waypoint system
To give you a brief rundown, I wanted to make a simple waypoint system for AI to navigate randomly, in order to fill a cityscape with life. However, there are some restrictions, like the fact that it’s in a city. This means pedestrians and vehicles need to move only where they would be allowed to move.
My system is a bit simpler than the one in the Unity video in a couple regards. Firstly, I never got around to making all that sexy custom editor GUI, mine only says "Create waypoint". Secondly, and perhaps most importantly, I reduced the complexity of the system to each waypoint only having the notion of connections - one way streets - to other waypoints, and each waypoint can have any number of connections. This gets rid of the awkward bridging system that was used in the video for crossings, and makes it very intuitive to choose which direction movement is allowed, if not just both. This also makes it easy to use the system for things like vehicles, pedestrians and even flying characters all at the same time.
The way my pedestrians and vehicles avoid then flip flopping between available points is they keep a short history of the latest points they’ve visited, and only consider going to the visited points if no other points are available.
Now the pedestrians unfortunately don’t collide with each other; only slightly repell each other as they would too easily clump up with proper collision. The cars, however, will stop if anything is in front of them, making it all look surprisingly organic with only very simple systems in place. One cool thing is that the structure of my system is a set up in an ideal way for graphi traversal, and so with a little bit of Djikstra or A\* one could add the ability to pathfind from point A to point B rather than just randomly wandering about.
But that’s for next time, here’s my final result!