0 votes

I am working on a project stemming from the KidsCanCode tank tutorial. i have done a few projects now on this but am just starting a new one to resolve certain issues I came across. getting the project to go in the direction a I want. my aim is in the direction of RTS like C&C

the issue I am stuck on right now has to do with tank turrets not colliding with the environment , the project I am started is just in the beginning stage. I have made a kinematicbody2d vehicle with a a script that I think can apply to both player and enemy , and a kinematicbody2d turret with its own script to handle a turret type of targeting that I think will be useful for either stationary or mobile turrets.

I have made a player tank inheriting from the vehicle body then instanced the turret to the player tank. i have this working the vehicle body is able to drive to click locations and the turret is able to target.

what I cant seem to get to happen is the turret react to collision with an obstacle.

my test scene is just a node 2d, a staticbody2d with a collision shape and the player tank, the only control I have on the tank is click to location, the obstacle is set in a layer that the turret will target and if I click to a location close to the obstacle the tank will drive up to it the turret will target but the collision shape on the muzzle of the turret will be ignored and the turret passes right into the obstacle, the tank collides with the obstacle just fine , and I know this time I have everything in the right collision layers.

I am kind of stumped now as to how to get the turret to act the way I am trying to get it to act.

in Engine by (85 points)

I got the collision to occur, it was not happening because there is no move and slide called for that body, I put in move and slide with velocity as an empty Vector 2 , now the turret collides but gets pushed off the tank :-\

2 Answers

0 votes

I don't think that Godot is meant to make a collision in the way I was looking for

I did get close to what I was looking for. I changed the node type for the turret to node2d , got rid of the turret collision shape , added a raycast2d, on collision with the obstacle call to get collision normal().angle() subtract that from the turrets current angle then add the result to the target angle. this keeps the turret out of obstacles and makes it react the way I wanted. it vibrates against the obstacle because it is stuck outside of its aim tolerance but is close enough for now.

by (85 points)
0 votes

Hey ArthurER:

I was able to get something working that I think approximates what you're looking for. The key was to use a single KinematicBody2D with multiple collision shapes so that all the collision shapes are updated with the same moveandslide() function. Here's what my tank scene looks like:

KinematicBody2D (Name: "TankBody")
  - Sprite (for the tank body)
  - CollisionShape2D (Name: "TankBodyShape", for the tank body)
  - CollisionShape2D (Name: "TankTurretShape", for the tank turret)
    > Sprite (for tank turret)
  - Camera2D

My obstacles are also pretty simple:

StaticBody2D
  - Sprite
  - CollisionShape2D

The messy part is the tank script which I will copy in a separate answer for you to look at. You're welcome to copy it and use it in your project however you see fit.

Your first problem was that when your tank collided with an obstacle, the turret got displaced from the tank's body. The way I solved this issue was to make the turret into a second CollisionShape2D so that Godot treats it as part of the same kinematic body. When you structure the tank in this way, both the tank's body and turret will stop when the turret collides with an obstacle. Unfortunately, making rotation work was a bit trickier, but I'll spare you the details and let you see for yourself.

The script below is mildly customizable. If you want the tank to move when the turret is rotated against an object, set "tankmovewhenturretrotates" to true. Otherwise, when it's false, the turret will stop rotating when it hits an object and only displace the tank by a small amount. It also lets you adjust how quickly the tank rotates towards your mouse cursor, how fast the tank moves, and how far the turret is from the main body of the tank. The script is designed to make the turret rotate around the center of the tank, so position your CollisionShape2D accordingly.

Note: This all works on one collision layer.

I tried doing a write up explaining how this works, but it's late and I am sleepy. I hope this gives you a good base to work off of. If you have any questions feel free to let me know! Happy programming.

by (74 points)

Here's the script, free for your perusal and use. Make sure you name your nodes as I named them above or you'll run into trouble. :)

extends KinematicBody2D

export var movement_speed = 150.0
var turret_offset = 40.0  #the radius of the circle that the turret will sweep through
var turret_rotation_speed = 30.0 #how quickly the turret rotates towards the cursor
export var tank_move_when_turret_rotates = false #whether turret rotation will cause the tank to move
export var dot_product_test_sensitivity = 0 #keep as is

func _ready():
    $TankTurretShape.position.x = turret_offset
    pass

