Replace body with WeakRef of body to fix crashes

When a physics body was freed by queue_free or free, it was possible
for the physics update to still happen on the specialized agent. The
Null body then caused a crash.
This commit is contained in:
Francois Belair 2020-02-20 10:58:00 -05:00
parent 68b85bb234
commit fbb92e3593
6 changed files with 116 additions and 57 deletions

View File

@ -2,6 +2,17 @@
This document lists new features, improvements, changes, and bug fixes in every release of the add-on. This document lists new features, improvements, changes, and bug fixes in every release of the add-on.
## Godot Steering AI Framework 2.1.1 ##
### Changes ###
- Unused and undocumented variable `_body_type` has been removed from `SpecializedAgent`
### Bug fixes ###
- The specialized agents now use WeakRef internally to prevent crashes when their `body` is freed.
- `RigidBody2DAgent` now properly connects to physics updates.
## Godot Steering AI Framework 2.1.0 ## ## Godot Steering AI Framework 2.1.0 ##
### Features ### ### Features ###

View File

@ -15,17 +15,18 @@ var body: KinematicBody2D setget _set_body
var movement_type: int var movement_type: int
var _last_position: Vector2 var _last_position: Vector2
var _body_ref: WeakRef
func _init(_body: KinematicBody2D, _movement_type: int = MovementType.SLIDE) -> void: func _init(_body: KinematicBody2D, _movement_type: int = MovementType.SLIDE) -> void:
if not _body.is_inside_tree(): if not _body.is_inside_tree():
yield(_body, "ready") yield(_body, "ready")
self.body = _body _body_ref = weakref(_body)
self.movement_type = _movement_type self.movement_type = _movement_type
# warning-ignore:return_value_discarded # warning-ignore:return_value_discarded
body.get_tree().connect("physics_frame", self, "_on_SceneTree_physics_frame") _body.get_tree().connect("physics_frame", self, "_on_SceneTree_physics_frame")
# Moves the agent's `body` by target `acceleration`. # Moves the agent's `body` by target `acceleration`.
@ -44,55 +45,75 @@ func _apply_steering(acceleration: GSAITargetAcceleration, delta: float) -> void
func _apply_sliding_steering(accel: Vector3) -> void: func _apply_sliding_steering(accel: Vector3) -> void:
var _body: KinematicBody2D = _body_ref.get_ref()
if not _body:
return
var velocity := GSAIUtils.to_vector2(linear_velocity + accel).clamped(linear_speed_max) var velocity := GSAIUtils.to_vector2(linear_velocity + accel).clamped(linear_speed_max)
if apply_linear_drag: if apply_linear_drag:
velocity = velocity.linear_interpolate(Vector2.ZERO, linear_drag_percentage) velocity = velocity.linear_interpolate(Vector2.ZERO, linear_drag_percentage)
velocity = body.move_and_slide(velocity) velocity = _body.move_and_slide(velocity)
if calculate_velocities: if calculate_velocities:
linear_velocity = GSAIUtils.to_vector3(velocity) linear_velocity = GSAIUtils.to_vector3(velocity)
func _apply_collide_steering(accel: Vector3, delta: float) -> void: func _apply_collide_steering(accel: Vector3, delta: float) -> void:
var _body: KinematicBody2D = _body_ref.get_ref()
if not _body:
return
var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max) var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max)
if apply_linear_drag: if apply_linear_drag:
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage) velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
# warning-ignore:return_value_discarded # warning-ignore:return_value_discarded
body.move_and_collide(GSAIUtils.to_vector2(velocity) * delta) _body.move_and_collide(GSAIUtils.to_vector2(velocity) * delta)
if calculate_velocities: if calculate_velocities:
linear_velocity = velocity linear_velocity = velocity
func _apply_position_steering(accel: Vector3, delta: float) -> void: func _apply_position_steering(accel: Vector3, delta: float) -> void:
var _body: KinematicBody2D = _body_ref.get_ref()
if not _body:
return
var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max) var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max)
if apply_linear_drag: if apply_linear_drag:
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage) velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
body.global_position += GSAIUtils.to_vector2(velocity) * delta _body.global_position += GSAIUtils.to_vector2(velocity) * delta
if calculate_velocities: if calculate_velocities:
linear_velocity = velocity linear_velocity = velocity
func _apply_orientation_steering(angular_acceleration: float, delta: float) -> void: func _apply_orientation_steering(angular_acceleration: float, delta: float) -> void:
var _body: KinematicBody2D = _body_ref.get_ref()
if not _body:
return
var velocity = angular_velocity + angular_acceleration var velocity = angular_velocity + angular_acceleration
if apply_angular_drag: if apply_angular_drag:
velocity = lerp(velocity, 0, angular_drag_percentage) velocity = lerp(velocity, 0, angular_drag_percentage)
body.rotation += velocity * delta _body.rotation += velocity * delta
if calculate_velocities: if calculate_velocities:
angular_velocity = velocity angular_velocity = velocity
func _set_body(value: KinematicBody2D) -> void: func _set_body(value: KinematicBody2D) -> void:
body = value _body_ref = weakref(body)
_last_position = body.global_position _last_position = value.global_position
_last_orientation = body.rotation _last_orientation = value.rotation
position = GSAIUtils.to_vector3(_last_position) position = GSAIUtils.to_vector3(_last_position)
orientation = _last_orientation orientation = _last_orientation
func _on_SceneTree_physics_frame() -> void: func _on_SceneTree_physics_frame() -> void:
var current_position := body.global_position var _body: KinematicBody2D = _body_ref.get_ref()
var current_orientation := body.rotation if not _body:
return
var current_position := _body.global_position
var current_orientation := _body.rotation
position = GSAIUtils.to_vector3(current_position) position = GSAIUtils.to_vector3(current_position)
orientation = current_orientation orientation = current_orientation

