Skip to content

Commit

Permalink
Allow 3D physics characters to push each other (#7292)
Browse files Browse the repository at this point in the history
  • Loading branch information
D8H authored Jan 15, 2025
1 parent b04c15f commit 2ce35a1
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 7 deletions.
24 changes: 24 additions & 0 deletions Extensions/Physics3DBehavior/JsExtension.js
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,13 @@ module.exports = {
return true;
}

if (propertyName === 'canBePushed') {
behaviorContent
.getChild('canBePushed')
.setBoolValue(newValue === '1');
return true;
}

return false;
};
behavior.getProperties = function (behaviorContent) {
Expand Down Expand Up @@ -1745,6 +1752,22 @@ module.exports = {
.setAdvanced(true)
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);

if (!behaviorContent.hasChild('canBePushed')) {
behaviorContent.addChild('canBePushed').setBoolValue(true);
}
behaviorProperties
.getOrCreate('canBePushed')
.setLabel('Can be pushed by other characters')
.setGroup(_('Walk'))
.setType('Boolean')
.setValue(
behaviorContent.getChild('canBePushed').getBoolValue()
? 'true'
: 'false'
)
.setAdvanced(true)
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);

return behaviorProperties;
};

Expand All @@ -1765,6 +1788,7 @@ module.exports = {
behaviorContent
.addChild('shouldBindObjectAndForwardAngle')
.setBoolValue(true);
behaviorContent.addChild('canBePushed').setBoolValue(true);
};

const aut = extension
Expand Down
30 changes: 24 additions & 6 deletions Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,15 +807,25 @@ namespace gdjs {
*/
getBodyLayer(): number {
return Jolt.ObjectLayerPairFilterMask.prototype.sGetObjectLayer(
// Make sure objects don't register in the wrong layer group.
this.isStatic()
? this.layers & gdjs.Physics3DSharedData.staticLayersMask
: this.layers & gdjs.Physics3DSharedData.dynamicLayersMask,
// Static objects accept all collisions as it's the mask of dynamic objects that matters.
this.isStatic() ? gdjs.Physics3DSharedData.allLayersMask : this.masks
this.getLayersAccordingToBodyType(),
this.getMasksAccordingToBodyType()
);
}

private getLayersAccordingToBodyType(): integer {
// Make sure objects don't register in the wrong layer group.
return this.isStatic()
? this.layers & gdjs.Physics3DSharedData.staticLayersMask
: this.layers & gdjs.Physics3DSharedData.dynamicLayersMask;
}

private getMasksAccordingToBodyType(): integer {
// Static objects accept all collisions as it's the mask of dynamic objects that matters.
return this.isStatic()
? gdjs.Physics3DSharedData.allLayersMask
: this.masks;
}

doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
// Step the world if not done this frame yet.
// Don't step at the first frame to allow events to handle overlapping objects.
Expand Down Expand Up @@ -1689,6 +1699,14 @@ namespace gdjs {
}
}

canCollideAgainst(otherBehavior: gdjs.Physics3DRuntimeBehavior): boolean {
return (
(this.getMasksAccordingToBodyType() &
otherBehavior.getLayersAccordingToBodyType()) !==
0
);
}

