The Godot Community Forums are back up and running! Please read the the announcement post for important information on what happened. Thanks!

Launching a RigidBody from a weapon

ErtainErtain Posts: 8Member
edited September 11 in Programming

In my game, there's a launcher weapon (sort of like a grenade launcher) which throws RigidBody bullets. The player aims the weapon, and launches the bullet from the muzzle of the weapon. My problem is that I can't get the bullet to properly fire. I use code stolen based upon the code from the third person shooter demo. It uses the crosshairs on the screen to determine where to fire the bullet. The problem is that the bullet doesn't fire in the correct direction; it'll fire off to the left, right, or wherever.

Here's my current code:

 # The code for the weapon
var central_point: Vector2 = $Crosshairs.rect_position + $Crosshairs.rect_size * 0.5
var fire_point: Vector3 = $muzzle.global_transform.origin
var ray_from: Vector3 = camera.project_ray_origin( central_point )
var ray_direction: Vector3 = camera.project_ray_normal( central_point )
var ball: RigidBody = bullet.instance()
var scene_root = get_tree().get_root().get_children()[0]
scene_root.add_child( ball )
ball.shooter = player_node
ball.global_transform.origin = $muzzle.global_transform.origin
var target: Vector3 =  ray_direction + ray_from * 100
var direction: Vector3 = (target - fire_point).normalized()
ball.direction = direction * firing_strength

# The code for the bullet
func _integrate_forces(_state):
   apply_impulse(global_transform.origin, direction)

This code is called when the player presses the fire button (i.e. in the _input() function). I would think that's the problem, but I have another weapon that's also fires in the _input() function, and that works correctly. If anyone has other ideas, I would like to read them.