View File

@ -15,17 +15,18 @@ var body: KinematicBody setget _set_body
var movement_type: int var movement_type: int
var _last_position: Vector3 var _last_position: Vector3
var _body_ref: WeakRef
func _init(_body: KinematicBody, _movement_type: int = MovementType.SLIDE) -> void: func _init(_body: KinematicBody, _movement_type: int = MovementType.SLIDE) -> void:
if not _body.is_inside_tree(): if not _body.is_inside_tree():
yield(_body, "ready") yield(_body, "ready")
self.body = _body self._body_ref = weakref(_body)
self.movement_type = _movement_type self.movement_type = _movement_type
# warning-ignore:return_value_discarded # warning-ignore:return_value_discarded
self.body.get_tree().connect("physics_frame", self, "_on_SceneTree_physics_frame") _body.get_tree().connect("physics_frame", self, "_on_SceneTree_physics_frame")
# Moves the agent's `body` by target `acceleration`. # Moves the agent's `body` by target `acceleration`.
@ -44,55 +45,75 @@ func _apply_steering(acceleration: GSAITargetAcceleration, delta: float) -> void
func _apply_sliding_steering(accel: Vector3) -> void: func _apply_sliding_steering(accel: Vector3) -> void:
var _body: KinematicBody = _body_ref.get_ref()
if not _body:
return
var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max) var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max)
if apply_linear_drag: if apply_linear_drag:
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage) velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
velocity = body.move_and_slide(velocity) velocity = _body.move_and_slide(velocity)
if calculate_velocities: if calculate_velocities:
linear_velocity = velocity linear_velocity = velocity
func _apply_collide_steering(accel: Vector3, delta: float) -> void: func _apply_collide_steering(accel: Vector3, delta: float) -> void:
var _body: KinematicBody = _body_ref.get_ref()
if not _body:
return
var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max) var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max)
if apply_linear_drag: if apply_linear_drag:
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage) velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
# warning-ignore:return_value_discarded # warning-ignore:return_value_discarded
body.move_and_collide(velocity * delta) _body.move_and_collide(velocity * delta)
if calculate_velocities: if calculate_velocities:
linear_velocity = velocity linear_velocity = velocity
func _apply_position_steering(accel: Vector3, delta: float) -> void: func _apply_position_steering(accel: Vector3, delta: float) -> void:
var _body: KinematicBody = _body_ref.get_ref()
if not _body:
return
var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max) var velocity := GSAIUtils.clampedv3(linear_velocity + accel, linear_speed_max)
if apply_linear_drag: if apply_linear_drag:
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage) velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
body.global_position += velocity * delta _body.global_position += velocity * delta
if calculate_velocities: if calculate_velocities:
linear_velocity = velocity linear_velocity = velocity
func _apply_orientation_steering(angular_acceleration: float, delta: float) -> void: func _apply_orientation_steering(angular_acceleration: float, delta: float) -> void:
var _body: KinematicBody = _body_ref.get_ref()
if not _body:
return
var velocity = angular_velocity + angular_acceleration var velocity = angular_velocity + angular_acceleration
if apply_angular_drag: if apply_angular_drag:
velocity = lerp(velocity, 0, angular_drag_percentage) velocity = lerp(velocity, 0, angular_drag_percentage)
body.rotation.y += velocity * delta _body.rotation.y += velocity * delta
if calculate_velocities: if calculate_velocities:
angular_velocity = velocity angular_velocity = velocity
func _set_body(value: KinematicBody) -> void: func _set_body(value: KinematicBody) -> void:
body = value _body_ref = weakref(value)
_last_position = body.transform.origin _last_position = value.transform.origin
_last_orientation = body.rotation.y _last_orientation = value.rotation.y
position = _last_position position = _last_position
orientation = _last_orientation orientation = _last_orientation
func _on_SceneTree_physics_frame() -> void: func _on_SceneTree_physics_frame() -> void:
var current_position := body.transform.origin var _body: KinematicBody = _body_ref.get_ref()
var current_orientation := body.rotation.y if not _body:
return
var current_position := _body.transform.origin
var current_orientation := _body.rotation.y
position = current_position position = current_position
orientation = current_orientation orientation = current_orientation

View File

