<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Vikram Codes</title>
      <link>https://vikram.codes</link>
      <description>Personal website of Vikram Saran, a Principal Core Engine Programmer and Technical Consultant specialising in game engine architecture and technical production.</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://vikram.codes/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sat, 28 Feb 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Self-hosting my website</title>
          <pubDate>Sat, 28 Feb 2026 00:00:00 +0000</pubDate>
          <author>Vikram Saran</author>
          <link>https://vikram.codes/blog/self-hosting-website/</link>
          <guid>https://vikram.codes/blog/self-hosting-website/</guid>
          <description xml:base="https://vikram.codes/blog/self-hosting-website/">&lt;h2 id=&quot;going-my-own-way&quot;&gt;Going my own way&lt;&#x2F;h2&gt;
&lt;p&gt;For the past decade I&#x27;ve used this website for intermittent &lt;a href=&quot;&#x2F;blog&quot;&gt;blogging&lt;&#x2F;a&gt;, updating my &lt;a href=&quot;&#x2F;resume&quot;&gt;CV&lt;&#x2F;a&gt;, as a contact point for some &lt;a href=&quot;&#x2F;#consulting&quot;&gt;consulting work&lt;&#x2F;a&gt;, but I don&#x27;t spend a lot of time on it.&lt;&#x2F;p&gt;
&lt;p&gt;I love having a personal website, and I love that the blog posts I do have seem to &lt;em&gt;help&lt;&#x2F;em&gt; people (I still get dozens of daily hits on my various posts about &lt;a href=&quot;&#x2F;tags&#x2F;unreal-engine&quot;&gt;AI&#x2F;Navigation in UE4&lt;&#x2F;a&gt;).
But I don&#x27;t really spend all that much time blogging, and yet I have paid through the nose for services I barely use, which are bloated and slow, and I am steadily losing trust in.&lt;&#x2F;p&gt;
&lt;p&gt;So, although my day job is managing teams of game engine developers to make some &lt;a href=&quot;https:&#x2F;&#x2F;pp.studio&#x2F;projects&#x2F;the-melba-engine&quot;&gt;really cool tech&lt;&#x2F;a&gt;, I decided to finally brush off my web-dev and sysadmin skills, apply my love of optimisation, and dive into the details of a tech stack I&#x27;m less familiar with.&lt;&#x2F;p&gt;
&lt;p&gt;This post is more of a diary entry, rather than a tutorial.
I&#x27;m chronicling what I&#x27;ve done, as well as why and how I&#x27;ve done it.
It should show how joining the &quot;indie web&quot; isn&#x27;t all that difficult if you&#x27;re a little tech savvy.&lt;&#x2F;p&gt;
&lt;p&gt;Bottom line up front?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Blog posts transfer 99% less data and load in 4% of the time&lt;&#x2F;li&gt;
&lt;li&gt;Costs went from ~USD$450 to ~EUR€180 per year (&amp;gt;50% savings)&lt;&#x2F;li&gt;
&lt;li&gt;More control over my data (tracking fewer things, in GDPR safe places)&lt;&#x2F;li&gt;
&lt;li&gt;My &quot;own&quot; server I can tinker with&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And most importantly - I learned &lt;em&gt;a lot&lt;&#x2F;em&gt; - and I love learning!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;&#x2F;h2&gt;
&lt;p&gt;I had a perfectly working website, why would I go through the hassle of self hosting?&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, I&#x27;ve been paying about $450 USD per year to Squarespace for a website (~$280), my domain name (~$80), and my google workspace (~$85).
Both Squarespace and Google Workspace provide a plethora of features that I don&#x27;t really need - I don&#x27;t need a visual editor for blog posts I author in markdown anyway.&lt;&#x2F;p&gt;
&lt;p&gt;Secondly, my website&#x27;s performance was &lt;strong&gt;abysmal&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-old-site.9f203e169a567a19.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-old-site.fb732aa74562edfb.avif 972w&quot; sizes=&quot;(max-width: 768px) 100vw, 972px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-old-site.b0feb0c3466651d2.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-old-site.549e7320b9e03f15.webp 972w&quot; sizes=&quot;(max-width: 768px) 100vw, 972px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-old-site.549e7320b9e03f15.webp&quot; alt=&quot;PageSpeed Insights gives 47&amp;#x2F;100 for performance on my first UE4 AI blog post&quot; loading=&quot;lazy&quot; width=&quot;972&quot; height=&quot;758&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;It transfers ~61.84 MB over 100 HTTP requests, and sometimes takes more than 10 seconds to finish fetching all network data. The &quot;Load&quot; Event takes nearly 2 seconds to occur.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;An LLM tried to tell me that &quot;In the world of web dev, 2 seconds for a load event isn&#x27;t actually that bad&quot; - maybe for a dynamic website, not a blog!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Thirdly, with the state of international politics right now, I wanted to bring my data under my own control - to try and achieve &quot;digital sovereignty&quot;.
My squarespace website had heaps of javascript frameworks, google analytics, and other things I find annoying as a user.
I&#x27;d rather replace it with simple server-side stats, no cookies, and no third-party scripts.&lt;&#x2F;p&gt;
&lt;p&gt;Finally... I like tinkering. Having my own server to play around with is highly appealing to me. Well, technically I already have a couple of servers at home - but maintaining something that&#x27;s &quot;live&quot; and used by other people is always a lot more interesting to me.
I wanted to own my own web footprint. I wanted to see how it all fit together and ticked. I wanted to optimise, tinker, break it, and make it better.&lt;&#x2F;p&gt;
&lt;p&gt;So to flip those frustrations into goals (in no particular order):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Save a bit of money in my hosting&lt;&#x2F;li&gt;
&lt;li&gt;Be &quot;blazingly fast&quot; 🔥🚀&lt;&#x2F;li&gt;
&lt;li&gt;Use the modern web (e.g. HTML5 &lt;code&gt;&amp;lt;details&amp;gt;&lt;&#x2F;code&gt;) rather than bloated JS frameworks&lt;&#x2F;li&gt;
&lt;li&gt;Use IaC&#x2F;DevOps best practices to enable the features I care about&lt;&#x2F;li&gt;
&lt;li&gt;Simplicity and ease of use for myself&lt;&#x2F;li&gt;
&lt;li&gt;The ability to add new things as I think of them&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;a.k.a. cheap, fast, using best practices, standards compliant, maintainable, and extensible.
All of the things a Technical Director tries to make a product.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;static-site-generator&quot;&gt;Static Site Generator&lt;&#x2F;h3&gt;
&lt;p&gt;This website is mostly a repository for blog posts, so using a static site generator to help with the markdown -&amp;gt; HTML step made sense. I don&#x27;t need a full CMS.&lt;&#x2F;p&gt;
&lt;p&gt;I know a lot of people are building their SSG, but for me - to start with - I picked one off the shelf. After a bit of research, I chose &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&quot;&gt;Zola&lt;&#x2F;a&gt;.
I didn&#x27;t spend a lot of time on this decision, something was better than nothing, and as long as it mostly worked out of the box, I could pick a template and write my own structures&#x2F;CSS later. Considering I&#x27;ve heard that Hugo&#x27;s templating engine is obtuse, I went with one I could eventually learn.&lt;&#x2F;p&gt;
&lt;p&gt;I started with the &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;themes&#x2F;anemone&#x2F;&quot;&gt;anemone template&lt;&#x2F;a&gt;, but quickly forked it and replaced it with my own templates once I realised how trivial it was to do so (plus I could avoid using a git submodule!). I have a couple of little quality of life elements on my website that I wanted, and it was easy enough to use modern HTML, CSS, and a little bit of raw javascript to make them work (e.g. the floating Table of Contents).&lt;&#x2F;p&gt;
&lt;details  &gt;
  &lt;summary&gt;Zola shortcode template example&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;I like accordions for details, and I use them thoroughly.
On my old website I had some ugly javascript hacks to make them work nicely,
but with modern HTML5 it&#x27;s easy.&lt;&#x2F;p&gt;
&lt;p&gt;You set up an &lt;code&gt;accordion.html&lt;&#x2F;code&gt; template:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;details &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;{% if open %}open{% endif %} {% if name %}name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;{{name}}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;{% endif %}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;{{ summary | markdown(inline=true) }}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;accordion-content&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;{{ body | markdown | safe }}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;details&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which lets me do this in my markdown:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;md&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-md &quot;&gt;&lt;code class=&quot;language-md&quot; data-lang=&quot;md&quot;&gt;&lt;span&gt;&#x2F;&#x2F; Note: %&amp;#39;s replaced with &amp;amp; to avoid triggering the template engine
&lt;&#x2F;span&gt;&lt;span&gt;{&amp;amp; accordion(summary=&amp;quot;Zola template example&amp;quot;) &amp;amp;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;I like accordions for details, and I use them thoroughly.
&lt;&#x2F;span&gt;&lt;span&gt;On my old website I had some ugly javascript hacks to make them work nicely,
&lt;&#x2F;span&gt;&lt;span&gt;but with modern HTML5 it&amp;#39;s easy.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{&amp;amp; end &amp;amp;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;On top of that, I was able to drop a bunch of weight in the data transfers by processing my images, sizing them properly (zola has a nice &lt;code&gt;resize_image&lt;&#x2F;code&gt; function), and using &lt;code&gt;webp&lt;&#x2F;code&gt; to reduce size further.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;m very comfortable with git and DevOps, I&#x27;ve worked in this area extensively, so it was natural for me to settle for GitLab to start off with.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Eventually I want to set up &lt;code&gt;gitea&lt;&#x2F;code&gt; and some non-yaml CI solution, but I haven&#x27;t seen anything that calls to me yet, so I&#x27;ll do this another day.
For now, GitLab with my home N150 NUC as a runner is plenty.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;details  &gt;
  &lt;summary&gt;GitLab CI Setup&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;My initial CI setup was quite simple.
A docker job:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;variables&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;DOCKER_IMAGE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;$CI_REGISTRY&#x2F;$CI_PROJECT_NAMESPACE&#x2F;$CI_PROJECT_NAME&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;$DOCKER_IMAGE:latest
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;docker
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;docker:latest
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;docker:dind
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;before_script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;echo &amp;quot;$CI_REGISTRY_PASSWORD&amp;quot; | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;docker build -t $DOCKER_IMAGE:latest .
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;docker push $DOCKER_IMAGE:latest
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;rules&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;$CI_PIPELINE_SOURCE == &amp;#39;merge_request_event&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;changes&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Dockerfile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A zola job:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;build-zola&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;build
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;zola-media-cache
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;static&#x2F;processed_images
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;zola check
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;zola build
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;public
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;rules&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;$CI_PIPELINE_SOURCE == &amp;#39;merge_request_event&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And a gitlab pages publish job:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;publish-website&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;publish
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;ls -la public
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;public
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;rules&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also hooked up &lt;code&gt;rendercv&lt;&#x2F;code&gt; for automatically making pretty resume CVs, &lt;code&gt;lychee&lt;&#x2F;code&gt; for link checking, and &lt;code&gt;markdownlint&lt;&#x2F;code&gt; for completeness (also in pre-commit).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;Once I had the website working on gitlab pages, I spent a bit of time porting my old content over. Easy enough, because I already used markdown for authoring content.&lt;&#x2F;p&gt;
&lt;p&gt;I didn&#x27;t have to change too much, just switch from inline html to shortcodes and add some frontmatter.
Fortunately, LLMs are pretty useful in this context.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hardware&quot;&gt;Hardware&lt;&#x2F;h3&gt;
&lt;p&gt;As much as I&#x27;d like to host from hardware at home, my ISP isn&#x27;t &lt;em&gt;that&lt;&#x2F;em&gt; reliable, and I&#x27;d like to avoid pointing web traffic to my home network, so I&#x27;m renting a Hetzner VPS (CX23). This is a steal at €3 per month (and still okay even though it&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;docs.hetzner.com&#x2F;general&#x2F;infrastructure-and-availability&#x2F;price-adjustment&#x2F;&quot;&gt;just raised&lt;&#x2F;a&gt; to €4).&lt;&#x2F;p&gt;
&lt;p&gt;As someone with not much infra&#x2F;backend experience (especially public facing), I found their UI perfectly cromulent, and would recommend them whole heartedly.
It&#x27;s probably the smoothest hardware requisitioning system I&#x27;ve ever used.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;server-infrastructure&quot;&gt;Server Infrastructure&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;m &lt;em&gt;moderately&lt;&#x2F;em&gt; comfortable with *nix systems, but I thought I&#x27;d try something I&#x27;ve been reading a lot about and would like to learn - &lt;a href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;&quot;&gt;NixOS&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&#x27;ve not heard of it, Nix is a package management tool (and programming language) which is declarative - the IaC dream.
NixOS takes it a step further. It&#x27;s a GNU&#x2F;Linux distribution that manages its own configuration entirely through the Nix language.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Although it took a bit of re-reading the docs, going through the &quot;nix pills&quot; (a &lt;a href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;guides&#x2F;nix-pills&#x2F;&quot;&gt;tutorial for nix&lt;&#x2F;a&gt;), and some LLM support, I&#x27;ve come to be pretty comfortable with messing around with my setup.
If you are comfortable with programming or scripting, I think it&#x27;s worth the plunge.&lt;&#x2F;p&gt;
&lt;p&gt;All of the following is done via &lt;code&gt;.nix&lt;&#x2F;code&gt; config files:&lt;&#x2F;p&gt;
&lt;details  &gt;
  &lt;summary&gt;Setting up NixOS on a Hetzner VPS&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Don&#x27;t try and use &lt;code&gt;nixos-infect&lt;&#x2F;code&gt;, Hetzner has a great UI for loading various ISOs and booting from them, including a NixOS 25.04 image which I used to install the OS to the main drive and the MBR.&lt;&#x2F;p&gt;
&lt;p&gt;I wish I could have used &lt;a href=&quot;https:&#x2F;&#x2F;docs.cloud-init.io&#x2F;en&#x2F;latest&#x2F;&quot;&gt;&lt;code&gt;cloud-init&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, but I didn&#x27;t quite figure it out. And in order to reset your VPS and start with a new &lt;code&gt;cloud-init&lt;&#x2F;code&gt; config, you need to delete it and spin up a new one - a bit too much of a hassle.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;The nicest thing here is I have a git repository set up for the &lt;code&gt;&#x2F;etc&#x2F;nixos&#x2F;&lt;&#x2F;code&gt; directory, so the entire server is trivial to reproduce in case of emergency.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;web-server&quot;&gt;Web Server&lt;&#x2F;h3&gt;
&lt;p&gt;I wanted something super simple, and from what I could find online, &lt;a href=&quot;https:&#x2F;&#x2F;caddyserver.com&#x2F;&quot;&gt;Caddy&lt;&#x2F;a&gt; is the best in that respect. It&#x27;s fast, memory efficient, automatically sets up HTTPS&#x2F;TLS certificates, and integrates easily with NixOS.
This was much much easier than my dim memories of struggling with apache&#x2F;nginx, and extending it later on was also equally easy.&lt;&#x2F;p&gt;
&lt;details  &gt;
  &lt;summary&gt;Caddy in NixOS&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;The following snippet is more or less all I needed to start with.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;caddy &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;enable &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;virtualHosts&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;vikram.codes&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot; = {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;extraConfig &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        root * &#x2F;var&#x2F;www&#x2F;vikram.codes
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        file_server
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        # Adding compression is as easy as
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        encode zstd gzip
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        # Technically this isn&amp;#39;t quite right
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        # But it&amp;#39;s good enough for now - &amp;quot;respond to all errors with my 404 page&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        handle_errors {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;          rewrite * &#x2F;404.html
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;          file_server
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s even easy to handle the constant stream of bots sniffing for vulnerabilities in WordPress setups:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span&gt;        @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;killbots &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;path &lt;&#x2F;span&gt;&lt;span&gt;*.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;wp-&lt;&#x2F;span&gt;&lt;span&gt;*
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;respond&lt;&#x2F;span&gt;&lt;span&gt; @killbots &amp;quot;Access Denied&amp;quot; 403
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, I spent a bit of time scraping my old website&#x2F;sitemap for urls I&#x27;d need to redirect, and dumped them into a block:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;caddy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;virtualHosts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;vikram.codes&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;extraConfig &lt;&#x2F;span&gt;&lt;span&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;mkAfter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      redir &#x2F;s&#x2F;git-slides.pdf &#x2F;blog&#x2F;git-lfs-file-locking&#x2F;git-slides.pdf permanent
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      redir &#x2F;s&#x2F;spike-slides.pdf &#x2F;blog&#x2F;agile-spikes&#x2F;spike-slides.pdf permanent
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      # etc.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h3 id=&quot;security&quot;&gt;Security&lt;&#x2F;h3&gt;
&lt;p&gt;I am certainly not an expert in security, but I have a couple of basics to help out.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, there are fundamentals (use a firewall, only open specific ports, don&#x27;t allow password-based SSH login, etc.), but I went a little beyond those.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Mic92&#x2F;sops-nix&quot;&gt;&lt;code&gt;sops-nix&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to manage secrets for me.&lt;&#x2F;p&gt;
&lt;details  &gt;
  &lt;summary&gt;Using sops-nix&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;At about this time I needed to switch to using nix &lt;a href=&quot;https:&#x2F;&#x2F;wiki.nixos.org&#x2F;wiki&#x2F;Flakes&quot;&gt;&quot;flakes&quot;&lt;&#x2F;a&gt;. It&#x27;s marked experimental, but it seems like plenty of people encourage their use.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;inputs &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;github:nixos&#x2F;nixpkgs&#x2F;nixos-25.11&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;sops-nix&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;github:Mic92&#x2F;sops-nix&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;sops-nix&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;follows &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;outputs &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;sops-nix&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, ... }&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;# &amp;#39;my-host-name&amp;#39; must match &amp;#39;networking.hostName&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;nixosConfigurations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;my-host-name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;nixosSystem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;specialArgs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;inherit &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;; };
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;modules &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= [
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;.&#x2F;configuration.nix
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;# This makes the sops.* options valid in configuration.nix
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;sops-nix&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;nixosModules&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;sops
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At this point the rebuild command changes slightly to &lt;code&gt;nixos-rebuild switch --flake .&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;Secondly, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fail2ban&#x2F;fail2ban&quot;&gt;&lt;code&gt;fail2ban&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to block bad actors.&lt;&#x2F;p&gt;
&lt;details  &gt;
  &lt;summary&gt;Fail2Ban Setup&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;My &lt;code&gt;fail2ban&lt;&#x2F;code&gt; set up is nothing fancy, but I&#x27;d like to think it&#x27;s functional:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;fail2ban &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;enable &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;bantime &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;1h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;maxretry &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;bantime-increment &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;enable &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;formula &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;ban.Time * math.exp(float(ban.Count+1)*banFactor)&#x2F;math.exp(1*banFactor)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;# Exponential growth
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;maxtime &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;168h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;# 1 week
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;overalljails &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;jails &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;caddy-4xx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;settings &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;enabled &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;port &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;http,https&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;logpath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&#x2F;var&#x2F;log&#x2F;caddy&#x2F;access-vikram.codes.log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;backend &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;polling&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;filter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;caddy-4xx&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;findtime &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;30m&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;datepattern &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&amp;quot;ts&amp;quot;:(?P&amp;lt;timestamp&amp;gt;\d{10}\.\d+) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;caddy-killbots&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;settings &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;enabled &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;port &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;http,https&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;logpath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&#x2F;var&#x2F;log&#x2F;caddy&#x2F;access-vikram.codes.log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;backend &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;polling&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;filter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;caddy-killbots&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;findtime &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;30m&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;maxretry &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;# Immediate
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;bantime &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;8760h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;# 1 year
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;datepattern &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&amp;quot;ts&amp;quot;:(?P&amp;lt;timestamp&amp;gt;\d{10}\.\d+) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;etc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;fail2ban&#x2F;filter.d&#x2F;caddy-4xx.conf&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;text &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;    [Definition]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;    failregex = ^.*&amp;quot;remote_ip&amp;quot;:&amp;quot;&amp;lt;HOST&amp;gt;&amp;quot;.*&amp;quot;status&amp;quot;:(404|403|401).*$
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;etc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;fail2ban&#x2F;filter.d&#x2F;caddy-killbots.conf&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;text &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;    [Definition]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;    failregex = ^.*&amp;quot;remote_ip&amp;quot;:&amp;quot;&amp;lt;HOST&amp;gt;&amp;quot;.*&amp;quot;uri&amp;quot;:&amp;quot;[^&amp;quot;]*(\.php|&#x2F;wp-).*$
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h3 id=&quot;dns-and-monitoring&quot;&gt;DNS and Monitoring&lt;&#x2F;h3&gt;
&lt;p&gt;Finally, when I migrated my DNS registration and records away from Squarespace, I made the decision to split them up. I use &lt;a href=&quot;https:&#x2F;&#x2F;www.gandi.net&quot;&gt;gandi&lt;&#x2F;a&gt; for registration, and &lt;a href=&quot;https:&#x2F;&#x2F;desec.io&quot;&gt;deSEC&lt;&#x2F;a&gt; to host my records.&lt;&#x2F;p&gt;
&lt;p&gt;Gandi is a bit more expensive than the US options, but not too much (~€100&#x2F;year).&lt;&#x2F;p&gt;
&lt;p&gt;Using deSEC made it trivial to update my DNS records inside my nix config using their API.
If my IP changes for any reason, e.g. in the future I migrate to my own hardware, it&#x27;s all handled automatically.&lt;&#x2F;p&gt;
&lt;details  &gt;
  &lt;summary&gt;Updating DNS records in NixOS&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;systemd&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;update-dns &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bb80b3;&quot;&gt;script &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      set -euo pipefail
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      TOKEN=&amp;quot;$(cat &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;sops&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;secrets&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;desec_token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;)&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      IP=&amp;quot;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&#x2F;bin&#x2F;curl -fsS https:&#x2F;&#x2F;api.ipify.org)&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      [[ -z &amp;quot;$IP&amp;quot; ]] &amp;amp;&amp;amp; echo &amp;quot;Could not fetch IP&amp;quot; &amp;amp;&amp;amp; exit 1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      PAYLOAD=&amp;quot;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;jq&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&#x2F;bin&#x2F;jq -n --arg ip &amp;quot;$IP&amp;quot; &amp;#39;[
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;          subname: &amp;quot;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;          type: &amp;quot;A&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;          records: [$ip],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;          ttl: 3600
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      ]&amp;#39;)&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      echo &amp;quot;Current IP: $IP&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      echo &amp;quot;DNS Payload: $PAYLOAD&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&#x2F;bin&#x2F;curl -fsS -X PATCH \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        https:&#x2F;&#x2F;desec.io&#x2F;api&#x2F;v1&#x2F;domains&#x2F;your.domain&#x2F;rrsets&#x2F; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        -H &amp;quot;Authorization: Token $TOKEN&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        -H &amp;quot;Content-Type: application&#x2F;json&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;        -d &amp;quot;$PAYLOAD&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;Adding new subdomains is trivial from there. I&#x27;ve put some in for stats tracking using &lt;a href=&quot;https:&#x2F;&#x2F;goaccess.io&#x2F;&quot;&gt;&lt;code&gt;goaccess&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and uptime monitoring with &lt;a href=&quot;https:&#x2F;&#x2F;gatus.io&#x2F;&quot;&gt;&lt;code&gt;Gatus&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-migration&quot;&gt;The Migration&lt;&#x2F;h2&gt;
&lt;p&gt;At this point I had everything ready to go. Parity with the website on gitlab pages. A server ready to host. A pipeline to get things from one to the other (I updated my gitlab-ci to &lt;code&gt;rsync&lt;&#x2F;code&gt; to my VPS).&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;To be honest, I might have muddled timelines and done some of the above later in the process - I&#x27;m writing this in retrospect.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;details  &gt;
  &lt;summary&gt;Publishing from GitLab CI&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;deploy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;publish
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eb606b;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;rsync -avz --delete public&#x2F; $DEPLOY_USER@$DEPLOY_HOST:&#x2F;var&#x2F;www&#x2F;vikram.codes&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;I was expecting it to be a bit dramatic - some failures or outages... but it was pretty easy after all that setup.
I got the transfer code from squarespace, waited a few days for my bank transfer to clear with gandi, moved my dns records to deSEC (but pointing at Squarespace to start with).&lt;&#x2F;p&gt;
&lt;p&gt;Once I was ready, I uncommented my NixOS update-dns script section the root domain, ran &lt;code&gt;nixos-rebuild switch --flake .&lt;&#x2F;code&gt;, and waited.
A few minutes of watching my &lt;a href=&quot;https:&#x2F;&#x2F;www.whatsmydns.net&#x2F;&quot;&gt;DNS updates roll out&lt;&#x2F;a&gt;, and that was it!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The hardest part was the psychology - double and triple checking that I hadn&#x27;t missed anything.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;mail-server&quot;&gt;Mail Server&lt;&#x2F;h3&gt;
&lt;p&gt;The last part I didn&#x27;t talk about above is migrating my email from Google Workspaces to &lt;a href=&quot;https:&#x2F;&#x2F;mailbox.org&#x2F;&quot;&gt;mailbox.org&lt;&#x2F;a&gt;.
I don&#x27;t use many workspace features, so this works well for me.&lt;&#x2F;p&gt;
&lt;p&gt;Because I went with a hosted solution, there&#x27;s not a lot of technical detail here. Mailbox has &lt;a href=&quot;https:&#x2F;&#x2F;kb.mailbox.org&#x2F;en&#x2F;private&#x2F;custom-domains&#x2F;using-emails-with-a-custom-domain&#x2F;&quot;&gt;a good set of docs&lt;&#x2F;a&gt; about how to do it.&lt;&#x2F;p&gt;
&lt;p&gt;The cut-over process is more or less the same (set up the new system, keep MX&#x2F;SPF&#x2F;DKIM records pointed at the old host, cut over when tested, test that it hits the new endpoint), but with a manual copy step using Thunderbird to transfer my mail history across (sadly, the automated migration tool didn&#x27;t work for me).&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;To ensure that your emails don&#x27;t bounce, setting MX records along isn&#x27;t good enough.
Mailbox.org suggests setting &lt;a href=&quot;https:&#x2F;&#x2F;kb.mailbox.org&#x2F;en&#x2F;private&#x2F;custom-domains&#x2F;spf-dkim-and-dmarc&#x2F;&quot;&gt;SPF, DKIM, and DMARC&lt;&#x2F;a&gt; as well.
Note: You can&#x27;t just copy-paste the values from the mailbox docs for deSEC - you need to ensure the &lt;code&gt;subname&lt;&#x2F;code&gt; is lowercase!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;&#x2F;h2&gt;
&lt;p&gt;My pages are close to 100 across the board in PageSpeed for the same post as before:&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-new-site.c4b71a7213e89d32.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-new-site.f8411f259890a627.avif 980w&quot; sizes=&quot;(max-width: 768px) 100vw, 980px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-new-site.cbeb809e1de22784.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-new-site.778f7333671c4154.webp 980w&quot; sizes=&quot;(max-width: 768px) 100vw, 980px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;page-speed-new-site.778f7333671c4154.webp&quot; alt=&quot;PageSpeed Insights gives 97&amp;#x2F;100 for the same post as above&quot; loading=&quot;lazy&quot; width=&quot;980&quot; height=&quot;829&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;I &lt;em&gt;do&lt;&#x2F;em&gt; have straight 100s for my home page, but that&#x27;s much easier.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I&#x27;ve dropped to under 500Kb total transfer, with it finishing in ~0.6s - and that&#x27;s &lt;strong&gt;without&lt;&#x2F;strong&gt; bothering to have a CDN.
I&#x27;m pretty sure I could improve it even further if I fixed up my &lt;a href=&quot;&#x2F;css&#x2F;fonts.css&quot;&gt;&lt;code&gt;fonts.css&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and used variants, or dropped custom fonts altogether.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Metric&lt;&#x2F;th&gt;&lt;th&gt;Squarespace&lt;&#x2F;th&gt;&lt;th&gt;NixOS + Zola&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Total transfer&lt;&#x2F;td&gt;&lt;td&gt;61.8 MB&lt;&#x2F;td&gt;&lt;td&gt;663 KB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;HTTP Requests&lt;&#x2F;td&gt;&lt;td&gt;100&lt;&#x2F;td&gt;&lt;td&gt;12&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Load time&lt;&#x2F;td&gt;&lt;td&gt;~10s&lt;&#x2F;td&gt;&lt;td&gt;&amp;lt;250ms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;PageSpeed Performance&lt;&#x2F;td&gt;&lt;td&gt;47&lt;&#x2F;td&gt;&lt;td&gt;97&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;PageSpeed Accessibility&lt;&#x2F;td&gt;&lt;td&gt;81&lt;&#x2F;td&gt;&lt;td&gt;100&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;blockquote&gt;
&lt;p&gt;All local tests were with a cold cache, private browser window, same version of Firefox&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Everything that I care about is achieved:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It&#x27;s cheaper, by far
&lt;ul&gt;
&lt;li&gt;~€4pm for the VPS&lt;&#x2F;li&gt;
&lt;li&gt;~€30pa for the mail server&lt;&#x2F;li&gt;
&lt;li&gt;~€100pa for the domain&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;It really is &quot;blazingly fast&quot; 🚀&lt;&#x2F;li&gt;
&lt;li&gt;Robust content pipelines&lt;&#x2F;li&gt;
&lt;li&gt;Greatly reduced digital footprint&lt;&#x2F;li&gt;
&lt;li&gt;I know the whole thing inside and out, so maintenance is easy&lt;&#x2F;li&gt;
&lt;li&gt;And I&#x27;ve already added a handful of services that I didn&#x27;t bother with before&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;&#x2F;h2&gt;
&lt;p&gt;As with many self-hosting&#x2F;homelab projects, I get a lot of enjoyment just from tinkering.
So I&#x27;m going to keep doing that, adding new services (e.g. &lt;a href=&quot;https:&#x2F;&#x2F;kanidm.github.io&#x2F;&quot;&gt;&lt;code&gt;kanidm&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; for identity management, a subdomain to host my drafts behind OAuth login, etc.), and continuing to learn and grow.&lt;&#x2F;p&gt;
&lt;p&gt;I have a few ideas on how I&#x27;d like to extend this, and I&#x27;m sure I&#x27;ll come up with more:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;For now I&#x27;m happy to use mailbox.org, but eventually I&#x27;d like to consider hosting my own mailserver (CalDAV&#x2F;CardDAV as well).&lt;&#x2F;li&gt;
&lt;li&gt;With &lt;a href=&quot;https:&#x2F;&#x2F;discord.com&#x2F;press-releases&#x2F;discord-launches-teen-by-default-settings-globally&quot;&gt;Discord asking for ID globally&lt;&#x2F;a&gt; I&#x27;m interested in hosting my own &lt;a href=&quot;https:&#x2F;&#x2F;xmpp.org&#x2F;&quot;&gt;XMPP server&lt;&#x2F;a&gt; (or &lt;a href=&quot;https:&#x2F;&#x2F;zulip.com&#x2F;&quot;&gt;something else&lt;&#x2F;a&gt;?).&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;m also looking at moving my (and my family&#x27;s) personal data out of Google Photos and Dropbox and investigating Immich&#x2F;Seafile.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;Should you do this? Absolutely - if you&#x27;re comfortable with learning the above, you don&#x27;t need e-commerce or something dynamic, and want to bring back the early magic of the &lt;em&gt;distributed&lt;&#x2F;em&gt; web.
Hopefully this blog post can help others try the same thing!&lt;&#x2F;p&gt;
&lt;p&gt;There &lt;strong&gt;is&lt;&#x2F;strong&gt; some risk - I am my own SRE, I don&#x27;t have a CDN, and if anything goes wrong I&#x27;m on the hook myself - but to me the tradeoff is worth it.
I have everything I need to spin up my website on a new VPS in a matter of hours, if not minutes.&lt;&#x2F;p&gt;
&lt;p&gt;Overall this took me maybe a week of effort spread over a couple of months - and I loved every part of doing it. The result, in my opinion, speaks for itself. As someone who cares about game engine performance, how can I not care about my website&#x27;s performance too?&lt;&#x2F;p&gt;
&lt;p&gt;If you want a peek at my git repositories, or have suggestions for improvements, please reach out &lt;a href=&quot;mailto:contact@vikram.codes&quot;&gt;via email&lt;&#x2F;a&gt;.
Oh and if anyone reading this has a &lt;a href=&quot;https:&#x2F;&#x2F;lobste.rs&#x2F;&quot;&gt;lobste.rs&lt;&#x2F;a&gt; account, I&#x27;d love an invite.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Git LFS File Locking</title>
          <pubDate>Fri, 22 Mar 2024 00:00:00 +0000</pubDate>
          <author>Vikram Saran</author>
          <link>https://vikram.codes/blog/git-lfs-file-locking/</link>
          <guid>https://vikram.codes/blog/git-lfs-file-locking/</guid>
          <description xml:base="https://vikram.codes/blog/git-lfs-file-locking/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This article is an accompaniment to a &lt;a href=&quot;https:&#x2F;&#x2F;www.gdcvault.com&#x2F;play&#x2F;1029938&#x2F;Scaling-Git-Workflows-for-Game&quot;&gt;presentation I gave at GDC 2024&lt;&#x2F;a&gt;. You can download the &lt;a href=&quot;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&#x2F;git-lfs-file-locking&#x2F;git-slides.pdf&quot;&gt;presentation slides&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git-lfs&#x2F;git-lfs&#x2F;wiki&#x2F;File-Locking&quot;&gt;Git lfs file locking&lt;&#x2F;a&gt; is a key feature for game development using modern git, although it is currently underutilised.&lt;&#x2F;p&gt;
&lt;p&gt;A lockable file, in git terms, is a file which can only be edited by one person at a time - perfect for binary files, like art assets, and therefore an incredibly useful tool for game development.&lt;&#x2F;p&gt;
&lt;p&gt;A majority of large game development studios use Perforce over git, despite the fact that, nowadays, &lt;a href=&quot;https:&#x2F;&#x2F;www.anchorpoint.app&#x2F;blog&#x2F;scaling-git-to-1tb-of-files-with-gitlab-and-anchorpoint-using-git-lfs&quot;&gt;git can scale&lt;&#x2F;a&gt;, and has the features to match Perforce, as well as a vibrant ecosystem of tooling.&lt;&#x2F;p&gt;
&lt;p&gt;My presentation, and this guide, are an attempt to provide an alternative - to show game developers that git is perfectly capable of handling what you need.&lt;&#x2F;p&gt;
&lt;p&gt;The below is primarily a guide for technical users, setting up and using git lfs file locks via command line, although there is a discussion in how to make this accessible for less-technical users in my presentation, as well as in the &lt;a href=&quot;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&#x2F;git-lfs-file-locking&#x2F;#making-it-accessible&quot;&gt;Making It Accessible&lt;&#x2F;a&gt; section below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lfs-and-file-locking&quot;&gt;LFS and File Locking&lt;&#x2F;h2&gt;
&lt;p&gt;Git lfs file locking has been introduced in mainline git since 2020 - although an early version was released in Git 2.0.0 in 2016.&lt;&#x2F;p&gt;
&lt;p&gt;Git lfs has has been described in a &lt;a href=&quot;https:&#x2F;&#x2F;git-lfs.com&#x2F;&quot;&gt;range of places&lt;&#x2F;a&gt; online, so I won&#x27;t re-iterate here.&lt;&#x2F;p&gt;
&lt;p&gt;But as a short summary: a file which is managed by git lfs (Large File System) is actually stored as a &quot;pointer&quot;, which is then de-referenced (&quot;smudged&quot;) when you need the actual file contents.&lt;&#x2F;p&gt;
&lt;p&gt;This happens automatically with all modern git clients. In the past, it was common to run into issues and needing&lt;&#x2F;p&gt;
&lt;p&gt;Git lfs file locking, despite its name, does not require that a given file be marked as an lfs file in order to lock it, although interacting with the file locking mechanism is done through the &lt;code&gt;git lfs&lt;&#x2F;code&gt; subcommand.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-locking-works&quot;&gt;How locking works&lt;&#x2F;h2&gt;
&lt;p&gt;A lockable file is read-only by default, and in order to edit the file you must ask the server if you can &lt;em&gt;acquire&lt;&#x2F;em&gt; the &quot;lock&quot;.
Only one person can acquire a lock, per file, at a time.
If a second person requests the lock, the request will be rejected unless the first person has unlocked the file.&lt;&#x2F;p&gt;
&lt;p&gt;Naturally, commands interacting with the locking mechanism must typically query a server.
All modern git hosts support both lfs and file locking.&lt;&#x2F;p&gt;
&lt;p&gt;One important caveat: lockable files in git are locked &lt;em&gt;across all branches&lt;&#x2F;em&gt;, as they are locked by filepath only.
This is in contrast with Perforce, where files can be locked per branch.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up&quot;&gt;Setting up&lt;&#x2F;h2&gt;
&lt;p&gt;Files which you want to be lockable need to be marked as such in your &lt;code&gt;.gitattributes&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;p&gt;You can do so manually, by editing the file to add the &lt;code&gt;lockable&lt;&#x2F;code&gt; attribute to any relevant filetypes:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;*.psd filter=lfs diff=lfs merge=lfs -text lockable
&lt;&#x2F;span&gt;&lt;span&gt;shared
&lt;&#x2F;span&gt;&lt;span&gt;sharedtextfile.txt lockable
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You don&#x27;t need to mark a file as &lt;code&gt;lfs&lt;&#x2F;code&gt; in order to give it the lockable attribute!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Or via the command line:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;git lfs track&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt; --&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;lockable &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;*.psd&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;git lfs track&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt; --&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;lockable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt; --&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;filename &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;sharedtextfile.txt&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: the last command will actually mark the &quot;sharedtextfile.txt&quot; as both lfs and lockable - if you want to create a non-lfs lockable file, you will need to do so manually.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;You can set the attribute for any set of files using any regex pattern - meaning this can apply to swathes of files by type, by folder structure, or by any other pattern.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-3-daily-commands&quot;&gt;The 3 daily commands&lt;&#x2F;h2&gt;
&lt;p&gt;There are three important commands for git:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git lfs locks&lt;&#x2F;code&gt; - to see which locks are held&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;git lfs lock &amp;lt;file&amp;gt;&lt;&#x2F;code&gt; - to try and acquire a lock&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;git lfs unlock &amp;lt;file&amp;gt;&lt;&#x2F;code&gt; - to release a lock&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can investigate these commands and their options by using &lt;code&gt;--help&lt;&#x2F;code&gt;, like all git commands.&lt;&#x2F;p&gt;
&lt;details  &gt;
  &lt;summary&gt;&lt;code&gt;git lfs locks&lt;&#x2F;code&gt;&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;This command is lists all of the currently locked files by all users.&lt;&#x2F;p&gt;
&lt;p&gt;You can optionally pass in a path &lt;code&gt;--path=&amp;lt;path&amp;gt;&lt;&#x2F;code&gt; in order to check who owns a specific lock.&lt;&#x2F;p&gt;
&lt;p&gt;Another helpful option is &lt;code&gt;--local&lt;&#x2F;code&gt;, which lists only your own locks which are cached locally. This can be done offline without a call to the server.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  &gt;
  &lt;summary&gt;&lt;code&gt;git lfs lock &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;This command is as simple as it gets. You ask the server to &quot;lock&quot; the given file to your user. This blocks attempts by other users to lock or edit the file themselves, until the lock is released.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  &gt;
  &lt;summary&gt;&lt;code&gt;git lfs unlock &amp;lt;file&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Equally simple, this command lets the server know that you are done with the lock, and which to relinquish it.&lt;&#x2F;p&gt;
&lt;p&gt;If you have sufficient permissions, you can &lt;code&gt;--force&lt;&#x2F;code&gt; the server to unlock a file held by another user. This should be used sparingly, but can be useful if, for example, someone is off on leave and forgot to unlock a file!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  &gt;
  &lt;summary&gt;Bonus Options&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;As of git lfs 3.0, both lock and unlock support submitting multiple files.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, there is a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git-lfs&#x2F;git-lfs&#x2F;blob&#x2F;main&#x2F;docs&#x2F;api&#x2F;locking.md&quot;&gt;HTTP based API&lt;&#x2F;a&gt; if you want to integrate lock or unlock commands into a tool that you are building, and want to avoid command line calls to the git executable.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;switching-to-rebase&quot;&gt;Switching to Rebase&lt;&#x2F;h2&gt;
&lt;p&gt;Because git file locking works on file paths, and locks the file across all branches, this means that we are unfortunately unable to &lt;code&gt;git merge&lt;&#x2F;code&gt; a locked file.&lt;&#x2F;p&gt;
&lt;p&gt;Naturally, this can cause some problems! If you need to update a feature branch or release branch (the target) but the branch you are trying to update &lt;em&gt;from&lt;&#x2F;em&gt; (the origin) has a change to a file which someone holds a lock for... well... you are then unable to lock the file, and therefore unable to use &lt;code&gt;git merge&lt;&#x2F;code&gt; to update your branch.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: this is because of the way that git merge works. The merge commit created includes all of the changes that the origin branch has received since the target branch was last updated in this way.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Fortunately, git has &lt;code&gt;rebase&lt;&#x2F;code&gt; in order to do what you need.
Instead of merging the updated state of the origin branch into your target branch, you reapply your target branch &lt;em&gt;on top of&lt;&#x2F;em&gt; the origin.&lt;&#x2F;p&gt;
&lt;p&gt;Rebase has been &lt;a href=&quot;https:&#x2F;&#x2F;www.atlassian.com&#x2F;git&#x2F;tutorials&#x2F;rewriting-history&#x2F;git-rebase&quot;&gt;sufficiently explained&lt;&#x2F;a&gt; elsewhere, and a multitude of git experts have explained the &lt;a href=&quot;https:&#x2F;&#x2F;blog.git-init.com&#x2F;differences-between-git-merge-and-rebase-and-why-you-should-care&#x2F;&quot;&gt;other benefits&lt;&#x2F;a&gt; of &lt;a href=&quot;https:&#x2F;&#x2F;www.atlassian.com&#x2F;git&#x2F;tutorials&#x2F;merging-vs-rebasing&quot;&gt;switching to rebase&lt;&#x2F;a&gt;, so we can move on.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;trunk-based-development-and-devops&quot;&gt;Trunk Based Development and DevOps&lt;&#x2F;h2&gt;
&lt;p&gt;One thing that you get, as a side effect of using a rebase-centric workflow, is easier access to being able to achieve &lt;a href=&quot;https:&#x2F;&#x2F;trunkbaseddevelopment.com&#x2F;&quot;&gt;trunk based development&lt;&#x2F;a&gt;, which is one of the key technical capabilities of &lt;a href=&quot;https:&#x2F;&#x2F;dora.dev&#x2F;research&#x2F;&quot;&gt;world class devops&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;making-it-accessible&quot;&gt;Making It Accessible&lt;&#x2F;h2&gt;
&lt;p&gt;One tricky problem in game development is users who don&#x27;t want or need to know about their version control internals, yet need to be able to work with it - your designers and artists.&lt;&#x2F;p&gt;
&lt;p&gt;For file locking, I would generally recommend that all lockable files have an integration in their DCC (Digital Content Creation) tool of choice, whether it&#x27;s a game engine or anything else.&lt;&#x2F;p&gt;
&lt;p&gt;However, in general, less-technical users will want a GUI method to interact with git. Whatever method you select should include a way to run the above git lfs file locking commands. To date, I believe the best in this area is &lt;a href=&quot;https:&#x2F;&#x2F;git-fork.com&#x2F;&quot;&gt;git-fork&lt;&#x2F;a&gt;, although I have also had success with &lt;a href=&quot;https:&#x2F;&#x2F;tortoisegit.org&#x2F;&quot;&gt;TortoiseGit&lt;&#x2F;a&gt;.
One interesting option is &lt;a href=&quot;https:&#x2F;&#x2F;www.anchorpoint.app&#x2F;&quot;&gt;AnchorPoint&lt;&#x2F;a&gt;, although note that it does &lt;em&gt;not&lt;&#x2F;em&gt; use git&#x27;s implementation of file locking, but rather its own system.&lt;&#x2F;p&gt;
&lt;p&gt;In order to ensure that rebases are trivial, there are a few simple tricks:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Reduce the number of required rebases&lt;&#x2F;li&gt;
&lt;li&gt;Reuse your team&#x27;s expertise in git through good visual documentation and live support&lt;&#x2F;li&gt;
&lt;li&gt;Recycle* whatever rebases are still required by using automation&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;blockquote&gt;
&lt;p&gt;*It&#x27;s actually just reduce how many user-initiated rebases are needed. but I really wanted to do the 3 R&#x27;s&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;All of the above are described in greater detail in &lt;a href=&quot;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&#x2F;git-lfs-file-locking&#x2F;git-slides.pdf&quot;&gt;my presentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;details  &gt;
  &lt;summary&gt;References&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Most searchers for git lfs file locking stumble upon the &lt;em&gt;proposal&lt;&#x2F;em&gt; docs rather than the final docs, so part of this guide&#x27;s purpose is to improve the SEO of the actual documentation pages (linked throughout, and below).&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git-lfs&#x2F;git-lfs&#x2F;blob&#x2F;main&#x2F;docs&#x2F;man&#x2F;git-lfs-track.adoc&quot;&gt;git lfs track&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git-lfs&#x2F;git-lfs&#x2F;blob&#x2F;main&#x2F;docs&#x2F;man&#x2F;git-lfs-locks.adoc&quot;&gt;git lfs locks&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git-lfs&#x2F;git-lfs&#x2F;blob&#x2F;main&#x2F;docs&#x2F;man&#x2F;git-lfs-lock.adoc&quot;&gt;git lfs lock&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git-lfs&#x2F;git-lfs&#x2F;blob&#x2F;main&#x2F;docs&#x2F;man&#x2F;git-lfs-unlock.adoc&quot;&gt;git lfs unlock&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
</description>
      </item>
      <item>
          <title>Agile Spikes</title>
          <pubDate>Fri, 15 Mar 2024 00:00:00 +0000</pubDate>
          <author>Vikram Saran</author>
          <link>https://vikram.codes/blog/agile-spikes/</link>
          <guid>https://vikram.codes/blog/agile-spikes/</guid>
          <description xml:base="https://vikram.codes/blog/agile-spikes/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This article is a companion to my talk at GDC 2024,
&lt;a href=&quot;https:&#x2F;&#x2F;www.gdcvault.com&#x2F;play&#x2F;1029907&#x2F;Agile-Spikes-in-Game-Dev&quot;&gt;&quot;Agile Spikes in Game Dev: Getting to the Point&quot;&lt;&#x2F;a&gt;.
You can download the &lt;a href=&quot;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&#x2F;agile-spikes&#x2F;spike-slides.pdf&quot;&gt;presentation slides&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The meat of this article is presented in the two attached templates.
If you download them and try them out, please &lt;a href=&quot;mailto:contact@vikram.codes&quot;&gt;email me&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&#x2F;agile-spikes&#x2F;spike-template.docx&quot;&gt;Word doc&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&#x2F;agile-spikes&#x2F;spike-template.md.txt&quot;&gt;Markdown&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Alternatively, you can try Microsoft&#x27;s
&lt;a href=&quot;https:&#x2F;&#x2F;microsoft.github.io&#x2F;code-with-engineering-playbook&#x2F;design&#x2F;design-reviews&#x2F;recipes&#x2F;technical-spike&#x2F;&quot;&gt;much lighter template&lt;&#x2F;a&gt;
but if you don&#x27;t have much experience with Spikes it might be safer to start with the guard rails on.&lt;&#x2F;p&gt;
&lt;p&gt;The following is a very brief overview of what an Agile Spike is, and some links to further reading.
There is a lot more information in the GDC talk (linked above).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-a-spike&quot;&gt;What is a Spike?&lt;&#x2F;h2&gt;
&lt;p&gt;As is the recent trend, I asked ChatGPT (instead of a dictionary) to provide a definition for what is an Agile Spike:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;An Agile spike, often referred to simply as a &quot;spike,&quot; is a time-boxed research or investigative activity
conducted by Agile teams to gather information, answer a specific question, or mitigate a risk.
It is a technique commonly used in Agile software development methodologies like Scrum or Extreme Programming (XP).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Not bad, ChatGPT! My own definition is this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A spike is a short, reproducible, exploratory task to gain the knowledge you need to reduce the risk of a
technical approach, better understand a requirement, or increase the reliability of a story estimate.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The key difference between mine and most other definitions is &quot;reproducible&quot;. I find that it helps
improve the quality of the spike reports, and help build a knowledge base that is more useful in a longer term.&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully, the links below, the templates, and the above definitions help you all out with figuring
out how to apply this to your own projects - and if all else fails, reach out, I&#x27;m always happy to answer questions.&lt;&#x2F;p&gt;
&lt;details  &gt;
  &lt;summary&gt;References&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.extremeprogramming.org&#x2F;rules&#x2F;spike.html&quot;&gt;Extreme Programming&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.c2.com&#x2F;?SpikeSolution&quot;&gt;WikiWikiWeb&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;scaledagileframework.com&#x2F;spikes&#x2F;&quot;&gt;SaFE&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;microsoft.github.io&#x2F;code-with-engineering-playbook&#x2F;design&#x2F;design-reviews&#x2F;recipes&#x2F;technical-spike&#x2F;&quot;&gt;Microsoft&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;users.cecs.anu.edu.au&#x2F;~ejmontgomery&#x2F;publications&#x2F;2013-08_spikes_tale.pdf&quot;&gt;Agile Development Spikes Applied to Computer Science Education, Woodward et al.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
</description>
      </item>
      <item>
          <title>Navigation Filtering and Avoidance - Basic Tag AI in UE4</title>
          <pubDate>Sun, 18 Mar 2018 00:00:00 +0000</pubDate>
          <author>Vikram Saran</author>
          <link>https://vikram.codes/blog/basic-tag/</link>
          <guid>https://vikram.codes/blog/basic-tag/</guid>
          <description xml:base="https://vikram.codes/blog/basic-tag/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;p&gt;In the last &lt;a href=&quot;&#x2F;blog&#x2F;basic-navigation&quot;&gt;couple&lt;&#x2F;a&gt; of &lt;a href=&quot;&#x2F;blog&#x2F;nav-modifiers-links&quot;&gt;posts&lt;&#x2F;a&gt;, we&#x27;ve explored some of the abilities of the UE4 Navigation System.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, we&#x27;re going to look at setting up some rudimentary AI logic using Blueprints&#x2F;C++. We&#x27;re still going to touch on some Navigation concepts however, specifically, &lt;em&gt;Filtering&lt;&#x2F;em&gt; and &lt;em&gt;Avoidance&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;By the end of the today, you&#x27;re going to have a fully functioning AI vs AI &quot;Game of Tag&quot;!&lt;&#x2F;p&gt;
&lt;p&gt;Specifically, we&#x27;re going to add:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A &quot;tagged&quot; status, to indicate if our AI is &quot;it&quot; or &quot;not-it&quot;. When the &quot;tagged&quot; status is changed, we&#x27;re going to change:
&lt;ul&gt;
&lt;li&gt;The colour of the mesh&lt;&#x2F;li&gt;
&lt;li&gt;The maximum walking speed
&lt;ul&gt;
&lt;li&gt;&quot;it&quot; bots can move slightly faster&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Depending upon being &quot;tagged&quot;, the AI will behave differently:
&lt;ul&gt;
&lt;li&gt;&quot;it&quot; bots will move towards the nearest &quot;not-it&quot; bot that it can see, or a random spot on the map if it can&#x27;t see&lt;&#x2F;li&gt;
&lt;li&gt;&quot;not-it&quot; bots will simply run to random points on the map
&lt;ul&gt;
&lt;li&gt;but we&#x27;ll bias this to try and run away from any visible &quot;it&quot; bots!&lt;&#x2F;li&gt;
&lt;li&gt;we&#x27;ll also turn on Avoidance for them, so they try and avoid being touched by the &quot;it&quot; bots, even when passing close by!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ll make it work with a little gameplay - an &quot;it&quot; bot can &quot;touch&quot; a &quot;not-it&quot; bot to make it &quot;it&quot;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: this variant of tag is sometimes known as &quot;zombie tag&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Finally, we&#x27;re going to use some Navigation Filtering to change whether the bot can use the Launchpads, so that &quot;it&quot; bots can&#x27;t use them - giving the &quot;not-it&quot; bots a chance to get away!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A lot of this, if not all of this, will be replaced in later stages (using the UE4 Behaviour Tree, Environmental Query System, and Perception System), but it&#x27;s useful to have a simple baseline script to compare against.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The project has been updated to use version 4.18. This change required no adjustments to the code that we&#x27;ve written so far.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;If you haven&#x27;t followed along, or just want to make sure you&#x27;re on the same page, you can download &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;vikram-codes&#x2F;ai-tag&#x2F;-&#x2F;archive&#x2F;basic-tag-template&#x2F;ai-tag-basic-tag-template.zip&quot;&gt;the current state of the project&lt;&#x2F;a&gt; from the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;vikram-codes&#x2F;ai-tag&quot;&gt;GitLab repository&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Apologies for the delay between posts - but hopefully you all enjoy this one!
Due to the time taken to get this ready for publication, 4.19 has just been released! There &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;vikram-codes&#x2F;ai-tag&#x2F;-&#x2F;commit&#x2F;2ff8b88feb6cd4214e3114e6dbabfe1bcb9c181a&quot;&gt;appear to be no issues with using 4.19&lt;&#x2F;a&gt; at this stage, and the next post will be released using 4.19.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;ai-character-tagged-status&quot;&gt;AI Character: Tagged Status&lt;&#x2F;h2&gt;
&lt;p&gt;The first thing we need to do is add a &quot;Tagged&quot; status to the Character.
We do this on the Character class because it is &lt;em&gt;game logic&lt;&#x2F;em&gt;, not &lt;em&gt;input logic&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re also going to make this a visible change, so in a new &lt;code&gt;ChangeTaggedStatus&lt;&#x2F;code&gt; function we&#x27;re going to make this set the material of the character.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re also going to add two collision volumes that we can use later. One will be for &quot;it&quot; bots to tag a new bot, and the other will be for the AI&#x27;s &quot;vision radius&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Before we start scripting, we&#x27;ll need to create two Material Instances of the Mannequin Body Material found at &lt;code&gt;TemplateContent&#x2F;Mannequin&#x2F;Character&#x2F;Materials&#x2F;M_UE4Man_Body&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;01-MaterialInstance.4a79f789157887f4.avif 321w&quot; sizes=&quot;(max-width: 768px) 100vw, 321px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;01-MaterialInstance.8c873fb2ebf4182b.webp 321w&quot; sizes=&quot;(max-width: 768px) 100vw, 321px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;01-MaterialInstance.8c873fb2ebf4182b.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;321&quot; height=&quot;564&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;You can then pick any two colours that you want to use for &quot;it&quot; and &quot;not-it&quot; bots. I chose Red and Blue:&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-MaterialInstances.0107fd0757792dcb.avif 327w&quot; sizes=&quot;(max-width: 768px) 100vw, 327px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-MaterialInstances.55cc66a789ad7202.webp 327w&quot; sizes=&quot;(max-width: 768px) 100vw, 327px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-MaterialInstances.55cc66a789ad7202.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;327&quot; height=&quot;126&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: If you want to learn more about Material Instances, the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Engine&#x2F;Rendering&#x2F;Materials&#x2F;MaterialInstances&#x2F;&quot;&gt;Documentation&lt;&#x2F;a&gt; is a great resource!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;details  name=&quot;tagged-status&quot; &gt;
  &lt;summary&gt;Blueprint: Tagged Status&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Although we could use an &lt;a href=&quot;https:&#x2F;&#x2F;wiki.unrealengine.com&#x2F;Blueprint_Essentials_-_5_-_Enum_Variables&quot;&gt;Enum&lt;&#x2F;a&gt; to indicate the status of our bot, because we are either &quot;tagged&quot; or not, we&#x27;re just going to create a single public Boolean variable &lt;code&gt;Tagged&lt;&#x2F;code&gt; on our Character class:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If we had more than two states, we would use an enum (and the rest of the tutorial would continue the same).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;03-Tagged.418c1181c70903fd.avif 288w&quot; sizes=&quot;(max-width: 768px) 100vw, 288px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;03-Tagged.ad3b998437417c85.webp 288w&quot; sizes=&quot;(max-width: 768px) 100vw, 288px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;03-Tagged.ad3b998437417c85.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;288&quot; height=&quot;50&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;We&#x27;re now going to create a function, &lt;code&gt;ChangeTaggedStatus&lt;&#x2F;code&gt;, which will allow us to use this. This function is going to do more than just set the value of our variable, it&#x27;s going to also set the material on our mesh and change our maximum walking speed:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-ChangeTaggedStatus.aae92431ed672fd8.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-ChangeTaggedStatus.2c673cb0bc3668cc.avif 1007w&quot; sizes=&quot;(max-width: 768px) 100vw, 1007px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-ChangeTaggedStatus.36fb81b0603882e9.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-ChangeTaggedStatus.843e34b6da0463b3.webp 1007w&quot; sizes=&quot;(max-width: 768px) 100vw, 1007px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-ChangeTaggedStatus.843e34b6da0463b3.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1007&quot; height=&quot;594&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You could easily replace the &#x27;magic&#x27; numbers &lt;code&gt;660.f&lt;&#x2F;code&gt; and &lt;code&gt;600.f&lt;&#x2F;code&gt; with variables that the designers could tweak in the editor.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;We also want to be able to set our Tagged status in the editor, so lets call this function in the construction script:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-ConstructionScript.d04f5f32d28494f6.avif 529w&quot; sizes=&quot;(max-width: 768px) 100vw, 529px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-ConstructionScript.82137964f75f5e0f.webp 529w&quot; sizes=&quot;(max-width: 768px) 100vw, 529px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-ConstructionScript.82137964f75f5e0f.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;223&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now we have a functioning &quot;tag&quot; in our game logic!&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;img src=&quot;06-TaggedWorking.gif&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;598&quot; &#x2F;&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Before we continue, lets add two collision volumes. One is our &quot;interaction&quot; volume (where an &quot;it&quot; bot can touch a &quot;not-it&quot; bot), and the other is our &quot;vision&quot; volume:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-VolumedCharacter.0ed89149fd05bcf6.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-VolumedCharacter.778c557b8b4b0747.avif 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-VolumedCharacter.0f8d45285d5e8840.avif 1388w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-VolumedCharacter.c429c85a142d6cb7.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-VolumedCharacter.c46bcd27e5154863.webp 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-VolumedCharacter.31940413e71c2654.webp 1388w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-VolumedCharacter.c46bcd27e5154863.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;832&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The reason we use a Vision volume, is because we want to ray-trace against every known possible enemy. This is fairly cheap with a Collider, because they update their contained actors automatically as we move, but if we were to use (for example) &lt;code&gt;Get All Actors of Class&lt;&#x2F;code&gt; to find all possible enemies, it would be very expensive!
Later in this series we&#x27;ll use the built in Unreal Engine systems to handle this for us.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;tagged-status&quot; &gt;
  &lt;summary&gt;C++: Tagged Status&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;There are a couple of small things to note when doing this in C++.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, if you&#x27;ve downloaded a copy of the project at the top of this blog, you&#x27;ll notice that there are two characters. We will be using the one with the &lt;code&gt;_cpp&lt;&#x2F;code&gt; suffix (that does not contain any BP logic).&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Halfway through the development of this post, I moved this into a &lt;code&gt;cpp&lt;&#x2F;code&gt; folder, and split up the level files into (BP&#x2F;cpp&#x2F;Geo). If you&#x27;re following just C++, you shouldn&#x27;t have an issue.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Secondly, our Controller is in C++, and you can&#x27;t utilise blueprint functions from C++ without a little bit of messing around! The main reason we want the BP character, anyway, is because it&#x27;s easier to set Meshes and Collision sizes there, etc. So for this section, we&#x27;re going to:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new &lt;code&gt;ATagCharacter&lt;&#x2F;code&gt; class&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;Derived from the &lt;code&gt;ACharacter&lt;&#x2F;code&gt; class&lt;&#x2F;li&gt;
&lt;li&gt;Add the variables, functions, and components which we might want to utilise in C++&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;ol&gt;
&lt;li&gt;Reparent the Blueprint class from the &lt;code&gt;Character&lt;&#x2F;code&gt; class to &lt;code&gt;TagCharacter&lt;&#x2F;code&gt; class in the Class Settings panel&lt;&#x2F;li&gt;
&lt;li&gt;Set the dimensions of our collision volumes in the child &lt;code&gt;BP_TagCharacter_cpp&lt;&#x2F;code&gt; class!&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Once you create the &lt;code&gt;ATagCharacter&lt;&#x2F;code&gt; class, it&#x27;ll have a load of cruft in there that we don&#x27;t need. You can empty out the class declaration and lets fill it in with:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UCLASS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;TAG_API &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fac863;&quot;&gt;ATagCharacter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;ACharacter
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GENERATED_BODY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;public&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Denotes the area that an &amp;quot;it&amp;quot; bot can &amp;quot;tag&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;VisibleAnywhere&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  UShapeComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; InteractionVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Denotes the area a bot can see
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;VisibleAnywhere&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  UShapeComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; VisionVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Whether the bot is &amp;quot;it&amp;quot; or &amp;quot;not-it&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Do not edit directly in any code (use ChangeTaggedStatus)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;EditAnywhere&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; BlueprintReadOnly&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt; Tagged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Sets the correct material and speed when changing Tag status
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;BlueprintCallable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ChangeTaggedStatus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;NewStatus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Call ChangeTaggedStatus if required
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnConstruction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FTransform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Transform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;override&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;private&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Using static material interfaces,
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; we can ensure we only load each material once
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt; UMaterialInterface&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; RedMaterial&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt; UMaterialInterface&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; BlueMaterial&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With just the above, you can probably fill in the implementation yourself! There&#x27;s nothing particularly novel that we&#x27;re doing. Let&#x27;s go over each function, however.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, the constructor. All we have to do is create our two components (I chose a Box and a Sphere as my shapes of choice) and load our materials into the static member variables:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; By default, the materials are null
&lt;&#x2F;span&gt;&lt;span&gt;UMaterialInterface&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;RedMaterial &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;nullptr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;UMaterialInterface&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;BlueMaterial &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;nullptr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; We are not going to use on tick logic in our character if we can avoid it
&lt;&#x2F;span&gt;&lt;span&gt; PrimaryActorTick&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;bCanEverTick &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Build default shapes for our volumes
&lt;&#x2F;span&gt;&lt;span&gt; InteractionVolume &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CreateDefaultSubobject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UBoxComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Interaction Volume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt; InteractionVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AttachToComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;RootComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; VisionVolume &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CreateDefaultSubobject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;USphereComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Vision Volume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt; VisionVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AttachToComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;RootComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; If the static material hasn&amp;#39;t been loaded yet
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;BlueMaterial &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;nullptr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Load the Material from our Content folder
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; BlueMaterialLoader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ConstructorHelpers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FObjectFinderOptional&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UMaterialInterface&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;    TEXT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&#x2F;Game&#x2F;TemplateContent&#x2F;Mannequin&#x2F;Character&#x2F;Materials&#x2F;MI_Body_Blue&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;BlueMaterialLoader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Succeeded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;   BlueMaterial &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; BlueMaterialLoader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Get&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; If the static material hasn&amp;#39;t been loaded yet
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;RedMaterial &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;nullptr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Load the Material from our Content folder
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; RedMaterialLoader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ConstructorHelpers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FObjectFinderOptional&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UMaterialInterface&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;    TEXT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&#x2F;Game&#x2F;TemplateContent&#x2F;Mannequin&#x2F;Character&#x2F;Materials&#x2F;MI_Body_Red&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;RedMaterialLoader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Succeeded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;   RedMaterial &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; RedMaterialLoader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Get&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If you make a mistake with the Constructor Helper calls, it may cause your project to crash upon opening. It&#x27;s easy enough to open the code file and comment it out. Just be careful with the line and you should be ok! Consider yourselves warned.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Next, the &lt;code&gt;OnConstruction&lt;&#x2F;code&gt; function simply calls &lt;code&gt;ChangeTaggedStatus&lt;&#x2F;code&gt; (as described in the header).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnConstruction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FTransform &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Transform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Super&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnConstruction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Transform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ChangeTaggedStatus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Tagged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, the &lt;code&gt;ChangeTaggedStatus&lt;&#x2F;code&gt; function does three things. Sets &lt;code&gt;Tagged&lt;&#x2F;code&gt; to argument passed in, and then based on the value, &lt;code&gt;SetMaterial&lt;&#x2F;code&gt; on the &lt;code&gt;Mesh&lt;&#x2F;code&gt; and the set the value of &lt;code&gt;MaxWalkSpeed&lt;&#x2F;code&gt; on the &lt;code&gt;CharacterMovementComponent&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ChangeTaggedStatus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;NewStatus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; Tagged &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; NewStatus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Select the correct material using the Ternary operator
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; Material &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tagged &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt; RedMaterial &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; BlueMaterial&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; If we have a mesh
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; Mesh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetMesh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Mesh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Apply the material
&lt;&#x2F;span&gt;&lt;span&gt;  Mesh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetMaterial&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Material&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Select the Walk Speed using the Ternary operator
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; MaxWalkSpeed &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tagged &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;660&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;f &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;600&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Apply the walk speed
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetCharacterMovement&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;MaxWalkSpeed &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; MaxWalkSpeed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we&#x27;ve made our base Character class in C++, lets change the &lt;code&gt;Parent Class&lt;&#x2F;code&gt; of our &lt;code&gt;BP_TagCharacter_cpp&lt;&#x2F;code&gt;. We can do this in the Class Settings panel:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-ClassSettings.5a1d9ce6dd25629f.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-ClassSettings.b4889ef4feeb235a.avif 777w&quot; sizes=&quot;(max-width: 768px) 100vw, 777px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-ClassSettings.542fba312a30533f.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-ClassSettings.5902647926e48e22.webp 777w&quot; sizes=&quot;(max-width: 768px) 100vw, 777px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-ClassSettings.5902647926e48e22.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;777&quot; height=&quot;69&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-ParentClass.f2f26db3bfba1c36.avif 473w&quot; sizes=&quot;(max-width: 768px) 100vw, 473px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-ParentClass.90536b8228a56ad6.webp 473w&quot; sizes=&quot;(max-width: 768px) 100vw, 473px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-ParentClass.90536b8228a56ad6.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;235&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Once you&#x27;ve done that, you can edit the settings of the Interaction and Vision Volumes so that they fit exactly where you want them to:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-VolumedCharacter.c1963db9cf709164.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-VolumedCharacter.6128a0bd0881e9fa.avif 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-VolumedCharacter.e15027c2e803be2a.avif 1920w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-VolumedCharacter.9c27b69b218efbeb.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-VolumedCharacter.5fb601daefb51498.webp 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-VolumedCharacter.f2c8a303fc725db8.webp 1920w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-VolumedCharacter.5fb601daefb51498.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;766&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: This is one of the best ways to work with C++ and BP in Unreal. Using C++ to drive your core game logic, and using the BP visual editor to help select and place your Meshes&#x2F;Materials&#x2F;etc.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You could easily replace the &#x27;magic&#x27; numbers &lt;code&gt;660.f&lt;&#x2F;code&gt; and &lt;code&gt;600.f&lt;&#x2F;code&gt; with UPROPERTYs that the designers could tweak in the editor.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;fixing-the-launchpad&quot;&gt;Fixing the Launchpad&lt;&#x2F;h2&gt;
&lt;p&gt;At this point, you may notice that your AI starts having weird interactions with any Launchpads around the level - this is because the extra volumes we&#x27;ve created are triggering the Launchpad!&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re going to have to make a couple of fixes.&lt;&#x2F;p&gt;
&lt;details  name=&quot;fix-launchpad&quot; &gt;
  &lt;summary&gt;Blueprint: Fixing the Launchpad&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Firstly, before we trigger the launch, lets check that the &lt;code&gt;Other Comp&lt;&#x2F;code&gt; was the &lt;code&gt;Root Component&lt;&#x2F;code&gt; of the &lt;code&gt;Other Actor&lt;&#x2F;code&gt;. This means the launch will only trigger when we touch the Launchpad with our main collider:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;11-LaunchpadTrigger.07275cf9f2b1188a.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;11-LaunchpadTrigger.b2d04a60bcd0bf26.avif 1122w&quot; sizes=&quot;(max-width: 768px) 100vw, 1122px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;11-LaunchpadTrigger.2f34810b96758dfb.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;11-LaunchpadTrigger.1c84c62226b759a8.webp 1122w&quot; sizes=&quot;(max-width: 768px) 100vw, 1122px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;11-LaunchpadTrigger.1c84c62226b759a8.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1122&quot; height=&quot;220&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Secondly, we&#x27;ll need to change the &lt;code&gt;Calculate Launch Velocity&lt;&#x2F;code&gt; function we wrote to use the bounds of the root component, instead of the actor, when calculating our &lt;code&gt;Start&lt;&#x2F;code&gt; location for the Launch:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;12-LaunchpadVelocity.6ea0cf3f8ac82393.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;12-LaunchpadVelocity.f291870e840f00ce.avif 1119w&quot; sizes=&quot;(max-width: 768px) 100vw, 1119px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;12-LaunchpadVelocity.867958486334ff08.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;12-LaunchpadVelocity.038826b0e960d36c.webp 1119w&quot; sizes=&quot;(max-width: 768px) 100vw, 1119px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;12-LaunchpadVelocity.038826b0e960d36c.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1119&quot; height=&quot;356&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;And now the Launchpads should work properly again!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;fix-launchpad&quot; &gt;
  &lt;summary&gt;C++: Fixing the Launchpad&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Firstly, before we trigger the launch, lets check that the &lt;code&gt;OtherComp&lt;&#x2F;code&gt; was the &lt;code&gt;Root Component&lt;&#x2F;code&gt; of the &lt;code&gt;Other&lt;&#x2F;code&gt; Actor. This means the launch will only trigger when we touch the Launchpad with our main collider. To do so, change the line in &lt;code&gt;OnTriggerBeginOverlap&lt;&#x2F;code&gt; from:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Character &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt; OtherComp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span&gt; Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetRootComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;())
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Secondly, we&#x27;ll need to change the &lt;code&gt;CalculateLaunchVelocity&lt;&#x2F;code&gt; function we wrote to use the bounds of the root component, instead of the actor, when calculating our &lt;code&gt;Start&lt;&#x2F;code&gt; location for the Launch.&lt;&#x2F;p&gt;
&lt;p&gt;This will be easier if we change the function&#x27;s &lt;code&gt;AActor* LaunchedActor&lt;&#x2F;code&gt; parameter to &lt;code&gt;ACharacter* LaunchedActor&lt;&#x2F;code&gt; (as we have already cast the Character, this should work easily).&lt;&#x2F;p&gt;
&lt;p&gt;Then, the line that was:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt; Start&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Z &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-=&lt;&#x2F;span&gt;&lt;span&gt; LaunchedActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetSimpleCollisionHalfHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Becomes:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt; Start&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Z &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-=&lt;&#x2F;span&gt;&lt;span&gt; LaunchedCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetCapsuleComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetScaledCapsuleHalfHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And now the Launchpads should work properly again!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;ai-controller-movement-behaviour&quot;&gt;AI Controller: Movement Behaviour&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have our status, we&#x27;re going to tweak our Controller to take notice of this.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, we&#x27;re going to need a way for our AI to &quot;see&quot; other bots!&lt;&#x2F;p&gt;
&lt;p&gt;Then, if we&#x27;re &quot;it&quot;, we&#x27;ll move straight towards them!&lt;&#x2F;p&gt;
&lt;p&gt;If we&#x27;re &quot;not-it&quot;, we&#x27;re just going to move to a random point. However, if we can see some &quot;it&quot; bots, we&#x27;re going &lt;em&gt;bias&lt;&#x2F;em&gt; our search for our random point away from any visible &quot;its&quot;.&lt;&#x2F;p&gt;
&lt;details  name=&quot;movement-behaviour&quot; &gt;
  &lt;summary&gt;Blueprint: Movement Behaviour&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;To let our AI &quot;see&quot; the other bots inside the vision sphere, lets make a new &lt;em&gt;pure&lt;&#x2F;em&gt; function inside our &lt;code&gt;BP_TagCharacter&lt;&#x2F;code&gt; - &lt;code&gt;GetVisibleEnemies&lt;&#x2F;code&gt;. This function is going to get all of the AI Characters that are inside the volume, filter by &quot;Tagged&quot; status, and then ray-cast to them to ensure we can see them.&lt;&#x2F;p&gt;
&lt;p&gt;The initial part of the function looks like this:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-GetVisibleEnemies.a02e02fec410ece3.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-GetVisibleEnemies.b79db59adbe13759.avif 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-GetVisibleEnemies.e47ba75fef05a3c3.avif 1404w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-GetVisibleEnemies.1ae71d198b817ba8.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-GetVisibleEnemies.b10e25356f47d52d.webp 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-GetVisibleEnemies.4ff9af6459090cc3.webp 1404w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-GetVisibleEnemies.b10e25356f47d52d.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;479&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The raycast is a little tricky. What we want to do is raycast from the eyes of our AI, towards the centre of the possibly visible enemy. If the first object we touch is the enemy, then we know we have a direct &quot;line of sight&quot;! To do this, we&#x27;re going to write a new &lt;em&gt;pure&lt;&#x2F;em&gt; function, &lt;code&gt;CanSeeTarget&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-CanSeeTarget.f102fc90687f209e.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-CanSeeTarget.0f45f17300070119.avif 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-CanSeeTarget.4085197fbc920116.avif 1555w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-CanSeeTarget.2c061b638cb7d2ec.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-CanSeeTarget.7221acd654978e71.webp 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-CanSeeTarget.4cb0fc6f8511adfb.webp 1555w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-CanSeeTarget.7221acd654978e71.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;535&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Pay attention to the argument of the Trace Channel parameter - we change from &lt;code&gt;Visibility&lt;&#x2F;code&gt; to &lt;code&gt;Camera&lt;&#x2F;code&gt;, because Characters don&#x27;t block Visibility by default!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: By default, Base Eye Height is 64, but we should set it to be 72 for the default mannequin. This can be set on the character, under the &lt;code&gt;Camera&lt;&#x2F;code&gt; heading.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If you wanted to be a bit more accurate, you could raycast from the eyes to the feet, centre, &lt;em&gt;and&lt;&#x2F;em&gt; head, and if any of the three raycasts succeeds you can see the target. But that may be overkill!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Hook up the new function in the comment we left earlier, and it should end up looking like this:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-GetVisibleEnemiesFull.e6892f277e2b512a.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-GetVisibleEnemiesFull.06bbd887f239b621.avif 1090w&quot; sizes=&quot;(max-width: 768px) 100vw, 1090px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-GetVisibleEnemiesFull.fb89b33dae2519c4.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-GetVisibleEnemiesFull.7d6f3a0416cee4ad.webp 1090w&quot; sizes=&quot;(max-width: 768px) 100vw, 1090px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-GetVisibleEnemiesFull.7d6f3a0416cee4ad.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1090&quot; height=&quot;350&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now that the Character has its gameplay logic hooked up, we can head over to the Controller and start changing how our AI &lt;em&gt;thinks&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing we need to do is store a reference to our Controlled Pawn which has been Cast correctly, because we need to start using the values (like &lt;code&gt;Tagged&lt;&#x2F;code&gt;) that we&#x27;re adding to the &lt;code&gt;BP_TagCharacter&lt;&#x2F;code&gt; class. We&#x27;re also going to replace calls of &lt;code&gt;Go To Random Waypoint&lt;&#x2F;code&gt; with &lt;code&gt;Movement Behaviour&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-StoreTagCharacter.c7ff50dcca803a14.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-StoreTagCharacter.2356b7336c1375e6.avif 1058w&quot; sizes=&quot;(max-width: 768px) 100vw, 1058px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-StoreTagCharacter.65cd29c92f4273a6.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-StoreTagCharacter.1daa019b867cc24a.webp 1058w&quot; sizes=&quot;(max-width: 768px) 100vw, 1058px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-StoreTagCharacter.1daa019b867cc24a.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1058&quot; height=&quot;588&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We have to call Movement Behaviour &lt;em&gt;after&lt;&#x2F;em&gt; we store our Tag Character.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;code&gt;Movement Behaviour&lt;&#x2F;code&gt; is a really simply function which &lt;em&gt;abstracts&lt;&#x2F;em&gt; away how we&#x27;re handling our movement:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-MovementBehaviour.6ddd8e0edadaaac9.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-MovementBehaviour.04ebe2c249fafaa7.avif 840w&quot; sizes=&quot;(max-width: 768px) 100vw, 840px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-MovementBehaviour.fe803359137a72de.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-MovementBehaviour.f0b80fb430fc1226.webp 840w&quot; sizes=&quot;(max-width: 768px) 100vw, 840px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-MovementBehaviour.f0b80fb430fc1226.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;840&quot; height=&quot;300&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;In our &lt;code&gt;It Behaviour&lt;&#x2F;code&gt; function, we want to either &lt;code&gt;Move to Actor&lt;&#x2F;code&gt; on the first any we can see, if we can see any. Otherwise, we&#x27;ll &lt;code&gt;Go To Random Waypoint&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-ItBehaviour.1cfc35088a7f1985.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-ItBehaviour.90b0cc5ae433c987.avif 1172w&quot; sizes=&quot;(max-width: 768px) 100vw, 1172px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-ItBehaviour.7e10693763b13bac.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-ItBehaviour.02fb64829fd5efa0.webp 1172w&quot; sizes=&quot;(max-width: 768px) 100vw, 1172px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-ItBehaviour.02fb64829fd5efa0.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1172&quot; height=&quot;370&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: You can make the behaviour a bit more deterministic (and smarter) by making it move towards the nearest target, rather than the 0th target in the array.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In our &lt;code&gt;Not-it Behaviour&lt;&#x2F;code&gt; function, we&#x27;re going to use similar logic. However, if enemies are around, we&#x27;re going to generate three random waypoints, and pick the one that is the best option. We&#x27;ll do this logic in a new &lt;em&gt;pure&lt;&#x2F;em&gt; function &lt;code&gt;Find Flee Target&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-NotItBehaviour.cecc375a8009a134.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-NotItBehaviour.c30a5a58d4c56e1d.avif 1172w&quot; sizes=&quot;(max-width: 768px) 100vw, 1172px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-NotItBehaviour.c41bc12a16c48fd0.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-NotItBehaviour.24ec08f606e265a2.webp 1172w&quot; sizes=&quot;(max-width: 768px) 100vw, 1172px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-NotItBehaviour.24ec08f606e265a2.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1172&quot; height=&quot;437&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;By the time we get into the function, we are guaranteed to have at least one enemy. To bias our waypoint target away from all enemies, we&#x27;re going to generate &lt;strong&gt;three&lt;&#x2F;strong&gt; &lt;code&gt;Random Waypoint&lt;&#x2F;code&gt;s and then we&#x27;ll find the one that is the furthest away from all enemies.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: At this point, I usually prefer to write the code in C++, as it can get quite messy in BP, but it&#x27;s perfectly viable.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The first thing we need to do inside the &lt;code&gt;Find Flee Target&lt;&#x2F;code&gt; is set up some local variables:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;20-LocalVariables.d8ad1c384ab39bf9.avif 283w&quot; sizes=&quot;(max-width: 768px) 100vw, 283px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;20-LocalVariables.32a653475e61d4a6.webp 283w&quot; sizes=&quot;(max-width: 768px) 100vw, 283px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;20-LocalVariables.32a653475e61d4a6.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;283&quot; height=&quot;114&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Local variables are accessible only during the function call, and will be set to their default values every time the function is called (i.e. they don&#x27;t carry over information).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;21-WaypointDistanceVariables.364da139a38c6843.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;21-WaypointDistanceVariables.ea3b14dd5c4b4507.avif 872w&quot; sizes=&quot;(max-width: 768px) 100vw, 872px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;21-WaypointDistanceVariables.8fac8cade19f1bfe.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;21-WaypointDistanceVariables.a26900a335d1527e.webp 872w&quot; sizes=&quot;(max-width: 768px) 100vw, 872px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;21-WaypointDistanceVariables.a26900a335d1527e.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;872&quot; height=&quot;344&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Next, we&#x27;re going to loop through each of the Waypoints that we generated, and figure out which is the furthest away from all the visible enemies:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-FindFleeTarget.e56b5308ad6a6c8f.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-FindFleeTarget.c4564c35dca07a4c.avif 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-FindFleeTarget.f8e71d04ffa26574.avif 1458w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-FindFleeTarget.0dc9187b7ce39d5c.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-FindFleeTarget.43a177b05623330d.webp 1280w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-FindFleeTarget.e31dec19f9056e2f.webp 1458w&quot; sizes=&quot;(max-width: 768px) 100vw, 1280px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-FindFleeTarget.43a177b05623330d.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;397&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now if you drop in two AI characters, one who&#x27;s &lt;code&gt;Tagged&lt;&#x2F;code&gt; by default, you should see them start chasing each other around!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;movement-behaviour&quot; &gt;
  &lt;summary&gt;C++: Movement Behaviour&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Firstly, lets add a couple of functions to our &lt;code&gt;TagCharacter&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;public&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;BlueprintCallable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; TArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetVisibleEnemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;BlueprintPure&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CanSeeEnemy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The code for these is rather simple:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; TArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetVisibleEnemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt; TArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; VisibleCharacters&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; VisionVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetOverlappingActors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;VisibleCharacters&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;StaticClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; TArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; VisibleEnemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; Character &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; VisibleCharacters&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Character &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; TagCharacter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Cast&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; If they don&amp;#39;t have the same tag as me, and I can see them
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Tagged &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;!=&lt;&#x2F;span&gt;&lt;span&gt; Tagged &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CanSeeEnemy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;   VisibleEnemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Add&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; VisibleEnemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We use &lt;a href=&quot;http:&#x2F;&#x2F;blog.mbedded.ninja&#x2F;programming&#x2F;languages&#x2F;c-plus-plus&#x2F;magic-statics&quot;&gt;function-local static initialization&lt;&#x2F;a&gt; to  prevent us from creating &lt;code&gt;TArray&lt;&#x2F;code&gt;&#x27;s for &lt;code&gt;VisibleCharacters&lt;&#x2F;code&gt; with every call to this function. It might be an unecessary optimisation at &lt;em&gt;this&lt;&#x2F;em&gt; point - but this kind of thinking early on can help you scale your code!
We don&#x27;t do this for the second array, because we can rely on &lt;a href=&quot;https:&#x2F;&#x2F;en.cppreference.com&#x2F;w&#x2F;cpp&#x2F;language&#x2F;copy_elision&quot;&gt;Return Value Optimisation&lt;&#x2F;a&gt; to take care of the optimisation for that.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CanSeeEnemy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Raycast from my eyes
&lt;&#x2F;span&gt;&lt;span&gt; FVector &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;RayOrigin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetCapsuleComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Bounds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Origin &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span&gt; RayOrigin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Z &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;+=&lt;&#x2F;span&gt;&lt;span&gt; BaseEyeHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; To the middle of their body
&lt;&#x2F;span&gt;&lt;span&gt; FVector &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;RayTarget&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetCapsuleComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Bounds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Origin &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt; FHitResult &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;HitResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ForceInit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; FCollisionQueryParams params&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; params&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AddIgnoredActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetWorld&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;LineTraceSingleByChannel&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;HitResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; RayOrigin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; RayTarget&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ECC_Camera&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; params&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; HitResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Actor &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span&gt; Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We use the same static initialisation for the HitResult. Using &lt;em&gt;&quot;magic statics&quot;&lt;&#x2F;em&gt; is ideal in situations where you will be creating a large data structure (&amp;gt;32 bytes) frequently, but do not need it to remain useful for a long period of time.
This does mean, however, that you &lt;a href=&quot;https:&#x2F;&#x2F;en.cppreference.com&#x2F;w&#x2F;cpp&#x2F;language&#x2F;storage_duration#Static_local_variables&quot;&gt;&lt;em&gt;can not truly multithread such code&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Alternatively, you can use a private member variable to achieve the same effect. However, this increases the size of your &lt;code&gt;TagCharacter&lt;&#x2F;code&gt; &lt;em&gt;per instance&lt;&#x2F;em&gt;, and any function utilising that cannot be &lt;code&gt;const&lt;&#x2F;code&gt; (without using the &lt;a href=&quot;https:&#x2F;&#x2F;en.cppreference.com&#x2F;w&#x2F;cpp&#x2F;language&#x2F;cv&quot;&gt;&lt;code&gt;mutable&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; type specifier).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Pay attention to the argument of the Trace Channel parameter - we change from &lt;code&gt;Visibility&lt;&#x2F;code&gt; to &lt;code&gt;Camera&lt;&#x2F;code&gt;, because Characters don&#x27;t block Visibility by default!&lt;&#x2F;p&gt;
&lt;p&gt;By default, Base Eye Height is 64, but we should set it to be 72 for the default mannequin. This can be set on the character, under the &lt;code&gt;Camera&lt;&#x2F;code&gt; heading, or in the constructor.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If you wanted to be a bit more accurate, you could raycast from the eyes to the feet, centre, &lt;em&gt;and&lt;&#x2F;em&gt; head, and if any of the three raycasts succeeds you can see the target. But that may be overkill!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Now that the Character has its gameplay logic hooked up, we can head over to the Controller and start changing how our AI &lt;em&gt;thinks&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s define the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;MovementBehaviour&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FindFleeTarget&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; TArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Enemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;First, lets replace all existing uses of &lt;code&gt;GoToRandomWaypoint&lt;&#x2F;code&gt; to use &lt;code&gt;MovementBehaviour&lt;&#x2F;code&gt; instead, and store a pre-cast reference to our &lt;code&gt;TagCharacter&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;BeginPlay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Super&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;BeginPlay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UGameplayStatics&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetAllActorsOfClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ATargetPoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;StaticClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(),&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; Waypoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; TagCharacter &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Cast&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;LandedDelegate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AddUniqueDynamic&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;OnLanded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;MovementModeChangedDelegate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AddUniqueDynamic&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;OnMovementModeChanged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;MovementBehaviour&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnMoveCompleted&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;FAIRequestID &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;RequestID&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FPathFollowingResult &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Super&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnMoveCompleted&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;RequestID&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetWorldTimerManager&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetTimer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;TimerHandle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;MovementBehaviour&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next, lets write our &lt;code&gt;MovementBehaviour&lt;&#x2F;code&gt; function:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;MovementBehaviour&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const auto&lt;&#x2F;span&gt;&lt;span&gt; VisibleEnemies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetVisibleEnemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;VisibleEnemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Num&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;() == &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GoToRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Tagged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;MoveToActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;VisibleEnemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;else
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;MoveToActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FindFleeTarget&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;VisibleEnemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;));
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, lets write the &lt;code&gt;FindFleeTarget&lt;&#x2F;code&gt; function.
By the time we get into the function, we are guaranteed to have at least one enemy. To bias our waypoint target away from all enemies, we&#x27;re going to generate &lt;strong&gt;three&lt;&#x2F;strong&gt; &lt;code&gt;Random Waypoint&lt;&#x2F;code&gt;s and then we&#x27;ll find the one that is the furthest away from all enemies.&lt;&#x2F;p&gt;
&lt;p&gt;Once we generate the waypoints, lets &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Core&#x2F;Containers&#x2F;Algo&#x2F;Algo__Sort&#x2F;2&#x2F;index.html&quot;&gt;&lt;code&gt;Algo::Sort&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; them by distance to the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;Kismet&#x2F;UGameplayStatics&#x2F;GetActorArrayAverageLocation&#x2F;&quot;&gt;&lt;code&gt;ActorArrayAverageLocation&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; for the enemies:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FindFleeTarget&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; TArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Enemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FVector EnemyAverageLocation &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UGameplayStatics&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetActorArrayAverageLocation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(static_cast&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&amp;gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Enemies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; TArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; FleePoints &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;() };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Sort by the greatest distance to the enemy average location
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Algo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Sort&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FleePoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, [&amp;amp;](&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; A&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; B&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;) {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FVector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;DistSquared&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;A&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetActorLocation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(),&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; EnemyAverageLocation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FVector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;DistSquared&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;B&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetActorLocation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(),&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; EnemyAverageLocation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; FleePoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;];
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: I had to add &lt;code&gt;const&lt;&#x2F;code&gt; to &lt;code&gt;GetRandomWaypoint&lt;&#x2F;code&gt; - which should have been marked as such from the beginning!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We use &lt;code&gt;DistSquared&lt;&#x2F;code&gt; for our distance check, because we don&#x27;t care about the exact distance, but only the &lt;em&gt;relative distance&lt;&#x2F;em&gt; from each waypoint. Using the &lt;code&gt;Squared&lt;&#x2F;code&gt; version allows us to &lt;a href=&quot;https:&#x2F;&#x2F;books.google.com.au&#x2F;books?id=RFF0AgAAQBAJ&amp;amp;pg=PA134&amp;amp;lpg=PA134&amp;amp;dq=squared&quot;&gt;avoid an expensive &lt;code&gt;sqrt&lt;&#x2F;code&gt; operation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The funny-looking second parameter to &lt;code&gt;Algo::Sort&lt;&#x2F;code&gt; is a &lt;a href=&quot;https:&#x2F;&#x2F;en.cppreference.com&#x2F;w&#x2F;cpp&#x2F;language&#x2F;lambda&quot;&gt;&lt;em&gt;lambda&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;. You can think of it as an unnamed function which: &quot;captures&quot; (&lt;code&gt;[&amp;amp;]&lt;&#x2F;code&gt;) all local variables (by reference), accepts two &lt;code&gt;const AActor*&lt;&#x2F;code&gt; as parameters, and has a function body with automatic return type deduction.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We return true if A should be earlier in the order than B, so we return true if A&#x27;s distance to the enemies &lt;em&gt;greater than&lt;&#x2F;em&gt; B&#x27;s distance to the enemies, and then return the first result.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Now if you drop in two AI characters, one who&#x27;s &lt;code&gt;Tagged&lt;&#x2F;code&gt; by default, you should see them start chasing each other around!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;gameplay-tagging-behaviour&quot;&gt;Gameplay: Tagging Behaviour&lt;&#x2F;h2&gt;
&lt;p&gt;Now lets add &quot;tagging&quot; when the &quot;it&quot; bot gets close enough to its target.&lt;&#x2F;p&gt;
&lt;p&gt;For the Character, we want to add a couple of bits of game logic:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When we are &quot;it&quot; and a &quot;not-it&quot; Character is in range, &quot;tag&quot; the target&lt;&#x2F;li&gt;
&lt;li&gt;When we are tagged, update our behaviour&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If we wanted to, we could also make this a little more &quot;interactive&quot;, by raising an event in the Character, and having the Controller decide whether or not to &quot;tag&quot; the target. This would allow, for example, a small grace period if an AI bot were to catch a player bot - creating much more user-friendly gameplay.&lt;&#x2F;p&gt;
&lt;p&gt;However, as we are just making a purely AI-vs-AI demo, we&#x27;re going to code everything to happen immediately.&lt;&#x2F;p&gt;
&lt;details  name=&quot;tagging-behaviour&quot; &gt;
  &lt;summary&gt;Blueprint: Tagging Behaviour&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Firstly, when the Character can touch something (Interaction Component Begin Overlap), and that thing is another player (BP_TagCharacter), and we are &quot;it&quot;, and they are &quot;not-it&quot; - we will &quot;tag&quot; them!&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-TaggingTarget.9e3136211bf7dd91.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-TaggingTarget.8057c0669584c690.avif 1232w&quot; sizes=&quot;(max-width: 768px) 100vw, 1232px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-TaggingTarget.9996f92c910accce.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-TaggingTarget.5627c0525290c217.webp 1232w&quot; sizes=&quot;(max-width: 768px) 100vw, 1232px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-TaggingTarget.5627c0525290c217.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1232&quot; height=&quot;259&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Secondly, when we &lt;code&gt;ChangeTaggedStatus&lt;&#x2F;code&gt; (and we are actively in game, because we have a controller), lets stop movement, so that we immediately  find a new place to move to (or other bot to run towards)!&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;24-ChangeTaggedStatusStopMovement.f8df0836c051fe54.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;24-ChangeTaggedStatusStopMovement.ad4d85e6650b50d4.avif 767w&quot; sizes=&quot;(max-width: 768px) 100vw, 767px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;24-ChangeTaggedStatusStopMovement.04a9fa2129bfd699.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;24-ChangeTaggedStatusStopMovement.10fd0776da497b43.webp 767w&quot; sizes=&quot;(max-width: 768px) 100vw, 767px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;24-ChangeTaggedStatusStopMovement.10fd0776da497b43.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;231&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;However, if you start running the game, you&#x27;ll notice that your bots are getting tagged very early! This is because our Interaction Component is triggering the overlap event with the target&#x27;s Vision Component! We want our Interaction Component to Ignore anything that&#x27;s not a Pawn (which the Mesh and Capsule components are marked as) - so let&#x27;s set up some Custom Collision:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;25-InteractionWithMesh.8234f8edc0b1c374.avif 408w&quot; sizes=&quot;(max-width: 768px) 100vw, 408px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;25-InteractionWithMesh.9df8844524d45068.webp 408w&quot; sizes=&quot;(max-width: 768px) 100vw, 408px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;25-InteractionWithMesh.9df8844524d45068.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;408&quot; height=&quot;328&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;At this point, depending on how your waypoints are set up, you might get the &quot;it&quot; bot running blindly past a potential target on its way to its waypoint. There are a few ways you can fix this, but I&#x27;ll leave this as an excercise for the reader. Here are some ideas to get you started:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;On Controller Tick (but make Tick Interval 0.5s or higher):
&lt;ul&gt;
&lt;li&gt;If we are moving towards a waypoint&lt;&#x2F;li&gt;
&lt;li&gt;But &lt;code&gt;Get Visible Enemies&lt;&#x2F;code&gt; returns something&lt;&#x2F;li&gt;
&lt;li&gt;Call &lt;code&gt;Movement Behaviour&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;On Character Vision Volume Begin Overlap
&lt;ul&gt;
&lt;li&gt;If the other actor is an enemy Tag Character&lt;&#x2F;li&gt;
&lt;li&gt;And we &lt;code&gt;Can See Target&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Call an event dispatcher (&lt;code&gt;New Target Seen&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;In the Controller:
&lt;ul&gt;
&lt;li&gt;Call &lt;code&gt;Movement Behaviour&lt;&#x2F;code&gt; when the Character&#x27;s &lt;code&gt;New Target Seen&lt;&#x2F;code&gt; event fires&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Both of these methods should improve behaviour for both &quot;it&quot; and &quot;not-it&quot; bots! However, they both have pros and cons - the first might be a little bit more responsive (depending on how you tune the Tick Interval) - but it means all of our bots use the Tick event, which is slow. We&#x27;ll eventually replace this with the built-in &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;BlueprintAPI&#x2F;AI&#x2F;Perception&#x2F;index.html&quot;&gt;Unreal Engine Perception System&lt;&#x2F;a&gt; and get the best of both worlds!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;tagging-behaviour&quot; &gt;
  &lt;summary&gt;C++: Tagging Behaviour&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Firstly, when the Character can touch something (Interaction Component Begin Overlap), and that thing is another player (TagCharacter), and we are &quot;it&quot;, and they are &quot;not-it&quot; - we will &quot;tag&quot; them!
We&#x27;ll have to define a function, as well as add it as a delegate (just like we did with the Controller before):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; TagCharacter.h - ATagCharacter
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;BlueprintCallable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnInteractionEnter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt; UPrimitiveComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OverlappedComp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt; AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt; UPrimitiveComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherComp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; int32 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherBodyIndex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;bFromSweep&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FHitResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;SweepResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; TagCharacter.cpp - ATagCharacter::ATagCharacter()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; InteractionVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;OnComponentBeginOverlap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AddUniqueDynamic&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;OnInteractionEnter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then we&#x27;ll implement the function as:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnInteractionEnter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;UPrimitiveComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OverlappedComp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; AActor &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; UPrimitiveComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherComp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; int32 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherBodyIndex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;bFromSweep&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FHitResult &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;SweepResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; Target &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Cast&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ATagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OtherActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Tagged &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp;&amp;amp; !&lt;&#x2F;span&gt;&lt;span&gt;Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Tagged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;   Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ChangeTaggedStatus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Secondly, when we &lt;code&gt;ChangeTaggedStatus&lt;&#x2F;code&gt; (and we are actively in game, because we have a controller), lets stop movement, so that we immediately  find a new place to move to (or other bot to run towards)!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; TagCharacter.cpp - ATagCharacter::ChangeTaggedStatus()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; If we&amp;#39;re in-game, stop moving!
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; AIController &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Cast&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;AAIController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;());
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;AIController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  AIController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;StopMovement&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We cast to &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;AAIController&#x2F;&quot;&gt;&lt;code&gt;AAIController&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; instead of &lt;code&gt;ATagController&lt;&#x2F;code&gt; to avoid &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Circular_dependency&quot;&gt;circular references&lt;&#x2F;a&gt; and having to &lt;a href=&quot;https:&#x2F;&#x2F;wiki.unrealengine.com&#x2F;Forward_Declarations&quot;&gt;forward declare&lt;&#x2F;a&gt; anything.
This means that, unlike the blueprint version where new behaviour is immediate, C++ TagCharacters will pause when touched. You can likely figure out a way to work around this if it bothers you! ;)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;However, if you start running the game, you&#x27;ll notice that your bots are getting tagged very early! This is because our Interaction Component is triggering the overlap event with the target&#x27;s Vision Component! We want our Interaction Component to Ignore anything that&#x27;s not a Pawn (which the Mesh and Capsule components are marked as) - so let&#x27;s set up some Custom Collision:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; TagCharacter.cpp - ATagCharacter::ATagCharacter()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; FCollisionResponseContainer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;InteractionResponse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; ECR_Ignore &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span&gt; InteractionResponse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetResponse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ECC_Pawn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ECR_Overlap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; InteractionVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetCollisionResponseToChannels&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;InteractionResponse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At this point, depending on how your waypoints are set up, you might get the &quot;it&quot; bot running blindly past a potential target on its way to its waypoint. There are a few ways you can fix this, but I&#x27;ll leave this as an excercise for the reader. Here are some ideas to get you started:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;On Controller Tick (but make Tick Interval 0.5s or higher):
&lt;ul&gt;
&lt;li&gt;If we are moving towards a waypoint&lt;&#x2F;li&gt;
&lt;li&gt;But &lt;code&gt;GetVisibleEnemies&lt;&#x2F;code&gt; returns something&lt;&#x2F;li&gt;
&lt;li&gt;Call &lt;code&gt;MovementBehaviour&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;On Character Vision Volume Begin Overlap
&lt;ul&gt;
&lt;li&gt;If the other actor is an enemy Tag Character&lt;&#x2F;li&gt;
&lt;li&gt;And we &lt;code&gt;CanSeeTarget&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Call a &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Programming&#x2F;UnrealArchitecture&#x2F;Delegates&#x2F;&quot;&gt;delegate&lt;&#x2F;a&gt; (&lt;code&gt;NewTargetSeen&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;In the Controller:
&lt;ul&gt;
&lt;li&gt;Call &lt;code&gt;MovementBehaviour&lt;&#x2F;code&gt; when the Character&#x27;s &lt;code&gt;NewTargetSeen&lt;&#x2F;code&gt; event fires&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Both of these methods should improve behaviour for both &quot;it&quot; and &quot;not-it&quot; bots! However, they both have pros and cons - the first might be a little bit more responsive (depending on how you tune the Tick Interval) - but it means all of our bots use the Tick event, which is slow. We&#x27;ll eventually replace this with the built-in &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;BlueprintAPI&#x2F;AI&#x2F;Perception&#x2F;index.html&quot;&gt;Unreal Engine Perception System&lt;&#x2F;a&gt; and get the best of both worlds!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;navigation-filtering-and-avoidance&quot;&gt;Navigation: Filtering and Avoidance&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, lets give our &quot;not-it&quot; bots a chance to get away.&lt;&#x2F;p&gt;
&lt;p&gt;Lets filter our Navigation so that &quot;it&quot; bots don&#x27;t try to use Launchpads to move, and lets make the AI try and adjust its movement so even if it&#x27;s running straight past someone, it triesto avoid touching it.&lt;&#x2F;p&gt;
&lt;details  name=&quot;filtering&quot; &gt;
  &lt;summary&gt;Blueprint: Filtering&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;We&#x27;re no longer going to be able to use the &lt;code&gt;Simple Move to Actor&lt;&#x2F;code&gt; node - our move is just not that simple anymore.
However, it&#x27;s got a nice and predictable behaviour, there&#x27;s only one predicate - if our Character is &lt;code&gt;Tagged&lt;&#x2F;code&gt; or not!&lt;&#x2F;p&gt;
&lt;p&gt;What we&#x27;re going to do is make a new function which &lt;em&gt;pretends&lt;&#x2F;em&gt; to be a &lt;code&gt;Simple Move to Actor&lt;&#x2F;code&gt;, and handles the additional logic!
To do so, find one of our calls to &lt;code&gt;Simple Move to Actor&lt;&#x2F;code&gt;, select that node and the preceding &lt;code&gt;self&lt;&#x2F;code&gt; node, right click and collapse to function:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;26-CollapseToFunction.60abc7ecc8aff251.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;26-CollapseToFunction.9d732fe855c10339.avif 727w&quot; sizes=&quot;(max-width: 768px) 100vw, 727px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;26-CollapseToFunction.358dbe68ee4c0a5c.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;26-CollapseToFunction.3a4901bd5b961d63.webp 727w&quot; sizes=&quot;(max-width: 768px) 100vw, 727px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;26-CollapseToFunction.3a4901bd5b961d63.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;727&quot; height=&quot;495&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Name our new function &lt;code&gt;Move to Actor&lt;&#x2F;code&gt;, and then go ahead and find and replace all current calls of &lt;code&gt;Simple Move to Actor&lt;&#x2F;code&gt; with our new function.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: You can use &lt;code&gt;ctrl&lt;&#x2F;code&gt; + &lt;code&gt;F&lt;&#x2F;code&gt; to find nodes, just like a text editor.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Inside the function, we now want to replace &lt;code&gt;*Simple* Move to Actor&lt;&#x2F;code&gt; with &lt;code&gt;Move To Actor&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;27-MoveToActor.665d07eb3bd0ad19.avif 594w&quot; sizes=&quot;(max-width: 768px) 100vw, 594px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;27-MoveToActor.4fa821e497a5ca10.webp 594w&quot; sizes=&quot;(max-width: 768px) 100vw, 594px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;27-MoveToActor.4fa821e497a5ca10.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;372&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Finally, lets add a Navigation Filter if we&#x27;re tagged, and use the default Navigation Filter if we&#x27;re not.
We&#x27;re going to do three things to set this up:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new &lt;code&gt;NavArea_Launchpad&lt;&#x2F;code&gt;, the same way we created &lt;code&gt;NavArea_Stairs&lt;&#x2F;code&gt; in &lt;a href=&quot;&#x2F;blog&#x2F;nav-modifiers-links&quot;&gt;the last post&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Create a new &lt;code&gt;NavFilter_Tagged&lt;&#x2F;code&gt;, which is a new blueprint based on the &lt;code&gt;NavigationQueryFilter&lt;&#x2F;code&gt; class, and add the Area as excluded:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
 &lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;28-NavigationFilter.3dcefde21b1b1bc3.avif 455w&quot; sizes=&quot;(max-width: 768px) 100vw, 455px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;28-NavigationFilter.baabf92f5bf57fbb.webp 455w&quot; sizes=&quot;(max-width: 768px) 100vw, 455px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;28-NavigationFilter.baabf92f5bf57fbb.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;455&quot; height=&quot;221&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;ol&gt;
&lt;li&gt;In our &lt;code&gt;Move To Actor&lt;&#x2F;code&gt; function, &lt;code&gt;Select&lt;&#x2F;code&gt; which Navigation Filter we&#x27;re going to use, based on our Character&#x27;s &lt;code&gt;Tagged&lt;&#x2F;code&gt; state:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
 &lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;29-FilteredMoveTo.6b338861fdab891b.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;29-FilteredMoveTo.559a646e7b731c6f.avif 993w&quot; sizes=&quot;(max-width: 768px) 100vw, 993px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;29-FilteredMoveTo.d6383bad4d098b58.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;29-FilteredMoveTo.a1bebfa797502d89.webp 993w&quot; sizes=&quot;(max-width: 768px) 100vw, 993px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;29-FilteredMoveTo.a1bebfa797502d89.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;993&quot; height=&quot;450&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;And that&#x27;s it! Now our &quot;not-it&quot; AI has a chance to get away!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The &quot;it&quot; AI can still use the launchpads if they &lt;em&gt;accidentally&lt;&#x2F;em&gt; run over them - feel free to fix that if it bothers you!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;avoidance&quot; &gt;
  &lt;summary&gt;Blueprint: Avoidance&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Setting up avoidance in our Character is quite easy, to get the basics right. Simply making every agent avoid every other one just requires turning on &lt;code&gt;RVOAvoidance&lt;&#x2F;code&gt; on the &lt;code&gt;Character Movement Component&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ensure that the Avoidance Weight is set to 0.5 as well - a good default value which you can tweak as desired.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;30-RVO.104a685c97ce05e9.avif 603w&quot; sizes=&quot;(max-width: 768px) 100vw, 603px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;30-RVO.c79968d636751479.webp 603w&quot; sizes=&quot;(max-width: 768px) 100vw, 603px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;30-RVO.c79968d636751479.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;281&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;If you wanted to tweak this so that It and Not-it have different behaviour, you&#x27;ll want to set up this information using the Avoidance Groups.
For example, set It bots to Avoidance Group 1, and have them Ignore Group 0 for avoidance purposes. For Not-it bots, leave them as Avoidance Group 0, and let them avoid both Group 0 and 1.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: Unreal offers a slightly more advanced form of Avoidance called Detour Crowd Avoidance, which is slightly &quot;better&quot; than RVO - but we&#x27;ll implement it another time!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;filtering&quot; &gt;
  &lt;summary&gt;C++: Filtering&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Generally, when we call &lt;code&gt;MoveToActor&lt;&#x2F;code&gt;, we&#x27;re just passing in the first argument.
However, we now want to start passing in a Filter as well, and (for now), keep the rest of the arguments default.
To do this simply, we&#x27;re going to wrap the &lt;code&gt;AAIController&lt;&#x2F;code&gt;&#x27;s method with our own:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; TagController.h
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;private&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TaggedMove&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Goal&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The implementation is going to be incredibly simple:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; TagController.cpp
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TaggedMove&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;AActor &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Goal&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; TSubclassOf&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;UNavigationQueryFilter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; FilterClass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;nullptr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;TagCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Tagged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  FilterClass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UNavFilter_Tagged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;StaticClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;MoveToActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Goal&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; FilterClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Go back and replace all calls of &lt;code&gt;MoveToActor&lt;&#x2F;code&gt; with our new &lt;code&gt;TaggedMove&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As you can see, we&#x27;re going to need a new class - &lt;code&gt;UNavFilter_Tagged&lt;&#x2F;code&gt;. Create this as a child of &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;Navigation&#x2F;UNavFilter_AIControllerDefault&#x2F;&quot;&gt;&lt;code&gt;UNavFilter_AIControllerDefault&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Add a default constructor for our &lt;code&gt;UNavFilter_Tagged&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; NavFilter_Tagged.cpp
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;UNavFilter_Tagged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UNavFilter_Tagged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; FNavigationFilterArea &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FilterArea &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{ };
&lt;&#x2F;span&gt;&lt;span&gt; FilterArea&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;AreaClass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UNavArea_Launchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;StaticClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; FilterArea&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;bIsExcluded &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; Areas&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Add&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;FilterArea&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;re also going to have a create a &lt;code&gt;UNavArea_Launchpad&lt;&#x2F;code&gt;, from &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;AI&#x2F;Navigation&#x2F;NavAreas&#x2F;UNavArea&#x2F;&quot;&gt;&lt;code&gt;UNavArea&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; - but we won&#x27;t need to add any code to it.
We will, however, adjust our &lt;code&gt;ALaunchpad&lt;&#x2F;code&gt; class to use it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Launchpad.cpp - ALaunchpad::UpdateNavLinks()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; Link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetAreaClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UNavArea_Launchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;StaticClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;());
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And that&#x27;s it! Now our &quot;not-it&quot; AI has a chance to get away!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The &quot;it&quot; AI can still use the launchpads if they &lt;em&gt;accidentally&lt;&#x2F;em&gt; run over them - feel free to fix that if it bothers you!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;avoidance&quot; &gt;
  &lt;summary&gt;C++: Avoidance&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Setting up avoidance in our Character is quite easy, to get the basics right. Simply making every agent avoid every other one just requires setting the &lt;code&gt;UCharacterMovementComponent::bUseRVOAvoidance&lt;&#x2F;code&gt; flag to true!.
You can do this in either blueprint or C++.&lt;&#x2F;p&gt;
&lt;p&gt;Ensure that the &lt;code&gt;AvoidanceWeight&lt;&#x2F;code&gt; is set to 0.5 as well - a good default value which you can tweak as desired.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: Unreal offers a slightly more advanced form of Avoidance called Detour Crowd Avoidance, which is slightly &quot;better&quot; than RVO - but we&#x27;ll implement it another time!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;&#x2F;h2&gt;
&lt;p&gt;Now you know how to set up Navigation Filtering, how to get your AI to avoid each other, and hopefully have learned a little bit about best practices in UE4!&lt;&#x2F;p&gt;
&lt;figure&gt;


&lt;img src=&quot;31-CompletelyWorking-b.gif&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1201&quot; height=&quot;642&quot; &#x2F;&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;Everything might be feeling a little complicated now - especially in Blueprints - but we start using more powerful tools to help us from here.&lt;&#x2F;p&gt;
&lt;p&gt;For the next post (which is hopefully a little shorter), we&#x27;re going to start looking at the in-built UE4 advanced AI system - Behaviour Trees!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: You can visualise what your AI is doing by using the AI Debug view. To enable this in the editor, look under &lt;em&gt;Show -&amp;gt; Developer -&amp;gt; AI Debug&lt;&#x2F;em&gt;. Clicking on one of your AI controlled bots will then show what path they&#x27;re taking, as well as hordes of other useful info to help you debug!&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;32-AIDebug.08e6681423227da1.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;32-AIDebug.c30841e4e52522cb.avif 1210w&quot; sizes=&quot;(max-width: 768px) 100vw, 1210px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;32-AIDebug.e2ca4a913cc47aa4.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;32-AIDebug.7adffb407a7d8976.webp 1210w&quot; sizes=&quot;(max-width: 768px) 100vw, 1210px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;32-AIDebug.7adffb407a7d8976.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1210&quot; height=&quot;654&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I&#x27;d love to hear whether you&#x27;re following along using blueprints or C++ (or both)! &lt;a href=&quot;mailto:contact@vikram.codes&quot;&gt;Email me&lt;&#x2F;a&gt; and let me know!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can subscribe to this blog using any old RSS reader, if it tickles your fancy to follow along! Just plug &lt;a href=&quot;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&quot;&gt;&lt;code&gt;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; into your RSS reader of choice and it should work.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</description>
      </item>
      <item>
          <title>Navigation Modifiers and Links in UE4</title>
          <pubDate>Sun, 05 Nov 2017 00:00:00 +0000</pubDate>
          <author>Vikram Saran</author>
          <link>https://vikram.codes/blog/nav-modifiers-links/</link>
          <guid>https://vikram.codes/blog/nav-modifiers-links/</guid>
          <description xml:base="https://vikram.codes/blog/nav-modifiers-links/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;p&gt;Continuing on from &lt;a href=&quot;&#x2F;blog&#x2F;basic-navigation&quot;&gt;navigation basics we covered in the last post&lt;&#x2F;a&gt;, we&#x27;re going to have a look at how we can extend the NavMesh to do all sorts of cool things.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you haven&#x27;t followed along, or just want to make sure you&#x27;re on the same page, you can download &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;vikram-codes&#x2F;ai-tag&#x2F;-&#x2F;archive&#x2F;basic-navigation&#x2F;ai-tag-basic-navigation.zip&quot;&gt;what we&#x27;ve done so far&lt;&#x2F;a&gt; from the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;vikram-codes&#x2F;ai-tag&quot;&gt;GitLab repository&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The topics that we&#x27;re going to cover include:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Nav Mesh Links&lt;&#x2F;li&gt;
&lt;li&gt;Nav Mesh Areas and Nav Mesh Modifiers&lt;&#x2F;li&gt;
&lt;li&gt;Navigation Filters&lt;&#x2F;li&gt;
&lt;li&gt;Using Navigation for AI that have different sizes&lt;&#x2F;li&gt;
&lt;li&gt;Runtime Nav Mesh Generation&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;As you can tell, there&#x27;s quite a bit to cover!
To make it easier to follow, I&#x27;ll be splitting this up into two articles, with this one covering Navigation Links, Areas, and Modifiers.&lt;&#x2F;p&gt;
&lt;p&gt;Specifically, we&#x27;re going to add:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a preference for the AI to avoid the stairs&lt;&#x2F;li&gt;
&lt;li&gt;the ability for the AI to jump off the raised platform&lt;&#x2F;li&gt;
&lt;li&gt;a launchpad that launches a bot onto the platform&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Unreal Engine 4.18 was released while this blog post was being written. This post will continue to use 4.17, although the codebase may be updated for the next post in the series (and a download link will be provided).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;nav-areas&quot;&gt;Nav Areas&lt;&#x2F;h2&gt;
&lt;p&gt;Before we start making anything, we&#x27;ll begin by discussing the idea of navigation areas.&lt;&#x2F;p&gt;
&lt;p&gt;By default, all areas of a NavMesh are entirely equal. The AI only ever considers distance when determining what path it wants to take. We call this the &quot;cost function&quot;. If we wanted the AI to avoid certain areas, we would need to make it &quot;cost&quot; something to enter or to traverse the area. We could make it &quot;heavier&quot; or &quot;lighter&quot; than the rest of the map to create preferences.&lt;&#x2F;p&gt;
&lt;p&gt;Unreal Engine uses the concept of a Nav Area for this. We can create our own areas the same way we create any class (in either Blueprints or C++):&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;01-NavArea.fd50a66541847385.avif 542w&quot; sizes=&quot;(max-width: 768px) 100vw, 542px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;01-NavArea.808970c48be14fb8.webp 542w&quot; sizes=&quot;(max-width: 768px) 100vw, 542px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;01-NavArea.808970c48be14fb8.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;542&quot; height=&quot;644&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;Once we&#x27;ve created an area, we can tweak a few simple settings (C++ can do a lot more, but we&#x27;ll cover that later).&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavAreaSettings.56f47996455721e6.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavAreaSettings.8438a8bcef72124d.avif 680w&quot; sizes=&quot;(max-width: 768px) 100vw, 680px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavAreaSettings.75ce8133c49782e6.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavAreaSettings.19440b14ea10cf0d.webp 680w&quot; sizes=&quot;(max-width: 768px) 100vw, 680px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavAreaSettings.19440b14ea10cf0d.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;397&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;&lt;code&gt;Default Cost&lt;&#x2F;code&gt; is used to make walking through an area be more or less attractive. The default area cost is 1.0. This is multiplied by the distance that the AI would have to walk through the area, so higher numbers cause the AI to avoid that zone.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Fixed Area Entering Cost&lt;&#x2F;code&gt; can be used for things like the &quot;oil spills&quot; in Divinity: Original Sin, that apply a status effect when you first enter the zone.&lt;&#x2F;p&gt;
&lt;p&gt;Also, we can edit which colour is used to represent the Nav Area. The Default Nav Area class uses a light green, and visualising where the other areas are can help our level designers.&lt;&#x2F;p&gt;
&lt;p&gt;Lets create a &lt;code&gt;NavArea_Stairs&lt;&#x2F;code&gt; and set the &lt;code&gt;Default Cost&lt;&#x2F;code&gt; to 1.2, and the colour to a yellow&#x2F;orange. We&#x27;ll apply this in a second using...&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: if you want to do this in C++, I&#x27;m sure you can figure it out! Just subclass the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;AI&#x2F;Navigation&#x2F;NavAreas&#x2F;UNavArea&#x2F;index.html&quot;&gt;&lt;code&gt;UNavArea&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; class and set the values you&#x27;d like in the constructor.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;nav-modifiers&quot;&gt;Nav Modifiers&lt;&#x2F;h2&gt;
&lt;p&gt;Nav Areas can be applied in a few different ways. The main method for marking a space in your game as a specific Nav Area is the &lt;code&gt;Nav Modifier Volume&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;03-NavModifier.2990e9a041758463.avif 617w&quot; sizes=&quot;(max-width: 768px) 100vw, 617px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;03-NavModifier.330891e5e6742a0a.webp 617w&quot; sizes=&quot;(max-width: 768px) 100vw, 617px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;03-NavModifier.330891e5e6742a0a.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;462&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;When you first drag one onto the stairs, you&#x27;ll notice that it actually &lt;em&gt;deletes&lt;&#x2F;em&gt; the NavMesh!&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-NavModifierDefault.799b5a64eac2156b.avif 418w&quot; sizes=&quot;(max-width: 768px) 100vw, 418px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-NavModifierDefault.095ec64d3a4ad33b.webp 418w&quot; sizes=&quot;(max-width: 768px) 100vw, 418px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-NavModifierDefault.095ec64d3a4ad33b.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;418&quot; height=&quot;338&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;This is because the &lt;code&gt;Area Class&lt;&#x2F;code&gt; is set to &lt;code&gt;NavArea_Null&lt;&#x2F;code&gt;. Lets change it to our Nav Area that we created:&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-NavModifierStairs.3280ea5a9d99fbe2.avif 385w&quot; sizes=&quot;(max-width: 768px) 100vw, 385px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-NavModifierStairs.8f6a882361475475.webp 385w&quot; sizes=&quot;(max-width: 768px) 100vw, 385px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-NavModifierStairs.8f6a882361475475.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;385&quot; height=&quot;423&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;The moment we do that, however, the stairs go back to green! This is because &lt;a href=&quot;https:&#x2F;&#x2F;issues.unrealengine.com&#x2F;issue&#x2F;UE-30174&quot;&gt;there is currently a bug in UE4 regarding custom Nav Areas&lt;&#x2F;a&gt;. There is a simple fix for now, just restart the editor and move your nav area slightly to force a rebuild! You should then see your stairs highlighted in your chosen colour - showing that the area has been applied properly.&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;06-NavAreaApplied.26cdd30f55adeb41.avif 367w&quot; sizes=&quot;(max-width: 768px) 100vw, 367px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;06-NavAreaApplied.0ab47528358dc2ba.webp 367w&quot; sizes=&quot;(max-width: 768px) 100vw, 367px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;06-NavAreaApplied.0ab47528358dc2ba.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;367&quot; height=&quot;355&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;nav-link-basics&quot;&gt;Nav Link Basics&lt;&#x2F;h2&gt;
&lt;p&gt;Our next challenge is to add the ability for the AI to &quot;drop&quot; from one part of the NavMesh to another part, without having to go around.&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-NavLinkScenario.7a69a3b7d00823ae.avif 403w&quot; sizes=&quot;(max-width: 768px) 100vw, 403px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-NavLinkScenario.d3b9d472c0b85082.webp 403w&quot; sizes=&quot;(max-width: 768px) 100vw, 403px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-NavLinkScenario.d3b9d472c0b85082.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;403&quot; height=&quot;379&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;Basically, we want to create a &#x27;link&#x27; from one predetermined point on the mesh to another, which the underlying algorithm can&#x27;t find by itself. Fortunately, Unreal Engine provides us with the incredibly useful &lt;code&gt;Nav Link Proxy Actor&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-NavLinkProxy.bbbf4b517408bd86.avif 367w&quot; sizes=&quot;(max-width: 768px) 100vw, 367px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-NavLinkProxy.f50c2c155b1ff198.webp 367w&quot; sizes=&quot;(max-width: 768px) 100vw, 367px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-NavLinkProxy.f50c2c155b1ff198.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;367&quot; height=&quot;206&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;When you drag one into the scene, you&#x27;ll see this:&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-NavLinkLook.c21348067b3b81f3.avif 379w&quot; sizes=&quot;(max-width: 768px) 100vw, 379px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-NavLinkLook.c4f3b654300097a8.webp 379w&quot; sizes=&quot;(max-width: 768px) 100vw, 379px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-NavLinkLook.c4f3b654300097a8.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;379&quot; height=&quot;244&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;And it&#x27;s details panel looks like this:&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-NavLinkDetails.eeea77d9806cb992.avif 378w&quot; sizes=&quot;(max-width: 768px) 100vw, 378px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-NavLinkDetails.bd0294a9dcbc5e45.webp 378w&quot; sizes=&quot;(max-width: 768px) 100vw, 378px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-NavLinkDetails.bd0294a9dcbc5e45.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;378&quot; height=&quot;579&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;This actor has some quirks, however, so lets make sure we&#x27;re on the same page. Mainly, a Nav Link Proxy has &lt;strong&gt;two&lt;&#x2F;strong&gt; types of links that we can create: Simple links and Smart Links.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;all-links&quot;&gt;All links&lt;&#x2F;h3&gt;
&lt;p&gt;Both Simple and Smart Links have the following in common:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They have a &quot;left&quot; and a &quot;right&quot;&lt;&#x2F;li&gt;
&lt;li&gt;They have a direction:
&lt;ul&gt;
&lt;li&gt;Left to Right&lt;&#x2F;li&gt;
&lt;li&gt;Right to Left&lt;&#x2F;li&gt;
&lt;li&gt;or Both ways&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;They can be assigned a Nav Area&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;simple-links&quot;&gt;Simple Links&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Exist in an Array (called &lt;code&gt;Point Links&lt;&#x2F;code&gt;)
&lt;ul&gt;
&lt;li&gt;i.e. you can have multiple Simple Links per Nav Link Proxy&lt;&#x2F;li&gt;
&lt;li&gt;Each Nav Link Proxy actor comes with one element in the array by default&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Visible in the Scene
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Left&lt;&#x2F;code&gt; and &lt;code&gt;Right&lt;&#x2F;code&gt; visible elements are from the default Simple Link in the Actor&lt;&#x2F;li&gt;
&lt;li&gt;This makes Simple Links easy to move around and place in the scene&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;smart-links&quot;&gt;Smart Links&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Disabled by default
&lt;ul&gt;
&lt;li&gt;Turn on by ticking &lt;code&gt;Smart Link Is Relevant&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Can be turned on and off at runtime
&lt;ul&gt;
&lt;li&gt;And will notify nearby actors who want to know!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;No visible editor
&lt;ul&gt;
&lt;li&gt;You have to manually enter the Left&#x2F;Right location via the Details panel&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Only one Smart Link per Nav Link Proxy&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;practical-example&quot;&gt;Practical Example&lt;&#x2F;h3&gt;
&lt;p&gt;Lets make a simple nav link first, and we can play with the advanced options later on. If we just want our bot to be able to jump off the cliff, lets put our Proxy Actor in the middle, and set up a few simple links like so:&lt;&#x2F;p&gt;
&lt;figure&gt;


&lt;img src=&quot;11-NavLinkExample.gif&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;393&quot; &#x2F;&gt;


&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: There are three simple links from one &lt;code&gt;Nav Link Proxy&lt;&#x2F;code&gt;, and each has been set as a &lt;code&gt;Left to Right&lt;&#x2F;code&gt; link.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This easy setup will allow our AI to path off of that ledge - it&#x27;s that simple!&lt;&#x2F;p&gt;
&lt;figure&gt;


&lt;img src=&quot;12-NavLinkUsage.gif&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;393&quot; &#x2F;&gt;


&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;nav-link-advanced-usage&quot;&gt;Nav Link Advanced Usage&lt;&#x2F;h2&gt;
&lt;p&gt;If we want to create a two-way Nav Link (or a Nav Link that doesn&#x27;t just fall down a cliff), we need to have a way of moving our AI from one place to the other. This isn&#x27;t very difficult to do, but can be a bit hard to wrap your head around when starting out.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re going to create a Launchpad actor (similar to the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Engine&#x2F;Blueprints&#x2F;QuickStart&#x2F;&quot;&gt;Blueprint Quick Start&lt;&#x2F;a&gt;), which will launch our AI to a specific point, and work with our Nav Mesh.&lt;&#x2F;p&gt;
&lt;p&gt;There are a few different ways to do this in C++, so for now we&#x27;re just going to use a simple method that mimics what we can do in Blueprints. We&#x27;ll investigate other options in later posts.&lt;&#x2F;p&gt;
&lt;details  name=&quot;launchpad-impl&quot; &gt;
  &lt;summary&gt;Blueprint: Launchpad&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Unfortunately, this isn&#x27;t currently possible to do in Blueprints only (I will be submitting a Pull Request after this blog post goes live, to try and get it working for 4.19)!&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re going to have to create a little C++ helper class. The following steps will get you back into blueprints pretty quickly.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;c-nav-link-component&quot;&gt;C++ Nav Link Component&lt;&#x2F;h3&gt;
&lt;p&gt;Create a C++ Class based on the &lt;code&gt;NavLinkComponent&lt;&#x2F;code&gt;. You&#x27;ll need to tick &quot;Show All Classes&quot;. Call it &lt;code&gt;BlueprintableNavLinkComponent&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-CustomNavLinkComponent.a56aeecd59b8bbd4.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-CustomNavLinkComponent.98116e08d3e13098.avif 946w&quot; sizes=&quot;(max-width: 768px) 100vw, 946px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-CustomNavLinkComponent.3d87cd5c4a3286bd.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-CustomNavLinkComponent.6e0f858cbe39f8d1.webp 946w&quot; sizes=&quot;(max-width: 768px) 100vw, 946px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-CustomNavLinkComponent.6e0f858cbe39f8d1.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;946&quot; height=&quot;570&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Once the class has been created and opened, we&#x27;re going to have to edit &lt;em&gt;two&lt;&#x2F;em&gt; files. One is called &lt;code&gt;BlueprintableNavLinkComponent.h&lt;&#x2F;code&gt; and is known as a &quot;header&quot; file, the other has the &lt;code&gt;.cpp&lt;&#x2F;code&gt; extension as is called a &quot;source&quot; file.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll make two slight additions to the header file.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, find the line that looks like &lt;code&gt;UCLASS()&lt;&#x2F;code&gt;, and add &lt;code&gt;meta = (BlueprintSpawnableComponent)&lt;&#x2F;code&gt; in between the brackets, so it looks like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UCLASS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;meta &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;BlueprintSpawnableComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, add the following two lines of code under the &lt;code&gt;GENERATED_BODY()&lt;&#x2F;code&gt; line:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;BlueprintCallable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetupLink&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;FVector &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Left&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; FVector &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Right&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ENavLinkDirection&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;Type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Direction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This declares that we want to create a function called &lt;code&gt;Setup Link&lt;&#x2F;code&gt; which accepts two Vector input pins and a Link Direction.&lt;&#x2F;p&gt;
&lt;p&gt;In the source file (BlueprintableNavLinkComponent.cpp), add the following code:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;UBlueprintableNavLinkComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetupLink&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;FVector &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Left&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; FVector &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Right&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ENavLinkDirection&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;Type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Direction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Links&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;].&lt;&#x2F;span&gt;&lt;span&gt;Left &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Left&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Links&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;].&lt;&#x2F;span&gt;&lt;span&gt;Right &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Right&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Links&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;].&lt;&#x2F;span&gt;&lt;span&gt;Direction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Direction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This says that when we call the function, get the first link in the Links array (which has an index of &lt;code&gt;0&lt;&#x2F;code&gt;) and assign the values which we pass in to the function.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Remember that the &lt;code&gt;Simple Links&lt;&#x2F;code&gt; array has one value by default, this is the value that we&#x27;re changing now! If you wanted to be able to set values for multiple simple links, you&#x27;d want to change the &lt;code&gt;0&lt;&#x2F;code&gt; using another input parameter - and you&#x27;d have to check that link existed too!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;blueprint-launchpad&quot;&gt;Blueprint Launchpad&lt;&#x2F;h3&gt;
&lt;p&gt;Now we&#x27;ve done that, we can jump back into blueprints and create a class (based on an Actor), which has the following components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Root: Scene Component
&lt;ul&gt;
&lt;li&gt;Static Mesh
&lt;ul&gt;
&lt;li&gt;Collider (I used a sphere)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Billboard&lt;&#x2F;li&gt;
&lt;li&gt;Blueprintable Nav Link&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-LaunchpadComponents.173e0dc210e77262.avif 291w&quot; sizes=&quot;(max-width: 768px) 100vw, 291px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-LaunchpadComponents.c5527d11a764f738.webp 291w&quot; sizes=&quot;(max-width: 768px) 100vw, 291px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;14-LaunchpadComponents.c5527d11a764f738.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;291&quot; height=&quot;243&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Select any mesh that you like (I use a flatish hexagonal cylinder). Then, add a Vector variable called &lt;code&gt;Target&lt;&#x2F;code&gt;, set its default value to &lt;code&gt;(200, 0, 0)&lt;&#x2F;code&gt;, and check both &lt;code&gt;Instance Editable&lt;&#x2F;code&gt; and &lt;code&gt;Show 3D Widget&lt;&#x2F;code&gt; in the details panel.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-LaunchpadVariable.9d0cec347d43da07.avif 478w&quot; sizes=&quot;(max-width: 768px) 100vw, 478px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-LaunchpadVariable.710caf20858d43ad.webp 478w&quot; sizes=&quot;(max-width: 768px) 100vw, 478px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-LaunchpadVariable.710caf20858d43ad.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;419&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now we can start writing some scripts to make this useful!
To make it work with Nav Meshes, all we need to do is call the the function we created in the Construction Script!&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-LaunchpadConstructionScript.acbc0b952e16930a.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-LaunchpadConstructionScript.f65b71fc909543e8.avif 891w&quot; sizes=&quot;(max-width: 768px) 100vw, 891px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-LaunchpadConstructionScript.c6f46cdd9cc02cca.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-LaunchpadConstructionScript.cbdf027f257f6c96.webp 891w&quot; sizes=&quot;(max-width: 768px) 100vw, 891px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-LaunchpadConstructionScript.cbdf027f257f6c96.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;891&quot; height=&quot;266&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now we can drag the actor into our scene, and set it up!
You should notice that when you drag around the &lt;code&gt;Target&lt;&#x2F;code&gt; widget, the Nav Link automatically updates to point towards it!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You can&#x27;t use a Component as a Target, it has to be a plain Vector, because of a &lt;a href=&quot;https:&#x2F;&#x2F;issues.unrealengine.com&#x2F;issue&#x2F;UE-28690&quot;&gt;known issue in the engine&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.3c7033f2d5c6d35b.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.07b7043dc9e49806.avif 751w&quot; sizes=&quot;(max-width: 768px) 100vw, 751px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.dcb3e8e472222889.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.c09cba3d21b9e610.webp 751w&quot; sizes=&quot;(max-width: 768px) 100vw, 751px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.c09cba3d21b9e610.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;751&quot; height=&quot;424&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Now we need to make the AI get launched when they touch our collider. This will require a new function - &lt;code&gt;Calculate Launch Velocity&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This function is pretty simple, if we use Unreal&#x27;s in-built capability to &lt;code&gt;Suggest Projectile Velocity&lt;&#x2F;code&gt;. Specifically, we can use &lt;code&gt;Custom Arc&lt;&#x2F;code&gt; version of the node, which is a bit easier to make work:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-LaunchpadCalculateVelocity.6016b047906d3af5.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-LaunchpadCalculateVelocity.2167676760aa537b.avif 1174w&quot; sizes=&quot;(max-width: 768px) 100vw, 1174px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-LaunchpadCalculateVelocity.81ce59d967b56e5b.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-LaunchpadCalculateVelocity.442c5ff668946569.webp 1174w&quot; sizes=&quot;(max-width: 768px) 100vw, 1174px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-LaunchpadCalculateVelocity.442c5ff668946569.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1174&quot; height=&quot;349&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: You can split &lt;code&gt;Structs&lt;&#x2F;code&gt; (like Vectors) by right clicking on them! This is handy when you just want to utilise one component, like the Z axis value, above.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If you wanted this Launchpad to only launch up to a certain velocity, you could use the base version of this node. However, you&#x27;d probably want to add a Construction Script check that the destination was reachable!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Now we need to utilise this calculation. Unfortunately (See Issue #1,  below), we can&#x27;t use the &lt;code&gt;Launch Velocity&lt;&#x2F;code&gt; node. We have to do the following instead:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-LaunchpadTrigger.9474710e671632fa.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-LaunchpadTrigger.2fc05222c7405bd1.avif 1090w&quot; sizes=&quot;(max-width: 768px) 100vw, 1090px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-LaunchpadTrigger.07ad83388aa7ab08.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-LaunchpadTrigger.b44b0a6bbb9b9e7a.webp 1090w&quot; sizes=&quot;(max-width: 768px) 100vw, 1090px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-LaunchpadTrigger.b44b0a6bbb9b9e7a.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1090&quot; height=&quot;219&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This is the perfect place to use a Custom Movement Mode, but for now we&#x27;re just going to use the &quot;falling&quot; mode, which is perfectly fine to override the way we&#x27;re doing it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This &lt;em&gt;almost&lt;&#x2F;em&gt; works!&lt;&#x2F;p&gt;
&lt;p&gt;If you run it now, you&#x27;ll see your AI launching correctly, but then halfway through the jump they fall straight down.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;blueprint-character&quot;&gt;Blueprint Character&lt;&#x2F;h3&gt;
&lt;p&gt;This &lt;a href=&quot;https:&#x2F;&#x2F;answers.unrealengine.com&#x2F;questions&#x2F;338823&#x2F;ai-move-to-jump-stopping-half-way-through-jump.html&quot;&gt;appears to have something to do with the Controller&lt;&#x2F;a&gt;. The default Controller logic will stop the Character&#x27;s X and Y movement at the apex of a jump (apparently because the &lt;a href=&quot;https:&#x2F;&#x2F;answers.unrealengine.com&#x2F;questions&#x2F;53382&#x2F;is-the-launch-character-node-interrupted-or-overri.html&quot;&gt;default &lt;code&gt;AIController&lt;&#x2F;code&gt; isn&#x27;t designed to deal with external movement&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;To make this work, we need to disable the &lt;code&gt;Path Following Component&lt;&#x2F;code&gt; on our character when we jump, and then re-enable it when we land!&lt;&#x2F;p&gt;
&lt;p&gt;There are two functions already provided for us in the &lt;code&gt;Character&lt;&#x2F;code&gt; class, which we can override in our blueprint (&lt;code&gt;BP_TagCharacter&lt;&#x2F;code&gt;) - &lt;code&gt;On Landed&lt;&#x2F;code&gt; and &lt;code&gt;On Movement Mode Changed&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Override the &lt;code&gt;On Movement Mode Changed&lt;&#x2F;code&gt; function under the function section on the blueprint:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;20-FunctionOverride.e18735250ab276b6.avif 332w&quot; sizes=&quot;(max-width: 768px) 100vw, 332px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;20-FunctionOverride.b92df3699c4fa053.webp 332w&quot; sizes=&quot;(max-width: 768px) 100vw, 332px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;20-FunctionOverride.b92df3699c4fa053.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;332&quot; height=&quot;780&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The first thing to do is to make sure we still call the default logic for this event. Do this by adding a call to the parent function:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;21-ParentCall.a4d162b8cd498e6a.avif 513w&quot; sizes=&quot;(max-width: 768px) 100vw, 513px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;21-ParentCall.8550da2584631c34.webp 513w&quot; sizes=&quot;(max-width: 768px) 100vw, 513px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;21-ParentCall.8550da2584631c34.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;513&quot; height=&quot;637&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Then, if the movement mode changed to &lt;code&gt;Falling&lt;&#x2F;code&gt;, we want to get the &lt;code&gt;AIController&lt;&#x2F;code&gt; (if any) and &lt;code&gt;Deactivate&lt;&#x2F;code&gt; the &lt;code&gt;Path Following Component&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-OnMovementModeChanged.85e058b4210b0ae3.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-OnMovementModeChanged.500272a33f580e3f.avif 1129w&quot; sizes=&quot;(max-width: 768px) 100vw, 1129px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-OnMovementModeChanged.57ed02ad9d91641b.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-OnMovementModeChanged.a4b85ae0f46243c5.webp 1129w&quot; sizes=&quot;(max-width: 768px) 100vw, 1129px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;22-OnMovementModeChanged.a4b85ae0f46243c5.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1129&quot; height=&quot;286&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Our AI will now jump all the way to end of our nav link path! Although once it gets there, it&#x27;s just going to stand still.&lt;&#x2F;p&gt;
&lt;p&gt;To fix this, lets add a similar bit of logic by overriding the &lt;code&gt;On Landed&lt;&#x2F;code&gt; event to re-activate the &lt;code&gt;Path Following Component&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-OnLanded.be3141bf2391bebf.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-OnLanded.896c976ba9d0700c.avif 890w&quot; sizes=&quot;(max-width: 768px) 100vw, 890px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-OnLanded.26eed833b30a2676.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-OnLanded.94e078d9e630ba41.webp 890w&quot; sizes=&quot;(max-width: 768px) 100vw, 890px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;23-OnLanded.94e078d9e630ba41.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;255&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;And that&#x27;s it! We&#x27;ve now got a shortcut for AI to get up on the ledge without having to go up the stairs - and the AI will properly follow our path using physics!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;launchpad-impl&quot; &gt;
  &lt;summary&gt;C++: Launchpad&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Our first C++ method is (nearly) identical to the BP method above. In fact, this is simpler to do in C++ because we don&#x27;t have to create a custom &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;AI&#x2F;Navigation&#x2F;UNavLinkComponent&#x2F;index.html&quot;&gt;&lt;code&gt;UNavLinkComponent&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to be able to edit the properties!&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll create one class (&lt;code&gt;ALaunchpad&lt;&#x2F;code&gt;) and edit our &lt;code&gt;ATagController&lt;&#x2F;code&gt; class a little bit, and that&#x27;s it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;launchpad&quot;&gt;Launchpad&lt;&#x2F;h3&gt;
&lt;p&gt;For &lt;code&gt;ALaunchpad&lt;&#x2F;code&gt;, create a new class derived from &lt;code&gt;AActor&lt;&#x2F;code&gt;, and fill the header out with the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;public&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ALaunchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;VisibleAnywhere&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt; USceneComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; Root&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;VisibleAnywhere&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt; UStaticMeshComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; Launchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;VisibleAnywhere&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt; USphereComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; TriggerVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; This component&amp;#39;s details are automatically set in the Construction Script
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;VisibleAnywhere&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt; UNavLinkComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; NavLink&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;EditAnywhere&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; BlueprintReadWrite&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; meta &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;MakeEditWidget &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &amp;quot;&amp;quot;))
&lt;&#x2F;span&gt;&lt;span&gt;  FVector Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;BlueprintPure&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  FVector &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CalculateLaunchVelocity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;LaunchedActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; When our Trigger Volume overlaps with something, this function gets called
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnTriggerBeginOverlap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;UPrimitiveComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OverlappedComp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Other&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; UPrimitiveComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherComp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; int32 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherBodyIndex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;bFromSweep&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FHitResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;SweepResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;private&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;LaunchCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ACharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Whenever we edit the Actor Transform or any properties via the Details panel, this will trigger.
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnConstruction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FTransform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Transform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;override&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Update the Nav Link Component to link from the Mesh to the Target
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UpdateNavLinks&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This sets up our basic Components (a Root, a Mesh, a Trigger Collider, and our Nav Link), a single public &lt;code&gt;UPROPERTY&lt;&#x2F;code&gt; for our target destination, and a few functions.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The LaunchCharacter function must be a &lt;code&gt;UFUNCTION&lt;&#x2F;code&gt; to work with Binding, which we&#x27;ll use in our constructor.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We use &lt;code&gt;meta = (MakeEditWidget = &quot;&quot;)&lt;&#x2F;code&gt; to make our FVector have a visible 3D Widget in the editor, this makes it nice and easy for our designers to use our class.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: you can set up any kind of Collider component that you like instead of a Sphere. For my particular Launchpad mesh, the Sphere works perfectly.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Don&#x27;t forget to add the following includes:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Components&#x2F;StaticMeshComponent.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Components&#x2F;SphereComponent.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;AI&#x2F;Navigation&#x2F;NavLinkComponent.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can write our source file. At the top, include the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;UObject&#x2F;ConstructorHelpers.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Kismet&#x2F;GameplayStatics.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;GameFramework&#x2F;Character.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;GameFramework&#x2F;CharacterMovementComponent.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;#include &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;AI&#x2F;Navigation&#x2F;NavigationSystem.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For the constructor, we simply set up our components and default values to reasonable defaults (including loading a mesh), and then we need to &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Programming&#x2F;UnrealArchitecture&#x2F;Delegates&#x2F;index.html&quot;&gt;bind a delegate&lt;&#x2F;a&gt;, so that when the &lt;code&gt;TriggerVolume&lt;&#x2F;code&gt; component begins to overlap with another actor, our &lt;code&gt;OnTriggerBeginOverlap&lt;&#x2F;code&gt; function is called.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;ALaunchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ALaunchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; We don&amp;#39;t need our Launchpad to Tick
&lt;&#x2F;span&gt;&lt;span&gt; PrimaryActorTick&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;bCanEverTick &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Create our Components and setup default values
&lt;&#x2F;span&gt;&lt;span&gt; Root &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CreateDefaultSubobject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;USceneComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Root&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt; RootComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Root&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; Launchpad &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CreateDefaultSubobject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UStaticMeshComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Launchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt; Launchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetupAttachment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Root&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; Launchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetRelativeLocation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;FVector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;ZeroVector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Load the Launchpad Mesh from our Content folder
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;static auto&lt;&#x2F;span&gt;&lt;span&gt; LaunchpadMesh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ConstructorHelpers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FObjectFinderOptional&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UStaticMesh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;&#x2F;Game&#x2F;Meshes&#x2F;SM_Launchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;LaunchpadMesh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Succeeded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;())
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  Launchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetStaticMesh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;LaunchpadMesh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Get&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;());
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; TriggerVolume &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CreateDefaultSubobject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;USphereComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Trigger Volume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt; TriggerVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetupAttachment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Launchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; TriggerVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetSphereRadius&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; NavLink &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CreateDefaultSubobject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UNavLinkComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99c794;&quot;&gt;Nav Link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt; NavLink&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetupAttachment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Root&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Set a default target to be slightly outside our mesh
&lt;&#x2F;span&gt;&lt;span&gt; Target &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;f &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Bind so that when the trigger gets touched we get notified
&lt;&#x2F;span&gt;&lt;span&gt; TriggerVolume&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;OnComponentBeginOverlap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AddUniqueDynamic&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ALaunchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;OnTriggerBeginOverlap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If you make a mistake with the Constructor Helper line (&lt;code&gt;ConstructorHelpers::FObjectFinderOptional&amp;lt;UStaticMesh&amp;gt;(TEXT(&quot;&#x2F;Game&#x2F;Meshes&#x2F;SM_Launchpad&quot;));&lt;&#x2F;code&gt;), it may cause your project to crash upon opening. It&#x27;s easy enough to open the code file and comment it out. Just be careful with the line and you should be ok! Consider yourselves warned.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;At this point, create empty functions for all of the signatures we created. We&#x27;re going to quickly get this working nicely with the Nav Links, before moving back to working on the AI side of things.&lt;&#x2F;p&gt;
&lt;p&gt;To get the Nav Link to update every time we edit the Target vector, we can use the &lt;code&gt;OnConstruction&lt;&#x2F;code&gt; function, which works exactly like the Construction Script in blueprints! To make this clean and easy to read, we create a simple function helper &lt;code&gt;UpdateNavLinks&lt;&#x2F;code&gt; as well:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ALaunchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnConstruction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FTransform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Transform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Super&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnConstruction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Transform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UpdateNavLinks&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ALaunchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UpdateNavLinks&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; We should never manually edit this actor&amp;#39;s links.
&lt;&#x2F;span&gt;&lt;span&gt; NavLink&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetRelativeLocation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;FVector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;ZeroVector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Assert that we haven&amp;#39;t added or removed any Simple Links
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;NavLink&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Links&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Num&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;() == &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Setup link properties
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt; Link &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; NavLink&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Links&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;];
&lt;&#x2F;span&gt;&lt;span&gt; Link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Left &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; FVector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;ZeroVector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; Link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Right &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; Link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Direction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; ENavLinkDirection&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;LeftToRight&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Force rebuild of local NavMesh
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; World &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetWorld&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;World&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; NavSystem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; World&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetNavigationSystem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;NavSystem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;   NavSystem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UpdateComponentInNavOctree&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(*&lt;&#x2F;span&gt;&lt;span&gt;NavLink&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now you should be able to place your actor in the world and move around the Target widget, and see the Nav Link updating in the editor!&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.3c7033f2d5c6d35b.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.07b7043dc9e49806.avif 751w&quot; sizes=&quot;(max-width: 768px) 100vw, 751px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.dcb3e8e472222889.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.c09cba3d21b9e610.webp 751w&quot; sizes=&quot;(max-width: 768px) 100vw, 751px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;17-LaunchpadSetup.c09cba3d21b9e610.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;751&quot; height=&quot;424&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Next, we want to have the AI actually get launched! We&#x27;ll need to handle our trigger event at this point.
Our &lt;code&gt;OnTriggerBeginOverlap&lt;&#x2F;code&gt; function is quite simple, it just calls our &lt;code&gt;LaunchCharacter&lt;&#x2F;code&gt; function if the overlapping actor is the right type:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ALaunchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnTriggerBeginOverlap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;UPrimitiveComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OverlappedComp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Other&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; UPrimitiveComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherComp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; int32 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;OtherBodyIndex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;bFromSweep&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FHitResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;SweepResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; Character &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Cast&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ACharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Other&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;LaunchCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: you may run into issues where the Character doesn&#x27;t believe that it sucessfully reached the entry point of the Nav Link.
If that happens, your trigger collider may be too wide!
You can resolve this issue by using a &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Programming&#x2F;UnrealArchitecture&#x2F;Timers&#x2F;&quot;&gt;Timer&lt;&#x2F;a&gt; to wait a short time (0.1s is likely enough) before launching the character.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Our &lt;code&gt;LaunchCharacter&lt;&#x2F;code&gt; function is fairly simple, although not as perfectly simple as we&#x27;d like - because the in-built &lt;code&gt;ACharacter::LaunchCharacter&lt;&#x2F;code&gt; function doesn&#x27;t allow us to set X&#x2F;Y movement when using Root Motion animations (see Issue #1, below)!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ALaunchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;LaunchCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ACharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Set the Velocity and Mode manually
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; MovementComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetCharacterMovement&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; MovementComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Velocity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CalculateLaunchVelocity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; MovementComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetMovementMode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;EMovementMode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;MOVE_Falling&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: the above is the perfect place to use a &lt;a href=&quot;https:&#x2F;&#x2F;forums.unrealengine.com&#x2F;showthread.php?5047-How-to-add-a-movement-type-inherited-from-Walking-Like-Covering&quot;&gt;Custom Movement Mode&lt;&#x2F;a&gt;! We use the in-built &lt;code&gt;Falling&lt;&#x2F;code&gt; movement mode, but you could use a Custom one if you wanted.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Our Launch Velocity calculation is quite simple, thankfully, due to the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;Kismet&#x2F;UGameplayStatics&#x2F;SuggestProjectileVelocity&#x2F;&quot;&gt;&lt;code&gt;SuggestProjectileVelocity&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; function which UE4 provides for our use (specifically the simpler &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;Kismet&#x2F;UGameplayStatics&#x2F;SuggestProjectileVelocity_Custom-&#x2F;index.html&quot;&gt;&lt;code&gt;_CustomArc&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; version):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;FVector ALaunchpad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;CalculateLaunchVelocity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;LaunchedActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Launch from the &amp;quot;feet&amp;quot; of the Character
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; Start &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; LaunchedActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetActorLocation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; Start&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Z &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-=&lt;&#x2F;span&gt;&lt;span&gt; LaunchedActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetSimpleCollisionHalfHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; Launch to the Target in WS
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; End &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetActorTransform&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;TransformPosition&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; FVector Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UGameplayStatics&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SuggestProjectileVelocity_CustomArc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; Start&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; End&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And with all that, we should have a perfectly working launchpad!&lt;&#x2F;p&gt;
&lt;p&gt;But if you try it out... our AI &quot;derps&quot; a little bit! In the middle of the jump, the AI stops moving along the X&#x2F;Y direction, and just falls straight down.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;controller&quot;&gt;Controller&lt;&#x2F;h3&gt;
&lt;p&gt;To fix this, we&#x27;re going to have to tweak our &lt;code&gt;ATagController&lt;&#x2F;code&gt; class. Actually, we&#x27;re going to tweak the &lt;code&gt;ACharacter&lt;&#x2F;code&gt; class and add some additional logic to the &lt;code&gt;Landed&lt;&#x2F;code&gt; and &lt;code&gt;OnMovementModeChanged&lt;&#x2F;code&gt; functions. We could do this in two ways: we could either create a &lt;code&gt;ATagCharacter&lt;&#x2F;code&gt; and just override the two functions, or we can bind the events from our &lt;code&gt;ATagController&lt;&#x2F;code&gt; and create two functions to handle it.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s slightly more complicated to do it via the Controller, but I believe that it&#x27;s the &quot;better&quot; option, so lets do it that way.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is because we&#x27;re going to be accessing the Controller inside our handlers anyway - it&#x27;s Controller logic that we&#x27;re handling, but triggered by the Character events!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Create the following two functions in our &lt;code&gt;ATagController&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;private&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnMovementModeChanged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ACharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;MovedCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; EMovementMode &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;PrevMovementMode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; uint8 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;PreviousCustomMode &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnLanded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FHitResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Hit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At the end of our &lt;code&gt;ATagController::BeginPlay()&lt;&#x2F;code&gt;, we want to bind these to the events in our Character:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; Character &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;MovementModeChangedDelegate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AddUniqueDynamic&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;OnMovementModeChanged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  Character&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;LandedDelegate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;AddUniqueDynamic&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;OnLanded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And finally, we want to fill them out. Whenever the character enters the &lt;code&gt;Falling&lt;&#x2F;code&gt; state, we want to &lt;code&gt;Deactivate&lt;&#x2F;code&gt; the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;Navigation&#x2F;UPathFollowingComponent&#x2F;&quot;&gt;&lt;code&gt;UPathFollowingComponent&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; of the Controller. Whenever we land, we just re-enable it!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnMovementModeChanged&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ACharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;MovedCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; EMovementMode &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;PrevMovementMode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; uint8 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;PreviousCustomMode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5f6364;&quot;&gt;&#x2F;&#x2F; If the new movement mode is Falling
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;MovedCharacter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetCharacterMovement&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;MovementMode &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span&gt; EMovementMode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;MOVE_Falling&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetPathFollowingComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Deactivate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnLanded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FHitResult &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Hit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetPathFollowingComponent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Activate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And now our AI won&#x27;t stop mid-air - and that&#x27;s all we need to do to have a working Launchpad!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: An even more &quot;correct&quot; way to do this, rather than deactivating the PathFollowingComponent, would be to use a custom movement mode, which we will explore in a later post.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;&#x2F;h2&gt;
&lt;p&gt;Now you know how to modify the Nav Mesh to make it as accessible, or inaccessible, as you would like!&lt;&#x2F;p&gt;
&lt;figure&gt;


&lt;img src=&quot;24-CompletelyWorking.gif&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;703&quot; height=&quot;381&quot; &#x2F;&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;For the &lt;a href=&quot;&#x2F;blog&#x2F;basic-tag&quot;&gt;next post&lt;&#x2F;a&gt;, We continue with navigation, making our AI act differently as it reaches different sections of our mesh, and finish the basic &quot;game&quot; logic!&lt;&#x2F;p&gt;
&lt;p&gt;Want to get in touch? &lt;a href=&quot;mailto:contact@vikram.codes&quot;&gt;Email me&lt;&#x2F;a&gt; and let me know!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can subscribe to this blog using any old RSS reader, if it tickles your fancy to follow along! Just plug &lt;a href=&quot;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&quot;&gt;&lt;code&gt;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; into your RSS reader of choice and it should work.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;details  &gt;
  &lt;summary&gt;Issues&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Unfortunately, simply using &lt;code&gt;Launch Character&lt;&#x2F;code&gt; does not work! This &lt;a href=&quot;https:&#x2F;&#x2F;answers.unrealengine.com&#x2F;questions&#x2F;227726&#x2F;addimpulse-and-launchcharacter-fail-on-x-and-y-onl.html&quot;&gt;appears to be&lt;&#x2F;a&gt; because the mesh&#x2F;animation set we are using uses Root Motion.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;To work around this, we &lt;code&gt;Set Velocity&lt;&#x2F;code&gt; on the Movement Component directly, and then &lt;code&gt;Set Movement Mode&lt;&#x2F;code&gt; to Falling.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Even with the above fix, the Character stops moving at the Apex of the jump.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The default Controller logic will stop the Character&#x27;s X and Y movement at the apex of a jump (apparently because the &lt;a href=&quot;https:&#x2F;&#x2F;answers.unrealengine.com&#x2F;questions&#x2F;53382&#x2F;is-the-launch-character-node-interrupted-or-overri.html&quot;&gt;default &lt;code&gt;AIController&lt;&#x2F;code&gt; isn&#x27;t designed to deal with external movement&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Deactivating and reactiving the PathFollowingComponent will fix this issue.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;In future installments, we&#x27;ll investigate writing our own PathFollowingComponent and Movement Mode which will handle this for us.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;It&#x27;s impossible to edit Nav Link component Simple Links in Blueprints - this makes us need the &lt;code&gt;BlueprintImplementableNavLink&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;This is because the &lt;code&gt;FVector FNavigationLink.Right&lt;&#x2F;code&gt; is not marked &lt;code&gt;BlueprintReadWrite&lt;&#x2F;code&gt; in the engine source code.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;If that property was marked as such, we could make the nav link work entirely via construction script, instead of requiring the C++ component class (hook up the Target Relative Location to the added &lt;code&gt;Right&lt;&#x2F;code&gt; input node):&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;xx-bp-navigation-link.439b0e2c4c558337.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;xx-bp-navigation-link.9574f8662e8d7fd4.avif 853w&quot; sizes=&quot;(max-width: 768px) 100vw, 853px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;xx-bp-navigation-link.e468c1b6757e9c60.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;xx-bp-navigation-link.30ec72342ccf1fe4.webp 853w&quot; sizes=&quot;(max-width: 768px) 100vw, 853px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;xx-bp-navigation-link.30ec72342ccf1fe4.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;853&quot; height=&quot;394&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
</description>
      </item>
      <item>
          <title>Basic Navigation in UE4</title>
          <pubDate>Fri, 13 Oct 2017 00:00:00 +0000</pubDate>
          <author>Vikram Saran</author>
          <link>https://vikram.codes/blog/basic-navigation/</link>
          <guid>https://vikram.codes/blog/basic-navigation/</guid>
          <description xml:base="https://vikram.codes/blog/basic-navigation/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;p&gt;There exist a &lt;a href=&quot;https:&#x2F;&#x2F;denisrizov.com&#x2F;2017&#x2F;05&#x2F;08&#x2F;ai-perception-in-unreal-engine-4-how-to-setup&#x2F;&quot;&gt;large&lt;&#x2F;a&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=PgxuaTSkyu4&quot;&gt;number&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=cZqIidnnK5A&quot;&gt;excellent&lt;&#x2F;a&gt;
&lt;a href=&quot;http:&#x2F;&#x2F;orfeasel.com&#x2F;creating-ai-with-perception&#x2F;&quot;&gt;resources&lt;&#x2F;a&gt; for game developers seeking to learn how to
utilise the in-built AI features in Unreal Engine 4.
Unfortunately, a number of these are
&lt;a href=&quot;https:&#x2F;&#x2F;wiki.unrealengine.com&#x2F;Unreal_Engine_AI_Tutorial_-_1_-_Making_AI_Jump_as_a_Part_of_Path_Following&quot;&gt;outdated&lt;&#x2F;a&gt;
or &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Resources&#x2F;ContentExamples&#x2F;NavMesh&#x2F;1_1&#x2F;&quot;&gt;teach only specific elements&lt;&#x2F;a&gt;
or are part of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tomlooman&#x2F;EpicSurvivalGameSeries&quot;&gt;large tutorial series&lt;&#x2F;a&gt;
or &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Engine&#x2F;AI&#x2F;BehaviorTrees&#x2F;QuickStart&#x2F;index.html&quot;&gt;assume knowledge&lt;&#x2F;a&gt;
that you might not have.&lt;&#x2F;p&gt;
&lt;p&gt;This blog series aims to provide context and teach best practices for utilising the AI systems available in
Unreal Engine 4 (version 4.17).
It is aimed at beginning-to-intermediate developers, covering both C++ and Blueprint usage of AI.
Each post will show Blueprint scripts first and then show how to achieve the same results in C++.&lt;&#x2F;p&gt;
&lt;p&gt;It is assumed that you have basic understanding of how to use the engine and editor and have, at least,
followed the official &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Engine&#x2F;Blueprints&#x2F;QuickStart&#x2F;index.html&quot;&gt;Blueprint&lt;&#x2F;a&gt;
or &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Programming&#x2F;QuickStart&#x2F;index.html&quot;&gt;Programming&lt;&#x2F;a&gt; Quick Start Guides.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;goal&quot;&gt;Goal&lt;&#x2F;h2&gt;
&lt;p&gt;Over the next few blog posts, we&#x27;re going to build an AI-only &quot;Game of Tag&quot; demo project.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll do this one step at a time, starting with an (almost) entirely blank project, and then building up the demo with proper usage of the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Gameplay&#x2F;Framework&#x2F;&quot;&gt;Unreal Engine Gameplay Framework&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This first post will look at how we can get our AI to &lt;em&gt;move&lt;&#x2F;em&gt;. We&#x27;ll be using the Unreal Engine Navigation System to do so.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-the-project&quot;&gt;Setting Up the Project&lt;&#x2F;h2&gt;
&lt;p&gt;Download the provided &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;vikram-codes&#x2F;ai-tag&#x2F;-&#x2F;archive&#x2F;template&#x2F;ai-tag-template.zip&quot;&gt;project template&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This minimised project file includes the mannequin (from the UE4 templates) and a basic Character blueprint (&lt;code&gt;BP_TagCharacter&lt;&#x2F;code&gt;) with the Mesh and Animation Blueprint setup (and correctly scaled&#x2F;rotated components), and a rudimentary geometry-based level for testing purposes.&lt;&#x2F;p&gt;
&lt;p&gt;Alternatively, if you are using &lt;em&gt;git&lt;&#x2F;em&gt;, you can access the project from the &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;vikram-codes&#x2F;ai-tag&quot;&gt;GitLab repository&lt;&#x2F;a&gt;. The initial download commit has the &lt;strong&gt;template&lt;&#x2F;strong&gt; tag, and the state of the codebase at the end of any blog post is also tagged with the name of the post (e.g. &lt;strong&gt;basic-navigation&lt;&#x2F;strong&gt;).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-the-nav-mesh&quot;&gt;Setting up the Nav Mesh&lt;&#x2F;h2&gt;
&lt;p&gt;Before we do anything advanced, lets take a small step first - making our AI Characters walk around using the UE4 Navigation System.&lt;&#x2F;p&gt;
&lt;p&gt;The Unreal Engine navigation system utilises a &lt;em&gt;Nav Mesh&lt;&#x2F;em&gt; (Navigation Mesh) to know where the walkable sections of our level are. To tell UE4 to build a nav mesh, let&#x27;s go ahead and add a &lt;em&gt;Nav Mesh Bounds Volume&lt;&#x2F;em&gt; to the level, and resize it so that it fits all of the walkable space in our level!&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;01-Navmesh.55d99abeb593ddbf.avif 381w&quot; sizes=&quot;(max-width: 768px) 100vw, 381px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;01-Navmesh.2b98cc342d6876fd.webp 381w&quot; sizes=&quot;(max-width: 768px) 100vw, 381px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;01-Navmesh.2b98cc342d6876fd.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;381&quot; height=&quot;232&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;figcaption&gt;The Nav Mesh Bounds volume can be found in the placement panel&lt;&#x2F;figcaption&gt;

&lt;&#x2F;figure&gt;
&lt;p&gt;Once you&#x27;ve fit the bounds, press &lt;strong&gt;P&lt;&#x2F;strong&gt; to make the Nav Mesh visible.&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavmeshVisibility.b9b814121ba69a04.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavmeshVisibility.0175c543cd8edd12.avif 1205w&quot; sizes=&quot;(max-width: 768px) 100vw, 1205px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavmeshVisibility.83d6a533e29914b5.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavmeshVisibility.3e2392c66a96cfcc.webp 1205w&quot; sizes=&quot;(max-width: 768px) 100vw, 1205px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;02-NavmeshVisibility.3e2392c66a96cfcc.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1205&quot; height=&quot;651&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;figcaption&gt;The Nav Mesh Bounds volume can be seen with the P key&lt;&#x2F;figcaption&gt;

&lt;&#x2F;figure&gt;
&lt;p&gt;When you add the Volume to the level, you&#x27;ll notice that a &lt;code&gt;RecastNavMesh-Default&lt;&#x2F;code&gt; actor was added as well. This actor has settings we can use to visualise different elements of our NavMesh, and we can also tweak how it&#x27;s generated.&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;03-RecastNavMeshActor.c928a5b94794be7b.avif 379w&quot; sizes=&quot;(max-width: 768px) 100vw, 379px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;03-RecastNavMeshActor.6e40dea3f85408bf.webp 379w&quot; sizes=&quot;(max-width: 768px) 100vw, 379px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;03-RecastNavMeshActor.6e40dea3f85408bf.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;379&quot; height=&quot;166&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;figcaption&gt;The Recast NavMesh actor can be found in the World Outliner&lt;&#x2F;figcaption&gt;

&lt;&#x2F;figure&gt;
&lt;p&gt;We should start by setting up the Agent Radius and Height to be 44.0 and 192.0 to match our Character. This will cause the NavMesh to properly identify all of the walkable areas of our game for our specific Character size.&lt;&#x2F;p&gt;
&lt;p&gt;Once we&#x27;ve done that, we can edit the Cell Size from 19.0 to 5.0 to improve the accuracy of the walkable space available (especially on our stairs):&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Cell Size 19.0&lt;&#x2F;th&gt;&lt;th&gt;Cell Size 5.0&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-NavMeshTileSize.8b66d111db10fcb6.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-NavMeshTileSize.71a04e708c2bfcf4.avif 946w&quot; sizes=&quot;(max-width: 768px) 100vw, 946px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-NavMeshTileSize.9b8fcb5ef38191af.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-NavMeshTileSize.82c304eb1fc7300c.webp 946w&quot; sizes=&quot;(max-width: 768px) 100vw, 946px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;04-NavMeshTileSize.82c304eb1fc7300c.webp&quot; alt=&quot;Cell Size 19.0&quot; loading=&quot;lazy&quot; width=&quot;946&quot; height=&quot;419&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;&#x2F;td&gt;&lt;td&gt;&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-NavMeshTileSize.8cc323081a4541f3.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-NavMeshTileSize.df1e59ca54233de1.avif 946w&quot; sizes=&quot;(max-width: 768px) 100vw, 946px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-NavMeshTileSize.76d49019901833a2.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-NavMeshTileSize.0b70b5480f37669e.webp 946w&quot; sizes=&quot;(max-width: 768px) 100vw, 946px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;05-NavMeshTileSize.0b70b5480f37669e.webp&quot; alt=&quot;Cell Size 5.0&quot; loading=&quot;lazy&quot; width=&quot;946&quot; height=&quot;422&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;This does take some more time to generate the NavMesh, so it&#x27;s not perfectly suitable for all games, but for our demo this will be fine.&lt;&#x2F;p&gt;
&lt;p&gt;Note: Test your Nav Mesh now. It may not properly appear (or may disappear when you simulate the game). If this is the case, close and re-open your project and force the mesh to rebuild (by moving it a bit, then shift it back). This is likely due to strange interactions with BSP&#x2F;Geometry based levels. Ensure that your nav mesh appears as in the picture above, and that it is still visible if you Simulate (&lt;code&gt;Alt+S&lt;&#x2F;code&gt;) the game&lt;&#x2F;p&gt;
&lt;h2 id=&quot;moving-around&quot;&gt;Moving Around&lt;&#x2F;h2&gt;
&lt;p&gt;Our next step is to get a single AI Character into the game and moving around. To do this properly, we&#x27;re going to create a custom AI Controller class which will pick a random &quot;waypoint&quot; and move the AI there, and then wait before continuing again.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s first set up our &quot;Waypoints&quot; around the map. To make this quick and easy, drag in a few &lt;code&gt;Target Point&lt;&#x2F;code&gt; actors into various locations around our map:&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;06-TargetPoints.995cd0d454b48f72.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;06-TargetPoints.33e93e2962441686.avif 1218w&quot; sizes=&quot;(max-width: 768px) 100vw, 1218px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;06-TargetPoints.eb426c841a1a0bf3.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;06-TargetPoints.f81f05520f5cd39a.webp 1218w&quot; sizes=&quot;(max-width: 768px) 100vw, 1218px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;06-TargetPoints.f81f05520f5cd39a.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1218&quot; height=&quot;585&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;figcaption&gt;Target Points can be added via the Placement Panel&lt;&#x2F;figcaption&gt;

&lt;&#x2F;figure&gt;
&lt;p&gt;We&#x27;re then going to create a new Class based on the AI Controller class to control our AI. The following instructions are given both in Blueprint and C++, so pick which one you want to work with.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: An AIController is used as the &quot;brain&quot; of a Pawn or Character. Much like a PlayerController, it mainly is concerned with determining what the Character &lt;em&gt;wants&lt;&#x2F;em&gt; to do next, rather than whether it &lt;em&gt;can&lt;&#x2F;em&gt; (i.e. Controllers should contain Input&#x2F;Intelligence, and Pawns should contain Game Logic).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;details  name=&quot;ai-controller-impl&quot; &gt;
  &lt;summary&gt;Blueprint: AI Controller&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Create a new Blueprint Class, and when asked for the Parent Class, go into the &lt;em&gt;All Classes&lt;&#x2F;em&gt; section, select AI Controller, and create our &lt;code&gt;BP_TagController&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-BlueprintParentClass.afa155f4abbda84c.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-BlueprintParentClass.ba622db5649147ae.avif 687w&quot; sizes=&quot;(max-width: 768px) 100vw, 687px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-BlueprintParentClass.b34b27250f90c5b9.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-BlueprintParentClass.daf27047fdb79334.webp 687w&quot; sizes=&quot;(max-width: 768px) 100vw, 687px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;07-BlueprintParentClass.daf27047fdb79334.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;687&quot; height=&quot;605&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The first thing that our Tag Controller should do is find and store a reference to all of the Waypoints in our level. We can do this using the &lt;code&gt;Get All Actors Of Class&lt;&#x2F;code&gt; node:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-StoreWaypoints.06d9f51af9f5b482.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-StoreWaypoints.c94c06b27fab7bee.avif 688w&quot; sizes=&quot;(max-width: 768px) 100vw, 688px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-StoreWaypoints.abb1d5f89ac13279.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-StoreWaypoints.42453986adf19c90.webp 688w&quot; sizes=&quot;(max-width: 768px) 100vw, 688px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;08-StoreWaypoints.42453986adf19c90.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;160&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;We then want to be able to &lt;code&gt;Get Random Waypoint&lt;&#x2F;code&gt;, so lets create a function which does exactly that (Note: I marked this function &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Engine&#x2F;Blueprints&#x2F;UserGuide&#x2F;Functions&#x2F;#purevsstopimpure&quot;&gt;&lt;em&gt;Pure&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; as it does not change any &lt;em&gt;State&lt;&#x2F;em&gt;):&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-GetRandomWaypoint.a0deebe031326391.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-GetRandomWaypoint.e7396adc22c5ec38.avif 1090w&quot; sizes=&quot;(max-width: 768px) 100vw, 1090px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-GetRandomWaypoint.a3da75bd9ac3d9db.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-GetRandomWaypoint.0e8fe8831e84a80e.webp 1090w&quot; sizes=&quot;(max-width: 768px) 100vw, 1090px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;09-GetRandomWaypoint.0e8fe8831e84a80e.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1090&quot; height=&quot;342&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Finally, we&#x27;re going to &lt;code&gt;Go To Random Waypoint&lt;&#x2F;code&gt; a lot, so lets create that function, and call it on Begin Play.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-GoToRandomWaypoint.321937ac90a46395.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-GoToRandomWaypoint.b3242282868416e4.avif 812w&quot; sizes=&quot;(max-width: 768px) 100vw, 812px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-GoToRandomWaypoint.4f2b467ece2bd602.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-GoToRandomWaypoint.b117dba60c70c899.webp 812w&quot; sizes=&quot;(max-width: 768px) 100vw, 812px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;10-GoToRandomWaypoint.b117dba60c70c899.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;812&quot; height=&quot;303&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;But what goes &lt;em&gt;into&lt;&#x2F;em&gt; this function? This is where we start using the UE4 Navigation system.
We actually have &lt;strong&gt;4&lt;&#x2F;strong&gt; options for which node to use, depending on whether we want to move to an &lt;em&gt;Actor&lt;&#x2F;em&gt; or &lt;em&gt;Location&lt;&#x2F;em&gt;, and whether we want to use a &lt;em&gt;simple&lt;&#x2F;em&gt; version of the function or one with &lt;em&gt;all of the details&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;11-NavigationNodes.c48456a4d789e0cf.avif 635w&quot; sizes=&quot;(max-width: 768px) 100vw, 635px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;11-NavigationNodes.75fb2a9020d29534.webp 635w&quot; sizes=&quot;(max-width: 768px) 100vw, 635px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;11-NavigationNodes.75fb2a9020d29534.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;635&quot; height=&quot;667&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;For now, we&#x27;re just going to use the simplest possible variation - &lt;code&gt;Simple Move to Actor&lt;&#x2F;code&gt;, and feed in a random waypoint:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;12-GoToRandomWaypoint.5b9f1f5e89637042.avif 488w&quot; sizes=&quot;(max-width: 768px) 100vw, 488px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;12-GoToRandomWaypoint.5b11af8b6979e60c.webp 488w&quot; sizes=&quot;(max-width: 768px) 100vw, 488px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;12-GoToRandomWaypoint.5b11af8b6979e60c.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;488&quot; height=&quot;308&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;And our basic Controller is all done!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;ai-controller-impl&quot; &gt;
  &lt;summary&gt;C++: AI Controller&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;Create a new C++ class based on the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;AAIController&#x2F;index.html&quot;&gt;&lt;code&gt;AIController&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; class called &lt;code&gt;TagController&lt;&#x2F;code&gt;. You will need to check &lt;em&gt;Show All Classes&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: whenever I mention an Unreal Engine API class or function, I will link it directly to any available online documentation. Go ahead and click on &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;AAIController&#x2F;index.html&quot;&gt;&lt;code&gt;AIController&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to learn about what we&#x27;re overriding.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-CParentClass.3c2f6597f3edcbfd.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-CParentClass.083143c7f50543b0.avif 947w&quot; sizes=&quot;(max-width: 768px) 100vw, 947px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-CParentClass.21248353cf774d3c.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-CParentClass.7fd8757901e1bd53.webp 947w&quot; sizes=&quot;(max-width: 768px) 100vw, 947px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;19-CParentClass.7fd8757901e1bd53.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;947&quot; height=&quot;569&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Our AI is rather simple, all we need is to &lt;code&gt;override&lt;&#x2F;code&gt; the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;GameFramework&#x2F;AActor&#x2F;BeginPlay&#x2F;index.html&quot;&gt;&lt;code&gt;BeginPlay()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; function, and to create the following &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Programming&#x2F;UnrealArchitecture&#x2F;Reference&#x2F;Functions&#x2F;&quot;&gt;&lt;code&gt;UFUNCTION&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&#x27;s and &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;Programming&#x2F;UnrealArchitecture&#x2F;Reference&#x2F;Properties&#x2F;&quot;&gt;&lt;code&gt;UPROPERTY&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Every time you use an Unreal Engine class, you need to &lt;code&gt;#include&lt;&#x2F;code&gt; it, they are not included by default.
Don&#x27;t forget to &lt;code&gt;#include &quot;Engine&#x2F;TargetPoint.h&quot;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;public&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;BeginPlay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;override&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;private&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UPROPERTY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  TArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;AActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;*&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Waypoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  ATargetPoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UFUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GoToRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now lets fill in our functions.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, Begin Play will store all of the Waypoints in the game. To do this, we&#x27;re going to use &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;Kismet&#x2F;UGameplayStatics&#x2F;GetAllActorsOfClass&#x2F;index.html&quot;&gt;&lt;code&gt;UGameplayStatics::GetAllActorsOfClass&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, as per the second answer on &lt;a href=&quot;https:&#x2F;&#x2F;answers.unrealengine.com&#x2F;questions&#x2F;289453&#x2F;get-all-actors-of-class-in-c.html&quot;&gt;this answerhub question&lt;&#x2F;a&gt;.
Then, all we need to do is call &lt;code&gt;GoToRandomWaypoint&lt;&#x2F;code&gt;, which we will fill in shortly.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Again, you&#x27;ll need to include a new header file. This time you want to &lt;code&gt;#include &quot;Kismet&#x2F;GameplayStatics.h&quot;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;BeginPlay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Super&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;BeginPlay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;UGameplayStatics&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetAllActorsOfClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetWorld&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ATargetPoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;StaticClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(),&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; Waypoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GoToRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Don&#x27;t forget to call &lt;code&gt;Super::BeginPlay()&lt;&#x2F;code&gt;
If you don&#x27;t - your character will never move, and you&#x27;ll spend hours trying to figure out what you forgot!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Before we can &lt;code&gt;GoToRandomWaypoint&lt;&#x2F;code&gt;, we need to &lt;code&gt;GetRandomWaypoint&lt;&#x2F;code&gt;, so lets use UE4&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Core&#x2F;Math&#x2F;FMath&#x2F;index.html&quot;&gt;&lt;code&gt;FMath&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; library to do so. We&#x27;re also going to &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Functions&#x2F;Cast&#x2F;index.html&quot;&gt;&lt;code&gt;Cast&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; our return value so that it&#x27;s the correct type:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;ATargetPoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;auto&lt;&#x2F;span&gt;&lt;span&gt; index &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;FMath&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;RandRange&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; Waypoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Num&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;() - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Cast&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;ATargetPoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Waypoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;index&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;]);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, lets fill in the &lt;code&gt;GoToRandomWaypoint&lt;&#x2F;code&gt; function. This has a few options from the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;AAIController&#x2F;index.html&quot;&gt;&lt;code&gt;AIController&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; - &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;AAIController&#x2F;MoveTo&#x2F;index.html&quot;&gt;&lt;code&gt;MoveTo&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;AAIController&#x2F;MoveToActor&#x2F;index.html&quot;&gt;&lt;code&gt;MoveToActor&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;AAIController&#x2F;MoveToLocation&#x2F;index.html&quot;&gt;&lt;code&gt;MoveToLocation&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re going to use the simplest possible option for now - &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;AAIController&#x2F;MoveToActor&#x2F;index.html&quot;&gt;&lt;code&gt;MoveToActor&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, with all of the default arguments:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GoToRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;MoveToActor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;());
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And that&#x27;s it!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;using-the-controller&quot;&gt;Using the Controller&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we&#x27;ve got our &quot;brain&quot; built, it&#x27;s time to fit it into our &quot;body&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;To do so, open up the &lt;code&gt;BP_TagCharacter&lt;&#x2F;code&gt; and look for the Pawn&#x27;s AI Controller Class variable, and set it to your custom controller:&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-AIControllerClass.5d73ff1452462cfe.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-AIControllerClass.e9d8a3ceec1b8e5a.avif 1047w&quot; sizes=&quot;(max-width: 768px) 100vw, 1047px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-AIControllerClass.d565e25276c72444.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-AIControllerClass.3125f793b01fde12.webp 1047w&quot; sizes=&quot;(max-width: 768px) 100vw, 1047px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;13-AIControllerClass.3125f793b01fde12.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;1047&quot; height=&quot;315&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;Compile, save, and drag an instance of the Character into the scene. Run, and you should have an AI that runs to a random waypoint! Make sure to stop and start a few times to test it out fully!&lt;&#x2F;p&gt;
&lt;figure&gt;


&lt;img src=&quot;14-SimpleMovement.gif&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;547&quot; &#x2F;&gt;


&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;adding-further-movement&quot;&gt;Adding further movement&lt;&#x2F;h2&gt;
&lt;p&gt;At this point, we&#x27;ve covered everything required to make our AI move around the map, but our bot is a little stupid right now. Let&#x27;s make it a little bit smarter by making it wait a short while once it reaches its destination, before picking and moving to another random waypoint.&lt;&#x2F;p&gt;
&lt;p&gt;Again, this section is split up for Blueprints and C++.&lt;&#x2F;p&gt;
&lt;details  name=&quot;move-completed-impl&quot; &gt;
  &lt;summary&gt;Blueprint: ReceiveMoveCompleted&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;To do this, we&#x27;re going to need to know &lt;em&gt;when&lt;&#x2F;em&gt; the AI has completed moving. We can do this by binding a custom event to  &lt;code&gt;ReceiveMoveCompleted&lt;&#x2F;code&gt;. We want to bind the event on Begin Play (so that when we finish &lt;em&gt;any&lt;&#x2F;em&gt; movement in the game it is called), so add it like so:&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-ReceiveMoveCompleted.e8a99a8de8d686a9.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-ReceiveMoveCompleted.847289c990653ed5.avif 885w&quot; sizes=&quot;(max-width: 768px) 100vw, 885px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-ReceiveMoveCompleted.c385a085f4862861.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-ReceiveMoveCompleted.aeb6e941f99241c5.webp 885w&quot; sizes=&quot;(max-width: 768px) 100vw, 885px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;15-ReceiveMoveCompleted.aeb6e941f99241c5.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;885&quot; height=&quot;611&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;This custom event will be triggered by UE4 whenever any navigation movement finishes, as long as the &lt;em&gt;bind&lt;&#x2F;em&gt; node ran first.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: To be fully correct, you want to bind the event &lt;em&gt;before&lt;&#x2F;em&gt; calling &lt;code&gt;Go To Random Waypoint&lt;&#x2F;code&gt;. This is wise in case the movement fails for any reason (such as you forgetting to put in Target Point actors), because the &lt;strong&gt;Result&lt;&#x2F;strong&gt; struct will provide you with debugging information.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Once movement finishes, we should wait a short while, and then call &lt;code&gt;Go To Random Waypoint&lt;&#x2F;code&gt; again.&lt;&#x2F;p&gt;
&lt;figure&gt;
&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-FinishedMovement.dfe8b945a1111a80.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-FinishedMovement.7d34cefebf440049.avif 684w&quot; sizes=&quot;(max-width: 768px) 100vw, 684px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-FinishedMovement.360c0bd704cf141f.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-FinishedMovement.0e18985fe2940b6a.webp 684w&quot; sizes=&quot;(max-width: 768px) 100vw, 684px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;16-FinishedMovement.0e18985fe2940b6a.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;180&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;
&lt;&#x2F;figure&gt;&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;details  name=&quot;move-completed-impl&quot; &gt;
  &lt;summary&gt;C++: OnMoveCompleted&lt;&#x2F;summary&gt;
  &lt;div class=&quot;accordion-content&quot;&gt;&lt;p&gt;In C++, doing something immediately when a move is completed is even easier than in Blueprints - thanks to the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;AIModule&#x2F;AAIController&#x2F;OnMoveCompleted&#x2F;1&#x2F;index.html&quot;&gt;&lt;code&gt;AIController::OnMoveCompleted&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; virtual function!&lt;&#x2F;p&gt;
&lt;p&gt;All we need to do is overwrite the function in our header file:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;public&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnMoveCompleted&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;FAIRequestID &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;RequestID&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FPathFollowingResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;override&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And if we didn&#x27;t want to wait a second once we reached our target, the function implementation would be trivial!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnMoveCompleted&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;FAIRequestID &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;RequestID&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FPathFollowingResult &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Super&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnMoveCompleted&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;RequestID&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GoToRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But adding the equivalent of a Delay node for 1 second is a touch tricky - we&#x27;re going to need to use an &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;FTimerManager&#x2F;SetTimer&#x2F;index.html&quot;&gt;&lt;code&gt;FTimerHandle&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Add one to your header file:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;private&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; FTimerHandle TimerHandle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And we can then use the &lt;a href=&quot;https:&#x2F;&#x2F;docs.unrealengine.com&#x2F;latest&#x2F;INT&#x2F;API&#x2F;Runtime&#x2F;Engine&#x2F;FTimerManager&#x2F;SetTimer&#x2F;4&#x2F;index.html&quot;&gt;&lt;code&gt;SetTimer&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; function to call &lt;code&gt;GoToRandomWaypoint&lt;&#x2F;code&gt; after a second, rather than directly calling it ourselves:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b2c2f;color:#cccece;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnMoveCompleted&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;FAIRequestID &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;RequestID&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; FPathFollowingResult &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;Super&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;OnMoveCompleted&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;RequestID&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;GetWorldTimerManager&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;SetTimer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;TimerHandle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ec5f67;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;ATagController&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;GoToRandomWaypoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f99157;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5fb3b3;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The &lt;code&gt;GetWorldTimerManager()&lt;&#x2F;code&gt; function may give you a warning of &quot;incomplete type is not allowed&quot; - but it will compile and work perfectly.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;details&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;&#x2F;h2&gt;
&lt;p&gt;We now have the basics set up to control a Pawn, and move it around the level using AI!&lt;&#x2F;p&gt;
&lt;figure&gt;


&lt;img src=&quot;17-RandomMovement.gif&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;581&quot; &#x2F;&gt;


&lt;&#x2F;figure&gt;
&lt;p&gt;For the next post, I&#x27;m actually undecided which way I want to go! I&#x27;m tossing up between starting on the Behaviour Tree for our AI (making them &quot;think&quot;!), or continuing with navigation, making our AI act differently as it reaches different sections of our mesh, using custom Movement Modes and Path Following Components.&lt;&#x2F;p&gt;
&lt;p&gt;What do you think? &lt;a href=&quot;mailto:contact@vikram.codes&quot;&gt;Email me&lt;&#x2F;a&gt; and let me know!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: You can view lots of information about your NavMesh and whether your AI can get from one point to another by using the Navigation Testing Actor. To use them, drag two into your level, and on one of the instances, select the other instance as the &lt;code&gt;Other Actor&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;



















&lt;picture&gt;
  &lt;source type=&quot;image&#x2F;avif&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-NavigationTestingActor.03ffb5a20dd1eb71.avif 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-NavigationTestingActor.9cb3fcc2fe33a5b7.avif 963w&quot; sizes=&quot;(max-width: 768px) 100vw, 963px&quot;&gt;
  &lt;source type=&quot;image&#x2F;webp&quot; srcset=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-NavigationTestingActor.988e0b5e73cda438.webp 640w, https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-NavigationTestingActor.d19ce7251f39bc0a.webp 963w&quot; sizes=&quot;(max-width: 768px) 100vw, 963px&quot;&gt;
  &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;vikram.codes&amp;#x2F;processed_images&amp;#x2F;18-NavigationTestingActor.d19ce7251f39bc0a.webp&quot; alt=&quot;Image&quot; loading=&quot;lazy&quot; width=&quot;963&quot; height=&quot;357&quot; &#x2F;&gt;
&lt;&#x2F;picture&gt;


&lt;&#x2F;figure&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;You can subscribe to this blog using any old RSS reader, if it tickles your fancy to follow along! Just plug &lt;a href=&quot;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&quot;&gt;&lt;code&gt;https:&#x2F;&#x2F;vikram.codes&#x2F;blog&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; into your RSS reader of choice and it should work.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</description>
      </item>
    </channel>
</rss>
