Backported from godot4: Rework Navigation Avoidance

Rework Navigation Avoidance.
- smix8
a6ac305f96
This commit is contained in:
Relintai 2023-06-10 20:58:49 +02:00
parent 8b18898609
commit f058c87868
132 changed files with 8830 additions and 1448 deletions

View File

@ -184,7 +184,8 @@ opts.Add(BoolVariable("builtin_opus", "Use the built-in Opus library", True))
opts.Add(BoolVariable("builtin_pcre2", "Use the built-in PCRE2 library", True))
opts.Add(BoolVariable("builtin_pcre2_with_jit", "Use JIT compiler for the built-in PCRE2 library", True))
opts.Add(BoolVariable("builtin_recast", "Use the built-in Recast library", True))
opts.Add(BoolVariable("builtin_rvo2", "Use the built-in RVO2 library", True))
opts.Add(BoolVariable("builtin_rvo2_2d", "Use the built-in RVO2 2D library", True))
opts.Add(BoolVariable("builtin_rvo2_3d", "Use the built-in RVO2 3D library", True))
opts.Add(BoolVariable("builtin_squish", "Use the built-in squish library", True))
opts.Add(BoolVariable("builtin_zlib", "Use the built-in zlib library", True))
opts.Add(BoolVariable("builtin_zstd", "Use the built-in Zstd library", True))

View File

@ -602,6 +602,7 @@ void register_global_constants() {
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_RENDER);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_PHYSICS);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_NAVIGATION);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_AVOIDANCE);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_FILE);
BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_DIR);

View File

@ -70,6 +70,7 @@ enum PropertyHint {
PROPERTY_HINT_LAYERS_3D_RENDER,
PROPERTY_HINT_LAYERS_3D_PHYSICS,
PROPERTY_HINT_LAYERS_3D_NAVIGATION,
PROPERTY_HINT_LAYERS_AVOIDANCE,
PROPERTY_HINT_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
PROPERTY_HINT_DIR, ///< a directory path must be passed
PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"

View File

@ -1449,10 +1449,6 @@
Hints that an integer, float or string property is an enumerated value to pick in a list specified via a hint string.
The hint string is a comma separated list of names such as [code]"Hello,Something,Else"[/code]. Whitespaces are [b]not[/b] removed from either end of a name. For integer and float properties, the first name in the list has value 0, the next 1, and so on. Explicit values can also be specified by appending [code]:integer[/code] to the name, e.g. [code]"Zero,One,Three:3,Four,Six:6"[/code].
</constant>
<constant name="PROPERTY_HINT_ENUM_SUGGESTION" value="39" enum="PropertyHint">
Hints that a string property can be an enumerated value to pick in a list specified via a hint string such as [code]"Hello,Something,Else"[/code].
Unlike [constant PROPERTY_HINT_ENUM] a property with this hint still accepts arbitrary values and can be empty. The list of values serves to suggest possible values.
</constant>
<constant name="PROPERTY_HINT_EXP_EASING" value="4" enum="PropertyHint">
Hints that a float property should be edited via an exponential easing function. The hint string can include [code]"attenuation"[/code] to flip the curve horizontally and/or [code]"inout"[/code] to also include in/out easing.
</constant>
@ -1486,39 +1482,42 @@
<constant name="PROPERTY_HINT_LAYERS_3D_NAVIGATION" value="14" enum="PropertyHint">
Hints that an integer property is a bitmask using the optionally named 2D navigation layers.
</constant>
<constant name="PROPERTY_HINT_FILE" value="15" enum="PropertyHint">
<constant name="PROPERTY_HINT_LAYERS_AVOIDANCE" value="15" enum="PropertyHint">
Hints that an integer property is a bitmask using the optionally named avoidance layers.
</constant>
<constant name="PROPERTY_HINT_FILE" value="16" enum="PropertyHint">
Hints that a string property is a path to a file. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code].
</constant>
<constant name="PROPERTY_HINT_DIR" value="16" enum="PropertyHint">
<constant name="PROPERTY_HINT_DIR" value="17" enum="PropertyHint">
Hints that a string property is a path to a directory. Editing it will show a file dialog for picking the path.
</constant>
<constant name="PROPERTY_HINT_GLOBAL_FILE" value="17" enum="PropertyHint">
<constant name="PROPERTY_HINT_GLOBAL_FILE" value="18" enum="PropertyHint">
Hints that a string property is an absolute path to a file outside the project folder. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code].
</constant>
<constant name="PROPERTY_HINT_GLOBAL_DIR" value="18" enum="PropertyHint">
<constant name="PROPERTY_HINT_GLOBAL_DIR" value="19" enum="PropertyHint">
Hints that a string property is an absolute path to a directory outside the project folder. Editing it will show a file dialog for picking the path.
</constant>
<constant name="PROPERTY_HINT_RESOURCE_TYPE" value="19" enum="PropertyHint">
<constant name="PROPERTY_HINT_RESOURCE_TYPE" value="20" enum="PropertyHint">
Hints that a property is an instance of a [Resource]-derived type, optionally specified via the hint string (e.g. [code]"Texture"[/code]). Editing it will show a popup menu of valid resource types to instantiate.
</constant>
<constant name="PROPERTY_HINT_MULTILINE_TEXT" value="20" enum="PropertyHint">
<constant name="PROPERTY_HINT_MULTILINE_TEXT" value="21" enum="PropertyHint">
Hints that a string property is text with line breaks. Editing it will show a text input field where line breaks can be typed.
</constant>
<constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="21" enum="PropertyHint">
<constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="22" enum="PropertyHint">
Hints that a string property should have a placeholder text visible on its input field, whenever the property is empty. The hint string is the placeholder text to use.
</constant>
<constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="22" enum="PropertyHint">
<constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="23" enum="PropertyHint">
Hints that a color property should be edited without changing its alpha component, i.e. only R, G and B channels are edited.
</constant>
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="23" enum="PropertyHint">
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="24" enum="PropertyHint">
Hints that an image is compressed using lossy compression.
</constant>
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="24" enum="PropertyHint">
<constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="25" enum="PropertyHint">
Hints that an image is compressed using lossless compression.
</constant>
<constant name="PROPERTY_HINT_OBJECT_ID" value="25" enum="PropertyHint">
<constant name="PROPERTY_HINT_OBJECT_ID" value="26" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_TYPE_STRING" value="26" enum="PropertyHint">
<constant name="PROPERTY_HINT_TYPE_STRING" value="27" enum="PropertyHint">
Hint that a property represents a particular type. If a property is [constant TYPE_STRING], allows to set a type from the create dialog. If you need to create an [Array] to contain elements of a specific type, the [code]hint_string[/code] must encode nested types using [code]":"[/code] and [code]"/"[/code] for specifying [Resource] types. For instance:
[codeblock]
hint_string = "%s:" % [TYPE_INT] # Array of inteters.
@ -1528,34 +1527,38 @@
[/codeblock]
[b]Note:[/b] The final colon is required to specify for properly detecting built-in types.
</constant>
<constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="27" enum="PropertyHint">
<constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="28" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_METHOD_OF_VARIANT_TYPE" value="28" enum="PropertyHint">
<constant name="PROPERTY_HINT_METHOD_OF_VARIANT_TYPE" value="29" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_METHOD_OF_BASE_TYPE" value="29" enum="PropertyHint">
<constant name="PROPERTY_HINT_METHOD_OF_BASE_TYPE" value="30" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_METHOD_OF_INSTANCE" value="30" enum="PropertyHint">
<constant name="PROPERTY_HINT_METHOD_OF_INSTANCE" value="31" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_METHOD_OF_SCRIPT" value="31" enum="PropertyHint">
<constant name="PROPERTY_HINT_METHOD_OF_SCRIPT" value="32" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE" value="32" enum="PropertyHint">
<constant name="PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE" value="33" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_PROPERTY_OF_BASE_TYPE" value="33" enum="PropertyHint">
<constant name="PROPERTY_HINT_PROPERTY_OF_BASE_TYPE" value="34" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_PROPERTY_OF_INSTANCE" value="34" enum="PropertyHint">
<constant name="PROPERTY_HINT_PROPERTY_OF_INSTANCE" value="35" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_PROPERTY_OF_SCRIPT" value="35" enum="PropertyHint">
<constant name="PROPERTY_HINT_PROPERTY_OF_SCRIPT" value="36" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="36" enum="PropertyHint">
<constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="37" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="37" enum="PropertyHint">
<constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="38" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_SAVE_FILE" value="38" enum="PropertyHint">
<constant name="PROPERTY_HINT_SAVE_FILE" value="39" enum="PropertyHint">
</constant>
<constant name="PROPERTY_HINT_LINK" value="40" enum="PropertyHint">
<constant name="PROPERTY_HINT_ENUM_SUGGESTION" value="40" enum="PropertyHint">
Hints that a string property can be an enumerated value to pick in a list specified via a hint string such as [code]"Hello,Something,Else"[/code].
Unlike [constant PROPERTY_HINT_ENUM] a property with this hint still accepts arbitrary values and can be empty. The list of values serves to suggest possible values.
</constant>
<constant name="PROPERTY_HINT_LINK" value="41" enum="PropertyHint">
Hints that a vector property should allow linking values (e.g. to edit both [code]x[/code] and [code]y[/code] together).
</constant>
<constant name="PROPERTY_HINT_MAX" value="41" enum="PropertyHint">
<constant name="PROPERTY_HINT_MAX" value="42" enum="PropertyHint">
</constant>
<constant name="PROPERTY_USAGE_STORAGE" value="1" enum="PropertyUsageFlags">
The property is serialized and saved in the scene file (default).

View File

@ -23,6 +23,13 @@
Creates the agent.
</description>
</method>
<method name="agent_get_avoidance_enabled" qualifiers="const">
<return type="bool" />
<param index="0" name="agent" type="RID" />
<description>
Return [code]true[/code] if the specified [param agent] uses avoidance.
</description>
</method>
<method name="agent_get_map" qualifiers="const">
<return type="RID" />
<argument index="0" name="agent" type="RID" />
@ -37,15 +44,48 @@
Returns [code]true[/code] if the map got changed the previous frame.
</description>
</method>
<method name="agent_set_callback">
<method name="agent_set_avoidance_callback">
<return type="void" />
<argument index="0" name="agent" type="RID" />
<argument index="1" name="object_id" type="int" />
<argument index="2" name="method" type="StringName" />
<argument index="3" name="userdata" type="Variant" default="null" />
<description>
Sets the callback [code]object_id[/code] and [code]method[/code] that gets called after each avoidance processing step for the [code]agent[/code]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
[b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [code]object_id[/code].
Sets the callback [Callable] that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
[b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_avoidance_callback] again with an empty [Callable].
</description>
</method>
<method name="agent_set_avoidance_enabled">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
If [param enabled] is [code]true[/code] the specified [param agent] uses avoidance.
</description>
</method>
<method name="agent_set_avoidance_layers">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="layers" type="int" />
<description>
Set the agent's [code]avoidance_layers[/code] bitmask.
</description>
</method>
<method name="agent_set_avoidance_mask">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="mask" type="int" />
<description>
Set the agent's [code]avoidance_mask[/code] bitmask.
</description>
</method>
<method name="agent_set_avoidance_priority">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="priority" type="float" />
<description>
Set the agent's [code]avoidance_priority[/code] with a [param priority] between 0.0 (lowest priority) to 1.0 (highest priority).
The specified [param agent] does not adjust the velocity for other agents that would match the [code]avoidance_mask[/code] but have a lower [code] avoidance_priority[/code]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
</description>
</method>
<method name="agent_set_map">
@ -96,20 +136,20 @@
Sets the radius of the agent.
</description>
</method>
<method name="agent_set_target_velocity">
<method name="agent_set_time_horizon_agents">
<return type="void" />
<argument index="0" name="agent" type="RID" />
<argument index="1" name="target_velocity" type="Vector2" />
<argument index="1" name="time_horizon" type="float" />
<description>
Sets the new target velocity.
The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</description>
</method>
<method name="agent_set_time_horizon">
<method name="agent_set_time_horizon_obstacles">
<return type="void" />
<argument index="0" name="agent" type="RID" />
<argument index="1" name="time" type="float" />
<argument index="1" name="time_horizon" type="float" />
<description>
The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to static avoidance obstacles. The larger this number, the sooner this agent will respond to the presence of static avoidance obstacles, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</description>
</method>
<method name="agent_set_velocity">
@ -117,7 +157,15 @@
<argument index="0" name="agent" type="RID" />
<argument index="1" name="velocity" type="Vector2" />
<description>
Sets the current velocity of the agent.
Sets [param velocity] as the new wanted velocity for the specified [param agent]. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position far away use [method agent_set_velocity_forced] instead to reset the internal velocity state.
</description>
</method>
<method name="agent_set_velocity_forced">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="velocity" type="Vector2" />
<description>
Replaces the internal velocity in the collision avoidance simulation with [param velocity] for the specified [param agent]. When an agent is teleported to a new position far away this function should be used in the same frame. If called frequently this function can get agents stuck.
</description>
</method>
<method name="free_rid">
@ -339,6 +387,13 @@
Returns all navigation link [RID]s that are currently assigned to the requested navigation [code]map[/code].
</description>
</method>
<method name="map_get_obstacles" qualifiers="const">
<return type="RID[]" />
<param index="0" name="map" type="RID" />
<description>
Returns all navigation obstacle [RID]s that are currently assigned to the requested navigation [code]map[/code].
</description>
</method>
<method name="map_get_path" qualifiers="const">
<return type="PoolVector2Array" />
<argument index="0" name="map" type="RID" />
@ -403,6 +458,50 @@
Set the map edge connection margin used to weld the compatible region edges.
</description>
</method>
<method name="obstacle_create">
<return type="RID" />
<description>
Creates a new navigation obstacle.
</description>
</method>
<method name="obstacle_get_map" qualifiers="const">
<return type="RID" />
<param index="0" name="obstacle" type="RID" />
<description>
Returns the navigation map [RID] the requested [param obstacle] is currently assigned to.
</description>
</method>
<method name="obstacle_set_avoidance_layers">
<return type="void" />
<param index="0" name="obstacle" type="RID" />
<param index="1" name="layers" type="int" />
<description>
</description>
</method>
<method name="obstacle_set_map">
<return type="void" />
<param index="0" name="obstacle" type="RID" />
<param index="1" name="map" type="RID" />
<description>
Sets the navigation map [RID] for the obstacle.
</description>
</method>
<method name="obstacle_set_position">
<return type="void" />
<param index="0" name="obstacle" type="RID" />
<param index="1" name="position" type="Vector2" />
<description>
Sets the position of the obstacle in world space.
</description>
</method>
<method name="obstacle_set_vertices">
<return type="void" />
<param index="0" name="obstacle" type="RID" />
<param index="1" name="vertices" type="PackedVector2Array" />
<description>
Sets the outline vertices for the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out.
</description>
</method>
<method name="query_path" qualifiers="const">
<return type="void" />
<param index="0" name="parameters" type="NavigationPathQueryParameters2D" />

View File

@ -5,8 +5,7 @@
</brief_description>
<description>
3D agent that is used in navigation to reach a position while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. By default this node will register to the default [World3D] navigation map. If this node is a child of a [Navigation] node it will register to the navigation map of the navigation node or the function [method set_navigation] can be used to set the navigation node directly. [NavigationAgent] is physics safe.
[b]Note:[/b] After setting [member target_position] it is required to use the [method get_next_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node.
[b]Note:[/b] By default, the expensive calculations for avoidance are done in a thread. In HTML5 exports without thread support, they will be done on the main thread, which can lead to performance issues.
[b]Note:[/b] After [member target_position] is set, the [method get_next_path_position] function must be used once every physics frame to update the internal path logic of the NavigationAgent. The returned position from this function should be used as the next movement position for the agent's parent node.
</description>
<tutorials>
</tutorials>
@ -17,6 +16,20 @@
Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate.
</description>
</method>
<method name="get_avoidance_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_avoidance_mask_value" qualifiers="const">
<return type="bool" />
<param index="0" name="mask_number" type="int" />
<description>
Returns whether or not the specified mask of the [member avoidance_mask] bitmask is enabled, given a [param mask_number] between 1 and 32.
</description>
</method>
<method name="get_current_navigation_result" qualifiers="const">
<return type="NavigationPathQueryResult3D" />
<description>
@ -26,16 +39,16 @@
<method name="get_final_position">
<return type="Vector3" />
<description>
Returns the reachable final position in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
Returns the reachable final position of the current navigation path in global coordinates. This position can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
</description>
</method>
<method name="get_nav_path" qualifiers="const">
<method name="get_current_navigation_path" qualifiers="const">
<return type="PoolVector3Array" />
<description>
Returns this agent's current path from start to finish in global coordinates. The path only updates when the target position is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_position] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic.
</description>
</method>
<method name="get_nav_path_index" qualifiers="const">
<method name="get_current_navigation_path_index" qualifiers="const">
<return type="int" />
<description>
Returns which index the agent is currently on in the navigation path's [PoolVector3Array].
@ -81,7 +94,7 @@
<method name="is_target_reachable">
<return type="bool" />
<description>
Returns [code]true[/code] if [member target_position] is reachable.
Returns true if [member target_position] is reachable. The target position is set using [member target_position].
</description>
</method>
<method name="is_target_reached" qualifiers="const">
@ -97,6 +110,22 @@
Sets the [Navigation] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation] node.
</description>
</method>
<method name="set_avoidance_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_avoidance_mask_value">
<return type="void" />
<param index="0" name="mask_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified mask in the [member avoidance_mask] bitmask, given a [param mask_number] between 1 and 32.
</description>
</method>
<method name="set_navigation_layer_value">
<return type="void" />
<argument index="0" name="layer_number" type="int" />
@ -112,20 +141,26 @@
Sets the [RID] of the navigation map this NavigationAgent node should use and also updates the [code]agent[/code] on the NavigationServer.
</description>
</method>
<method name="set_velocity">
<method name="set_velocity_forced">
<return type="void" />
<argument index="0" name="velocity" type="Vector3" />
<description>
Sends the passed in velocity to the collision avoidance algorithm. It will adjust the velocity to avoid collisions. Once the adjustment to the velocity is complete, it will emit the [signal velocity_computed] signal.
Replaces the internal velocity in the collision avoidance simulation with [param velocity]. When an agent is teleported to a new position this function should be used in the same frame. If called frequently this function can get agents stuck.
</description>
</method>
</methods>
<members>
<member name="agent_height_offset" type="float" setter="set_agent_height_offset" getter="get_agent_height_offset" default="0.0">
The NavigationAgent height offset is subtracted from the y-axis value of any vector path position for this NavigationAgent. The NavigationAgent height offset does not change or influence the navigation mesh or pathfinding query result. Additional navigation maps that use regions with navigation meshes that the developer baked with appropriate agent radius or height values are required to support different-sized agents.
</member>
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false">
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer]. When [method set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer3D]. When [member velocity] is set and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
</member>
<member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
A bitfield determining the avoidance layers for this NavigationAgent. Other agent's with a matching bit on the [member avoidance_mask] will avoid this agent.
</member>
<member name="avoidance_mask" type="int" setter="set_avoidance_mask" getter="get_avoidance_mask" default="1">
A bitfield determining what other avoidance agent's and obstacles this NavigationAgent will avoid when a bit matches at least one of their [member avoidance_layers].
</member>
<member name="avoidance_priority" type="float" setter="set_avoidance_priority" getter="get_avoidance_priority" default="1.0">
The agent does not adjust the velocity for other agents that would match the [member avoidance_mask] but have a lower [member avoidance_priority]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
</member>
<member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false">
If [code]true[/code] shows debug visuals for this agent.
@ -139,8 +174,8 @@
<member name="debug_use_custom" type="bool" setter="set_debug_use_custom" getter="get_debug_use_custom" default="false">
If [code]true[/code] uses the defined [member debug_path_custom_color] for this agent instead of global color.
</member>
<member name="ignore_y" type="bool" setter="set_ignore_y" getter="get_ignore_y" default="false">
Ignores collisions on the Y axis. Must be [code]true[/code] to move on a horizontal plane.
<member name="height" type="float" setter="set_height" getter="get_height" default="1.0">
The height of the avoidance agent. Agent's will ignore other agents or obstacles that are above or below their current position + height in 2D avoidance. Does nothing in 3D avoidance which uses radius spheres alone.
</member>
<member name="max_neighbors" type="int" setter="set_max_neighbors" getter="get_max_neighbors" default="0">
The maximum number of neighbors for the agent to consider.
@ -157,6 +192,9 @@
<member name="path_desired_distance" type="float" setter="set_path_desired_distance" getter="get_path_desired_distance" default="1.0">
The distance threshold before a path point is considered to be reached. This will allow an agent to not have to hit a path point on the path exactly, but in the area. If this value is set to high the NavigationAgent will skip points on the path which can lead to leaving the navigation mesh. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the next point on each physics frame update.
</member>
<member name="path_height_offset" type="float" setter="set_path_height_offset" getter="get_path_height_offset" default="0.0">
The height offset is subtracted from the y-axis value of any vector path position for this NavigationAgent. The NavigationAgent height offset does not change or influence the navigation mesh or pathfinding query result. Additional navigation maps that use regions with navigation meshes that the developer baked with appropriate agent radius or height values are required to support different-sized agents.
</member>
<member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="3.0">
The maximum distance the agent is allowed away from the ideal path to the final position. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path.
</member>
@ -171,10 +209,20 @@
The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update.
</member>
<member name="target_position" type="Vector3" setter="set_target_position" getter="get_target_position" default="Vector3( 0, 0, 0 )">
The user-defined target position. Setting this property will clear the current navigation path.
If set a new navigation path from the current agent position to the [member target_position] is requested from the NavigationServer.
</member>
<member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="0.0">
The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but the less freedom in choosing its velocities. Must be positive.
<member name="time_horizon_agents" type="float" setter="set_time_horizon_agents" getter="get_time_horizon_agents" default="1.0">
The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</member>
<member name="time_horizon_obstacles" type="float" setter="set_time_horizon_obstacles" getter="get_time_horizon_obstacles" default="0.0">
The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to static avoidance obstacles. The larger the number, the sooner the agent will respond to static avoidance obstacles, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</member>
<member name="use_3d_avoidance" type="bool" setter="set_use_3d_avoidance" getter="get_use_3d_avoidance" default="false">
If [code]true[/code] the agent calculates avoidance velocities in 3D for the xyz-axis, e.g. for games that take place in air, unterwater or space. The 3D using agent only avoids other 3D avoidance using agent's. The 3D using agent only reacts to radius based avoidance obstacles. The 3D using agent ignores any vertices based obstacles. The 3D using agent only avoids other 3D using agent's.
If [code]false[/code] the agent calculates avoidance velocities in 2D along the xz-axis ignoring the y-axis. The 2D using agent only avoids other 2D avoidance using agent's. The 2D using agent reacts to radius avoidance obstacles. The 2D using agent reacts to vertices based avoidance obstacles. The 2D using agent only avoids other 2D using agent's. 2D using agents will ignore other 2D using agents or obstacles that are below their current position or above their current position including [member height] in 2D avoidance.
</member>
<member name="velocity" type="Vector3" setter="set_velocity" getter="get_velocity" default="Vector3(0, 0, 0)">
Sets the new wanted velocity for the agent. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position use [method set_velocity_forced] as well to reset the internal simulation velocity.
</member>
</members>
<signals>
@ -209,7 +257,7 @@
<signal name="velocity_computed">
<argument index="0" name="safe_velocity" type="Vector3" />
<description>
Notifies when the collision avoidance velocity is calculated after a call to [method set_velocity]. Only emitted when [member avoidance_enabled] is true.
Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true.
</description>
</signal>
<signal name="waypoint_reached">

View File

@ -5,8 +5,7 @@
</brief_description>
<description>
2D agent that is used in navigation to reach a position while avoiding static and dynamic obstacles. The dynamic obstacles are avoided using RVO (Reciprocal Velocity Obstacles) collision avoidance. The agent needs navigation data to work correctly. By default this node will register to the default [World2D] navigation map. If this node is a child of a [Navigation2D] node it will register to the navigation map of the navigation node or the function [method set_navigation] can be used to set the navigation node directly. [NavigationAgent2D] is physics safe.
[b]Note:[/b] After setting [member target_position] it is required to use the [method get_next_position] function once every physics frame to update the internal path logic of the NavigationAgent. The returned vector position from this function should be used as the next movement position for the agent's parent Node.
[b]Note:[/b] By default, the expensive calculations for avoidance are done in a thread. In HTML5 exports without thread support, they will be done on the main thread, which can lead to performance issues.
[b]Note:[/b] After [member target_position] is set, the [method get_next_path_position] function must be used once every physics frame to update the internal path logic of the NavigationAgent. The returned position from this function should be used as the next movement position for the agent's parent node.
</description>
<tutorials>
</tutorials>
@ -17,6 +16,20 @@
Returns the distance to the target position, using the agent's global position. The user must set [member target_position] in order for this to be accurate.
</description>
</method>
<method name="get_avoidance_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_avoidance_mask_value" qualifiers="const">
<return type="bool" />
<param index="0" name="mask_number" type="int" />
<description>
Returns whether or not the specified mask of the [member avoidance_mask] bitmask is enabled, given a [param mask_number] between 1 and 32.
</description>
</method>
<method name="get_current_navigation_result" qualifiers="const">
<return type="NavigationPathQueryResult2D" />
<description>
@ -26,16 +39,16 @@
<method name="get_final_position">
<return type="Vector2" />
<description>
Returns the reachable final position in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
Returns the reachable final position of the current navigation path in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
</description>
</method>
<method name="get_nav_path" qualifiers="const">
<method name="get_current_navigation_path" qualifiers="const">
<return type="PoolVector2Array" />
<description>
Returns this agent's current path from start to finish in global coordinates. The path only updates when the target position is changed or the agent requires a repath. The path array is not intended to be used in direct path movement as the agent has its own internal path logic that would get corrupted by changing the path array manually. Use the intended [method get_next_position] once every physics frame to receive the next path point for the agents movement as this function also updates the internal path logic.
</description>
</method>
<method name="get_nav_path_index" qualifiers="const">
<method name="get_current_navigation_path_index" qualifiers="const">
<return type="int" />
<description>
Returns which index the agent is currently on in the navigation path's [PoolVector2Array].
@ -81,7 +94,7 @@
<method name="is_target_reachable">
<return type="bool" />
<description>
Returns [code]true[/code] if [member target_position] is reachable.
Returns true if [member target_position] is reachable. The target position is set using [member target_position].
</description>
</method>
<method name="is_target_reached" qualifiers="const">
@ -97,6 +110,22 @@
Sets the [Navigation2D] node used by the agent. Useful when you don't want to make the agent a child of a [Navigation2D] node.
</description>
</method>
<method name="set_avoidance_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_avoidance_mask_value">
<return type="void" />
<param index="0" name="mask_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified mask in the [member avoidance_mask] bitmask, given a [param mask_number] between 1 and 32.
</description>
</method>
<method name="set_navigation_layer_value">
<return type="void" />
<argument index="0" name="layer_number" type="int" />
@ -112,17 +141,26 @@
Sets the [RID] of the navigation map this NavigationAgent node should use and also updates the [code]agent[/code] on the NavigationServer.
</description>
</method>
<method name="set_velocity">
<method name="set_velocity_forced">
<return type="void" />
<argument index="0" name="velocity" type="Vector2" />
<description>
Sends the passed in velocity to the collision avoidance algorithm. It will adjust the velocity to avoid collisions. Once the adjustment to the velocity is complete, it will emit the [signal velocity_computed] signal.
Replaces the internal velocity in the collision avoidance simulation with [param velocity]. When an agent is teleported to a new position this function should be used in the same frame. If called frequently this function can get agents stuck.
</description>
</method>
</methods>
<members>
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false">
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [Navigation2DServer]. When [method set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer2D]. When [member velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
</member>
<member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
A bitfield determining the avoidance layers for this NavigationAgent. Other agent's with a matching bit on the [member avoidance_mask] will avoid this agent.
</member>
<member name="avoidance_mask" type="int" setter="set_avoidance_mask" getter="get_avoidance_mask" default="1">
A bitfield determining what other avoidance agent's and obstacles this NavigationAgent will avoid when a bit matches at least one of their [member avoidance_layers].
</member>
<member name="avoidance_priority" type="float" setter="set_avoidance_priority" getter="get_avoidance_priority" default="1.0">
The agent does not adjust the velocity for other agents that would match the [member avoidance_mask] but have a lower [member avoidance_priority]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
</member>
<member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false">
If [code]true[/code] shows debug visuals for this agent.
@ -168,10 +206,16 @@
The distance threshold before the final target point is considered to be reached. This will allow an agent to not have to hit the point of the final target exactly, but only the area. If this value is set to low the NavigationAgent will be stuck in a repath loop cause it will constantly overshoot or undershoot the distance to the final target point on each physics frame update.
</member>
<member name="target_position" type="Vector2" setter="set_target_position" getter="get_target_position" default="Vector2( 0, 0 )">
The user-defined target position. Setting this property will clear the current navigation path.
If set a new navigation path from the current agent position to the [member target_position] is requested from the NavigationServer.
</member>
<member name="time_horizon" type="float" setter="set_time_horizon" getter="get_time_horizon" default="0.0">
The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but the less freedom in choosing its velocities. Must be positive.
<member name="time_horizon_agents" type="float" setter="set_time_horizon_agents" getter="get_time_horizon_agents" default="1.0">
The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to other agents. The larger the number, the sooner the agent will respond to other agents, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</member>
<member name="time_horizon_obstacles" type="float" setter="set_time_horizon_obstacles" getter="get_time_horizon_obstacles" default="0.0">
The minimal amount of time for which this agent's velocities, that are computed with the collision avoidance algorithm, are safe with respect to static avoidance obstacles. The larger the number, the sooner the agent will respond to static avoidance obstacles, but less freedom in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</member>
<member name="velocity" type="Vector2" setter="set_velocity" getter="get_velocity" default="Vector2(0, 0)">
Sets the new wanted velocity for the agent. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position use [method set_velocity_forced] as well to reset the internal simulation velocity.
</member>
</members>
<signals>
@ -206,7 +250,7 @@
<signal name="velocity_computed">
<argument index="0" name="safe_velocity" type="Vector2" />
<description>
Notifies when the collision avoidance velocity is calculated after a call to [method set_velocity]. Only emitted when [member avoidance_enabled] is true.
Notifies when the collision avoidance velocity is calculated. Emitted when [member velocity] is set. Only emitted when [member avoidance_enabled] is true.
</description>
</signal>
<signal name="waypoint_reached">

View File

@ -1,25 +1,54 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationObstacle" inherits="Node" version="3.11">
<class name="NavigationObstacle" inherits="Spatial" version="3.11">
<brief_description>
3D obstacle used in navigation for collision avoidance.
3D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area.
</brief_description>
<description>
3D obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. This can be done by having the obstacle as a child of a [Navigation] node, or using [method set_navigation]. [NavigationObstacle] is physics safe.
Obstacles [b]don't[/b] change the resulting path from the pathfinding, they only affect the navigation agent movement in a radius. Therefore, using obstacles for the static walls in your level won't work because those walls don't exist in the pathfinding. The navigation agent will be pushed in a semi-random direction away while moving inside that radius. Obstacles are intended as a last resort option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently.
3D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area. The obstacle needs a navigation map and outline vertices defined to work correctly.
If the obstacle's vertices are winded in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Outlines must not cross or overlap.
Obstacles are [b]not[/b] a replacement for a (re)baked navigation mesh. Obstacles [b]don't[/b] change the resulting path from the pathfinding, obstacles only affect the navigation avoidance agent movement by altering the suggested velocity of the avoidance agent.
Obstacles using vertices can warp to a new position but should not moved every frame as each move requires a rebuild of the avoidance map.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_agent_rid" qualifiers="const">
<return type="RID" />
<description>
Returns the [RID] of this agent on the [NavigationServer3D]. This [RID] is used for the moving avoidance "obstacle" component (using a fake avoidance agent) which size is defined by [member radius] and velocity set by using [member velocity].
</description>
</method>
<method name="get_avoidance_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_navigation_map" qualifiers="const">
<return type="RID" />
<description>
Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract obstacle on the NavigationServer. If the obstacle map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the obstacle on the NavigationServer.
</description>
</method>
<method name="get_navigation" qualifiers="const">
<return type="Node" />
<description>
Returns the [Navigation] node that the obstacle is using for its navigation system.
</description>
</method>
<method name="get_rid" qualifiers="const">
<method name="get_obstacle_rid" qualifiers="const">
<return type="RID" />
<description>
Returns the [RID] of this obstacle on the [NavigationServer].
Returns the [RID] of this obstacle on the [NavigationServer3D]. This [RID] is used for the static avoidance obstacle component which shape is defined by [member vertices].
</description>
</method>
<method name="set_avoidance_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_navigation">
@ -29,13 +58,33 @@
Sets the [Navigation] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation] node.
</description>
</method>
<method name="set_navigation_map">
<return type="void" />
<param index="0" name="navigation_map" type="RID" />
<description>
Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]obstacle[/code] on the NavigationServer.
</description>
</method>
</methods>
<members>
<member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
<member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
A bitfield determining the avoidance layers for this obstacle. Agent's with a matching bit on the their avoidance mask will avoid this obstacle.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
The radius of the agent. Used only if [member estimate_radius] is set to [code]false[/code].
<member name="height" type="float" setter="set_height" getter="get_height" default="1.0">
Sets the obstacle height used in 2D avoidance. 2D avoidance using agent's ignore obstacles that are below or above them.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.0">
Sets the avoidance radius for the obstacle.
</member>
<member name="use_3d_avoidance" type="bool" setter="set_use_3d_avoidance" getter="get_use_3d_avoidance" default="false">
If [code]true[/code] the obstacle affects 3D avoidance using agent's with obstacle [member radius].
If [code]false[/code] the obstacle affects 2D avoidance using agent's with both obstacle [member vertices] as well as obstacle [member radius].
</member>
<member name="velocity" type="Vector3" setter="set_velocity" getter="get_velocity" default="Vector3(0, 0, 0)">
Sets the wanted velocity for the obstacle so other agent's can better predict the obstacle if it is moved with a velocity regularly (every frame) instead of warped to a new position. Does only affect avoidance for the obstacles [member radius]. Does nothing for the obstacles static vertices.
</member>
<member name="vertices" type="PackedVector3Array" setter="set_vertices" getter="get_vertices" default="PackedVector3Array()">
The outline vertices of the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out. Outlines can not be crossed or overlap. Should the vertices using obstacle be warped to a new position agent's can not predict this movement and may get trapped inside the obstacle.
</member>
</members>
<constants>

View File