@ -7,43 +7,48 @@ class_name GSAIRigidBody2DAgent
var body: RigidBody2D setget _set_body var body: RigidBody2D setget _set_body
var _last_position: Vector2 var _last_position: Vector2
var _body_ref: WeakRef
func _init(_body: RigidBody2D) -> void: func _init(_body: RigidBody2D) -> void:
if not _body.is_inside_tree(): if not _body.is_inside_tree():
yield(_body, "ready") yield(_body, "ready")
self.body = _body _body_ref = weakref(_body)
# warning-ignore:return_value_discarded
_body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
# Moves the agent's `body` by target `acceleration`. # Moves the agent's `body` by target `acceleration`.
# tags: virtual # tags: virtual
func _apply_steering(acceleration: GSAITargetAcceleration, _delta: float) -> void: func _apply_steering(acceleration: GSAITargetAcceleration, _delta: float) -> void:
var _body: RigidBody2D = _body_ref.get_ref()
if not _body:
return
_applied_steering = true _applied_steering = true
body.apply_central_impulse(GSAIUtils.to_vector2(acceleration.linear)) _body.apply_central_impulse(GSAIUtils.to_vector2(acceleration.linear))
body.apply_torque_impulse(acceleration.angular) _body.apply_torque_impulse(acceleration.angular)
if calculate_velocities: if calculate_velocities:
linear_velocity = GSAIUtils.to_vector3(body.linear_velocity) linear_velocity = GSAIUtils.to_vector3(_body.linear_velocity)
angular_velocity = body.angular_velocity angular_velocity = _body.angular_velocity
func _set_body(value: RigidBody2D) -> void: func _set_body(value: RigidBody2D) -> void:
body = value _body_ref = weakref(value)
_last_position = body.global_position _last_position = value.global_position
_last_orientation = body.rotation _last_orientation = value.rotation
position = GSAIUtils.to_vector3(_last_position) position = GSAIUtils.to_vector3(_last_position)
orientation = _last_orientation orientation = _last_orientation
func _on_body_ready() -> void:
# warning-ignore:return_value_discarded
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
_set_body(body)
func _on_SceneTree_frame() -> void: func _on_SceneTree_frame() -> void:
var _body: RigidBody2D = _body_ref.get_ref()
if not _body:
return
var current_position := body.global_position var current_position := body.global_position
var current_orientation := body.rotation var current_orientation := body.rotation

View File

@ -7,47 +7,49 @@ class_name GSAIRigidBody3DAgent
var body: RigidBody setget _set_body var body: RigidBody setget _set_body
var _last_position: Vector3 var _last_position: Vector3
var _body_ref: WeakRef
func _init(_body: RigidBody) -> void: func _init(_body: RigidBody) -> void:
if not _body.is_inside_tree(): if not _body.is_inside_tree():
yield(_body, "ready") yield(_body, "ready")
self.body = _body _body_ref = weakref(_body)
# warning-ignore:return_value_discarded # warning-ignore:return_value_discarded
self.body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame") _body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
# Moves the agent's `body` by target `acceleration`. # Moves the agent's `body` by target `acceleration`.
# tags: virtual # tags: virtual
func _apply_steering(acceleration: GSAITargetAcceleration, _delta: float) -> void: func _apply_steering(acceleration: GSAITargetAcceleration, _delta: float) -> void:
var _body: RigidBody = _body_ref.get_ref()
if not _body:
return
_applied_steering = true _applied_steering = true
body.apply_central_impulse(acceleration.linear) _body.apply_central_impulse(acceleration.linear)
body.apply_torque_impulse(Vector3.UP * acceleration.angular) _body.apply_torque_impulse(Vector3.UP * acceleration.angular)
if calculate_velocities: if calculate_velocities:
linear_velocity = body.linear_velocity linear_velocity = _body.linear_velocity
angular_velocity = body.angular_velocity.y angular_velocity = _body.angular_velocity.y
func _set_body(value: RigidBody) -> void: func _set_body(value: RigidBody) -> void:
body = value _body_ref = weakref(value)
_last_position = body.transform.origin _last_position = value.transform.origin
_last_orientation = body.rotation.y _last_orientation = value.rotation.y
position = _last_position position = _last_position
orientation = _last_orientation orientation = _last_orientation
func _on_body_ready() -> void:
# warning-ignore:return_value_discarded
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
_set_body(body)
func _on_SceneTree_frame() -> void: func _on_SceneTree_frame() -> void:
var current_position := body.transform.origin var _body: RigidBody = _body_ref.get_ref()
var current_orientation := body.rotation.y if not _body:
return
var current_position := _body.transform.origin
var current_orientation := _body.rotation.y
position = current_position position = current_position
orientation = current_orientation orientation = current_orientation
@ -56,5 +58,5 @@ func _on_SceneTree_frame() -> void:
if _applied_steering: if _applied_steering:
_applied_steering = false _applied_steering = false
else: else:
linear_velocity = body.linear_velocity linear_velocity = _body.linear_velocity
angular_velocity = body.angular_velocity.y angular_velocity = _body.angular_velocity.y

View File

@ -29,7 +29,6 @@ var linear_drag_percentage := 0.0
var angular_drag_percentage := 0.0 var angular_drag_percentage := 0.0
var _last_orientation: float var _last_orientation: float
var _body_type: int
var _applied_steering := false var _applied_steering := false