• Godot Help
  • aligning weapon with an enemy using another vectors direction

Given that the player holds the gun at a gun handle, but the direction from which the bullet flies from is located at the gun's barrel (direction vector) - how do I find the angle which the gun should be rotated to, in order for the bullet to fly straight at the enemy, i.e. in order for the gun to point straight at the enemy?

here is the example of the scene:

where:

  1. is the pivot point/rotation origin
  2. the barrel direction vector, origin of the bullet
  3. the enemy

if I were to simplify the drawing it would look like this. Is what I have:

is what I want to achieve

I am using godot 4.0 stable and a C# but answers in any language will help.. Here is the code I have, but it does not work..

public override void _Process(double delta) {
        Vector2 pivotPoint = GlobalPosition;

        var marker2DFrom = GetNode<Marker2D>("Marker2DFrom");
        var marker2DTo = GetNode<Marker2D>("Marker2DTo");
        Vector2 directionVector = (marker2DTo.GlobalPosition - marker2DFrom.GlobalPosition);

        var enemy = GetNode<Node2D>("Enemy");
        Vector2 targetPoint = enemy.GlobalPosition;

        Vector2 pivotToPoint = targetPoint - pivotPoint;

        float dot = directionVector.Dot(pivotToPoint);
        float cross = directionVector.Cross(pivotToPoint);

        float radians = Mathf.Atan2(cross, dot);
        float degrees = radians * (180f / Mathf.Pi);

        GlobalRotationDegrees = GlobalRotationDegrees + degrees;
}

this produces the exact same (wrong) result at simply using LookAt(enemy.GlobalPosition); - it works, but the barrel vector is not pointing at the enemy..

  • dearme Rotation angle should be the angle between vector markerTo-markerFrom and vector enemy-markerFrom

I have made some progress. Following code works as long as the target/enemy is far away from the barrel. if the enemy moves closer - the gun starts shaking, not being able to find the shooting angle:

    var marker2DFrom = GetNode<Marker2D>("Marker2DFrom");
    var marker2DTo = GetNode<Marker2D>("Marker2DTo");
    Vector2 directionVector = marker2DFrom.GlobalPosition.DirectionTo(marker2DTo.GlobalPosition);

    var shootToPoint = marker2DTo.GlobalPosition.DirectionTo(closestEnemy.GlobalPosition);
    var angle = directionVector.AngleTo(shootToPoint);

    GlobalRotation = GlobalRotation + angle;

any ways to fix it?

  • xyz replied to this.

    dearme Rotation angle should be the angle between vector markerTo-markerFrom and vector enemy-markerFrom

      xyz
      your suggestion made me realize what the problem is. In order for the rotation to work correctly the markerFrom vector must be perpendicular to start position of the gun like so:

      then the code as per your suggestion is

          var marker2DFrom = GetNode<Marker2D>("Marker2DFrom");
          var marker2DTo = GetNode<Marker2D>("Marker2DTo");
      
          var directionVector = marker2DTo.GlobalPosition.DirectionTo(marker2DFrom.GlobalPosition);
          var shootToPoint = closestEnemy.GlobalPosition.DirectionTo(marker2DFrom.GlobalPosition);
      
          var angle = directionVector.AngleTo(shootToPoint);
      
          GlobalRotation = GlobalRotation + angle;

        Sorry I went on a bit of a ramble then about the 3D application of bullet spread, I'm not sure why you're doing this so complicated, why not take advantage of raycast nodes and do it that way instead? Is there a special reason you're trying to use this kind of code? I've been doing a bullet spread setup for 3D but I'm very sure that the same method I've adopted will work fine for 2D as well and it automatically takes care of the rotation maths for you, have the node as a child of the gun itself when it's being rotated and job done.

          dearme your suggestion made me realize what the problem is. In order for the rotation to work correctly the markerFrom vector must be perpendicular to start position of the gun like so:

          2 birds 1 stone. Now the gun will also rotate from where the wrist would be. Rather than in between the hand/grip. 😉 If not you might have to also move the object origin a bit.

            Megalomaniak
            the gun starts rotating like crazy when in close proximity to the target. I have solved this issue by adding arbitrary minimum range that forces auto-aim to freeze.

                var distanceToStartPosition = MarkerFrom.GlobalPosition.DistanceTo(targetPosition);
                if (distanceToStartPosition > 10f)
                {
                    //..
                }

            I think this solution is viable as there is no need to aim anyways when something is THAT close..

            dearme I'm not, I'm just trying to understand why you would use an aiming system this way when you've got perfectly good raycast nodes, I've never encountered this method before, it seems overly complicated.

            Here's the type of thing I'm writing about, much less code too, attach your raycast node to the gun muzzle, job done for aiming.

              dearme your suggestion made me realize what the problem is. In order for the rotation to work correctly the markerFrom vector must be perpendicular to start position of the gun

              I don't think this is a prerequisite. It should work with from/to markers at arbitrary positions. If we suppose markers are children of the gun node and origin of the gun node is the rotation pivot, then script in the gun node should do as follows (GDScript):

              var enemy # initialize before
              
              func _process(delta):
              	var m = $markerTo.global_position - $markerFrom.global_position
              	var a = enemy.global_position - $markerFrom.global_position
              	rotate(m.angle_to(a))

                Lethn in the video attached I could not see the person using a raycast2d.. he talks about using the smooth animation only.. I might be missing a point, but i think that using raycast wont magically solve all my problems. When I set the target position on the raycast2d - it has no effect on the rotation of the parent node, it means that I will still need to calculate the rotation/position myself after pointing a raycast.. Am I missing something?

                  xyz this code works in exactly the same way as the one that I posted above.. Thank you for posting a solution in gdscript version in case there are future readers who are interested in this!

                  • xyz replied to this.

                    dearme Yeah, it's the same thing except your code uses reversed vectors, pointing inwards instead towards the enemy. This mathematically works the same but looks a bit strange to someone who's reading the code trying to figure out what is happening. Anyway, it should work for any marker positions except when when enemy is closer to the pivot than the "from" marker. This basically should never happen as it'd mean that enemy is between the gun and the player, which doesn't make sense. But you already figured that out.

                    Posted video deals with a different (though related) problem. Same for raycasting.

                    dearme Sorry, I should mention, what you would do in this situation is make the raycast node a child of the object being rotated, this would then handle all the maths for you automatically because the raycast node is copying the rotation of it's parent. I've done a similar trick making a raycast node a child of my mouse look node in 3D and then I've handled all of my bullet spread stuff from there rather than having to do any fancy maths. I'm just trying to save you guys the bother of doing a silly amount of code when there's a built in option available that does exactly the same thing unless you're looking for something specific with his method that I'm not understanding.

                    • xyz replied to this.

                      Lethn because the raycast node is copying the rotation of it's parent

                      The original problem was how to actually find the rotation of that parent so that arbitrary line segment under it points exactly at the enemy. I don't see how raycast node can help with that. There's no complex math involved. The solution is literally 2 lines of code.