Search Unity

[SOLVED] Fire Projectile towards last position of player and make it continue in that direction

Discussion in 'Physics' started by transparo21, Feb 11, 2017.

  1. transparo21

    transparo21

    Joined:
    Jan 27, 2017
    Posts:
    4
    Hi guys. OK, i managed to make a script that makes an enemy shoot a projectile at last player position. The projectile will hit the player if he is standing still but will miss if he moves, giving him a chance. However, the projectile stops at the target position and stay there.

    My problem is how to make the projectile move towards last player position and continue moving in that direction indefinitely?

    I want to avoid using rigidBody if possible because the projectile gets stuck in objects and causes weird interaction with the other enemies.

    Code (CSharp):
    1.      
    2.     void Start()
    3.     {
    4.        // Get last know player position, y is +1 so the bullet appears to "float above ground a bit"
    5.        lastPos = new Vector3(player.transform.position.x, player.transform.position.y+1,          player.transform.position.z);[/INDENT]
    6.     }
    7.  
    8.     // Update is called once per frame
    9.     void Update()
    10.     {
    11.        // Move the projectile towards player's last position
    12.      transform.position = Vector3.MoveTowards(transform.position, lastPos, speed * Time.deltaTime);
    13.  
    14.     }
    Thanks in advance.
     
    loremonger likes this.
  2. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Vector3.MoveTowards moves the bullet towards the player's last knwon position, what you want is to move it in the player's last known direction. This should work:

    Code (CSharp):
    1.     void Start()
    2.     {
    3.         // Add + 1 to player's last known position so bullet appears to float above ground.
    4.         Vector3 playerPos = new Vector3(player.position.x, player.position.y + 1, player.position.z);
    5.  
    6.         // Aim bullet in player's direction.
    7.         transform.rotation = Quaternion.LookRotation(playerPos);
    8.     }
    9.  
    10.     // Update is called once per frame
    11.     void Update()
    12.     {
    13.         // Move the projectile forward towards the player's last known direction;
    14.         transform.position += transform.forward * speed * Time.deltaTime;
    15.     }
     
  3. transparo21

    transparo21

    Joined:
    Jan 27, 2017
    Posts:
    4
    Thanks Isaiah =). Glad to receive a reply, especially when starting out in Unity and seeking advice in these forums.

    I am using navmesh for my enemies to move towards my player so they are already facing the playing and line 14 works perfectly.

    I will keep the Quaternion.LookRotation in mind in case I need to objects to "aim" at the player first.

    Cheers =D
     
  4. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Glad it helped you. :)

    The simplest method is actually Transform.LookAt:
    Code (CSharp):
    1. transform.LookAt(player.position);
    However, Quaternion.LookRotation is helpful when you want to look at a Vector3 instead of a Transform. Which I needed in the above example since I was modifying the player's position beforehand and returning a vector3 to look at instead of the transform.

    Also for simple questions like this Unity Answers might be a better place. They probably have answers to very similar questions there already and you can learn a lot by just browsing it.

    Best of luck with Unity!
     
    Last edited: Feb 16, 2017
    TheUltimateCreator498 likes this.
  5. SlappyTheMonkeyBoy

    SlappyTheMonkeyBoy

    Joined:
    Nov 20, 2018
    Posts:
    1
    this was super helpful
     
  6. Sabot95

    Sabot95

    Joined:
    Feb 4, 2019
    Posts:
    1
    Once again old posts have saved me.
     
  7. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Great to see people are still finding this helpful. You can also just hit that like button to show your appreciation, instead of resurrecting an ancient thread ;)
     
    Last edited: Jul 16, 2020
  8. aristheviking

    aristheviking

    Joined:
    Jan 2, 2020
    Posts:
    5
    I am also having the same problem, but in 2d. I already have a fireball script that can fire straight on and hit a wall based on Rigidbody.AddForce, but I cannot make it follow the player using the same principles. Any ideas?
    Also, here is my fireball script:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Fireball :MonoBehaviour
    4. {
    5.     public GameObject shooter;
    6.     private float _xDir;
    7.     private Rigidbody2D _rb;
    8.     private bool _hasCollided;
    9.     public bool followPlayer;
    10.     private const float RotateSpeed = 5f;
    11.     private const float MoveSpeed = 5f;
    12.     private const float FollowTimer = 0.005f;
    13.     public float followTimerCountdown = FollowTimer;
    14.     private PlayerController player;
    15.    
    16.  
    17.     private void Start()
    18.     {
    19.         _xDir = shooter.transform.localScale.x;
    20.         transform.localScale = new Vector3(_xDir/Mathf.Abs(_xDir), 1, 1);
    21.         _rb = GetComponent<Rigidbody2D>();
    22.         player = FindObjectOfType<PlayerController>();
    23.     }
    24.  
    25.  
    26.     private void OnTriggerEnter2D(Collider2D other)
    27.     {
    28.        
    29.         if (shooter.CompareTag("Player"))
    30.             // if the shooter of this fireball is the player, then set the collisions to:
    31.             _hasCollided = other.CompareTag("DestroyAble") || other.CompareTag("Ground");
    32.         else if (shooter.CompareTag("DestroyAble"))
    33.             // otherwise, if the shooter of this fireball is any enemy or cannon, then set the collisions to:
    34.             _hasCollided = other.CompareTag("Player") || other.CompareTag("Ground");
    35.        
    36.        
    37.        
    38.     }
    39.  
    40.     private void Update()
    41.     {
    42.         if (!_hasCollided)
    43.         {
    44.             if (followPlayer)
    45.             {
    46.                 //The area that I want to have the script for the fireball to follow the player
    47.                
    48.             }
    49.             else
    50.             {
    51.  
    52.                 _rb.AddForce(new Vector2(5 * _xDir / Mathf.Abs(_xDir), 0));
    53.             }
    54.         }
    55.         else
    56.         {
    57.             Destroy(gameObject);
    58.         }
    59.     }
    60. }
     
  9. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    @aristheviking looks like you want a vector instead of quaternion. You can convert a quaternion using the eulerAngles but it will probably be easier to just do this:

    Code (CSharp):
    1.  
    2. if (followPlayer)
    3. {
    4.     // Get player direction and normalize it so it doesn't exceed 1.
    5.     Vector3 aim = (playerObject.transform.position - transform.position).normalized;
    6.     // Add force based on that direction, modifying it as needed.
    7.     _rb.AddForce(aim * speed);
    8. }
    9.  
     
    unity_x_M1dTZ3aJZ_Ag likes this.
  10. aristheviking

    aristheviking

    Joined:
    Jan 2, 2020
    Posts:
    5
    Wow thanks a lot, Isaiah!
    The only problem is, I have the Cannon shooting two fireballs at a time, and they are following the exact same ai. is there a way to make it wiggle a bit and move around objects?
    I tried Random.Range, but that just made its landing destination different.
     
  11. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    That's a lot more complicated and the exact way to do this completely depends on what effect you want. But it sounds like you just want them to be offset a bit so they don't follow the same exact path?

    Where are you using Random.Range? Using that in fixed update to apply a little random force continuously should work very well for fireballs and offset them each a little. You could also make them homing missiles (good tutorial here) and fire each one at a different angle so they each follow a slightly different path.
     
  12. aristheviking

    aristheviking

    Joined:
    Jan 2, 2020
    Posts:
    5
    Here is a link to a video to show you what I mean:
     
    Last edited: Jul 20, 2020
  13. aristheviking

    aristheviking

    Joined:
    Jan 2, 2020
    Posts:
    5
    @IsaiahKelly I think I got it working with A* Pathfinding
     
  14. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    @aristheviking Sorry for not responding sooner. Got busy and totally missed your posts :oops:. Thanks for making that video. It's very helpful.

    The rotation issue you see is caused from the fireball's forward facing direction not matching the rigid body's, which is transform.up in the example script. To fix this all you need to do is either rotate the sprite in a paint program so forward direction is up, or change all occurrences of transform.up to transform.down/right/left so it matches the sprite's facing direction.

    To make the fireball not follow the player at first, set angleChangingSpeed to something very low at start and then increase value after some time. Here's a script I made based on the original HomingMissile example that uses an AnimationCurve to smoothly increase turn speed over time.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [RequireComponent(typeof(Rigidbody2D))]
    4. public class HomingMissile : MonoBehaviour
    5. {
    6.     [Tooltip("Transform to home in on")]
    7.     public Transform target;
    8.     [Tooltip("Constant forward movement speed")]
    9.     public float moveSpeed = 5;
    10.     [Tooltip("How quickly missile will rotate towards target")]
    11.     public float turnSpeed = 50;
    12.     [Tooltip("Amount of turn speed applied over time")]
    13.     public AnimationCurve turnSpeedCurve;
    14.     [Tooltip("Total time (in seconds) to apply curve")]
    15.     public float turnSpeedCurveTime = 1;
    16.  
    17.     Rigidbody2D body;
    18.     float curveTime = 0;
    19.  
    20.     private void Start()
    21.     {
    22.         // Automatically get rigidbody component so we don't have to manually.
    23.         body = GetComponent<Rigidbody2D>();
    24.     }
    25.  
    26.     private void FixedUpdate()
    27.     {
    28.         Vector2 direction = (Vector2)target.position - body.position;
    29.         direction.Normalize();
    30.         curveTime += Time.fixedDeltaTime / turnSpeedCurveTime;
    31.         float rotateSpeed = turnSpeed * turnSpeedCurve.Evaluate(curveTime);
    32.         float rotateAmount = Vector3.Cross(direction, transform.right).z;
    33.  
    34.         body.angularVelocity = -rotateSpeed * rotateAmount;
    35.         body.velocity = transform.right * moveSpeed;
    36.     }
    37. }
     
  15. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    That or Unity's NavMesh is something you'll definitely want to use if you want them to navigate around obstacles.
     
  16. Horribleme

    Horribleme

    Joined:
    Oct 5, 2020
    Posts:
    1
    I am having a bit of an issue and it appears you have the solution. I am trying to get a projectile to fire straight and not follow the nose cone of the aircraft. For example if I fire at a target and move my craft the "bullet" will no longer be heading in the same direction, but will move in whichever direction I am. If you could provide a basic generic code to solve this problem It would be appreciated.

    1 using System.Collections;
    2 using System.Collections.Generic;
    3 using UnityEngine;
    4
    5 public class firebolts : MonoBehaviour {
    6 public GameObject projectile;
    7 public float projectilespeed;
    8
    9
    10
    11
    12// Update is called once per frame
    13 void Update ()
    14 {
    15 if (Input.GetKeyDown (KeyCode.Space))
    16 {
    17 GameObject plasmabolt = Instantiate (projectile,transform) as GameObject;
    18 Rigidbody rb = plasmabolt.GetComponent<Rigidbody>();
    19 rb.velocity = (transform.rotation * -40);
    20
    21 }
    22 }
    23}