func _physics_process(delta): #process tank movement
    var tank_moving = false
    if Input.is_action_pressed("ui_up"):
        move_and_slide(Vector2(0.0,-1.0)*movement_speed)
        tank_moving = true
    if Input.is_action_pressed("ui_down"):
        move_and_slide(Vector2(0.0,1.0)*movement_speed)
        tank_moving = true
    if Input.is_action_pressed("ui_left"):
        move_and_slide(Vector2(-1.0,0.0)*movement_speed)
        tank_moving = true
    if Input.is_action_pressed("ui_right"):
        move_and_slide(Vector2(1.0,0.0)*movement_speed)
        tank_moving = true
    if tank_moving == false:
        move_and_slide(Vector2(0.0,0.0))

    var collisions = [] #set up variables to receive collision information
    var collided_with_turret = false
    var turret_collision_global_position
    var turret_collision_relative_to_tank
    var obstacle_turret_collided_with
    var obstacle_turret_collided_with_global_position
    var obstacle_turret_collided_with_position_relative_to_tank
    for i in get_slide_count():
        if get_slide_collision(i).local_shape.name == "TankTurretShape":
            collided_with_turret = true
            turret_collision_global_position = get_slide_collision(i).position 
            obstacle_turret_collided_with = get_slide_collision(i).collider
            obstacle_turret_collided_with_global_position = obstacle_turret_collided_with.global_position
            obstacle_turret_collided_with_position_relative_to_tank = obstacle_turret_collided_with_global_position - self.global_position
    if turret_collision_global_position != null:
        turret_collision_relative_to_tank = turret_collision_global_position - self.global_position
        turret_collision_relative_to_tank = turret_collision_relative_to_tank.normalized()

    if tank_move_when_turret_rotates == false: #handle turret rotation
        if collided_with_turret == false:
            var mouse_position = get_viewport().get_mouse_position()
            var global_tank_position = get_global_transform_with_canvas().get_origin()
            var target_turret_vector = mouse_position - global_tank_position
            var current_turret_vector = $TankTurretShape.position.normalized()*turret_offset

            target_turret_vector = target_turret_vector.normalized()*turret_offset
            var neutral_turret_position = Vector2(1.0,0.0)
            var target_turret_rotation_angle = target_turret_vector.angle_to(neutral_turret_position)

            var position_displacement_direction = (target_turret_vector - current_turret_vector).normalized()
            var final_position_displacement_vector = position_displacement_direction*turret_rotation_speed*delta

            var final_turret_position_this_physics_frame = current_turret_vector + final_position_displacement_vector
            var final_turret_rotation_angle = final_turret_position_this_physics_frame.angle_to(neutral_turret_position)

            $TankTurretShape.rotation_degrees = rad2deg(-final_turret_rotation_angle) #rotate the turret accordingly
            $TankTurretShape.position = final_turret_position_this_physics_frame
        else:
            var mouse_position = get_viewport().get_mouse_position()
            var global_tank_position = get_global_transform_with_canvas().get_origin()
            var target_turret_vector = mouse_position - global_tank_position
            var current_turret_vector = $TankTurretShape.position.normalized()*turret_offset

            target_turret_vector = target_turret_vector.normalized()*turret_offset
            var neutral_turret_position = Vector2(1.0,0.0)
            var target_turret_rotation_angle = target_turret_vector.angle_to(neutral_turret_position)
            var current_turret_rotation_angle = $TankTurretShape.position.normalized().angle_to(neutral_turret_position)

            var position_displacement_direction = (target_turret_vector - current_turret_vector).normalized()
            var final_position_displacement_vector = position_displacement_direction*turret_rotation_speed*delta

            var final_turret_position_this_physics_frame = current_turret_vector + final_position_displacement_vector
            var final_turret_rotation_angle = final_turret_position_this_physics_frame.angle_to(neutral_turret_position)

            #determine if we're rotating the turret away from or toward an obstacle
            var turret_turning_toward_obstacle

            var test_rotated_vector = current_turret_vector.rotated(final_turret_rotation_angle - current_turret_rotation_angle)
            var dot_product_test_current = current_turret_vector.normalized().dot(obstacle_turret_collided_with_position_relative_to_tank.normalized())
            var dot_product_test_new_rotated = test_rotated_vector.normalized().dot(obstacle_turret_collided_with_position_relative_to_tank.normalized())

            if dot_product_test_new_rotated < dot_product_test_current:
                turret_turning_toward_obstacle = true
            else:
                turret_turning_toward_obstacle = false
            if turret_turning_toward_obstacle == false:
                $TankTurretShape.rotation_degrees = rad2deg(-final_turret_rotation_angle)
                $TankTurretShape.position = final_turret_position_this_physics_frame
            else:
                pass
    else:
        var mouse_position = get_viewport().get_mouse_position()
        var global_tank_position = get_global_transform_with_canvas().get_origin()
        var target_turret_vector = mouse_position - global_tank_position
        var current_turret_vector = $TankTurretShape.position.normalized()*turret_offset

        target_turret_vector = target_turret_vector.normalized()*turret_offset
        var neutral_turret_position = Vector2(1.0,0.0)
        var target_turret_rotation_angle = target_turret_vector.angle_to(neutral_turret_position)

        var position_displacement_direction = (target_turret_vector - current_turret_vector).normalized()
        var final_position_displacement_vector = position_displacement_direction*turret_rotation_speed*delta

        var final_turret_position_this_physics_frame = current_turret_vector + final_position_displacement_vector
        var final_turret_rotation_angle = final_turret_position_this_physics_frame.angle_to(neutral_turret_position)

        $TankTurretShape.rotation_degrees = rad2deg(-final_turret_rotation_angle) #rotate the turret accordingly
        $TankTurretShape.position = final_turret_position_this_physics_frame

thanks I appreciate the reply and efforts, I mentioned above that I had found a solution that I am reasonably happy with, it could perhaps stand a little tweaking but for now I am putting that aside and going on to learning the box selecting method for selecting multiple units. my project is not that similar to yours I don't drive or aim the units, just point them at where to go, sort of aiming for a Command and Conquer style of RTS.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.