diff --git a/extensions/community/Collision3D.json b/extensions/community/Collision3D.json new file mode 100644 index 00000000..5cd0510c --- /dev/null +++ b/extensions/community/Collision3D.json @@ -0,0 +1,861 @@ +{ + "author": "", + "category": "General", + "extensionNamespace": "", + "fullName": "3D collision", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXZlY3Rvci1pbnRlcnNlY3Rpb24iIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMy4xNCwxQTIuMTQsMi4xNCAwIDAsMCAxLDMuMTRWNUgzVjNINVYxSDMuMTRNNywxVjNIMTBWMUg3TTEyLDFWM0gxNFY1SDE2VjMuMTRDMTYsMS45NiAxNS4wNCwxIDEzLjg2LDFIMTJNMSw3VjEwSDNWN0gxTTksN0M3Ljg5LDcgNyw3Ljg5IDcsOUM3LDExLjMzIDcsMTYgNywxNkM3LDE2IDExLjU3LDE2IDEzLjg2LDE2QTIuMTQsMi4xNCAwIDAsMCAxNiwxMy44NkMxNiwxMS41NyAxNiw3IDE2LDdDMTYsNyAxMS4zMyw3IDksN00xOCw3VjlIMjBWMTFIMjJWOUMyMiw3Ljg5IDIxLjExLDcgMjAsN0gxOE05LDlIMTRWMTRIOVY5TTEsMTJWMTMuODZDMSwxNS4wNCAxLjk2LDE2IDMuMTQsMTZINVYxNEgzVjEySDFNMjAsMTNWMTZIMjJWMTNIMjBNNywxOFYyMEM3LDIxLjExIDcuODksMjIgOSwyMkgxMVYyMEg5VjE4SDdNMjAsMThWMjBIMThWMjJIMjBDMjEuMTEsMjIgMjIsMjEuMTEgMjIsMjBWMThIMjBNMTMsMjBWMjJIMTZWMjBIMTNaIiAvPjwvc3ZnPg==", + "name": "Collision3D", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/7a5696a515bf40813692e118147568392a854f65f5b50750c9b9aaa967aba7df_vector-intersection.svg", + "shortDescription": "Check collision and distance between 3D objects.", + "version": "0.1.0", + "description": "Check collision and distance between 3D objects and separate them from each other.", + "tags": [ + "3d", + "collision" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2", + "PEzPAaWHgYgk5UwIPppsL6f2ugp2" + ], + "dependencies": [], + "eventsFunctions": [ + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "if (gdjs._collision3DExtension) {", + " return;", + "}", + "", + "/**", + " * @param {gdjs.RuntimeObject} object", + " * @param {float} x", + " * @param {float} y", + " * @param {float} z", + " * @return {number}", + " */", + "const getSqDistanceToPosition = (object, x, y, z) => {", + " const deltaX = object.getCenterXInScene() - x;", + " const deltaY = object.getCenterYInScene() - y;", + " const deltaZ = (object.getCenterZInScene ? object.getCenterZInScene() : 0) - z;", + " return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;", + "};", + "", + "/**", + " * @param {gdjs.RuntimeObject} object", + " * @param {float} x", + " * @param {float} y", + " * @param {float} z", + " * @return {number}", + " */", + "const getDistanceToPosition = (object, x, y, z) => Math.sqrt(getSqDistanceToPosition(object, x, y, z));", + "", + "/**", + " * @param {gdjs.RuntimeObject} object", + " * @param {gdjs.RuntimeObject} otherObject", + " * @return {number}", + " */", + "const getSqDistanceToObject = (object, otherObject) => {", + " return getSqDistanceToPosition(", + " object,", + " otherObject.getCenterXInScene(),", + " otherObject.getCenterYInScene(),", + " otherObject.getCenterZInScene ? otherObject.getCenterZInScene() : 0", + " );", + "};", + "", + "/**", + " * @param {gdjs.RuntimeObject} object", + " * @param {gdjs.RuntimeObject} otherObject", + " * @return {number}", + " */", + "const getDistanceToObject = (object, otherObject) => Math.sqrt(getSqDistanceToObject(object, otherObject));", + "", + "/**", + " * @param {gdjs.RuntimeObject} object", + " * @param {gdjs.RuntimeObject} otherObject", + " * @param {number} distanceSq", + " * @return {boolean}", + " */", + "const areObjectsWithinDistance = (object, otherObject, distanceSq) => {", + " return getSqDistanceToObject(object, otherObject) <= distanceSq;", + "};", + "", + "/**", + " * @param {gdjs.ObjectList} objectsLists1", + " * @param {gdjs.ObjectList} objectsLists2", + " * @param {number} distance", + " * @param {boolean} inverted", + " * @return {boolean}", + " */", + "const pickObjectsWithinDistance = (objectsLists1, objectsLists2, distance, inverted) => {", + " const distanceSq = distance * distance;", + " return gdjs.evtTools.object.twoListsTest(", + " areObjectsWithinDistance,", + " objectsLists1,", + " objectsLists2,", + " inverted,", + " distance * distance", + " );", + "}", + "", + "/**", + " * @param {gdjs.ObjectList} objectsLists", + " * @param {gdjs.ObjectList} objectsLists2", + " * @param {number} x", + " * @param {number} y", + " * @param {number} z", + " * @param {boolean} inverted", + " * @return {boolean}", + " */", + "const pickNearestObject = function (objectsLists, x, y, z, inverted) {", + " let bestObject = null;", + " let best = 0;", + " let first = true;", + " const lists = gdjs.staticArray(gdjs._collision3DExtension.pickNearestObject);", + " objectsLists.values(lists);", + " for (let i = 0, len = lists.length; i < len; ++i) {", + " const list = lists[i];", + " for (let j = 0; j < list.length; ++j) {", + " const object = list[j];", + " const distance = getSqDistanceToPosition(object, x, y, z);", + " // @ts-ignore", + " if (first || (distance < best) ^ inverted) {", + " best = distance;", + " bestObject = object;", + " }", + " first = false;", + " }", + " }", + " if (bestObject) {", + " gdjs.evtTools.object.pickOnly(objectsLists, bestObject);", + " }", + " return !!bestObject;", + "}", + "", + "/**", + " * @param {gdjs.RuntimeObject} object", + " * @return {float}", + " */", + "const getObjectMinZ = (object) =>", + " object.getDrawableZ ? object.getDrawableZ() : 0", + "", + "/**", + " * @param {gdjs.RuntimeObject} object", + " * @return {float}", + " */", + "const getObjectMaxZ = (object) =>", + " object.getDrawableZ ? object.getDrawableZ() + object.getDepth() : 0;", + "", + "/**", + " * @param {gdjs.RuntimeObject} object1", + " * @param {gdjs.RuntimeObject} object2", + " * @param {boolean} ignoreTouchingEdges If true, then edges that are touching each other, without the hitbox polygons actually overlapping, won't be considered in collision.", + " * @return {boolean}", + " */", + "const areObjectsInCollision = (", + " object1,", + " object2,", + " ignoreTouchingEdges,", + ") => {", + " const object1MinZ = getObjectMinZ(object1);", + " const object1MaxZ = getObjectMaxZ(object1);", + " const object2MinZ = getObjectMinZ(object2);", + " const object2MaxZ = getObjectMaxZ(object2);", + "", + " const canObjectsOverlapOnZ = ignoreTouchingEdges ?", + " (object1MinZ < object2MaxZ && object1MaxZ > object2MinZ) :", + " (object1MinZ <= object2MaxZ && object1MaxZ >= object2MinZ);", + "", + " return canObjectsOverlapOnZ &&", + " gdjs.RuntimeObject.collisionTest(", + " object1,", + " object2,", + " ignoreTouchingEdges", + " );", + "}", + "", + "/**", + " * @param {gdjs.ObjectList} objectsLists", + " * @param {gdjs.ObjectList} objectsLists2", + " * @param {boolean} inverted", + " * @return {boolean}", + " */", + "const pickObjectsInCollision = (", + " objectsLists1,", + " objectsLists2,", + " inverted,", + ") => {", + " return gdjs.evtTools.object.twoListsTest(", + " areObjectsInCollision,", + " objectsLists1,", + " objectsLists2,", + " inverted,", + " );", + "}", + "", + "/**", + " * @param {gdjs.RuntimeObject} movingObject", + " * @param {gdjs.RuntimeObject} object", + " * @return {float}", + " */", + "const getSeparationDeltaZ = (", + " movingObject,", + " object,", + ") => {", + " const movingObjectMinZ = getObjectMinZ(movingObject);", + " const movingObjectMaxZ = getObjectMaxZ(movingObject);", + " const objectMinZ = getObjectMinZ(object);", + " const objectMaxZ = getObjectMaxZ(object);", + "", + " const downwardDeltaZ = Math.min(0, objectMinZ - movingObjectMaxZ);", + " const upwardDeltaZ = Math.max(0, objectMaxZ - movingObjectMinZ);", + "", + " return -downwardDeltaZ < upwardDeltaZ ? downwardDeltaZ : upwardDeltaZ", + "}", + "", + "// This function is an exact copy from GDJS/Runtime/runtimeobject.ts", + "/**", + " * Move the object using the results from collisionTest call.", + " * This moves the object according to the direction of the longest vector,", + " * and projects the others on the orthogonal vector.", + " *", + " * See {@link RuntimeObject.separateFromObjects}", + " *", + " * @param {gdjs.RuntimeObject} object The object to move.", + " * @param {float[]} moveXArray The X coordinates of the vectors to move the object.", + " * @param {float[]} moveYArray The Y coordinates of the vectors to move the object.", + " * @return {boolean} true if the object was moved.", + " */", + "const moveFollowingSeparatingVectors = (", + " object,", + " moveXArray,", + " moveYArray", + ") => {", + " if (moveXArray.length === 0) {", + " moveXArray.length = 0;", + " moveYArray.length = 0;", + " return false;", + " }", + " if (moveXArray.length === 1) {", + " // Move according to the results returned by the collision algorithm.", + " object.setPosition(", + " object.getX() + moveXArray[0],", + " object.getY() + moveYArray[0]", + " );", + " moveXArray.length = 0;", + " moveYArray.length = 0;", + " return true;", + " }", + "", + " // Find the longest vector", + " let squaredDistanceMax = 0;", + " let distanceMaxIndex = 0;", + " for (let index = 0; index < moveXArray.length; index++) {", + " const moveX = moveXArray[index];", + " const moveY = moveYArray[index];", + "", + " const squaredDistance = moveX * moveX + moveY * moveY;", + " if (squaredDistance > squaredDistanceMax) {", + " squaredDistanceMax = squaredDistance;", + " distanceMaxIndex = index;", + " }", + " }", + "", + " const distanceMax = Math.sqrt(squaredDistanceMax);", + " // unit vector of the longest vector", + " const uX = moveXArray[distanceMaxIndex] / distanceMax;", + " const uY = moveYArray[distanceMaxIndex] / distanceMax;", + "", + " // normal vector of the longest vector", + " const vX = -uY;", + " const vY = uX;", + "", + " // Project other vectors on the normal", + " let scalarProductMin = 0;", + " let scalarProductMax = 0;", + " for (let index = 0; index < moveXArray.length; index++) {", + " const moveX = moveXArray[index];", + " const moveY = moveYArray[index];", + "", + " const scalarProduct = moveX * vX + moveY * vY;", + " scalarProductMin = Math.min(scalarProductMin, scalarProduct);", + " scalarProductMax = Math.max(scalarProductMax, scalarProduct);", + " }", + "", + " // Apply the longest vector", + " let deltaX = moveXArray[distanceMaxIndex];", + " let deltaY = moveYArray[distanceMaxIndex];", + "", + " // Apply the longest projected vector if they all are in the same direction", + " // Some projections could have rounding errors,", + " // they are considered negligible under a 1 for 1,000,000 ratio.", + " const scalarProductMinIsNegligible =", + " -scalarProductMin < scalarProductMax / 1048576;", + " const scalarProductMaxIsNegligible =", + " scalarProductMax < -scalarProductMin / 1048576;", + " if (scalarProductMinIsNegligible !== scalarProductMaxIsNegligible) {", + " if (scalarProductMaxIsNegligible) {", + " deltaX += scalarProductMin * vX;", + " deltaY += scalarProductMin * vY;", + " } else {", + " deltaX += scalarProductMax * vX;", + " deltaY += scalarProductMax * vY;", + " }", + " }", + " object.setPosition(object.getX() + deltaX, object.getY() + deltaY);", + " moveXArray.length = 0;", + " moveYArray.length = 0;", + " return true;", + "};", + "", + "/**", + " * Arrays and data structure that are (re)used by", + " * {@link separateFromObjects} to avoid any allocation.", + " * @type {moveXArray: float[], moveYArray: float[], moveZArray: float[]}", + " */", + "const separateFromObjectsStatics = {", + " moveXArray: [],", + " moveYArray: [],", + " moveZArray: [],", + "};", + "", + "/**", + " * Separate the object from others objects, using their hitboxes.", + " * @param {gdjs.RuntimeObject} object The object that moves.", + " * @param {gdjs.RuntimeObject[]} objects The objects that stay still.", + " * @return {boolean} true if the object was moved", + " */", + "const separateFromObjects = (", + " object,", + " objects", + ") => {", + " const moveXArray = separateFromObjectsStatics.moveXArray;", + " const moveYArray = separateFromObjectsStatics.moveYArray;", + " moveXArray.length = 0;", + " moveYArray.length = 0;", + " let moveZMax = 0;", + "", + " // We can assume that the moving object is not grid based,", + " // so there is no need for optimization:", + " // getHitBoxes can be called directly.", + " const hitBoxes = object.getHitBoxes();", + " /** @type {gdjs.AABB | null} */", + " let aabb = null;", + "", + " // Check if there is a collision with each object", + " for (const otherObject of objects) {", + " if (otherObject.id === object.id) {", + " continue;", + " }", + " const moveZ = getSeparationDeltaZ(object, otherObject);", + " if (moveZ === 0) {", + " continue;", + " }", + " const moveZSq = moveZ * moveZ;", + "", + " let otherHitBoxesArray = otherObject.getHitBoxes();", + " /** @type {Iterable} */", + " let otherHitBoxes = otherHitBoxesArray;", + " if (otherHitBoxesArray.length > 4) {", + " // The other object has a lot of hit boxes.", + " // Try to reduce the amount of hitboxes to check.", + " if (!aabb) {", + " aabb = object.getAABB();", + " }", + " otherHitBoxes = otherObject.getHitBoxesAround(", + " aabb.min[0],", + " aabb.min[1],", + " aabb.max[0],", + " aabb.max[1]", + " );", + " }", + " for (const hitBox of hitBoxes) {", + " for (const otherHitBox of otherHitBoxes) {", + " const result = gdjs.Polygon.collisionTest(", + " hitBox,", + " otherHitBox,", + " true", + " );", + " if (result.collision) {", + " const moveX = result.move_axis[0];", + " const moveY = result.move_axis[1];", + " if (moveZSq < moveX * moveX + moveY * moveY) {", + " if (Math.abs(moveZ) > Math.abs(moveZMax)) {", + " moveZMax = moveZ;", + " }", + " }", + " else {", + " moveXArray.push(moveX);", + " moveYArray.push(moveY);", + " }", + " }", + " }", + " }", + " }", + " const hasObjectMovedOnZ = moveZMax !== 0;", + " if (hasObjectMovedOnZ) {", + " object.setZ(object.getZ() + moveZMax);", + " }", + " const hasObjectMovedOnXY = moveFollowingSeparatingVectors(object, moveXArray, moveYArray);", + " return hasObjectMovedOnXY || hasObjectMovedOnZ;", + "}", + "", + "gdjs._collision3DExtension = {", + " getSqDistanceToPosition,", + " getDistanceToPosition,", + " getSqDistanceToObject,", + " getDistanceToObject,", + " pickObjectsWithinDistance,", + " pickNearestObject,", + " pickObjectsInCollision,", + " separateFromObjects", + "}" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onFirstSceneLoaded", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Collision3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Compare the distance between two objects in 3D.", + "fullName": "Distance between two objects in 3D", + "functionType": "Condition", + "group": "Position", + "name": "AreWithinDistance", + "sentence": "_PARAM1_ distance to _PARAM2_ is below _PARAM3_ pixels", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "// TODO If condition is inverted, only objects that have a distance greater than specified to any other object will be picked.\r", + "const inverted = false;\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._collision3DExtension.pickObjectsWithinDistance(\r", + " eventsFunctionContext.getObjectsLists(\"Object\"),\r", + " eventsFunctionContext.getObjectsLists(\"OtherObject\"),\r", + " eventsFunctionContext.getArgument(\"Distance\"),\r", + " inverted\r", + ");\r", + "" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "objectList" + }, + { + "description": "Other object", + "name": "OtherObject", + "type": "objectList" + }, + { + "description": "Distance", + "name": "Distance", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Distance between two objects in 3D.", + "fullName": "Distance between two objects in 3D", + "functionType": "Expression", + "group": "Position", + "name": "Distance", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "const otherObjects = eventsFunctionContext.getObjectsLists(\"OtherObject\");\r", + "\r", + "if (objects.length > 0 && otherObjects.length > 0) {\r", + " eventsFunctionContext.returnValue = gdjs._collision3DExtension.getDistanceToObject(\r", + " objects[0],\r", + " otherObjects[0]\r", + " );\r", + "}\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "objectList" + }, + { + "description": "Other object", + "name": "OtherObject", + "type": "objectList" + } + ], + "objectGroups": [] + }, + { + "description": "Square distance between two objects in 3D.", + "fullName": "Square distance between two objects", + "functionType": "Expression", + "group": "Position", + "name": "SqDistance", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "const otherObjects = eventsFunctionContext.getObjectsLists(\"OtherObject\");\r", + "\r", + "if (objects.length > 0 && otherObjects.length > 0) {\r", + " eventsFunctionContext.returnValue = gdjs._collision3DExtension.getSqDistanceToObject(\r", + " objects[0],\r", + " otherObjects[0]\r", + " );\r", + "}\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "objectList" + }, + { + "description": "Other object", + "name": "OtherObject", + "type": "objectList" + } + ], + "objectGroups": [] + }, + { + "description": "Distance between an object and a position in 3D.", + "fullName": "Distance between an object and a position", + "functionType": "Expression", + "group": "Position", + "name": "DistanceToPosition", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "if (objects.length > 0) {\r", + " eventsFunctionContext.returnValue = gdjs._collision3DExtension.getSqDistanceToPosition(\r", + " objects[0],\r", + " eventsFunctionContext.getArgument(\"PositionX\"),\r", + " eventsFunctionContext.getArgument(\"PositionY\"),\r", + " eventsFunctionContext.getArgument(\"PositionZ\"),\r", + " );\r", + "}\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "objectList" + }, + { + "description": "X position", + "name": "PositionX", + "type": "expression" + }, + { + "description": "Y position", + "name": "PositionY", + "type": "expression" + }, + { + "description": "Z position", + "name": "PositionZ", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Square distance between an object and a position in 3D.", + "fullName": "Square distance between an object and a position", + "functionType": "Expression", + "group": "Position", + "name": "SqDistanceToPosition", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "if (objects.length > 0) {\r", + " eventsFunctionContext.returnValue = gdjs._collision3DExtension.getDistanceToPosition(\r", + " objects[0],\r", + " eventsFunctionContext.getArgument(\"PositionX\"),\r", + " eventsFunctionContext.getArgument(\"PositionY\"),\r", + " eventsFunctionContext.getArgument(\"PositionZ\"),\r", + " );\r", + "}\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "objectList" + }, + { + "description": "X position", + "name": "PositionX", + "type": "expression" + }, + { + "description": "Y position", + "name": "PositionY", + "type": "expression" + }, + { + "description": "Z position", + "name": "PositionZ", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Pick the object of this type that is nearest to the specified position in 3D.", + "fullName": "Pick nearest object in 3D", + "functionType": "Condition", + "group": "Objects", + "name": "PickNearest", + "sentence": "Pick the _PARAM1_ that is nearest to _PARAM2_ ; _PARAM3_ ; _PARAM4_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "// TODO If the condition is inverted, the object farthest from the specified position is picked instead.\r", + "const inverted = false;\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._collision3DExtension.pickNearestObject(\r", + " eventsFunctionContext.getObjectsLists(\"Object\"),\r", + " eventsFunctionContext.getArgument(\"PositionX\"),\r", + " eventsFunctionContext.getArgument(\"PositionY\"),\r", + " eventsFunctionContext.getArgument(\"PositionZ\"),\r", + " inverted\r", + ");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "objectList" + }, + { + "description": "X position", + "name": "PositionX", + "type": "expression" + }, + { + "description": "Y position", + "name": "PositionY", + "type": "expression" + }, + { + "description": "Z position", + "name": "PositionZ", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Check the collision between two objects using their collision masks. Object rotation around X and Y axes are ignored.", + "fullName": "Collision in 3D", + "functionType": "Condition", + "group": "Collision", + "name": "AreInCollision", + "sentence": "_PARAM1_ is in collision with _PARAM3_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "// TODO If the condition is inverted, the object farthest from the specified position is picked instead.\r", + "const inverted = false;\r", + "\r", + "eventsFunctionContext.returnValue = gdjs._collision3DExtension.pickObjectsInCollision(\r", + " eventsFunctionContext.getObjectsLists(\"Object\"),\r", + " eventsFunctionContext.getObjectsLists(\"OtherObject\"),\r", + " eventsFunctionContext.getArgument(\"IgnoreEdges\"),\r", + " inverted\r", + ");" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "objectList" + }, + { + "description": "3D capability", + "name": "Object3D", + "supplementaryInformation": "Scene3D::Base3DBehavior", + "type": "behavior" + }, + { + "description": "Other object", + "name": "OtherObject", + "type": "objectList" + }, + { + "description": "3D capability", + "name": "OtherObjectCapability", + "supplementaryInformation": "Scene3D::Base3DBehavior", + "type": "behavior" + }, + { + "description": "Ignore objects that are touching each other on their edges, but are not overlapping (default: no)", + "name": "IgnoreEdges", + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "Move an object away from another using their collision masks. Object rotation around X and Y axes are ignored.\nBe sure to call this action on a reasonable number of objects to avoid slowing down the game.", + "fullName": "Separate objects in 3D", + "functionType": "Action", + "group": "Position", + "name": "SeparateFromObjects", + "sentence": "Move _PARAM1_ away from _PARAM3_ (only _PARAM1_ will move)", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "\r", + "const otherObjects = eventsFunctionContext.getObjects(\"OtherObject\");\r", + "\r", + "for (const object of objects) {\r", + " gdjs._collision3DExtension.separateFromObjects(\r", + " object,\r", + " otherObjects,\r", + " );\r", + "}" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "type": "objectList" + }, + { + "description": "3D capability", + "name": "Object3D", + "supplementaryInformation": "Scene3D::Base3DBehavior", + "type": "behavior" + }, + { + "description": "Objects (won't move)", + "name": "OtherObject", + "type": "objectList" + }, + { + "description": "3D capability", + "name": "OtherObject3D", + "supplementaryInformation": "Scene3D::Base3DBehavior", + "type": "behavior" + } + ], + "objectGroups": [] + } + ], + "eventsBasedBehaviors": [], + "eventsBasedObjects": [] +} \ No newline at end of file diff --git a/scripts/lib/ExtensionsValidatorExceptions.js b/scripts/lib/ExtensionsValidatorExceptions.js index 53357481..02ed5232 100644 --- a/scripts/lib/ExtensionsValidatorExceptions.js +++ b/scripts/lib/ExtensionsValidatorExceptions.js @@ -177,6 +177,17 @@ const extensionsAllowedProperties = { runtimeSceneAllowedProperties: [], javaScriptObjectAllowedProperties: [], }, + Collision3D: { + gdjsAllowedProperties: [ + '_collision3DExtension', + 'ObjectList', + 'AABB', + 'Polygon', + ], + gdjsEvtToolsAllowedProperties: ['object'], + runtimeSceneAllowedProperties: [], + javaScriptObjectAllowedProperties: [], + }, Compressor: { gdjsAllowedProperties: ['_pakoTools'], gdjsEvtToolsAllowedProperties: [],