|
|
|
|
Reply From: |
RaebaldKashban |
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 move_and_slide() 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 “tank_move_when_turret_rotates” 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.
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
RaebaldKashban | 2020-09-27 04:10
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.
ArthurER | 2020-09-27 08:39