Adds enemies
This commit is contained in:
parent
cf8ba8bacb
commit
76e5b1927f
324 changed files with 28447 additions and 106 deletions
69
demo/ai/tasks/arrive_pos.gd
Normal file
69
demo/ai/tasks/arrive_pos.gd
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#*
|
||||
#* arrive_pos.gd
|
||||
#* =============================================================================
|
||||
#* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
|
||||
#*
|
||||
#* Use of this source code is governed by an MIT-style
|
||||
#* license that can be found in the LICENSE file or at
|
||||
#* https://opensource.org/licenses/MIT.
|
||||
#* =============================================================================
|
||||
#*
|
||||
@tool
|
||||
extends BTAction
|
||||
## Moves the agent to the specified position, favoring horizontal movement. [br]
|
||||
## Returns [code]SUCCESS[/code] when close to the target position (see [member tolerance]);
|
||||
## otherwise returns [code]RUNNING[/code].
|
||||
|
||||
## Blackboard variable that stores the target position (Vector2)
|
||||
@export var target_position_var := &"pos"
|
||||
|
||||
## Variable that stores desired speed (float)
|
||||
@export var speed_var := &"speed"
|
||||
|
||||
## How close should the agent be to the target position to return SUCCESS.
|
||||
@export var tolerance := 50.0
|
||||
|
||||
## Specifies the node to avoid (valid Node2D is expected).
|
||||
## If not empty, agent will circle around the node while moving into position.
|
||||
@export var avoid_var: StringName
|
||||
|
||||
|
||||
func _generate_name() -> String:
|
||||
return "Arrive pos: %s%s" % [
|
||||
LimboUtility.decorate_var(target_position_var),
|
||||
"" if avoid_var.is_empty() else " avoid: " + LimboUtility.decorate_var(avoid_var)
|
||||
]
|
||||
|
||||
|
||||
func _tick(_delta: float) -> Status:
|
||||
var target_pos: Vector2 = blackboard.get_var(target_position_var, Vector2.ZERO)
|
||||
if target_pos.distance_to(agent.global_position) < tolerance:
|
||||
return SUCCESS
|
||||
|
||||
var speed: float = blackboard.get_var(speed_var, 10.0)
|
||||
var dist: float = absf(agent.global_position.x - target_pos.x)
|
||||
var dir: Vector2 = agent.global_position.direction_to(target_pos)
|
||||
|
||||
# Prefer horizontal movement:
|
||||
var vertical_factor: float = remap(dist, 200.0, 500.0, 1.0, 0.0)
|
||||
vertical_factor = clampf(vertical_factor, 0.0, 1.0)
|
||||
dir.y *= vertical_factor
|
||||
|
||||
# Avoid the node specified by `avoid_var`.
|
||||
# I.e., if `avoid_var` is set, agent will circle around that node while moving into position.
|
||||
if not avoid_var.is_empty():
|
||||
var avoid_node: Node2D = blackboard.get_var(avoid_var)
|
||||
if is_instance_valid(avoid_node):
|
||||
var distance_vector: Vector2 = avoid_node.global_position - agent.global_position
|
||||
if dir.dot(distance_vector) > 0.0:
|
||||
var side := dir.rotated(PI * 0.5).normalized()
|
||||
# The closer we are to the avoid target, the stronger is the avoidance.
|
||||
var strength: float = remap(distance_vector.length(), 200.0, 400.0, 1.0, 0.0)
|
||||
strength = clampf(strength, 0.0, 1.0)
|
||||
var avoidance := side * signf(-side.dot(distance_vector)) * strength
|
||||
dir += avoidance
|
||||
|
||||
var desired_velocity: Vector2 = dir.normalized() * speed
|
||||
agent.move(desired_velocity)
|
||||
agent.update_facing()
|
||||
return RUNNING
|
||||
39
demo/ai/tasks/back_away.gd
Normal file
39
demo/ai/tasks/back_away.gd
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#*
|
||||
#* back_away.gd
|
||||
#* =============================================================================
|
||||
#* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
|
||||
#*
|
||||
#* Use of this source code is governed by an MIT-style
|
||||
#* license that can be found in the LICENSE file or at
|
||||
#* https://opensource.org/licenses/MIT.
|
||||
#* =============================================================================
|
||||
#*
|
||||
@tool
|
||||
extends BTAction
|
||||
## Moves the agent in the opposite direction of its current facing. [br]
|
||||
## Returns [code]RUNNING[/code] always.
|
||||
|
||||
## Blackboard variable that stores desired speed.
|
||||
@export var speed_var: StringName = &"speed"
|
||||
|
||||
## How much can we deviate from the "away" direction (in radians).
|
||||
@export var max_angle_deviation: float = 0.7
|
||||
|
||||
var _dir: Vector2
|
||||
var _desired_velocity: Vector2
|
||||
|
||||
|
||||
# Called each time this task is entered.
|
||||
func _enter() -> void:
|
||||
# Determine "away" direction and desired velocity
|
||||
_dir = Vector2.LEFT * agent.get_facing()
|
||||
var speed: float = blackboard.get_var(speed_var, 200.0)
|
||||
var rand_angle = randf_range(-max_angle_deviation, max_angle_deviation)
|
||||
_desired_velocity = _dir.rotated(rand_angle) * speed
|
||||
|
||||
|
||||
# Called each time this task is ticked (aka executed).
|
||||
func _tick(_delta: float) -> Status:
|
||||
agent.move(_desired_velocity)
|
||||
agent.face_dir(-signf(_dir.x))
|
||||
return RUNNING
|
||||
32
demo/ai/tasks/face_target.gd
Normal file
32
demo/ai/tasks/face_target.gd
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#*
|
||||
#* face_target.gd
|
||||
#* =============================================================================
|
||||
#* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
|
||||
#*
|
||||
#* Use of this source code is governed by an MIT-style
|
||||
#* license that can be found in the LICENSE file or at
|
||||
#* https://opensource.org/licenses/MIT.
|
||||
#* =============================================================================
|
||||
#*
|
||||
@tool
|
||||
extends BTAction
|
||||
## Flips the agent to face the target, returning [code]SUCCESS[/code]. [br]
|
||||
## Returns [code]FAILURE[/code] if [member target_var] is not a valid [Node2D] instance.
|
||||
|
||||
## Blackboard variable that stores our target (expecting Node2D).
|
||||
@export var target_var: StringName = &"target"
|
||||
|
||||
# Display a customized name (requires @tool).
|
||||
func _generate_name() -> String:
|
||||
return "FaceTarget " + LimboUtility.decorate_var(target_var)
|
||||
|
||||
|
||||
# Called each time this task is ticked (aka executed).
|
||||
func _tick(_delta: float) -> Status:
|
||||
var target: Node2D = blackboard.get_var(target_var)
|
||||
if not is_instance_valid(target):
|
||||
return FAILURE
|
||||
var dir: float = target.global_position.x - agent.global_position.x
|
||||
agent.velocity = Vector2.ZERO
|
||||
agent.face_dir(dir)
|
||||
return SUCCESS
|
||||
34
demo/ai/tasks/get_first_in_group.gd
Normal file
34
demo/ai/tasks/get_first_in_group.gd
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#*
|
||||
#* get_first_in_group.gd
|
||||
#* =============================================================================
|
||||
#* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
|
||||
#*
|
||||
#* Use of this source code is governed by an MIT-style
|
||||
#* license that can be found in the LICENSE file or at
|
||||
#* https://opensource.org/licenses/MIT.
|
||||
#* =============================================================================
|
||||
#*
|
||||
@tool
|
||||
extends BTAction
|
||||
## Stores the first node in the [member group] on the blackboard, returning [code]SUCCESS[/code]. [br]
|
||||
## Returns [code]FAILURE[/code] if the group contains 0 nodes.
|
||||
|
||||
## Name of the SceneTree group.
|
||||
@export var group: StringName
|
||||
|
||||
## Blackboard variable in which the task will store the acquired node.
|
||||
@export var output_var: StringName = &"target"
|
||||
|
||||
|
||||
func _generate_name() -> String:
|
||||
return "GetFirstNodeInGroup \"%s\" ➜%s" % [
|
||||
group,
|
||||
LimboUtility.decorate_var(output_var)
|
||||
]
|
||||
|
||||
func _tick(_delta: float) -> Status:
|
||||
var nodes: Array[Node] = agent.get_tree().get_nodes_in_group(group)
|
||||
if nodes.size() == 0:
|
||||
return FAILURE
|
||||
blackboard.set_var(output_var, nodes[0])
|
||||
return SUCCESS
|
||||
54
demo/ai/tasks/in_range.gd
Normal file
54
demo/ai/tasks/in_range.gd
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#*
|
||||
#* in_range.gd
|
||||
#* =============================================================================
|
||||
#* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
|
||||
#*
|
||||
#* Use of this source code is governed by an MIT-style
|
||||
#* license that can be found in the LICENSE file or at
|
||||
#* https://opensource.org/licenses/MIT.
|
||||
#* =============================================================================
|
||||
#*
|
||||
@tool
|
||||
extends BTCondition
|
||||
## InRange condition checks if the agent is within a range of target,
|
||||
## defined by [member distance_min] and [member distance_max]. [br]
|
||||
## Returns [code]SUCCESS[/code] if the agent is within the given range;
|
||||
## otherwise, returns [code]FAILURE[/code].
|
||||
|
||||
## Minimum distance to target.
|
||||
@export var distance_min: float
|
||||
|
||||
## Maximum distance to target.
|
||||
@export var distance_max: float
|
||||
|
||||
## Blackboard variable that holds the target (expecting Node2D).
|
||||
@export var target_var: StringName = &"target"
|
||||
|
||||
var _min_distance_squared: float
|
||||
var _max_distance_squared: float
|
||||
|
||||
|
||||
# Called to generate a display name for the task.
|
||||
func _generate_name() -> String:
|
||||
return "InRange (%d, %d) of %s" % [distance_min, distance_max,
|
||||
LimboUtility.decorate_var(target_var)]
|
||||
|
||||
|
||||
# Called to initialize the task.
|
||||
func _setup() -> void:
|
||||
## Small performace optimization
|
||||
_min_distance_squared = distance_min * distance_min
|
||||
_max_distance_squared = distance_max * distance_max
|
||||
|
||||
|
||||
# Called when the task is executed.
|
||||
func _tick(_delta: float) -> Status:
|
||||
var target: Node2D = blackboard.get_var(target_var, null)
|
||||
if not is_instance_valid(target):
|
||||
return FAILURE
|
||||
|
||||
var dist_sq: float = agent.global_position.distance_squared_to(target.global_position)
|
||||
if dist_sq >= _min_distance_squared and dist_sq <= _max_distance_squared:
|
||||
return SUCCESS
|
||||
else:
|
||||
return FAILURE
|
||||
35
demo/ai/tasks/is_aligned_with_target.gd
Normal file
35
demo/ai/tasks/is_aligned_with_target.gd
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#*
|
||||
#* is_aligned_with_target.gd
|
||||
#* =============================================================================
|
||||
#* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
|
||||
#*
|
||||
#* Use of this source code is governed by an MIT-style
|
||||
#* license that can be found in the LICENSE file or at
|
||||
#* https://opensource.org/licenses/MIT.
|
||||
#* =============================================================================
|
||||
#*
|
||||
@tool
|
||||
extends BTCondition
|
||||
## Checks if the agent is horizontally aligned with the target. [br]
|
||||
## Returns [code]SUCCESS[/code] if the agent is horizontally aligned with the target.
|
||||
## Returns [code]FAILURE[/code] if not aligned or if target is not a valid node instance.
|
||||
|
||||
|
||||
@export var target_var: StringName = &"target"
|
||||
@export var tolerance: float = 30.0
|
||||
|
||||
|
||||
# Display a customized name (requires @tool).
|
||||
func _generate_name() -> String:
|
||||
return "IsAlignedWithTarget " + LimboUtility.decorate_var(target_var)
|
||||
|
||||
|
||||
# Called each time this task is ticked (aka executed).
|
||||
func _tick(_delta: float) -> Status:
|
||||
var target := blackboard.get_var(target_var) as Node2D
|
||||
if not is_instance_valid(target):
|
||||
return FAILURE
|
||||
var y_diff: float = absf(target.global_position.y - agent.global_position.y)
|
||||
if y_diff < tolerance:
|
||||
return SUCCESS
|
||||
return FAILURE
|
||||
39
demo/ai/tasks/move_forward.gd
Normal file
39
demo/ai/tasks/move_forward.gd
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#*
|
||||
#* move_forward.gd
|
||||
#* =============================================================================
|
||||
#* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
|
||||
#*
|
||||
#* Use of this source code is governed by an MIT-style
|
||||
#* license that can be found in the LICENSE file or at
|
||||
#* https://opensource.org/licenses/MIT.
|
||||
#* =============================================================================
|
||||
#*
|
||||
@tool
|
||||
extends BTAction
|
||||
## Applies velocity in the direction the agent is facing on each tick
|
||||
## until the [member duration] is exceeded. [br]
|
||||
## Returns [code]SUCCESS[/code] if the elapsed time exceeds [member duration]. [br]
|
||||
## Returns [code]RUNNING[/code] if the elapsed time does not exceed [member duration]. [br]
|
||||
|
||||
## Blackboard variable that stores desired speed.
|
||||
@export var speed_var: StringName = &"speed"
|
||||
|
||||
## How long to perform this task (in seconds).
|
||||
@export var duration: float = 0.1
|
||||
|
||||
# Display a customized name (requires @tool).
|
||||
func _generate_name() -> String:
|
||||
return "MoveForward speed: %s duration: %ss" % [
|
||||
LimboUtility.decorate_var(speed_var),
|
||||
duration]
|
||||
|
||||
# Called each time this task is ticked (aka executed).
|
||||
func _tick(_delta: float) -> Status:
|
||||
var facing: float = agent.get_facing()
|
||||
var speed: float = blackboard.get_var(speed_var, 100.0)
|
||||
var desired_velocity: Vector2 = Vector2.RIGHT * facing * speed
|
||||
agent.move(desired_velocity)
|
||||
agent.update_facing()
|
||||
if elapsed_time > duration:
|
||||
return SUCCESS
|
||||
return RUNNING
|
||||
79
demo/ai/tasks/pursue.gd
Normal file
79
demo/ai/tasks/pursue.gd
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#*
|
||||
#* pursue.gd
|
||||
#* =============================================================================
|
||||
#* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
|
||||
#*
|
||||
#* Use of this source code is governed by an MIT-style
|
||||
#* license that can be found in the LICENSE file or at
|
||||
#* https://opensource.org/licenses/MIT.
|
||||
#* =============================================================================
|
||||
#*
|
||||
@tool
|
||||
extends BTAction
|
||||
## Move towards the target until the agent is flanking it. [br]
|
||||
## Returns [code]RUNNING[/code] while moving towards the target but not yet at the desired position. [br]
|
||||
## Returns [code]SUCCESS[/code] when at the desired position relative to the target (flanking it). [br]
|
||||
## Returns [code]FAILURE[/code] if the target is not a valid [Node2D] instance. [br]
|
||||
|
||||
## How close should the agent be to the desired position to return SUCCESS.
|
||||
const TOLERANCE := 30.0
|
||||
|
||||
## Blackboard variable that stores our target (expecting Node2D).
|
||||
@export var target_var: StringName = &"target"
|
||||
|
||||
## Blackboard variable that stores desired speed.
|
||||
@export var speed_var: StringName = &"speed"
|
||||
|
||||
## Desired distance from target.
|
||||
@export var approach_distance: float = 100.0
|
||||
|
||||
var _waypoint: Vector2
|
||||
|
||||
|
||||
# Display a customized name (requires @tool).
|
||||
func _generate_name() -> String:
|
||||
return "Pursue %s" % [LimboUtility.decorate_var(target_var)]
|
||||
|
||||
|
||||
# Called each time this task is entered.
|
||||
func _enter() -> void:
|
||||
var target: Node2D = blackboard.get_var(target_var, null)
|
||||
if is_instance_valid(target):
|
||||
# Movement is performed in smaller steps.
|
||||
# For each step, we select a new waypoint.
|
||||
_select_new_waypoint(_get_desired_position(target))
|
||||
|
||||
|
||||
# Called each time this task is ticked (aka executed).
|
||||
func _tick(_delta: float) -> Status:
|
||||
var target: Node2D = blackboard.get_var(target_var, null)
|
||||
if not is_instance_valid(target):
|
||||
return FAILURE
|
||||
|
||||
var desired_pos: Vector2 = _get_desired_position(target)
|
||||
if agent.global_position.distance_to(desired_pos) < TOLERANCE:
|
||||
return SUCCESS
|
||||
|
||||
if agent.global_position.distance_to(_waypoint) < TOLERANCE:
|
||||
_select_new_waypoint(desired_pos)
|
||||
|
||||
var speed: float = blackboard.get_var(speed_var, 200.0)
|
||||
var desired_velocity: Vector2 = agent.global_position.direction_to(_waypoint) * speed
|
||||
agent.move(desired_velocity)
|
||||
agent.update_facing()
|
||||
return RUNNING
|
||||
|
||||
|
||||
## Get the closest flanking position to target.
|
||||
func _get_desired_position(target: Node2D) -> Vector2:
|
||||
var side: float = signf(agent.global_position.x - target.global_position.x)
|
||||
var desired_pos: Vector2 = target.global_position
|
||||
desired_pos.x += approach_distance * side
|
||||
return desired_pos
|
||||
|
||||
|
||||
## Select an intermidiate waypoint towards the desired position.
|
||||
func _select_new_waypoint(desired_position: Vector2) -> void:
|
||||
var distance_vector: Vector2 = desired_position - agent.global_position
|
||||
var angle_variation: float = randf_range(-0.2, 0.2)
|
||||
_waypoint = agent.global_position + distance_vector.limit_length(150.0).rotated(angle_variation)
|
||||
74
demo/ai/tasks/select_flanking_pos.gd
Normal file
74
demo/ai/tasks/select_flanking_pos.gd
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#*
|
||||
#* select_flanking_pos.gd
|
||||
#* =============================================================================
|
||||
#* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
|
||||
#*
|
||||
#* Use of this source code is governed by an MIT-style
|
||||
#* license that can be found in the LICENSE file or at
|
||||
#* https://opensource.org/licenses/MIT.
|
||||
#* =============================================================================
|
||||
#*
|
||||
@tool
|
||||
extends BTAction
|
||||
## Selects a position on the target's side and stores it on the
|
||||
## blackboard, returning [code]SUCCESS[/code]. [br]
|
||||
## Returns [code]FAILURE[/code] if the target is not valid.
|
||||
|
||||
enum AgentSide {
|
||||
CLOSEST,
|
||||
FARTHEST,
|
||||
BACK,
|
||||
FRONT,
|
||||
}
|
||||
|
||||
## Blackboard variable that holds current target (should be a Node2D instance).
|
||||
@export var target_var: StringName = &"target"
|
||||
|
||||
## Which agent's side should we flank?
|
||||
@export var flank_side: AgentSide = AgentSide.CLOSEST
|
||||
|
||||
## Minimum range relative to the target.
|
||||
@export var range_min: int = 300
|
||||
|
||||
## Maximum range relative to the target.
|
||||
@export var range_max: int = 400
|
||||
|
||||
## Blackboard variable that will be used to store selected position.
|
||||
@export var position_var: StringName = &"pos"
|
||||
|
||||
|
||||
# Display a customized name (requires @tool).
|
||||
func _generate_name() -> String:
|
||||
return "SelectFlankingPos target: %s range: [%s, %s] side: %s ➜%s" % [
|
||||
LimboUtility.decorate_var(target_var),
|
||||
range_min,
|
||||
range_max,
|
||||
AgentSide.keys()[flank_side],
|
||||
LimboUtility.decorate_var(position_var)]
|
||||
|
||||
|
||||
# Called each time this task is ticked (aka executed).
|
||||
func _tick(_delta: float) -> Status:
|
||||
var target := blackboard.get_var(target_var) as Node2D
|
||||
if not is_instance_valid(target):
|
||||
return FAILURE
|
||||
|
||||
var dir: float # 1.0 is right, -1.0 is left (relative to target)
|
||||
match flank_side:
|
||||
AgentSide.FARTHEST:
|
||||
dir = signf(target.global_position.x - agent.global_position.x)
|
||||
AgentSide.CLOSEST :
|
||||
dir = -signf(target.global_position.x - agent.global_position.x)
|
||||
AgentSide.BACK:
|
||||
dir = -target.get_facing()
|
||||
AgentSide.FRONT:
|
||||
dir = target.get_facing()
|
||||
|
||||
var flank_pos: Vector2
|
||||
var offset := Vector2(dir * randf_range(range_min, range_max), 0.0)
|
||||
flank_pos = target.global_position + offset
|
||||
if not agent.is_good_position(flank_pos):
|
||||
# Choose the opposite side if the preferred side is not good (i.e., inside a collision shape).
|
||||
flank_pos = target.global_position - offset
|
||||
blackboard.set_var(position_var, flank_pos)
|
||||
return SUCCESS
|
||||
34
demo/ai/tasks/select_random_nearby_pos.gd
Normal file
34
demo/ai/tasks/select_random_nearby_pos.gd
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
@tool
|
||||
extends BTAction
|
||||
## Selects a random position nearby within the specified range and stores it on the blackboard. [br]
|
||||
## Returns [code]SUCCESS[/code].
|
||||
|
||||
## Minimum distance to the desired position.
|
||||
@export var range_min: float = 300.0
|
||||
|
||||
## Maximum distance to the desired position.
|
||||
@export var range_max: float = 500.0
|
||||
|
||||
## Blackboard variable that will be used to store the desired position.
|
||||
@export var position_var: StringName = &"pos"
|
||||
|
||||
|
||||
# Display a customized name (requires @tool).
|
||||
func _generate_name() -> String:
|
||||
return "SelectRandomNearbyPos range: [%s, %s] ➜%s" % [
|
||||
range_min, range_max,
|
||||
LimboUtility.decorate_var(position_var)]
|
||||
|
||||
|
||||
# Called each time this task is ticked (aka executed).
|
||||
func _tick(_delta: float) -> Status:
|
||||
var pos: Vector2
|
||||
var is_good_position: bool = false
|
||||
while not is_good_position:
|
||||
# Randomize until we find a good position (good position == not outside the arena).
|
||||
var angle: float = randf() * TAU
|
||||
var rand_distance: float = randf_range(range_min, range_max)
|
||||
pos = agent.global_position + Vector2(sin(angle), cos(angle)) * rand_distance
|
||||
is_good_position = agent.is_good_position(pos)
|
||||
blackboard.set_var(position_var, pos)
|
||||
return SUCCESS
|
||||
Loading…
Add table
Add a link
Reference in a new issue