Answers

  • I have a link of video......not exactly same but on vr.. But code and working is same on vr and on normal 3d . So, feel free to get help from this video

  • ErtainErtain Posts: 8Member

    Bastiaan's video didn't really help. His sniper rifle uses the opposite of the Z component of the muzzle's basis property, as seen in the code here:

    new_bullet.linear_velocity = -new_transform.basis.z * initial_bullet_speed
    

    That's a bit different from what I'm doing. Not only is there aiming, but the bullet also has to shoot out from the muzzle of the weapon.

    He also applies some code on the next line, but I think that's for the path of the bullet.

  • TwistedTwiglegTwistedTwigleg Posts: 3,141Admin

    I don't know if this would work, but could you have the bullet face the direction of the target from the raycast, rotate the bullet using something like look_at, and then use the resulting direction vector to move the bullet into position? Something like this maybe?

     # The code for the weapon
    var central_point: Vector2 = $Crosshairs.rect_position + $Crosshairs.rect_size * 0.5
    var fire_point: Vector3 = $muzzle.global_transform.origin
    var ray_from: Vector3 = camera.project_ray_origin( central_point )
    var ray_direction: Vector3 = camera.project_ray_normal( central_point )
    var ball: RigidBody = bullet.instance()
    var scene_root = get_tree().get_root().get_children()[0]
    scene_root.add_child( ball )
    ball.shooter = player_node
    ball.global_transform.origin = $muzzle.global_transform.origin
    var target: Vector3 =  ray_direction + ray_from * 100
    
    # New code:
    var direction: Vector3 = ball.looking_at(target, Vector3.UP).basis.z.normalized()
    
    ball.direction = direction * firing_strength
    
    # The code for the bullet
    func _integrate_forces(_state):
       apply_impulse(global_transform.origin, direction)
    
  • ErtainErtain Posts: 8Member

    That didn't work either because the code you gave me has an error.

    The code

    var direction: Vector3 = ball.looking_at(target, Vector3.UP).basis.z.normalized()
    

    doesn't return a Transform; it doesn't return anything. What's even stranger is that the code

    ball.global_transform.look_at(target, Vector3.UP).basis.z.normalized()
    

    doesn't work either. In the editor, that code gives the error The method "look_at" isn't declared on base "Transform", even though in the documentation the class Transform does have a look_at() method. So I don't know what's going on.

  • MegalomaniakMegalomaniak Posts: 2,911Admin

    You might want to read this:
    https://docs.godotengine.org/en/stable/tutorials/physics/rigid_body.html#rigidbody

    If you only need to place a rigid body once, for example to set its initial location, you can use the methods provided by the Spatial node, such as set_global_transform() or look_at(). However, these methods cannot be called every frame or the physics engine will not be able to correctly simulate the body's state. As an example, consider a rigid body that you want to rotate so that it points towards another object. A common mistake when implementing this kind of behavior is to use look_at() every frame, which breaks the physics simulation. Below, we'll demonstrate how to implement this correctly.

  • TwistedTwiglegTwistedTwigleg Posts: 3,141Admin

    @Ertain said:
    That didn't work either because the code you gave me has an error.

    The code

    var direction: Vector3 = ball.looking_at(target, Vector3.UP).basis.z.normalized()
    

    doesn't return a Transform; it doesn't return anything. What's even stranger is that the code

    ball.global_transform.look_at(target, Vector3.UP).basis.z.normalized()
    

    doesn't work either. In the editor, that code gives the error The method "look_at" isn't declared on base "Transform", even though in the documentation the class Transform does have a look_at() method. So I don't know what's going on.

    My bad, I had the code wrong. look_at is a void function that rotates the Spatial to look at the target rather than returning a transform that is rotated. Instead, you need to use the looking_at function (not sure why the name is different TBH) in Transform instead:

    var direction: Vector3 = ball.global_transform.looking_at(target, Vector3.UP).basis.z.normalized()
    
  • ErtainErtain Posts: 8Member

    The bullet is shooting forward. But it still veers off target.

    That new code is doing something. But I don't know how to fix this. The code is now updated to look like this:

    # Code for the weapon.
    var central_point: Vector2 = $Crosshairs.rect_position + $Crosshairs.rect_size * 0.5
    var fire_point: Vector3 = $muzzle.global_transform.origin
    var ray_from: Vector3 = camera.project_ray_origin( central_point )
    var ray_direction: Vector3 = camera.project_ray_normal( central_point )
    var ball: RigidBody = bullet.instance()
    var scene_root = get_tree().get_root().get_children()[0]
    scene_root.add_child( ball )
    ball.shooter = player_node
    ball.global_transform = $muzzle.global_transform
    var target: Vector3 =  ray_direction + ray_from
    var direction: Vector3 = ball.global_transform.looking_at(target, Vector3.UP).basis.z.normalized()
    ball.direction = direction * firing_strength
    
    # Code for the bullet.
    var direction: Vector3 = Vector3()
    ...
    func _integrate_forces(_state):
         apply_impulse(Vector3(0,0,0), direction)
    
  • TwistedTwiglegTwistedTwigleg Posts: 3,141Admin
    edited September 15

    There are a few things I'd try based on the GIF.

    The first thing I'd check is to see if the target position is where you expect it to be. I would make a MeshInstance node attached to the weapon called something like DEBUG and have it be a small, brightly colored sphere that has good constrast with the environment. Then I'd do something like get_node("DEBUG").global_transform.origin = target in the code for the weapon so you can see if the target position is where you would expect it to be. You could also check to make sure the direction is pointing in the correct direction the same way. I think the target position and direction should be correct, but I have found that double checking may reveal that something minor is off in the calculation.

    The second thing I'd do is temporarily comment out the _integrate_forces function in the bullet, and instead add something like this to it's _ready function:

    func _ready():
        apply_impulse(Vector3.ZERO, direction)
    

    That would help tell if the impulse being applied each time _integrate_forces is being called is causing the issue or not. Another thing to try is locking the rotation of the RigidBody, as while I think the apply_impulse function isn't dependent on the rotation of the node, it might be and that may explain why the bullet is veering off to the side.

  • ErtainErtain Posts: 8Member

    I tried what you suggested, using a red DEBUG sphere to check the positions. While the target variable is lining up, the direction keeps moving the sphere in the wrong place.

    As you can see, the red sphere isn't exactly going in the direction I would expect.

  • ErtainErtain Posts: 8Member

    Couldn't figure out how to make this work, and so I ended up using a Spatial node to "aim" the bullet. Not the best answer, but it's the best I can do right now.

  • TwistedTwiglegTwistedTwigleg Posts: 3,141Admin

    I'm glad you were able to find a working solution, even if it is not ideal!
    (Sorry about not replying sooner! I saw the GIF and meant to reply the day it was posted, but I was busy at the time and then I forgot. Sorry!)

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Emoji
Image
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file