Search Unity

i need help with enemy projectile

Discussion in 'Scripting' started by Glowball123, Jan 31, 2021.

  1. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    how can i have my enemy shoot towards the player around a tube at a set speed like in the example i drew above.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    Not sure I have enough information, but I'll take a stab, assuming you're doing something like Gyruss or Tempest.

    If you treat the angle going around as one axis and the depth down the tube as the other, then it's just like aiming in 2D.

    If you encode each object's position as (angle, depth) instead of (x,y), (you can obviously just use a Vector2), then the aim towards is simply:

    Code (csharp):
    1. Vector2 aimTowardAngleDepth = (playersAngleDepth - enemyAngleDepth).normalized;
    Multiply that by the speed you want it to go.

    As long as you respect Angle and Depth in the same orthographic way you would with X,Y in 2D when you move the projectile, it should connect just fine.
     
    tree_arb likes this.
  3. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    so playersAngleDepth would be new vector2 (player.transform.eulerangles.z, player.transform.position.z) and enemyAngleDepth would be new vector2 (enmy.transform.eulerangles.z, enemy.transform.position.z). is that what your saying? also im confused what i would do with aimTowardAngleDepth.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    You should never read from
    eulerAngles
    values: they can be ambiguous due to gimbal lock (I'll leave the googling to you for the details).

    Instead if your playfield cylinder coordinates are angle/depth, you would update everything in that coordinate space, then "project" the coordinates into an actual position on the tube. That lets you bend the tube however you want, and as long as your projection mechanism can follow the bend, the enemies just think they're traveling in 2D space, so the aiming works.

    The way it is set up above, it is the Vector2 from your enemy to your player. You would use that as the motion to fire the bullet towards the player. If the player remained stationary, the bullet would pass right through them.
     
  5. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    so what specifically do i replace with Euler?
     
  6. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    Code (CSharp):
    1.  
    2.     Transform player;
    3.     public float speed = 50;
    4.     Vector2 aimTowardAngleDepth;
    5.     void Start()
    6.     {
    7.         player = GameObject.FindGameObjectWithTag("Player").transform;
    8.         Vector2 playerAngle = new Vector2(player.rotation.z, player.position.z);
    9.         Vector2 bulletAngle = new Vector2(transform.rotation.z, transform.position.z);
    10.         aimTowardAngleDepth = (playerAngle - bulletAngle).normalized * speed;
    11.      
    12.  
    13.     }
    14.  
    15.     void Update()
    16.     {
    17.         transform.position += new Vector3(0, 0, aimTowardAngleDepth.y);
    18.         transform.RotateAround(Vector3.zero, Vector3.forward, aimTowardAngleDepth.x);
    19.     }
    i tried doing what i think you said and it didnt work
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    Quaternion x,y,z,w fields are NOT what you think they are. You cannot use them directly.

    You also cannot use eulerAngles as I indicated above.

    YOU must choose to store an angle and a depth for your enemy (how else are you knowing where they are on your cylinder?), and from those external variables, put them into a Vector2 and do the computation I indicated in the first reply above.

    The effect of that operation is to basically make it "as if" your game is taking place on a 2D piece of paper, but that paper is magically wrapped around a cylinder. The way that wrapping would work is you use the angle and depth to compute the actual 3D space position of the items.

    I put together a little scene demoing cylindrical coordinates inside my Proximity Buttons demo package. The enemy goes around in a little curving path and fires bullets ALWAYS at the player. You can move around the perimeter.

    Note how all coordinates for a player are stored in a CylindricalPosition object and how it consists of an Angle and Depth. Everything is projected based on that, including the ballistic objects, the player, the enemy, etc. Those coordinates are projected through the CylindricalMapper, which can be as complicated or as simple as you want, but the enemies still just use 2D space of Angle/Depth and do all their calculations that way.

    I made a video, links to the full source code and set up scene and project in the comments to the video:



    proximity_buttons is presently hosted at these locations:

    https://bitbucket.org/kurtdekker/proximity_buttons

    https://github.com/kurtdekker/proximity_buttons

    https://gitlab.com/kurtdekker/proximity_buttons

    https://sourceforge.net/projects/proximity-buttons/
     
  8. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    Wow you really went above and beyond, thank you! i just have one last follow up question, how can i make the enemy predict where the player will go based off its current movement and shoot a bullet there?
     
    Kurt-Dekker likes this.
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    You're welcome! I played Gyruss and Tempest back in the day, but I'd never put it all together, so I had a good time and hopefully some of it can be useful to you or anyone else who stumbles on it.

    The basic inputs you need for this prediction are:

    - the estimated time of flight from the enemy to the player (distance divided by shot speed)
    - the current position of the player (obviously)
    - the current velocity of the player

    Referring to the code above, I would probably just .AddComponent<> yet another CylindricalPosition to the enemy, and have him use that as his player prediction workpad. He knows how fast his bullet travels, and in terms of angle/depth, he knows the approximate time of flight, so just extrapolate the player's current velocity (you gotta note this somehow so the enemy can get at it too, perhaps in yet another variable on the player) for that amount of time and aim there.

    To do it with high precision you really need to do what is called a "solver," where you make a first estimate of the shot, then "play it out" immediately and adjust because if the player is moving so that the actual predicted flight time is shorter or longer at impact, then that feeds back and you need to iterate. This is called a "solver" approach and is an iterative integration solution.

    Or just say "meh, close enough, aim as if the shot flight time was equivalent to his current distance from me."

    Also, don't forget it's often more fun from a game standpoint if the enemy blindly shoots where the player is now, or sprays a few shots on either side of the player. That lets the player actually know he has to keep moving, adds a sense of urgency.
     
  10. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    there's just one more issue, I plan on having walls that the player can bump into when moving forward through the tube. another problem I have is that when projectiles are moving very fast they sometimes fly through the player/enemies. How would i go about moving objects through and around the tube using velocity rather than teleporting them with position?
     
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    Glowball! You're back! Yaay!

    If you're using Unity physics (eg, moving Rigidbodies), it is as easy as:

    - enabling continuous collision detection in the Rigidbody

    - making sure you are NOT moving the Rigidbody by setting transform.position (this is bad). To move a Rigidbody yourself, you must call the .MovePosition() method on the rigidbody.

    That's all you generally need for Rigidbody collisions at high speed.

    ONLY if you are not using Rigidbodies, you kinda have to do the work yourself by breaking a single move step in a frame into multiple steps and doing them all in one frame.

    So if in a single given frame you were going from position A to position B, you would:

    - find the distance between them
    - divide that distance by the maximum you want to move (distance)
    - turn that result into an integer
    - add 1 to it (just to make sure you ALWAYS take one step)
    - do a for() loop from 1 to that number, "stepping" it from A to B with Vector3.Lerp():

    Code (csharp):
    1. float distance = Vector3.Distance( A, B);
    2.  
    3. int count = 1 + (int)( distance / MaxMoveStepDistance);
    4.  
    5. for (int i = 0; i < count; i++)
    6. {
    7.    fraction = (1.0f + i) / count;
    8.  
    9.    Vector3 testPosition = Vector3.Lerp( A, B, fraction);
    10.  
    11.    // todo: ask if testPosition hits your obstacle
    12. }
    Now actually MOVE the actual Unity object to its new destination B
     
  12. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    i thought that using rb.moveposition() would fix the problem aswell. but unfortunately it does not fix the issue of moving through walls. Here is your code for the cylindrical mapper but modified to use rigidbody move instead:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CylindricalMapper : MonoBehaviour
    6. {
    7.     // so everybody has access to the current one always
    8.     public static CylindricalMapper Instance {get; private set; }
    9.  
    10.     void Awake()
    11.     {
    12.         Instance = this;
    13.     }
    14.  
    15.     // cheap and cheerful way to map a CylindricalPosition
    16.     // into 3D space... this is just a default cylinder
    17.  
    18.     public float Radius;
    19.     public float Length;
    20.     //public float Speed;
    21.     // this is where all the magic happens
    22.     public void ProjectCylindricalToWorld3D( CylindricalPosition cp, Rigidbody rb)
    23.     {
    24.         // first handle position
    25.         var pos = transform.position;        // center
    26.  
    27.         // agreed based on setup of scene (+Z cylinder)
    28.         // plus I want zero to be the bottom (down)
    29.         var offset = Vector3.down * Radius;
    30.  
    31.         // and rotated around the long axis of the cylinder
    32.         offset = Quaternion.Euler( 0, 0, cp.Angle) * offset;
    33.  
    34.         // move it to the outside
    35.         pos += offset;
    36.  
    37.         // again, agreed because we are +Z
    38.         var forward = Vector3.forward;
    39.  
    40.         // normalize apply depth
    41.         forward *= cp.Depth;
    42.  
    43.         // scale to length of cylinder
    44.         forward *= Length;
    45.  
    46.         pos += forward/(Length*Radius);
    47.  
    48.         rb.MovePosition(pos);
    49.  
    50.         // now to handle rotation
    51.         Quaternion rotation = Quaternion.identity;
    52.  
    53.         // again , by +Z cylinder agreement
    54.         rotation = Quaternion.Euler( 0, 0, cp.Angle) * rotation;
    55.  
    56.         rb.MoveRotation(rotation);
    57.     }
    58. }
    59.    

    despite the player moving around the tube fine, it is still able to pass through walls while being kinematic or non kinematic and I'm not sure why.
     
  13. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    hope you had a happy thanksgiving!
     
  14. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    But did you set the Rigidbody's collision detection to be continuous (either in the inspector ideally, or I guess you COULD set it in the code, but that is less commonly done)?
     
  15. Glowball123

    Glowball123

    Joined:
    Mar 10, 2016
    Posts:
    78
    yes i have set it to continuous.