Skip to content

Commit e012fbe

Browse files
authored
[Reviewed] [3D particles] Fix a crash when there is no animation nor frame (#1727)
1 parent 10c235b commit e012fbe

2 files changed

Lines changed: 213 additions & 26 deletions

File tree

extensions/reviewed/ParticleEmitter3D.json

Lines changed: 212 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"name": "ParticleEmitter3D",
1010
"previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/f2e5a34bf465f781866677762d385d6c8e9e8d203383f2df9a3b7e0fad6a2cb5_fire.svg",
1111
"shortDescription": "Display a large number of particles in 3D to create visual effects in a 3D game.",
12-
"version": "2.1.1",
12+
"version": "2.2.0",
1313
"description": [
1414
"3D particle emitters let you create and display many small particles to simulate visual effects in your game — like fire, explosions, smoke, or dust.",
1515
"",
@@ -62,6 +62,8 @@
6262
" return;",
6363
"}",
6464
"",
65+
"const game = runtimeScene.getGame();",
66+
"",
6567
"class ParticleEmitter3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {",
6668
" constructor(",
6769
" object,",
@@ -88,6 +90,7 @@
8890
" this._object.getZ()",
8991
" );",
9092
"",
93+
" // Force the scale to 1 because the particle emitter doesn't really has a size.",
9194
" threeObject3D.scale.set(",
9295
" this._object.isFlippedX() ? -1 : 1,",
9396
" this._object.isFlippedY() ? -1 : 1,",
@@ -100,6 +103,89 @@
100103
" }",
101104
"}",
102105
"",
106+
"const coneLength = 64;",
107+
"",
108+
"class ParticleEmitterHelper extends THREE.Object3D {",
109+
" /** @type {gdjs.CustomRuntimeObject3D} */",
110+
" object;",
111+
" /** @type {THREE.LineSegments} */",
112+
" cone;",
113+
" /** @type {THREE.LineSegments} */",
114+
" centerLine;",
115+
" /** @type {THREE.Mesh} */",
116+
" originBox;",
117+
"",
118+
"\t/**",
119+
" * @param gdjs.CustomRuntimeObject3D object",
120+
"\t */",
121+
" constructor(object) {",
122+
" super();",
123+
" this.object = object;",
124+
" this.type = 'ParticleEmitterHelper';",
125+
"",
126+
" const coneGeometry = new THREE.BufferGeometry();",
127+
" const conePositions = [",
128+
" 0, 0, 0, 0, 0, 1,",
129+
" 0, 0, 0, 1, 0, 1,",
130+
" 0, 0, 0, - 1, 0, 1,",
131+
" 0, 0, 0, 0, 1, 1,",
132+
" 0, 0, 0, 0, - 1, 1",
133+
" ];",
134+
" for (let i = 0, j = 1, l = 32; i < l; i++ , j++) {",
135+
" const p1 = (i / l) * Math.PI * 2;",
136+
" const p2 = (j / l) * Math.PI * 2;",
137+
" conePositions.push(",
138+
" Math.cos(p1), Math.sin(p1), 1,",
139+
" Math.cos(p2), Math.sin(p2), 1",
140+
" );",
141+
" }",
142+
" coneGeometry.setAttribute('position', new THREE.Float32BufferAttribute(conePositions, 3));",
143+
" this.cone = new THREE.LineSegments(",
144+
" coneGeometry,",
145+
" new THREE.LineBasicMaterial({ fog: false, toneMapped: false })",
146+
" );",
147+
" this.add(this.cone);",
148+
" const centerLineGeometry = new THREE.BufferGeometry();",
149+
" centerLineGeometry.setAttribute('position', new THREE.Float32BufferAttribute([",
150+
" 0, 0, 0, 0, 0, coneLength",
151+
" ], 3));",
152+
" this.centerLine = new THREE.LineSegments(",
153+
" centerLineGeometry,",
154+
" new THREE.LineBasicMaterial({ fog: false, toneMapped: false })",
155+
" );",
156+
" this.add(this.centerLine);",
157+
"",
158+
" this.originBox = new THREE.Mesh(",
159+
" new THREE.BoxGeometry(16, 16, 16),",
160+
" new THREE.MeshBasicMaterial({ fog: false, toneMapped: false })",
161+
" );",
162+
" this.add(this.originBox);",
163+
" this.update();",
164+
"",
165+
" this.originBox.gdjsRuntimeObject = object;",
166+
" }",
167+
"",
168+
" dispose() {",
169+
" this.cone.geometry.dispose();",
170+
" this.cone.material.dispose();",
171+
" this.centerLine.geometry.dispose();",
172+
" this.centerLine.material.dispose();",
173+
" this.originBox.geometry.dispose();",
174+
" this.originBox.material.dispose();",
175+
" }",
176+
"",
177+
" update() {",
178+
" const coneWidth = coneLength * Math.sin(gdjs.toRad(this.object._getSpayConeAngle()));",
179+
" const coneHeight = coneLength * Math.cos(gdjs.toRad(this.object._getSpayConeAngle()));",
180+
" this.cone.scale.set(coneWidth, coneWidth, coneHeight);",
181+
" const startColor = gdjs.rgbOrHexStringToNumber(this.object._getStartColor())",
182+
" const endColor = gdjs.rgbOrHexStringToNumber(this.object._getEndColor());",
183+
" this.cone.material.color.set(endColor);",
184+
" this.centerLine.material.color.set(startColor);",
185+
" this.originBox.material.color.set(startColor);",
186+
" }",
187+
"}",
188+
"",
103189
"/**",
104190
" * @param {string} colorString",
105191
" * @param {THREE.Vector4} threeColor",
@@ -290,6 +376,11 @@
290376
" break;",
291377
" }",
292378
" }",
379+
"",
380+
" setImage(resourceName) {",
381+
" const texture = game.getImageManager().getThreeTexture(resourceName);",
382+
" this.particleSystem.texture = texture;",
383+
" }",
293384
"}",
294385
"",
295386
"",
@@ -6666,6 +6757,7 @@
66666757
"gdjs.__particleEmmiter3DExtension = {",
66676758
" ParticleEmitter3DRenderer,",
66686759
" ParticleEmitterAdapter,",
6760+
" ParticleEmitterHelper,",
66696761
"",
66706762
" ApplyCollision,",
66716763
" ApplyForce,",
@@ -6879,37 +6971,34 @@
68796971
{
68806972
"type": "BuiltinCommonInstructions::JsCode",
68816973
"inlineCode": [
6882-
"const BatchedRenderer = gdjs.__particleEmmiter3DExtension.BatchedRenderer;",
6883-
"const ParticleSystem = gdjs.__particleEmmiter3DExtension.ParticleSystem;",
6884-
"const TextureLoader = gdjs.__particleEmmiter3DExtension.TextureLoader;",
6885-
"const IntervalValue = gdjs.__particleEmmiter3DExtension.IntervalValue;",
6886-
"const ConstantValue = gdjs.__particleEmmiter3DExtension.ConstantValue;",
6887-
"const ConstantColor = gdjs.__particleEmmiter3DExtension.ConstantColor;",
6888-
"const ColorOverLife = gdjs.__particleEmmiter3DExtension.ColorOverLife;",
6889-
"const SizeOverLife = gdjs.__particleEmmiter3DExtension.SizeOverLife;",
6890-
"const ApplyForce = gdjs.__particleEmmiter3DExtension.ApplyForce;",
6891-
"const Gradient = gdjs.__particleEmmiter3DExtension.Gradient;",
6892-
"const PiecewiseBezier = gdjs.__particleEmmiter3DExtension.PiecewiseBezier;",
6893-
"const Bezier = gdjs.__particleEmmiter3DExtension.Bezier;",
6894-
"const PointEmitter = gdjs.__particleEmmiter3DExtension.PointEmitter;",
6895-
"const ConeEmitter = gdjs.__particleEmmiter3DExtension.ConeEmitter;",
6896-
"const RenderMode = gdjs.__particleEmmiter3DExtension.RenderMode;",
6974+
"const {",
6975+
" ParticleEmitterAdapter,",
6976+
" ParticleEmitter3DRenderer,",
6977+
" ParticleEmitterHelper,",
68976978
"",
6898-
"const { ParticleEmitterAdapter, ParticleEmitter3DRenderer } = gdjs.__particleEmmiter3DExtension;",
6979+
" ParticleSystem,",
6980+
" IntervalValue,",
6981+
" ConstantValue,",
6982+
" ConstantColor,",
6983+
" ColorOverLife,",
6984+
" SizeOverLife,",
6985+
" ApplyForce,",
6986+
" Gradient,",
6987+
" PiecewiseBezier,",
6988+
" Bezier,",
6989+
" PointEmitter,",
6990+
" ConeEmitter,",
6991+
" RenderMode",
6992+
"} = gdjs.__particleEmmiter3DExtension;",
68996993
"",
69006994
"/** @type {gdjs.CustomRuntimeObject} */",
69016995
"const object = objects[0];",
69026996
"",
69036997
"// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.",
69046998
"const gameScene = object.getRuntimeScene();",
6999+
"const game = runtimeScene.getGame();",
69057000
"",
6906-
"/** @type {SpriteObjectDataType} */",
6907-
"const particleSpriteData = object._instanceContainer._objects.get(\"Particle\");",
6908-
"const resourceName = particleSpriteData.animations[0].directions[0].sprites[0].image;",
6909-
"const texture = object",
6910-
" .getInstanceContainer()",
6911-
" .getGame()",
6912-
" .getImageManager().getThreeTexture(resourceName);",
7001+
"const texture = game.getImageManager().getThreeTexture('');",
69137002
"",
69147003
"// Set the blending here because changes are not applied after the emitter creation.",
69157004
"const blendingString = object._getBlending();",
@@ -6991,8 +7080,15 @@
69917080
"",
69927081
"const particleEmitter3DRenderer = new ParticleEmitter3DRenderer(object, object._instanceContainer, object.getInstanceContainer());",
69937082
"object._renderer = particleEmitter3DRenderer;",
6994-
"particleEmitter3DRenderer._threeGroup = particleSystem.emitter;",
6995-
"layer.getRenderer().add3DRendererObject(particleSystem.emitter);",
7083+
"if (game.isInGameEdition && game.isInGameEdition()) {",
7084+
" const particleEmitterHelper = new ParticleEmitterHelper(object);",
7085+
" particleEmitter3DRenderer._threeGroup = particleEmitterHelper;",
7086+
" layer.getRenderer().add3DRendererObject(particleEmitterHelper);",
7087+
"}",
7088+
"else {",
7089+
" particleEmitter3DRenderer._threeGroup = particleSystem.emitter;",
7090+
" layer.getRenderer().add3DRendererObject(particleSystem.emitter);",
7091+
"}",
69967092
"",
69977093
"particleSystem.emitter.updateMatrixWorld(true);",
69987094
"",
@@ -7344,6 +7440,15 @@
73447440
"Object.GravityTop()",
73457441
""
73467442
]
7443+
},
7444+
{
7445+
"type": {
7446+
"value": "ParticleEmitter3D::ParticleEmitter3D::UpdateImage"
7447+
},
7448+
"parameters": [
7449+
"Object",
7450+
""
7451+
]
73477452
}
73487453
],
73497454
"events": [
@@ -7433,6 +7538,87 @@
74337538
]
74347539
}
74357540
]
7541+
},
7542+
{
7543+
"type": "BuiltinCommonInstructions::Standard",
7544+
"conditions": [],
7545+
"actions": [
7546+
{
7547+
"type": {
7548+
"value": "ParticleEmitter3D::ParticleEmitter3D::UpdateHelper"
7549+
},
7550+
"parameters": [
7551+
"Object",
7552+
""
7553+
]
7554+
}
7555+
]
7556+
}
7557+
],
7558+
"parameters": [
7559+
{
7560+
"description": "Object",
7561+
"name": "Object",
7562+
"supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
7563+
"type": "object"
7564+
}
7565+
],
7566+
"objectGroups": []
7567+
},
7568+
{
7569+
"fullName": "Update helper",
7570+
"functionType": "Action",
7571+
"name": "UpdateHelper",
7572+
"private": true,
7573+
"sentence": "Update graphical helper of _PARAM0_",
7574+
"events": [
7575+
{
7576+
"type": "BuiltinCommonInstructions::JsCode",
7577+
"inlineCode": [
7578+
"const game = runtimeScene.getGame();",
7579+
"if (game.isInGameEdition && game.isInGameEdition()) {",
7580+
" const particleEmitterHelper = objects[0].get3DRendererObject();",
7581+
" particleEmitterHelper.update();",
7582+
"}"
7583+
],
7584+
"parameterObjects": "Object",
7585+
"useStrict": true,
7586+
"eventsSheetExpanded": false
7587+
}
7588+
],
7589+
"parameters": [
7590+
{
7591+
"description": "Object",
7592+
"name": "Object",
7593+
"supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
7594+
"type": "object"
7595+
}
7596+
],
7597+
"objectGroups": []
7598+
},
7599+
{
7600+
"fullName": "Update particle image",
7601+
"functionType": "Action",
7602+
"name": "UpdateImage",
7603+
"private": true,
7604+
"sentence": "Update particle image of _PARAM0_",
7605+
"events": [
7606+
{
7607+
"type": "BuiltinCommonInstructions::JsCode",
7608+
"inlineCode": [
7609+
"/** @type {gdjs.CustomRuntimeObject3D} */\r",
7610+
"const object = objects[0];\r",
7611+
"/** @type {SpriteObjectDataType} */\r",
7612+
"const particleSpriteData = object._instanceContainer._objects.get(\"Particle\");\r",
7613+
"/** @type {gdjs.SpriteAnimationData} */\r",
7614+
"const animation = particleSpriteData.animations[0];\r",
7615+
"const animationFrame = animation ? animation.directions[0].sprites[0] : null;\r",
7616+
"const resourceName = animationFrame ? animationFrame.image : '';\r",
7617+
"object.__particleEmitterAdapter.setImage(resourceName);"
7618+
],
7619+
"parameterObjects": "Object",
7620+
"useStrict": true,
7621+
"eventsSheetExpanded": true
74367622
}
74377623
],
74387624
"parameters": [

scripts/lib/ExtensionsValidatorExceptions.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ const extensionsAllowedProperties = {
389389
'CustomRuntimeObjectInstanceContainer',
390390
'CustomRuntimeObject3DRenderer',
391391
'CustomRuntimeObject3D',
392+
'SpriteAnimationData',
392393
],
393394
gdjsEvtToolsAllowedProperties: [],
394395
runtimeSceneAllowedProperties: ['__particleEmmiter3DExtension'],

0 commit comments

Comments
 (0)