top of page

Wake Up Devlog Week 2

  • Writer: Tristan Atkinson
    Tristan Atkinson
  • Sep 30, 2024
  • 5 min read

Updated: Mar 11


After building a more solid understanding of my Game last week, I decided to focus on getting some of the basic combat system sorted this week: namely, Basic versions of abilities for the first couple of Player Classes, and a Basic Melee and Ranged Enemy with AI.


Firstly, I did some research on how different Player classes add variety to runs in more consistent Roguelikes (Roguelikes which are less affected by item RNG than Variable Roguelikes). This reinforced to me the fact that Classes I create should fill unique combat niches and be distinct enough from each other to make playing a run as each one change how the Player approaches said run (in terms of item prioritisation, Combat strategy etc.) with balanced strengths and weaknesses.


The first Class I designed, ‘The Bearer of Lust’, is the Class Players will have unlocked upon first opening the Game. With that in mind, I made sure it felt like an all-rounder to help Players get into how the Game works. Making this class relatively simple but reliable does two things for the replayability of runs:


  • Unlocking new classes will feel more fun, as they will fill more niche roles, making them more interesting to play the more playtime a Player has, as well as being utilised better by a more experienced Player.


  • The relative simplicity of this class allows the Player to get used to the Game’s systems without also having to learn a Character’s combat niche



The Bearer of Lust uses Wind-Projectiles which are decently-sized and have piercing; making it good at dealing with hordes of enemies. The Class’ Utility Skill gives it a temporary speed boost and gives it 5%HP/sec in healing for the duration; and its special skill is a large AOE in front of the Player which deals high damage and stuns enemies briefly, with a drawback of having a long cooldown.

Its Basic stats will work as a Baseline for other classes- with the Bearer of Lusts’ being considered the average to work the other Class’ around, with a Max HP of 100, Movespeed of 600 and BaseDamage of 10.

The Second Class I designed was ‘The Bearer of Wrath’. Unlocked after the Player’s first death; The Bearer of Wrath has an emphasis on dealing large amounts of damage in bursts.

Its Primary fire can be fired 6 times in very quick succession (every 0.2 secs) but has a 1 second charge regen time, which can leave the Player somewhat vulnerable if they use all these charges too hastily.

Its secondary is a lightning arc with good single-shot damage and piercing as well as being horizontally long; which helps with dealing with crowds, but not as reliable as the Bearer of Lust’s primary and secondary projectiles.



The Bearer of Wrath’s Utility Skill increases movespeed and base damage by 1.5x; but also increases a float value possessed by all Player and Enemy classes, called ‘DamageTakenMod’ by 1.5x. This float has a value of ‘1.0’ by default on all Characters, and its value is multiplied with the [AttackerbaseDamage*DamageModifier] on any given Projectile or AOE when calculating damage against any target.

The Bearer of Wrath’s Utility skill increasing this value by 1.5x basically means that after use, the Player will take 1.5x Damage for the duration of the skill. This utility skill basically makes you into a glass cannon temporarily, so has to be used with care.


Finally, the special skill is a large ‘energy ball’ projectile that deals high damage and explodes into an AOE upon hit, damaging other enemies in the vicinity.




After researching and Designing a couple of classes, I decided to create a baseline for Enemies. However, it was a little after creating a basic melee enemy that follows the player and attacks with a small AOE when it gets close, that I realised something. I was doing inheritance with my Player and Enemy classes wrong, and the longer I left this issue, the more of an inconvenience it was going to be later down the line.


Essentially, my mistake was that I had not created a character base class for my player and enemy base to inherit from; meaning that I would have to create a multitude of functions and variables multiple times (taking damage, basic stats, death etc.)


After realising this I immediately stopped what I was working on to rectify this issue.

Originally, values such as the Basic Stats struct, and functions such as ‘takedamage’ were declared on the ‘BP_PlayerBase’ class; so as I was working on an enemy base class I was having to create these variables and functions again.


I created a Blueprint Character Class called “BP_CharBase”, in which these general values and functions are declared (values and functions shared by Player and Enemy Classes).

From this Blueprint Class, I created two Children- “BP_PlayerCharBase” and “BP_EnemyCharBase”.


I copied all of the Events, Functions and input compatibility from my original Player Base Class into “BP_PlayerCharBase” and continued the work I was doing with my Enemy Base class in “BP_EnemyCharBase”.


This changed in inheritance means that I can declare functions such as status debuffs etc. later on in development once in the “BP_CharBase” and have it apply to both Player and Enemy characters, and it also allows Projectile and my AOE Blueprint Classes to call their “Deal Damage” event from a “BP_CharBase” reference regardless of whether an enemy or player it is being fired at.


I am extremely relieved I noticed this as early as I did- but in the future I should make sure to carry out a good-practice inheritance structure such as this from the beginning to avoid any unnecessary work at all.




Later on this week, I would find that I had repeated this problem with the Projectile and AOE classes I had created in the Week leading up to Week 1.

I rectified this issue in much the same way: making my Projectile and AOE attack blueprint classes inherit from a common parent of “BP_AttackBase” with basic values and functions such as the ‘Damage Modifier’ float and ‘Deal Damage’ event.


Additionally, I added a boolean to the new Projectile Base Class called “Piercing?” which is checked by a branch in its “ActorBeginOverlap” event to determine whether or not the Projectile should destroy itself after applying damage to a hit actor.




When the inheritance issues were fixed, and the two Player Classes I implemented were re-implemented (on a Basic Level) I decided to get back to work on the enemy AI.


I used Unreal Engine’s Behaviour Trees and Blackboard to get some basic Melee and Ranged Behaviour created.


The Blackboard holds values for the Attack Target and the Distance for the enemy to hover from the Player (used in Ranged Enemy Behaviour) which are set on running the Behaviour tree, which is handled in the Enemy AI controller.






















   The Basic Melee behaviour is very simple:

  • The Enemy unfocusses the Player to make sure it does not strafe while moving towards them

  • They move towards the Player using NavMeshBounds (‘AttackTarget’ Blackboard key is used for the AI move to Target

  • The Enemy focusses the Player, making sure they are facing the Player before attacking

  • Behaviour tree calls the Enemy Actor’s “UseMeleeAttack” function

  • Enemy Waits for a second



The Ranged Enemy Behaviour is slightly more complicated:

  • Selector node chooses the first output that succeeds to run: the first of which will only succeed if its decorator returns true. Said decorator checks that distance between the the Controlled Enemy  ‘AttackTarget’ is less or equal to the Enemy’s ‘Distance Hover’ float

When this returns false, it will run the ‘GetIntoPosition’ sequence

  • Unfocuses attack target

  • Moves towards Target: the acceptance radius is set to the enemy’s ‘DistancetoHoverto’ variable before calling the enemy’s “UseRangedAttack” function and waiting a second

When this returns true, it will run the other sequence

  • Focus Target

  • Strafe counter-clockwise around Target (finishes execute after a 1 sec timer)

  • Focus just in case

  • Uses the Enemy’s “UseRangedAttack” function



 There are still improvements to be made even on the basic version of this AI: for example I need to figure out how to randomise the direction around the Player the Enemy strafes without constantly changing direction, which leads to no actual movement.


Comments


Follow me:

  • itch-io
  • Twitter
  • LinkedIn
  • Youtube
bottom of page