@ -1,25 +1,54 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationObstacle2D" inherits="Node" version="3.11">
<class name="NavigationObstacle2D" inherits="Node2D" version="3.11">
<brief_description>
2D obstacle used in navigation for collision avoidance.
2D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area.
</brief_description>
<description>
2D obstacle used in navigation for collision avoidance. The obstacle needs navigation data to work correctly. This can be done by having the obstacle as a child of a [Navigation2D] node, or using [method set_navigation]. [NavigationObstacle2D] is physics safe.
Obstacles [b]don't[/b] change the resulting path from the pathfinding, they only affect the navigation agent movement in a radius. Therefore, using obstacles for the static walls in your level won't work because those walls don't exist in the pathfinding. The navigation agent will be pushed in a semi-random direction away while moving inside that radius. Obstacles are intended as a last resort option for constantly moving objects that cannot be (re)baked to a navigation mesh efficiently.
2D Obstacle used in navigation to constrain avoidance controlled agents outside or inside an area. The obstacle needs a navigation map and outline vertices defined to work correctly.
If the obstacle's vertices are winded in clockwise order, avoidance agents will be pushed in by the obstacle, otherwise, avoidance agents will be pushed out. Outlines must not cross or overlap.
Obstacles are [b]not[/b] a replacement for a (re)baked navigation mesh. Obstacles [b]don't[/b] change the resulting path from the pathfinding, obstacles only affect the navigation avoidance agent movement by altering the suggested velocity of the avoidance agent.
Obstacles using vertices can warp to a new position but should not moved every frame as each move requires a rebuild of the avoidance map.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_agent_rid" qualifiers="const">
<return type="RID" />
<description>
Returns the [RID] of this agent on the [NavigationServer2D]. This [RID] is used for the moving avoidance "obstacle" component (using a fake avoidance agent) which size is defined by [member radius] and velocity set by using [member velocity].
</description>
</method>
<method name="get_avoidance_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_navigation_map" qualifiers="const">
<return type="RID" />
<description>
Returns the [RID] of the navigation map for this NavigationObstacle node. This function returns always the map set on the NavigationObstacle node and not the map of the abstract obstacle on the NavigationServer. If the obstacle map is changed directly with the NavigationServer API the NavigationObstacle node will not be aware of the map change. Use [method set_navigation_map] to change the navigation map for the NavigationObstacle and also update the obstacle on the NavigationServer.
</description>
</method>
<method name="get_navigation" qualifiers="const">
<return type="Node" />
<description>
Returns the [Navigation2D] node that the obstacle is using for its navigation system.
</description>
</method>
<method name="get_rid" qualifiers="const">
<method name="get_obstacle_rid" qualifiers="const">
<return type="RID" />
<description>
Returns the [RID] of this obstacle on the [Navigation2DServer].
Returns the [RID] of this obstacle on the [NavigationServer2D]. This [RID] is used for the static avoidance obstacle component which shape is defined by [member vertices].
</description>
</method>
<method name="set_avoidance_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_navigation">
@ -29,13 +58,26 @@
Sets the [Navigation2D] node used by the obstacle. Useful when you don't want to make the obstacle a child of a [Navigation2D] node.
</description>
</method>
<method name="set_navigation_map">
<return type="void" />
<param index="0" name="navigation_map" type="RID" />
<description>
Sets the [RID] of the navigation map this NavigationObstacle node should use and also updates the [code]obstacle[/code] on the NavigationServer.
</description>
</method>
</methods>
<members>
<member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
<member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
A bitfield determining the avoidance layers for this obstacle. Agent's with a matching bit on the their avoidance mask will avoid this obstacle.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
The radius of the agent. Used only if [member estimate_radius] is set to [code]false[/code].
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.0">
Sets the avoidance radius for the obstacle.
</member>
<member name="velocity" type="Vector2" setter="set_velocity" getter="get_velocity" default="Vector2(0, 0)">
Sets the wanted velocity for the obstacle so other agent's can better predict the obstacle if it is moved with a velocity regularly (every frame) instead of warped to a new position. Does only affect avoidance for the obstacles [member radius]. Does nothing for the obstacles static vertices.
</member>
<member name="vertices" type="PackedVector2Array" setter="set_vertices" getter="get_vertices" default="PackedVector2Array()">
The outline vertices of the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out. Outlines can not be crossed or overlap. Should the vertices using obstacle be warped to a new position agent's can not predict this movement and may get trapped inside the obstacle.
</member>
</members>
<constants>

View File

@ -15,6 +15,13 @@
<tutorials>
</tutorials>
<methods>
<method name="get_avoidance_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="get_navigation_layer_value" qualifiers="const">
<return type="bool" />
<argument index="0" name="layer_number" type="int" />
@ -41,6 +48,14 @@
Returns the current navigation map [RID] use by this region.
</description>
</method>
<method name="set_avoidance_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
<param index="1" name="value" type="bool" />
<description>
Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
<method name="set_navigation_layer_value">
<return type="void" />
<argument index="0" name="layer_number" type="int" />
@ -58,6 +73,13 @@
</method>
</methods>
<members>
<member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
A bitfield determining all avoidance layers for the avoidance constrain.
</member>
<member name="constrain_avoidance" type="bool" setter="set_constrain_avoidance" getter="get_constrain_avoidance" default="false">
If [code]true[/code] constraints avoidance agent's with an avoidance mask bit that matches with a bit of the [member avoidance_layers] to the navigation polygon. Due to each navigation polygon outline creating an obstacle and each polygon edge creating an avoidance line constrain keep the navigation polygon shape as simple as possible for performance.
[b]Experimental:[/b] This is an experimental feature and should not be used in production as agent's can get stuck on the navigation polygon corners and edges especially at high frame rate.
</member>
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
Determines if the [NavigationPolygonInstance] is enabled or disabled.
</member>

View File

@ -23,6 +23,13 @@
Creates the agent.
</description>
</method>
<method name="agent_get_avoidance_enabled" qualifiers="const">
<return type="bool" />
<param index="0" name="agent" type="RID" />
<description>
Returns [code]true[/code] if the provided [param agent] has avoidance enabled.
</description>
</method>
<method name="agent_get_map" qualifiers="const">
<return type="RID" />
<argument index="0" name="agent" type="RID" />
@ -30,6 +37,13 @@
Returns the navigation map [RID] the requested [code]agent[/code] is currently assigned to.
</description>
</method>
<method name="agent_get_use_3d_avoidance" qualifiers="const">
<return type="bool" />
<param index="0" name="agent" type="RID" />
<description>
Returns [code]true[/code] if the provided [param agent] uses avoidance in 3D space Vector3(x,y,z) instead of horizontal 2D Vector2(x,y) / Vector3(x,0.0,z).
</description>
</method>
<method name="agent_is_map_changed" qualifiers="const">
<return type="bool" />
<argument index="0" name="agent" type="RID" />
@ -37,15 +51,56 @@
Returns [code]true[/code] if the map got changed the previous frame.
</description>
</method>
<method name="agent_set_callback">
<method name="agent_set_avoidance_callback">
<return type="void" />
<argument index="0" name="agent" type="RID" />
<argument index="1" name="object_id" type="int" />
<argument index="2" name="method" type="StringName" />
<argument index="3" name="userdata" type="Variant" default="null" />
<description>
Sets the callback [code]object_id[/code] and [code]method[/code] that gets called after each avoidance processing step for the [code]agent[/code]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
[b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [code]object_id[/code].
Sets the callback [Callable] that gets called after each avoidance processing step for the [param agent]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations.
[b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_avoidance_callback] again with an empty [Callable].
</description>
</method>
<method name="agent_set_avoidance_enabled">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
If [param enabled] the provided [param agent] calculates avoidance.
</description>
</method>
<method name="agent_set_avoidance_layers">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="layers" type="int" />
<description>
Set the agent's [code]avoidance_layers[/code] bitmask.
</description>
</method>
<method name="agent_set_avoidance_mask">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="mask" type="int" />
<description>
Set the agent's [code]avoidance_mask[/code] bitmask.
</description>
</method>
<method name="agent_set_avoidance_priority">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="priority" type="float" />
<description>
Set the agent's [code]avoidance_priority[/code] with a [param priority] between 0.0 (lowest priority) to 1.0 (highest priority).
The specified [param agent] does not adjust the velocity for other agents that would match the [code]avoidance_mask[/code] but have a lower [code] avoidance_priority[/code]. This in turn makes the other agents with lower priority adjust their velocities even more to avoid collision with this agent.
</description>
</method>
<method name="agent_set_height">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="height" type="float" />
<description>
Updates the provided [param agent] [param height].
</description>
</method>
<method name="agent_set_map">
@ -96,20 +151,30 @@
Sets the radius of the agent.
</description>
</method>
<method name="agent_set_target_velocity">
<method name="agent_set_time_horizon_agents">
<return type="void" />
<argument index="0" name="agent" type="RID" />
<argument index="1" name="target_velocity" type="Vector3" />
<param index="0" name="agent" type="RID" />
<param index="1" name="time_horizon" type="float" />
<description>
Sets the new target velocity.
The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</description>
</method>
<method name="agent_set_time_horizon">
<method name="agent_set_time_horizon_obstacles">
<return type="void" />
<argument index="0" name="agent" type="RID" />
<argument index="1" name="time" type="float" />
<param index="1" name="time_horizon" type="float" />
<description>
The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to other agents. The larger this number, the sooner this agent will respond to the presence of other agents, but the less freedom this agent has in choosing its velocities. Must be positive.
The minimal amount of time for which the agent's velocities that are computed by the simulation are safe with respect to static avoidance obstacles. The larger this number, the sooner this agent will respond to the presence of static avoidance obstacles, but the less freedom this agent has in choosing its velocities. A too high value will slow down agents movement considerably. Must be positive.
</description>
</method>
<method name="agent_set_use_3d_avoidance">
<return type="void" />
<argument index="0" name="agent" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
Sets if the agent uses the 2D avoidance or the 3D avoidance while avoidance is enabled.
If [code]true[/code] the agent calculates avoidance velocities in 3D for the xyz-axis, e.g. for games that take place in air, unterwater or space. The 3D using agent only avoids other 3D avoidance using agent's. The 3D using agent only reacts to radius based avoidance obstacles. The 3D using agent ignores any vertices based obstacles. The 3D using agent only avoids other 3D using agent's.
If [code]false[/code] the agent calculates avoidance velocities in 2D along the xz-axis ignoring the y-axis. The 2D using agent only avoids other 2D avoidance using agent's. The 2D using agent reacts to radius avoidance obstacles. The 2D using agent reacts to vertices based avoidance obstacles. The 2D using agent only avoids other 2D using agent's. 2D using agents will ignore other 2D using agents or obstacles that are below their current position or above their current position including the agents height in 2D avoidance.
</description>
</method>
<method name="agent_set_velocity">
@ -117,7 +182,15 @@
<argument index="0" name="agent" type="RID" />
<argument index="1" name="velocity" type="Vector3" />
<description>
Sets the current velocity of the agent.
Sets [param velocity] as the new wanted velocity for the specified [param agent]. The avoidance simulation will try to fulfil this velocity if possible but will modify it to avoid collision with other agent's and obstacles. When an agent is teleported to a new position use [method agent_set_velocity_forced] as well to reset the internal simulation velocity.
</description>
</method>
<method name="agent_set_velocity_forced">
<return type="void" />
<param index="0" name="agent" type="RID" />
<param index="1" name="velocity" type="Vector3" />
<description>
Replaces the internal velocity in the collision avoidance simulation with [param velocity] for the specified [param agent]. When an agent is teleported to a new position this function should be used in the same frame. If called frequently this function can get agents stuck.
</description>
</method>
<method name="free_rid">
@ -365,6 +438,13 @@
Returns all navigation link [RID]s that are currently assigned to the requested navigation [code]map[/code].
</description>
</method>
<method name="map_get_obstacles" qualifiers="const">
<return type="RID[]" />
<param index="0" name="map" type="RID" />
<description>
Returns all navigation obstacle [RID]s that are currently assigned to the requested navigation [code]map[/code].
</description>
</method>
<method name="map_get_path" qualifiers="const">
<return type="PoolVector3Array" />
<argument index="0" name="map" type="RID" />
@ -452,6 +532,59 @@
Sets the map up direction.
</description>
</method>
<method name="obstacle_create">
<return type="RID" />
<description>
Creates a new obstacle.
</description>
</method>
<method name="obstacle_get_map" qualifiers="const">
<return type="RID" />
<param index="0" name="obstacle" type="RID" />
<description>
Returns the navigation map [RID] the requested [param obstacle] is currently assigned to.
</description>
</method>
<method name="obstacle_set_avoidance_layers">
<return type="void" />
<param index="0" name="obstacle" type="RID" />
<param index="1" name="layers" type="int" />
<description>
Set the obstacles's [code]avoidance_layers[/code] bitmask.
</description>
</method>
<method name="obstacle_set_height">
<return type="void" />
<param index="0" name="obstacle" type="RID" />
<param index="1" name="height" type="float" />
<description>
Sets the [param height] for the [param obstacle]. In 3D agents will ignore obstacles that are above or below them while using 2D avoidance.
</description>
</method>
<method name="obstacle_set_map">
<return type="void" />
<param index="0" name="obstacle" type="RID" />
<param index="1" name="map" type="RID" />
<description>
Assigns the [param obstacle] to a navigation map.
</description>
</method>
<method name="obstacle_set_position">
<return type="void" />
<param index="0" name="obstacle" type="RID" />
<param index="1" name="position" type="Vector3" />
<description>
Updates the [param position] in world space for the [param obstacle].
</description>
</method>
<method name="obstacle_set_vertices">
<return type="void" />
<param index="0" name="obstacle" type="RID" />
<param index="1" name="vertices" type="PackedVector3Array" />
<description>
Sets the outline vertices for the obstacle. If the vertices are winded in clockwise order agents will be pushed in by the obstacle, else they will be pushed out.
</description>
</method>
<method name="map_set_use_edge_connections">
<return type="void" />
<param index="0" name="map" type="RID" />
@ -631,6 +764,11 @@
</method>
</methods>
<signals>
<signal name="avoidance_debug_changed">
<description>
Emitted when avoidance debug settings are changed. Only available in debug builds.
</description>
</signal>
<signal name="map_changed">
<argument index="0" name="map" type="RID" />
<description>

View File

@ -517,6 +517,33 @@
<member name="debug/settings/stdout/verbose_stdout" type="bool" setter="" getter="" default="false">
Print more information to standard output when running. It displays information such as memory leaks, which scenes and resources are being loaded, etc.
</member>
<member name="debug/shapes/avoidance/agents_radius_color" type="Color" setter="" getter="" default="Color(1, 1, 0, 0.25)">
Color of the avoidance agents radius, visible when "Visible Avoidance" is enabled in the Debug menu.
</member>
<member name="debug/shapes/avoidance/enable_agents_radius" type="bool" setter="" getter="" default="true">
If enabled, displays avoidance agents radius when "Visible Avoidance" is enabled in the Debug menu.
</member>
<member name="debug/shapes/avoidance/enable_obstacles_radius" type="bool" setter="" getter="" default="true">
If enabled, displays avoidance obstacles radius when "Visible Avoidance" is enabled in the Debug menu.
</member>
<member name="debug/shapes/avoidance/enable_obstacles_static" type="bool" setter="" getter="" default="true">
If enabled, displays static avoidance obstacles when "Visible Avoidance" is enabled in the Debug menu.
</member>
<member name="debug/shapes/avoidance/obstacles_radius_color" type="Color" setter="" getter="" default="Color(1, 0.5, 0, 0.25)">
Color of the avoidance obstacles radius, visible when "Visible Avoidance" is enabled in the Debug menu.
</member>
<member name="debug/shapes/avoidance/obstacles_static_edge_pushin_color" type="Color" setter="" getter="" default="Color(1, 0, 0, 1)">
Color of the static avoidance obstacles edges when their vertices are winded in order to push agents in, visible when "Visible Avoidance" is enabled in the Debug menu.
</member>
<member name="debug/shapes/avoidance/obstacles_static_edge_pushout_color" type="Color" setter="" getter="" default="Color(1, 1, 0, 1)">
Color of the static avoidance obstacles edges when their vertices are winded in order to push agents out, visible when "Visible Avoidance" is enabled in the Debug menu.
</member>
<member name="debug/shapes/avoidance/obstacles_static_face_pushin_color" type="Color" setter="" getter="" default="Color(1, 0, 0, 0)">
Color of the static avoidance obstacles faces when their vertices are winded in order to push agents in, visible when "Visible Avoidance" is enabled in the Debug menu.
</member>
<member name="debug/shapes/avoidance/obstacles_static_face_pushout_color" type="Color" setter="" getter="" default="Color(1, 1, 0, 0.5)">
Color of the static avoidance obstacles faces when their vertices are winded in order to push agents out, visible when "Visible Avoidance" is enabled in the Debug menu.
</member>
<member name="debug/shapes/collision/contact_color" type="Color" setter="" getter="" default="Color( 1, 0.2, 0.1, 0.8 )">
Color of the contact points between collision shapes, visible when "Visible Collision Shapes" is enabled in the Debug menu.
</member>
@ -1351,6 +1378,102 @@
<member name="layer_names/3d_render/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 3D render layer 9.
</member>
<member name="layer_names/avoidance/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 1. If left empty, the layer will display as "Layer 1".
</member>
<member name="layer_names/avoidance/layer_2" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 2. If left empty, the layer will display as "Layer 2".
</member>
<member name="layer_names/avoidance/layer_3" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 3. If left empty, the layer will display as "Layer 3".
</member>
<member name="layer_names/avoidance/layer_4" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 4. If left empty, the layer will display as "Layer 4".
</member>
<member name="layer_names/avoidance/layer_5" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 5. If left empty, the layer will display as "Layer 5".
</member>
<member name="layer_names/avoidance/layer_6" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 6. If left empty, the layer will display as "Layer 6".
</member>
<member name="layer_names/avoidance/layer_7" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 7. If left empty, the layer will display as "Layer 7".
</member>
<member name="layer_names/avoidance/layer_8" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 8. If left empty, the layer will display as "Layer 8".
</member>
<member name="layer_names/avoidance/layer_9" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 9. If left empty, the layer will display as "Layer 9".
</member>
<member name="layer_names/avoidance/layer_10" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 10. If left empty, the layer will display as "Layer 10".
</member>
<member name="layer_names/avoidance/layer_11" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 11. If left empty, the layer will display as "Layer 11".
</member>
<member name="layer_names/avoidance/layer_12" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 12. If left empty, the layer will display as "Layer 12".
</member>
<member name="layer_names/avoidance/layer_13" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 13. If left empty, the layer will display as "Layer 13".
</member>
<member name="layer_names/avoidance/layer_14" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 14. If left empty, the layer will display as "Layer 14".
</member>
<member name="layer_names/avoidance/layer_15" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 15. If left empty, the layer will display as "Layer 15".
</member>
<member name="layer_names/avoidance/layer_16" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 16. If left empty, the layer will display as "Layer 16".
</member>
<member name="layer_names/avoidance/layer_17" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 17. If left empty, the layer will display as "Layer 17".
</member>
<member name="layer_names/avoidance/layer_18" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 18. If left empty, the layer will display as "Layer 18".
</member>
<member name="layer_names/avoidance/layer_19" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 19. If left empty, the layer will display as "Layer 19".
</member>
<member name="layer_names/avoidance/layer_20" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 20. If left empty, the layer will display as "Layer 20".
</member>
<member name="layer_names/avoidance/layer_21" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 21. If left empty, the layer will display as "Layer 21".
</member>
<member name="layer_names/avoidance/layer_22" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 22. If left empty, the layer will display as "Layer 22".
</member>
<member name="layer_names/avoidance/layer_23" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 23. If left empty, the layer will display as "Layer 23".
</member>
<member name="layer_names/avoidance/layer_24" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 24. If left empty, the layer will display as "Layer 24".
</member>
<member name="layer_names/avoidance/layer_25" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 25. If left empty, the layer will display as "Layer 25".
</member>
<member name="layer_names/avoidance/layer_26" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 26. If left empty, the layer will display as "Layer 26".
</member>
<member name="layer_names/avoidance/layer_27" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 27. If left empty, the layer will display as "Layer 27".
</member>
<member name="layer_names/avoidance/layer_28" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 28. If left empty, the layer will display as "Layer 28".
</member>
<member name="layer_names/avoidance/layer_29" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 29. If left empty, the layer will display as "Layer 29".
</member>
<member name="layer_names/avoidance/layer_30" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 30. If left empty, the layer will display as "Layer 30".
</member>
<member name="layer_names/avoidance/layer_31" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 31. If left empty, the layer will display as "Layer 31".
</member>
<member name="layer_names/avoidance/layer_32" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the navigation avoidance layer 32. If left empty, the layer will display as "Layer 32".
</member>
<member name="locale/fallback" type="String" setter="" getter="" default="&quot;en&quot;">
The locale to fall back to if a translation isn't available in a given language. If left empty, [code]en[/code] (English) will be used.
</member>
@ -1408,6 +1531,12 @@
<member name="navigation/3d/default_link_connection_radius" type="float" setter="" getter="" default="1.0">
Default link connection radius for 3D navigation maps. See [method NavigationServer3D.map_set_link_connection_radius].
</member>
<member name="navigation/avoidance/thread_model/avoidance_use_high_priority_threads" type="bool" setter="" getter="" default="true">
If enabled and avoidance calculations use multiple threads the threads run with high priority.
</member>
<member name="navigation/avoidance/thread_model/avoidance_use_multiple_threads" type="bool" setter="" getter="" default="true">
If enabled the avoidance calculations use multiple threads.
</member>
<member name="navigation/3d/use_edge_connections" type="bool" setter="" getter="" default="true">
If enabled 3D navigation regions will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. This setting only affects World3D default navigation maps.
</member>

View File

@ -367,6 +367,10 @@ void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags)
r_flags.push_back("--debug-navigation");
}
if (p_flags & DEBUG_FLAG_VIEW_AVOIDANCE) {
r_flags.push_back("--debug-avoidance");
}
if (p_flags & DEBUG_FLAG_SHADER_FALLBACKS) {
r_flags.push_back("--debug-shader-fallbacks");
}
@ -1293,6 +1297,10 @@ void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags
if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
r_flags.push_back("--debug-navigation");
}
if (p_flags & DEBUG_FLAG_VIEW_AVOIDANCE) {
r_flags.push_back("--debug-avoidance");
}
}
EditorExportPlatform::EditorExportPlatform() {
}

View File

@ -321,7 +321,8 @@ public:
DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST = 4,
DEBUG_FLAG_VIEW_COLLISONS = 8,
DEBUG_FLAG_VIEW_NAVIGATION = 16,
DEBUG_FLAG_SHADER_FALLBACKS = 32,
DEBUG_FLAG_VIEW_AVOIDANCE = 32,
DEBUG_FLAG_SHADER_FALLBACKS = 64,
};
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }

View File

@ -127,6 +127,8 @@
#include "editor/plugins/mesh_instance_editor_plugin.h"
#include "editor/plugins/multimesh_editor_plugin.h"
#include "editor/plugins/navigation_link_2d_editor_plugin.h"
#include "editor/plugins/navigation_obstacle_2d_editor_plugin.h"
#include "editor/plugins/navigation_obstacle_3d_editor_plugin.h"
#include "editor/plugins/packed_scene_editor_plugin.h"
#include "editor/plugins/path_2d_editor_plugin.h"
#include "editor/plugins/path_editor_plugin.h"
@ -2767,6 +2769,14 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
editor_run.set_debug_navigation(!ischecked);
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_navigation", !ischecked);
} break;
case RUN_DEBUG_AVOIDANCE: {
bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_AVOIDANCE));
debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_AVOIDANCE), !ischecked);
run_native->set_debug_avoidance(!ischecked);
editor_run.set_debug_avoidance(!ischecked);
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_avoidance", !ischecked);
} break;
case RUN_DEBUG_SHADER_FALLBACKS: {
bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_SHADER_FALLBACKS));
@ -3059,6 +3069,7 @@ void EditorNode::_update_debug_options() {
bool check_file_server = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false);
bool check_debug_collisons = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisons", false);
bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
bool check_debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false);
bool check_debug_shader_fallbacks = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_shader_fallbacks", false);
bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true);
bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true);
@ -3075,6 +3086,9 @@ void EditorNode::_update_debug_options() {
if (check_debug_navigation) {
_menu_option_confirm(RUN_DEBUG_NAVIGATION, true);
}
if (check_debug_avoidance) {
_menu_option_confirm(RUN_DEBUG_AVOIDANCE, true);
}
if (check_debug_shader_fallbacks) {
_menu_option_confirm(RUN_DEBUG_SHADER_FALLBACKS, true);
}
@ -6509,6 +6523,11 @@ EditorNode::EditorNode() {
p->get_item_count() - 1,
TTR("When this option is enabled, navigation meshes and polygons will be visible in the running project."));
p->add_check_shortcut(ED_SHORTCUT("editor/visible_avoidance", TTR("Visible Avoidance")), RUN_DEBUG_AVOIDANCE);
p->set_item_tooltip(
p->get_item_count() - 1,
TTR("When this option is enabled, avoidance debug information will be visible in the running project."));
if (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3") {
p->add_separator();
@ -7011,12 +7030,14 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(RoomManagerEditorPlugin(this)));
add_editor_plugin(memnew(RoomEditorPlugin(this)));
add_editor_plugin(memnew(OccluderEditorPlugin(this)));
add_editor_plugin(memnew(NavigationObstacleEditorPlugin(this)));
add_editor_plugin(memnew(PortalEditorPlugin(this)));
add_editor_plugin(memnew(PackedSceneEditorPlugin(this)));
add_editor_plugin(memnew(Path2DEditorPlugin(this)));
add_editor_plugin(memnew(PathEditorPlugin(this)));
add_editor_plugin(memnew(Line2DEditorPlugin(this)));
add_editor_plugin(memnew(NavigationLink2DEditorPlugin));
add_editor_plugin(memnew(NavigationObstacle2DEditorPlugin(this)));
add_editor_plugin(memnew(Polygon2DEditorPlugin(this)));
add_editor_plugin(memnew(LightOccluder2DEditorPlugin(this)));
add_editor_plugin(memnew(GradientEditorPlugin(this)));

View File

@ -447,6 +447,7 @@ private:
RUN_LIVE_DEBUG,
RUN_DEBUG_COLLISONS,
RUN_DEBUG_NAVIGATION,
RUN_DEBUG_AVOIDANCE,
RUN_DEBUG_SHADER_FALLBACKS,
RUN_DEPLOY_REMOTE_DEBUG,
RUN_RELOAD_SCRIPTS,

View File

@ -1117,6 +1117,12 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) {
layer_group_size = 4;
layer_count = 32;
} break;
case LAYER_AVOIDANCE: {
basename = "layer_names/avoidance";
layer_group_size = 4;
layer_count = 32;
} break;
}
Vector<String> names;
@ -3691,7 +3697,13 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
editor->setup(options);
add_property_editor(p_path, editor);
} else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || p_hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION) {
} else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS ||
p_hint == PROPERTY_HINT_LAYERS_2D_RENDER ||
p_hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION ||
p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS ||
p_hint == PROPERTY_HINT_LAYERS_3D_RENDER ||
p_hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION ||
p_hint == PROPERTY_HINT_LAYERS_AVOIDANCE) {
EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D;
switch (p_hint) {
case PROPERTY_HINT_LAYERS_2D_RENDER:
@ -3712,6 +3724,9 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
case PROPERTY_HINT_LAYERS_3D_NAVIGATION:
lt = EditorPropertyLayers::LAYER_NAVIGATION_3D;
break;
case PROPERTY_HINT_LAYERS_AVOIDANCE:
lt = EditorPropertyLayers::LAYER_AVOIDANCE;
break;
default: {
} //compiler could be smarter here and realize this can't happen
}

View File

@ -313,6 +313,7 @@ public:
LAYER_PHYSICS_3D,
LAYER_RENDER_3D,
LAYER_NAVIGATION_3D,
LAYER_AVOIDANCE,
};
private:

View File

@ -98,6 +98,10 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L
args.push_back("--debug-navigation");
}
if (debug_avoidance) {
args.push_back("--debug-avoidance");
}
if (debug_shader_fallbacks) {
args.push_back("--debug-shader-fallbacks");
}
@ -305,6 +309,14 @@ bool EditorRun::get_debug_navigation() const {
return debug_navigation;
}
void EditorRun::set_debug_avoidance(bool p_debug) {
debug_avoidance = p_debug;
}
bool EditorRun::get_debug_avoidance() const {
return debug_avoidance;
}
void EditorRun::set_debug_shader_fallbacks(bool p_debug) {
debug_shader_fallbacks = p_debug;
}
@ -318,5 +330,6 @@ EditorRun::EditorRun() {
running_scene = "";
debug_collisions = false;
debug_navigation = false;
debug_avoidance = false;
debug_shader_fallbacks = false;
}

View File

@ -50,6 +50,7 @@ public:
private:
bool debug_collisions;
bool debug_navigation;
bool debug_avoidance;
bool debug_shader_fallbacks;
Status status;
String running_scene;
@ -69,6 +70,9 @@ public:
void set_debug_navigation(bool p_debug);
bool get_debug_navigation() const;
void set_debug_avoidance(bool p_debug);
bool get_debug_avoidance() const;
void set_debug_shader_fallbacks(bool p_debug);
bool get_debug_shader_fallbacks() const;

View File