static areObjectsColliding(
object1: gdjs.RuntimeObject,
object2: gdjs.RuntimeObject,
Expand Down
115 changes: 114 additions & 1 deletion Extensions/Physics3DBehavior/PhysicsCharacter3DRuntimeBehavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ namespace gdjs {
private _jumpSpeed: float;
private _jumpSustainTime: float;
private _stairHeightMax: float;
_canBePushed: boolean;

private _hasPressedForwardKey: boolean = false;
private _hasPressedBackwardKey: boolean = false;
Expand Down Expand Up @@ -145,6 +146,10 @@ namespace gdjs {
behaviorData.stairHeightMax === undefined
? 20
: behaviorData.stairHeightMax;
this._canBePushed =
behaviorData.canBePushed === undefined
? true
: behaviorData.canBePushed;
}

private getVec3(x: float, y: float, z: float): Jolt.Vec3 {
Expand Down Expand Up @@ -1324,6 +1329,8 @@ namespace gdjs {
export namespace PhysicsCharacter3DRuntimeBehavior {
export class CharacterBodyUpdater {
characterBehavior: gdjs.PhysicsCharacter3DRuntimeBehavior;
/** Handle collisions between characters that can push each other. */
static characterVsCharacterCollision: Jolt.CharacterVsCharacterCollisionSimple | null = null;

constructor(characterBehavior: gdjs.PhysicsCharacter3DRuntimeBehavior) {
this.characterBehavior = characterBehavior;
Expand All @@ -1336,7 +1343,13 @@ namespace gdjs {
const shape = behavior.createShape();

const settings = new Jolt.CharacterVirtualSettings();
settings.mInnerBodyLayer = behavior.getBodyLayer();
// Characters innerBody are Kinematic body, they don't allow other
// characters to push them.
// The layer 0 doesn't allow any collision as masking them always result to 0.
// This allows CharacterVsCharacterCollisionSimple to handle the collisions.
settings.mInnerBodyLayer = this.characterBehavior._canBePushed
? 0
: behavior.getBodyLayer();
settings.mInnerBodyShape = shape;
settings.mMass = shape.GetMassProperties().get_mMass();
settings.mMaxSlopeAngle = gdjs.toRad(_slopeMaxAngle);
Expand Down Expand Up @@ -1376,6 +1389,106 @@ namespace gdjs {
.GetBodyLockInterface()
.TryGetBody(character.GetInnerBodyID());
this.characterBehavior.character = character;

if (this.characterBehavior._canBePushed) {
// CharacterVsCharacterCollisionSimple handle characters pushing each other.
let characterVsCharacterCollision =
CharacterBodyUpdater.characterVsCharacterCollision;
if (!characterVsCharacterCollision) {
characterVsCharacterCollision = new Jolt.CharacterVsCharacterCollisionSimple();
CharacterBodyUpdater.characterVsCharacterCollision = characterVsCharacterCollision;
}
characterVsCharacterCollision.Add(character);
character.SetCharacterVsCharacterCollision(
characterVsCharacterCollision
);

const characterContactListener = new Jolt.CharacterContactListenerJS();
characterContactListener.OnAdjustBodyVelocity = (
character,
body2Ptr,
linearVelocityPtr,
angularVelocity
) => {};
characterContactListener.OnContactValidate = (
character,
bodyID2,
subShapeID2
) => {
return true;
};
characterContactListener.OnCharacterContactValidate = (
characterPtr,
otherCharacterPtr,
subShapeID2
) => {
// CharacterVsCharacterCollisionSimple doesn't handle collision layers.
// We have to filter characters ourself.
const character = Jolt.wrapPointer(
characterPtr,
Jolt.CharacterVirtual
);
const otherCharacter = Jolt.wrapPointer(
otherCharacterPtr,
Jolt.CharacterVirtual
);

const body = _sharedData.physicsSystem
.GetBodyLockInterface()
.TryGetBody(character.GetInnerBodyID());
const otherBody = _sharedData.physicsSystem
.GetBodyLockInterface()
.TryGetBody(otherCharacter.GetInnerBodyID());

const physicsBehavior = body.gdjsAssociatedBehavior;
const otherPhysicsBehavior = otherBody.gdjsAssociatedBehavior;

if (!physicsBehavior || !otherPhysicsBehavior) {
return true;
}
return physicsBehavior.canCollideAgainst(otherPhysicsBehavior);
};
characterContactListener.OnContactAdded = (
character,
bodyID2,
subShapeID2,
contactPosition,
contactNormal,
settings
) => {};
characterContactListener.OnCharacterContactAdded = (
character,
otherCharacter,
subShapeID2,
contactPosition,
contactNormal,
settings
) => {};
characterContactListener.OnContactSolve = (
character,
bodyID2,
subShapeID2,
contactPosition,
contactNormal,
contactVelocity,
contactMaterial,
characterVelocity,
newCharacterVelocity
) => {};
characterContactListener.OnCharacterContactSolve = (
character,
otherCharacter,
subShapeID2,
contactPosition,
contactNormal,
contactVelocity,
contactMaterial,
characterVelocityPtr,
newCharacterVelocityPtr
) => {};
character.SetListener(characterContactListener);
}

// TODO This is not really reliable. We could choose to disable it and force user to use the "is on platform" condition.
//body.SetCollideKinematicVsNonDynamic(true);
return body;
Expand Down

0 comments on commit 2ce35a1

Please sign in to comment.