diff --git a/Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts b/Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts index 9bdc14d9f285..351042ec6da3 100644 --- a/Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts +++ b/Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts @@ -409,31 +409,7 @@ namespace gdjs { this.collisionChecker = new gdjs.Physics3DRuntimeBehavior.DefaultCollisionChecker(this); this.owner3D = owner; - this.bodyType = behaviorData.bodyType; - this.bullet = behaviorData.bullet; - this.fixedRotation = behaviorData.fixedRotation; - this._shape = behaviorData.shape; - this.meshShapeResourceName = behaviorData.meshShapeResourceName || ''; - this.shapeOrientation = - behaviorData.shape === 'Box' ? 'Z' : behaviorData.shapeOrientation; - this.shapeDimensionA = behaviorData.shapeDimensionA; - this.shapeDimensionB = behaviorData.shapeDimensionB; - this.shapeDimensionC = behaviorData.shapeDimensionC; - this.shapeOffsetX = behaviorData.shapeOffsetX || 0; - this.shapeOffsetY = behaviorData.shapeOffsetY || 0; - this.shapeOffsetZ = behaviorData.shapeOffsetZ || 0; - this.massCenterOffsetX = behaviorData.massCenterOffsetX || 0; - this.massCenterOffsetY = behaviorData.massCenterOffsetY || 0; - this.massCenterOffsetZ = behaviorData.massCenterOffsetZ || 0; - this.density = Math.max(0.0001, behaviorData.density); - this.massOverride = behaviorData.massOverride || 0; - this.friction = behaviorData.friction; - this.restitution = behaviorData.restitution; - this.linearDamping = Math.max(0, behaviorData.linearDamping); - this.angularDamping = Math.max(0, behaviorData.angularDamping); - this.gravityScale = behaviorData.gravityScale; - this.layers = behaviorData.layers; - this.masks = behaviorData.masks; + this._applyBehaviorData(behaviorData); this._sharedData = Physics3DSharedData.getSharedData( instanceContainer.getScene(), behaviorData.name @@ -459,59 +435,6 @@ namespace gdjs { return tempQuat; } - override applyBehaviorOverriding(behaviorData): boolean { - if (behaviorData.bullet !== undefined) { - this.setBullet(behaviorData.bullet); - } - if (behaviorData.fixedRotation !== undefined) { - this.setFixedRotation(behaviorData.fixedRotation); - } - if (behaviorData.shapeDimensionA !== undefined) { - this.shapeDimensionA = behaviorData.shapeDimensionA; - this._needToRecreateShape = true; - } - if (behaviorData.shapeDimensionB !== undefined) { - this.shapeDimensionB = behaviorData.shapeDimensionB; - this._needToRecreateShape = true; - } - if (behaviorData.density !== undefined) { - this.setDensity(behaviorData.density); - } - if (behaviorData.friction !== undefined) { - this.setFriction(behaviorData.friction); - } - if (behaviorData.restitution !== undefined) { - this.setRestitution(behaviorData.restitution); - } - if (behaviorData.linearDamping !== undefined) { - this.setLinearDamping(behaviorData.linearDamping); - } - if (behaviorData.angularDamping !== undefined) { - this.setAngularDamping(behaviorData.angularDamping); - } - if (behaviorData.gravityScale !== undefined) { - this.setGravityScale(behaviorData.gravityScale); - } - - // TODO: make these properties updatable. - if (behaviorData.layers !== undefined) { - return false; - } - if (behaviorData.masks !== undefined) { - return false; - } - if (behaviorData.vertices !== undefined) { - return false; - } - if (behaviorData.bodyType !== undefined) { - return false; - } - if (behaviorData.shape !== undefined) { - return false; - } - return true; - } - override getNetworkSyncData( options: GetNetworkSyncDataOptions ): Physics3DNetworkSyncData { @@ -1131,6 +1054,77 @@ namespace gdjs { this._sharedData.stepped = false; } + private _applyBehaviorData(data: any): void { + if ('bodyType' in data) this.bodyType = data.bodyType; + if ('bullet' in data) this.bullet = data.bullet; + if ('fixedRotation' in data) this.fixedRotation = data.fixedRotation; + + if ('shape' in data) { + this._shape = data.shape; + const orientation = + 'shapeOrientation' in data + ? data.shapeOrientation + : this.shapeOrientation; + this.shapeOrientation = data.shape === 'Box' ? 'Z' : orientation; + } else if ('shapeOrientation' in data) { + this.shapeOrientation = + this._shape === 'Box' ? 'Z' : data.shapeOrientation; + } + + if ('meshShapeResourceName' in data) + this.meshShapeResourceName = data.meshShapeResourceName || ''; + if ('shapeDimensionA' in data) + this.shapeDimensionA = data.shapeDimensionA; + if ('shapeDimensionB' in data) + this.shapeDimensionB = data.shapeDimensionB; + if ('shapeDimensionC' in data) + this.shapeDimensionC = data.shapeDimensionC; + if ('shapeOffsetX' in data) this.shapeOffsetX = data.shapeOffsetX || 0; + if ('shapeOffsetY' in data) this.shapeOffsetY = data.shapeOffsetY || 0; + if ('shapeOffsetZ' in data) this.shapeOffsetZ = data.shapeOffsetZ || 0; + if ('massCenterOffsetX' in data) + this.massCenterOffsetX = data.massCenterOffsetX || 0; + if ('massCenterOffsetY' in data) + this.massCenterOffsetY = data.massCenterOffsetY || 0; + if ('massCenterOffsetZ' in data) + this.massCenterOffsetZ = data.massCenterOffsetZ || 0; + if ('density' in data) this.density = Math.max(0.0001, data.density); + if ('massOverride' in data) this.massOverride = data.massOverride || 0; + if ('friction' in data) this.friction = data.friction; + if ('restitution' in data) this.restitution = data.restitution; + if ('linearDamping' in data) + this.linearDamping = Math.max(0, data.linearDamping); + if ('angularDamping' in data) + this.angularDamping = Math.max(0, data.angularDamping); + if ('gravityScale' in data) this.gravityScale = data.gravityScale; + if ('layers' in data) this.layers = data.layers; + if ('masks' in data) this.masks = data.masks; + } + + override applyBehaviorOverriding(behaviorData): boolean { + this._applyBehaviorData(behaviorData); + + // Recreate the body if any shape-related property changed. + const shapeChanged = + 'shape' in behaviorData || + 'shapeOrientation' in behaviorData || + 'shapeDimensionA' in behaviorData || + 'shapeDimensionB' in behaviorData || + 'shapeDimensionC' in behaviorData || + 'shapeOffsetX' in behaviorData || + 'shapeOffsetY' in behaviorData || + 'shapeOffsetZ' in behaviorData || + 'massCenterOffsetX' in behaviorData || + 'massCenterOffsetY' in behaviorData || + 'massCenterOffsetZ' in behaviorData || + 'meshShapeResourceName' in behaviorData || + 'bodyType' in behaviorData; + if (shapeChanged) { + this.recreateBody(); + } + return true; + } + onObjectHotReloaded() { this.updateBodyFromObject(); } diff --git a/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/index.js b/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/index.js index a1cbafbd48a6..5efd333aea37 100644 --- a/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/index.js +++ b/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/index.js @@ -24,6 +24,8 @@ import { ColumnStackLayout, LineStackLayout } from '../../UI/Layout'; import { IconContainer } from '../../UI/IconContainer'; import RemoveIcon from '../../UI/CustomSvgIcons/Remove'; import useForceUpdate from '../../Utils/UseForceUpdate'; +import { useDebounce } from '../../Utils/UseDebounce'; +import { useIsMounted } from '../../Utils/UseIsMounted'; import ChevronArrowRight from '../../UI/CustomSvgIcons/ChevronArrowRight'; import ChevronArrowBottom from '../../UI/CustomSvgIcons/ChevronArrowBottom'; import ChevronArrowDownWithRoundedBorder from '../../UI/CustomSvgIcons/ChevronArrowDownWithRoundedBorder'; @@ -315,6 +317,15 @@ export const CompactObjectPropertiesEditor = ({ isBehaviorListLocked, }: Props): React.Node => { const forceUpdate = useForceUpdate(); + const isMounted = useIsMounted(); + // Debounced to avoid one hot reload per keystroke on fields. + const debouncedNotifyBehaviorUpdated = useDebounce( + (objectToNotify: gdObject) => { + if (!isMounted.current) return; + onObjectsModified([objectToNotify]); + }, + 250 + ); const [isPropertiesFolded, setIsPropertiesFolded] = React.useState(false); const [isBehaviorsFolded, setIsBehaviorsFolded] = React.useState(false); const [isVariablesFolded, setIsVariablesFolded] = React.useState(false); @@ -826,8 +837,10 @@ export const CompactObjectPropertiesEditor = ({ behaviorOverriding={null} initialInstance={null} object={object} + onBehaviorUpdated={() => + debouncedNotifyBehaviorUpdated(object) + } layersContainer={layersContainer} - onBehaviorUpdated={() => {}} resourceManagementProps={resourceManagementProps} onOpenFullEditor={() => onEditObject(object, 'behaviors')