@ -30,23 +30,23 @@
#include "editor_run_native.h"
#include "core/object/class_db.h"
#include "core/error/error_macros.h"
#include "core/io/image.h"
#include "core/os/memory.h"
#include "core/object/class_db.h"
#include "core/object/reference.h"
#include "core/typedefs.h"
#include "core/os/memory.h"
#include "core/string/ustring.h"
#include "core/typedefs.h"
#include "core/variant/variant.h"
#include "editor_export.h"
#include "editor_node.h"
#include "editor_scale.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/popup_menu.h"
#include "scene/gui/rich_text_label.h"
#include "scene/main/node.h"
#include "scene/resources/texture.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/rich_text_label.h"
void EditorRunNative::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
@ -156,6 +156,9 @@ void EditorRunNative::_run_native(int p_idx, int p_platform) {
if (debug_navigation) {
flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION;
}
if (debug_avoidance) {
flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_AVOIDANCE;
}
if (debug_shader_fallbacks) {
flags |= EditorExportPlatform::DEBUG_FLAG_SHADER_FALLBACKS;
}
@ -210,6 +213,14 @@ bool EditorRunNative::get_debug_navigation() const {
return debug_navigation;
}
void EditorRunNative::set_debug_avoidance(bool p_debug) {
debug_avoidance = p_debug;
}
bool EditorRunNative::get_debug_avoidance() const {
return debug_avoidance;
}
void EditorRunNative::set_debug_shader_fallbacks(bool p_debug) {
debug_shader_fallbacks = p_debug;
}
@ -234,6 +245,7 @@ EditorRunNative::EditorRunNative() {
deploy_debug_remote = false;
debug_collisions = false;
debug_navigation = false;
debug_avoidance = false;
debug_shader_fallbacks = false;
resume_idx = 0;
resume_platform = 0;

View File

@ -51,6 +51,7 @@ class EditorRunNative : public HBoxContainer {
bool deploy_debug_remote;
bool debug_collisions;
bool debug_navigation;
bool debug_avoidance;
bool debug_shader_fallbacks;
int resume_idx;
@ -75,6 +76,9 @@ public:
void set_debug_navigation(bool p_debug);
bool get_debug_navigation() const;
void set_debug_avoidance(bool p_debug);
bool get_debug_avoidance() const;
void set_debug_shader_fallbacks(bool p_debug);
bool get_debug_shader_fallbacks() const;

View File

@ -0,0 +1,75 @@
/**************************************************************************/
/* navigation_obstacle_2d_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "navigation_obstacle_2d_editor_plugin.h"
#include "core/object/undo_redo.h"
#include "editor/editor_node.h"
Node2D *NavigationObstacle2DEditor::_get_node() const {
return node;
}
void NavigationObstacle2DEditor::_set_node(Node *p_polygon) {
node = Object::cast_to<NavigationObstacle2D>(p_polygon);
}
void NavigationObstacle2DEditor::_action_add_polygon(const Variant &p_polygon) {
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action("NavigationObstacle2D Add Polygon");
undo_redo->add_do_method(node, "set_vertices", p_polygon);
undo_redo->add_undo_method(node, "set_vertices", node->get_vertices());
undo_redo->commit_action();
}
void NavigationObstacle2DEditor::_action_remove_polygon(int p_idx) {
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action("NavigationObstacle2D Remove Polygon");
undo_redo->add_do_method(node, "set_vertices", Variant(Vector<Vector2>()));
undo_redo->add_undo_method(node, "set_vertices", node->get_vertices());
undo_redo->commit_action();
}
void NavigationObstacle2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action("NavigationObstacle2D Set Polygon");
undo_redo->add_do_method(node, "set_vertices", p_polygon);
undo_redo->add_undo_method(node, "set_vertices", node->get_vertices());
undo_redo->commit_action();
}
NavigationObstacle2DEditor::NavigationObstacle2DEditor(EditorNode *p_editor, bool p_wip_destructive) :
AbstractPolygon2DEditor(p_editor, p_wip_destructive) {
node = nullptr;
}
NavigationObstacle2DEditorPlugin::NavigationObstacle2DEditorPlugin(EditorNode *p_node) :
AbstractPolygon2DEditorPlugin(p_node, memnew(NavigationObstacle2DEditor(p_node)), "NavigationObstacle2D") {
}

View File

@ -0,0 +1,61 @@
#ifndef NAVIGATION_OBSTACLE_2D_EDITOR_PLUGIN_H
#define NAVIGATION_OBSTACLE_2D_EDITOR_PLUGIN_H
/**************************************************************************/
/* navigation_obstacle_2d_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "editor/plugins/abstract_polygon_2d_editor.h"
#include "scene/2d/navigation_obstacle_2d.h"
class NavigationObstacle2DEditor : public AbstractPolygon2DEditor {
GDCLASS(NavigationObstacle2DEditor, AbstractPolygon2DEditor);
NavigationObstacle2D *node;
protected:
virtual Node2D *_get_node() const ;
virtual void _set_node(Node *p_polygon) ;
virtual void _action_add_polygon(const Variant &p_polygon) ;
virtual void _action_remove_polygon(int p_idx) ;
virtual void _action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) ;
public:
NavigationObstacle2DEditor(EditorNode *p_editor, bool p_wip_destructive = true);
};
class NavigationObstacle2DEditorPlugin : public AbstractPolygon2DEditorPlugin {
GDCLASS(NavigationObstacle2DEditorPlugin, AbstractPolygon2DEditorPlugin);
public:
NavigationObstacle2DEditorPlugin(EditorNode *p_node);
};
#endif // NAVIGATION_OBSTACLE_2D_EDITOR_PLUGIN_H

View File

@ -0,0 +1,624 @@
/**************************************************************************/
/* navigation_obstacle_3d_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "navigation_obstacle_3d_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
#include "core/core_string_names.h"
#include "core/input/input.h"
#include "core/math/geometry.h"
#include "core/object/undo_redo.h"
#include "core/os/file_access.h"
#include "core/os/keyboard.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "scene/3d/camera.h"
#include "scene/gui/separator.h"
#include "spatial_editor_plugin.h"
void NavigationObstacleEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
button_create->set_icon(get_theme_icon("Edit", "EditorIcons"));
button_edit->set_icon(get_theme_icon("MovePoint", "EditorIcons"));
button_edit->set_pressed(true);
get_tree()->connect("node_removed", this, "_node_removed");
} break;
}
}
void NavigationObstacleEditor::_node_removed(Node *p_node) {
if (p_node == obstacle_node) {
obstacle_node = nullptr;
if (point_lines_meshinstance->get_parent() == p_node) {
p_node->remove_child(point_lines_meshinstance);
}
hide();
}
}
void NavigationObstacleEditor::_menu_option(int p_option) {
switch (p_option) {
case MODE_CREATE: {
mode = MODE_CREATE;
button_create->set_pressed(true);
button_edit->set_pressed(false);
} break;
case MODE_EDIT: {
mode = MODE_EDIT;
button_create->set_pressed(false);
button_edit->set_pressed(true);
} break;
}
}
void NavigationObstacleEditor::_wip_close() {
ERR_FAIL_COND_MSG(!obstacle_node, "Edited NavigationObstacle is not valid.");
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action(TTR("Set NavigationObstacle Vertices"));
undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
Vector<Vector2> wipt;
wipt.resize(wip.size());
for (int i = 0; i < wip.size(); ++i) {
wipt.write[i] = wip[i];
}
PoolVector3Array polygon_3d_vertices;
Vector<int> triangulated_polygon_2d_indices = Geometry::triangulate_polygon(wipt);
if (!triangulated_polygon_2d_indices.empty()) {
polygon_3d_vertices.resize(wip.size());
PoolVector3Array::Write p3dvw = polygon_3d_vertices.write();
Vector3 *polygon_3d_vertices_ptr = p3dvw.ptr();
for (int i = 0; i < wip.size(); i++) {
const Vector2 &vert = wip[i];
polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y);
}
}
undo_redo->add_do_method(obstacle_node, "set_vertices", polygon_3d_vertices);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
wip.clear();
wip_active = false;
mode = MODE_EDIT;
button_edit->set_pressed(true);
button_create->set_pressed(false);
edited_point = -1;
undo_redo->commit_action();
}
EditorPlugin::AfterGUIInput NavigationObstacleEditor::forward_3d_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
if (!obstacle_node) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Transform gt = obstacle_node->get_global_transform();
Transform gi = gt.affine_inverse();
Plane p(Vector3(0.0, 1.0, 0.0), gt.origin);
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
Vector2 gpoint = mb->get_position();
Vector3 ray_from = p_camera->project_ray_origin(gpoint);
Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
Vector2 cpoint(spoint.x, spoint.z);
//DO NOT snap here, it's confusing in 3D for adding points.
//Let the snap happen when the point is being moved, instead.
//cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
PoolVector2Array poly = _get_polygon();
//first check if a point is to be added (segment split)
real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
switch (mode) {
case MODE_CREATE: {
if (mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
if (!wip_active) {
wip.clear();
wip.push_back(cpoint);
wip_active = true;
edited_point_pos = cpoint;
snap_ignore = false;
_polygon_draw();
edited_point = 1;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, 0.0, wip[0].y))).distance_to(gpoint) < grab_threshold) {
//wip closed
_wip_close();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
wip.push_back(cpoint);
edited_point = wip.size();
snap_ignore = false;
_polygon_draw();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && wip_active) {
_wip_close();
}
} break;
case MODE_EDIT: {
if (mb->get_button_index() == BUTTON_LEFT) {
if (mb->is_pressed()) {
if (mb->get_control()) {
if (poly.size() < 3) {
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action(TTR("Edit Vertices"));
undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
poly.push_back(cpoint);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
//search edges
int closest_idx = -1;
Vector2 closest_pos;
real_t closest_dist = 1e10;
for (int i = 0; i < poly.size(); i++) {
Vector2 points[2] = {
p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))),
p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, 0.0, poly[(i + 1) % poly.size()].y)))
};
Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint, points);
if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) {
continue; //not valid to reuse point
}
real_t d = cp.distance_to(gpoint);
if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
closest_idx = i;
}
}
if (closest_idx >= 0) {
pre_move_edit = poly;
poly.insert(closest_idx + 1, cpoint);
edited_point = closest_idx + 1;
edited_point_pos = cpoint;
_set_polygon(poly);
_polygon_draw();
snap_ignore = true;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
} else {
//look for points to move
int closest_idx = -1;
Vector2 closest_pos;
real_t closest_dist = 1e10;
for (int i = 0; i < poly.size(); i++) {
Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y)));
real_t d = cp.distance_to(gpoint);
if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
closest_idx = i;
}
}
if (closest_idx >= 0) {
pre_move_edit = poly;
edited_point = closest_idx;
edited_point_pos = poly[closest_idx];
_polygon_draw();
snap_ignore = false;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} else {
snap_ignore = false;
if (edited_point != -1) {
//apply
ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
poly.set(edited_point, edited_point_pos);
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action(TTR("Edit Poly"));
//undo_redo->add_do_method(obj, "set_polygon", poly);
//undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
edited_point = -1;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && edited_point == -1) {
int closest_idx = -1;
Vector2 closest_pos;
real_t closest_dist = 1e10;
for (int i = 0; i < poly.size(); i++) {
Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y)));
real_t d = cp.distance_to(gpoint);
if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
closest_idx = i;
}
}
if (closest_idx >= 0) {
UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
//undo_redo->add_undo_method(obj, "set_polygon", poly);
poly.remove(closest_idx);
//undo_redo->add_do_method(obj, "set_polygon", poly);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} break;
}
}
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (edited_point != -1 && (wip_active || (mm->get_button_mask() & BUTTON_MASK_LEFT) != 0)) {
Vector2 gpoint = mm->get_position();
Vector3 ray_from = p_camera->project_ray_origin(gpoint);
Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
Vector2 cpoint(spoint.x, spoint.z);
if (snap_ignore && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
snap_ignore = false;
}
if (!snap_ignore && SpatialEditor::get_singleton()->is_snap_enabled()) {
cpoint = cpoint.snapped(Vector2(
SpatialEditor::get_singleton()->get_translate_snap(),
SpatialEditor::get_singleton()->get_translate_snap()));
}
edited_point_pos = cpoint;
_polygon_draw();
}
}
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
PoolVector2Array NavigationObstacleEditor::_get_polygon() {
ERR_FAIL_COND_V_MSG(!obstacle_node, PoolVector2Array(), "Edited object is not valid.");
return PoolVector2Array(obstacle_node->call("get_polygon"));
}
void NavigationObstacleEditor::_set_polygon(PoolVector2Array p_poly) {
ERR_FAIL_COND_MSG(!obstacle_node, "Edited object is not valid.");
obstacle_node->call("set_polygon", p_poly);
}
void NavigationObstacleEditor::_polygon_draw() {
if (!obstacle_node) {
return;
}
PoolVector2Array poly;
PoolVector3Array polygon_3d_vertices;
if (wip_active) {
poly = wip;
} else {
poly = _get_polygon();
}
polygon_3d_vertices.resize(poly.size());
PoolVector3Array::Write p3dvw = polygon_3d_vertices.write();
Vector3 *polygon_3d_vertices_ptr = p3dvw.ptr();
for (int i = 0; i < poly.size(); i++) {
const Vector2 &vert = poly[i];
polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y);
}
point_handle_mesh->clear_surfaces();
point_lines_mesh->clear_surfaces();
point_lines_meshinstance->set_material_override(line_material);
point_lines_mesh->surface_begin(Mesh::PRIMITIVE_LINES);
Rect2 rect;
for (int i = 0; i < poly.size(); i++) {
Vector2 p, p2;
if (i == edited_point) {
p = edited_point_pos;
} else {
p = poly[i];
}
if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) {
p2 = edited_point_pos;
} else {
p2 = poly[(i + 1) % poly.size()];
}
if (i == 0) {
rect.position = p;
} else {
rect.expand_to(p);
}
Vector3 point = Vector3(p.x, 0.0, p.y);
Vector3 next_point = Vector3(p2.x, 0.0, p2.y);
point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
point_lines_mesh->surface_add_vertex(point);
point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
point_lines_mesh->surface_add_vertex(next_point);
//Color col=Color(1,0.3,0.1,0.8);
//vpc->draw_line(point,next_point,col,2);
//vpc->draw_texture(handle,point-handle->get_size()*0.5);
}
rect = rect.grow(1);
AABB r;
r.position.x = rect.position.x;
r.position.y = 0.0;
r.position.z = rect.position.y;
r.size.x = rect.size.x;
r.size.y = 0;
r.size.z = rect.size.y;
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position);
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(0.3, 0, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position);
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(0.0, 0.3, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + r.size);
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.3, 0, 0));
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + r.size);
point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0));
point_lines_mesh->surface_end();
if (poly.size() == 0) {
return;
}
Array point_handle_mesh_array;
point_handle_mesh_array.resize(Mesh::ARRAY_MAX);
Vector<Vector3> point_handle_mesh_vertices;
point_handle_mesh_vertices.resize(poly.size());
Vector3 *point_handle_mesh_vertices_ptr = point_handle_mesh_vertices.ptrw();
for (int i = 0; i < poly.size(); i++) {
Vector2 point_2d;
Vector2 p2;
if (i == edited_point) {
point_2d = edited_point_pos;
} else {
point_2d = poly[i];
}
Vector3 point_handle_3d = Vector3(point_2d.x, 0.0, point_2d.y);
point_handle_mesh_vertices_ptr[i] = point_handle_3d;
}
point_handle_mesh_array[Mesh::ARRAY_VERTEX] = point_handle_mesh_vertices;
point_handle_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, point_handle_mesh_array);
point_handle_mesh->surface_set_material(0, handle_material);
}
void NavigationObstacleEditor::edit(Node *p_node) {
obstacle_node = Object::cast_to<NavigationObstacle>(p_node);
if (obstacle_node) {
//Enable the pencil tool if the polygon is empty
if (_get_polygon().empty()) {
_menu_option(MODE_CREATE);
}
wip.clear();
wip_active = false;
edited_point = -1;
p_node->add_child(point_lines_meshinstance);
_polygon_draw();
} else {
obstacle_node = nullptr;
if (point_lines_meshinstance->get_parent()) {
point_lines_meshinstance->get_parent()->remove_child(point_lines_meshinstance);
}
}
}
void NavigationObstacleEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_polygon_draw"), &NavigationObstacleEditor::_polygon_draw);
ClassDB::bind_method(D_METHOD("_node_removed"), &NavigationObstacleEditor::_node_removed);
ClassDB::bind_method(D_METHOD("_menu_option"), &NavigationObstacleEditor::_menu_option);
}
NavigationObstacleEditor::NavigationObstacleEditor() {
obstacle_node = nullptr;
button_create = nullptr;
button_edit = nullptr;
panel = nullptr;
obstacle_node = nullptr;
point_lines_meshinstance = nullptr;
point_handles_meshinstance = nullptr;
options = nullptr;
edited_point = 0;
wip_active = false;
snap_ignore = false;
prev_depth = 0.0f;
add_child(memnew(VSeparator));
button_create = memnew(Button);
button_create->set_flat(true);
add_child(button_create);
button_create->connect("pressed", this, "_menu_option", varray(MODE_CREATE));
button_create->set_toggle_mode(true);
button_edit = memnew(Button);
button_edit->set_flat(true);
add_child(button_edit);
button_edit->connect("pressed", this, "_menu_option", varray(MODE_EDIT));
button_edit->set_toggle_mode(true);
mode = MODE_EDIT;
wip_active = false;
point_lines_meshinstance = memnew(MeshInstance);
point_lines_mesh.instance();
point_lines_meshinstance->set_mesh(point_lines_mesh);
point_lines_meshinstance->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001)));
line_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
line_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
line_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
line_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
line_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
line_material->set_albedo(Color(1, 1, 1));
handle_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
handle_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
handle_material->set_flag(SpatialMaterial::FLAG_USE_POINT_SIZE, true);
handle_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
handle_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
handle_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
Ref<Texture> handle = EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Editor3DHandle", "EditorIcons");
handle_material->set_point_size(handle->get_width());
handle_material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, handle);
point_handles_meshinstance = memnew(MeshInstance);
point_lines_meshinstance->add_child(point_handles_meshinstance);
point_handle_mesh.instance();
point_handles_meshinstance->set_mesh(point_handle_mesh);
point_handles_meshinstance->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001)));
snap_ignore = false;
}
NavigationObstacleEditor::~NavigationObstacleEditor() {
memdelete(point_lines_meshinstance);
}
void NavigationObstacleEditorPlugin::edit(Object *p_object) {
obstacle_editor->edit(Object::cast_to<Node>(p_object));
}
bool NavigationObstacleEditorPlugin::handles(Object *p_object) const {
return Object::cast_to<NavigationObstacle>(p_object);
}
void NavigationObstacleEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
obstacle_editor->show();
} else {
obstacle_editor->hide();
obstacle_editor->edit(nullptr);
}
}
NavigationObstacleEditorPlugin::NavigationObstacleEditorPlugin(EditorNode *p_node) {
obstacle_editor = memnew(NavigationObstacleEditor);
SpatialEditor::get_singleton()->add_control_to_menu_panel(obstacle_editor);
obstacle_editor->hide();
}
NavigationObstacleEditorPlugin::~NavigationObstacleEditorPlugin() {
}

View File

@ -0,0 +1,117 @@
#ifndef NAVIGATION_OBSTACLE_EDITOR_PLUGIN_H
#define NAVIGATION_OBSTACLE_EDITOR_PLUGIN_H
/**************************************************************************/
/* navigation_obstacle_3d_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "editor/editor_plugin.h"
#include "scene/3d/collision_polygon.h"
#include "scene/3d/mesh_instance.h"
#include "scene/gui/box_container.h"
#include "scene/resources/immediate_mesh.h"
#include "scene/3d/navigation_obstacle.h"
class CanvasItemEditor;
class MenuButton;
class NavigationObstacleEditor : public HBoxContainer {
GDCLASS(NavigationObstacleEditor, HBoxContainer);
enum Mode {
MODE_CREATE,
MODE_EDIT,
};
Mode mode;
Button *button_create;
Button *button_edit;
Ref<SpatialMaterial> line_material;
Ref<SpatialMaterial> handle_material;
Panel *panel;
NavigationObstacle *obstacle_node;
Ref<ImmediateMesh> point_lines_mesh;
MeshInstance *point_lines_meshinstance;
MeshInstance *point_handles_meshinstance;
Ref<ArrayMesh> point_handle_mesh;
MenuButton *options;
int edited_point;
Vector2 edited_point_pos;
PoolVector2Array pre_move_edit;
PoolVector2Array wip;
bool wip_active;
bool snap_ignore;
float prev_depth;
void _wip_close();
void _polygon_draw();
void _menu_option(int p_option);
PoolVector2Array _get_polygon();
void _set_polygon(PoolVector2Array p_poly);
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
public:
virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event);
void edit(Node *p_node);
NavigationObstacleEditor();
~NavigationObstacleEditor();
};
class NavigationObstacleEditorPlugin : public EditorPlugin {
GDCLASS(NavigationObstacleEditorPlugin, EditorPlugin);
NavigationObstacleEditor *obstacle_editor = nullptr;
public:
virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return obstacle_editor->forward_3d_gui_input(p_camera, p_event); }
virtual String get_name() const { return "NavigationObstacleEditor"; }
bool has_main_screen() const { return false; }
virtual void edit(Object *p_object);
virtual bool handles(Object *p_object) const;
virtual void make_visible(bool p_visible);
NavigationObstacleEditorPlugin(EditorNode *p_node);
~NavigationObstacleEditorPlugin();
};
#endif // NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H

View File

@ -30,15 +30,19 @@
#include "property_editor.h"
#include "core/variant/array.h"
#include "core/object/class_db.h"
#include "core/math/color.h"
#include "core/config/project_settings.h"
#include "core/containers/pair.h"
#include "core/containers/rb_map.h"
#include "core/containers/rb_set.h"
#include "core/containers/rid.h"
#include "core/error/error_list.h"
#include "core/error/error_macros.h"
#include "core/input/input.h"
#include "core/input/input_event.h"
#include "core/io/resource_loader.h"
#include "core/containers/rb_map.h"
#include "core/math/aabb.h"
#include "core/math/basis.h"
#include "core/math/color.h"
#include "core/math/expression.h"
#include "core/math/math_funcs.h"
#include "core/math/plane.h"
@ -48,20 +52,16 @@
#include "core/math/transform_2d.h"
#include "core/math/vector2.h"
#include "core/math/vector3.h"
#include "core/input/input.h"
#include "core/input/input_event.h"
#include "core/object/class_db.h"
#include "core/object/ref_ptr.h"
#include "core/object/resource.h"
#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/os/main_loop.h"
#include "core/os/memory.h"
#include "core/containers/pair.h"
#include "core/config/project_settings.h"
#include "core/object/ref_ptr.h"
#include "core/object/resource.h"
#include "core/containers/rid.h"
#include "core/object/script_language.h"
#include "core/containers/rb_set.h"
#include "core/string/string_name.h"
#include "core/typedefs.h"
#include "core/variant/array.h"
#include "editor/array_property_edit.h"
#include "editor/create_dialog.h"
#include "editor/dictionary_property_edit.h"
@ -460,7 +460,13 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
updating = false;
return false;
} else if (hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || hint == PROPERTY_HINT_LAYERS_2D_RENDER || hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION || hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || hint == PROPERTY_HINT_LAYERS_3D_RENDER || hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION) {
} else if (hint == PROPERTY_HINT_LAYERS_2D_PHYSICS ||
hint == PROPERTY_HINT_LAYERS_2D_RENDER ||
hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION ||
hint == PROPERTY_HINT_LAYERS_3D_PHYSICS ||
hint == PROPERTY_HINT_LAYERS_3D_RENDER ||
hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION ||
hint == PROPERTY_HINT_LAYERS_AVOIDANCE) {
String basename;
switch (hint) {
case PROPERTY_HINT_LAYERS_2D_RENDER:
@ -481,6 +487,9 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
case PROPERTY_HINT_LAYERS_3D_NAVIGATION:
basename = "layer_names/3d_navigation";
break;
case PROPERTY_HINT_LAYERS_AVOIDANCE:
basename = "layer_names/avoidance";
break;
}
checks20gc->show();

View File

@ -159,6 +159,7 @@ static bool use_debug_profiler = false;
#ifdef DEBUG_ENABLED
static bool debug_collisions = false;
static bool debug_navigation = false;
static bool debug_avoidance = false;
static bool debug_shader_fallbacks = false;
#endif
static int frame_delay = 0;
@ -375,6 +376,7 @@ void Main::print_help(const char *p_binary) {
#if defined(DEBUG_ENABLED) && !defined(SERVER_ENABLED)
OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n");
OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n");
OS::get_singleton()->print(" --debug-avoidance Show navigation polygons when running the scene.\n");
OS::get_singleton()->print(" --debug-shader-fallbacks Use the fallbacks of the shaders which have one when running the scene (GL ES 3 only).\n");
#endif
OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).\n");
@ -912,6 +914,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
debug_collisions = true;
} else if (I->get() == "--debug-navigation") {
debug_navigation = true;
} else if (I->get() == "--debug-avoidance") {
debug_avoidance = true;
} else if (I->get() == "--debug-shader-fallbacks") {
debug_shader_fallbacks = true;
#endif
@ -1928,8 +1932,19 @@ bool Main::start() {
}
if (debug_navigation) {
sml->set_debug_navigation_hint(true);
}
if (debug_avoidance) {
sml->set_debug_avoidance_hint(true);
}
if (debug_navigation || debug_avoidance) {
NavigationServer::get_singleton()->set_active(true);
NavigationServer::get_singleton()->set_debug_enabled(true);
if (debug_navigation) {
NavigationServer::get_singleton()->set_debug_navigation_enabled(true);
}
if (debug_avoidance) {
NavigationServer::get_singleton()->set_debug_avoidance_enabled(true);
}
}
#endif

View File

@ -4266,6 +4266,16 @@ void CScriptParser::_parse_class(ClassNode *p_class) {
break;
}
if (tokenizer->get_token() == CScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "PROPERTY_HINT_LAYERS_AVOIDANCE") {
_ADVANCE_AND_CONSUME_NEWLINES;
if (tokenizer->get_token() != CScriptTokenizer::TK_PARENTHESIS_CLOSE) {
_set_error("Expected \")\" in the avoidance 3D navigation hint.");
return;
}
current_export.hint = PROPERTY_HINT_LAYERS_AVOIDANCE;
break;
}
if (tokenizer->get_token() == CScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) {
//enumeration
current_export.hint = PROPERTY_HINT_ENUM;

View File

@ -550,7 +550,7 @@ void ItemTemplate::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_teaches_spells"), &ItemTemplate::get_teaches_spells);
ClassDB::bind_method(D_METHOD("set_teaches_spells", "spells"), &ItemTemplate::set_teaches_spells);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "teaches_spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_teaches_spells", "get_teaches_spells");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "teaches_spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_teaches_spells", "get_teaches_spells");
//// Grants Spells ////
ClassDB::bind_method(D_METHOD("get_num_grants_spells"), &ItemTemplate::get_num_grants_spells);
@ -561,7 +561,7 @@ void ItemTemplate::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_grants_spells"), &ItemTemplate::get_grants_spells);
ClassDB::bind_method(D_METHOD("set_grants_spells", "spells"), &ItemTemplate::set_grants_spells);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "grants_spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_grants_spells", "get_grants_spells");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "grants_spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_grants_spells", "get_grants_spells");
//// Auras ////
ClassDB::bind_method(D_METHOD("get_num_auras"), &ItemTemplate::get_num_auras);
@ -572,7 +572,7 @@ void ItemTemplate::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auras"), &ItemTemplate::get_auras);
ClassDB::bind_method(D_METHOD("set_auras", "auras"), &ItemTemplate::set_auras);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
//// Required Skills ////
ClassDB::bind_method(D_METHOD("get_num_required_skills"), &ItemTemplate::get_num_required_skills);
@ -582,7 +582,7 @@ void ItemTemplate::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_required_skills"), &ItemTemplate::get_required_skills);
ClassDB::bind_method(D_METHOD("set_required_skills", "auras"), &ItemTemplate::set_required_skills);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "required_skills", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_required_skills", "get_required_skills");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "required_skills", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_required_skills", "get_required_skills");
//Use spell
ClassDB::bind_method(D_METHOD("get_use_spell"), &ItemTemplate::get_use_spell);

View File

@ -96,5 +96,5 @@ void ModelVisual::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_visual_entries"), &ModelVisual::get_visual_entries);
ClassDB::bind_method(D_METHOD("set_visual_entries", "visual_entrys"), &ModelVisual::set_visual_entries);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "visual_entries", PROPERTY_HINT_NONE, "23/19:ModelVisualEntry", PROPERTY_USAGE_DEFAULT, "ModelVisualEntry"), "set_visual_entries", "get_visual_entries");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "visual_entries", PROPERTY_HINT_NONE, "23/20:ModelVisualEntry", PROPERTY_USAGE_DEFAULT, "ModelVisualEntry"), "set_visual_entries", "get_visual_entries");
}

View File

@ -202,7 +202,7 @@ void EntitySpeciesData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_model_datas"), &EntitySpeciesData::get_model_datas);
ClassDB::bind_method(D_METHOD("set_model_datas", "model_datas"), &EntitySpeciesData::set_model_datas);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "model_datas", PROPERTY_HINT_NONE, "23/19:SpeciesModelData", PROPERTY_USAGE_DEFAULT, "SpeciesModelData"), "set_model_datas", "get_model_datas");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "model_datas", PROPERTY_HINT_NONE, "23/20:SpeciesModelData", PROPERTY_USAGE_DEFAULT, "SpeciesModelData"), "set_model_datas", "get_model_datas");
//Spells
ClassDB::bind_method(D_METHOD("get_spell", "index"), &EntitySpeciesData::get_spell);
@ -214,7 +214,7 @@ void EntitySpeciesData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_spells"), &EntitySpeciesData::get_spells);
ClassDB::bind_method(D_METHOD("set_spells", "spells"), &EntitySpeciesData::set_spells);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
//Auras
ClassDB::bind_method(D_METHOD("get_aura", "index"), &EntitySpeciesData::get_aura);
@ -226,5 +226,5 @@ void EntitySpeciesData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auras"), &EntitySpeciesData::get_auras);
ClassDB::bind_method(D_METHOD("set_auras", "auras"), &EntitySpeciesData::set_auras);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
}

View File

@ -331,7 +331,7 @@ void SpeciesModelData::_get_property_list(List<PropertyInfo> *p_list) const {
int count = _customizable_slots_string.get_slice_count(",");
for (int i = 0; i < count; ++i) {
p_list->push_back(PropertyInfo(Variant::ARRAY, "customizable_slots/" + itos(i) + "_" + _customizable_slots_string.get_slicec(',', i), PROPERTY_HINT_NONE, "23/19:ModelVisualEntry", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL, "ModelVisualEntry"));
p_list->push_back(PropertyInfo(Variant::ARRAY, "customizable_slots/" + itos(i) + "_" + _customizable_slots_string.get_slicec(',', i), PROPERTY_HINT_NONE, "23/20:ModelVisualEntry", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL, "ModelVisualEntry"));
}
count = _customizable_color_slots_string.get_slice_count(",");
@ -339,7 +339,7 @@ void SpeciesModelData::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < count; ++i) {
p_list->push_back(PropertyInfo(Variant::INT, "customizable_color_slots/" + itos(i) + "_" + _customizable_color_slots_string.get_slicec(',', i) + "/texture_layer", PROPERTY_HINT_ENUM, ESS::get_singleton()->texture_layers_get(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED));
p_list->push_back(PropertyInfo(Variant::INT, "customizable_color_slots/" + itos(i) + "_" + _customizable_color_slots_string.get_slicec(',', i) + "/bone_slot_mask", PROPERTY_HINT_FLAGS, ESS::get_singleton()->skeletons_bones_index_get(_bone_structure), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::ARRAY, "customizable_color_slots/" + itos(i) + "_" + _customizable_color_slots_string.get_slicec(',', i) + "/colors", PROPERTY_HINT_NONE, "23/19:Color", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL, "Color"));
p_list->push_back(PropertyInfo(Variant::ARRAY, "customizable_color_slots/" + itos(i) + "_" + _customizable_color_slots_string.get_slicec(',', i) + "/colors", PROPERTY_HINT_NONE, "23/20:Color", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL, "Color"));
}
}
void SpeciesModelData::_validate_property(PropertyInfo &property) const {

View File

@ -2728,7 +2728,7 @@ void Spell::_bind_methods() {
ClassDB::bind_method(D_METHOD("spells_cast_on_caster_get"), &Spell::spells_cast_on_caster_get);
ClassDB::bind_method(D_METHOD("spells_cast_on_caster_set", "caster_aura_applys"), &Spell::spells_cast_on_caster_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_caster", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_caster_set", "spells_cast_on_caster_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_caster", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_caster_set", "spells_cast_on_caster_get");
//ADD_GROUP("Target Aura Apply", "target_aura_applys");
ClassDB::bind_method(D_METHOD("spells_cast_on_target_num_get"), &Spell::spells_cast_on_target_num_get);
@ -2739,7 +2739,7 @@ void Spell::_bind_methods() {
ClassDB::bind_method(D_METHOD("spells_cast_on_target_get"), &Spell::spells_cast_on_target_get);
ClassDB::bind_method(D_METHOD("spells_cast_on_target_set", "target_aura_applys"), &Spell::spells_cast_on_target_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_target", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_target_set", "spells_cast_on_target_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_target", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_target_set", "spells_cast_on_target_get");
//ADD_GROUP("Apply Auras On Learn", "on_learn_auras");
ClassDB::bind_method(D_METHOD("on_learn_cast_spells_num_get"), &Spell::on_learn_cast_spells_num_get);
@ -2750,7 +2750,7 @@ void Spell::_bind_methods() {
ClassDB::bind_method(D_METHOD("spells_cast_on_learn_get"), &Spell::spells_cast_on_learn_get);
ClassDB::bind_method(D_METHOD("spells_cast_on_learn_set", "spells"), &Spell::spells_cast_on_learn_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_learn", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_learn_set", "spells_cast_on_learn_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells_cast_on_learn", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "spells_cast_on_learn_set", "spells_cast_on_learn_get");
ADD_GROUP("Texts", "text");
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::STRING, "desc"), "_get_description", PropertyInfo(Variant::INT, "class_level"), PropertyInfo(Variant::INT, "character_level")));

View File

@ -312,11 +312,11 @@ void ESSResourceDBStatic::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_remap_ids", "value"), &ESSResourceDBStatic::set_remap_ids);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "remap_ids"), "set_remap_ids", "get_remap_ids");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_resources", PROPERTY_HINT_NONE, "23/19:EntityResource", PROPERTY_USAGE_DEFAULT, "EntityResource"), "set_entity_resources", "get_entity_resources");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_skills", PROPERTY_HINT_NONE, "23/19:EntitySkillData", PROPERTY_USAGE_DEFAULT, "EntitySkillData"), "set_entity_skills", "get_entity_skills");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_datas", PROPERTY_HINT_NONE, "23/19:EntityData", PROPERTY_USAGE_DEFAULT, "EntityData"), "set_entity_datas", "get_entity_datas");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "craft_recipes", PROPERTY_HINT_NONE, "23/19:CraftRecipe", PROPERTY_USAGE_DEFAULT, "CraftRecipe"), "set_craft_recipes", "get_craft_recipes");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "item_templates", PROPERTY_HINT_NONE, "23/19:ItemTemplate", PROPERTY_USAGE_DEFAULT, "ItemTemplate"), "set_item_templates", "get_item_templates");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_species_datas", PROPERTY_HINT_NONE, "23/19:EntitySpeciesData", PROPERTY_USAGE_DEFAULT, "EntitySpeciesData"), "set_entity_species_datas", "get_entity_species_datas");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_resources", PROPERTY_HINT_NONE, "23/20:EntityResource", PROPERTY_USAGE_DEFAULT, "EntityResource"), "set_entity_resources", "get_entity_resources");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_skills", PROPERTY_HINT_NONE, "23/20:EntitySkillData", PROPERTY_USAGE_DEFAULT, "EntitySkillData"), "set_entity_skills", "get_entity_skills");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_datas", PROPERTY_HINT_NONE, "23/20:EntityData", PROPERTY_USAGE_DEFAULT, "EntityData"), "set_entity_datas", "get_entity_datas");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "craft_recipes", PROPERTY_HINT_NONE, "23/20:CraftRecipe", PROPERTY_USAGE_DEFAULT, "CraftRecipe"), "set_craft_recipes", "get_craft_recipes");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "item_templates", PROPERTY_HINT_NONE, "23/20:ItemTemplate", PROPERTY_USAGE_DEFAULT, "ItemTemplate"), "set_item_templates", "get_item_templates");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_species_datas", PROPERTY_HINT_NONE, "23/20:EntitySpeciesData", PROPERTY_USAGE_DEFAULT, "EntitySpeciesData"), "set_entity_species_datas", "get_entity_species_datas");
}

View File

@ -415,7 +415,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_entity_resources"), &EntityClassData::get_entity_resources);
ClassDB::bind_method(D_METHOD("set_entity_resources", "entity_resources"), &EntityClassData::set_entity_resources);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_resources", PROPERTY_HINT_NONE, "23/19:EntityResource", PROPERTY_USAGE_DEFAULT, "EntityResource"), "set_entity_resources", "get_entity_resources");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entity_resources", PROPERTY_HINT_NONE, "23/20:EntityResource", PROPERTY_USAGE_DEFAULT, "EntityResource"), "set_entity_resources", "get_entity_resources");
ClassDB::bind_method(D_METHOD("_setup_resources", "entity"), &EntityClassData::_setup_resources);
@ -428,7 +428,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_specs"), &EntityClassData::get_specs);
ClassDB::bind_method(D_METHOD("set_specs", "specs"), &EntityClassData::set_specs);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "specs", PROPERTY_HINT_NONE, "23/19:CharacterSpec", PROPERTY_USAGE_DEFAULT, "CharacterSpec"), "set_specs", "get_specs");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "specs", PROPERTY_HINT_NONE, "23/20:CharacterSpec", PROPERTY_USAGE_DEFAULT, "CharacterSpec"), "set_specs", "get_specs");
//// Spell ////
ClassDB::bind_method(D_METHOD("get_num_spells"), &EntityClassData::get_num_spells);
@ -439,7 +439,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_spells"), &EntityClassData::get_spells);
ClassDB::bind_method(D_METHOD("set_spells", "spells"), &EntityClassData::set_spells);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_spells", "get_spells");
//// Start Spells ////
ClassDB::bind_method(D_METHOD("get_num_start_spells"), &EntityClassData::get_num_start_spells);
@ -450,7 +450,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_start_spells"), &EntityClassData::get_start_spells);
ClassDB::bind_method(D_METHOD("set_start_spells", "spells"), &EntityClassData::set_start_spells);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "start_spells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_start_spells", "get_start_spells");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "start_spells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_start_spells", "get_start_spells");
//// AURAS ////
ClassDB::bind_method(D_METHOD("get_num_auras"), &EntityClassData::get_num_auras);
@ -461,7 +461,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auras"), &EntityClassData::get_auras);
ClassDB::bind_method(D_METHOD("set_auras", "auras"), &EntityClassData::set_auras);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "auras", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_DEFAULT, "Spell"), "set_auras", "get_auras");
//Vendor
ClassDB::bind_method(D_METHOD("get_vendor_item_data"), &EntityClassData::get_vendor_item_data);
@ -485,7 +485,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_craft_recipes"), &EntityClassData::get_craft_recipes);
ClassDB::bind_method(D_METHOD("set_craft_recipes", "recipe"), &EntityClassData::set_craft_recipes);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "craft_recipes", PROPERTY_HINT_NONE, "23/19:CraftRecipe", PROPERTY_USAGE_DEFAULT, "CraftRecipe"), "set_craft_recipes", "get_craft_recipes");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "craft_recipes", PROPERTY_HINT_NONE, "23/20:CraftRecipe", PROPERTY_USAGE_DEFAULT, "CraftRecipe"), "set_craft_recipes", "get_craft_recipes");
//// AI ACTIONS ////
ClassDB::bind_method(D_METHOD("get_num_ais"), &EntityClassData::get_num_ais);
@ -496,7 +496,7 @@ void EntityClassData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_ais"), &EntityClassData::get_ais);
ClassDB::bind_method(D_METHOD("set_ais", "auras"), &EntityClassData::set_ais);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "ais", PROPERTY_HINT_NONE, "23/19:EntityAI", PROPERTY_USAGE_DEFAULT, "EntityAI"), "set_ais", "get_ais");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "ais", PROPERTY_HINT_NONE, "23/20:EntityAI", PROPERTY_USAGE_DEFAULT, "EntityAI"), "set_ais", "get_ais");
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "EntityAI"), "_get_ai_instance"));

View File

@ -70,5 +70,5 @@ void ItemContainerData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_container_datas"), &ItemContainerData::get_container_datas);
ClassDB::bind_method(D_METHOD("set_container_datas", "container_datas"), &ItemContainerData::set_container_datas);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "container_datas", PROPERTY_HINT_NONE, "23/19:ItemContainerDataEntry", PROPERTY_USAGE_DEFAULT, "ItemContainerDataEntry"), "set_container_datas", "get_container_datas");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "container_datas", PROPERTY_HINT_NONE, "23/20:ItemContainerDataEntry", PROPERTY_USAGE_DEFAULT, "ItemContainerDataEntry"), "set_container_datas", "get_container_datas");
}

View File

@ -64,5 +64,5 @@ void VendorItemData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_vendor_datas"), &VendorItemData::get_vendor_datas);
ClassDB::bind_method(D_METHOD("set_vendor_datas", "vendor_datas"), &VendorItemData::set_vendor_datas);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "vendor_datas", PROPERTY_HINT_NONE, "23/19:VendorItemDataEntry", PROPERTY_USAGE_DEFAULT, "VendorItemDataEntry"), "set_vendor_datas", "get_vendor_datas");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "vendor_datas", PROPERTY_HINT_NONE, "23/20:VendorItemDataEntry", PROPERTY_USAGE_DEFAULT, "VendorItemDataEntry"), "set_vendor_datas", "get_vendor_datas");
}

View File

@ -7082,7 +7082,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sauras_get"), &Entity::sauras_get);
ClassDB::bind_method(D_METHOD("sauras_set", "data"), &Entity::sauras_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sauras", PROPERTY_HINT_NONE, "23/19:AuraData", PROPERTY_USAGE_ENTITY_HIDDEN, "AuraData"), "sauras_set", "sauras_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sauras", PROPERTY_HINT_NONE, "23/20:AuraData", PROPERTY_USAGE_ENTITY_HIDDEN, "AuraData"), "sauras_set", "sauras_get");
//Hooks
BIND_VMETHOD(MethodInfo("_moved"));
@ -7332,7 +7332,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sresources_get"), &Entity::sresources_get);
ClassDB::bind_method(D_METHOD("sresources_set", "caster_aura_applys"), &Entity::sresources_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sresources", PROPERTY_HINT_NONE, "23/19:EntityResource", PROPERTY_USAGE_ENTITY_HIDDEN, "EntityResource"), "sresources_set", "sresources_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sresources", PROPERTY_HINT_NONE, "23/20:EntityResource", PROPERTY_USAGE_ENTITY_HIDDEN, "EntityResource"), "sresources_set", "sresources_get");
//GCD
ADD_SIGNAL(MethodInfo("sgcd_started", PropertyInfo(Variant::OBJECT, "entity", PROPERTY_HINT_RESOURCE_TYPE, "Entity"), PropertyInfo(Variant::REAL, "value")));
@ -7367,7 +7367,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sdatas_get"), &Entity::sdatas_get);
ClassDB::bind_method(D_METHOD("sdatas_set", "data"), &Entity::sdatas_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sdatas", PROPERTY_HINT_NONE, "23/19:EntityDataContainer", PROPERTY_USAGE_ENTITY_HIDDEN, "EntityDataContainer"), "sdatas_set", "sdatas_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sdatas", PROPERTY_HINT_NONE, "23/20:EntityDataContainer", PROPERTY_USAGE_ENTITY_HIDDEN, "EntityDataContainer"), "sdatas_set", "sdatas_get");
//States
ADD_SIGNAL(MethodInfo("sstate_changed", PropertyInfo(Variant::INT, "value")));
@ -7503,7 +7503,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sspells_get"), &Entity::sspells_get);
ClassDB::bind_method(D_METHOD("sspells_set", "data"), &Entity::sspells_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sspells", PROPERTY_HINT_NONE, "23/19:Spell", PROPERTY_USAGE_ENTITY_HIDDEN, "Spell"), "sspells_set", "sspells_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sspells", PROPERTY_HINT_NONE, "23/20:Spell", PROPERTY_USAGE_ENTITY_HIDDEN, "Spell"), "sspells_set", "sspells_get");
//Crafting
BIND_VMETHOD(MethodInfo("_crafts", PropertyInfo(Variant::INT, "id")));
@ -7538,7 +7538,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("scraft_recipes_get"), &Entity::scraft_recipes_get);
ClassDB::bind_method(D_METHOD("scraft_recipes_set", "caster_aura_applys"), &Entity::scraft_recipes_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "scraft_recipes", PROPERTY_HINT_NONE, "23/19:CraftRecipe", PROPERTY_USAGE_ENTITY_HIDDEN, "CraftRecipe"), "scraft_recipes_set", "scraft_recipes_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "scraft_recipes", PROPERTY_HINT_NONE, "23/20:CraftRecipe", PROPERTY_USAGE_ENTITY_HIDDEN, "CraftRecipe"), "scraft_recipes_set", "scraft_recipes_get");
//Skills
ClassDB::bind_method(D_METHOD("skill_hass_id", "id"), &Entity::skill_hass_id);
@ -7562,7 +7562,7 @@ void Entity::_bind_methods() {
ClassDB::bind_method(D_METHOD("sskills_get"), &Entity::sskills_get);
ClassDB::bind_method(D_METHOD("sskills_set", "data"), &Entity::sskills_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sskills", PROPERTY_HINT_NONE, "23/19:EntitySkill", PROPERTY_USAGE_ENTITY_HIDDEN, "EntitySkill"), "sskills_set", "sskills_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "sskills", PROPERTY_HINT_NONE, "23/20:EntitySkill", PROPERTY_USAGE_ENTITY_HIDDEN, "EntitySkill"), "sskills_set", "sskills_get");
//skeleton
ClassDB::bind_method(D_METHOD("body_get"), &Entity::body_get);

View File

@ -337,7 +337,7 @@ void ESSMaterialCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &ESSMaterialCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &ESSMaterialCache::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("texture_add", "texture"), &ESSMaterialCache::texture_add);
ClassDB::bind_method(D_METHOD("texture_remove", "texture"), &ESSMaterialCache::texture_remove);

View File

@ -857,7 +857,7 @@ void ESS::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &ESS::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &ESS::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("material_cache_get", "key"), &ESS::material_cache_get);
ClassDB::bind_method(D_METHOD("material_cache_unref", "key"), &ESS::material_cache_unref);

View File

@ -30,14 +30,14 @@
#include "gdscript_parser.h"
#include "core/core_string_names.h"
#include "core/config/engine.h"
#include "core/io/resource_loader.h"
#include "core/os/file_access.h"
#include "core/string/print_string.h"
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
#include "core/object/reference.h"
#include "core/object/script_language.h"
#include "core/os/file_access.h"
#include "core/string/print_string.h"
#include "gdscript.h"
template <class T>
@ -4342,6 +4342,16 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
break;
}
if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "PROPERTY_HINT_LAYERS_AVOIDANCE") {
_ADVANCE_AND_CONSUME_NEWLINES;
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
_set_error("Expected \")\" in the avoidance 3D navigation hint.");
return;
}
current_export.hint = PROPERTY_HINT_LAYERS_AVOIDANCE;
break;
}
if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) {
//enumeration
current_export.hint = PROPERTY_HINT_ENUM;

View File

@ -221,7 +221,7 @@ void MMMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_nodes"), &MMMaterial::get_nodes);
ClassDB::bind_method(D_METHOD("set_nodes", "value"), &MMMaterial::set_nodes);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_NONE, "23/19:MMNode", PROPERTY_USAGE_DEFAULT, "MMNode"), "set_nodes", "get_nodes");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_NONE, "23/20:MMNode", PROPERTY_USAGE_DEFAULT, "MMNode"), "set_nodes", "get_nodes");
ClassDB::bind_method(D_METHOD("get_initialized"), &MMMaterial::get_initialized);
ClassDB::bind_method(D_METHOD("set_initialized", "value"), &MMMaterial::set_initialized);

View File

@ -236,11 +236,11 @@ void MMNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_input_properties"), &MMNode::get_input_properties);
ClassDB::bind_method(D_METHOD("set_input_properties", "value"), &MMNode::set_input_properties);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "input_properties", PROPERTY_HINT_NONE, "23/19:MMNodeUniversalProperty", PROPERTY_USAGE_DEFAULT, "MMNodeUniversalProperty"), "set_input_properties", "get_input_properties");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "input_properties", PROPERTY_HINT_NONE, "23/20:MMNodeUniversalProperty", PROPERTY_USAGE_DEFAULT, "MMNodeUniversalProperty"), "set_input_properties", "get_input_properties");
ClassDB::bind_method(D_METHOD("get_output_properties"), &MMNode::get_output_properties);
ClassDB::bind_method(D_METHOD("set_output_properties", "value"), &MMNode::set_output_properties);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "output_properties", PROPERTY_HINT_NONE, "23/19:MMNodeUniversalProperty", PROPERTY_USAGE_DEFAULT, "MMNodeUniversalProperty"), "set_output_properties", "get_output_properties");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "output_properties", PROPERTY_HINT_NONE, "23/20:MMNodeUniversalProperty", PROPERTY_USAGE_DEFAULT, "MMNodeUniversalProperty"), "set_output_properties", "get_output_properties");
ClassDB::bind_method(D_METHOD("get_properties_initialized"), &MMNode::get_properties_initialized);
ClassDB::bind_method(D_METHOD("set_properties_initialized", "value"), &MMNode::set_properties_initialized);

View File

@ -34,12 +34,14 @@ if env["builtin_recast"]:
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
# RVO2
if env["builtin_rvo2"]:
thirdparty_dir = "#thirdparty/rvo2/"
# RVO 2D Thirdparty source files
if env["builtin_rvo2_2d"]:
thirdparty_dir = "#thirdparty/rvo2/rvo2_2d/"
thirdparty_sources = [
"Agent.cpp",
"KdTree.cpp",
"Agent2d.cpp",
"Obstacle2d.cpp",
"KdTree2d.cpp",
"RVOSimulator2d.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
@ -48,8 +50,25 @@ if env["builtin_rvo2"]:
env_thirdparty = env_navigation.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
#env.modules_sources += thirdparty_obj
# RVO 3D Thirdparty source files
if env["builtin_rvo2_3d"]:
thirdparty_dir = "#thirdparty/rvo2/rvo2_3d/"
thirdparty_sources = [
"Agent3d.cpp",
"KdTree3d.cpp",
"RVOSimulator3d.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_navigation.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_navigation.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
# Pandemonium source files

View File

@ -0,0 +1,373 @@
/**************************************************************************/
/* nav_agent.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "nav_agent.h"
#include "nav_map.h"
NavAgent::NavAgent() {
height = 1.0;
radius = 1.0;
max_speed = 1.0;
time_horizon_agents = 1.0;
time_horizon_obstacles = 0.0;
max_neighbors = 5;
neighbor_distance = 5.0;
clamp_speed = true; // Experimental, clamps velocity to max_speed.
map = nullptr;
use_3d_avoidance = false;
avoidance_enabled = false;
avoidance_layers = 1;
avoidance_mask = 1;
avoidance_priority = 1.0;
avoidance_callback.id = ObjectID(0);
agent_dirty = true;
map_update_id = 0;
}
void NavAgent::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
_update_rvo_agent_properties();
}
void NavAgent::set_use_3d_avoidance(bool p_enabled) {
use_3d_avoidance = p_enabled;
_update_rvo_agent_properties();
}
void NavAgent::_update_rvo_agent_properties() {
if (use_3d_avoidance) {
rvo_agent_3d.neighborDist_ = neighbor_distance;
rvo_agent_3d.maxNeighbors_ = max_neighbors;
rvo_agent_3d.timeHorizon_ = time_horizon_agents;
rvo_agent_3d.timeHorizonObst_ = time_horizon_obstacles;
rvo_agent_3d.radius_ = radius;
rvo_agent_3d.maxSpeed_ = max_speed;
rvo_agent_3d.position_ = RVO3D::Vector3(position.x, position.y, position.z);
// Replacing the internal velocity directly causes major jitter / bugs due to unpredictable velocity jumps, left line here for testing.
//rvo_agent_3d.velocity_ = RVO3D::Vector3(velocity.x, velocity.y ,velocity.z);
rvo_agent_3d.prefVelocity_ = RVO3D::Vector3(velocity.x, velocity.y, velocity.z);
rvo_agent_3d.height_ = height;
rvo_agent_3d.avoidance_layers_ = avoidance_layers;
rvo_agent_3d.avoidance_mask_ = avoidance_mask;
rvo_agent_3d.avoidance_priority_ = avoidance_priority;
} else {
rvo_agent_2d.neighborDist_ = neighbor_distance;
rvo_agent_2d.maxNeighbors_ = max_neighbors;
rvo_agent_2d.timeHorizon_ = time_horizon_agents;
rvo_agent_2d.timeHorizonObst_ = time_horizon_obstacles;
rvo_agent_2d.radius_ = radius;
rvo_agent_2d.maxSpeed_ = max_speed;
rvo_agent_2d.position_ = RVO2D::Vector2(position.x, position.z);
rvo_agent_2d.elevation_ = position.y;
// Replacing the internal velocity directly causes major jitter / bugs due to unpredictable velocity jumps, left line here for testing.
//rvo_agent_2d.velocity_ = RVO2D::Vector2(velocity.x, velocity.z);
rvo_agent_2d.prefVelocity_ = RVO2D::Vector2(velocity.x, velocity.z);
rvo_agent_2d.height_ = height;
rvo_agent_2d.avoidance_layers_ = avoidance_layers;
rvo_agent_2d.avoidance_mask_ = avoidance_mask;
rvo_agent_2d.avoidance_priority_ = avoidance_priority;
}
if (map != nullptr) {
if (avoidance_enabled) {
map->set_agent_as_controlled(this);
} else {
map->remove_agent_as_controlled(this);
}
}
agent_dirty = true;
}
void NavAgent::set_map(NavMap *p_map) {
map = p_map;
agent_dirty = true;
}
bool NavAgent::is_map_changed() {
if (map) {
bool is_changed = map->get_map_update_id() != map_update_id;
map_update_id = map->get_map_update_id();
return is_changed;
} else {
return false;
}
}
void NavAgent::set_neighbor_distance(real_t p_neighbor_distance) {
neighbor_distance = p_neighbor_distance;
if (use_3d_avoidance) {
rvo_agent_3d.neighborDist_ = neighbor_distance;
} else {
rvo_agent_2d.neighborDist_ = neighbor_distance;
}
agent_dirty = true;
}
void NavAgent::set_max_neighbors(int p_max_neighbors) {
max_neighbors = p_max_neighbors;
if (use_3d_avoidance) {
rvo_agent_3d.maxNeighbors_ = max_neighbors;
} else {
rvo_agent_2d.maxNeighbors_ = max_neighbors;
}
agent_dirty = true;
}
void NavAgent::set_time_horizon_agents(real_t p_time_horizon) {
time_horizon_agents = p_time_horizon;
if (use_3d_avoidance) {
rvo_agent_3d.timeHorizon_ = time_horizon_agents;
} else {
rvo_agent_2d.timeHorizon_ = time_horizon_agents;
}
agent_dirty = true;
}
void NavAgent::set_time_horizon_obstacles(real_t p_time_horizon) {
time_horizon_obstacles = p_time_horizon;
if (use_3d_avoidance) {
rvo_agent_3d.timeHorizonObst_ = time_horizon_obstacles;
} else {
rvo_agent_2d.timeHorizonObst_ = time_horizon_obstacles;
}
agent_dirty = true;
}
void NavAgent::set_radius(real_t p_radius) {
radius = p_radius;
if (use_3d_avoidance) {
rvo_agent_3d.radius_ = radius;
} else {
rvo_agent_2d.radius_ = radius;
}
agent_dirty = true;
}
void NavAgent::set_height(real_t p_height) {
height = p_height;
if (use_3d_avoidance) {
rvo_agent_3d.height_ = height;
} else {
rvo_agent_2d.height_ = height;
}
agent_dirty = true;
}
void NavAgent::set_max_speed(real_t p_max_speed) {
max_speed = p_max_speed;
if (avoidance_enabled) {
if (use_3d_avoidance) {
rvo_agent_3d.maxSpeed_ = max_speed;
} else {
rvo_agent_2d.maxSpeed_ = max_speed;
}
}
agent_dirty = true;
}
void NavAgent::set_position(const Vector3 p_position) {
position = p_position;
if (avoidance_enabled) {
if (use_3d_avoidance) {
rvo_agent_3d.position_ = RVO3D::Vector3(p_position.x, p_position.y, p_position.z);
} else {
rvo_agent_2d.elevation_ = p_position.y;
rvo_agent_2d.position_ = RVO2D::Vector2(p_position.x, p_position.z);
}
}
agent_dirty = true;
}
void NavAgent::set_target_position(const Vector3 p_target_position) {
target_position = p_target_position;
}
void NavAgent::set_velocity(const Vector3 p_velocity) {
// Sets the "wanted" velocity for an agent as a suggestion
// This velocity is not guaranteed, RVO simulation will only try to fulfill it
velocity = p_velocity;
if (avoidance_enabled) {
if (use_3d_avoidance) {
rvo_agent_3d.prefVelocity_ = RVO3D::Vector3(velocity.x, velocity.y, velocity.z);
} else {
rvo_agent_2d.prefVelocity_ = RVO2D::Vector2(velocity.x, velocity.z);
}
}
agent_dirty = true;
}
void NavAgent::set_velocity_forced(const Vector3 p_velocity) {
// This function replaces the internal rvo simulation velocity
// should only be used after the agent was teleported
// as it destroys consistency in movement in cramped situations
// use velocity instead to update with a safer "suggestion"
velocity_forced = p_velocity;
if (avoidance_enabled) {
if (use_3d_avoidance) {
rvo_agent_3d.velocity_ = RVO3D::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
} else {
rvo_agent_2d.velocity_ = RVO2D::Vector2(p_velocity.x, p_velocity.z);
}
}
agent_dirty = true;
}
void NavAgent::update() {
// Updates this agent with the calculated results from the rvo simulation
if (avoidance_enabled) {
if (use_3d_avoidance) {
velocity = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z());
} else {
velocity = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y());
}
}
}
void NavAgent::set_avoidance_mask(uint32_t p_mask) {
avoidance_mask = p_mask;
if (use_3d_avoidance) {
rvo_agent_3d.avoidance_mask_ = avoidance_mask;
} else {
rvo_agent_2d.avoidance_mask_ = avoidance_mask;
}
agent_dirty = true;
}
void NavAgent::set_avoidance_layers(uint32_t p_layers) {
avoidance_layers = p_layers;
if (use_3d_avoidance) {
rvo_agent_3d.avoidance_layers_ = avoidance_layers;
} else {
rvo_agent_2d.avoidance_layers_ = avoidance_layers;
}
agent_dirty = true;
}
void NavAgent::set_avoidance_priority(real_t p_priority) {
ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
avoidance_priority = p_priority;
if (use_3d_avoidance) {
rvo_agent_3d.avoidance_priority_ = avoidance_priority;
} else {
rvo_agent_2d.avoidance_priority_ = avoidance_priority;
}
agent_dirty = true;
};
bool NavAgent::check_dirty() {
const bool was_dirty = agent_dirty;
agent_dirty = false;
return was_dirty;
}
const Dictionary NavAgent::get_avoidance_data() const {
// Returns debug data from RVO simulation internals of this agent.
Dictionary _avoidance_data;
if (use_3d_avoidance) {
_avoidance_data["max_neighbors"] = int(rvo_agent_3d.maxNeighbors_);
_avoidance_data["max_speed"] = float(rvo_agent_3d.maxSpeed_);
_avoidance_data["neighbor_distance"] = float(rvo_agent_3d.neighborDist_);
_avoidance_data["new_velocity"] = Vector3(rvo_agent_3d.newVelocity_.x(), rvo_agent_3d.newVelocity_.y(), rvo_agent_3d.newVelocity_.z());
_avoidance_data["velocity"] = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z());
_avoidance_data["position"] = Vector3(rvo_agent_3d.position_.x(), rvo_agent_3d.position_.y(), rvo_agent_3d.position_.z());
_avoidance_data["prefered_velocity"] = Vector3(rvo_agent_3d.prefVelocity_.x(), rvo_agent_3d.prefVelocity_.y(), rvo_agent_3d.prefVelocity_.z());
_avoidance_data["radius"] = float(rvo_agent_3d.radius_);
_avoidance_data["time_horizon_agents"] = float(rvo_agent_3d.timeHorizon_);
_avoidance_data["time_horizon_obstacles"] = 0.0;
_avoidance_data["height"] = float(rvo_agent_3d.height_);
_avoidance_data["avoidance_layers"] = int(rvo_agent_3d.avoidance_layers_);
_avoidance_data["avoidance_mask"] = int(rvo_agent_3d.avoidance_mask_);
_avoidance_data["avoidance_priority"] = float(rvo_agent_3d.avoidance_priority_);
} else {
_avoidance_data["max_neighbors"] = int(rvo_agent_2d.maxNeighbors_);
_avoidance_data["max_speed"] = float(rvo_agent_2d.maxSpeed_);
_avoidance_data["neighbor_distance"] = float(rvo_agent_2d.neighborDist_);
_avoidance_data["new_velocity"] = Vector3(rvo_agent_2d.newVelocity_.x(), 0.0, rvo_agent_2d.newVelocity_.y());
_avoidance_data["velocity"] = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y());
_avoidance_data["position"] = Vector3(rvo_agent_2d.position_.x(), 0.0, rvo_agent_2d.position_.y());
_avoidance_data["prefered_velocity"] = Vector3(rvo_agent_2d.prefVelocity_.x(), 0.0, rvo_agent_2d.prefVelocity_.y());
_avoidance_data["radius"] = float(rvo_agent_2d.radius_);
_avoidance_data["time_horizon_agents"] = float(rvo_agent_2d.timeHorizon_);
_avoidance_data["time_horizon_obstacles"] = float(rvo_agent_2d.timeHorizonObst_);
_avoidance_data["height"] = float(rvo_agent_2d.height_);
_avoidance_data["avoidance_layers"] = int(rvo_agent_2d.avoidance_layers_);
_avoidance_data["avoidance_mask"] = int(rvo_agent_2d.avoidance_mask_);
_avoidance_data["avoidance_priority"] = float(rvo_agent_2d.avoidance_priority_);
}
return _avoidance_data;
}
void NavAgent::set_avoidance_callback(ObjectID p_id, const StringName p_method, const Variant p_udata) {
avoidance_callback.id = p_id;
avoidance_callback.method = p_method;
avoidance_callback.udata = p_udata;
}
bool NavAgent::has_avoidance_callback() const {
return avoidance_callback.id != 0;
}
void NavAgent::dispatch_avoidance_callback() {
if (avoidance_callback.id == 0) {
return;
}
Object *obj = ObjectDB::get_instance(avoidance_callback.id);
if (!obj) {
avoidance_callback.id = ObjectID(0);
return;
}
Variant::CallError responseCallError;
Vector3 new_velocity;
if (use_3d_avoidance) {
new_velocity = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z());
} else {
new_velocity = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y());
}
if (clamp_speed) {
new_velocity = new_velocity.limit_length(max_speed);
}
avoidance_callback.new_velocity = new_velocity;
const Variant *vp[2] = { &avoidance_callback.new_velocity, &avoidance_callback.udata };
int argc = (avoidance_callback.udata.get_type() == Variant::NIL) ? 1 : 2;
obj->call(avoidance_callback.method, vp, argc, responseCallError);
}

View File

@ -0,0 +1,160 @@
#ifndef NAV_AGENT_H
#define NAV_AGENT_H
/**************************************************************************/
/* nav_agent.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "core/containers/local_vector.h"
#include "core/object/class_db.h"
#include "nav_agent.h"
#include "nav_rid.h"
#include <Agent2d.h>
#include <Agent3d.h>
class NavMap;
class NavAgent : public NavRid {
struct AvoidanceComputedCallback {
ObjectID id;
StringName method;
Variant udata;
Variant new_velocity;
};
Vector3 position;
Vector3 target_position;
Vector3 velocity;
Vector3 velocity_forced;
real_t height;
real_t radius;
real_t max_speed;
real_t time_horizon_agents;
real_t time_horizon_obstacles;
int max_neighbors;
real_t neighbor_distance;
Vector3 safe_velocity;
bool clamp_speed; // Experimental, clamps velocity to max_speed.
NavMap *map = nullptr;
RVO2D::Agent2D rvo_agent_2d;
RVO3D::Agent3D rvo_agent_3d;
bool use_3d_avoidance;
bool avoidance_enabled;
uint32_t avoidance_layers;
uint32_t avoidance_mask;
real_t avoidance_priority;
AvoidanceComputedCallback avoidance_callback;
bool agent_dirty;
uint32_t map_update_id;
public:
NavAgent();
void set_avoidance_enabled(bool p_enabled);
bool is_avoidance_enabled() { return avoidance_enabled; }
void set_use_3d_avoidance(bool p_enabled);
bool get_use_3d_avoidance() { return use_3d_avoidance; }
void set_map(NavMap *p_map);
NavMap *get_map() { return map; }
bool is_map_changed();
RVO2D::Agent2D *get_rvo_agent_2d() { return &rvo_agent_2d; }
RVO3D::Agent3D *get_rvo_agent_3d() { return &rvo_agent_3d; }
void set_neighbor_distance(real_t p_neighbor_distance);
real_t get_neighbor_distance() const { return neighbor_distance; }
void set_max_neighbors(int p_max_neighbors);
int get_max_neighbors() const { return max_neighbors; }
void set_time_horizon_agents(real_t p_time_horizon);
real_t get_time_horizon_agents() const { return time_horizon_agents; }
void set_time_horizon_obstacles(real_t p_time_horizon);
real_t get_time_horizon_obstacles() const { return time_horizon_obstacles; }
void set_radius(real_t p_radius);
real_t get_radius() const { return radius; }
void set_height(real_t p_height);
real_t get_height() const { return height; }
void set_max_speed(real_t p_max_speed);
real_t get_max_speed() const { return max_speed; }
void set_position(const Vector3 p_position);
const Vector3 &get_position() const { return position; }
void set_target_position(const Vector3 p_target_position);
const Vector3 &get_target_position() const { return target_position; }
void set_velocity(const Vector3 p_velocity);
const Vector3 &get_velocity() const { return velocity; }
void set_velocity_forced(const Vector3 p_velocity);
const Vector3 &get_velocity_forced() const { return velocity_forced; }
void set_avoidance_layers(uint32_t p_layers);
uint32_t get_avoidance_layers() const { return avoidance_layers; };
void set_avoidance_mask(uint32_t p_mask);
uint32_t get_avoidance_mask() const { return avoidance_mask; };
void set_avoidance_priority(real_t p_priority);
real_t get_avoidance_priority() const { return avoidance_priority; };
bool check_dirty();
// Updates this agent with rvo data after the rvo simulation avoidance step.
void update();
// RVO debug data from the last frame update.
const Dictionary get_avoidance_data() const;
void set_avoidance_callback(ObjectID p_id, const StringName p_method, const Variant p_udata = Variant());
bool has_avoidance_callback() const;
void dispatch_avoidance_callback();
private:
void _update_rvo_agent_properties();
};
#endif // NAV_AGENT_H

View File

@ -30,13 +30,16 @@
#include "nav_map.h"
#include "core/config/project_settings.h"
#include "core/os/threaded_array_processor.h"
#include "nav_agent.h"
#include "nav_link.h"
#include "nav_obstacle.h"
#include "nav_region.h"
#include "rvo_agent.h"
#include "scene/resources/mesh.h"
#include "scene/resources/navigation_mesh.h"
#include <algorithm>
#include <Obstacle2d.h>
#define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a)))
@ -593,7 +596,7 @@ void NavMap::add_region(NavRegion *p_region) {
void NavMap::remove_region(NavRegion *p_region) {
int64_t region_index = regions.find(p_region);
if (region_index != -1) {
if (region_index >= 0) {
regions.remove_unordered(region_index);
regenerate_links = true;
}
@ -606,47 +609,79 @@ void NavMap::add_link(NavLink *p_link) {
void NavMap::remove_link(NavLink *p_link) {
int64_t link_index = links.find(p_link);
if (link_index != -1) {
if (link_index >= 0) {
links.remove_unordered(link_index);
regenerate_links = true;
}
}
bool NavMap::has_agent(RvoAgent *agent) const {
return (agents.find(agent) != -1);
bool NavMap::has_agent(NavAgent *agent) const {
return (agents.find(agent) >= 0);
}
void NavMap::add_agent(RvoAgent *agent) {
void NavMap::add_agent(NavAgent *agent) {
if (!has_agent(agent)) {
agents.push_back(agent);
agents_dirty = true;
}
}
void NavMap::remove_agent(RvoAgent *agent) {
void NavMap::remove_agent(NavAgent *agent) {
remove_agent_as_controlled(agent);
int64_t agent_index = agents.find(agent);
if (agent_index != -1) {
if (agent_index >= 0) {
agents.remove_unordered(agent_index);
agents_dirty = true;
}
}
void NavMap::set_agent_as_controlled(RvoAgent *agent) {
const bool exist = (controlled_agents.find(agent) != -1);
bool NavMap::has_obstacle(NavObstacle *obstacle) const {
return (obstacles.find(obstacle) >= 0);
}
if (!exist) {
ERR_FAIL_COND(!has_agent(agent));
controlled_agents.push_back(agent);
void NavMap::add_obstacle(NavObstacle *obstacle) {
if (!has_obstacle(obstacle)) {
obstacles.push_back(obstacle);
obstacles_dirty = true;
}
}
void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
int64_t active_avoidance_agent_index = controlled_agents.find(agent);
if (active_avoidance_agent_index != -1) {
controlled_agents.remove_unordered(active_avoidance_agent_index);
void NavMap::remove_obstacle(NavObstacle *obstacle) {
int64_t obstacle_index = obstacles.find(obstacle);
if (obstacle_index >= 0) {
obstacles.remove_unordered(obstacle_index);
obstacles_dirty = true;
}
}
void NavMap::set_agent_as_controlled(NavAgent *agent) {
remove_agent_as_controlled(agent);
if (agent->get_use_3d_avoidance()) {
int64_t agent_3d_index = active_3d_avoidance_agents.find(agent);
if (agent_3d_index < 0) {
active_3d_avoidance_agents.push_back(agent);
agents_dirty = true;
}
} else {
int64_t agent_2d_index = active_2d_avoidance_agents.find(agent);
if (agent_2d_index < 0) {
active_2d_avoidance_agents.push_back(agent);
agents_dirty = true;
}
}
}
void NavMap::remove_agent_as_controlled(NavAgent *agent) {
int64_t agent_3d_index = active_3d_avoidance_agents.find(agent);
if (agent_3d_index >= 0) {
active_3d_avoidance_agents.remove_unordered(agent_3d_index);
agents_dirty = true;
}
int64_t agent_2d_index = active_2d_avoidance_agents.find(agent);
if (agent_2d_index >= 0) {
active_2d_avoidance_agents.remove_unordered(agent_2d_index);
agents_dirty = true;
}
}
@ -951,20 +986,30 @@ void NavMap::sync() {
map_update_id = (map_update_id + 1) % 9999999;
}
// Update agents tree.
if (agents_dirty) {
// cannot use LocalVector here as RVO library expects std::vector to build KdTree
std::vector<RVO::Agent *> raw_agents;
raw_agents.reserve(agents.size());
for (size_t i(0); i < agents.size(); i++) {
raw_agents.push_back(agents[i]->get_agent());
}
// Do we have modified obstacle positions?
for (uint32_t i = 0; i < obstacles.size(); ++i) {
NavObstacle *obstacle = obstacles[i];
rvo.buildAgentTree(raw_agents);
if (obstacle->check_dirty()) {
obstacles_dirty = true;
}
}
// Do we have modified agent arrays?
for (uint32_t i = 0; i < agents.size(); ++i) {
NavAgent *agent = agents[i];
if (agent->check_dirty()) {
agents_dirty = true;
}
}
// Update avoidance worlds.
if (obstacles_dirty || agents_dirty) {
_update_rvo_simulation();
}
regenerate_polygons = false;
regenerate_links = false;
obstacles_dirty = false;
agents_dirty = false;
// Performance Monitor
@ -978,35 +1023,197 @@ void NavMap::sync() {
pm_edge_free_count = _new_pm_edge_free_count;
}
void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) {
(*(agent + index))->get_agent()->computeNeighbors(&rvo);
(*(agent + index))->get_agent()->computeNewVelocity(deltatime);
void NavMap::_update_rvo_obstacles_tree_2d() {
int obstacle_vertex_count = 0;
for (uint32_t i = 0; i < obstacles.size(); ++i) {
NavObstacle *obstacle = obstacles[i];
obstacle_vertex_count += obstacle->get_vertices().size();
}
// Cannot use LocalVector here as RVO library expects std::vector to build KdTree
std::vector<RVO2D::Obstacle2D *> raw_obstacles;
raw_obstacles.reserve(obstacle_vertex_count);
// The following block is modified copy from RVO2D::AddObstacle()
// Obstacles are linked and depend on all other obstacles.
for (uint32_t k = 0; k < obstacles.size(); ++k) {
NavObstacle *obstacle = obstacles[k];
const Vector3 &_obstacle_position = obstacle->get_position();
const Vector<Vector3> &_obstacle_vertices = obstacle->get_vertices();
if (_obstacle_vertices.size() < 2) {
continue;
}
std::vector<RVO2D::Vector2> rvo_2d_vertices;
rvo_2d_vertices.reserve(_obstacle_vertices.size());
uint32_t _obstacle_avoidance_layers = obstacle->get_avoidance_layers();
for (int i = 0; i < _obstacle_vertices.size(); ++i) {
const Vector3 &_obstacle_vertex = _obstacle_vertices[i];
rvo_2d_vertices.push_back(RVO2D::Vector2(_obstacle_vertex.x + _obstacle_position.x, _obstacle_vertex.z + _obstacle_position.z));
}
const size_t obstacleNo = raw_obstacles.size();
for (size_t i = 0; i < rvo_2d_vertices.size(); i++) {
RVO2D::Obstacle2D *rvo_2d_obstacle = new RVO2D::Obstacle2D();
rvo_2d_obstacle->point_ = rvo_2d_vertices[i];
rvo_2d_obstacle->avoidance_layers_ = _obstacle_avoidance_layers;
if (i != 0) {
rvo_2d_obstacle->prevObstacle_ = raw_obstacles.back();
rvo_2d_obstacle->prevObstacle_->nextObstacle_ = rvo_2d_obstacle;
}
if (i == rvo_2d_vertices.size() - 1) {
rvo_2d_obstacle->nextObstacle_ = raw_obstacles[obstacleNo];
rvo_2d_obstacle->nextObstacle_->prevObstacle_ = rvo_2d_obstacle;
}
rvo_2d_obstacle->unitDir_ = normalize(rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)] - rvo_2d_vertices[i]);
if (rvo_2d_vertices.size() == 2) {
rvo_2d_obstacle->isConvex_ = true;
} else {
rvo_2d_obstacle->isConvex_ = (leftOf(rvo_2d_vertices[(i == 0 ? rvo_2d_vertices.size() - 1 : i - 1)], rvo_2d_vertices[i], rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)]) >= 0.0f);
}
rvo_2d_obstacle->id_ = raw_obstacles.size();
raw_obstacles.push_back(rvo_2d_obstacle);
}
}
rvo_simulation_2d.kdTree_->buildObstacleTree(raw_obstacles);
}
void NavMap::_update_rvo_agents_tree_2d() {
// Cannot use LocalVector here as RVO library expects std::vector to build KdTree.
std::vector<RVO2D::Agent2D *> raw_agents;
raw_agents.reserve(active_2d_avoidance_agents.size());
for (uint32_t k = 0; k < active_2d_avoidance_agents.size(); ++k) {
NavAgent *agent = active_2d_avoidance_agents[k];
raw_agents.push_back(agent->get_rvo_agent_2d());
}
rvo_simulation_2d.kdTree_->buildAgentTree(raw_agents);
}
void NavMap::_update_rvo_agents_tree_3d() {
// Cannot use LocalVector here as RVO library expects std::vector to build KdTree.
std::vector<RVO3D::Agent3D *> raw_agents;
raw_agents.reserve(active_3d_avoidance_agents.size());
for (uint32_t k = 0; k < active_3d_avoidance_agents.size(); ++k) {
NavAgent *agent = active_3d_avoidance_agents[k];
raw_agents.push_back(agent->get_rvo_agent_3d());
}
rvo_simulation_3d.kdTree_->buildAgentTree(raw_agents);
}
void NavMap::_update_rvo_simulation() {
if (obstacles_dirty) {
_update_rvo_obstacles_tree_2d();
}
if (agents_dirty) {
_update_rvo_agents_tree_2d();
_update_rvo_agents_tree_3d();
}
}
void NavMap::compute_single_avoidance_step_2d(uint32_t index, NavAgent **agent) {
(*(agent + index))->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);
(*(agent + index))->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);
(*(agent + index))->get_rvo_agent_2d()->update(&rvo_simulation_2d);
(*(agent + index))->update();
}
void NavMap::compute_single_avoidance_step_3d(uint32_t index, NavAgent **agent) {
(*(agent + index))->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);
(*(agent + index))->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);
(*(agent + index))->get_rvo_agent_3d()->update(&rvo_simulation_3d);
(*(agent + index))->update();
}
void NavMap::step(real_t p_deltatime) {
deltatime = p_deltatime;
if (controlled_agents.size() > 0) {
if (active_2d_avoidance_agents.size() > 0) {
#ifndef NO_THREADS
if (step_work_pool.get_thread_count() == 0) {
step_work_pool.init();
if (use_threads && avoidance_use_multiple_threads) {
if (step_work_pool.get_thread_count() == 0) {
step_work_pool.init();
}
step_work_pool.do_work(
active_2d_avoidance_agents.size(),
this,
&NavMap::compute_single_avoidance_step_2d,
active_2d_avoidance_agents.ptr());
} else {
for (int i(0); i < static_cast<int>(active_2d_avoidance_agents.size()); i++) {
NavAgent *agent = active_2d_avoidance_agents[i];
agent->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);
agent->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);
agent->get_rvo_agent_2d()->update(&rvo_simulation_2d);
agent->update();
}
}
step_work_pool.do_work(
controlled_agents.size(),
this,
&NavMap::compute_single_step,
controlled_agents.ptr());
#else
for (int i(0); i < static_cast<int>(controlled_agents.size()); i++) {
controlled_agents[i]->get_agent()->computeNeighbors(&rvo);
controlled_agents[i]->get_agent()->computeNewVelocity(deltatime);
for (int i(0); i < static_cast<int>(active_2d_avoidance_agents.size()); i++) {
NavAgent *agent = active_2d_avoidance_agents[i];
agent->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d);
agent->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d);
agent->get_rvo_agent_2d()->update(&rvo_simulation_2d);
agent->update();
}
#endif // NO_THREADS
}
if (active_3d_avoidance_agents.size() > 0) {
#ifndef NO_THREADS
if (use_threads && avoidance_use_multiple_threads) {
if (step_work_pool.get_thread_count() == 0) {
step_work_pool.init();
}
step_work_pool.do_work(
active_3d_avoidance_agents.size(),
this,
&NavMap::compute_single_avoidance_step_3d,
active_3d_avoidance_agents.ptr());
} else {
for (int i(0); i < static_cast<int>(active_3d_avoidance_agents.size()); i++) {
NavAgent *agent = active_3d_avoidance_agents[i];
agent->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);
agent->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);
agent->get_rvo_agent_3d()->update(&rvo_simulation_3d);
agent->update();
}
}
#else
for (int i(0); i < static_cast<int>(active_3d_avoidance_agents.size()); i++) {
NavAgent *agent = active_3d_avoidance_agents[i];
agent->get_rvo_agent_3d()->computeNeighbors(&rvo_simulation_3d);
agent->get_rvo_agent_3d()->computeNewVelocity(&rvo_simulation_3d);
agent->get_rvo_agent_3d()->update(&rvo_simulation_3d);
agent->update();
}
#endif // NO_THREADS
}
}
void NavMap::dispatch_callbacks() {
for (int i(0); i < static_cast<int>(controlled_agents.size()); i++) {
controlled_agents[i]->dispatch_callback();
for (int i(0); i < static_cast<int>(active_2d_avoidance_agents.size()); i++) {
active_2d_avoidance_agents[i]->dispatch_avoidance_callback();
}
for (int i(0); i < static_cast<int>(active_3d_avoidance_agents.size()); i++) {
active_3d_avoidance_agents[i]->dispatch_avoidance_callback();
}
}
@ -1054,6 +1261,11 @@ NavMap::NavMap() {
regenerate_polygons = true;
regenerate_links = true;
agents_dirty = false;
agents_dirty = true;
obstacles_dirty = true;
use_threads = true;
avoidance_use_multiple_threads = true;
avoidance_use_high_priority_threads = true;
deltatime = 0.0;
map_update_id = 0;
link_connection_radius = 1.0;
@ -1068,6 +1280,9 @@ NavMap::NavMap() {
pm_edge_merge_count = 0;
pm_edge_connection_count = 0;
pm_edge_free_count = 0;
avoidance_use_multiple_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_multiple_threads");
avoidance_use_high_priority_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_high_priority_threads");
}
NavMap::~NavMap() {

View File

@ -38,12 +38,17 @@
#include "core/os/thread_work_pool.h"
#include "nav_utils.h"
#include <KdTree.h>
#include <KdTree2d.h>
#include <RVOSimulator2d.h>
#include <KdTree3d.h>
#include <RVOSimulator3d.h>
class NavLink;
class NavRegion;
class RvoAgent;
class NavAgent;
class NavRegion;
class NavObstacle;
class NavMap : public NavRid {
/// Map Up
@ -75,17 +80,25 @@ class NavMap : public NavRid {
/// Map polygons
LocalVector<gd::Polygon> polygons;
/// Rvo world
RVO::KdTree rvo;
/// RVO avoidance worlds
RVO2D::RVOSimulator2D rvo_simulation_2d;
RVO3D::RVOSimulator3D rvo_simulation_3d;
/// Is agent array modified?
bool agents_dirty;
/// avoidance controlled agents
LocalVector<NavAgent *> active_2d_avoidance_agents;
LocalVector<NavAgent *> active_3d_avoidance_agents;
/// dirty flag when one of the agent's arrays are modified
bool agents_dirty = true;
/// All the Agents (even the controlled one)
LocalVector<RvoAgent *> agents;
LocalVector<NavAgent *> agents;
/// Controlled agents
LocalVector<RvoAgent *> controlled_agents;
/// All the avoidance obstacles (both static and dynamic)
LocalVector<NavObstacle *> obstacles;
/// Are rvo obstacles modified?
bool obstacles_dirty;
/// Physics delta time
real_t deltatime;
@ -93,6 +106,10 @@ class NavMap : public NavRid {
/// Change the id each time the map is updated.
uint32_t map_update_id;
bool use_threads;
bool avoidance_use_multiple_threads;
bool avoidance_use_high_priority_threads;
// Performance Monitor
int pm_region_count;
int pm_agent_count;
@ -163,15 +180,22 @@ public:
return links;
}
bool has_agent(RvoAgent *agent) const;
void add_agent(RvoAgent *agent);
void remove_agent(RvoAgent *agent);
const LocalVector<RvoAgent *> &get_agents() const {
bool has_agent(NavAgent *agent) const;
void add_agent(NavAgent *agent);
void remove_agent(NavAgent *agent);
const LocalVector<NavAgent *> &get_agents() const {
return agents;
}
void set_agent_as_controlled(RvoAgent *agent);
void remove_agent_as_controlled(RvoAgent *agent);
void set_agent_as_controlled(NavAgent *agent);
void remove_agent_as_controlled(NavAgent *agent);
bool has_obstacle(NavObstacle *obstacle) const;
void add_obstacle(NavObstacle *obstacle);
void remove_obstacle(NavObstacle *obstacle);
const LocalVector<NavObstacle *> &get_obstacles() const {
return obstacles;
}
uint32_t get_map_update_id() const {
return map_update_id;
@ -192,8 +216,15 @@ public:
int get_pm_edge_free_count() const { return pm_edge_free_count; }
private:
void compute_single_step(uint32_t index, RvoAgent **agent);
void compute_single_avoidance_step_2d(uint32_t index, NavAgent **agent);
void compute_single_avoidance_step_3d(uint32_t index, NavAgent **agent);
void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, Array *r_path_rids, Vector<uint64_t> *r_path_owners) const;
void _update_rvo_simulation();
void _update_rvo_obstacles_tree_2d();
void _update_rvo_agents_tree_2d();
void _update_rvo_agents_tree_3d();
};
#endif // NAV_MAP_H

View File

@ -1,48 +1,74 @@
/*************************************************************************/
/* rvo_agent.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/**************************************************************************/
/* nav_obstacle.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "rvo_agent.h"
#include "nav_obstacle.h"
#include "nav_map.h"
RvoAgent::RvoAgent() {
NavObstacle::NavObstacle() {
map = nullptr;
callback.id = ObjectID(0);
height = 0.0;
avoidance_layers = 1;
obstacle_dirty = true;
map_update_id = 0;
}
void RvoAgent::set_map(NavMap *p_map) {
map = p_map;
NavObstacle::~NavObstacle() {}
void NavObstacle::set_map(NavMap *p_map) {
if (map != p_map) {
map = p_map;
obstacle_dirty = true;
}
}
bool RvoAgent::is_map_changed() {
void NavObstacle::set_position(const Vector3 p_position) {
if (position != p_position) {
position = p_position;
obstacle_dirty = true;
}
}
void NavObstacle::set_height(const real_t p_height) {
if (height != p_height) {
height = p_height;
obstacle_dirty = true;
}
}
void NavObstacle::set_vertices(const Vector<Vector3> &p_vertices) {
vertices = p_vertices;
obstacle_dirty = true;
}
bool NavObstacle::is_map_changed() {
if (map) {
bool is_changed = map->get_map_update_id() != map_update_id;
map_update_id = map->get_map_update_id();
@ -52,31 +78,13 @@ bool RvoAgent::is_map_changed() {
}
}
void RvoAgent::set_callback(ObjectID p_id, const StringName p_method, const Variant p_udata) {
callback.id = p_id;
callback.method = p_method;
callback.udata = p_udata;
void NavObstacle::set_avoidance_layers(uint32_t p_layers) {
avoidance_layers = p_layers;
obstacle_dirty = true;
}
bool RvoAgent::has_callback() const {
return callback.id != 0;
}
void RvoAgent::dispatch_callback() {
if (callback.id == 0) {
return;
}
Object *obj = ObjectDB::get_instance(callback.id);
if (!obj) {
callback.id = ObjectID(0);
return;
}
Variant::CallError responseCallError;
callback.new_velocity = Vector3(agent.newVelocity_.x(), agent.newVelocity_.y(), agent.newVelocity_.z());
const Variant *vp[2] = { &callback.new_velocity, &callback.udata };
int argc = (callback.udata.get_type() == Variant::NIL) ? 1 : 2;
obj->call(callback.method, vp, argc, responseCallError);
bool NavObstacle::check_dirty() {
const bool was_dirty = obstacle_dirty;
obstacle_dirty = false;
return was_dirty;
}

View File

@ -1,73 +1,79 @@
#ifndef RVO_AGENT_H
#define RVO_AGENT_H
/*************************************************************************/
/* rvo_agent.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "core/object/object.h"
#ifndef NAV_OBSTACLE_H
#define NAV_OBSTACLE_H
/**************************************************************************/
/* nav_obstacle.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "core/object/class_db.h"
#include "core/containers/local_vector.h"
#include "nav_agent.h"
#include "nav_rid.h"
#include <Agent.h>
class NavMap;
class RvoAgent : public NavRid {
struct AvoidanceComputedCallback {
ObjectID id;
StringName method;
Variant udata;
Variant new_velocity;
};
class NavObstacle : public NavRid {
NavMap *map;
RVO::Agent agent;
AvoidanceComputedCallback callback;
Vector3 position;
Vector<Vector3> vertices;
real_t height;
uint32_t avoidance_layers;
bool obstacle_dirty;
uint32_t map_update_id;
public:
RvoAgent();
NavObstacle();
~NavObstacle();
void set_map(NavMap *p_map);
NavMap *get_map() {
return map;
}
NavMap *get_map() { return map; }
RVO::Agent *get_agent() {
return &agent;
}
void set_position(const Vector3 p_position);
const Vector3 &get_position() const { return position; }
void set_height(const real_t p_height);
real_t get_height() const { return height; }
void set_vertices(const Vector<Vector3> &p_vertices);
const Vector<Vector3> &get_vertices() const { return vertices; }
bool is_map_changed();
void set_callback(ObjectID p_id, const StringName p_method, const Variant p_udata = Variant());
bool has_callback() const;
void set_avoidance_layers(uint32_t p_layers);
uint32_t get_avoidance_layers() const { return avoidance_layers; };
void dispatch_callback();
bool check_dirty();
};
#endif // RVO_AGENT_H
#endif // NAV_OBSTACLE_H

View File

@ -158,6 +158,15 @@ Vector<Vector2> vector_v3_to_v2(const Vector<Vector3> &d) {
return nd;
}
static Vector<Vector3> vector_v2_to_v3(const Vector<Vector2> &d) {
Vector<Vector3> nd;
nd.resize(d.size());
for (int i(0); i < nd.size(); i++) {
nd.write[i] = v2_to_v3(d[i]);
}
return nd;
}
PoolVector<Vector2> poolvector_v3_to_v2(const PoolVector<Vector3> &d) {
PoolVector<Vector2> nd;
nd.resize(d.size());
@ -221,6 +230,8 @@ Array FORWARD_1_C(map_get_regions, RID, p_map, rid_to_rid);
Array FORWARD_1_C(map_get_agents, RID, p_map, rid_to_rid);
Array FORWARD_1_C(map_get_obstacles, RID, p_map, rid_to_rid);
RID FORWARD_1_C(region_get_map, RID, p_region, rid_to_rid);
RID FORWARD_1_C(agent_get_map, RID, p_agent, rid_to_rid);
@ -302,36 +313,56 @@ ObjectID FORWARD_1_C(link_get_owner_id, RID, p_link, rid_to_rid);
RID PandemoniumNavigation2DServer::agent_create() {
RID agent = NavigationServer::get_singleton()->agent_create();
NavigationServer::get_singleton()->agent_set_ignore_y(agent, true);
return agent;
}
void FORWARD_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(agent_get_avoidance_enabled, RID, p_agent, rid_to_rid);
void FORWARD_2(agent_set_map, RID, p_agent, RID, p_map, rid_to_rid, rid_to_rid);
void FORWARD_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_neighbors, RID, p_agent, int, p_count, rid_to_rid, int_to_int);
void FORWARD_2(agent_set_time_horizon, RID, p_agent, real_t, p_time, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_radius, RID, p_agent, real_t, p_radius, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed, rid_to_rid, real_to_real);
void FORWARD_2(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_velocity_forced, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_target_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3);
void FORWARD_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid);
void FORWARD_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, rid_to_rid, id_to_id, sn_to_sn, var_to_var);
void FORWARD_1(free, RID, p_object, rid_to_rid);
void FORWARD_4(agent_set_avoidance_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, rid_to_rid, id_to_id, sn_to_sn, var_to_var);
void FORWARD_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
void FORWARD_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask, rid_to_rid, uint32_to_uint32);
void FORWARD_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority, rid_to_rid, real_to_real);
RID PandemoniumNavigation2DServer::obstacle_create() {
RID obstacle = NavigationServer::get_singleton()->obstacle_create();
return obstacle;
}
void FORWARD_2(obstacle_set_map, RID, p_obstacle, RID, p_map, rid_to_rid, rid_to_rid);
RID FORWARD_1_C(obstacle_get_map, RID, p_obstacle, rid_to_rid);
void FORWARD_2(obstacle_set_position, RID, p_obstacle, Vector2, p_position, rid_to_rid, v2_to_v3);
void FORWARD_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers, rid_to_rid, uint32_to_uint32);
void PandemoniumNavigation2DServer::obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) {
NavigationServer::get_singleton()->obstacle_set_vertices(p_obstacle, vector_v2_to_v3(p_vertices));
}
NavigationUtilities::PathQueryResult2D PandemoniumNavigation2DServer::_query_path(const NavigationUtilities::PathQueryParameters2D &p_parameters) const {
NavigationUtilities::PathQueryParameters params;
params.pathfinding_algorithm = p_parameters.pathfinding_algorithm;

View File

@ -91,6 +91,7 @@ public:
virtual Array map_get_links(RID p_map) const;
virtual Array map_get_regions(RID p_map) const;
virtual Array map_get_agents(RID p_map) const;
virtual Array map_get_obstacles(RID p_map) const;
virtual void map_force_update(RID p_map);
@ -175,6 +176,9 @@ public:
virtual void agent_set_map(RID p_agent, RID p_map);
virtual RID agent_get_map(RID p_agent) const;
virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled);
virtual bool agent_get_avoidance_enabled(RID p_agent) const;
/// The maximum distance (center point to
/// center point) to other agents this agent
/// takes into account in the navigation. The
@ -200,7 +204,8 @@ public:
/// other agents, but the less freedom this
/// agent has in choosing its velocities.
/// Must be positive.
virtual void agent_set_time_horizon(RID p_agent, real_t p_time);
virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon);
virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon);
/// The radius of this agent.
/// Must be non-negative.
@ -210,23 +215,33 @@ public:
/// Must be non-negative.
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed);
/// Current velocity of the agent
virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity);
/// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly
virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity);
/// The new target velocity.
virtual void agent_set_target_velocity(RID p_agent, Vector2 p_velocity);
/// The wanted velocity for the agent as a "suggestion" to the avoidance simulation.
/// The simulation will try to fulfil this velocity wish if possible but may change the velocity depending on other agent's and obstacles'.
virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity);
/// Position of the agent in world space.
virtual void agent_set_position(RID p_agent, Vector2 p_position);
/// Agent ignore the Y axis and avoid collisions by moving only on the horizontal plane
virtual void agent_set_ignore_y(RID p_agent, bool p_ignore);
/// Returns true if the map got changed the previous frame.
virtual bool agent_is_map_changed(RID p_agent) const;
/// Callback called at the end of the RVO process
virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant());
virtual void agent_set_avoidance_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant());
virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers);
virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask);
virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority);
/// Creates the obstacle.
virtual RID obstacle_create();
virtual void obstacle_set_map(RID p_obstacle, RID p_map);
virtual RID obstacle_get_map(RID p_obstacle) const;
virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position);
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices);
virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers);
/// Destroy the `RID`
virtual void free(RID p_object);

View File

@ -115,6 +115,15 @@ using namespace NavigationUtilities;
PandemoniumNavigationServer::PandemoniumNavigationServer() {
active = true;
pm_region_count = 0;
pm_agent_count = 0;
pm_link_count = 0;
pm_polygon_count = 0;
pm_edge_count = 0;
pm_edge_merge_count = 0;
pm_edge_connection_count = 0;
pm_edge_free_count = 0;
}
PandemoniumNavigationServer::~PandemoniumNavigationServer() {
@ -326,7 +335,7 @@ Array PandemoniumNavigationServer::map_get_agents(RID p_map) const {
const NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND_V(map == nullptr, agents_rids);
const LocalVector<RvoAgent *> agents = map->get_agents();
const LocalVector<NavAgent *> agents = map->get_agents();
agents_rids.resize(agents.size());
for (uint32_t i = 0; i < agents.size(); i++) {
@ -336,6 +345,18 @@ Array PandemoniumNavigationServer::map_get_agents(RID p_map) const {
return agents_rids;
}
Array PandemoniumNavigationServer::map_get_obstacles(RID p_map) const {
Array obstacles_rids;
const NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND_V(map == nullptr, obstacles_rids);
const LocalVector<NavObstacle *> obstacles = map->get_obstacles();
obstacles_rids.resize(obstacles.size());
for (uint32_t i = 0; i < obstacles.size(); i++) {
obstacles_rids[i] = obstacles[i]->get_self();
}
return obstacles_rids;
}
RID PandemoniumNavigationServer::region_get_map(RID p_region) const {
NavRegion *region = region_owner.getornull(p_region);
ERR_FAIL_COND_V(region == nullptr, RID());
@ -348,7 +369,7 @@ RID PandemoniumNavigationServer::region_get_map(RID p_region) const {
}
RID PandemoniumNavigationServer::agent_get_map(RID p_agent) const {
RvoAgent *agent = agent_owner.getornull(p_agent);
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND_V(agent == nullptr, RID());
if (agent->get_map()) {
@ -648,14 +669,42 @@ ObjectID PandemoniumNavigationServer::link_get_owner_id(RID p_link) const {
RID PandemoniumNavigationServer::agent_create() {
PandemoniumNavigationServer *mut_this = const_cast<PandemoniumNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
RvoAgent *agent = memnew(RvoAgent());
NavAgent *agent = memnew(NavAgent());
RID rid = agent_owner.make_rid(agent);
agent->set_self(rid);
return rid;
}
COMMAND_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled) {
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->set_avoidance_enabled(p_enabled);
}
bool PandemoniumNavigationServer::agent_get_avoidance_enabled(RID p_agent) const {
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND_V(agent == nullptr, false);
return agent->is_avoidance_enabled();
}
COMMAND_2(agent_set_use_3d_avoidance, RID, p_agent, bool, p_enabled) {
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->set_use_3d_avoidance(p_enabled);
}
bool PandemoniumNavigationServer::agent_get_use_3d_avoidance(RID p_agent) const {
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND_V(agent == nullptr, false);
return agent->get_use_3d_avoidance();
}
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
RvoAgent *agent = agent_owner.getornull(p_agent);
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
if (agent->get_map()) {
@ -675,87 +724,99 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
agent->set_map(map);
map->add_agent(agent);
if (agent->has_callback()) {
if (agent->has_avoidance_callback()) {
map->set_agent_as_controlled(agent);
}
}
}
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist) {
RvoAgent *agent = agent_owner.getornull(p_agent);
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->neighborDist_ = p_dist;
agent->set_neighbor_distance(p_dist);
}
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) {
RvoAgent *agent = agent_owner.getornull(p_agent);
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->maxNeighbors_ = p_count;
agent->set_max_neighbors(p_count);
}
COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time) {
RvoAgent *agent = agent_owner.getornull(p_agent);
COMMAND_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon) {
ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->timeHorizon_ = p_time;
agent->set_time_horizon_agents(p_time_horizon);
}
COMMAND_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon) {
ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->set_time_horizon_obstacles(p_time_horizon);
}
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) {
RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->radius_ = p_radius;
agent->set_radius(p_radius);
}
COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height) {
ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive.");
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->set_height(p_height);
}
COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) {
RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->maxSpeed_ = p_max_speed;
agent->set_max_speed(p_max_speed);
}
COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) {
RvoAgent *agent = agent_owner.getornull(p_agent);
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->velocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
agent->set_velocity(p_velocity);
}
COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity) {
RvoAgent *agent = agent_owner.getornull(p_agent);
COMMAND_2(agent_set_velocity_forced, RID, p_agent, Vector3, p_velocity) {
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->prefVelocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
agent->set_velocity_forced(p_velocity);
}
COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) {
RvoAgent *agent = agent_owner.getornull(p_agent);
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->position_ = RVO::Vector3(p_position.x, p_position.y, p_position.z);
}
COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore) {
RvoAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->ignore_y_ = p_ignore;
agent->set_position(p_position);
}
bool PandemoniumNavigationServer::agent_is_map_changed(RID p_agent) const {
RvoAgent *agent = agent_owner.getornull(p_agent);
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND_V(agent == nullptr, false);
return agent->is_map_changed();
}
COMMAND_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata) {
RvoAgent *agent = agent_owner.getornull(p_agent);
COMMAND_4(agent_set_avoidance_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata) {
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->set_callback(p_object_id, p_method, p_udata);
agent->set_avoidance_callback(p_object_id, p_method, p_udata);
if (agent->get_map()) {
if (p_object_id == ObjectID()) {
@ -766,6 +827,91 @@ COMMAND_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p
}
}
COMMAND_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers) {
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->set_avoidance_layers(p_layers);
}
COMMAND_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask) {
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->set_avoidance_mask(p_mask);
}
COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority) {
ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
NavAgent *agent = agent_owner.getornull(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->set_avoidance_priority(p_priority);
}
RID PandemoniumNavigationServer::obstacle_create() {
PandemoniumNavigationServer *mut_this = const_cast<PandemoniumNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
NavObstacle *obstacle = memnew(NavObstacle());
RID rid = obstacle_owner.make_rid(obstacle);
obstacle->set_self(rid);
return rid;
}
COMMAND_2(obstacle_set_map, RID, p_obstacle, RID, p_map) {
NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
ERR_FAIL_COND(obstacle == nullptr);
if (obstacle->get_map()) {
if (obstacle->get_map()->get_self() == p_map) {
return; // Pointless
}
obstacle->get_map()->remove_obstacle(obstacle);
}
obstacle->set_map(nullptr);
if (p_map.is_valid()) {
NavMap *map = map_owner.getornull(p_map);
ERR_FAIL_COND(map == nullptr);
obstacle->set_map(map);
map->add_obstacle(obstacle);
}
}
RID PandemoniumNavigationServer::obstacle_get_map(RID p_obstacle) const {
NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
ERR_FAIL_COND_V(obstacle == nullptr, RID());
if (obstacle->get_map()) {
return obstacle->get_map()->get_self();
}
return RID();
}
COMMAND_2(obstacle_set_height, RID, p_obstacle, real_t, p_height) {
NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
ERR_FAIL_COND(obstacle == nullptr);
obstacle->set_height(p_height);
}
COMMAND_2(obstacle_set_position, RID, p_obstacle, Vector3, p_position) {
NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
ERR_FAIL_COND(obstacle == nullptr);
obstacle->set_position(p_position);
}
void PandemoniumNavigationServer::obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) {
NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
ERR_FAIL_COND(obstacle == nullptr);
obstacle->set_vertices(p_vertices);
}
COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) {
NavObstacle *obstacle = obstacle_owner.getornull(p_obstacle);
ERR_FAIL_COND(obstacle == nullptr);
obstacle->set_avoidance_layers(p_layers);
}
COMMAND_1(free, RID, p_object) {
if (!p_object.is_valid()) {
ERR_FAIL_MSG("Invalid RID.");
@ -790,12 +936,19 @@ COMMAND_1(free, RID, p_object) {
}
// Remove any assigned agent
LocalVector<RvoAgent *> agents = map->get_agents();
LocalVector<NavAgent *> agents = map->get_agents();
for (uint32_t i = 0; i < agents.size(); i++) {
map->remove_agent(agents[i]);
agents[i]->set_map(nullptr);
}
// Remove any assigned obstacles
LocalVector<NavObstacle *> obstacles = map->get_obstacles();
for (uint32_t i = 0; i < obstacles.size(); i++) {
map->remove_obstacle(obstacles[i]);
obstacles[i]->set_map(nullptr);
}
int map_index = active_maps.find(map);
active_maps.remove(map_index);
active_maps_update_id.remove(map_index);
@ -826,7 +979,7 @@ COMMAND_1(free, RID, p_object) {
link_owner.free(p_object);
} else if (agent_owner.owns(p_object)) {
RvoAgent *agent = agent_owner.getornull(p_object);
NavAgent *agent = agent_owner.getornull(p_object);
// Removes this agent from the map if assigned
if (agent->get_map() != nullptr) {
@ -837,6 +990,16 @@ COMMAND_1(free, RID, p_object) {
agent_owner.free(p_object);
memdelete(agent);
} else if (obstacle_owner.owns(p_object)) {
NavObstacle *obstacle = obstacle_owner.getornull(p_object);
// Removes this agent from the map if assigned
if (obstacle->get_map() != nullptr) {
obstacle->get_map()->remove_obstacle(obstacle);
obstacle->set_map(nullptr);
}
obstacle_owner.free(p_object);
} else {
ERR_FAIL_COND("Invalid RID.");
}

View File

@ -34,10 +34,11 @@
#include "core/containers/rid.h"
#include "nav_agent.h"
#include "nav_link.h"
#include "nav_map.h"
#include "nav_region.h"
#include "rvo_agent.h"
#include "nav_obstacle.h"
/// The commands are functions executed during the `sync` phase.
@ -73,21 +74,22 @@ class PandemoniumNavigationServer : public NavigationServer {
mutable RID_Owner<NavLink> link_owner;
mutable RID_Owner<NavMap> map_owner;
mutable RID_Owner<NavRegion> region_owner;
mutable RID_Owner<RvoAgent> agent_owner;
mutable RID_Owner<NavAgent> agent_owner;
mutable RID_Owner<NavObstacle> obstacle_owner;
bool active;
LocalVector<NavMap *> active_maps;
LocalVector<uint32_t> active_maps_update_id;
// Performance Monitor
int pm_region_count = 0;
int pm_agent_count = 0;
int pm_link_count = 0;
int pm_polygon_count = 0;
int pm_edge_count = 0;
int pm_edge_merge_count = 0;
int pm_edge_connection_count = 0;
int pm_edge_free_count = 0;
int pm_region_count;
int pm_agent_count;
int pm_link_count;
int pm_polygon_count;
int pm_edge_count;
int pm_edge_merge_count;
int pm_edge_connection_count;
int pm_edge_free_count;
public:
PandemoniumNavigationServer();
@ -129,6 +131,7 @@ public:
virtual Array map_get_links(RID p_map) const;
virtual Array map_get_regions(RID p_map) const;
virtual Array map_get_agents(RID p_map) const;
virtual Array map_get_obstacles(RID p_map) const;
virtual void map_force_update(RID p_map);
@ -176,19 +179,36 @@ public:
virtual ObjectID link_get_owner_id(RID p_link) const;
virtual RID agent_create();
COMMAND_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled);
virtual bool agent_get_avoidance_enabled(RID p_agent) const;
COMMAND_2(agent_set_use_3d_avoidance, RID, p_agent, bool, p_enabled);
virtual bool agent_get_use_3d_avoidance(RID p_agent) const;
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map);
virtual RID agent_get_map(RID p_agent) const;
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist);
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count);
COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time);
COMMAND_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon);
COMMAND_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon);
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius);
COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height);
COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed);
COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity);
COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity);
COMMAND_2(agent_set_velocity_forced, RID, p_agent, Vector3, p_velocity);
COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position);
COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore);
virtual bool agent_is_map_changed(RID p_agent) const;
COMMAND_4_DEF(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, Variant());
COMMAND_4_DEF(agent_set_avoidance_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, Variant());
COMMAND_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers);
COMMAND_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask);
COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority);
virtual RID obstacle_create();
COMMAND_2(obstacle_set_map, RID, p_obstacle, RID, p_map);
virtual RID obstacle_get_map(RID p_obstacle) const;
COMMAND_2(obstacle_set_position, RID, p_obstacle, Vector3, p_position);
COMMAND_2(obstacle_set_height, RID, p_obstacle, real_t, p_height);
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices);
COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers);
COMMAND_1(free, RID, p_object);

View File

@ -33,6 +33,7 @@ public:
virtual Array map_get_links(RID p_map) const { return Array(); }
virtual Array map_get_regions(RID p_map) const { return Array(); }
virtual Array map_get_agents(RID p_map) const { return Array(); }
virtual Array map_get_obstacles(RID p_map) const { return Array(); }
virtual void map_force_update(RID p_map) {}
virtual RID region_create() { return RID(); }
@ -76,17 +77,31 @@ public:
virtual RID agent_create() { return RID(); }
virtual void agent_set_map(RID p_agent, RID p_map) {}
virtual RID agent_get_map(RID p_agent) const { return RID(); }
virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) {}
virtual bool agent_get_avoidance_enabled(RID p_agent) const { return false; }
virtual void agent_set_neighbor_dist(RID p_agent, real_t p_dist) {}
virtual void agent_set_max_neighbors(RID p_agent, int p_count) {}
virtual void agent_set_time_horizon(RID p_agent, real_t p_time) {}
virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) {}
virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) {}
virtual void agent_set_radius(RID p_agent, real_t p_radius) {}
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) {}
virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) {}
virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) {}
virtual void agent_set_target_velocity(RID p_agent, Vector2 p_velocity) {}
virtual void agent_set_position(RID p_agent, Vector2 p_position) {}
virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) {}
virtual bool agent_is_map_changed(RID p_agent) const { return false; }
virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) {}
virtual void agent_set_avoidance_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) {}
virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) {}
virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) {}
virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) {}
/// Creates the obstacle.
virtual RID obstacle_create() { return RID(); }
virtual void obstacle_set_map(RID p_obstacle, RID p_map) {}
virtual RID obstacle_get_map(RID p_obstacle) const { return RID(); }
virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position) {}
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) {}
virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) {}
virtual void free(RID p_object) {}

View File

@ -35,6 +35,7 @@ public:
virtual Array map_get_links(RID p_map) const { return Array(); }
virtual Array map_get_regions(RID p_map) const { return Array(); }
virtual Array map_get_agents(RID p_map) const { return Array(); }
virtual Array map_get_obstacles(RID p_map) const { return Array(); }
virtual void map_force_update(RID p_map) {}
virtual RID region_create() { return RID(); }
@ -78,17 +79,34 @@ public:
virtual RID agent_create() { return RID(); }
virtual void agent_set_map(RID p_agent, RID p_map) {}
virtual RID agent_get_map(RID p_agent) const { return RID(); }
virtual void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) {}
virtual bool agent_get_avoidance_enabled(RID p_agent) const { return false; }
virtual void agent_set_use_3d_avoidance(RID p_agent, bool p_enabled) {}
virtual bool agent_get_use_3d_avoidance(RID p_agent) const { return false; }
virtual void agent_set_neighbor_dist(RID p_agent, real_t p_dist) {}
virtual void agent_set_max_neighbors(RID p_agent, int p_count) {}
virtual void agent_set_time_horizon(RID p_agent, real_t p_time) {}
virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) {}
virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) {}
virtual void agent_set_radius(RID p_agent, real_t p_radius) {}
virtual void agent_set_height(RID p_agent, real_t p_height) {}
virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) {}
virtual void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) {}
virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) {}
virtual void agent_set_target_velocity(RID p_agent, Vector3 p_velocity) {}
virtual void agent_set_position(RID p_agent, Vector3 p_position) {}
virtual void agent_set_ignore_y(RID p_agent, bool p_ignore) {}
virtual bool agent_is_map_changed(RID p_agent) const { return false; }
virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) {}
virtual void agent_set_avoidance_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) {}
virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) {}
virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) {}
virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) {}
virtual RID obstacle_create() { return RID(); }
virtual void obstacle_set_map(RID p_obstacle, RID p_map) {}
virtual RID obstacle_get_map(RID p_obstacle) const { return RID(); }
virtual void obstacle_set_height(RID p_obstacle, real_t p_height) {}
virtual void obstacle_set_position(RID p_obstacle, Vector3 p_position) {}
virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) {}
virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) {}
virtual void free(RID p_object){};

View File

@ -329,7 +329,7 @@ void PropMaterialCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &PropMaterialCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &PropMaterialCache::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("texture_add", "texture"), &PropMaterialCache::texture_add);
ClassDB::bind_method(D_METHOD("texture_remove", "texture"), &PropMaterialCache::texture_remove);

View File

@ -944,7 +944,7 @@ void PropInstanceMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &PropInstanceMerger::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &PropInstanceMerger::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
//Meshes
ClassDB::bind_method(D_METHOD("mesh_get", "index"), &PropInstanceMerger::mesh_get);

View File

@ -163,7 +163,7 @@ void PropData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_props"), &PropData::get_props);
ClassDB::bind_method(D_METHOD("set_props", "props"), &PropData::set_props);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropDataEntry", PROPERTY_USAGE_DEFAULT, "PropDataEntry"), "set_props", "get_props");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropDataEntry", PROPERTY_USAGE_DEFAULT, "PropDataEntry"), "set_props", "get_props");
#ifdef MODULE_TEXTURE_PACKER_ENABLED
ClassDB::bind_method(D_METHOD("add_textures_into", "texture_packer"), &PropData::add_textures_into);

View File

@ -408,7 +408,7 @@ void PropCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &PropCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &PropCache::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("material_cache_get", "prop"), &PropCache::material_cache_get);
ClassDB::bind_method(D_METHOD("material_cache_unref", "prop"), &PropCache::material_cache_unref);

View File

@ -673,7 +673,7 @@ void TiledWallData::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &TiledWallData::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &TiledWallData::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
#ifdef MODULE_TEXTURE_PACKER_ENABLED
ClassDB::bind_method(D_METHOD("add_textures_into", "texture_packer"), &TiledWallData::add_textures_into);

View File

@ -141,7 +141,7 @@ void Prop2DData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_props"), &Prop2DData::get_props);
ClassDB::bind_method(D_METHOD("set_props", "props"), &Prop2DData::set_props);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:Prop2DDataEntry", PROPERTY_USAGE_DEFAULT, "Prop2DDataEntry"), "set_props", "get_props");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:Prop2DDataEntry", PROPERTY_USAGE_DEFAULT, "Prop2DDataEntry"), "set_props", "get_props");
#ifdef MODULE_TEXTURE_PACKER_ENABLED
ClassDB::bind_method(D_METHOD("add_textures_into", "texture_packer"), &Prop2DData::add_textures_into);

View File

@ -257,7 +257,7 @@ void TiledWall2DData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_textures"), &TiledWall2DData::get_textures);
ClassDB::bind_method(D_METHOD("set_textures", "textures"), &TiledWall2DData::set_textures);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "23/19:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_textures", "get_textures");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "23/20:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_textures", "get_textures");
//flavour_textures
ClassDB::bind_method(D_METHOD("get_flavour_texture", "index"), &TiledWall2DData::get_flavour_texture);
@ -269,7 +269,7 @@ void TiledWall2DData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_flavour_textures"), &TiledWall2DData::get_flavour_textures);
ClassDB::bind_method(D_METHOD("set_flavour_textures", "textures"), &TiledWall2DData::set_flavour_textures);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "flavour_textures", PROPERTY_HINT_NONE, "23/19:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_flavour_textures", "get_flavour_textures");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "flavour_textures", PROPERTY_HINT_NONE, "23/20:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_flavour_textures", "get_flavour_textures");
ClassDB::bind_method(D_METHOD("get_flavour_chance"), &TiledWall2DData::get_flavour_chance);
ClassDB::bind_method(D_METHOD("set_flavour_chance", "texture"), &TiledWall2DData::set_flavour_chance);

View File

@ -56,7 +56,7 @@ void GSAIProximity::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_agents"), &GSAIProximity::get_agents_arr);
ClassDB::bind_method(D_METHOD("set_agents", "arr"), &GSAIProximity::set_agents_arr);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "agents", PROPERTY_HINT_NONE, "23/19:GSAISteeringAgent", PROPERTY_USAGE_DEFAULT, "GSAISteeringAgent"), "set_agents", "get_agents");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "agents", PROPERTY_HINT_NONE, "23/20:GSAISteeringAgent", PROPERTY_USAGE_DEFAULT, "GSAISteeringAgent"), "set_agents", "get_agents");
BIND_VMETHOD(MethodInfo(Variant::INT, "_find_neighbors", PropertyInfo(Variant::OBJECT, "callback", PROPERTY_HINT_RESOURCE_TYPE, "FuncRef")));

View File

@ -392,7 +392,7 @@ void TerrainLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &TerrainLibrary::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &TerrainLibrary::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
BIND_VMETHOD(MethodInfo("_liquid_material_cache_get_key", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerrainChunk")));
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "TerrainMaterialCache"), "_liquid_material_cache_get", PropertyInfo(Variant::INT, "key")));
@ -416,7 +416,7 @@ void TerrainLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("liquid_materials_get"), &TerrainLibrary::liquid_materials_get);
ClassDB::bind_method(D_METHOD("liquid_materials_set"), &TerrainLibrary::liquid_materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "liquid_materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "liquid_materials_set", "liquid_materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "liquid_materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "liquid_materials_set", "liquid_materials_get");
BIND_VMETHOD(MethodInfo("_prop_material_cache_get_key", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerrainChunk")));
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "TerrainMaterialCache"), "_prop_material_cache_get", PropertyInfo(Variant::INT, "key")));
@ -440,7 +440,7 @@ void TerrainLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("prop_materials_get"), &TerrainLibrary::prop_materials_get);
ClassDB::bind_method(D_METHOD("prop_materials_set"), &TerrainLibrary::prop_materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "prop_materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "prop_materials_set", "prop_materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "prop_materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "prop_materials_set", "prop_materials_get");
ClassDB::bind_method(D_METHOD("terra_surface_get", "index"), &TerrainLibrary::terra_surface_get);
ClassDB::bind_method(D_METHOD("terra_surface_add", "value"), &TerrainLibrary::terra_surface_add);

View File

@ -462,12 +462,12 @@ void TerrainLibraryMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &TerrainLibraryMerger::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &TerrainLibraryMerger::set_terra_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:TerrainSurfaceMerger", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:TerrainSurfaceMerger", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
#ifdef MODULE_PROPS_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &TerrainLibraryMerger::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &TerrainLibraryMerger::set_props);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &TerrainLibraryMerger::get_prop_uv_rect);

View File

@ -1002,12 +1002,12 @@ void TerrainLibraryMergerPCM::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &TerrainLibraryMergerPCM::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &TerrainLibraryMergerPCM::set_terra_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:TerrainSurfaceMerger", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:TerrainSurfaceMerger", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
#ifdef MODULE_PROPS_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &TerrainLibraryMergerPCM::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &TerrainLibraryMergerPCM::set_props);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &TerrainLibraryMergerPCM::get_prop_uv_rect);

View File

@ -147,5 +147,5 @@ void TerrainLibrarySimple::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &TerrainLibrarySimple::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &TerrainLibrarySimple::set_terra_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:TerrainSurfaceSimple", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceSimple"), "set_terra_surfaces", "get_terra_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:TerrainSurfaceSimple", PROPERTY_USAGE_DEFAULT, "TerrainSurfaceSimple"), "set_terra_surfaces", "get_terra_surfaces");
}

View File

@ -296,7 +296,7 @@ void TerrainMaterialCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &TerrainMaterialCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &TerrainMaterialCache::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("surface_get", "index"), &TerrainMaterialCache::surface_get);
ClassDB::bind_method(D_METHOD("surface_id_get", "index"), &TerrainMaterialCache::surface_id_get);

View File

@ -1462,7 +1462,7 @@ void TerrainChunk::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &TerrainChunk::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &TerrainChunk::voxel_structures_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:TerrainStructure", PROPERTY_USAGE_DEFAULT, "TerrainStructure"), "voxel_structures_set", "voxel_structures_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:TerrainStructure", PROPERTY_USAGE_DEFAULT, "TerrainStructure"), "voxel_structures_set", "voxel_structures_get");
//Meshes

View File

@ -1151,7 +1151,7 @@ void TerrainWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &TerrainWorld::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &TerrainWorld::voxel_structures_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:TerrainStructure", PROPERTY_USAGE_DEFAULT, "TerrainStructure"), "voxel_structures_set", "voxel_structures_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:TerrainStructure", PROPERTY_USAGE_DEFAULT, "TerrainStructure"), "voxel_structures_set", "voxel_structures_get");
BIND_VMETHOD(MethodInfo("_chunk_added", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "TerrainChunk")));
@ -1168,7 +1168,7 @@ void TerrainWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("chunks_get"), &TerrainWorld::chunks_get);
ClassDB::bind_method(D_METHOD("chunks_set"), &TerrainWorld::chunks_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/19:TerrainChunk", PROPERTY_USAGE_DEFAULT, "TerrainChunk"), "chunks_set", "chunks_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/20:TerrainChunk", PROPERTY_USAGE_DEFAULT, "TerrainChunk"), "chunks_set", "chunks_get");
ClassDB::bind_method(D_METHOD("generation_queue_add_to", "chunk"), &TerrainWorld::generation_queue_add_to);
ClassDB::bind_method(D_METHOD("generation_queue_get_index", "index"), &TerrainWorld::generation_queue_get_index);

View File

@ -416,12 +416,12 @@ void Terrain2DLibraryMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &Terrain2DLibraryMerger::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &Terrain2DLibraryMerger::set_terra_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:Terrain2DSurfaceMerger", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:Terrain2DSurfaceMerger", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
#ifdef MODULE_PROPS_2D_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &Terrain2DLibraryMerger::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &Terrain2DLibraryMerger::set_props);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:Prop2DData", PROPERTY_USAGE_DEFAULT, "Prop2DData"), "set_props", "get_props");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:Prop2DData", PROPERTY_USAGE_DEFAULT, "Prop2DData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &Terrain2DLibraryMerger::get_prop_uv_rect);

View File

@ -792,12 +792,12 @@ void Terrain2DLibraryMergerPCM::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &Terrain2DLibraryMergerPCM::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &Terrain2DLibraryMergerPCM::set_terra_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:Terrain2DSurfaceMerger", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:Terrain2DSurfaceMerger", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceMerger"), "set_terra_surfaces", "get_terra_surfaces");
#ifdef MODULE_PROPS_2D_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &Terrain2DLibraryMergerPCM::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &Terrain2DLibraryMergerPCM::set_props);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:Prop2DData", PROPERTY_USAGE_DEFAULT, "Prop2DData"), "set_props", "get_props");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:Prop2DData", PROPERTY_USAGE_DEFAULT, "Prop2DData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &Terrain2DLibraryMergerPCM::get_prop_uv_rect);

View File

@ -147,5 +147,5 @@ void Terrain2DLibrarySimple::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terra_surfaces"), &Terrain2DLibrarySimple::get_terra_surfaces);
ClassDB::bind_method(D_METHOD("set_terra_surfaces"), &Terrain2DLibrarySimple::set_terra_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/19:Terrain2DSurfaceSimple", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceSimple"), "set_terra_surfaces", "get_terra_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "terra_surfaces", PROPERTY_HINT_NONE, "23/20:Terrain2DSurfaceSimple", PROPERTY_USAGE_DEFAULT, "Terrain2DSurfaceSimple"), "set_terra_surfaces", "get_terra_surfaces");
}

View File

@ -1584,7 +1584,7 @@ void Terrain2DChunk::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &Terrain2DChunk::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &Terrain2DChunk::voxel_structures_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:Terrain2DStructure", PROPERTY_USAGE_DEFAULT, "Terrain2DStructure"), "voxel_structures_set", "voxel_structures_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:Terrain2DStructure", PROPERTY_USAGE_DEFAULT, "Terrain2DStructure"), "voxel_structures_set", "voxel_structures_get");
//Meshes

View File

@ -1238,7 +1238,7 @@ void Terrain2DWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &Terrain2DWorld::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &Terrain2DWorld::voxel_structures_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:Terrain2DStructure", PROPERTY_USAGE_DEFAULT, "Terrain2DStructure"), "voxel_structures_set", "voxel_structures_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:Terrain2DStructure", PROPERTY_USAGE_DEFAULT, "Terrain2DStructure"), "voxel_structures_set", "voxel_structures_get");
BIND_VMETHOD(MethodInfo("_chunk_added", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "Terrain2DChunk")));
@ -1255,7 +1255,7 @@ void Terrain2DWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("chunks_get"), &Terrain2DWorld::chunks_get);
ClassDB::bind_method(D_METHOD("chunks_set"), &Terrain2DWorld::chunks_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/19:Terrain2DChunk", PROPERTY_USAGE_DEFAULT, "Terrain2DChunk"), "chunks_set", "chunks_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/20:Terrain2DChunk", PROPERTY_USAGE_DEFAULT, "Terrain2DChunk"), "chunks_set", "chunks_get");
ClassDB::bind_method(D_METHOD("generation_queue_add_to", "chunk"), &Terrain2DWorld::generation_queue_add_to);
ClassDB::bind_method(D_METHOD("generation_queue_get_index", "index"), &Terrain2DWorld::generation_queue_get_index);

View File

@ -314,7 +314,7 @@ void TextureMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_textures"), &TextureMerger::get_textures);
ClassDB::bind_method(D_METHOD("set_textures", "textures"), &TextureMerger::set_textures);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "23/19:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_textures", "get_textures");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "23/20:Texture", PROPERTY_USAGE_DEFAULT, "Texture"), "set_textures", "get_textures");
ClassDB::bind_method(D_METHOD("get_packer"), &TextureMerger::get_packer);
ClassDB::bind_method(D_METHOD("set_packer", "packer"), &TextureMerger::set_packer);

View File

@ -128,7 +128,7 @@ UserManagerStatic::~UserManagerStatic() {
void UserManagerStatic::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_users"), &UserManagerStatic::get_users);
ClassDB::bind_method(D_METHOD("set_users", "users"), &UserManagerStatic::set_users);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "users", PROPERTY_HINT_NONE, "23/19:User", PROPERTY_USAGE_DEFAULT, "User"), "set_users", "get_users");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "users", PROPERTY_HINT_NONE, "23/20:User", PROPERTY_USAGE_DEFAULT, "User"), "set_users", "get_users");
ClassDB::bind_method(D_METHOD("get_create_user_name"), &UserManagerStatic::get_create_user_name_bind);
ClassDB::bind_method(D_METHOD("set_create_user_name", "val"), &UserManagerStatic::set_create_user_name_bind);

View File

@ -327,7 +327,7 @@ void User::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_modules"), &User::get_modules);
ClassDB::bind_method(D_METHOD("set_modules", "modules"), &User::set_modules);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "modules", PROPERTY_HINT_NONE, "23/19:UserModule", PROPERTY_USAGE_DEFAULT, "UserModule"), "set_modules", "get_modules");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "modules", PROPERTY_HINT_NONE, "23/20:UserModule", PROPERTY_USAGE_DEFAULT, "UserModule"), "set_modules", "get_modules");
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::BOOL, "ret"), "_check_password", PropertyInfo(Variant::STRING, "password")));
BIND_VMETHOD(MethodInfo("_create_password", PropertyInfo(Variant::STRING, "password")));

View File

@ -393,7 +393,7 @@ void VoxelLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &VoxelLibrary::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &VoxelLibrary::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
BIND_VMETHOD(MethodInfo("_liquid_material_cache_get_key", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "VoxelChunk")));
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "TerrainMaterialCache"), "_liquid_material_cache_get", PropertyInfo(Variant::INT, "key")));
@ -417,7 +417,7 @@ void VoxelLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("liquid_materials_get"), &VoxelLibrary::liquid_materials_get);
ClassDB::bind_method(D_METHOD("liquid_materials_set"), &VoxelLibrary::liquid_materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "liquid_materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "liquid_materials_set", "liquid_materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "liquid_materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "liquid_materials_set", "liquid_materials_get");
BIND_VMETHOD(MethodInfo("_prop_material_cache_get_key", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "VoxelChunk")));
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "ret", PROPERTY_HINT_RESOURCE_TYPE, "TerrainMaterialCache"), "_prop_material_cache_get", PropertyInfo(Variant::INT, "key")));
@ -441,7 +441,7 @@ void VoxelLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("prop_materials_get"), &VoxelLibrary::prop_materials_get);
ClassDB::bind_method(D_METHOD("prop_materials_set"), &VoxelLibrary::prop_materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "prop_materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "prop_materials_set", "prop_materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "prop_materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "prop_materials_set", "prop_materials_get");
ClassDB::bind_method(D_METHOD("voxel_surface_get", "index"), &VoxelLibrary::voxel_surface_get);
ClassDB::bind_method(D_METHOD("voxel_surface_add", "value"), &VoxelLibrary::voxel_surface_add);

View File

@ -462,12 +462,12 @@ void VoxelLibraryMerger::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel_surfaces"), &VoxelLibraryMerger::get_voxel_surfaces);
ClassDB::bind_method(D_METHOD("set_voxel_surfaces"), &VoxelLibraryMerger::set_voxel_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/19:VoxelSurfaceMerger", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceMerger"), "set_voxel_surfaces", "get_voxel_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/20:VoxelSurfaceMerger", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceMerger"), "set_voxel_surfaces", "get_voxel_surfaces");
#ifdef MODULE_PROPS_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &VoxelLibraryMerger::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &VoxelLibraryMerger::set_props);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &VoxelLibraryMerger::get_prop_uv_rect);

View File

@ -805,12 +805,12 @@ void VoxelLibraryMergerPCM::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel_surfaces"), &VoxelLibraryMergerPCM::get_voxel_surfaces);
ClassDB::bind_method(D_METHOD("set_voxel_surfaces"), &VoxelLibraryMergerPCM::set_voxel_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/19:VoxelSurfaceMerger", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceMerger"), "set_voxel_surfaces", "get_voxel_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/20:VoxelSurfaceMerger", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceMerger"), "set_voxel_surfaces", "get_voxel_surfaces");
#ifdef MODULE_PROPS_ENABLED
ClassDB::bind_method(D_METHOD("get_props"), &VoxelLibraryMergerPCM::get_props);
ClassDB::bind_method(D_METHOD("set_props"), &VoxelLibraryMergerPCM::set_props);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/19:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "props", PROPERTY_HINT_NONE, "23/20:PropData", PROPERTY_USAGE_DEFAULT, "PropData"), "set_props", "get_props");
ClassDB::bind_method(D_METHOD("get_prop_uv_rect", "texture"), &VoxelLibraryMergerPCM::get_prop_uv_rect);

View File

@ -147,5 +147,5 @@ void VoxelLibrarySimple::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_voxel_surfaces"), &VoxelLibrarySimple::get_voxel_surfaces);
ClassDB::bind_method(D_METHOD("set_voxel_surfaces"), &VoxelLibrarySimple::set_voxel_surfaces);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/19:VoxelSurfaceSimple", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceSimple"), "set_voxel_surfaces", "get_voxel_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_surfaces", PROPERTY_HINT_NONE, "23/20:VoxelSurfaceSimple", PROPERTY_USAGE_DEFAULT, "VoxelSurfaceSimple"), "set_voxel_surfaces", "get_voxel_surfaces");
}

View File

@ -296,7 +296,7 @@ void VoxelMaterialCache::_bind_methods() {
ClassDB::bind_method(D_METHOD("materials_get"), &VoxelMaterialCache::materials_get);
ClassDB::bind_method(D_METHOD("materials_set"), &VoxelMaterialCache::materials_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/19:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "23/20:Material", PROPERTY_USAGE_DEFAULT, "Material"), "materials_set", "materials_get");
ClassDB::bind_method(D_METHOD("surface_get", "index"), &VoxelMaterialCache::surface_get);
ClassDB::bind_method(D_METHOD("surface_id_get", "index"), &VoxelMaterialCache::surface_id_get);

View File

@ -1450,7 +1450,7 @@ void VoxelChunk::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &VoxelChunk::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &VoxelChunk::voxel_structures_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:VoxelStructure", PROPERTY_USAGE_DEFAULT, "VoxelStructure"), "voxel_structures_set", "voxel_structures_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:VoxelStructure", PROPERTY_USAGE_DEFAULT, "VoxelStructure"), "voxel_structures_set", "voxel_structures_get");
//Meshes

View File

@ -1111,7 +1111,7 @@ void VoxelWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("voxel_structures_get"), &VoxelWorld::voxel_structures_get);
ClassDB::bind_method(D_METHOD("voxel_structures_set"), &VoxelWorld::voxel_structures_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/19:VoxelStructure", PROPERTY_USAGE_DEFAULT, "VoxelStructure"), "voxel_structures_set", "voxel_structures_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "voxel_structures", PROPERTY_HINT_NONE, "23/20:VoxelStructure", PROPERTY_USAGE_DEFAULT, "VoxelStructure"), "voxel_structures_set", "voxel_structures_get");
BIND_VMETHOD(MethodInfo("_chunk_added", PropertyInfo(Variant::OBJECT, "chunk", PROPERTY_HINT_RESOURCE_TYPE, "VoxelChunk")));
@ -1128,7 +1128,7 @@ void VoxelWorld::_bind_methods() {
ClassDB::bind_method(D_METHOD("chunks_get"), &VoxelWorld::chunks_get);
ClassDB::bind_method(D_METHOD("chunks_set"), &VoxelWorld::chunks_set);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/19:VoxelChunk", PROPERTY_USAGE_DEFAULT, "VoxelChunk"), "chunks_set", "chunks_get");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "chunks", PROPERTY_HINT_NONE, "23/20:VoxelChunk", PROPERTY_USAGE_DEFAULT, "VoxelChunk"), "chunks_set", "chunks_get");
ClassDB::bind_method(D_METHOD("generation_queue_add_to", "chunk"), &VoxelWorld::generation_queue_add_to);
ClassDB::bind_method(D_METHOD("generation_queue_get_index", "index"), &VoxelWorld::generation_queue_get_index);

View File

@ -527,7 +527,7 @@ void BBCodeParserTag::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_child_tags"), &BBCodeParserTag::get_child_tags);
ClassDB::bind_method(D_METHOD("set_child_tags", "val"), &BBCodeParserTag::set_child_tags);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "child_tags", PROPERTY_HINT_NONE, "23/19:BBCodeParserTag", PROPERTY_USAGE_DEFAULT, "BBCodeParserTag"), "set_child_tags", "get_child_tags");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "child_tags", PROPERTY_HINT_NONE, "23/20:BBCodeParserTag", PROPERTY_USAGE_DEFAULT, "BBCodeParserTag"), "set_child_tags", "get_child_tags");
ClassDB::bind_method(D_METHOD("add_child_attribute", "tag"), &BBCodeParserTag::add_child_attribute);
ClassDB::bind_method(D_METHOD("remote_child_attribute", "index"), &BBCodeParserTag::remote_child_attribute);
@ -537,7 +537,7 @@ void BBCodeParserTag::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_attributes"), &BBCodeParserTag::get_attributes);
ClassDB::bind_method(D_METHOD("set_attributes", "val"), &BBCodeParserTag::set_attributes);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "attributes", PROPERTY_HINT_NONE, "23/19:BBCodeParserAttribute", PROPERTY_USAGE_DEFAULT, "BBCodeParserAttribute"), "set_attributes", "get_attributes");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "attributes", PROPERTY_HINT_NONE, "23/20:BBCodeParserAttribute", PROPERTY_USAGE_DEFAULT, "BBCodeParserAttribute"), "set_attributes", "get_attributes");
ClassDB::bind_method(D_METHOD("get_first", "t"), &BBCodeParserTag::get_first);
ClassDB::bind_method(D_METHOD("get_firstc", "t", "attrib", "val"), &BBCodeParserTag::get_firstc);

View File

@ -796,7 +796,7 @@ void FormField::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_entries"), &FormField::get_entries);
ClassDB::bind_method(D_METHOD("set_entries", "array"), &FormField::set_entries);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entries", PROPERTY_HINT_NONE, "23/19:FormFieldEntry", PROPERTY_USAGE_DEFAULT, "FormFieldEntry"), "set_entries", "get_entries");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "entries", PROPERTY_HINT_NONE, "23/20:FormFieldEntry", PROPERTY_USAGE_DEFAULT, "FormFieldEntry"), "set_entries", "get_entries");
ClassDB::bind_method(D_METHOD("need_to_exist"), &FormField::need_to_exist);
ClassDB::bind_method(D_METHOD("need_to_be_int"), &FormField::need_to_be_int);
@ -905,7 +905,7 @@ void FormValidator::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_fields"), &FormValidator::get_fields);
ClassDB::bind_method(D_METHOD("set_fields", "array"), &FormValidator::set_fields);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fields", PROPERTY_HINT_NONE, "23/19:FormField", PROPERTY_USAGE_DEFAULT, "FormField"), "set_fields", "get_fields");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fields", PROPERTY_HINT_NONE, "23/20:FormField", PROPERTY_USAGE_DEFAULT, "FormField"), "set_fields", "get_fields");
BIND_VMETHOD(MethodInfo("_validate", PropertyInfo(Variant::OBJECT, "request", PROPERTY_HINT_RESOURCE_TYPE, "WebServerRequest")));
ClassDB::bind_method(D_METHOD("validate", "request"), &FormValidator::validate);

View File

@ -570,7 +570,7 @@ void HTMLParserTag::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_child_tags"), &HTMLParserTag::get_child_tags);
ClassDB::bind_method(D_METHOD("set_child_tags", "val"), &HTMLParserTag::set_child_tags);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "child_tags", PROPERTY_HINT_NONE, "23/19:HTMLParserTag", PROPERTY_USAGE_DEFAULT, "HTMLParserTag"), "set_child_tags", "get_child_tags");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "child_tags", PROPERTY_HINT_NONE, "23/20:HTMLParserTag", PROPERTY_USAGE_DEFAULT, "HTMLParserTag"), "set_child_tags", "get_child_tags");
ClassDB::bind_method(D_METHOD("add_child_attribute", "tag"), &HTMLParserTag::add_child_attribute);
ClassDB::bind_method(D_METHOD("remote_child_attribute", "index"), &HTMLParserTag::remote_child_attribute);
@ -580,7 +580,7 @@ void HTMLParserTag::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_attributes"), &HTMLParserTag::get_attributes);
ClassDB::bind_method(D_METHOD("set_attributes", "val"), &HTMLParserTag::set_attributes);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "attributes", PROPERTY_HINT_NONE, "23/19:HTMLParserAttribute", PROPERTY_USAGE_DEFAULT, "HTMLParserAttribute"), "set_attributes", "get_attributes");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "attributes", PROPERTY_HINT_NONE, "23/20:HTMLParserAttribute", PROPERTY_USAGE_DEFAULT, "HTMLParserAttribute"), "set_attributes", "get_attributes");
ClassDB::bind_method(D_METHOD("get_first", "t"), &HTMLParserTag::get_first);
ClassDB::bind_method(D_METHOD("get_firstc", "t", "attrib", "val"), &HTMLParserTag::get_firstc);

View File

@ -209,7 +209,7 @@ void WebRoot::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_middlewares"), &WebRoot::get_middlewares);
ClassDB::bind_method(D_METHOD("set_middlewares", "data"), &WebRoot::set_middlewares);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "middlewares", PROPERTY_HINT_NONE, "23/19:WebServerMiddleware", PROPERTY_USAGE_DEFAULT, "WebServerMiddleware"), "set_middlewares", "get_middlewares");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "middlewares", PROPERTY_HINT_NONE, "23/20:WebServerMiddleware", PROPERTY_USAGE_DEFAULT, "WebServerMiddleware"), "set_middlewares", "get_middlewares");
ClassDB::bind_method(D_METHOD("process_middlewares", "request"), &WebRoot::process_middlewares);
ClassDB::bind_method(D_METHOD("try_send_wwwroot_file", "request"), &WebRoot::try_send_wwwroot_file);

View File

@ -63,8 +63,11 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent2D::set_max_neighbors);
ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent2D::get_max_neighbors);
ClassDB::bind_method(D_METHOD("set_time_horizon", "time_horizon"), &NavigationAgent2D::set_time_horizon);
ClassDB::bind_method(D_METHOD("get_time_horizon"), &NavigationAgent2D::get_time_horizon);
ClassDB::bind_method(D_METHOD("set_time_horizon_agents", "time_horizon"), &NavigationAgent2D::set_time_horizon_agents);
ClassDB::bind_method(D_METHOD("get_time_horizon_agents"), &NavigationAgent2D::get_time_horizon_agents);
ClassDB::bind_method(D_METHOD("set_time_horizon_obstacles", "time_horizon"), &NavigationAgent2D::set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("get_time_horizon_obstacles"), &NavigationAgent2D::get_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("set_max_speed", "max_speed"), &NavigationAgent2D::set_max_speed);
ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent2D::get_max_speed);
@ -88,11 +91,15 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent2D::get_target_position);
ClassDB::bind_method(D_METHOD("get_next_position"), &NavigationAgent2D::get_next_position);
ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target);
ClassDB::bind_method(D_METHOD("set_velocity_forced", "velocity"), &NavigationAgent2D::set_velocity_forced);
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent2D::set_velocity);
ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationAgent2D::get_velocity);
ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent2D::distance_to_target);
ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent2D::get_current_navigation_result);
ClassDB::bind_method(D_METHOD("get_nav_path"), &NavigationAgent2D::get_nav_path);
ClassDB::bind_method(D_METHOD("get_nav_path_index"), &NavigationAgent2D::get_nav_path_index);
ClassDB::bind_method(D_METHOD("get_current_navigation_path"), &NavigationAgent2D::get_current_navigation_path);
ClassDB::bind_method(D_METHOD("get_current_navigation_path_index"), &NavigationAgent2D::get_current_navigation_path_index);
ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent2D::is_target_reached);
ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent2D::is_target_reachable);
ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent2D::is_navigation_finished);
@ -100,6 +107,17 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent2D::_avoidance_done);
ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationAgent2D::set_avoidance_layers);
ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationAgent2D::get_avoidance_layers);
ClassDB::bind_method(D_METHOD("set_avoidance_mask", "mask"), &NavigationAgent2D::set_avoidance_mask);
ClassDB::bind_method(D_METHOD("get_avoidance_mask"), &NavigationAgent2D::get_avoidance_mask);
ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationAgent2D::set_avoidance_layer_value);
ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationAgent2D::get_avoidance_layer_value);
ClassDB::bind_method(D_METHOD("set_avoidance_mask_value", "mask_number", "value"), &NavigationAgent2D::set_avoidance_mask_value);
ClassDB::bind_method(D_METHOD("get_avoidance_mask_value", "mask_number"), &NavigationAgent2D::get_avoidance_mask_value);
ClassDB::bind_method(D_METHOD("set_avoidance_priority", "priority"), &NavigationAgent2D::set_avoidance_priority);
ClassDB::bind_method(D_METHOD("get_avoidance_priority"), &NavigationAgent2D::get_avoidance_priority);
ADD_GROUP("Pathfinding", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_path_desired_distance", "get_path_desired_distance");
@ -110,13 +128,17 @@ void NavigationAgent2D::_bind_methods() {
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_velocity", "get_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,500,0.01"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,100000,0.01"), "set_neighbor_dist", "get_neighbor_dist");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_time_horizon", "get_time_horizon");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01"), "set_max_speed", "get_max_speed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1,or_greater"), "set_max_neighbors", "get_max_neighbors");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon_agents", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_agents", "get_time_horizon_agents");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon_obstacles", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_obstacles", "get_time_horizon_obstacles");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.01,100000,0.01,or_greater,suffix:px/s"), "set_max_speed", "get_max_speed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_mask", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_mask", "get_avoidance_mask");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "avoidance_priority", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_avoidance_priority", "get_avoidance_priority");
#ifdef DEBUG_ENABLED
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationAgent2D::set_debug_enabled);
ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationAgent2D::get_debug_enabled);
ClassDB::bind_method(D_METHOD("set_debug_use_custom", "enabled"), &NavigationAgent2D::set_debug_use_custom);
@ -136,7 +158,6 @@ void NavigationAgent2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_path_custom_color"), "set_debug_path_custom_color", "get_debug_path_custom_color");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "debug_path_custom_point_size", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_point_size", "get_debug_path_custom_point_size");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "debug_path_custom_line_width", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_line_width", "get_debug_path_custom_line_width");
#endif // DEBUG_ENABLED
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
@ -170,6 +191,10 @@ void NavigationAgent2D::_notification(int p_what) {
set_agent_parent(get_parent());
set_physics_process_internal(true);
if (agent_parent && avoidance_enabled) {
Navigation2DServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
}
#ifdef DEBUG_ENABLED
if (Navigation2DServer::get_singleton()->get_debug_enabled()) {
debug_path_dirty = true;
@ -222,10 +247,18 @@ void NavigationAgent2D::_notification(int p_what) {
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent) {
if (avoidance_enabled) {
// agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding
// no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used
Navigation2DServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
if (velocity_submitted) {
velocity_submitted = false;
if (avoidance_enabled) {
Navigation2DServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_position());
Navigation2DServer::get_singleton()->agent_set_velocity(agent, velocity);
}
}
if (velocity_forced_submitted) {
velocity_forced_submitted = false;
if (avoidance_enabled) {
Navigation2DServer::get_singleton()->agent_set_velocity_forced(agent, velocity_forced);
}
}
_check_distance_to_target();
}
@ -244,6 +277,9 @@ NavigationAgent2D::NavigationAgent2D() {
navigation = nullptr;
avoidance_enabled = false;
avoidance_layers = 1;
avoidance_mask = 1;
avoidance_priority = 1.0;
navigation_layers = 1;
path_metadata_flags = NavigationPathQueryParameters2D::PATH_METADATA_INCLUDE_ALL;
@ -257,11 +293,13 @@ NavigationAgent2D::NavigationAgent2D() {
set_neighbor_dist(500.0);
set_max_neighbors(10);
set_time_horizon(20.0);
set_time_horizon_agents(1.0);
set_time_horizon_obstacles(0.0);
set_radius(10.0);
set_max_speed(200.0);
time_horizon = 0.0;
velocity_forced_submitted = false;
target_position_submitted = false;
nav_path_index = 0;
update_frame_id = 0;
@ -269,7 +307,6 @@ NavigationAgent2D::NavigationAgent2D() {
navigation_query.instance();
navigation_result.instance();
#ifdef DEBUG_ENABLED
debug_enabled = false;
debug_path_dirty = true;
debug_path_custom_point_size = 4.0;
@ -277,8 +314,14 @@ NavigationAgent2D::NavigationAgent2D() {
debug_use_custom = false;
debug_path_custom_color = Color(1.0, 1.0, 1.0, 1.0);
#ifdef DEBUG_ENABLED
Navigation2DServer::get_singleton()->connect("navigation_debug_changed", this, "_navigation_debug_changed");
#endif // DEBUG_ENABLED
set_avoidance_layers(avoidance_layers);
set_avoidance_mask(avoidance_mask);
set_avoidance_priority(avoidance_priority);
set_avoidance_enabled(avoidance_enabled);
}
NavigationAgent2D::~NavigationAgent2D() {
@ -298,9 +341,11 @@ NavigationAgent2D::~NavigationAgent2D() {
void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
if (avoidance_enabled) {
Navigation2DServer::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done");
Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(agent, true);
Navigation2DServer::get_singleton()->agent_set_avoidance_callback(agent, get_instance_id(), "_avoidance_done");
} else {
Navigation2DServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(agent, false);
Navigation2DServer::get_singleton()->agent_set_avoidance_callback(agent, ObjectID(), "_avoidance_done");
}
}
@ -310,7 +355,7 @@ bool NavigationAgent2D::get_avoidance_enabled() const {
void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
Navigation2DServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
Navigation2DServer::get_singleton()->agent_set_avoidance_callback(agent, ObjectID(), "_avoidance_done");
if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
agent_parent = Object::cast_to<Node2D>(p_agent_parent);
@ -323,7 +368,7 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) {
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_2d()->get_navigation_map());
}
// create new avoidance callback if enabled
set_avoidance_enabled(avoidance_enabled);
Navigation2DServer::get_singleton()->agent_set_avoidance_callback(agent, get_instance_id(), "_avoidance_done");
} else {
agent_parent = nullptr;
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), RID());
@ -379,6 +424,71 @@ bool NavigationAgent2D::get_navigation_layer_value(int p_layer_number) const {
return get_navigation_layers() & (1 << (p_layer_number - 1));
}
void NavigationAgent2D::set_avoidance_layers(uint32_t p_layers) {
avoidance_layers = p_layers;
Navigation2DServer::get_singleton()->agent_set_avoidance_layers(get_rid(), avoidance_layers);
}
uint32_t NavigationAgent2D::get_avoidance_layers() const {
return avoidance_layers;
}
void NavigationAgent2D::set_avoidance_mask(uint32_t p_mask) {
avoidance_mask = p_mask;
Navigation2DServer::get_singleton()->agent_set_avoidance_mask(get_rid(), p_mask);
}
uint32_t NavigationAgent2D::get_avoidance_mask() const {
return avoidance_mask;
}
void NavigationAgent2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
uint32_t avoidance_layers_new = get_avoidance_layers();
if (p_value) {
avoidance_layers_new |= 1 << (p_layer_number - 1);
} else {
avoidance_layers_new &= ~(1 << (p_layer_number - 1));
}
set_avoidance_layers(avoidance_layers_new);
}
bool NavigationAgent2D::get_avoidance_layer_value(int p_layer_number) const {
ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
return get_avoidance_layers() & (1 << (p_layer_number - 1));
}
void NavigationAgent2D::set_avoidance_mask_value(int p_mask_number, bool p_value) {
ERR_FAIL_COND_MSG(p_mask_number < 1, "Avoidance mask number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_MSG(p_mask_number > 32, "Avoidance mask number must be between 1 and 32 inclusive.");
uint32_t mask = get_avoidance_mask();
if (p_value) {
mask |= 1 << (p_mask_number - 1);
} else {
mask &= ~(1 << (p_mask_number - 1));
}
set_avoidance_mask(mask);
}
bool NavigationAgent2D::get_avoidance_mask_value(int p_mask_number) const {
ERR_FAIL_COND_V_MSG(p_mask_number < 1, false, "Avoidance mask number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_V_MSG(p_mask_number > 32, false, "Avoidance mask number must be between 1 and 32 inclusive.");
return get_avoidance_mask() & (1 << (p_mask_number - 1));
}
void NavigationAgent2D::set_avoidance_priority(real_t p_priority) {
ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
avoidance_priority = p_priority;
Navigation2DServer::get_singleton()->agent_set_avoidance_priority(get_rid(), p_priority);
}
real_t NavigationAgent2D::get_avoidance_priority() const {
return avoidance_priority;
}
void NavigationAgent2D::set_path_metadata_flags(const int p_flags) {
path_metadata_flags = p_flags;
}
@ -412,6 +522,7 @@ void NavigationAgent2D::set_target_desired_distance(real_t p_dd) {
}
void NavigationAgent2D::set_radius(real_t p_radius) {
ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
radius = p_radius;
Navigation2DServer::get_singleton()->agent_set_radius(agent, radius);
}
@ -426,12 +537,26 @@ void NavigationAgent2D::set_max_neighbors(int p_count) {
Navigation2DServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
}
void NavigationAgent2D::set_time_horizon(real_t p_time) {
time_horizon = p_time;
Navigation2DServer::get_singleton()->agent_set_time_horizon(agent, time_horizon);
void NavigationAgent2D::set_time_horizon_agents(real_t p_time_horizon) {
ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
if (Math::is_equal_approx(time_horizon_agents, p_time_horizon)) {
return;
}
time_horizon_agents = p_time_horizon;
Navigation2DServer::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
}
void NavigationAgent2D::set_time_horizon_obstacles(real_t p_time_horizon) {
ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
if (Math::is_equal_approx(time_horizon_obstacles, p_time_horizon)) {
return;
}
time_horizon_obstacles = p_time_horizon;
Navigation2DServer::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
}
void NavigationAgent2D::set_max_speed(real_t p_max_speed) {
ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
max_speed = p_max_speed;
Navigation2DServer::get_singleton()->agent_set_max_speed(agent, max_speed);
}
@ -469,7 +594,7 @@ Ref<NavigationPathQueryResult2D> NavigationAgent2D::get_current_navigation_resul
return navigation_result;
}
const Vector<Vector2> &NavigationAgent2D::get_nav_path() const {
const Vector<Vector2> &NavigationAgent2D::get_current_navigation_path() const {
return navigation_result->get_path();
}
@ -501,24 +626,23 @@ Vector2 NavigationAgent2D::get_final_position() {
return navigation_path[navigation_path.size() - 1];
}
void NavigationAgent2D::set_velocity(Vector2 p_velocity) {
target_velocity = p_velocity;
Navigation2DServer::get_singleton()->agent_set_target_velocity(agent, target_velocity);
Navigation2DServer::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
void NavigationAgent2D::set_velocity_forced(Vector2 p_velocity) {
velocity_forced = p_velocity;
velocity_forced_submitted = true;
Navigation2DServer::get_singleton()->agent_set_velocity_forced(agent, velocity_forced);
}
void NavigationAgent2D::set_velocity(const Vector2 p_velocity) {
velocity = p_velocity;
velocity_submitted = true;
Navigation2DServer::get_singleton()->agent_set_velocity(agent, velocity);
}
void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) {
const Vector2 velocity = Vector2(p_new_velocity.x, p_new_velocity.z);
prev_safe_velocity = velocity;
if (!velocity_submitted) {
target_velocity = Vector2();
return;
}
velocity_submitted = false;
emit_signal("velocity_computed", velocity);
const Vector2 new_safe_velocity = Vector2(p_new_velocity.x, p_new_velocity.z);
safe_velocity = new_safe_velocity;
emit_signal("velocity_computed", safe_velocity);
}
String NavigationAgent2D::get_configuration_warning() const {
@ -536,9 +660,6 @@ void NavigationAgent2D::update_navigation() {
if (!agent_parent->is_inside_tree()) {
return;
}
if (update_frame_id == Engine::get_singleton()->get_physics_frames()) {
return;
}
update_frame_id = Engine::get_singleton()->get_physics_frames();
@ -690,7 +811,6 @@ void NavigationAgent2D::_check_distance_to_target() {
////////DEBUG////////////////////////////////////////////////////////////
#ifdef DEBUG_ENABLED
void NavigationAgent2D::set_debug_enabled(bool p_enabled) {
debug_enabled = p_enabled;
debug_path_dirty = true;
@ -736,6 +856,7 @@ float NavigationAgent2D::get_debug_path_custom_line_width() const {
return debug_path_custom_line_width;
}
#ifdef DEBUG_ENABLED
void NavigationAgent2D::_navigation_debug_changed() {
debug_path_dirty = true;
}

View File

@ -48,6 +48,9 @@ class NavigationAgent2D : public Node {
RID map_override;
bool avoidance_enabled;
uint32_t avoidance_layers;
uint32_t avoidance_mask;
real_t avoidance_priority;
uint32_t navigation_layers;
int path_metadata_flags;
@ -56,7 +59,8 @@ class NavigationAgent2D : public Node {
real_t radius;
real_t neighbor_dist;
int max_neighbors;
real_t time_horizon;
real_t time_horizon_agents;
real_t time_horizon_obstacles;
real_t max_speed;
real_t path_max_distance;
@ -65,16 +69,28 @@ class NavigationAgent2D : public Node {
Ref<NavigationPathQueryParameters2D> navigation_query;
Ref<NavigationPathQueryResult2D> navigation_result;
int nav_path_index;
// the velocity result of the avoidance simulation step
Vector2 safe_velocity;
/// The submitted target velocity, sets the "wanted" rvo agent velocity on the next update
// this velocity is not guaranteed, the simulation will try to fulfil it if possible
// if other agents or obstacles interfere it will be changed accordingly
Vector2 velocity;
bool velocity_submitted;
Vector2 prev_safe_velocity;
/// The submitted target velocity
Vector2 target_velocity;
/// The submitted forced velocity, overrides the rvo agent velocity on the next update
// should only be used very intentionally and not every frame as it interferes with the simulation stability
Vector2 velocity_forced;
bool velocity_forced_submitted;
bool target_position_submitted;
bool target_reached;
bool navigation_finished;
// No initialized on purpose
uint32_t update_frame_id;
#ifdef DEBUG_ENABLED
bool debug_enabled;
bool debug_path_dirty;
RID debug_path_instance;
@ -83,11 +99,6 @@ class NavigationAgent2D : public Node {
bool debug_use_custom;
Color debug_path_custom_color;
private:
void _navigation_debug_changed();
void _update_debug_path();
#endif // DEBUG_ENABLED
protected:
static void _bind_methods();
void _notification(int p_what);
@ -119,6 +130,21 @@ public:
void set_navigation_layer_value(int p_layer_number, bool p_value);
bool get_navigation_layer_value(int p_layer_number) const;
void set_avoidance_layers(uint32_t p_layers);
uint32_t get_avoidance_layers() const;
void set_avoidance_mask(uint32_t p_mask);
uint32_t get_avoidance_mask() const;
void set_avoidance_layer_value(int p_layer_number, bool p_value);
bool get_avoidance_layer_value(int p_layer_number) const;
void set_avoidance_mask_value(int p_mask_number, bool p_value);
bool get_avoidance_mask_value(int p_mask_number) const;
void set_avoidance_priority(real_t p_priority);
real_t get_avoidance_priority() const;
void set_path_metadata_flags(const int p_flags);
int get_path_metadata_flags() const;
@ -150,10 +176,11 @@ public:
return max_neighbors;
}
void set_time_horizon(real_t p_time);
real_t get_time_horizon() const {
return time_horizon;
}
void set_time_horizon_agents(real_t p_time_horizon);
real_t get_time_horizon_agents() const { return time_horizon_agents; }
void set_time_horizon_obstacles(real_t p_time_horizon);
real_t get_time_horizon_obstacles() const { return time_horizon_obstacles; }
void set_max_speed(real_t p_max_speed);
real_t get_max_speed() const {
@ -170,9 +197,9 @@ public:
Ref<NavigationPathQueryResult2D> get_current_navigation_result() const;
const Vector<Vector2> &get_nav_path() const;
const Vector<Vector2> &get_current_navigation_path() const;
int get_nav_path_index() const {
int get_current_navigation_path_index() const {
return nav_path_index;
}
@ -182,12 +209,15 @@ public:
bool is_navigation_finished();
Vector2 get_final_position();
void set_velocity(Vector2 p_velocity);
void set_velocity(const Vector2 p_velocity);
Vector2 get_velocity() { return velocity; }
void set_velocity_forced(const Vector2 p_velocity);
void _avoidance_done(Vector3 p_new_velocity);
virtual String get_configuration_warning() const;
#ifdef DEBUG_ENABLED
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;
@ -202,12 +232,16 @@ public:
void set_debug_path_custom_line_width(float p_line_width);
float get_debug_path_custom_line_width() const;
#endif // DEBUG_ENABLED
private:
void update_navigation();
void _request_repath();
void _check_distance_to_target();
#ifdef DEBUG_ENABLED
void _navigation_debug_changed();
void _update_debug_path();
#endif // DEBUG_ENABLED
};
#endif

View File

@ -109,7 +109,7 @@ void NavigationLink2D::_notification(int p_what) {
} break;
case NOTIFICATION_DRAW: {
#ifdef DEBUG_ENABLED
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled())) {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled())) {
Color color;
if (enabled) {
color = Navigation2DServer::get_singleton()->get_debug_navigation_link_connection_color();
@ -168,7 +168,7 @@ void NavigationLink2D::set_enabled(bool p_enabled) {
}
#ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) {
if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) {
update();
}
#endif // DEBUG_ENABLED
@ -232,7 +232,7 @@ void NavigationLink2D::set_start_position(Vector2 p_position) {
update_configuration_warning();
#ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) {
if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) {
update();
}
#endif // DEBUG_ENABLED
@ -254,7 +254,7 @@ void NavigationLink2D::set_end_position(Vector2 p_position) {
update_configuration_warning();
#ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) {
if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) {
update();
}
#endif // DEBUG_ENABLED

View File

@ -30,41 +30,45 @@
#include "navigation_obstacle_2d.h"
#include "core/config/engine.h"
#include "scene/2d/collision_shape_2d.h"
#include "scene/2d/navigation_2d.h"
#include "scene/2d/node_2d.h"
#include "scene/2d/physics_body_2d.h"
#include "scene/resources/world_2d.h"
#include "servers/navigation_2d_server.h"
void NavigationObstacle2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle2D::get_rid);
ClassDB::bind_method(D_METHOD("get_obstacle_rid"), &NavigationObstacle2D::get_obstacle_rid);
ClassDB::bind_method(D_METHOD("get_agent_rid"), &NavigationObstacle2D::get_agent_rid);
ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationObstacle2D::set_navigation_node);
ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationObstacle2D::get_navigation_node);
ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius);
ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle2D::get_radius);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.01,500,0.01"), "set_radius", "get_radius");
}
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationObstacle2D::set_velocity);
ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationObstacle2D::get_velocity);
void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "radius") {
if (estimate_radius) {
p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
}
ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationObstacle2D::set_vertices);
ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationObstacle2D::get_vertices);
ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationObstacle2D::set_avoidance_layers);
ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationObstacle2D::get_avoidance_layers);
ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationObstacle2D::set_avoidance_layer_value);
ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationObstacle2D::get_avoidance_layer_value);
ADD_GROUP("Avoidance", "avoidance_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_velocity", "get_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.0,500,0.01,suffix:px"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "vertices"), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
}
void NavigationObstacle2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
parent_node2d = Object::cast_to<Node2D>(get_parent());
reevaluate_agent_radius();
// Search the navigation node and set it
{
Navigation2D *nav = nullptr;
@ -78,69 +82,131 @@ void NavigationObstacle2D::_notification(int p_what) {
}
}
set_navigation(nav);
navigation = nav;
}
if (navigation) {
_update_map(navigation->get_rid());
} else if (map_override.is_valid()) {
_update_map(map_override);
} else if (is_inside_tree()) {
_update_map(get_world_2d()->get_navigation_map());
} else {
_update_map(RID());
}
previous_transform = get_global_transform();
// need to trigger map controlled agent assignment somehow for the fake_agent since obstacles use no callback like regular agents
Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0);
_update_position(get_global_transform().get_origin());
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
set_navigation(nullptr);
set_physics_process_internal(false);
_update_map(RID());
request_ready(); // required to solve an issue with losing the navigation
} break;
case NOTIFICATION_PARENTED: {
parent_node2d = Object::cast_to<Node2D>(get_parent());
reevaluate_agent_radius();
} break;
case NOTIFICATION_UNPARENTED: {
parent_node2d = nullptr;
} break;
case NOTIFICATION_PAUSED: {
if (parent_node2d && !parent_node2d->can_process()) {
map_before_pause = Navigation2DServer::get_singleton()->agent_get_map(get_rid());
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), RID());
} else if (parent_node2d && parent_node2d->can_process() && !(map_before_pause == RID())) {
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), map_before_pause);
if (!can_process()) {
map_before_pause = map_current;
_update_map(RID());
} else if (can_process() && !(map_before_pause == RID())) {
_update_map(map_before_pause);
map_before_pause = RID();
}
} break;
case NOTIFICATION_UNPAUSED: {
if (parent_node2d && !parent_node2d->can_process()) {
map_before_pause = Navigation2DServer::get_singleton()->agent_get_map(get_rid());
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), RID());
} else if (parent_node2d && parent_node2d->can_process() && !(map_before_pause == RID())) {
Navigation2DServer::get_singleton()->agent_set_map(get_rid(), map_before_pause);
if (!can_process()) {
map_before_pause = map_current;
_update_map(RID());
} else if (can_process() && !(map_before_pause == RID())) {
_update_map(map_before_pause);
map_before_pause = RID();
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (parent_node2d && parent_node2d->is_inside_tree()) {
Navigation2DServer::get_singleton()->agent_set_position(agent, parent_node2d->get_global_transform().get_origin());
}
if (is_inside_tree()) {
_update_position(get_global_transform().get_origin());
if (velocity_submitted) {
velocity_submitted = false;
// only update if there is a noticeable change, else the rvo agent preferred velocity stays the same
if (!previous_velocity.is_equal_approx(velocity)) {
Navigation2DServer::get_singleton()->agent_set_velocity(fake_agent, velocity);
}
previous_velocity = velocity;
}
}
} break;
case NOTIFICATION_DRAW: {
#ifdef DEBUG_ENABLED
if (is_inside_tree()) {
bool is_debug_enabled = false;
if (Engine::get_singleton()->is_editor_hint()) {
is_debug_enabled = true;
} else if (Navigation2DServer::get_singleton()->get_debug_enabled() && Navigation2DServer::get_singleton()->get_debug_avoidance_enabled()) {
is_debug_enabled = true;
}
if (is_debug_enabled) {
_update_fake_agent_radius_debug();
_update_static_obstacle_debug();
}
}
#endif // DEBUG_ENABLED
} break;
}
}
NavigationObstacle2D::NavigationObstacle2D() :
navigation(nullptr),
agent(RID()) {
agent = Navigation2DServer::get_singleton()->agent_create();
initialize_agent();
NavigationObstacle2D::NavigationObstacle2D() {
navigation = nullptr;
obstacle = Navigation2DServer::get_singleton()->obstacle_create();
fake_agent = Navigation2DServer::get_singleton()->agent_create();
// change properties of the fake agent so it can act as fake obstacle with a radius
Navigation2DServer::get_singleton()->agent_set_neighbor_dist(fake_agent, 0.0);
Navigation2DServer::get_singleton()->agent_set_max_neighbors(fake_agent, 0);
Navigation2DServer::get_singleton()->agent_set_time_horizon_agents(fake_agent, 0.0);
Navigation2DServer::get_singleton()->agent_set_time_horizon_obstacles(fake_agent, 0.0);
Navigation2DServer::get_singleton()->agent_set_max_speed(fake_agent, 0.0);
Navigation2DServer::get_singleton()->agent_set_avoidance_mask(fake_agent, 0);
Navigation2DServer::get_singleton()->agent_set_avoidance_priority(fake_agent, 1.0);
Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0);
set_radius(radius);
set_vertices(vertices);
set_avoidance_layers(avoidance_layers);
}
NavigationObstacle2D::~NavigationObstacle2D() {
Navigation2DServer::get_singleton()->free(agent);
agent = RID(); // Pointless
ERR_FAIL_NULL(Navigation2DServer::get_singleton());
Navigation2DServer::get_singleton()->free(obstacle);
obstacle = RID();
Navigation2DServer::get_singleton()->free(fake_agent);
fake_agent = RID();
}
void NavigationObstacle2D::set_navigation(Navigation2D *p_nav) {
if (navigation == p_nav && navigation != nullptr) {
if (navigation == p_nav) {
return; // Pointless
}
navigation = p_nav;
Navigation2DServer::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid());
if (navigation) {
_update_map(navigation->get_rid());
} else if (map_override.is_valid()) {
_update_map(map_override);
} else if (is_inside_tree()) {
_update_map(get_world_2d()->get_navigation_map());
} else {
_update_map(RID());
}
}
void NavigationObstacle2D::set_navigation_node(Node *p_nav) {
@ -153,75 +219,145 @@ Node *NavigationObstacle2D::get_navigation_node() const {
return Object::cast_to<Node>(navigation);
}
String NavigationObstacle2D::get_configuration_warning() const {
if (!Object::cast_to<Node2D>(get_parent())) {
return TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.");
}
if (Object::cast_to<StaticBody2D>(get_parent())) {
return TTR("The NavigationObstacle2D is intended for constantly moving bodies like KinematicBody2D or RigidBody2D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
"\nNot constantly moving or complete static objects should be captured with a refreshed NavigationPolygon so agents can not only avoid them but also move along those objects outline at high detail");
}
return String();
}
void NavigationObstacle2D::initialize_agent() {
Navigation2DServer::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
Navigation2DServer::get_singleton()->agent_set_max_neighbors(agent, 0);
Navigation2DServer::get_singleton()->agent_set_time_horizon(agent, 0.0);
Navigation2DServer::get_singleton()->agent_set_max_speed(agent, 0.0);
}
void NavigationObstacle2D::reevaluate_agent_radius() {
if (!estimate_radius) {
Navigation2DServer::get_singleton()->agent_set_radius(agent, radius);
} else if (parent_node2d && parent_node2d->is_inside_tree()) {
Navigation2DServer::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
void NavigationObstacle2D::set_vertices(const Vector<Vector2> &p_vertices) {
vertices = p_vertices;
Navigation2DServer::get_singleton()->obstacle_set_vertices(obstacle, vertices);
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
update();
}
}
real_t NavigationObstacle2D::estimate_agent_radius() const {
if (parent_node2d && parent_node2d->is_inside_tree()) {
// Estimate the radius of this physics body
real_t radius = 0.0;
for (int i(0); i < parent_node2d->get_child_count(); i++) {
// For each collision shape
CollisionShape2D *cs = Object::cast_to<CollisionShape2D>(parent_node2d->get_child(i));
if (cs && cs->is_inside_tree()) {
// Take the distance between the Body center to the shape center
real_t r = cs->get_transform().get_origin().length();
if (cs->get_shape().is_valid()) {
// and add the enclosing shape radius
r += cs->get_shape()->get_enclosing_radius();
}
Size2 s = cs->get_global_transform().get_scale();
r *= MAX(s.x, s.y);
// Takes the biggest radius
radius = MAX(radius, r);
} else if (cs && !cs->is_inside_tree()) {
WARN_PRINT("A CollisionShape2D of the NavigationObstacle2D parent node was not inside the SceneTree when estimating the obstacle radius."
"\nMove the NavigationObstacle2D to a child position below any CollisionShape2D node of the parent node so the CollisionShape2D is already inside the SceneTree.");
}
}
Vector2 s = parent_node2d->get_global_transform().get_scale();
radius *= MAX(s.x, s.y);
if (radius > 0.0) {
return radius;
}
void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) {
if (map_override == p_navigation_map) {
return;
}
return 1.0; // Never a 0 radius
map_override = p_navigation_map;
_update_map(map_override);
}
void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) {
estimate_radius = p_estimate_radius;
_change_notify();
reevaluate_agent_radius();
RID NavigationObstacle2D::get_navigation_map() const {
if (navigation) {
return navigation->get_rid();
} else if (map_override.is_valid()) {
return map_override;
} else if (is_inside_tree()) {
return get_world_2d()->get_navigation_map();
}
return RID();
}
void NavigationObstacle2D::set_radius(real_t p_radius) {
ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
if (Math::is_equal_approx(radius, p_radius)) {
return;
}
radius = p_radius;
reevaluate_agent_radius();
Navigation2DServer::get_singleton()->agent_set_avoidance_enabled(fake_agent, radius > 0.0);
Navigation2DServer::get_singleton()->agent_set_radius(fake_agent, radius);
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
update();
}
}
void NavigationObstacle2D::set_avoidance_layers(uint32_t p_layers) {
avoidance_layers = p_layers;
Navigation2DServer::get_singleton()->obstacle_set_avoidance_layers(obstacle, avoidance_layers);
Navigation2DServer::get_singleton()->agent_set_avoidance_layers(fake_agent, avoidance_layers);
}
uint32_t NavigationObstacle2D::get_avoidance_layers() const {
return avoidance_layers;
}
void NavigationObstacle2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
uint32_t avoidance_layers_new = get_avoidance_layers();
if (p_value) {
avoidance_layers_new |= 1 << (p_layer_number - 1);
} else {
avoidance_layers_new &= ~(1 << (p_layer_number - 1));
}
set_avoidance_layers(avoidance_layers_new);
}
bool NavigationObstacle2D::get_avoidance_layer_value(int p_layer_number) const {
ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
return get_avoidance_layers() & (1 << (p_layer_number - 1));
}
void NavigationObstacle2D::set_velocity(const Vector2 p_velocity) {
velocity = p_velocity;
velocity_submitted = true;
Navigation2DServer::get_singleton()->agent_set_velocity(fake_agent, velocity);
}
void NavigationObstacle2D::_update_map(RID p_map) {
Navigation2DServer::get_singleton()->obstacle_set_map(obstacle, p_map);
Navigation2DServer::get_singleton()->agent_set_map(fake_agent, p_map);
map_current = p_map;
}
void NavigationObstacle2D::_update_position(const Vector2 p_position) {
if (vertices.size() > 0) {
Navigation2DServer::get_singleton()->obstacle_set_position(obstacle, p_position);
}
if (radius > 0.0) {
Navigation2DServer::get_singleton()->agent_set_position(fake_agent, p_position);
}
}
#ifdef DEBUG_ENABLED
void NavigationObstacle2D::_update_fake_agent_radius_debug() {
if (radius > 0.0 && Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
Color debug_radius_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_color();
draw_circle(get_global_transform().get_origin(), radius, debug_radius_color);
}
}
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
void NavigationObstacle2D::_update_static_obstacle_debug() {
if (get_vertices().size() > 2 && Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static()) {
bool obstacle_pushes_inward = Geometry::is_polygon_clockwise(get_vertices());
Color debug_static_obstacle_face_color;
if (obstacle_pushes_inward) {
debug_static_obstacle_face_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_color();
} else {
debug_static_obstacle_face_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_color();
}
Vector<Vector2> debug_obstacle_polygon_vertices = get_vertices();
Vector<Color> debug_obstacle_polygon_colors;
debug_obstacle_polygon_colors.resize(debug_obstacle_polygon_vertices.size());
debug_obstacle_polygon_colors.fill(debug_static_obstacle_face_color);
RS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), debug_obstacle_polygon_vertices, debug_obstacle_polygon_colors);
Color debug_static_obstacle_edge_color;
if (obstacle_pushes_inward) {
debug_static_obstacle_edge_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
} else {
debug_static_obstacle_edge_color = Navigation2DServer::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
}
Vector<Vector2> debug_obstacle_line_vertices = get_vertices();
debug_obstacle_line_vertices.push_back(debug_obstacle_line_vertices[0]);
debug_obstacle_line_vertices.resize(debug_obstacle_line_vertices.size());
Vector<Color> debug_obstacle_line_colors;
debug_obstacle_line_colors.resize(debug_obstacle_line_vertices.size());
debug_obstacle_line_colors.fill(debug_static_obstacle_edge_color);
RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_obstacle_line_vertices, debug_obstacle_line_colors, 4.0);
}
}
#endif // DEBUG_ENABLED

View File

@ -30,26 +30,42 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "scene/main/node.h"
#include "scene/2d/node_2d.h"
class Navigation2D;
class Node2D;
class NavigationObstacle2D : public Node {
GDCLASS(NavigationObstacle2D, Node);
class NavigationObstacle2D : public Node2D {
GDCLASS(NavigationObstacle2D, Node2D);
Navigation2D *navigation;
Node2D *parent_node2d = nullptr;
RID agent;
RID obstacle;
RID map_before_pause;
RID map_override; //!!!
RID map_current;
bool estimate_radius = true;
real_t radius = 1.0;
real_t radius = 0.0;
Vector<Vector2> vertices;
RID fake_agent;
uint32_t avoidance_layers = 1;
Transform2D previous_transform;
Vector2 velocity;
Vector2 previous_velocity;
bool velocity_submitted = false;
#ifdef DEBUG_ENABLED
private:
void _update_fake_agent_radius_debug();
void _update_static_obstacle_debug();
#endif // DEBUG_ENABLED
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &property) const;
void _notification(int p_what);
public:
@ -64,24 +80,37 @@ public:
void set_navigation_node(Node *p_nav);
Node *get_navigation_node() const;
RID get_rid() const {
return agent;
}
RID get_obstacle_rid() const { return obstacle; }
RID get_agent_rid() const { return fake_agent; }
void set_navigation_map(RID p_navigation_map);
RID get_navigation_map() const;
void set_estimate_radius(bool p_estimate_radius);
bool is_radius_estimated() const {
return estimate_radius;
}
void set_radius(real_t p_radius);
real_t get_radius() const {
return radius;
}
virtual String get_configuration_warning() const;
void set_vertices(const Vector<Vector2> &p_vertices);
const Vector<Vector2> &get_vertices() const { return vertices; };
void set_avoidance_layers(uint32_t p_layers);
uint32_t get_avoidance_layers() const;
void set_avoidance_mask(uint32_t p_mask);
uint32_t get_avoidance_mask() const;
void set_avoidance_layer_value(int p_layer_number, bool p_value);
bool get_avoidance_layer_value(int p_layer_number) const;
void set_velocity(const Vector2 p_velocity);
Vector2 get_velocity() const { return velocity; };
void _avoidance_done(Vector3 p_new_velocity); // Dummy
private:
void initialize_agent();
void reevaluate_agent_radius();
real_t estimate_agent_radius() const;
void _update_map(RID p_map);
void _update_position(const Vector2 p_position);
};
#endif

View File

@ -32,10 +32,12 @@
#include "core/config/engine.h"
#include "core/core_string_names.h"
#include "core/math/geometry.h"
#include "core/object/func_ref.h"
#include "core/os/mutex.h"
#include "core/os/os.h"
#include "navigation_2d.h"
#include "scene/2d/navigation_obstacle_2d.h"
#include "scene/resources/navigation_mesh.h"
#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
#include "scene/resources/navigation_polygon.h"
@ -58,6 +60,7 @@ void NavigationPolygonInstance::set_enabled(bool p_enabled) {
if (!enabled) {
Navigation2DServer::get_singleton()->region_set_map(region, RID());
Navigation2DServer::get_singleton()->disconnect("map_changed", this, "_navigation_map_changed");
} else {
if (navigation != nullptr) {
Navigation2DServer::get_singleton()->region_set_map(region, navigation->get_rid());
@ -68,10 +71,12 @@ void NavigationPolygonInstance::set_enabled(bool p_enabled) {
Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
}
}
Navigation2DServer::get_singleton()->connect("map_changed", this, "_navigation_map_changed");
}
#ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) {
if (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) {
update();
}
#endif // DEBUG_ENABLED
@ -184,12 +189,17 @@ void NavigationPolygonInstance::_notification(int p_what) {
c = Object::cast_to<Node2D>(c->get_parent());
}
if (enabled && navigation == nullptr) {
// did not find a valid navigation node parent, fallback to default navigation map on world resource
if (map_override.is_valid()) {
Navigation2DServer::get_singleton()->region_set_map(region, map_override);
} else {
Navigation2DServer::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
if (enabled) {
RID map = get_navigation_map();
Navigation2DServer::get_singleton()->region_set_map(region, map);
Navigation2DServer::get_singleton()->connect("map_changed", this, "_navigation_map_changed");
for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
if (constrain_avoidance_obstacles[i].is_valid()) {
Navigation2DServer::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map);
Navigation2DServer::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
}
}
}
@ -198,6 +208,12 @@ void NavigationPolygonInstance::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
set_physics_process_internal(true);
for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
if (constrain_avoidance_obstacles[i].is_valid()) {
Navigation2DServer::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
}
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
set_physics_process_internal(false);
@ -218,14 +234,23 @@ void NavigationPolygonInstance::_notification(int p_what) {
}
} break;
case NOTIFICATION_EXIT_TREE: {
if (navigation) {
Navigation2DServer::get_singleton()->region_set_map(region, RID());
}
Navigation2DServer::get_singleton()->region_set_map(region, RID());
navigation = nullptr;
if (enabled) {
Navigation2DServer::get_singleton()->disconnect("map_changed", this, "_navigation_map_changed");
}
for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
if (constrain_avoidance_obstacles[i].is_valid()) {
Navigation2DServer::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], RID());
}
}
} break;
case NOTIFICATION_DRAW: {
#ifdef DEBUG_ENABLED
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_enabled()) && navpoly.is_valid()) {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || Navigation2DServer::get_singleton()->get_debug_navigation_enabled()) && navpoly.is_valid()) {
_update_debug_mesh();
_update_debug_edge_connections_mesh();
}
@ -240,17 +265,17 @@ void NavigationPolygonInstance::set_navigation_polygon(const Ref<NavigationPolyg
}
if (navpoly.is_valid()) {
navpoly->disconnect(CoreStringNames::get_singleton()->changed, this, "_navpoly_changed");
navpoly->disconnect(CoreStringNames::get_singleton()->changed, this, "_navigation_polygon_changed");
}
navpoly = p_navpoly;
Navigation2DServer::get_singleton()->region_set_navpoly(region, p_navpoly);
if (navpoly.is_valid()) {
navpoly->connect(CoreStringNames::get_singleton()->changed, this, "_navpoly_changed");
navpoly->connect(CoreStringNames::get_singleton()->changed, this, "_navigation_polygon_changed");
}
_navpoly_changed();
_navigation_polygon_changed();
}
Ref<NavigationPolygon> NavigationPolygonInstance::get_navigation_polygon() const {
@ -268,7 +293,9 @@ void NavigationPolygonInstance::set_navigation_map(RID p_navigation_map) {
}
RID NavigationPolygonInstance::get_navigation_map() const {
if (map_override.is_valid()) {
if (navigation) {
return navigation->get_rid();
} else if (map_override.is_valid()) {
return map_override;
} else if (is_inside_tree()) {
return get_world_2d()->get_navigation_map();
@ -305,7 +332,7 @@ void NavigationPolygonInstance::_bake_finished() {
emit_signal("bake_finished");
}
void NavigationPolygonInstance::_navpoly_changed() {
void NavigationPolygonInstance::_navigation_polygon_changed() {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
update();
}
@ -313,6 +340,8 @@ void NavigationPolygonInstance::_navpoly_changed() {
Navigation2DServer::get_singleton()->region_set_navpoly(region, navpoly);
}
_update_avoidance_constrain();
emit_signal("navigation_polygon_changed");
_change_notify("navpoly");
@ -365,6 +394,13 @@ void NavigationPolygonInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationPolygonInstance::set_navigation_layer_value);
ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationPolygonInstance::get_navigation_layer_value);
ClassDB::bind_method(D_METHOD("set_constrain_avoidance", "enabled"), &NavigationPolygonInstance::set_constrain_avoidance);
ClassDB::bind_method(D_METHOD("get_constrain_avoidance"), &NavigationPolygonInstance::get_constrain_avoidance);
ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationPolygonInstance::set_avoidance_layers);
ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationPolygonInstance::get_avoidance_layers);
ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationPolygonInstance::set_avoidance_layer_value);
ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationPolygonInstance::get_avoidance_layer_value);
ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationPolygonInstance::set_enter_cost);
ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationPolygonInstance::get_enter_cost);
@ -388,12 +424,14 @@ void NavigationPolygonInstance::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "enter_cost"), "set_enter_cost", "get_enter_cost");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "travel_cost"), "set_travel_cost", "get_travel_cost");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constrain_avoidance"), "set_constrain_avoidance", "get_constrain_avoidance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
#ifdef DEBUG_ENABLED
ClassDB::bind_method(D_METHOD("_navigation_map_changed"), &NavigationPolygonInstance::_navigation_map_changed);
ClassDB::bind_method(D_METHOD("_navigation_debug_changed"), &NavigationPolygonInstance::_navigation_debug_changed);
#endif
ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationPolygonInstance::_navpoly_changed);
ClassDB::bind_method(D_METHOD("_navigation_map_changed"), &NavigationPolygonInstance::_navigation_map_changed);
ClassDB::bind_method(D_METHOD("_navigation_polygon_changed"), &NavigationPolygonInstance::_navigation_polygon_changed);
ADD_SIGNAL(MethodInfo("navigation_polygon_changed"));
ADD_SIGNAL(MethodInfo("bake_finished"));
@ -412,13 +450,15 @@ NavigationPolygonInstance::NavigationPolygonInstance() {
navigation_layers = 1;
constrain_avoidance = false;
avoidance_layers = 1;
baking_started = false;
Navigation2DServer::get_singleton()->region_set_owner_id(region, get_instance_id());
Navigation2DServer::get_singleton()->region_set_enter_cost(region, get_enter_cost());
Navigation2DServer::get_singleton()->region_set_travel_cost(region, get_travel_cost());
#ifdef DEBUG_ENABLED
Navigation2DServer::get_singleton()->connect("map_changed", this, "_navigation_map_changed");
NavigationServer::get_singleton()->connect("navigation_debug_changed", this, "_navigation_debug_changed");
#endif // DEBUG_ENABLED
}
@ -428,68 +468,135 @@ NavigationPolygonInstance::~NavigationPolygonInstance() {
Navigation2DServer::get_singleton()->free(region);
for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
if (constrain_avoidance_obstacles[i].is_valid()) {
Navigation2DServer::get_singleton()->free(constrain_avoidance_obstacles[i]);
}
}
constrain_avoidance_obstacles.clear();
#ifdef DEBUG_ENABLED
Navigation2DServer::get_singleton()->disconnect("map_changed", this, "_navigation_map_changed");
NavigationServer::get_singleton()->disconnect("navigation_debug_changed", this, "_navigation_debug_changed");
#endif // DEBUG_ENABLED
}
#ifdef DEBUG_ENABLED
void NavigationPolygonInstance::_update_debug_mesh() {
/*
Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices();
if (navigation_polygon_vertices.size() < 3) {
void NavigationPolygonInstance::_update_avoidance_constrain() {
for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
if (constrain_avoidance_obstacles[i].is_valid()) {
Navigation2DServer::get_singleton()->free(constrain_avoidance_obstacles[i]);
constrain_avoidance_obstacles[i] = RID();
}
}
constrain_avoidance_obstacles.clear();
if (!constrain_avoidance) {
return;
}
const Navigation2DServer2D *ns2d = Navigation2DServer2D::get_singleton();
bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color();
bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines();
Color debug_face_color = ns2d->get_debug_navigation_geometry_face_color();
Color debug_edge_color = ns2d->get_debug_navigation_geometry_edge_color();
if (!enabled) {
debug_face_color = ns2d->get_debug_navigation_geometry_face_disabled_color();
debug_edge_color = ns2d->get_debug_navigation_geometry_edge_disabled_color();
if (get_navigation_polygon() == nullptr) {
return;
}
RandomPCG rand;
Ref<NavigationPolygon> _navpoly = get_navigation_polygon();
int _outline_count = _navpoly->get_outline_count();
if (_outline_count == 0) {
return;
}
for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) {
// An array of vertices for this polygon.
Vector<int> polygon = navigation_polygon->get_polygon(i);
Vector<Vector2> debug_polygon_vertices;
debug_polygon_vertices.resize(polygon.size());
for (int j = 0; j < polygon.size(); j++) {
ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size());
debug_polygon_vertices.write[j] = navigation_polygon_vertices[polygon[j]];
for (int outline_index(0); outline_index < _outline_count; outline_index++) {
//TODO navpoly should probably use normal vectors internally
const PoolVector<Vector2> &_outline = _navpoly->get_outline(outline_index);
Vector<Vector2> outline;
outline.resize(_outline.size());
for (int i = 0; i < _outline.size(); ++i) {
outline.write[i] = outline[i];
}
// Generate the polygon color, slightly randomly modified from the settings one.
Color random_variation_color = debug_face_color;
if (enabled_geometry_face_random_color) {
random_variation_color.set_hsv(
debug_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1,
debug_face_color.get_s(),
debug_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2);
const int outline_size = _outline.size();
if (outline_size < 3) {
ERR_FAIL_COND_MSG(_outline.size() < 3, "NavigationPolygon outline needs to have at least 3 vertex to create avoidance obstacles to constrain avoidance agent's");
continue;
}
random_variation_color.a = debug_face_color.a;
Vector<Color> debug_face_colors;
debug_face_colors.push_back(random_variation_color);
RS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), debug_polygon_vertices, debug_face_colors);
RID obstacle_rid = Navigation2DServer::get_singleton()->obstacle_create();
constrain_avoidance_obstacles.push_back(obstacle_rid);
if (enabled_edge_lines) {
Vector<Color> debug_edge_colors;
debug_edge_colors.push_back(debug_edge_color);
debug_polygon_vertices.push_back(debug_polygon_vertices[0]); // Add first again for closing polyline.
RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_polygon_vertices, debug_edge_colors);
Vector<Vector2> new_obstacle_outline;
if (outline_index == 0) {
for (int i(0); i < outline_size; i++) {
new_obstacle_outline.push_back(_outline[outline_size - i - 1]);
}
ERR_FAIL_COND_MSG(Geometry::is_polygon_clockwise(outline), "Outermost outline needs to be clockwise to push avoidance agent inside");
} else {
for (int i(0); i < outline_size; i++) {
new_obstacle_outline.push_back(_outline[i]);
}
}
new_obstacle_outline.resize(outline_size);
Navigation2DServer::get_singleton()->obstacle_set_vertices(obstacle_rid, new_obstacle_outline);
Navigation2DServer::get_singleton()->obstacle_set_avoidance_layers(obstacle_rid, avoidance_layers);
if (is_inside_tree()) {
Navigation2DServer::get_singleton()->obstacle_set_map(obstacle_rid, get_world_2d()->get_navigation_map());
Navigation2DServer::get_singleton()->obstacle_set_position(obstacle_rid, get_global_position());
}
}
*/
constrain_avoidance_obstacles.resize(_outline_count);
}
void NavigationPolygonInstance::set_constrain_avoidance(bool p_enabled) {
constrain_avoidance = p_enabled;
_update_avoidance_constrain();
property_list_changed_notify();
}
bool NavigationPolygonInstance::get_constrain_avoidance() const {
return constrain_avoidance;
}
void NavigationPolygonInstance::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "avoidance_layers") {
if (!constrain_avoidance) {
p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
}
}
void NavigationPolygonInstance::set_avoidance_layers(uint32_t p_layers) {
avoidance_layers = p_layers;
if (constrain_avoidance_obstacles.size() > 0) {
for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
Navigation2DServer::get_singleton()->obstacle_set_avoidance_layers(constrain_avoidance_obstacles[i], avoidance_layers);
}
}
}
uint32_t NavigationPolygonInstance::get_avoidance_layers() const {
return avoidance_layers;
}
void NavigationPolygonInstance::set_avoidance_layer_value(int p_layer_number, bool p_value) {
ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
uint32_t avoidance_layers_new = get_avoidance_layers();
if (p_value) {
avoidance_layers_new |= 1 << (p_layer_number - 1);
} else {
avoidance_layers_new &= ~(1 << (p_layer_number - 1));
}
set_avoidance_layers(avoidance_layers_new);
}
bool NavigationPolygonInstance::get_avoidance_layer_value(int p_layer_number) const {
ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
return get_avoidance_layers() & (1 << (p_layer_number - 1));
}
#ifdef DEBUG_ENABLED
void NavigationPolygonInstance::_update_debug_mesh() {
PoolVector<Vector2> navigation_polygon_vertices = navpoly->get_vertices();
if (navigation_polygon_vertices.size() < 3) {
return;
@ -499,11 +606,11 @@ void NavigationPolygonInstance::_update_debug_mesh() {
bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color();
bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines();
bool enable_edge_connections = use_edge_connections && ns2d->get_debug_navigation_enable_edge_connections() && ns2d->map_get_use_edge_connections(get_world_2d()->get_navigation_map());
//bool enable_edge_connections = use_edge_connections && ns2d->get_debug_navigation_enable_edge_connections() && ns2d->map_get_use_edge_connections(get_world_2d()->get_navigation_map());
Color debug_face_color = ns2d->get_debug_navigation_geometry_face_color();
Color debug_edge_color = ns2d->get_debug_navigation_geometry_edge_color();
Color debug_edge_connection_color = ns2d->get_debug_navigation_edge_connection_color();
//Color debug_edge_connection_color = ns2d->get_debug_navigation_edge_connection_color();
if (!enabled) {
debug_face_color = ns2d->get_debug_navigation_geometry_face_disabled_color();
@ -540,17 +647,24 @@ void NavigationPolygonInstance::_update_debug_mesh() {
RS::get_singleton()->canvas_item_add_polyline(get_canvas_item(), debug_polygon_vertices, debug_edge_colors);
}
}
}
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
void NavigationPolygonInstance::_update_debug_edge_connections_mesh() {
const Navigation2DServer *ns2d = Navigation2DServer::get_singleton();
bool enable_edge_connections = ns2d->get_debug_navigation_enable_edge_connections();
if (enable_edge_connections) {
Color debug_edge_connection_color = ns2d->get_debug_navigation_edge_connection_color();
// Draw the region edge connections.
Transform2D xform = get_global_transform();
RID map = get_navigation_map();
real_t radius = 1.0;
if (navigation != nullptr) {
radius = ns2d->map_get_edge_connection_margin(navigation->get_rid()) / 2.0;
} else {
radius = ns2d->map_get_edge_connection_margin(get_world_2d()->get_navigation_map()) / 2.0;
}
radius = ns2d->map_get_edge_connection_margin(map) / 2.0;
for (int i = 0; i < ns2d->region_get_connections_count(region); i++) {
// Two main points
@ -568,31 +682,3 @@ void NavigationPolygonInstance::_update_debug_mesh() {
}
}
#endif // DEBUG_ENABLED
#ifdef DEBUG_ENABLED
void NavigationPolygonInstance::_update_debug_edge_connections_mesh() {
const Navigation2DServer *ns2d = Navigation2DServer::get_singleton();
const Navigation2DServer *ns = Navigation2DServer::get_singleton();
bool enable_edge_connections = ns->get_debug_navigation_enable_edge_connections();
Color debug_edge_connection_color = ns->get_debug_navigation_edge_connection_color();
if (enable_edge_connections) {
// Draw the region edge connections.
Transform2D xform = get_global_transform();
real_t radius = ns2d->map_get_edge_connection_margin(get_world_2d()->get_navigation_map()) / 2.0;
for (int i = 0; i < ns2d->region_get_connections_count(region); i++) {
// Two main points
Vector2 a = ns2d->region_get_connection_pathway_start(region, i);
a = xform.affine_inverse().xform(a);
Vector2 b = ns2d->region_get_connection_pathway_end(region, i);
b = xform.affine_inverse().xform(b);
draw_line(a, b, debug_edge_connection_color);
// Draw a circle to illustrate the margins.
real_t angle = a.angle_to_point(b);
draw_arc(a, radius, angle + Math_PI / 2.0, angle - Math_PI / 2.0 + Math_TAU, 10, debug_edge_connection_color);
draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, debug_edge_connection_color);
}
}
}
#endif // DEBUG_ENABLED

View File

@ -57,7 +57,11 @@ class NavigationPolygonInstance : public Node2D {
Transform2D current_global_transform;
void _navpoly_changed();
bool constrain_avoidance;
LocalVector<RID> constrain_avoidance_obstacles;
uint32_t avoidance_layers;
void _navigation_polygon_changed();
#ifdef DEBUG_ENABLED
void _update_debug_mesh();
@ -68,6 +72,7 @@ class NavigationPolygonInstance : public Node2D {
protected:
void _notification(int p_what);
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
public:
@ -99,6 +104,15 @@ public:
void set_navigation_polygon(const Ref<NavigationPolygon> &p_navpoly);
Ref<NavigationPolygon> get_navigation_polygon() const;
void set_constrain_avoidance(bool p_enabled);
bool get_constrain_avoidance() const;
void set_avoidance_layers(uint32_t p_layers);
uint32_t get_avoidance_layers() const;
void set_avoidance_layer_value(int p_layer_number, bool p_value);
bool get_avoidance_layer_value(int p_layer_number) const;
void set_navigation_map(RID p_navigation_map);
RID get_navigation_map() const;
@ -109,6 +123,9 @@ public:
NavigationPolygonInstance();
~NavigationPolygonInstance();
private:
void _update_avoidance_constrain();
};
#endif // NAVIGATIONPOLYGON_H

View File

@ -56,11 +56,14 @@ void NavigationAgent::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationAgent::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationAgent::get_radius);
ClassDB::bind_method(D_METHOD("set_agent_height_offset", "agent_height_offset"), &NavigationAgent::set_agent_height_offset);
ClassDB::bind_method(D_METHOD("get_agent_height_offset"), &NavigationAgent::get_agent_height_offset);
ClassDB::bind_method(D_METHOD("set_height", "height"), &NavigationAgent::set_height);
ClassDB::bind_method(D_METHOD("get_height"), &NavigationAgent::get_height);
ClassDB::bind_method(D_METHOD("set_ignore_y", "ignore"), &NavigationAgent::set_ignore_y);
ClassDB::bind_method(D_METHOD("get_ignore_y"), &NavigationAgent::get_ignore_y);
ClassDB::bind_method(D_METHOD("set_path_height_offset", "path_height_offset"), &NavigationAgent::set_path_height_offset);
ClassDB::bind_method(D_METHOD("get_path_height_offset"), &NavigationAgent::get_path_height_offset);
ClassDB::bind_method(D_METHOD("set_use_3d_avoidance", "enabled"), &NavigationAgent::set_use_3d_avoidance);
ClassDB::bind_method(D_METHOD("get_use_3d_avoidance"), &NavigationAgent::get_use_3d_avoidance);
ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationAgent::set_navigation_node);
ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationAgent::get_navigation_node);
@ -74,8 +77,11 @@ void NavigationAgent::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_neighbors", "max_neighbors"), &NavigationAgent::set_max_neighbors);
ClassDB::bind_method(D_METHOD("get_max_neighbors"), &NavigationAgent::get_max_neighbors);
ClassDB::bind_method(D_METHOD("set_time_horizon", "time_horizon"), &NavigationAgent::set_time_horizon);
ClassDB::bind_method(D_METHOD("get_time_horizon"), &NavigationAgent::get_time_horizon);
ClassDB::bind_method(D_METHOD("set_time_horizon_agents", "time_horizon"), &NavigationAgent::set_time_horizon_agents);
ClassDB::bind_method(D_METHOD("get_time_horizon_agents"), &NavigationAgent::get_time_horizon_agents);
ClassDB::bind_method(D_METHOD("set_time_horizon_obstacles", "time_horizon"), &NavigationAgent::set_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("get_time_horizon_obstacles"), &NavigationAgent::get_time_horizon_obstacles);
ClassDB::bind_method(D_METHOD("set_max_speed", "max_speed"), &NavigationAgent::set_max_speed);
ClassDB::bind_method(D_METHOD("get_max_speed"), &NavigationAgent::get_max_speed);
@ -96,35 +102,58 @@ void NavigationAgent::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent::get_target_position);
ClassDB::bind_method(D_METHOD("get_next_position"), &NavigationAgent::get_next_position);
ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent::distance_to_target);
ClassDB::bind_method(D_METHOD("set_velocity_forced", "velocity"), &NavigationAgent::set_velocity_forced);
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &NavigationAgent::set_velocity);
ClassDB::bind_method(D_METHOD("get_velocity"), &NavigationAgent::get_velocity);
ClassDB::bind_method(D_METHOD("distance_to_target"), &NavigationAgent::distance_to_target);
ClassDB::bind_method(D_METHOD("get_current_navigation_result"), &NavigationAgent::get_current_navigation_result);
ClassDB::bind_method(D_METHOD("get_nav_path"), &NavigationAgent::get_nav_path);
ClassDB::bind_method(D_METHOD("get_nav_path_index"), &NavigationAgent::get_nav_path_index);
ClassDB::bind_method(D_METHOD("get_current_navigation_path"), &NavigationAgent::get_current_navigation_path);
ClassDB::bind_method(D_METHOD("get_current_navigation_path_index"), &NavigationAgent::get_current_navigation_path_index);
ClassDB::bind_method(D_METHOD("is_target_reached"), &NavigationAgent::is_target_reached);
ClassDB::bind_method(D_METHOD("is_target_reachable"), &NavigationAgent::is_target_reachable);
ClassDB::bind_method(D_METHOD("is_navigation_finished"), &NavigationAgent::is_navigation_finished);
ClassDB::bind_method(D_METHOD("get_final_position"), &NavigationAgent::get_final_position);
ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationAgent::set_avoidance_layers);
ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationAgent::get_avoidance_layers);
ClassDB::bind_method(D_METHOD("set_avoidance_mask", "mask"), &NavigationAgent::set_avoidance_mask);
ClassDB::bind_method(D_METHOD("get_avoidance_mask"), &NavigationAgent::get_avoidance_mask);
ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationAgent::set_avoidance_layer_value);
ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationAgent::get_avoidance_layer_value);
ClassDB::bind_method(D_METHOD("set_avoidance_mask_value", "mask_number", "value"), &NavigationAgent::set_avoidance_mask_value);
ClassDB::bind_method(D_METHOD("get_avoidance_mask_value", "mask_number"), &NavigationAgent::get_avoidance_mask_value);
ClassDB::bind_method(D_METHOD("set_avoidance_priority", "priority"), &NavigationAgent::set_avoidance_priority);
ClassDB::bind_method(D_METHOD("get_avoidance_priority"), &NavigationAgent::get_avoidance_priority);
ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent::_avoidance_done);
ADD_GROUP("Pathfinding", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_path_desired_distance", "get_path_desired_distance");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_target_desired_distance", "get_target_desired_distance");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01"), "set_agent_height_offset", "get_agent_height_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,or_greater,suffix:m"), "set_path_height_offset", "get_path_height_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1"), "set_path_max_distance", "get_path_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags");
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_velocity", "get_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.01,100,0.01,or_greater,suffix:m"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.01,100,0.01,or_greater,suffix:m"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_neighbor_dist", "get_neighbor_dist");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_time_horizon", "get_time_horizon");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_max_speed", "get_max_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon_agents", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_agents", "get_time_horizon_agents");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_horizon_obstacles", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_obstacles", "get_time_horizon_obstacles");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_speed", PROPERTY_HINT_RANGE, "0.01,10000,0.01,or_greater,suffix:m/s"), "set_max_speed", "get_max_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_3d_avoidance"), "set_use_3d_avoidance", "get_use_3d_avoidance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_mask", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_mask", "get_avoidance_mask");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "avoidance_priority", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_avoidance_priority", "get_avoidance_priority");
#ifdef DEBUG_ENABLED
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationAgent::set_debug_enabled);
@ -176,6 +205,10 @@ void NavigationAgent::_notification(int p_what) {
set_agent_parent(get_parent());
set_physics_process_internal(true);
if (avoidance_enabled) {
NavigationServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
}
#ifdef DEBUG_ENABLED
if (NavigationServer::get_singleton()->get_debug_enabled()) {
debug_path_dirty = true;
@ -227,11 +260,23 @@ void NavigationAgent::_notification(int p_what) {
}
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (agent_parent) {
if (avoidance_enabled) {
// agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding
// no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used
NavigationServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
if (agent_parent && target_position_submitted) {
if (velocity_submitted) {
velocity_submitted = false;
if (avoidance_enabled) {
NavigationServer::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin);
if (!use_3d_avoidance) {
stored_y_velocity = velocity.y;
velocity.y = 0.0;
}
NavigationServer::get_singleton()->agent_set_velocity(agent, velocity);
}
}
if (velocity_forced_submitted) {
velocity_forced_submitted = false;
if (avoidance_enabled) {
NavigationServer::get_singleton()->agent_set_velocity_forced(agent, velocity_forced);
}
}
_check_distance_to_target();
}
@ -250,24 +295,37 @@ NavigationAgent::NavigationAgent() {
navigation = nullptr;
avoidance_enabled = false;
use_3d_avoidance = false;
avoidance_layers = 1;
avoidance_mask = 1;
avoidance_priority = 1.0;
navigation_layers = 1;
path_metadata_flags = NavigationPathQueryParameters3D::PATH_METADATA_INCLUDE_ALL;
path_desired_distance = 1.0;
target_desired_distance = 1.0;
navigation_height_offset = 0.0;
path_height_offset = 0.0;
path_max_distance = 3.0;
velocity_submitted = false;
target_reached = false;
time_horizon_agents = 1.0;
time_horizon_obstacles = 0.0;
navigation_finished = true;
velocity_forced_submitted = false;
stored_y_velocity = 0.0;
target_position_submitted = false;
height = 1.0;
agent = NavigationServer::get_singleton()->agent_create();
set_neighbor_dist(0.0);
set_max_neighbors(0);
set_time_horizon(0.0);
set_radius(1.0);
set_max_speed(0.0);
set_ignore_y(false);
NavigationServer::get_singleton()->agent_set_neighbor_dist(agent, neighbor_dist);
NavigationServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
NavigationServer::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
NavigationServer::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
NavigationServer::get_singleton()->agent_set_radius(agent, radius);
NavigationServer::get_singleton()->agent_set_height(agent, height);
NavigationServer::get_singleton()->agent_set_max_speed(agent, max_speed);
nav_path_index = 0;
update_frame_id = 0;
@ -275,6 +333,12 @@ NavigationAgent::NavigationAgent() {
navigation_query.instance();
navigation_result.instance();
set_avoidance_layers(avoidance_layers);
set_avoidance_mask(avoidance_mask);
set_avoidance_priority(avoidance_priority);
set_use_3d_avoidance(use_3d_avoidance);
set_avoidance_enabled(avoidance_enabled);
#ifdef DEBUG_ENABLED
debug_enabled = false;
debug_path_dirty = true;
@ -307,9 +371,11 @@ NavigationAgent::~NavigationAgent() {
void NavigationAgent::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
if (avoidance_enabled) {
NavigationServer::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done");
NavigationServer::get_singleton()->agent_set_avoidance_enabled(agent, true);
NavigationServer::get_singleton()->agent_set_avoidance_callback(agent, get_instance_id(), "_avoidance_done");
} else {
NavigationServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
NavigationServer::get_singleton()->agent_set_avoidance_enabled(agent, false);
NavigationServer::get_singleton()->agent_set_avoidance_callback(agent, ObjectID(), "_avoidance_done");
}
}
@ -338,24 +404,17 @@ Node *NavigationAgent::get_navigation_node() const {
void NavigationAgent::set_agent_parent(Node *p_agent_parent) {
// remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map
NavigationServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done");
NavigationServer::get_singleton()->agent_set_avoidance_callback(agent, ObjectID(), "_avoidance_done");
if (Object::cast_to<Spatial>(p_agent_parent) != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
agent_parent = Object::cast_to<Spatial>(p_agent_parent);
if (map_override.is_valid()) {
NavigationServer::get_singleton()->agent_set_map(get_rid(), map_override);
} else if (navigation != nullptr) {
NavigationServer::get_singleton()->agent_set_map(get_rid(), navigation->get_rid());
} else {
// no navigation node found in parent nodes, use default navigation map from world resource
NavigationServer::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_3d()->get_navigation_map());
}
NavigationServer::get_singleton()->agent_set_map(get_rid(), get_navigation_map());
// create new avoidance callback if enabled
set_avoidance_enabled(avoidance_enabled);
} else {
agent_parent = nullptr;
NavigationServer::get_singleton()->agent_set_map(get_rid(), RID());
NavigationServer::get_singleton()->agent_set_map(get_rid(), get_navigation_map());
}
}
@ -403,7 +462,9 @@ void NavigationAgent::set_navigation_map(RID p_navigation_map) {
}
RID NavigationAgent::get_navigation_map() const {
if (map_override.is_valid()) {
if (navigation) {
return navigation->get_rid();
} else if (map_override.is_valid()) {
return map_override;
} else if (agent_parent != nullptr) {
return agent_parent->get_world_3d()->get_navigation_map();
@ -420,17 +481,29 @@ void NavigationAgent::set_target_desired_distance(real_t p_dd) {
}
void NavigationAgent::set_radius(real_t p_radius) {
ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive.");
radius = p_radius;
NavigationServer::get_singleton()->agent_set_radius(agent, radius);
}
void NavigationAgent::set_agent_height_offset(real_t p_hh) {
navigation_height_offset = p_hh;
void NavigationAgent::set_height(real_t p_height) {
ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive.");
if (Math::is_equal_approx(height, p_height)) {
return;
}
height = p_height;
NavigationServer::get_singleton()->agent_set_height(agent, height);
}
void NavigationAgent::set_ignore_y(bool p_ignore_y) {
ignore_y = p_ignore_y;
NavigationServer::get_singleton()->agent_set_ignore_y(agent, ignore_y);
void NavigationAgent::set_path_height_offset(real_t p_path_height_offset) {
path_height_offset = p_path_height_offset;
}
void NavigationAgent::set_use_3d_avoidance(bool p_use_3d_avoidance) {
use_3d_avoidance = p_use_3d_avoidance;
NavigationServer::get_singleton()->agent_set_use_3d_avoidance(agent, use_3d_avoidance);
property_list_changed_notify();
}
void NavigationAgent::set_neighbor_dist(real_t p_dist) {
@ -443,12 +516,26 @@ void NavigationAgent::set_max_neighbors(int p_count) {
NavigationServer::get_singleton()->agent_set_max_neighbors(agent, max_neighbors);
}
void NavigationAgent::set_time_horizon(real_t p_time) {
time_horizon = p_time;
NavigationServer::get_singleton()->agent_set_time_horizon(agent, time_horizon);
void NavigationAgent::set_time_horizon_agents(real_t p_time_horizon) {
ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
if (Math::is_equal_approx(time_horizon_agents, p_time_horizon)) {
return;
}
time_horizon_agents = p_time_horizon;
NavigationServer::get_singleton()->agent_set_time_horizon_agents(agent, time_horizon_agents);
}
void NavigationAgent::set_time_horizon_obstacles(real_t p_time_horizon) {
ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive.");
if (Math::is_equal_approx(time_horizon_obstacles, p_time_horizon)) {
return;
}
time_horizon_obstacles = p_time_horizon;
NavigationServer::get_singleton()->agent_set_time_horizon_obstacles(agent, time_horizon_obstacles);
}
void NavigationAgent::set_max_speed(real_t p_max_speed) {
ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive.");
max_speed = p_max_speed;
NavigationServer::get_singleton()->agent_set_max_speed(agent, max_speed);
}
@ -478,7 +565,7 @@ Vector3 NavigationAgent::get_next_position() {
ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent.");
return agent_parent->get_global_transform().origin;
} else {
return navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0);
return navigation_path[nav_path_index] - Vector3(0, path_height_offset, 0);
}
}
@ -486,7 +573,7 @@ Ref<NavigationPathQueryResult3D> NavigationAgent::get_current_navigation_result(
return navigation_result;
}
const Vector<Vector3> &NavigationAgent::get_nav_path() const {
const Vector<Vector3> &NavigationAgent::get_current_navigation_path() const {
return navigation_result->get_path();
}
@ -520,23 +607,28 @@ Vector3 NavigationAgent::get_final_position() {
return navigation_path[navigation_path.size() - 1];
}
void NavigationAgent::set_velocity(Vector3 p_velocity) {
target_velocity = p_velocity;
NavigationServer::get_singleton()->agent_set_target_velocity(agent, target_velocity);
NavigationServer::get_singleton()->agent_set_velocity(agent, prev_safe_velocity);
void NavigationAgent::set_velocity_forced(Vector3 p_velocity) {
// Intentionally not checking for equality of the parameter.
// We need to always submit the velocity to the navigation server, even when it is the same, in order to run avoidance every frame.
// Revisit later when the navigation server can update avoidance without users resubmitting the velocity.
velocity_forced = p_velocity;
velocity_forced_submitted = true;
}
void NavigationAgent::set_velocity(const Vector3 p_velocity) {
velocity = p_velocity;
velocity_submitted = true;
}
void NavigationAgent::_avoidance_done(Vector3 p_new_velocity) {
prev_safe_velocity = p_new_velocity;
safe_velocity = p_new_velocity;
if (!velocity_submitted) {
target_velocity = Vector3();
return;
if (!use_3d_avoidance) {
safe_velocity.y = stored_y_velocity;
}
velocity_submitted = false;
emit_signal("velocity_computed", p_new_velocity);
emit_signal("velocity_computed", safe_velocity);
}
String NavigationAgent::get_configuration_warning() const {
@ -578,8 +670,8 @@ void NavigationAgent::update_navigation() {
Vector3 segment[2];
segment[0] = navigation_path[nav_path_index - 1];
segment[1] = navigation_path[nav_path_index];
segment[0].y -= navigation_height_offset;
segment[1].y -= navigation_height_offset;
segment[0].y -= path_height_offset;
segment[1].y -= path_height_offset;
Vector3 p = Geometry::get_closest_point_to_segment(origin, segment);
if (origin.distance_to(p) >= path_max_distance) {
@ -594,14 +686,7 @@ void NavigationAgent::update_navigation() {
navigation_query->set_target_position(target_position);
navigation_query->set_navigation_layers(navigation_layers);
navigation_query->set_metadata_flags(path_metadata_flags);
if (map_override.is_valid()) {
navigation_query->set_map(map_override);
} else if (navigation != nullptr) {
navigation_query->set_map(navigation->get_rid());
} else {
navigation_query->set_map(agent_parent->get_world_3d()->get_navigation_map());
}
navigation_query->set_map(get_navigation_map());
NavigationServer::get_singleton()->query_path(navigation_query, navigation_result);
@ -626,7 +711,7 @@ void NavigationAgent::update_navigation() {
const Array &navigation_path_rids = navigation_result->get_path_rids();
const Vector<uint64_t> &navigation_path_owners = navigation_result->get_path_owner_ids();
while (origin.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < path_desired_distance) {
while (origin.distance_to(navigation_path[nav_path_index] - Vector3(0, path_height_offset, 0)) < path_desired_distance) {
Dictionary details;
const Vector3 waypoint = navigation_path[nav_path_index];
@ -695,6 +780,71 @@ void NavigationAgent::update_navigation() {
}
}
void NavigationAgent::set_avoidance_layers(uint32_t p_layers) {
avoidance_layers = p_layers;
NavigationServer::get_singleton()->agent_set_avoidance_layers(get_rid(), avoidance_layers);
}
uint32_t NavigationAgent::get_avoidance_layers() const {
return avoidance_layers;
}
void NavigationAgent::set_avoidance_mask(uint32_t p_mask) {
avoidance_mask = p_mask;
NavigationServer::get_singleton()->agent_set_avoidance_mask(get_rid(), avoidance_mask);
}
uint32_t NavigationAgent::get_avoidance_mask() const {
return avoidance_mask;
}
void NavigationAgent::set_avoidance_layer_value(int p_layer_number, bool p_value) {
ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
uint32_t avoidance_layers_new = get_avoidance_layers();
if (p_value) {
avoidance_layers_new |= 1 << (p_layer_number - 1);
} else {
avoidance_layers_new &= ~(1 << (p_layer_number - 1));
}
set_avoidance_layers(avoidance_layers_new);
}
bool NavigationAgent::get_avoidance_layer_value(int p_layer_number) const {
ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
return get_avoidance_layers() & (1 << (p_layer_number - 1));
}
void NavigationAgent::set_avoidance_mask_value(int p_mask_number, bool p_value) {
ERR_FAIL_COND_MSG(p_mask_number < 1, "Avoidance mask number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_MSG(p_mask_number > 32, "Avoidance mask number must be between 1 and 32 inclusive.");
uint32_t mask = get_avoidance_mask();
if (p_value) {
mask |= 1 << (p_mask_number - 1);
} else {
mask &= ~(1 << (p_mask_number - 1));
}
set_avoidance_mask(mask);
}
bool NavigationAgent::get_avoidance_mask_value(int p_mask_number) const {
ERR_FAIL_COND_V_MSG(p_mask_number < 1, false, "Avoidance mask number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_V_MSG(p_mask_number > 32, false, "Avoidance mask number must be between 1 and 32 inclusive.");
return get_avoidance_mask() & (1 << (p_mask_number - 1));
}
void NavigationAgent::set_avoidance_priority(real_t p_priority) {
ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive.");
avoidance_priority = p_priority;
NavigationServer::get_singleton()->agent_set_avoidance_priority(get_rid(), p_priority);
}
real_t NavigationAgent::get_avoidance_priority() const {
return avoidance_priority;
}
void NavigationAgent::_request_repath() {
navigation_result->reset();
target_reached = false;

View File

@ -1,5 +1,6 @@
#ifndef NAVIGATION_AGENT_H
#define NAVIGATION_AGENT_H
/*************************************************************************/
/* navigation_agent.h */
/*************************************************************************/
@ -50,29 +51,52 @@ class NavigationAgent : public Node {
RID map_override;
bool avoidance_enabled;
bool use_3d_avoidance;
uint32_t avoidance_layers;
uint32_t avoidance_mask;
real_t avoidance_priority;
uint32_t navigation_layers;
int path_metadata_flags;
real_t path_desired_distance;
real_t target_desired_distance;
real_t height;
real_t radius;
real_t navigation_height_offset;
bool ignore_y;
real_t path_height_offset;
real_t neighbor_dist;
int max_neighbors;
real_t time_horizon;
real_t time_horizon_agents;
real_t time_horizon_obstacles;
real_t max_speed;
real_t path_max_distance;
Vector3 target_position;
Ref<NavigationPathQueryParameters3D> navigation_query;
Ref<NavigationPathQueryResult3D> navigation_result;
int nav_path_index;
// the velocity result of the avoidance simulation step
Vector3 safe_velocity;
/// The submitted target velocity, sets the "wanted" rvo agent velocity on the next update
// this velocity is not guaranteed, the simulation will try to fulfil it if possible
// if other agents or obstacles interfere it will be changed accordingly
Vector3 velocity;
bool velocity_submitted;
Vector3 prev_safe_velocity;
/// The submitted target velocity
Vector3 target_velocity;
/// The submitted forced velocity, overrides the rvo agent velocity on the next update
// should only be used very intentionally and not every frame as it interferes with the simulation stability
Vector3 velocity_forced;
bool velocity_forced_submitted;
// 2D avoidance has no y-axis. This stores and reapplies the y-axis velocity to the agent before and after the avoidance step.
// While not perfect it at least looks way better than agent's that clip through everything that is not a flat surface
float stored_y_velocity;
bool target_position_submitted;
bool target_reached;
bool navigation_finished;
// No initialized on purpose
@ -88,10 +112,6 @@ class NavigationAgent : public Node {
Color debug_path_custom_color;
Ref<SpatialMaterial> debug_agent_path_line_custom_material;
Ref<SpatialMaterial> debug_agent_path_point_custom_material;
private:
void _navigation_debug_changed();
void _update_debug_path();
#endif // DEBUG_ENABLED
protected:
@ -125,6 +145,21 @@ public:
void set_navigation_layer_value(int p_layer_number, bool p_value);
bool get_navigation_layer_value(int p_layer_number) const;
void set_avoidance_layers(uint32_t p_layers);
uint32_t get_avoidance_layers() const;
void set_avoidance_mask(uint32_t p_mask);
uint32_t get_avoidance_mask() const;
void set_avoidance_layer_value(int p_layer_number, bool p_value);
bool get_avoidance_layer_value(int p_layer_number) const;
void set_avoidance_mask_value(int p_mask_number, bool p_value);
bool get_avoidance_mask_value(int p_mask_number) const;
void set_avoidance_priority(real_t p_priority);
real_t get_avoidance_priority() const;
void set_path_metadata_flags(const int p_flags);
int get_path_metadata_flags() const;
@ -146,15 +181,14 @@ public:
return radius;
}
void set_agent_height_offset(real_t p_hh);
real_t get_agent_height_offset() const {
return navigation_height_offset;
}
void set_height(real_t p_height);
real_t get_height() const { return height; }
void set_ignore_y(bool p_ignore_y);
bool get_ignore_y() const {
return ignore_y;
}
void set_path_height_offset(real_t p_path_height_offset);
real_t get_path_height_offset() const { return path_height_offset; }
void set_use_3d_avoidance(bool p_use_3d_avoidance);
bool get_use_3d_avoidance() const { return use_3d_avoidance; }
void set_neighbor_dist(real_t p_dist);
real_t get_neighbor_dist() const {
@ -166,10 +200,11 @@ public:
return max_neighbors;
}
void set_time_horizon(real_t p_time);
real_t get_time_horizon() const {
return time_horizon;
}
void set_time_horizon_agents(real_t p_time_horizon);
real_t get_time_horizon_agents() const { return time_horizon_agents; }
void set_time_horizon_obstacles(real_t p_time_horizon);
real_t get_time_horizon_obstacles() const { return time_horizon_obstacles; }
void set_max_speed(real_t p_max_speed);
real_t get_max_speed() const {
@ -186,9 +221,9 @@ public:
Ref<NavigationPathQueryResult3D> get_current_navigation_result() const;
const Vector<Vector3> &get_nav_path() const;
const Vector<Vector3> &get_current_navigation_path() const;
int get_nav_path_index() const {
int get_current_navigation_path_index() const {
return nav_path_index;
}
@ -198,7 +233,11 @@ public:
bool is_navigation_finished();
Vector3 get_final_position();
void set_velocity(Vector3 p_velocity);
void set_velocity(const Vector3 p_velocity);
Vector3 get_velocity() { return velocity; }
void set_velocity_forced(const Vector3 p_velocity);
void _avoidance_done(Vector3 p_new_velocity);
virtual String get_configuration_warning() const;
@ -221,6 +260,11 @@ private:
void update_navigation();
void _request_repath();
void _check_distance_to_target();
#ifdef DEBUG_ENABLED
void _navigation_debug_changed();
void _update_debug_path();
#endif // DEBUG_ENABLED
};
#endif

Some files were not shown because too many files have changed in this diff Show More