mirror of
https://github.com/GDQuest/godot-platformer-2d.git
synced 2026-07-20 17:59:49 +00:00
Filled all scripts with comments.
This commit is contained in:
parent
7f12ca3f0a
commit
92ebb8ca04
14 changed files with 193 additions and 21 deletions
|
|
@ -1,4 +1,10 @@
|
|||
extends Node
|
||||
"""
|
||||
Sample AI controller. It gets and sets the player in its `_ready` function as a target to various
|
||||
behaviours, and picks whichever one it should be calculating on. It also updates the controller
|
||||
with information for the behaviours to use. Then, based on what is calculated, it applies
|
||||
the calculated velocity to itself.
|
||||
"""
|
||||
|
||||
var _target: Node2D
|
||||
var _motion: = SteeringMotion2D.new()
|
||||
|
|
@ -54,6 +60,6 @@ func _process(delta):
|
|||
get_parent().rotate(_rotation_velocity)
|
||||
else:
|
||||
_rotation_velocity = 0
|
||||
_controller.set_rotation_velocity(0)
|
||||
_controller.current_rotation_velocity = 0
|
||||
|
||||
_last_target_position = target_position
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
extends Node2D
|
||||
"""
|
||||
Simple script that lets the user move around with the arrow keys.
|
||||
"""
|
||||
|
||||
var speed: float = 125
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ deceleration_radius = 150.0
|
|||
script = ExtResource( 5 )
|
||||
deceleration_radius = 45.0
|
||||
alignment_tolerance = 2.0
|
||||
time_to_target = 1.0
|
||||
|
||||
[node name="Sprite" type="Sprite" parent="AI"]
|
||||
texture = ExtResource( 6 )
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
extends Node
|
||||
class_name BehaviourController2D
|
||||
"""
|
||||
Meant to be the parent to behaviours. It is a helper to provide information to its children behaviours.
|
||||
|
||||
The controller is the hub through which behaviours access various information about themselves, and
|
||||
their targets. It holds the maximum speeds and acceleration amounts, and has space to hold current
|
||||
velocities that can be used in more predictive behaviours. However, the controller is naive and
|
||||
needs to be fed this information; where the information comes from should be determined by the programmer.
|
||||
"""
|
||||
|
||||
export(NodePath) var steerable_path: NodePath
|
||||
export(float) var max_linear_speed: float
|
||||
|
|
@ -15,23 +23,25 @@ var target_current_rotation_velocity: float = 0
|
|||
|
||||
onready var steerable: Node2D = get_node(steerable_path)
|
||||
|
||||
"""
|
||||
Returns true if a Node2D was successfully put into the controller.
|
||||
"""
|
||||
func valid() -> bool:
|
||||
return steerable != null
|
||||
|
||||
|
||||
"""
|
||||
Sets the current linear velocity by way of X and Y, rather than copying or creating new vectors.
|
||||
"""
|
||||
func set_linear_velocity(x: float, y: float) -> void:
|
||||
current_linear_velocity.x = x
|
||||
current_linear_velocity.y = y
|
||||
|
||||
|
||||
func set_rotation_velocity(rotational_velocity: float) -> void:
|
||||
current_rotation_velocity = rotational_velocity
|
||||
|
||||
|
||||
"""
|
||||
Sets the target's current linear velocity by way of X and Y, rather than copying or creating new vectors.
|
||||
Not all behaviours have a target that moves, so match requirements accordingly.
|
||||
"""
|
||||
func set_target_linear_velocity(x: float, y: float) -> void:
|
||||
target_current_linear_velocity.x = x
|
||||
target_current_linear_velocity.y = y
|
||||
|
||||
|
||||
func set_target_rotation_velocity(rotational_velocity: float) -> void:
|
||||
target_current_rotation_velocity = rotational_velocity
|
||||
target_current_linear_velocity.y = y
|
||||
|
|
@ -1,10 +1,30 @@
|
|||
extends SteeringBehaviour2D
|
||||
class_name BlendedBehaviour2D
|
||||
"""
|
||||
A behaviour that runs through all of its children and blends all motions together by a defined weight.
|
||||
|
||||
The blended behaviour will first calculate a child's motion, will multiply iy by its weight, then add it
|
||||
to the motion buffer before returning it. It will be clamped to the controller's maximums.
|
||||
The weights are not relative to 1, and instead act as a multiplier on the strength of the behaviour.
|
||||
|
||||
Notes
|
||||
-----
|
||||
More complex behaviours can be built with a blended behaviour than an indivudal one without writing
|
||||
a complex, custom behaviour, but it is more costly to run since all children will be evaluated. Figuring
|
||||
out the amount of weight to give each behaviour can also be a non-trivial, manual problem.
|
||||
|
||||
It can also lead to conflicts of force which may cause unusual behaviour.
|
||||
|
||||
The general workflow should be to have a priority steering with a set of blended behaviours, based on need.
|
||||
"""
|
||||
|
||||
export(float) var weight: float = 1
|
||||
|
||||
var internal_motion: = SteeringMotion2D.new()
|
||||
var _internal_motion: = SteeringMotion2D.new()
|
||||
|
||||
"""
|
||||
Returns the steering motion with a blend of all of the behaviour's children, capped by the controller's maximums.
|
||||
"""
|
||||
func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
motion.zero()
|
||||
|
||||
|
|
@ -13,9 +33,9 @@ func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
|||
continue
|
||||
|
||||
var steering: = child as SteeringBehaviour2D
|
||||
steering.calculate_steering(internal_motion)
|
||||
motion.linear_motion += (internal_motion.linear_motion * weight)
|
||||
motion.rotational_motion += (internal_motion.rotational_motion * weight)
|
||||
steering.calculate_steering(_internal_motion)
|
||||
motion.linear_motion += (_internal_motion.linear_motion * weight)
|
||||
motion.rotational_motion += (_internal_motion.rotational_motion * weight)
|
||||
|
||||
motion.linear_motion.clamped(controller.max_linear_acceleration)
|
||||
motion.rotational_motion = min(controller.max_rotational_acceleration, motion.rotational_motion)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
extends SteeringBehaviour2D
|
||||
class_name AlignBehaviour2D
|
||||
"""
|
||||
A behaviour that tries to rotate itself to match the target's own rotation.
|
||||
|
||||
The `alignment_tolerance` is there to prevent flicker, so as to not constantly overshoot
|
||||
and start rotating back the other way. The `deceleration_radius` is the interval of degrees
|
||||
to begin slowing down the act of rotating, and the `time_to_target` is the amount of time
|
||||
in a fixed time scale to weight the amount of rotation by. It should be a small value, and
|
||||
defaults to 0.1.
|
||||
|
||||
The only target supported is Node2D and should be fed in ahead of time.
|
||||
"""
|
||||
|
||||
export(float) var deceleration_radius: float
|
||||
export(float) var alignment_tolerance: float
|
||||
|
|
@ -7,12 +18,20 @@ export(float) var time_to_target: float = 0.1
|
|||
|
||||
var target: Node2D
|
||||
|
||||
"""
|
||||
Returns a steering motion that has no linear acceleration but a rotational acceleration that will
|
||||
match its own to its target's orientation.
|
||||
"""
|
||||
func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
if target == null:
|
||||
return motion.zero()
|
||||
return _align(motion, target.rotation)
|
||||
|
||||
|
||||
"""
|
||||
The internal function that calculates the alignment based on the radians passed into the function.
|
||||
Returns the steering motion with the rotational acceleration that will match its own to the rotation value.
|
||||
"""
|
||||
func _align(motion: SteeringMotion2D, desired_rotation: float) -> SteeringMotion2D:
|
||||
var rotation_size: = abs(desired_rotation - steerable().rotation)
|
||||
var alignment_tolerance_rad: = deg2rad(alignment_tolerance)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
extends SteeringBehaviour2D
|
||||
class_name ArriveBehaviour2D
|
||||
"""
|
||||
A behaviour that aims to arrive at where the target is, smoothly and within an acceptable distance threshold.
|
||||
|
||||
`arrival_tolerance` is the distance away from the target that will be good enough to stop moving.
|
||||
`deceleration_radius` is the distance away from the target that the AI should begin weighing down its acceleration
|
||||
and slow down.
|
||||
`time_to_target` is a fixed time scale value of time by which it should weigh its velocity by to arrive smoothly.
|
||||
This value should be small and defaults to 0.1.
|
||||
|
||||
Supported targets are Vector2 and Node2D.
|
||||
"""
|
||||
|
||||
export(float) var arrival_tolerance: float
|
||||
export(float) var deceleration_radius: float
|
||||
|
|
@ -7,6 +18,10 @@ export(float) var time_to_target: float = 0.1
|
|||
|
||||
var target
|
||||
|
||||
"""
|
||||
Returns the steering motion filled with a linear acceleration amount that will take it towards the target with a
|
||||
smooth deceleration, or zero if it is already inside of an arrival threshold.
|
||||
"""
|
||||
func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
if target == null:
|
||||
return motion.zero()
|
||||
|
|
@ -30,10 +45,9 @@ func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
|||
return motion
|
||||
|
||||
|
||||
func _arrive_vector(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
return motion.zero()
|
||||
|
||||
|
||||
"""
|
||||
Returns the target's position.
|
||||
"""
|
||||
func _to_target() -> Vector2:
|
||||
if target is Vector2:
|
||||
return target - steerable().position
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
extends InterceptBehaviour2D
|
||||
class_name EvadeBehaviour2D
|
||||
"""
|
||||
A behaviour that acts as the inverse of Intercept. It will move away from the predicted point where
|
||||
the target will be. See `InterceptBehaviour2D` for more information.
|
||||
|
||||
Supported targets are Vector2 and Node2D.
|
||||
"""
|
||||
|
||||
"""
|
||||
Returns a steering motion filled with a linear acceleration amount that will move it away from the
|
||||
point where the target will be in `max_prediction_time`.
|
||||
"""
|
||||
func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
._calculate_steering_internal(motion)
|
||||
motion.linear_motion *= -1
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
extends SeekBehaviour2D
|
||||
class_name FleeBehaviour2D
|
||||
"""
|
||||
A behaviour that acts like an inverse of Seek - it will constantly accelerate away from the target.
|
||||
|
||||
Supported targets are Node2D and Vector2.
|
||||
"""
|
||||
|
||||
func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
._calculate_steering_internal(motion)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,24 @@
|
|||
extends SteeringBehaviour2D
|
||||
class_name InterceptBehaviour2D
|
||||
"""
|
||||
A behaviour that will have the target attempt to predict where the target is headed, and point
|
||||
its acceleration towards that point.
|
||||
|
||||
`max_prediction_time` is how far into the future in fixed time scale the AI will use to predict
|
||||
the heading of the target.
|
||||
|
||||
Supported targets are Node2D and Vector2.
|
||||
"""
|
||||
|
||||
export(float) var max_prediction_time: float
|
||||
|
||||
var target
|
||||
|
||||
"""
|
||||
Returns the steering motion filled with a linear acceleration amount that will send it on an
|
||||
intercept course with wherever the target is headed so that its arrival will cross with the
|
||||
target's.
|
||||
"""
|
||||
func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
if target == null:
|
||||
return motion.zero()
|
||||
|
|
@ -27,6 +41,9 @@ func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
|||
return motion
|
||||
|
||||
|
||||
"""
|
||||
Returns the target's position.
|
||||
"""
|
||||
func _target() -> Vector2:
|
||||
if target is Vector2:
|
||||
return target
|
||||
|
|
|
|||
|
|
@ -1,8 +1,17 @@
|
|||
extends SteeringBehaviour2D
|
||||
class_name SeekBehaviour2D
|
||||
"""
|
||||
A behaviour that will constantly accelerate towards the target as fast as it can accelerate.
|
||||
|
||||
Supported targets are Node2D and Vector2.
|
||||
"""
|
||||
|
||||
var target
|
||||
|
||||
"""
|
||||
Returns the steering motion filled with a linear acceleration amount that will make it go to where
|
||||
the target currently is, as fast as it is allowed by the controller.
|
||||
"""
|
||||
func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
if target == null:
|
||||
return motion.zero()
|
||||
|
|
@ -12,6 +21,9 @@ func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
|||
return motion
|
||||
|
||||
|
||||
"""
|
||||
Returns the target's position.
|
||||
"""
|
||||
func _target() -> Vector2:
|
||||
if target is Vector2:
|
||||
return target
|
||||
|
|
|
|||
|
|
@ -1,20 +1,40 @@
|
|||
extends SteeringBehaviour2D
|
||||
class_name PrioritySteering2D
|
||||
"""
|
||||
An AI behaviour that runs through its children and stops as soon as one returns a non-zero motion.
|
||||
|
||||
Runs iteratively from top to bottom down its child-list and stops once one of them returns non-zero motion.
|
||||
Certain behaviours will always return an acceleration amount, while others may not - in many cases, you
|
||||
usually want to act on it (for instance, evading an obstacle to avoid impact.)
|
||||
"""
|
||||
|
||||
var last_selected_child_idx: int
|
||||
|
||||
"""
|
||||
Returns the child whose motion was non-zero. Returns null if no children produced an acceleration.
|
||||
"""
|
||||
func get_last_selected_child() -> SteeringBehaviour2D:
|
||||
if last_selected_child_idx >= 0:
|
||||
return get_child(last_selected_child_idx) as SteeringBehaviour2D
|
||||
return null
|
||||
|
||||
|
||||
"""
|
||||
Fills the steering motion object with the first non-zero calculation it can find. The priority goes from
|
||||
top to bottom.
|
||||
"""
|
||||
func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
last_selected_child_idx = -1
|
||||
var size: = get_child_count()
|
||||
|
||||
for i in range(0, size):
|
||||
last_selected_child_idx = i
|
||||
var child: = get_child(i) as SteeringBehaviour2D
|
||||
if child == null:
|
||||
continue
|
||||
|
||||
child.calculate_steering(motion)
|
||||
if motion.linear_motion.length_squared() != 0 or motion.rotational_motion != 0:
|
||||
if !motion.is_zero():
|
||||
last_selected_child_idx = i
|
||||
break
|
||||
|
||||
if size > 0:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
extends Node
|
||||
class_name SteeringBehaviour2D
|
||||
"""
|
||||
The base class for a behaviour in 2D space. Subclasses of this class should only
|
||||
override `_calculate_steering_internal`.
|
||||
|
||||
Calling `calculate_steering` on a class that extends this should fill the provided `SteeringMotion2D`
|
||||
with an amount of linear and rotational acceleration. It is up to the caller to then use that
|
||||
information to actually move the AI actor.
|
||||
"""
|
||||
|
||||
var enabled: = true
|
||||
var controller: BehaviourController2D
|
||||
|
|
@ -8,6 +16,10 @@ func _ready() -> void:
|
|||
_get_controller(self)
|
||||
|
||||
|
||||
"""
|
||||
The public face of the steering calculation.
|
||||
Returns the motion with the linear and/or rotational acceleration filled in, based on the behaviour.
|
||||
"""
|
||||
func calculate_steering(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
if !enabled or controller == null or !controller.valid():
|
||||
return motion.zero()
|
||||
|
|
@ -15,16 +27,26 @@ func calculate_steering(motion: SteeringMotion2D) -> SteeringMotion2D:
|
|||
return _calculate_steering_internal(motion)
|
||||
|
||||
|
||||
"""
|
||||
Returns the actor upon which the behaviours are acting, to access their position in space.
|
||||
"""
|
||||
func steerable() -> Node2D:
|
||||
if controller == null:
|
||||
return null
|
||||
return controller.steerable
|
||||
|
||||
|
||||
"""
|
||||
The privade, overriderable face of the steering calculation.
|
||||
Returns the motion with the linear and/or rotational acceleration filled in, based on the behaviour.
|
||||
"""
|
||||
func _calculate_steering_internal(motion: SteeringMotion2D) -> SteeringMotion2D:
|
||||
return motion
|
||||
|
||||
|
||||
"""
|
||||
Recursively searches its parent to find the behaviour controller that will feed it information.
|
||||
"""
|
||||
func _get_controller(current_node: Node) -> void:
|
||||
if current_node is BehaviourController2D:
|
||||
controller = current_node
|
||||
|
|
|
|||
|
|
@ -1,22 +1,37 @@
|
|||
extends Reference
|
||||
class_name SteeringMotion2D
|
||||
"""
|
||||
A container that holds a linear and rotational amount of acceleration generated by steering behaviours.
|
||||
"""
|
||||
|
||||
var linear_motion: = Vector2()
|
||||
var rotational_motion: float = 0
|
||||
|
||||
"""
|
||||
Sets the linear and rotational acceleration amounts to zero
|
||||
"""
|
||||
func zero() -> void:
|
||||
linear_motion.x = 0
|
||||
linear_motion.y = 0
|
||||
rotational_motion = 0
|
||||
|
||||
|
||||
"""
|
||||
Returns true if both the linear and rotational acceleration amounts are zero
|
||||
"""
|
||||
func is_zero() -> bool:
|
||||
return linear_is_zero() and rotational_is_zero()
|
||||
|
||||
|
||||
"""
|
||||
Returns true if the linear acceleration amount is zero
|
||||
"""
|
||||
func linear_is_zero() -> bool:
|
||||
return linear_motion.x == 0 and linear_motion.y == 0
|
||||
|
||||
|
||||
"""
|
||||
Returns true if the rotational acceleration amount is zero
|
||||
"""
|
||||
func rotational_is_zero() -> bool:
|
||||
return rotational_motion == 0
|
||||
Loading…
Add table
Add a link
Reference in a new issue