mirror of
https://github.com/Heretek-AI/GDevelop-Heretek.git
synced 2026-10-12 02:02:42 +00:00
Add experimental "navmesh-based" pathfinding behavior for 2D and 3D (#8943)
- This pathfinding works in 2D and 3D is more flexible than the "grid-based" existing pathfinding. - For 3D models, you can optionally choose, like for 3D physics, to follow the exact mesh of the 3D model for pathfinding - allowing characters and objects to move on or around the 3D model naturally. - It handles "crowds" of characters, which can all go to a destination avoiding each others.
This commit is contained in:
@@ -186,20 +186,6 @@ namespace gdjs {
|
||||
content: Object3DDataContent;
|
||||
}
|
||||
|
||||
/** @category Objects > 3D Objects */
|
||||
export namespace Base3DHandler {
|
||||
export const is3D = (
|
||||
object: gdjs.RuntimeObject
|
||||
): object is gdjs.RuntimeObject &
|
||||
gdjs.Base3DHandler &
|
||||
gdjs.Resizable &
|
||||
gdjs.Scalable &
|
||||
gdjs.Flippable => {
|
||||
//@ts-ignore We are checking if the methods are present.
|
||||
return object.getZ && object.setZ;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A behavior that forwards the Base3D interface to its object.
|
||||
* @category Core Engine > Behavior
|
||||
|
||||
@@ -0,0 +1,905 @@
|
||||
//@ts-check
|
||||
/// <reference path="../JsExtensionTypes.d.ts" />
|
||||
/**
|
||||
* This is a declaration of an extension for GDevelop 5.
|
||||
*
|
||||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||||
*
|
||||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||||
* and search for any errors.
|
||||
*
|
||||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||||
*/
|
||||
|
||||
/** @type {ExtensionModule} */
|
||||
module.exports = {
|
||||
createExtension: function (_, gd) {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension
|
||||
.setExtensionInformation(
|
||||
'NavMeshPathfinding',
|
||||
_('NavMesh pathfinding behavior'),
|
||||
'Pathfinding allows to compute an efficient path for objects, avoiding obstacles on the way.',
|
||||
'',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setShortDescription(
|
||||
'Nav-mesh based pathfinding : compute path avoiding obstacles and handle crowds.'
|
||||
)
|
||||
.setDimension('2D/3D')
|
||||
.setCategory('Movement')
|
||||
.setTags('pathfinding, obstacle, collision')
|
||||
.setExtensionHelpPath('/behaviors/nav-mesh-pathfinding');
|
||||
{
|
||||
const behavior = new gd.BehaviorJsImplementation();
|
||||
behavior.updateProperty = function (
|
||||
behaviorContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
if (propertyName === 'acceleration') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
behaviorContent
|
||||
.getOrCreateChild('acceleration')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'maxSpeed') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
behaviorContent
|
||||
.getOrCreateChild('maxSpeed')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'angularMaxSpeed') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
behaviorContent
|
||||
.getOrCreateChild('angularMaxSpeed')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'rotateObject') {
|
||||
behaviorContent
|
||||
.getOrCreateChild('rotateObject')
|
||||
.setBoolValue(newValue === '1');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'angleOffset') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
behaviorContent
|
||||
.getOrCreateChild('angleOffset')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'radius') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
behaviorContent
|
||||
.getOrCreateChild('radius')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'avoidanceSightRange') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
behaviorContent
|
||||
.getOrCreateChild('avoidanceSightRange')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
behavior.getProperties = function (behaviorContent) {
|
||||
const behaviorProperties = new gd.MapStringPropertyDescriptor();
|
||||
|
||||
behaviorProperties
|
||||
.getOrCreate('acceleration')
|
||||
.setValue(behaviorContent.getChild('acceleration').getStringValue())
|
||||
.setLabel(_('Acceleration'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixelAcceleration());
|
||||
|
||||
behaviorProperties
|
||||
.getOrCreate('maxSpeed')
|
||||
.setValue(behaviorContent.getChild('maxSpeed').getStringValue())
|
||||
.setLabel(_('Max. speed'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixelSpeed());
|
||||
|
||||
behaviorProperties
|
||||
.getOrCreate('angularMaxSpeed')
|
||||
.setValue(
|
||||
behaviorContent.getChild('angularMaxSpeed').getStringValue()
|
||||
)
|
||||
.setLabel(_('Rotation speed'))
|
||||
.setGroup(_('Rotation'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getAngularSpeed());
|
||||
|
||||
behaviorProperties
|
||||
.getOrCreate('rotateObject')
|
||||
.setValue(
|
||||
behaviorContent.getChild('rotateObject').getBoolValue()
|
||||
? 'true'
|
||||
: 'false'
|
||||
)
|
||||
.setLabel(_('Rotate object'))
|
||||
.setGroup(_('Rotation'))
|
||||
.setType('Boolean');
|
||||
|
||||
behaviorProperties
|
||||
.getOrCreate('angleOffset')
|
||||
.setValue(behaviorContent.getChild('angleOffset').getStringValue())
|
||||
.setLabel(_('Angle offset'))
|
||||
.setGroup(_('Rotation'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getDegreeAngle());
|
||||
|
||||
behaviorProperties
|
||||
.getOrCreate('radius')
|
||||
.setValue(behaviorContent.getChild('radius').getStringValue())
|
||||
.setLabel(_('Radius'))
|
||||
.setDescription(
|
||||
_(
|
||||
'Use the circle inside the object width and height when left to 0.'
|
||||
)
|
||||
)
|
||||
.setGroup(_('Collision'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel());
|
||||
|
||||
behaviorProperties
|
||||
.getOrCreate('avoidanceSightRange')
|
||||
.setValue(
|
||||
behaviorContent.getChild('avoidanceSightRange').getStringValue()
|
||||
)
|
||||
.setLabel(_('Avoidance sight range'))
|
||||
.setGroup(_('Collision'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel());
|
||||
|
||||
return behaviorProperties;
|
||||
};
|
||||
|
||||
behavior.initializeContent = function (behaviorContent) {
|
||||
behaviorContent.addChild('acceleration').setDoubleValue(400);
|
||||
behaviorContent.addChild('maxSpeed').setDoubleValue(200);
|
||||
behaviorContent.addChild('angularMaxSpeed').setDoubleValue(180);
|
||||
behaviorContent.addChild('rotateObject').setBoolValue(true);
|
||||
behaviorContent.addChild('angleOffset').setDoubleValue(0);
|
||||
behaviorContent.addChild('radius').setDoubleValue(0);
|
||||
behaviorContent.addChild('avoidanceSightRange').setDoubleValue(120);
|
||||
};
|
||||
|
||||
const sharedData = new gd.BehaviorSharedDataJsImplementation();
|
||||
sharedData.updateProperty = function (
|
||||
sharedContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
if (propertyName === 'cellSize') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
sharedContent
|
||||
.getOrCreateChild('cellSize')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
if (propertyName === 'cellDepth') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
sharedContent
|
||||
.getOrCreateChild('cellDepth')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
if (propertyName === 'slopeMaxAngle') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
sharedContent
|
||||
.getOrCreateChild('slopeMaxAngle')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
if (propertyName === 'stairHeightMax') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
sharedContent
|
||||
.getOrCreateChild('stairHeightMax')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
if (propertyName === 'walkableRadius') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
sharedContent
|
||||
.getOrCreateChild('walkableRadius')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
if (propertyName === 'walkableDepth') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
sharedContent
|
||||
.getOrCreateChild('walkableDepth')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
if (propertyName === 'speedScaleY') {
|
||||
const newValueAsNumber = parseFloat(newValue);
|
||||
if (newValueAsNumber !== newValueAsNumber) return false;
|
||||
sharedContent
|
||||
.getOrCreateChild('speedScaleY')
|
||||
.setDoubleValue(newValueAsNumber);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
sharedData.getProperties = function (sharedContent) {
|
||||
const sharedProperties = new gd.MapStringPropertyDescriptor();
|
||||
|
||||
sharedProperties
|
||||
.getOrCreate('cellSize')
|
||||
.setLabel(_('Cell size'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
|
||||
.setValue(
|
||||
sharedContent.getChild('cellSize').getDoubleValue().toString(10)
|
||||
)
|
||||
.setAdvanced(true)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
|
||||
sharedProperties
|
||||
.getOrCreate('cellDepth')
|
||||
.setLabel(_('Cell depth'))
|
||||
.setGroup(_('3D only'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
|
||||
.setValue(
|
||||
sharedContent.getChild('cellDepth').getDoubleValue().toString(10)
|
||||
)
|
||||
.setAdvanced(true)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
|
||||
sharedProperties
|
||||
.getOrCreate('slopeMaxAngle')
|
||||
.setLabel(_('Slope max. angle'))
|
||||
.setGroup(_('3D only'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getDegreeAngle())
|
||||
.setValue(
|
||||
sharedContent
|
||||
.getChild('slopeMaxAngle')
|
||||
.getDoubleValue()
|
||||
.toString(10)
|
||||
)
|
||||
.setAdvanced(true)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
|
||||
sharedProperties
|
||||
.getOrCreate('stairHeightMax')
|
||||
.setLabel(_('Max. stair height'))
|
||||
.setGroup(_('3D only'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
|
||||
.setValue(
|
||||
sharedContent
|
||||
.getChild('stairHeightMax')
|
||||
.getDoubleValue()
|
||||
.toString(10)
|
||||
)
|
||||
.setAdvanced(true)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
|
||||
sharedProperties
|
||||
.getOrCreate('walkableRadius')
|
||||
.setLabel(_('Walkable radius'))
|
||||
.setDescription(
|
||||
_(
|
||||
'The biggest character radius is automatically used when left negative.'
|
||||
)
|
||||
)
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
|
||||
.setValue(
|
||||
sharedContent
|
||||
.getChild('walkableRadius')
|
||||
.getDoubleValue()
|
||||
.toString(10)
|
||||
)
|
||||
.setAdvanced(true)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
|
||||
sharedProperties
|
||||
.getOrCreate('walkableDepth')
|
||||
.setLabel(_('Walkable depth'))
|
||||
.setDescription(
|
||||
_(
|
||||
'Minimum floor to ceiling height that will still allow the floor area to be considered walkable.'
|
||||
)
|
||||
)
|
||||
.setGroup(_('3D only'))
|
||||
.setType('Number')
|
||||
.setMeasurementUnit(gd.MeasurementUnit.getPixel())
|
||||
.setValue(
|
||||
sharedContent
|
||||
.getChild('walkableDepth')
|
||||
.getDoubleValue()
|
||||
.toString(10)
|
||||
)
|
||||
.setAdvanced(true)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
|
||||
sharedProperties
|
||||
.getOrCreate('speedScaleY')
|
||||
.setLabel(_('Y speed scale'))
|
||||
.setDescription(
|
||||
_(
|
||||
'Allow a depth effect for 2D games. Usually set to 0.5 for isometry.'
|
||||
)
|
||||
)
|
||||
.setGroup(_('2D only'))
|
||||
.setType('Number')
|
||||
.setValue(
|
||||
sharedContent.getChild('speedScaleY').getDoubleValue().toString(10)
|
||||
)
|
||||
.setAdvanced(true)
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
|
||||
|
||||
return sharedProperties;
|
||||
};
|
||||
sharedData.initializeContent = function (sharedContent) {
|
||||
sharedContent.addChild('cellSize').setDoubleValue(10);
|
||||
sharedContent.addChild('cellDepth').setDoubleValue(10);
|
||||
sharedContent.addChild('slopeMaxAngle').setDoubleValue(50);
|
||||
sharedContent.addChild('stairHeightMax').setDoubleValue(20);
|
||||
sharedContent.addChild('walkableRadius').setDoubleValue(-1);
|
||||
sharedContent.addChild('walkableDepth').setDoubleValue(150);
|
||||
sharedContent.addChild('speedScaleY').setDoubleValue(1);
|
||||
};
|
||||
|
||||
const aut = extension
|
||||
.addBehavior(
|
||||
'NavMeshCharacterBehavior',
|
||||
_('Pathfinding character (nav-mesh based)'),
|
||||
'NavMeshCharacter',
|
||||
_(
|
||||
'Move objects to a target while avoiding all objects that are flagged as obstacles.'
|
||||
),
|
||||
'',
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg',
|
||||
'NavMeshCharacterBehavior',
|
||||
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
|
||||
behavior,
|
||||
sharedData
|
||||
)
|
||||
.markAsIrrelevantForChildObjects()
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/NavMeshCharacterRuntimeBehavior.js'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/NavMeshObstacleRuntimeBehavior.js'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/recast-navigation.wasm.js'
|
||||
)
|
||||
.addRequiredFile(
|
||||
'Extensions/NavMeshPathfinding/recast-navigation.wasm.wasm'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/recast-navigation-generators.js'
|
||||
);
|
||||
|
||||
aut
|
||||
.addAction(
|
||||
'MoveTo',
|
||||
_('Move to a position'),
|
||||
_('Move the object to a position'),
|
||||
_('Move _PARAM0_ to _PARAM2_ ; _PARAM3_ ; _PARAM4_'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg',
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.addParameter('expression', _('Destination X position'))
|
||||
.addParameter('expression', _('Destination Y position'))
|
||||
.addParameter('expression', _('Destination Z position'))
|
||||
.setFunctionName('moveTo');
|
||||
|
||||
aut
|
||||
.addCondition(
|
||||
'PathFound',
|
||||
_('Path found'),
|
||||
_('Check if a path has been found.'),
|
||||
_('A path has been found for _PARAM0_'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg',
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('pathFound');
|
||||
|
||||
aut
|
||||
.addCondition(
|
||||
'DestinationReached',
|
||||
_('Destination reached'),
|
||||
_('Check if the destination was reached.'),
|
||||
_('_PARAM0_ reached its destination'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg',
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('destinationReached');
|
||||
|
||||
aut
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'Acceleration',
|
||||
_('Acceleration'),
|
||||
_('the acceleration when moving the object'),
|
||||
_('the acceleration on the path'),
|
||||
_('Pathfinding configuration'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setFunctionName('setAcceleration')
|
||||
.setGetter('getAcceleration');
|
||||
|
||||
aut
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'MaxSpeed',
|
||||
_('Maximum speed'),
|
||||
_('the maximum speed when moving the object'),
|
||||
_('the max. speed on the path'),
|
||||
_('Pathfinding configuration'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Max speed (in pixels per second)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setMaxSpeed')
|
||||
.setGetter('getMaxSpeed');
|
||||
|
||||
aut
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'Speed',
|
||||
_('Speed'),
|
||||
_('Change the speed of the object on the path'),
|
||||
_('the speed on the path'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Speed (in pixels per second)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setSpeed')
|
||||
.setGetter('getSpeed');
|
||||
|
||||
aut
|
||||
.addScopedCondition(
|
||||
'MovementAngleIsAround',
|
||||
_('Angle of movement on its path'),
|
||||
_('Compare the angle of movement of an object on its path.'),
|
||||
_('Angle of movement of _PARAM0_ is _PARAM2_ ± _PARAM3_°'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg',
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.addParameter('expression', _('Angle, in degrees'))
|
||||
.addParameter('expression', _('Tolerance, in degrees'));
|
||||
|
||||
aut
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'AngularMaxSpeed',
|
||||
_('Angular maximum speed'),
|
||||
_('the maximum angular speed when moving the object'),
|
||||
_('the max. angular speed on the path'),
|
||||
_('Pathfinding configuration'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Max angular speed (in degrees per second)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setAngularMaxSpeed')
|
||||
.setGetter('getAngularMaxSpeed');
|
||||
|
||||
aut
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'AngleOffset',
|
||||
_('Rotation offset'),
|
||||
_('the rotation offset applied when moving the object'),
|
||||
_('the rotation offset on the path'),
|
||||
_('Pathfinding configuration'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.useStandardParameters(
|
||||
'number',
|
||||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||||
_('Angle (in degrees)')
|
||||
)
|
||||
)
|
||||
.setFunctionName('setAngleOffset')
|
||||
.setGetter('getAngleOffset');
|
||||
|
||||
aut
|
||||
.addScopedAction(
|
||||
'RotateObject',
|
||||
_('Rotate the object'),
|
||||
_('Enable or disable rotation of the object on the path'),
|
||||
_('Enable rotation of _PARAM0_ on the path: _PARAM2_'),
|
||||
_('Pathfinding configuration'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg',
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.addParameter('yesorno', _('Rotate object?'))
|
||||
.setFunctionName('setRotateObject');
|
||||
|
||||
aut
|
||||
.addScopedCondition(
|
||||
'ObjectRotated',
|
||||
_('Object rotated'),
|
||||
_('Check if the object is rotated when traveling on its path.'),
|
||||
_('_PARAM0_ is rotated when traveling on its path'),
|
||||
_('Pathfinding configuration'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg',
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('isObjectRotated');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'GetNodeX',
|
||||
_('Get a waypoint X position'),
|
||||
_('Get a waypoint X position'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.addParameter('expression', _('Node index (start at 0!)'))
|
||||
.setFunctionName('getNodeX');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'GetNodeY',
|
||||
_('Get a waypoint Y position'),
|
||||
_('Get a waypoint Y position'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.addParameter('expression', _('Node index (start at 0!)'))
|
||||
.setFunctionName('getNodeY');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'GetNodeZ',
|
||||
_('Get a waypoint Z position'),
|
||||
_('Get a waypoint Z position'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.addParameter('expression', _('Node index (start at 0!)'))
|
||||
.setFunctionName('getNodeZ');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'NextNodeIndex',
|
||||
_('Index of the next waypoint'),
|
||||
_('Get the index of the next waypoint to reach'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getNextNodeIndex');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'NodeCount',
|
||||
_('Waypoint count'),
|
||||
_('Get the number of waypoints on the path'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getNodeCount');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'NextNodeX',
|
||||
_('Get next waypoint X position'),
|
||||
_('Get next waypoint X position'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getNextNodeX');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'NextNodeY',
|
||||
_('Get next waypoint Y position'),
|
||||
_('Get next waypoint Y position'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getNextNodeY');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'NextNodeZ',
|
||||
_('Get next waypoint Z position'),
|
||||
_('Get next waypoint Z position'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getNextNodeZ');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'PreviousNodeX',
|
||||
_('Previous waypoint X position'),
|
||||
_('Previous waypoint X position'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getPreviousNodeX');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'PreviousNodeY',
|
||||
_('Previous waypoint Y position'),
|
||||
_('Previous waypoint Y position'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getPreviousNodeY');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'PreviousNodeZ',
|
||||
_('Previous waypoint Z position'),
|
||||
_('Previous waypoint Z position'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getPreviousNodeZ');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'DestinationX',
|
||||
_('Destination X position'),
|
||||
_('Destination X position of the path'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getDestinationX');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'DestinationY',
|
||||
_('Destination Y position'),
|
||||
_('Destination Y position of the path'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getDestinationY');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'DestinationZ',
|
||||
_('Destination Z position'),
|
||||
_('Destination Z position of the path'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getDestinationZ');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'MovementAngle',
|
||||
_('Angle of movement on its path'),
|
||||
_('Angle of movement on its path'),
|
||||
_('Movement on the path'),
|
||||
'JsPlatform/Extensions/nav-mesh-character.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'))
|
||||
.addParameter('behavior', _('Behavior'), 'NavMeshCharacterBehavior')
|
||||
.setFunctionName('getMovementAngle');
|
||||
}
|
||||
{
|
||||
const behavior = new gd.BehaviorJsImplementation();
|
||||
behavior.updateProperty = function (
|
||||
behaviorContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
if (propertyName === 'shape') {
|
||||
const normalizedValue = newValue.toLowerCase();
|
||||
let shapeValue = '';
|
||||
if (normalizedValue === 'box') shapeValue = 'Box';
|
||||
else if (normalizedValue === 'mesh') shapeValue = 'Mesh';
|
||||
else return false;
|
||||
|
||||
behaviorContent.getOrCreateChild('shape').setStringValue(shapeValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyName === 'meshShapeResourceName') {
|
||||
behaviorContent
|
||||
.getOrCreateChild('meshShapeResourceName')
|
||||
.setStringValue(newValue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
behavior.getProperties = function (behaviorContent) {
|
||||
const behaviorProperties = new gd.MapStringPropertyDescriptor();
|
||||
|
||||
behaviorProperties
|
||||
.getOrCreate('shape')
|
||||
.setValue(behaviorContent.getChild('shape').getStringValue())
|
||||
.setType('Choice')
|
||||
.setLabel('Shape')
|
||||
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden)
|
||||
.addChoice('Box', _('Box'))
|
||||
.addChoice('Mesh', _('Mesh'));
|
||||
behaviorProperties
|
||||
.getOrCreate('meshShapeResourceName')
|
||||
.setValue(
|
||||
behaviorContent.getChild('meshShapeResourceName').getStringValue()
|
||||
)
|
||||
.setType('resource')
|
||||
.addExtraInfo('model3D')
|
||||
.setLabel(_("Simplified 3D model (leave empty to use object's one)"));
|
||||
|
||||
return behaviorProperties;
|
||||
};
|
||||
|
||||
behavior.initializeContent = function (behaviorContent) {
|
||||
behaviorContent.addChild('shape').setStringValue('Box');
|
||||
behaviorContent.addChild('meshShapeResourceName').setStringValue('');
|
||||
};
|
||||
|
||||
const sharedData = new gd.BehaviorSharedDataJsImplementation();
|
||||
sharedData.updateProperty = function (
|
||||
sharedContent,
|
||||
propertyName,
|
||||
newValue
|
||||
) {
|
||||
return false;
|
||||
};
|
||||
sharedData.getProperties = function (sharedContent) {
|
||||
const sharedProperties = new gd.MapStringPropertyDescriptor();
|
||||
return sharedProperties;
|
||||
};
|
||||
sharedData.initializeContent = function (sharedContent) {};
|
||||
|
||||
extension
|
||||
.addBehavior(
|
||||
'NavMeshObstacleBehavior',
|
||||
_('Obstacle for pathfinding (nav-mesh based)'),
|
||||
'NavMeshObstacle',
|
||||
_('Flag objects as being obstacles for nav-mesh pathfinding.'),
|
||||
'',
|
||||
'JsPlatform/Extensions/nav-mesh-obstacle.svg',
|
||||
'NavMeshObstacle',
|
||||
//@ts-ignore The class hierarchy is incorrect leading to a type error, but this is valid.
|
||||
behavior,
|
||||
sharedData
|
||||
)
|
||||
.markAsIrrelevantForChildObjects()
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/NavMeshObstacleRuntimeBehavior.js'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/recast-navigation.wasm.js'
|
||||
)
|
||||
.addRequiredFile(
|
||||
'Extensions/NavMeshPathfinding/recast-navigation.wasm.wasm'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/recast-navigation-generators.js'
|
||||
);
|
||||
}
|
||||
|
||||
extension
|
||||
.addAction(
|
||||
'EnableDebugDraw',
|
||||
_('Draw pathfinding walkable area'),
|
||||
_('This activates the display of the walkable area (in blue).'),
|
||||
_('Enable debugging view of pathfinding: _PARAM1_'),
|
||||
'',
|
||||
'res/actions/planicon24.png',
|
||||
'res/actions/planicon.png'
|
||||
)
|
||||
.addCodeOnlyParameter('currentScene', '')
|
||||
.addParameter('yesorno', _('Enable debug draw'), '', true)
|
||||
.getCodeExtraInformation()
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/NavMeshObstacleRuntimeBehavior.js'
|
||||
)
|
||||
.addIncludeFile('Extensions/NavMeshPathfinding/recast-navigation.wasm.js')
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/recast-navigation-generators.js'
|
||||
)
|
||||
.addIncludeFile(
|
||||
'Extensions/NavMeshPathfinding/NavMeshDebugPixiRenderer.js'
|
||||
)
|
||||
.setFunctionName('gdjs.NavMeshObstaclesManager.enableDebugDraw');
|
||||
|
||||
return extension;
|
||||
},
|
||||
|
||||
runExtensionSanityTests: function (gd, extension) {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,631 @@
|
||||
namespace gdjs {
|
||||
const loadRecast = async () => {
|
||||
try {
|
||||
const module = await import('./recast-navigation.wasm.js');
|
||||
const initializeRecast = module.default;
|
||||
if (!initializeRecast) {
|
||||
throw new Error('No default export found in Recast.');
|
||||
}
|
||||
|
||||
const Recast = await initializeRecast();
|
||||
|
||||
await RecastNav.init();
|
||||
//@ts-ignore
|
||||
window.Recast = Recast;
|
||||
} catch (err) {
|
||||
console.error('Unable to load Recast navigation mesh library.', err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
gdjs.registerAsynchronouslyLoadingLibraryPromise(loadRecast());
|
||||
|
||||
interface NavMeshCharacterNetworkSyncDataType {
|
||||
// Syncing the path and its position on it should be enough to have a good prediction.
|
||||
d: RecastNav.Vector3;
|
||||
pf: boolean;
|
||||
as: number;
|
||||
re: boolean;
|
||||
ma: number;
|
||||
}
|
||||
|
||||
/** @category Behaviors > NavMesh pathfinding */
|
||||
export interface NavMeshCharacterNetworkSyncData
|
||||
extends BehaviorNetworkSyncData {
|
||||
props: NavMeshCharacterNetworkSyncDataType;
|
||||
}
|
||||
|
||||
/**
|
||||
* NavMeshCharacterRuntimeBehavior represents a behavior allowing objects to
|
||||
* follow a path computed to avoid obstacles.
|
||||
* @category Behaviors > NavMesh pathfinding
|
||||
*/
|
||||
export class NavMeshCharacterRuntimeBehavior extends gdjs.RuntimeBehavior {
|
||||
_path: Array<RecastNav.Vector3> = [];
|
||||
|
||||
// Behavior configuration:
|
||||
_angularMaxSpeed: float;
|
||||
_rotateObject: boolean;
|
||||
_angleOffset: float;
|
||||
_radius: float;
|
||||
_agent: RecastNav.CrowdAgent | null = null;
|
||||
_crowdAgentParams: Partial<RecastNav.CrowdAgentParams> = {
|
||||
radius: 40,
|
||||
height: 100,
|
||||
maxAcceleration: 1000,
|
||||
maxSpeed: 300,
|
||||
collisionQueryRange: 120,
|
||||
separationWeight: 1.0,
|
||||
};
|
||||
|
||||
_oldX: float = 0;
|
||||
_oldY: float = 0;
|
||||
_oldZ: float = 0;
|
||||
|
||||
// Attributes used for traveling on the path:
|
||||
_pathFound: boolean = false;
|
||||
_reachedEnd: boolean = false;
|
||||
_movementAngle: float = 0;
|
||||
|
||||
_manager: NavMeshObstaclesManager;
|
||||
|
||||
constructor(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
behaviorData,
|
||||
owner: gdjs.RuntimeObject
|
||||
) {
|
||||
super(instanceContainer, behaviorData, owner);
|
||||
|
||||
this._angularMaxSpeed = behaviorData.angularMaxSpeed;
|
||||
this._rotateObject = behaviorData.rotateObject;
|
||||
this._angleOffset = behaviorData.angleOffset;
|
||||
this._radius = behaviorData.radius;
|
||||
this._crowdAgentParams.maxAcceleration = behaviorData.acceleration;
|
||||
this._crowdAgentParams.maxSpeed = behaviorData.maxSpeed;
|
||||
this._crowdAgentParams.collisionQueryRange =
|
||||
behaviorData.avoidanceSightRange;
|
||||
|
||||
this._manager =
|
||||
gdjs.NavMeshObstaclesManager.getManager(instanceContainer);
|
||||
this._manager.addCharacter(this);
|
||||
}
|
||||
|
||||
override applyBehaviorOverriding(behaviorData): boolean {
|
||||
if (behaviorData.acceleration !== undefined) {
|
||||
this.setAcceleration(behaviorData.acceleration);
|
||||
}
|
||||
if (behaviorData.maxSpeed !== undefined) {
|
||||
this.setMaxSpeed(behaviorData.maxSpeed);
|
||||
}
|
||||
if (behaviorData.angularMaxSpeed !== undefined) {
|
||||
this.setAngularMaxSpeed(behaviorData.angularMaxSpeed);
|
||||
}
|
||||
if (behaviorData.rotateObject !== undefined) {
|
||||
this.setRotateObject(behaviorData.rotateObject);
|
||||
}
|
||||
if (behaviorData.angleOffset !== undefined) {
|
||||
this.setAngleOffset(behaviorData.angleOffset);
|
||||
}
|
||||
if (behaviorData.radius !== undefined) {
|
||||
this._radius = behaviorData.radius;
|
||||
this._manager.invalidateNavMesh();
|
||||
}
|
||||
if (behaviorData.avoidanceSightRange !== undefined) {
|
||||
this._crowdAgentParams.collisionQueryRange =
|
||||
behaviorData.avoidanceSightRange;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
override getNetworkSyncData(
|
||||
options: GetNetworkSyncDataOptions
|
||||
): NavMeshCharacterNetworkSyncData {
|
||||
return {
|
||||
...super.getNetworkSyncData(options),
|
||||
props: {
|
||||
d: this._path[this._path.length - 1] || null,
|
||||
pf: this._pathFound,
|
||||
as: this._angularMaxSpeed,
|
||||
re: this._reachedEnd,
|
||||
ma: this._movementAngle,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
override updateFromNetworkSyncData(
|
||||
networkSyncData: NavMeshCharacterNetworkSyncData,
|
||||
options: UpdateFromNetworkSyncDataOptions
|
||||
): void {
|
||||
super.updateFromNetworkSyncData(networkSyncData, options);
|
||||
const behaviorSpecificProps = networkSyncData.props;
|
||||
if (behaviorSpecificProps.d !== undefined) {
|
||||
// TODO Try a more reliable synchronization by overriding the path using the low level API.
|
||||
const destination = this._path[this._path.length - 1];
|
||||
if (behaviorSpecificProps.d === null) {
|
||||
if (destination) {
|
||||
this.stop();
|
||||
}
|
||||
} else if (
|
||||
!destination ||
|
||||
destination.x !== behaviorSpecificProps.d.x ||
|
||||
destination.y !== behaviorSpecificProps.d.y ||
|
||||
destination.z !== behaviorSpecificProps.d.z
|
||||
) {
|
||||
this.moveTo(
|
||||
behaviorSpecificProps.d.x,
|
||||
behaviorSpecificProps.d.y,
|
||||
behaviorSpecificProps.d.z
|
||||
);
|
||||
}
|
||||
}
|
||||
if (behaviorSpecificProps.pf !== undefined) {
|
||||
this._pathFound = behaviorSpecificProps.pf;
|
||||
}
|
||||
if (behaviorSpecificProps.as !== undefined) {
|
||||
this._angularMaxSpeed = behaviorSpecificProps.as;
|
||||
}
|
||||
if (behaviorSpecificProps.re !== undefined) {
|
||||
this._reachedEnd = behaviorSpecificProps.re;
|
||||
}
|
||||
if (behaviorSpecificProps.ma !== undefined) {
|
||||
this._movementAngle = behaviorSpecificProps.ma;
|
||||
}
|
||||
}
|
||||
|
||||
override onActivate() {
|
||||
this._manager.addCharacter(this);
|
||||
}
|
||||
|
||||
override onDeActivate() {
|
||||
this._manager.removeCharacter(this);
|
||||
}
|
||||
|
||||
override onDestroy() {
|
||||
this._manager.removeCharacter(this);
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (!this._agent) {
|
||||
return;
|
||||
}
|
||||
this._path = [];
|
||||
this._pathFound = false;
|
||||
this._reachedEnd = false;
|
||||
this._agent.resetMoveTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and move on the path to the specified destination.
|
||||
*/
|
||||
moveTo(x: float, y: float, z: float): void {
|
||||
this._path = [];
|
||||
this._manager.rebuildNavMeshIfNeeded();
|
||||
if (!this._manager.navMesh || !this._agent) {
|
||||
this._pathFound = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const navMeshQuery = new RecastNav.NavMeshQuery(this._manager.navMesh);
|
||||
const { success: hasFoundOrigin, point: origin } =
|
||||
navMeshQuery.findClosestPoint(
|
||||
{
|
||||
x: this.owner.getX(),
|
||||
y: gdjs.Base3DHandler.is3D(this.owner) ? this.owner.getZ() : 0,
|
||||
z: this._manager.is3D
|
||||
? this.owner.getY()
|
||||
: this.owner.getY() * this._manager.inverseSpeedScaleY,
|
||||
},
|
||||
{
|
||||
halfExtents: {
|
||||
x: 100,
|
||||
y: this._manager.is3D
|
||||
? 100
|
||||
: gdjs.NavMeshObstaclesManager.cellHeightFor2D,
|
||||
z: 100,
|
||||
},
|
||||
}
|
||||
);
|
||||
if (!hasFoundOrigin) {
|
||||
navMeshQuery.destroy();
|
||||
this._pathFound = false;
|
||||
return;
|
||||
}
|
||||
const agentInitialPosition = this._agent.position();
|
||||
// Teleporting the agent resets its velocity and move target,
|
||||
// so it's only done (and rolled back) when actually needed.
|
||||
let hasTeleported = false;
|
||||
if (
|
||||
agentInitialPosition.x !== origin.x ||
|
||||
agentInitialPosition.y !== origin.y ||
|
||||
agentInitialPosition.z !== origin.z
|
||||
) {
|
||||
this._agent.teleport(origin);
|
||||
hasTeleported = true;
|
||||
}
|
||||
|
||||
const { success: hasFoundDestination, point: destination } =
|
||||
navMeshQuery.findClosestPoint(
|
||||
{
|
||||
x,
|
||||
y: z,
|
||||
z: this._manager.is3D ? y : y * this._manager.inverseSpeedScaleY,
|
||||
},
|
||||
{
|
||||
halfExtents: {
|
||||
x: 100,
|
||||
y: this._manager.is3D
|
||||
? 100
|
||||
: gdjs.NavMeshObstaclesManager.cellHeightFor2D,
|
||||
z: 100,
|
||||
},
|
||||
}
|
||||
);
|
||||
navMeshQuery.destroy();
|
||||
if (!hasFoundDestination) {
|
||||
this._pathFound = false;
|
||||
if (hasTeleported) {
|
||||
this._agent.teleport(agentInitialPosition);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._pathFound = this._agent.requestMoveTarget(destination);
|
||||
if (this._pathFound) {
|
||||
this._reachedEnd = false;
|
||||
const newX = origin.x;
|
||||
const newY = this._manager.is3D
|
||||
? origin.z
|
||||
: origin.z * this._manager.speedScaleY;
|
||||
const newZ = origin.y;
|
||||
this.owner.setX(newX);
|
||||
this.owner.setY(newY);
|
||||
if (gdjs.Base3DHandler.is3D(this.owner)) {
|
||||
this.owner.setZ(newZ);
|
||||
}
|
||||
this._oldX = newX;
|
||||
this._oldY = newY;
|
||||
this._oldZ = newZ;
|
||||
} else if (hasTeleported) {
|
||||
this._agent.teleport(agentInitialPosition);
|
||||
}
|
||||
}
|
||||
|
||||
teleportAgentToObjectIfNeeded(): void {
|
||||
if (
|
||||
this._oldX === this.owner.getX() &&
|
||||
this._oldY === this.owner.getY() &&
|
||||
(!gdjs.Base3DHandler.is3D(this.owner) ||
|
||||
this._oldZ === this.owner.getZ())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!this._agent) {
|
||||
// Try to create the agent at the new object position.
|
||||
this._manager.buildCharacterAgent(this);
|
||||
return;
|
||||
}
|
||||
const agent = this._agent;
|
||||
const oldX = this.owner.getX();
|
||||
const oldY = this.owner.getY();
|
||||
// For 2D we keep the agent position for Z because the ground may not be
|
||||
// at 0 because of rasterization.
|
||||
const oldZ = gdjs.Base3DHandler.is3D(this.owner)
|
||||
? this.owner.getZ()
|
||||
: agent.raw.get_npos(1);
|
||||
|
||||
agent.teleport({
|
||||
x: oldX,
|
||||
y: oldZ,
|
||||
z: this._manager.is3D ? oldY : oldY * this._manager.inverseSpeedScaleY,
|
||||
});
|
||||
this._path = [];
|
||||
this._pathFound = false;
|
||||
}
|
||||
|
||||
override doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
const timeDelta = this.owner.getElapsedTime() / 1000;
|
||||
// The wrapper interpolation seems bugged, so we don't use it.
|
||||
this._manager.step(timeDelta);
|
||||
|
||||
const agent = this._agent;
|
||||
if (!agent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Path found: memorize it
|
||||
// We can't get the agent path before a first step, so we do it here.
|
||||
if (this._path.length === 0 && agent.raw.ncorners > 0) {
|
||||
const path = agent.corners();
|
||||
path.push(agent.target());
|
||||
for (const point of path) {
|
||||
const y = point.y;
|
||||
point.y = point.z;
|
||||
point.z = y;
|
||||
if (!this._manager.is3D) {
|
||||
point.y *= this._manager.speedScaleY;
|
||||
}
|
||||
}
|
||||
this._path = path;
|
||||
}
|
||||
|
||||
let newX = agent.raw.get_npos(0);
|
||||
let newY = this._manager.is3D
|
||||
? agent.raw.get_npos(2)
|
||||
: agent.raw.get_npos(2) * this._manager.speedScaleY;
|
||||
let newZ = agent.raw.get_npos(1);
|
||||
|
||||
const destinationX = this.getDestinationX();
|
||||
const destinationY = this.getDestinationY();
|
||||
const destinationZ = this.getDestinationZ();
|
||||
|
||||
const velocity = agent.desiredVelocity();
|
||||
const velocityX = velocity.x;
|
||||
const velocityY = velocity.z;
|
||||
if (
|
||||
Math.abs(velocityX) + Math.abs(velocityY) > 0 &&
|
||||
// Avoid to rotate strangely at the end of the path
|
||||
(Math.abs(destinationX - newX) > 3 || Math.abs(destinationY - newY) > 3)
|
||||
) {
|
||||
this._movementAngle = gdjs.evtTools.common.mod(
|
||||
gdjs.toDegrees(Math.atan2(velocityY, velocityX)),
|
||||
360
|
||||
);
|
||||
}
|
||||
if (
|
||||
this._rotateObject &&
|
||||
this.owner.getAngle() !== this._movementAngle + this._angleOffset
|
||||
) {
|
||||
this.owner.rotateTowardAngle(
|
||||
this._movementAngle + this._angleOffset,
|
||||
this._angularMaxSpeed
|
||||
);
|
||||
}
|
||||
if (
|
||||
Math.abs(newX - destinationX) < 1 &&
|
||||
Math.abs(newY - destinationY) < 1 &&
|
||||
(!gdjs.Base3DHandler.is3D(this.owner) ||
|
||||
Math.abs(newZ - destinationZ) < 1)
|
||||
) {
|
||||
this._reachedEnd = true;
|
||||
agent.resetMoveTarget();
|
||||
}
|
||||
this.owner.setX(newX);
|
||||
this.owner.setY(newY);
|
||||
if (gdjs.Base3DHandler.is3D(this.owner)) {
|
||||
this.owner.setZ(newZ);
|
||||
}
|
||||
this._oldX = newX;
|
||||
this._oldY = newY;
|
||||
this._oldZ = newZ;
|
||||
}
|
||||
|
||||
override doStepPostEvents(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
) {
|
||||
this._manager.hasStepped = false;
|
||||
}
|
||||
|
||||
getRadius(): float {
|
||||
return (
|
||||
this._radius ||
|
||||
Math.min(this.owner.getWidth(), this.owner.getHeight()) / 2
|
||||
);
|
||||
}
|
||||
|
||||
setAcceleration(acceleration: float): void {
|
||||
this._crowdAgentParams.maxAcceleration = acceleration;
|
||||
if (this._agent) {
|
||||
this._agent.setParameters(this._crowdAgentParams);
|
||||
}
|
||||
}
|
||||
|
||||
getAcceleration(): float {
|
||||
return this._crowdAgentParams.maxAcceleration || 0;
|
||||
}
|
||||
|
||||
setMaxSpeed(maxSpeed: float): void {
|
||||
this._crowdAgentParams.maxSpeed = maxSpeed;
|
||||
if (this._agent) {
|
||||
this._agent.setParameters(this._crowdAgentParams);
|
||||
}
|
||||
}
|
||||
|
||||
getMaxSpeed(): float {
|
||||
return this._crowdAgentParams.maxSpeed || 0;
|
||||
}
|
||||
|
||||
setSpeed(speed: float): void {
|
||||
if (this._agent) {
|
||||
const velocityX = this._agent.raw.get_vel(0);
|
||||
const velocityY = this._agent.raw.get_vel(1);
|
||||
const velocityZ = this._agent.raw.get_vel(2);
|
||||
const oldSpeed = Math.hypot(velocityX, velocityY, velocityZ);
|
||||
if (oldSpeed === 0) {
|
||||
this._agent.requestMoveVelocity({ x: speed, y: 0, z: 0 });
|
||||
} else {
|
||||
const ratio = speed / oldSpeed;
|
||||
|
||||
this._agent.raw.set_vel(0, velocityX * ratio);
|
||||
this._agent.raw.set_vel(1, velocityY * ratio);
|
||||
this._agent.raw.set_vel(2, velocityZ * ratio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getSpeed(): float {
|
||||
if (!this._agent) {
|
||||
return 0;
|
||||
}
|
||||
const velocity = this._agent.desiredVelocity();
|
||||
|
||||
return Math.hypot(velocity.x, velocity.y, velocity.z);
|
||||
}
|
||||
|
||||
getMovementAngle(): float {
|
||||
return this._movementAngle;
|
||||
}
|
||||
|
||||
movementAngleIsAround(degreeAngle: float, tolerance: float): boolean {
|
||||
return (
|
||||
Math.abs(
|
||||
gdjs.evtTools.common.angleDifference(this._movementAngle, degreeAngle)
|
||||
) <= tolerance
|
||||
);
|
||||
}
|
||||
|
||||
setAngularMaxSpeed(angularMaxSpeed: float): void {
|
||||
this._angularMaxSpeed = angularMaxSpeed;
|
||||
}
|
||||
|
||||
getAngularMaxSpeed(): float {
|
||||
return this._angularMaxSpeed;
|
||||
}
|
||||
|
||||
setAngleOffset(angleOffset: float): void {
|
||||
this._angleOffset = angleOffset;
|
||||
}
|
||||
|
||||
getAngleOffset(): float {
|
||||
return this._angleOffset;
|
||||
}
|
||||
|
||||
setRotateObject(allow: boolean): void {
|
||||
this._rotateObject = allow;
|
||||
}
|
||||
|
||||
isObjectRotated(): boolean {
|
||||
return this._rotateObject;
|
||||
}
|
||||
|
||||
getNodeX(index: integer): float {
|
||||
if (index < this._path.length) {
|
||||
return this._path[index].x;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
getNodeY(index: integer): float {
|
||||
if (index < this._path.length) {
|
||||
return this._path[index].y;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
getNodeZ(index: integer): float {
|
||||
if (index < this._path.length) {
|
||||
return this._path[index].z;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
getNextNodeIndex(): integer {
|
||||
if (!this._agent) {
|
||||
return 0;
|
||||
}
|
||||
const remainingNodes = this._agent.raw.ncorners;
|
||||
return remainingNodes === 0
|
||||
? this._path.length - 1
|
||||
: this._path.length - remainingNodes;
|
||||
}
|
||||
|
||||
getNodeCount(): integer {
|
||||
return this._path.length;
|
||||
}
|
||||
|
||||
getNextNodeX(): float {
|
||||
if (this._path.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
const nextNodeIndex = this.getNextNodeIndex();
|
||||
return this._path[nextNodeIndex].x;
|
||||
}
|
||||
|
||||
getNextNodeY(): float {
|
||||
if (this._path.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
const nextNodeIndex = this.getNextNodeIndex();
|
||||
return this._path[nextNodeIndex].y;
|
||||
}
|
||||
|
||||
getNextNodeZ(): float {
|
||||
if (this._path.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
const nextNodeIndex = this.getNextNodeIndex();
|
||||
return this._path[nextNodeIndex].z;
|
||||
}
|
||||
|
||||
getPreviousNodeIndex(): integer {
|
||||
if (!this._agent) {
|
||||
return 0;
|
||||
}
|
||||
const remainingNodes = this._agent.raw.ncorners;
|
||||
return remainingNodes === this._path.length
|
||||
? 0
|
||||
: this._path.length - remainingNodes - 1;
|
||||
}
|
||||
|
||||
getPreviousNodeX(): float {
|
||||
if (this._path.length < 2) {
|
||||
return 0;
|
||||
}
|
||||
const previousNodeIndex = this.getPreviousNodeIndex();
|
||||
return this._path[previousNodeIndex].x;
|
||||
}
|
||||
|
||||
getPreviousNodeY(): float {
|
||||
if (this._path.length < 2) {
|
||||
return 0;
|
||||
}
|
||||
const previousNodeIndex = this.getPreviousNodeIndex();
|
||||
return this._path[previousNodeIndex].y;
|
||||
}
|
||||
|
||||
getPreviousNodeZ(): float {
|
||||
if (this._path.length < 2) {
|
||||
return 0;
|
||||
}
|
||||
const previousNodeIndex = this.getPreviousNodeIndex();
|
||||
return this._path[previousNodeIndex].z;
|
||||
}
|
||||
|
||||
getDestinationX(): float {
|
||||
if (this._path.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return this._path[this._path.length - 1].x;
|
||||
}
|
||||
|
||||
getDestinationY(): float {
|
||||
if (this._path.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return this._path[this._path.length - 1].y;
|
||||
}
|
||||
|
||||
getDestinationZ(): float {
|
||||
if (this._path.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
return this._path[this._path.length - 1].z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the latest call to moveTo succeeded.
|
||||
*/
|
||||
pathFound(): boolean {
|
||||
return this._pathFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the object reached its destination.
|
||||
*/
|
||||
destinationReached(): boolean {
|
||||
return this._reachedEnd;
|
||||
}
|
||||
}
|
||||
gdjs.registerBehavior(
|
||||
'NavMeshPathfinding::NavMeshCharacterBehavior',
|
||||
gdjs.NavMeshCharacterRuntimeBehavior
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
GDevelop - Pathfinding Behavior Extension
|
||||
Copyright (c) 2013-2016 Florian Rival ([email protected])
|
||||
*/
|
||||
|
||||
namespace gdjs {
|
||||
class NavMeshDebuggerPixiRenderer {
|
||||
obstaclesManager: NavMeshObstaclesManager;
|
||||
debugDrawerUtils: RecastNav.DebugDrawerUtils | null = null;
|
||||
enabled = false;
|
||||
meshes: Array<THREE.Mesh> = [];
|
||||
material: THREE.MeshStandardMaterial | null = null;
|
||||
isRegisteredFor2D = false;
|
||||
|
||||
constructor(obstaclesManager: NavMeshObstaclesManager) {
|
||||
this.obstaclesManager = obstaclesManager;
|
||||
}
|
||||
|
||||
setEnabled(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
enabled: boolean
|
||||
): void {
|
||||
this.enabled = enabled;
|
||||
if (enabled) {
|
||||
this.renderFor3D();
|
||||
if (!this.isRegisteredFor2D) {
|
||||
this.isRegisteredFor2D = true;
|
||||
this.registerFor2D(instanceContainer);
|
||||
}
|
||||
} else {
|
||||
this.removeFor3D();
|
||||
}
|
||||
}
|
||||
|
||||
private registerFor2D(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
instanceContainer
|
||||
.getScene()
|
||||
.registerDebugDrawHook((rendererObject: PIXI.Graphics) => {
|
||||
if (!this.enabled || this.obstaclesManager.is3D) {
|
||||
return;
|
||||
}
|
||||
const { navMesh, obstacles } = this.obstaclesManager;
|
||||
if (!navMesh) {
|
||||
return;
|
||||
}
|
||||
const firstObstacle: gdjs.NavMeshObstacleRuntimeBehavior = obstacles
|
||||
.values()
|
||||
.next().value;
|
||||
if (!firstObstacle) {
|
||||
return;
|
||||
}
|
||||
const scene = firstObstacle.owner.getRuntimeScene();
|
||||
const layer = scene.getLayer(firstObstacle.owner.getLayer());
|
||||
const workingPoint: FloatPoint = [0, 0];
|
||||
|
||||
rendererObject.beginFill();
|
||||
rendererObject.fill.alpha = 0.5;
|
||||
rendererObject.fill.color = 0x778ee8;
|
||||
|
||||
if (!this.debugDrawerUtils) {
|
||||
this.debugDrawerUtils = new RecastNav.DebugDrawerUtils();
|
||||
}
|
||||
const primitives = this.debugDrawerUtils.drawNavMesh(navMesh);
|
||||
const speedScaleY = this.obstaclesManager.speedScaleY;
|
||||
for (const primitive of primitives) {
|
||||
switch (primitive.type) {
|
||||
case 'tris':
|
||||
for (let i = 0; i + 2 < primitive.vertices.length; i += 3) {
|
||||
const [x0, y0, z0] = primitive.vertices[i];
|
||||
if (y0 < 1) {
|
||||
const [x1, _y1, z1] = primitive.vertices[i + 1];
|
||||
const [x2, _y2, z2] = primitive.vertices[i + 2];
|
||||
|
||||
const positions: Array<float> = [];
|
||||
positions.push.apply(
|
||||
positions,
|
||||
layer.applyLayerTransformation(
|
||||
x0,
|
||||
z0 * speedScaleY,
|
||||
0,
|
||||
workingPoint
|
||||
)
|
||||
);
|
||||
positions.push.apply(
|
||||
positions,
|
||||
layer.applyLayerTransformation(
|
||||
x1,
|
||||
z1 * speedScaleY,
|
||||
0,
|
||||
workingPoint
|
||||
)
|
||||
);
|
||||
positions.push.apply(
|
||||
positions,
|
||||
layer.applyLayerTransformation(
|
||||
x2,
|
||||
z2 * speedScaleY,
|
||||
0,
|
||||
workingPoint
|
||||
)
|
||||
);
|
||||
rendererObject.drawPolygon(positions);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
rendererObject.endFill();
|
||||
});
|
||||
}
|
||||
|
||||
removeFor3D() {
|
||||
for (const mesh of this.meshes) {
|
||||
mesh.removeFromParent();
|
||||
}
|
||||
this.meshes.length = 0;
|
||||
}
|
||||
|
||||
renderFor3D() {
|
||||
this.removeFor3D();
|
||||
if (!this.enabled || !this.obstaclesManager.is3D) {
|
||||
return;
|
||||
}
|
||||
const { navMesh, obstacles } = this.obstaclesManager;
|
||||
if (!navMesh) {
|
||||
return;
|
||||
}
|
||||
const firstObstacle: gdjs.NavMeshObstacleRuntimeBehavior = obstacles
|
||||
.values()
|
||||
.next().value;
|
||||
const scene = firstObstacle.owner.getRuntimeScene();
|
||||
const layer = scene.getLayer(firstObstacle.owner.getLayer());
|
||||
const rendererObject = layer.get3DRendererObject();
|
||||
if (!rendererObject) {
|
||||
return;
|
||||
}
|
||||
if (!this.debugDrawerUtils) {
|
||||
this.debugDrawerUtils = new RecastNav.DebugDrawerUtils();
|
||||
}
|
||||
const primitives = this.debugDrawerUtils.drawNavMesh(navMesh);
|
||||
for (const primitive of primitives) {
|
||||
switch (primitive.type) {
|
||||
case 'tris':
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
const positions = new Float32Array(primitive.vertices.length * 3);
|
||||
|
||||
for (let i = 0; i < primitive.vertices.length; i++) {
|
||||
const [x, y, z] = primitive.vertices[i];
|
||||
|
||||
// Invert triangles side
|
||||
const triangleVertexIndex = i % 3;
|
||||
const vertexIndex =
|
||||
triangleVertexIndex === 0
|
||||
? i + 1
|
||||
: triangleVertexIndex === 1
|
||||
? i - 1
|
||||
: i;
|
||||
|
||||
positions[vertexIndex * 3 + 0] = x;
|
||||
positions[vertexIndex * 3 + 1] = z;
|
||||
positions[vertexIndex * 3 + 2] = y;
|
||||
}
|
||||
|
||||
geometry.setAttribute(
|
||||
'position',
|
||||
new THREE.BufferAttribute(positions, 3)
|
||||
);
|
||||
|
||||
if (!this.material) {
|
||||
this.material = new THREE.MeshStandardMaterial({
|
||||
color: 0x778ee8,
|
||||
});
|
||||
}
|
||||
const mesh = new THREE.Mesh(geometry, this.material);
|
||||
this.meshes.push(mesh);
|
||||
rendererObject.add(mesh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register the class to let the engine use it.
|
||||
/**
|
||||
* @category Debugging > Debugger Renderer
|
||||
*/
|
||||
export type NavMeshDebuggerRenderer = NavMeshDebuggerPixiRenderer;
|
||||
/**
|
||||
* @category Debugging > Debugger Renderer
|
||||
*/
|
||||
export const NavMeshDebuggerRenderer = NavMeshDebuggerPixiRenderer;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
# NavMesh pathfinding extension
|
||||
|
||||
To update the library:
|
||||
- Clone this fork https://github.com/D8H/recast-navigation-js
|
||||
- Check out `main`
|
||||
- Get the wasm build with npm
|
||||
- Copy the wasm build into `recast-navigation-js/packages/recast-navigation-wasm/build`
|
||||
- Run `yarn build`
|
||||
- Check out `umd-build`
|
||||
- Run `yarn build`
|
||||
- Copy `recast-navigation-js/packages/recast-navigation/recast-navigation-generators.js` and the wasm build into this folder
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1,399 @@
|
||||
// @ts-check
|
||||
describe('gdjs.NavMeshCharacterRuntimeBehavior', function () {
|
||||
const characterBehaviorName = 'NavMeshCharacter';
|
||||
|
||||
before(async function () {
|
||||
// Give some time for the Recast navigation library (WASM) to be loaded.
|
||||
this.timeout(30000);
|
||||
await gdjs.getAllAsynchronouslyLoadingLibraryPromise();
|
||||
});
|
||||
|
||||
const createScene = (framePerSecond = 60) => {
|
||||
const runtimeGame = gdjs.getPixiRuntimeGame();
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
sceneData: {
|
||||
layers: [
|
||||
{
|
||||
name: '',
|
||||
visibility: true,
|
||||
effects: [],
|
||||
cameras: [],
|
||||
|
||||
ambientLightColorR: 0,
|
||||
ambientLightColorG: 0,
|
||||
ambientLightColorB: 0,
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: true,
|
||||
},
|
||||
],
|
||||
variables: [],
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: 'Scene1',
|
||||
name: 'Scene1',
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
objectsGroups: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
uiSettings: {
|
||||
grid: false,
|
||||
gridType: 'rectangular',
|
||||
gridWidth: 10,
|
||||
gridHeight: 10,
|
||||
gridDepth: 10,
|
||||
gridOffsetX: 0,
|
||||
gridOffsetY: 0,
|
||||
gridOffsetZ: 0,
|
||||
gridColor: 0,
|
||||
gridAlpha: 1,
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
usedExtensionsWithVariablesData: [],
|
||||
});
|
||||
const characterSharedData = {
|
||||
name: characterBehaviorName,
|
||||
type: 'NavMeshPathfinding::NavMeshCharacterBehavior',
|
||||
cellSize: 10,
|
||||
cellDepth: 10,
|
||||
slopeMaxAngle: 50,
|
||||
stairHeightMax: 20,
|
||||
walkableRadius: -1,
|
||||
walkableDepth: 150,
|
||||
speedScaleY: 1,
|
||||
};
|
||||
runtimeScene.setInitialSharedDataForBehavior(
|
||||
characterBehaviorName,
|
||||
characterSharedData
|
||||
);
|
||||
setFramePerSecond(runtimeScene, framePerSecond);
|
||||
return runtimeScene;
|
||||
};
|
||||
const setFramePerSecond = (runtimeScene, framePerSecond) => {
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return 1000 / framePerSecond;
|
||||
};
|
||||
};
|
||||
|
||||
const addCharacter = (runtimeScene) => {
|
||||
const character = new gdjs.RuntimeObject(
|
||||
runtimeScene,
|
||||
{
|
||||
name: 'character',
|
||||
type: '',
|
||||
behaviors: [
|
||||
{
|
||||
type: 'NavMeshPathfinding::NavMeshCharacterBehavior',
|
||||
name: characterBehaviorName,
|
||||
// @ts-ignore - properties are not typed
|
||||
acceleration: 400,
|
||||
maxSpeed: 200,
|
||||
angularMaxSpeed: 180,
|
||||
rotateObject: false,
|
||||
angleOffset: 0,
|
||||
radius: 0,
|
||||
avoidanceSightRange: 120,
|
||||
},
|
||||
],
|
||||
effects: [],
|
||||
variables: [],
|
||||
},
|
||||
undefined
|
||||
);
|
||||
character.getWidth = function () {
|
||||
return 20;
|
||||
};
|
||||
character.getHeight = function () {
|
||||
return 20;
|
||||
};
|
||||
runtimeScene.addObject(character);
|
||||
return character;
|
||||
};
|
||||
|
||||
const addObstacle = (runtimeScene, width = 100, height = 100) => {
|
||||
const obstacle = new gdjs.RuntimeObject(
|
||||
runtimeScene,
|
||||
{
|
||||
name: 'obstacle',
|
||||
type: '',
|
||||
behaviors: [
|
||||
{
|
||||
type: 'NavMeshPathfinding::NavMeshObstacleBehavior',
|
||||
name: 'NavMeshObstacle',
|
||||
// @ts-ignore - properties are not typed
|
||||
shape: 'Box',
|
||||
meshShapeResourceName: '',
|
||||
},
|
||||
],
|
||||
effects: [],
|
||||
variables: [],
|
||||
},
|
||||
undefined
|
||||
);
|
||||
obstacle.getWidth = function () {
|
||||
return width;
|
||||
};
|
||||
obstacle.getHeight = function () {
|
||||
return height;
|
||||
};
|
||||
runtimeScene.addObject(obstacle);
|
||||
return obstacle;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add obstacles far in 2 corners to define the walkable area:
|
||||
* the ground used by the nav mesh covers the bounding box of all
|
||||
* the obstacles.
|
||||
*/
|
||||
const addGroundDelimiters = (runtimeScene) => {
|
||||
const topLeftObstacle = addObstacle(runtimeScene);
|
||||
topLeftObstacle.setPosition(-300, -300);
|
||||
const bottomRightObstacle = addObstacle(runtimeScene);
|
||||
bottomRightObstacle.setPosition(1000, 700);
|
||||
};
|
||||
|
||||
/** @returns {gdjs.NavMeshCharacterRuntimeBehavior} */
|
||||
const getCharacterBehavior = (character) =>
|
||||
// @ts-ignore - the behavior is from this extension.
|
||||
character.getBehavior(characterBehaviorName);
|
||||
|
||||
/**
|
||||
* The path nodes are the remaining corners of the path (not including
|
||||
* the current character position), so the path length is measured
|
||||
* from the character position.
|
||||
*/
|
||||
const getPathLength = (character) => {
|
||||
const behavior = getCharacterBehavior(character);
|
||||
if (behavior.getNodeCount() < 1) {
|
||||
return 0;
|
||||
}
|
||||
let pathLength = 0;
|
||||
let previousNodeX = character.getX();
|
||||
let previousNodeY = character.getY();
|
||||
for (let index = 0; index < behavior.getNodeCount(); index++) {
|
||||
const nodeX = behavior.getNodeX(index);
|
||||
const nodeY = behavior.getNodeY(index);
|
||||
pathLength += Math.hypot(nodeX - previousNodeX, nodeY - previousNodeY);
|
||||
previousNodeX = nodeX;
|
||||
previousNodeY = nodeY;
|
||||
}
|
||||
return pathLength;
|
||||
};
|
||||
|
||||
const stepUntilDestinationIsReached = (runtimeScene, character) => {
|
||||
const behavior = getCharacterBehavior(character);
|
||||
for (
|
||||
let stepIndex = 0;
|
||||
stepIndex < 600 && !behavior.destinationReached();
|
||||
stepIndex++
|
||||
) {
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
}
|
||||
};
|
||||
|
||||
let runtimeScene;
|
||||
let character;
|
||||
beforeEach(function () {
|
||||
runtimeScene = createScene();
|
||||
character = addCharacter(runtimeScene);
|
||||
addGroundDelimiters(runtimeScene);
|
||||
});
|
||||
|
||||
it('can find a path without any obstacle in the way and reach the destination', function () {
|
||||
character.setPosition(400, 300);
|
||||
// To ensure obstacles are registered.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
getCharacterBehavior(character).moveTo(600, 300, 0);
|
||||
expect(getCharacterBehavior(character).pathFound()).to.be(true);
|
||||
// The path is extracted from the agent at the 1st step following moveTo.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
// The origin and destination are snapped on the nav mesh grid,
|
||||
// so allow a margin on the path length.
|
||||
expect(getPathLength(character)).to.be.within(180, 220);
|
||||
|
||||
stepUntilDestinationIsReached(runtimeScene, character);
|
||||
expect(getCharacterBehavior(character).destinationReached()).to.be(true);
|
||||
expect(character.getX()).to.be.within(580, 620);
|
||||
expect(character.getY()).to.be.within(280, 320);
|
||||
});
|
||||
|
||||
it('can find a path around an obstacle in the way and reach the destination', function () {
|
||||
const blockingObstacle = addObstacle(runtimeScene, 50, 300);
|
||||
blockingObstacle.setPosition(475, 150);
|
||||
character.setPosition(400, 300);
|
||||
// To ensure obstacles are registered.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
getCharacterBehavior(character).moveTo(600, 300, 0);
|
||||
expect(getCharacterBehavior(character).pathFound()).to.be(true);
|
||||
// The path is extracted from the agent at the 1st step following moveTo.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
// The path can't be a straight line, it has to go around the obstacle.
|
||||
expect(getPathLength(character)).to.be.above(240);
|
||||
|
||||
stepUntilDestinationIsReached(runtimeScene, character);
|
||||
expect(getCharacterBehavior(character).destinationReached()).to.be(true);
|
||||
expect(character.getX()).to.be.within(580, 620);
|
||||
expect(character.getY()).to.be.within(280, 320);
|
||||
});
|
||||
|
||||
it('must not find a path to a destination outside of the walkable area', function () {
|
||||
character.setPosition(400, 300);
|
||||
// To ensure obstacles are registered.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
// The destination is way outside of the walkable area
|
||||
// (which is the bounding box of all the obstacles).
|
||||
getCharacterBehavior(character).moveTo(600, 5000, 0);
|
||||
expect(getCharacterBehavior(character).pathFound()).to.be(false);
|
||||
|
||||
// The character must not move.
|
||||
for (let stepIndex = 0; stepIndex < 10; stepIndex++) {
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
}
|
||||
expect(character.getX()).to.be.within(399, 401);
|
||||
expect(character.getY()).to.be.within(299, 301);
|
||||
});
|
||||
|
||||
it("keeps moving to its destination when a new destination can't be found", function () {
|
||||
character.setPosition(400, 300);
|
||||
// To ensure obstacles are registered.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
getCharacterBehavior(character).moveTo(600, 300, 0);
|
||||
expect(getCharacterBehavior(character).pathFound()).to.be(true);
|
||||
|
||||
// Let the character travel a bit.
|
||||
for (let stepIndex = 0; stepIndex < 30; stepIndex++) {
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
}
|
||||
expect(character.getX()).to.be.above(420);
|
||||
|
||||
// Ask for a destination that can't be reached.
|
||||
getCharacterBehavior(character).moveTo(600, 5000, 0);
|
||||
expect(getCharacterBehavior(character).pathFound()).to.be(false);
|
||||
|
||||
// The character keeps moving to its previous destination.
|
||||
stepUntilDestinationIsReached(runtimeScene, character);
|
||||
expect(getCharacterBehavior(character).destinationReached()).to.be(true);
|
||||
expect(character.getX()).to.be.within(580, 620);
|
||||
expect(character.getY()).to.be.within(280, 320);
|
||||
});
|
||||
|
||||
it('can find a shorter path when an obstacle is deactivated', function () {
|
||||
const blockingObstacle = addObstacle(runtimeScene, 50, 300);
|
||||
blockingObstacle.setPosition(475, 150);
|
||||
character.setPosition(400, 300);
|
||||
// To ensure obstacles are registered.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
// To build the nav mesh a 1st time with the blocking obstacle.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
const obstacleBehavior =
|
||||
/** @type {gdjs.NavMeshObstacleRuntimeBehavior} */ (
|
||||
blockingObstacle.getBehavior('NavMeshObstacle')
|
||||
);
|
||||
obstacleBehavior.activate(false);
|
||||
// The nav mesh is rebuilt at most once per second.
|
||||
for (let stepIndex = 0; stepIndex < 61; stepIndex++) {
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
}
|
||||
|
||||
getCharacterBehavior(character).moveTo(600, 300, 0);
|
||||
expect(getCharacterBehavior(character).pathFound()).to.be(true);
|
||||
// The path is extracted from the agent at the 1st step following moveTo.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
// The path is a straight line as the obstacle is gone.
|
||||
expect(getPathLength(character)).to.be.within(180, 220);
|
||||
});
|
||||
|
||||
describe('(network synchronization)', function () {
|
||||
it('can sync an idle character over an idle character without error', function () {
|
||||
const otherCharacter = addCharacter(runtimeScene);
|
||||
otherCharacter.setPosition(200, 300);
|
||||
character.setPosition(400, 300);
|
||||
// To ensure obstacles are registered.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
// Neither characters moved: their path is empty.
|
||||
const syncData = getCharacterBehavior(character).getNetworkSyncData({});
|
||||
expect(syncData.props.d).to.be(null);
|
||||
|
||||
// This must not crash nor start any movement.
|
||||
getCharacterBehavior(otherCharacter).updateFromNetworkSyncData(
|
||||
syncData,
|
||||
{}
|
||||
);
|
||||
expect(getCharacterBehavior(otherCharacter).pathFound()).to.be(false);
|
||||
expect(getCharacterBehavior(otherCharacter).getNodeCount()).to.be(0);
|
||||
});
|
||||
|
||||
it('can sync a moving character over an idle character', function () {
|
||||
const otherCharacter = addCharacter(runtimeScene);
|
||||
otherCharacter.setPosition(300, 200);
|
||||
character.setPosition(400, 300);
|
||||
// To ensure obstacles are registered.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
getCharacterBehavior(character).moveTo(600, 300, 0);
|
||||
// Let the path be extracted from the agent.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
expect(getCharacterBehavior(character).getNodeCount()).to.be.above(1);
|
||||
|
||||
const syncData = getCharacterBehavior(character).getNetworkSyncData({});
|
||||
getCharacterBehavior(otherCharacter).updateFromNetworkSyncData(
|
||||
syncData,
|
||||
{}
|
||||
);
|
||||
expect(getCharacterBehavior(otherCharacter).pathFound()).to.be(true);
|
||||
|
||||
stepUntilDestinationIsReached(runtimeScene, otherCharacter);
|
||||
expect(getCharacterBehavior(otherCharacter).destinationReached()).to.be(
|
||||
true
|
||||
);
|
||||
expect(otherCharacter.getX()).to.be.within(580, 620);
|
||||
expect(otherCharacter.getY()).to.be.within(280, 320);
|
||||
});
|
||||
|
||||
it('can sync an idle character over a moving character to stop it', function () {
|
||||
const otherCharacter = addCharacter(runtimeScene);
|
||||
otherCharacter.setPosition(400, 300);
|
||||
character.setPosition(400, 300);
|
||||
// To ensure obstacles are registered.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
|
||||
getCharacterBehavior(otherCharacter).moveTo(600, 300, 0);
|
||||
// Let the path be extracted from the agent.
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
expect(getCharacterBehavior(otherCharacter).getNodeCount()).to.be.above(
|
||||
1
|
||||
);
|
||||
|
||||
// The (idle) character path is empty.
|
||||
const syncData = getCharacterBehavior(character).getNetworkSyncData({});
|
||||
expect(syncData.props.d).to.be(null);
|
||||
|
||||
getCharacterBehavior(otherCharacter).updateFromNetworkSyncData(
|
||||
syncData,
|
||||
{}
|
||||
);
|
||||
expect(getCharacterBehavior(otherCharacter).pathFound()).to.be(false);
|
||||
expect(getCharacterBehavior(otherCharacter).getNodeCount()).to.be(0);
|
||||
|
||||
// The character must not move anymore.
|
||||
const oldX = otherCharacter.getX();
|
||||
const oldY = otherCharacter.getY();
|
||||
for (let stepIndex = 0; stepIndex < 10; stepIndex++) {
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
}
|
||||
expect(otherCharacter.getX()).to.be.within(oldX - 1, oldX + 1);
|
||||
expect(otherCharacter.getY()).to.be.within(oldY - 1, oldY + 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -27,18 +27,18 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
.SetTags("pathfinding, obstacle, collision")
|
||||
.SetExtensionHelpPath("/behaviors/pathfinding");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Pathfinding behavior"))
|
||||
.SetIcon("CppPlatform/Extensions/AStaricon16.png");
|
||||
.SetIcon("CppPlatform/Extensions/pathfinding-character.svg");
|
||||
|
||||
{
|
||||
gd::BehaviorMetadata& aut =
|
||||
extension.AddBehavior("PathfindingBehavior",
|
||||
_("Pathfinding"),
|
||||
_("Pathfinding character (grid-based)"),
|
||||
"Pathfinding",
|
||||
_("Move objects to a target "
|
||||
"while avoiding all objects that are "
|
||||
"flagged as obstacles."),
|
||||
"",
|
||||
"CppPlatform/Extensions/AStaricon.png",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"PathfindingBehavior",
|
||||
std::make_shared<PathfindingBehavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>());
|
||||
@@ -48,8 +48,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Move the object to a position"),
|
||||
_("Move _PARAM0_ to _PARAM3_;_PARAM4_"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
@@ -63,8 +63,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Check if a path has been found."),
|
||||
_("A path has been found for _PARAM0_"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -75,8 +75,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Check if the destination was reached."),
|
||||
_("_PARAM0_ reached its destination"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -87,8 +87,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the width of the cells of the virtual grid."),
|
||||
_("the width of the virtual cells"),
|
||||
_("Virtual grid"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -102,8 +102,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the width of the cells of the virtual grid."),
|
||||
_("the width of the virtual cells"),
|
||||
_("Virtual grid"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -116,8 +116,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the height of the cells of the virtual grid."),
|
||||
_("the height of the virtual cells"),
|
||||
_("Virtual grid"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -131,8 +131,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the height of the cells of the virtual grid."),
|
||||
_("the height of the virtual cells"),
|
||||
_("Virtual grid"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -145,8 +145,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the acceleration when moving the object"),
|
||||
_("the acceleration on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -160,8 +160,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the acceleration when moving the object"),
|
||||
_("the acceleration"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -174,8 +174,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the maximum speed when moving the object"),
|
||||
_("the max. speed on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -191,8 +191,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the maximum speed when moving the object"),
|
||||
_("the max. speed"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -207,8 +207,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the speed of the object on the path"),
|
||||
_("the speed on the path"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -224,8 +224,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the speed of the object on its path."),
|
||||
_("the speed"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -241,8 +241,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the angle of movement of an object on its path."),
|
||||
_("Angle of movement of _PARAM0_ is _PARAM2_ ± _PARAM3_°"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -254,8 +254,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the maximum angular speed when moving the object"),
|
||||
_("the max. angular speed on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -272,8 +272,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the maximum angular speed when moving the object"),
|
||||
_("the max. angular speed"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -289,8 +289,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the rotation offset applied when moving the object"),
|
||||
_("the rotation offset on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -306,8 +306,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the rotation offset when moving the object"),
|
||||
_("the rotation offset"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -324,8 +324,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"planning a path"),
|
||||
_("the size of the extra border on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -340,8 +340,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"object when planning a path"),
|
||||
_("the size of the extra border on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -355,8 +355,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Allow or restrict diagonal movement on the path"),
|
||||
_("Allow diagonal movement for _PARAM0_ on the path: _PARAM2_"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -369,8 +369,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"diagonally on the path"),
|
||||
_("Diagonal moves allowed for _PARAM0_"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -381,8 +381,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Enable or disable rotation of the object on the path"),
|
||||
_("Enable rotation of _PARAM0_ on the path: _PARAM2_"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -395,8 +395,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"its path."),
|
||||
_("_PARAM0_ is rotated when traveling on its path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon24.png",
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg",
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
@@ -404,9 +404,9 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
|
||||
aut.AddExpression("GetNodeX",
|
||||
_("Get a waypoint X position"),
|
||||
_("Get next waypoint X position"),
|
||||
_("Get a waypoint X position"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.AddParameter("expression", _("Node index (start at 0!)"))
|
||||
@@ -414,9 +414,9 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
|
||||
aut.AddExpression("GetNodeY",
|
||||
_("Get a waypoint Y position"),
|
||||
_("Get next waypoint Y position"),
|
||||
_("Get a waypoint Y position"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.AddParameter("expression", _("Node index (start at 0!)"))
|
||||
@@ -426,7 +426,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Index of the next waypoint"),
|
||||
_("Get the index of the next waypoint to reach"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetNextNodeIndex");
|
||||
@@ -435,7 +435,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Waypoint count"),
|
||||
_("Get the number of waypoints on the path"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetNodeCount");
|
||||
@@ -444,7 +444,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Get next waypoint X position"),
|
||||
_("Get next waypoint X position"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetNextNodeX");
|
||||
@@ -453,7 +453,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Get next waypoint Y position"),
|
||||
_("Get next waypoint Y position"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetNextNodeY");
|
||||
@@ -462,7 +462,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Last waypoint X position"),
|
||||
_("Last waypoint X position"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetLastNodeX");
|
||||
@@ -471,7 +471,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Last waypoint Y position"),
|
||||
_("Last waypoint Y position"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetLastNodeY");
|
||||
@@ -480,7 +480,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Destination X position"),
|
||||
_("Destination X position"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetDestinationX");
|
||||
@@ -489,7 +489,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Destination Y position"),
|
||||
_("Destination Y position"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetDestinationY");
|
||||
@@ -498,7 +498,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Acceleration"),
|
||||
_("Acceleration of the object on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetAcceleration");
|
||||
@@ -507,7 +507,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Maximum speed"),
|
||||
_("Maximum speed of the object on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetMaxSpeed");
|
||||
@@ -516,7 +516,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Speed"),
|
||||
_("Speed of the object on the path"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetSpeed");
|
||||
@@ -525,7 +525,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Angular maximum speed"),
|
||||
_("Angular maximum speed of the object on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetAngularMaxSpeed");
|
||||
@@ -534,7 +534,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Rotation offset"),
|
||||
_("Rotation offset applied the object on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetAngleOffset");
|
||||
@@ -543,7 +543,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Extra border size"),
|
||||
_("Extra border applied the object on the path"),
|
||||
_("Pathfinding configuration"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetExtraBorder");
|
||||
@@ -552,7 +552,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Width of a cell"),
|
||||
_("Width of the virtual grid"),
|
||||
_("Virtual grid"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetCellWidth");
|
||||
@@ -561,7 +561,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Height of a cell"),
|
||||
_("Height of the virtual grid"),
|
||||
_("Virtual grid"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.SetFunctionName("GetCellHeight");
|
||||
@@ -570,7 +570,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Angle of movement on its path"),
|
||||
_("Angle of movement on its path"),
|
||||
_("Movement on the path"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior");
|
||||
|
||||
@@ -580,7 +580,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("X offset of the virtual grid"),
|
||||
_("X offset of the virtual grid"),
|
||||
_("Virtual grid"),
|
||||
"CppPlatform/Extensions/AStaricon24.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions());
|
||||
@@ -591,7 +591,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Y offset of the virtual grid"),
|
||||
_("Y offset of the virtual grid"),
|
||||
_("Virtual grid"),
|
||||
"CppPlatform/Extensions/AStaricon24.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingBehavior")
|
||||
.UseStandardParameters("number", gd::ParameterOptions::MakeNewOptions());
|
||||
@@ -600,11 +600,11 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
{
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"PathfindingObstacleBehavior",
|
||||
_("Obstacle for pathfinding"),
|
||||
_("Obstacle for pathfinding (grid-based)"),
|
||||
"PathfindingObstacle",
|
||||
_("Flag objects as being obstacles for pathfinding."),
|
||||
"",
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon.png",
|
||||
"CppPlatform/Extensions/pathfinding-obstacle.svg",
|
||||
"PathfindingObstacleBehavior",
|
||||
std::make_shared<PathfindingObstacleBehavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>());
|
||||
@@ -614,8 +614,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the cost of going through the object."),
|
||||
_("the cost"),
|
||||
_("Obstacles"),
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon24.png",
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-obstacle.svg",
|
||||
"CppPlatform/Extensions/pathfinding-obstacle.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
|
||||
@@ -629,8 +629,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the cost of going through the object"),
|
||||
_("the cost"),
|
||||
_("Obstacles"),
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon24.png",
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-obstacle.svg",
|
||||
"CppPlatform/Extensions/pathfinding-obstacle.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
|
||||
@@ -643,8 +643,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Decide if the object is an impassable obstacle."),
|
||||
_("Set _PARAM0_ as an impassable obstacle: _PARAM2_"),
|
||||
_("Obstacles"),
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon24.png",
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-obstacle.svg",
|
||||
"CppPlatform/Extensions/pathfinding-obstacle.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
|
||||
@@ -656,8 +656,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Check if the obstacle is impassable."),
|
||||
_("_PARAM0_ is impassable"),
|
||||
_("Obstacles"),
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon24.png",
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-obstacle.svg",
|
||||
"CppPlatform/Extensions/pathfinding-obstacle.svg")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
|
||||
@@ -667,7 +667,7 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Cost"),
|
||||
_("Obstacle cost"),
|
||||
_("Obstacles"),
|
||||
"CppPlatform/Extensions/AStaricon16.png")
|
||||
"CppPlatform/Extensions/pathfinding-character.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
|
||||
.SetFunctionName("GetCost");
|
||||
|
||||
@@ -14,8 +14,7 @@ namespace gdjs {
|
||||
* having a pathfinding behavior: In particular, the obstacles behaviors are
|
||||
* required to declare themselves (see
|
||||
* `PathfindingObstaclesManager.addObstacle`) to the manager of their
|
||||
* associated container (see
|
||||
* `gdjs.PathfindingRuntimeBehavior.obstaclesManagers`).
|
||||
* associated container (see `PathfindingObstaclesManager.getManager`).
|
||||
* @category Behaviors > 2D Pathfinding
|
||||
*/
|
||||
export class PathfindingObstaclesManager {
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace gdjs {
|
||||
* Manages the common objects shared by objects having a
|
||||
* platform behavior: in particular, the platforms behaviors are required to
|
||||
* declare themselves (see PlatformObjectsManager.addPlatform) to the manager
|
||||
* of their associated container (see PlatformRuntimeBehavior.getManager).
|
||||
* of their associated container (see PlatformObjectsManager.getManager).
|
||||
* @category Behaviors > Platform
|
||||
*/
|
||||
export class PlatformObjectsManager {
|
||||
|
||||
@@ -280,9 +280,11 @@ namespace gdjs {
|
||||
if (this._debugDrawEnabled) {
|
||||
this._debuggerRenderer.renderDebugDraw(
|
||||
allInstancesList,
|
||||
this._debugDrawShowHitBoxes,
|
||||
this._debugDrawShowHiddenInstances,
|
||||
this._debugDrawShowPointsNames,
|
||||
this._debugDrawShowCustomPoints
|
||||
this._debugDrawShowCustomPoints,
|
||||
this._debugDrawHooks
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,9 +52,11 @@ namespace gdjs {
|
||||
|
||||
// Options for the debug draw:
|
||||
_debugDrawEnabled: boolean = false;
|
||||
_debugDrawShowHitBoxes: boolean = false;
|
||||
_debugDrawShowHiddenInstances: boolean = false;
|
||||
_debugDrawShowPointsNames: boolean = false;
|
||||
_debugDrawShowCustomPoints: boolean = false;
|
||||
_debugDrawHooks: Array<(rendererObject: any) => void> = [];
|
||||
|
||||
_onceTriggers: OnceTriggers;
|
||||
|
||||
@@ -243,11 +245,16 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
this._debugDrawEnabled = enableDebugDraw;
|
||||
this._debugDrawShowHitBoxes = enableDebugDraw;
|
||||
this._debugDrawShowHiddenInstances = showHiddenInstances;
|
||||
this._debugDrawShowPointsNames = showPointsNames;
|
||||
this._debugDrawShowCustomPoints = showCustomPoints;
|
||||
}
|
||||
|
||||
registerDebugDrawHook(render: (rendererObject: any) => void) {
|
||||
this._debugDrawHooks.push(render);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an object is registered, meaning that instances of it can be
|
||||
* created and lives in the container.
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace gdjs {
|
||||
* @category Debugging > Debugger Renderer
|
||||
*/
|
||||
export class DebuggerPixiRenderer {
|
||||
// as this is for debug draw.
|
||||
_instanceContainer: gdjs.RuntimeInstanceContainer;
|
||||
_debugDraw: PIXI.Graphics | null = null;
|
||||
_debugDrawContainer: PIXI.Container | null = null;
|
||||
@@ -34,9 +35,11 @@ namespace gdjs {
|
||||
*/
|
||||
renderDebugDraw(
|
||||
instances: gdjs.RuntimeObject[],
|
||||
showHitBoxes: boolean,
|
||||
showHiddenInstances: boolean,
|
||||
showPointsNames: boolean,
|
||||
showCustomPoints: boolean
|
||||
showCustomPoints: boolean,
|
||||
debugDrawHooks: Array<(rendererObject: any) => void>
|
||||
) {
|
||||
const pixiContainer = this._instanceContainer
|
||||
.getRenderer()
|
||||
@@ -84,6 +87,15 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
debugDraw.clear();
|
||||
|
||||
for (const render of debugDrawHooks) {
|
||||
render(debugDraw);
|
||||
}
|
||||
|
||||
if (!showHitBoxes) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugDraw.beginFill();
|
||||
debugDraw.alpha = 0.8;
|
||||
debugDraw.lineStyle(2, 0x0000ff, 1);
|
||||
|
||||
@@ -4,6 +4,21 @@
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
namespace gdjs {
|
||||
export type AbstractRuntimeObject3D = gdjs.RuntimeObject &
|
||||
gdjs.Base3DHandler &
|
||||
gdjs.Resizable &
|
||||
gdjs.Scalable &
|
||||
gdjs.Flippable;
|
||||
/** @category Objects > 3D Objects */
|
||||
export namespace Base3DHandler {
|
||||
export const is3D = (
|
||||
object: gdjs.RuntimeObject
|
||||
): object is AbstractRuntimeObject3D => {
|
||||
//@ts-ignore We are checking if the methods are present.
|
||||
return object.getZ && object.setZ;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* An axis-aligned bounding box. Used to represents a box around an object for example.
|
||||
* @category Core Engine > Object
|
||||
|
||||
@@ -478,9 +478,11 @@ namespace gdjs {
|
||||
if (this._debugDrawEnabled) {
|
||||
this._debuggerRenderer.renderDebugDraw(
|
||||
this.getAdhocListOfAllInstances(),
|
||||
this._debugDrawShowHitBoxes,
|
||||
this._debugDrawShowHiddenInstances,
|
||||
this._debugDrawShowPointsNames,
|
||||
this._debugDrawShowCustomPoints
|
||||
this._debugDrawShowCustomPoints,
|
||||
this._debugDrawHooks
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
import {
|
||||
init,
|
||||
DebugDrawerUtils,
|
||||
RecastBuildContext,
|
||||
RecastChunkyTriMesh,
|
||||
RecastSpan,
|
||||
RecastSpanPool,
|
||||
RecastHeightfield,
|
||||
RecastCompactCell,
|
||||
RecastCompactSpan,
|
||||
RecastCompactHeightfield,
|
||||
RecastContour,
|
||||
RecastContourSet,
|
||||
RecastHeightfieldLayer,
|
||||
RecastHeightfieldLayerSet,
|
||||
RecastPolyMesh,
|
||||
RecastPolyMeshDetail,
|
||||
IntArray,
|
||||
UnsignedIntArray,
|
||||
UnsignedCharArray,
|
||||
UnsignedShortArray,
|
||||
FloatArray,
|
||||
QueryFilter,
|
||||
NavMeshQuery,
|
||||
CrowdAgent,
|
||||
Crowd,
|
||||
NavMeshGetTilesAtResult,
|
||||
NavMeshRemoveTileResult,
|
||||
NavMeshCalcTileLocResult,
|
||||
NavMeshStoreTileStateResult,
|
||||
NavMeshParams,
|
||||
NavMesh,
|
||||
DetourTileCacheParams,
|
||||
TileCache,
|
||||
TileCacheMeshProcess,
|
||||
DetourPolyDetail,
|
||||
DetourLink,
|
||||
DetourBVNode,
|
||||
DetourOffMeshConnection,
|
||||
DetourMeshHeader,
|
||||
DetourPoly,
|
||||
DetourMeshTile,
|
||||
NavMeshCreateParams,
|
||||
DebugDrawerPrimitiveType,
|
||||
DebugDrawerPrimitive,
|
||||
ObstacleRef,
|
||||
BoxObstacle,
|
||||
CylinderObstacle,
|
||||
AddObstacleResult,
|
||||
RemoveObstacleResult,
|
||||
Obstacle,
|
||||
TileCacheParamsType,
|
||||
TileCacheUpdateResult,
|
||||
NavMeshDecodePolyIdResult,
|
||||
NavMeshParamsType,
|
||||
ImportNavMeshResult,
|
||||
ImportTileCacheResult,
|
||||
Pretty,
|
||||
RecastConfig,
|
||||
OffMeshConnectionParams,
|
||||
Vector3,
|
||||
Vector3Tuple,
|
||||
Vector2,
|
||||
Vector2Tuple,
|
||||
CrowdAgentParams,
|
||||
CrowdParams,
|
||||
NavMeshQueryParams,
|
||||
} from 'recast-navigation';
|
||||
|
||||
import {
|
||||
buildTiledNavMeshRcConfig,
|
||||
createDefaultTileCacheMeshProcess,
|
||||
dtIlog2,
|
||||
dtNextPow2,
|
||||
generateSoloNavMesh,
|
||||
generateSoloNavMeshData,
|
||||
generateTileCache,
|
||||
generateTileNavMeshData,
|
||||
generateTiledNavMesh,
|
||||
getBoundingBox,
|
||||
mergePositionsAndIndices,
|
||||
soloNavMeshGeneratorConfigDefaults,
|
||||
tileCacheGeneratorConfigDefaults,
|
||||
tiledNavMeshGeneratorConfigDefaults,
|
||||
SoloNavMeshGeneratorConfig,
|
||||
} from 'recast-navigation/generators';
|
||||
|
||||
declare global {
|
||||
namespace RecastNav {
|
||||
export {
|
||||
init,
|
||||
DebugDrawerUtils,
|
||||
RecastBuildContext,
|
||||
RecastChunkyTriMesh,
|
||||
RecastSpan,
|
||||
RecastSpanPool,
|
||||
RecastHeightfield,
|
||||
RecastCompactCell,
|
||||
RecastCompactSpan,
|
||||
RecastCompactHeightfield,
|
||||
RecastContour,
|
||||
RecastContourSet,
|
||||
RecastHeightfieldLayer,
|
||||
RecastHeightfieldLayerSet,
|
||||
RecastPolyMesh,
|
||||
RecastPolyMeshDetail,
|
||||
IntArray,
|
||||
UnsignedIntArray,
|
||||
UnsignedCharArray,
|
||||
UnsignedShortArray,
|
||||
FloatArray,
|
||||
QueryFilter,
|
||||
NavMeshQuery,
|
||||
CrowdAgent,
|
||||
Crowd,
|
||||
NavMeshGetTilesAtResult,
|
||||
NavMeshRemoveTileResult,
|
||||
NavMeshCalcTileLocResult,
|
||||
NavMeshStoreTileStateResult,
|
||||
NavMeshParams,
|
||||
NavMesh,
|
||||
DetourTileCacheParams,
|
||||
TileCache,
|
||||
TileCacheMeshProcess,
|
||||
DetourPolyDetail,
|
||||
DetourLink,
|
||||
DetourBVNode,
|
||||
DetourOffMeshConnection,
|
||||
DetourMeshHeader,
|
||||
DetourPoly,
|
||||
DetourMeshTile,
|
||||
NavMeshCreateParams,
|
||||
DebugDrawerPrimitiveType,
|
||||
DebugDrawerPrimitive,
|
||||
ObstacleRef,
|
||||
BoxObstacle,
|
||||
CylinderObstacle,
|
||||
AddObstacleResult,
|
||||
RemoveObstacleResult,
|
||||
Obstacle,
|
||||
TileCacheParamsType,
|
||||
TileCacheUpdateResult,
|
||||
NavMeshDecodePolyIdResult,
|
||||
NavMeshParamsType,
|
||||
ImportNavMeshResult,
|
||||
ImportTileCacheResult,
|
||||
Pretty,
|
||||
RecastConfig,
|
||||
OffMeshConnectionParams,
|
||||
Vector3,
|
||||
Vector3Tuple,
|
||||
Vector2,
|
||||
Vector2Tuple,
|
||||
CrowdAgentParams,
|
||||
CrowdParams,
|
||||
NavMeshQueryParams,
|
||||
buildTiledNavMeshRcConfig,
|
||||
createDefaultTileCacheMeshProcess,
|
||||
dtIlog2,
|
||||
dtNextPow2,
|
||||
generateSoloNavMesh,
|
||||
generateSoloNavMeshData,
|
||||
generateTileCache,
|
||||
generateTileNavMeshData,
|
||||
generateTiledNavMesh,
|
||||
getBoundingBox,
|
||||
mergePositionsAndIndices,
|
||||
soloNavMeshGeneratorConfigDefaults,
|
||||
tileCacheGeneratorConfigDefaults,
|
||||
tiledNavMeshGeneratorConfigDefaults,
|
||||
SoloNavMeshGeneratorConfig,
|
||||
};
|
||||
}
|
||||
}
|
||||
Generated
+40
@@ -22,6 +22,7 @@
|
||||
"patch-package": "^6.4.7",
|
||||
"pixi.js": "7.4.2",
|
||||
"prettier": "3.4.2",
|
||||
"recast-navigation": "0.43.1",
|
||||
"recursive-readdir": "^2.2.2",
|
||||
"shelljs": "^0.8.4",
|
||||
"typedoc": "^0.25.13",
|
||||
@@ -285,6 +286,34 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@recast-navigation/core": {
|
||||
"version": "0.43.1",
|
||||
"resolved": "https://registry.npmjs.org/@recast-navigation/core/-/core-0.43.1.tgz",
|
||||
"integrity": "sha512-4igfPgnoV90O92sDSIA0xHa6tanh3z/udlgGmD0SqGQ6PKV4JS2l9jWO8YnxfZuXP/NV52H4c5FU6pSdZc9WDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@recast-navigation/wasm": "0.43.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@recast-navigation/generators": {
|
||||
"version": "0.43.1",
|
||||
"resolved": "https://registry.npmjs.org/@recast-navigation/generators/-/generators-0.43.1.tgz",
|
||||
"integrity": "sha512-w7r6k/A93wWxuSN+Lg+PmlsUVXq06r5bqXmBglolxqDt2O/Y776BIhYtb6P2P6pbYqDWORu9NCZZJzfY4FNpaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@recast-navigation/core": "0.43.1",
|
||||
"@recast-navigation/wasm": "0.43.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@recast-navigation/wasm": {
|
||||
"version": "0.43.1",
|
||||
"resolved": "https://registry.npmjs.org/@recast-navigation/wasm/-/wasm-0.43.1.tgz",
|
||||
"integrity": "sha512-XFL6PhO8JodwXUHBSAkfaLUdUEweIkJsdP3HTmtJvMs5fkqEWxWJmBskZlACHJepv55NAriqWxOtuLlG3t7+Hg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/css-font-loading-module": {
|
||||
"version": "0.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.12.tgz",
|
||||
@@ -2252,6 +2281,17 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/recast-navigation": {
|
||||
"version": "0.43.1",
|
||||
"resolved": "https://registry.npmjs.org/recast-navigation/-/recast-navigation-0.43.1.tgz",
|
||||
"integrity": "sha512-BVBQEHE6uqD36opJomVkI5TxMVZ8bBLdDn90mYtBUYJnNlqEuNFOL8DH8lLOksfVVaC+kjykYuS57P6MrxVB7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@recast-navigation/core": "0.43.1",
|
||||
"@recast-navigation/generators": "0.43.1"
|
||||
}
|
||||
},
|
||||
"node_modules/rechoir": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"patch-package": "^6.4.7",
|
||||
"@esotericsoftware/spine-pixi-v7": "~4.2.116",
|
||||
"pixi.js": "7.4.2",
|
||||
"recast-navigation": "0.43.1",
|
||||
"prettier": "3.4.2",
|
||||
"recursive-readdir": "^2.2.2",
|
||||
"shelljs": "^0.8.4",
|
||||
|
||||
@@ -144,6 +144,25 @@ module.exports = function (config) {
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/PathfindingBehavior/PathTools.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/PathfindingBehavior/pathfindingobstacleruntimebehavior.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/PathfindingBehavior/pathfindingruntimebehavior.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/NavMeshPathfinding/recast-navigation-generators.js',
|
||||
{
|
||||
pattern:
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/NavMeshPathfinding/recast-navigation.wasm.js',
|
||||
watched: true,
|
||||
included: false,
|
||||
served: true,
|
||||
nocache: false,
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/NavMeshPathfinding/recast-navigation.wasm.wasm',
|
||||
watched: true,
|
||||
included: false,
|
||||
served: true,
|
||||
nocache: false,
|
||||
},
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/NavMeshPathfinding/NavMeshObstacleRuntimeBehavior.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/NavMeshPathfinding/NavMeshCharacterRuntimeBehavior.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/PrimitiveDrawing/shapepainterruntimeobject.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/PrimitiveDrawing/shapepainterruntimeobject-pixi-renderer.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/Extensions/TextInput/textinputruntimeobject.js',
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 0H0V14H14V0Z" fill="#27AAE1"/>
|
||||
<path d="M10 9.5H9V13H10V9.5Z" fill="#2B3990"/>
|
||||
<path d="M13 11H9V9H13L12 10L13 11Z" fill="#2B3990"/>
|
||||
<path d="M3 5C4.10457 5 5 4.10457 5 3C5 1.89543 4.10457 1 3 1C1.89543 1 1 1.89543 1 3C1 4.10457 1.89543 5 3 5Z" fill="#2B3990"/>
|
||||
<path d="M3 4C3.55228 4 4 3.55228 4 3C4 2.44772 3.55228 2 3 2C2.44772 2 2 2.44772 2 3C2 3.55228 2.44772 4 3 4Z" fill="white"/>
|
||||
<path d="M11 2.5H10V4.5H11V2.5Z" fill="white"/>
|
||||
<path d="M8 2.5H6V3.5H8V2.5Z" fill="white"/>
|
||||
<path d="M11 2.5H9V3.5H11V2.5Z" fill="white"/>
|
||||
<path d="M11 5.5H10V7.5H11V5.5Z" fill="white"/>
|
||||
<path d="M11 6.5H9V7.5H11V6.5Z" fill="white"/>
|
||||
<path d="M8 6.5H6V7.5H8V6.5Z" fill="white"/>
|
||||
<path d="M5 6.5H3V7.5H5V6.5Z" fill="white"/>
|
||||
<path d="M4 6.5H3V8.5H4V6.5Z" fill="white"/>
|
||||
<path d="M4 9.5H3V11.5H4V9.5Z" fill="white"/>
|
||||
<path d="M5 10.5H3V11.5H5V10.5Z" fill="white"/>
|
||||
<path d="M8 10.5H6V11.5H8V10.5Z" fill="white"/>
|
||||
<g opacity="0.1">
|
||||
<path d="M14 14L0 0V14H14Z" fill="black"/>
|
||||
<path d="M14 0H0L14 14V0Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14" height="14" fill="none" version="1.1" viewBox="0 0 14 14" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g fill="#2b3990"><path d="m7 6.5h-1v2h1z"/><path d="m7 9.5h-1v2h1z"/><path d="m7 10.5h-2v1h2z"/><path d="m4 10.5h-2v1h2z"/><path d="m7 3.5h-1v2h1z"/><path d="m6.5 2-1.5 2h3z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/></g><g transform="matrix(0 .85714 -.875 0 28.375 5.2857)"><path d="m1 17-4 4h2l4-4z" fill="#27aae1"/><path d="m-1 17-4 4h2l4-4z" fill="#2b3990"/><path d="m-3 17-2 2v2l4-4z" fill="#27aae1"/><path d="m-3 17h-2v2z" fill="#2b3990"/><path d="m7 21h2v-2z" fill="#2b3990"/><path d="m9 17-4 4h2l2-2z" fill="#27aae1"/><path d="m7 17-4 4h2l4-4z" fill="#2b3990"/><path d="m5 17-4 4h2l4-4z" fill="#27aae1"/><path d="m3 17-4 4h2l4-4z" fill="#2b3990"/></g></svg>
|
||||
|
After Width: | Height: | Size: 861 B |
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14" height="14" fill="none" version="1.1" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 0H0V14H14V0Z" fill="#27AAE1"/>
|
||||
<g fill="#2B3990">
|
||||
<path d="M10 9.5H9V13H10V9.5Z"/>
|
||||
<path d="m13 11h-4v-2h4l-1 1 1 1z"/>
|
||||
<path d="m3 5c1.1046 0 2-0.89543 2-2s-0.89543-2-2-2-2 0.89543-2 2 0.89543 2 2 2z"/>
|
||||
</g>
|
||||
<g fill="#fff">
|
||||
<path d="m3 4c0.55228 0 1-0.44772 1-1s-0.44772-1-1-1-1 0.44772-1 1 0.44772 1 1 1z"/>
|
||||
<path d="m10.93 4.1018h-1v2h1z"/>
|
||||
<path d="m8.1107 3.0757-1.8794-0.68404-0.34202 0.93969 1.8794 0.68404z"/>
|
||||
<path d="m10.93 4.1018-1.8794-0.68404-0.34202 0.93969 1.8794 0.68404z"/>
|
||||
</g>
|
||||
<g transform="rotate(-20,7,7)" fill="#fff">
|
||||
<path d="m11 6.5h-2v1h2z"/>
|
||||
<path d="m8 6.5h-2v1h2z"/>
|
||||
<path d="m5 6.5h-2v1h2z"/>
|
||||
</g>
|
||||
<g fill="#fff">
|
||||
<path d="m4.0702 7.8982h-1v2h1z"/>
|
||||
<path d="m5.2916 9.6426-1.8794-0.68404-0.34202 0.93969 1.8794 0.68404z"/>
|
||||
<path d="m8.1107 10.669-1.8794-0.68404-0.34202 0.93969 1.8794 0.68404z"/>
|
||||
</g>
|
||||
<g opacity=".1">
|
||||
<path d="m14 14-14-14v14z" fill="#000"/>
|
||||
<path d="m14 0h-14l14 14z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14" height="14" fill="none" version="1.1" viewBox="0 0 14 14" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m4 10.5h-2v1h2z" fill="#2b3990"/><g transform="rotate(20 5.5 2.9931)" fill="#2b3990"><path d="m7 6.5h-1v2h1z"/><path d="m7 9.5h-1v2h1z"/><path d="m7 3.5h-1v2h1z"/><path d="m6.5 2-1.5 2h3z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/></g><g stroke-width=".85714"><path d="m13 4.4286-2.1429-2.1429-0.42857 1.2857 2.5714 2.5714z" fill="#27aae1"/><path d="m13 2.7143-1.7143-1.7143-0.42857 1.2857 2.1429 2.1429z" fill="#2b3990"/><path d="m13 1h-1.7143l1.7143 1.7143z" fill="#27aae1"/><path d="m8.7143 8.7143-0.42857 1.2857 3 3h1.7143z" fill="#2b3990"/><path d="m7.8571 11.286-0.42857 1.2857-0.14314 0.42857h2.286z" fill="#2b3990"/><path d="m13 11.286-3.8571-3.8571-0.42857 1.2857 4.2857 4.2857z" fill="#27aae1"/><path d="m11.286 13-3-3-0.42857 1.2857 1.7143 1.7143z" fill="#27aae1"/><path d="m13 9.5714-3.4286-3.4286-0.42857 1.2857 3.8571 3.8571z" fill="#2b3990"/><path d="m13 7.8571-3-3-0.42857 1.2857 3.4286 3.4286z" fill="#27aae1"/><path d="m13 6.1429-2.5714-2.5714-0.42857 1.2857 3 3z" fill="#2b3990"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -31,7 +31,7 @@ type GetExpectedNumberOfJSExtensionModulesArguments = {|
|
||||
function getExpectedNumberOfJSExtensionModules(
|
||||
{ filterExamples } /*: GetExpectedNumberOfJSExtensionModulesArguments*/
|
||||
) /*:number*/ {
|
||||
return 29 + (filterExamples ? 0 : 1);
|
||||
return 30 + (filterExamples ? 0 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user