Need some guidance on how to proceed with my boss attack pattern

Good Evening,

So I am looking for some advice on how to go about a certain attack pattern for my boss unit. I am making a 2d metroidvania and I am struggling with his swoop attack ( I am going to insert the GDD entry for him in post)

  • Eagle Commander Ozzie

    • Stats
      • Health: 25
      • Armor: 15
      • Contact Damage: 10
      • Nexa Stone: 20
    • Movement Pattern- Ozzie will be flying in the air and will move back and forth.
    • Swooping Strike- While Ozzie is flying she will swoop down and strike the player with her talons.
      • Damage Done: 8
    • Summon Tornado- Ozzie will place three tornados down. These tornados will last for 5 seconds. If the player is caught in these they will be incapacitated for 5 seconds and will take damage.
      • Damage Done: 3 damage per second
    • Dire Mode
      • Tempest - Ozzie will flap her wings creating a violent gust of wind that will push the player back into a pit. She will do this for 6 seconds. If the player doesn’t take cover they will take 3 damage every second.
        • Damage Done: 3 every second for 6 seconds

So like I said earlier I am struggling with the swoop. I have attempted some code and I have got her to move diagonally down but she keeps moving and doesn’t stop XD. I couldn’t figure out how to fix it I came up with some ideas but I didn’t implement them. I instead tried to just use a simple animation by making Ozzie stab down. I am having issues with the animation resetting back to it’s starting position (yes I have tried finding solutions to this by making an empty game object and making it its parent and it still doesn’t work) So I have been struggling with this for a few days now and I am coming to you guys for advice/guidance.
The thoughts I had to try and fix the problem are as follows
Make two transform points for when Ozzie swoops when Ozzie reaches one she will stop descending then she will ascent then once she hits the ascension point she will continue on normal path. If that doesn’t work then make her more ground based with aerial attack.

I really don’t want to have to rework her so I want to try and find a solution to this problem (I have made a quick sketch of how swoop will work)

So when the player is in range she will swoop down and attempt to strike the player then she will ascend up and resume her normal patrol path. I struggled a lot with coding in college but I think that maybe a lerp would be able to get my desired outcome. If more information is needed please let me know I am really struggling here.

Kindly,

Harpoaian

P.S. Sorry for the word vomit <3

Hey, first of all i’d like to start by giving some general tips when it comes having multiple behaviours/actions when it comes to bosses/NPCs

  • 99% of typical NPC behaviour is expressed through a Finite State Machine (FSM), think of it as a bunch of nodes with lines linking them, the nodes represent the behaviour of a cetain state/action (Running , Hitting , Jumping) , the lines represent the transition with one state to the other under certain conditions
  • typically i would advise to make your own FSM since that gives you flexibility with design choice and custom ways to handle edges cases , if that’s not an option , you can actually use unity’s Animator as an FSM , there’s a unity base class called StateMachineBehaviour (Click to go to docs) that you can inherit and define your own Enter/Update/Exit , that way you get to use a FSM with a very good workflow using the Animator’s user-friendly editor to do the whole linking between the nodes , feel free to search YT for tutorials on that , you’ll get the gist.
  • When it comes to non-linear “shapes” and “curves” , look into AnimationCurve , it’s very friendly way to express that in the editor and use it in code.
  • With all of that in mind , now to the meat of the question , How to make the swoopy-swoop go wiiiiiiiiiishhhh!
    First i would make the swoop attack it’s own FSM node , then when ever the boss enters the “Swoop” node , it should start swooshing.
    when it comes to the logic of the swoosh it self ,i’d make something like this
    ( NOTE : i didn’t test OR run this , i just typed it in an online code editor, so test it first and tweak to your needs and most importantly try to understand it, it probably has some minor errors but it should get the point across)
public class SwoopState : StateMachineBehaviour 
{ 
    public Transform playerTransform;
    public Transform bossTransfrom;
    
    [SerializeField]
    public float duration;
    
    [SerializeField]
    private float range;
    
    [SerializeField]
    private float swoopDownDistance;
    private Vector2 startPos;
    private Vector2 endPos;
    private Vector2 dipPos;
    private Vector2 direction;
    private float currentTimer;
    
    
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // assuming the animtor is placed on the boss
        bossTransfrom = animation.transform;
        
        // just spit balling here , you migh have your own way to get the player
        playerTransform = GameObject.FindObjectWithTag("Player");
        
        // fix the starting pos
        startPos = playerTransform.position;
        
        // set the direction to face the player
        direction = (playerTransform.position - startPos).normalized;
        
        // flatten the Y since we want only the direction horizontally
        direction.y = 0;
        
        // end position is start pos + direction but we clamp it by the range
        endPos = startPos + (direction  * range);
        
        // position at the lowest point of the swoosh attack
        dipPos = ((startPos + endPos) * 0.5f) + (Vector2.down *  swoopDownDistance);
        
        currentTimer = 0;
    }

    override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
       currentTimer += Time.deltaTime;
       
       if(currentTime >= duration)
       {
           ExitLmaoIForgotHow();
           return;
       }
       
       float halfDuration = (duration * 0.5f);
       
       // logic to go from start to the lowest dip point of the attack
       if( currentTimer < halfDuration)
       {
           // this will normalize the currentTimer into a 0 to 1 representing where we are in the attack
           float swoopDownPercentage = currentTimer / halfDuration;
           Vector2 currentPosition = Vector2.Lerp(startPos , startPos + (direction *  range * 0.5f), swoopDownPercentage);
           
           // this here is how we get a "curved" dip and not a linear a-to-b movement
           currentPosition += Vector2.down * Mathf.Sin( Mathf.PI * 0.5f * swoopDownPercentage) * swoopDownDistance;
           bossTransfrom.position = currentPosition;
       }
       
       // same logic but going from dip to the end pos
       else
       {
           float swoopDownPercentage = (currentTimer - halfDuration) / halfDuration;
           Vector2 currentPosition = Vector2.Lerp(dipPos , dipPos + (direction * range * 0.5f) , swoopDownPercentage);
           
           currentPosition += Vector2.up * Mathf.Sin( Mathf.PI * 0.5f * swoopDownPercentage) * swoopDownDistance;
           bossTransfrom.position = currentPosition;
       }
    }
    
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {

       
    }
}