Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
d726686125 | |||
4492995a59 |
@ -5,21 +5,69 @@ import leenkx.trait.physics.RigidBody;
|
||||
|
||||
class HasContactNode extends LogicNode {
|
||||
|
||||
public var property0: Bool; // "any object" checkbox
|
||||
|
||||
// Performance optimization: Cache RigidBody lookups
|
||||
var cachedRb1: RigidBody = null;
|
||||
var cachedRb2: RigidBody = null;
|
||||
var lastObject1: Object = null;
|
||||
var lastObject2: Object = null;
|
||||
var lastContactCount: Int = 0;
|
||||
var lastFrame: Int = -1;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var object1: Object = inputs[0].get();
|
||||
var object2: Object = inputs[1].get();
|
||||
|
||||
if (object1 == null || object2 == null) return false;
|
||||
if (object1 == null) return false;
|
||||
|
||||
#if lnx_physics
|
||||
// Enhanced caching: Check both object reference AND RigidBody availability
|
||||
var shouldUpdateCache1 = false;
|
||||
if (object1 != lastObject1) {
|
||||
// Object reference changed - always update cache
|
||||
shouldUpdateCache1 = true;
|
||||
} else if (cachedRb1 == null) {
|
||||
// Same object but no cached RigidBody - check if one was added
|
||||
var currentRb1 = object1.getTrait(RigidBody);
|
||||
if (currentRb1 != null) {
|
||||
shouldUpdateCache1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldUpdateCache1) {
|
||||
cachedRb1 = object1.getTrait(RigidBody);
|
||||
lastObject1 = object1;
|
||||
}
|
||||
if (cachedRb1 == null) return false;
|
||||
|
||||
var physics = leenkx.trait.physics.PhysicsWorld.active;
|
||||
var rb2 = object2.getTrait(RigidBody);
|
||||
var rbs = physics.getContacts(object1.getTrait(RigidBody));
|
||||
if (rbs != null) for (rb in rbs) if (rb == rb2) return true;
|
||||
var rbs = physics.getContacts(cachedRb1);
|
||||
|
||||
// Early exit if no contacts
|
||||
if (rbs == null || rbs.length == 0) return false;
|
||||
|
||||
// If "any object" mode, any contact is valid - return immediately
|
||||
if (property0) return true;
|
||||
|
||||
// Specific object mode - check if second input exists
|
||||
if (inputs.length <= 1) return false;
|
||||
|
||||
var object2: Object = inputs[1].get();
|
||||
if (object2 == null) return false;
|
||||
|
||||
// Cache second RigidBody lookup
|
||||
if (object2 != lastObject2) {
|
||||
cachedRb2 = object2.getTrait(RigidBody);
|
||||
lastObject2 = object2;
|
||||
}
|
||||
if (cachedRb2 == null) return false;
|
||||
|
||||
// Use indexOf for better performance than manual loop
|
||||
// This is more efficient for small arrays and uses native array methods
|
||||
return rbs.indexOf(cachedRb2) >= 0;
|
||||
#end
|
||||
return false;
|
||||
}
|
||||
|
@ -5,53 +5,99 @@ import leenkx.trait.physics.RigidBody;
|
||||
|
||||
class OnContactNode extends LogicNode {
|
||||
|
||||
public var property0: String;
|
||||
var lastContact = false;
|
||||
public var property0: String; // Contact type: "begin", "overlap", "end"
|
||||
public var property1: Bool; // "On Any Object" checkbox
|
||||
|
||||
// Cache for performance optimization
|
||||
var cachedRb1: RigidBody = null;
|
||||
var cachedRb2: RigidBody = null;
|
||||
var lastObject1: Object = null;
|
||||
var lastObject2: Object = null;
|
||||
var lastObject1TraitCount: Int = 0;
|
||||
var lastObject2TraitCount: Int = 0;
|
||||
var lastContact = false; // Track previous contact state
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
|
||||
// Subscribe to update events for event-based triggering
|
||||
tree.notifyOnUpdate(update);
|
||||
}
|
||||
|
||||
function update() {
|
||||
var object1: Object = inputs[0].get();
|
||||
var object2: Object = inputs[1].get();
|
||||
|
||||
if (object1 == null) object1 = tree.object;
|
||||
if (object2 == null) object2 = tree.object;
|
||||
|
||||
var contact = false;
|
||||
if (object1 == null) return;
|
||||
|
||||
#if lnx_physics
|
||||
var physics = leenkx.trait.physics.PhysicsWorld.active;
|
||||
var rb1 = object1.getTrait(RigidBody);
|
||||
if (rb1 != null) {
|
||||
var rbs = physics.getContacts(rb1);
|
||||
if (rbs != null) {
|
||||
var rb2 = object2.getTrait(RigidBody);
|
||||
for (rb in rbs) {
|
||||
if (rb == rb2) {
|
||||
contact = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
// Smart caching: update cache if object changes OR if trait count changes
|
||||
var shouldUpdateCache1 = (object1 != lastObject1) ||
|
||||
(lastObject1 != null && lastObject1.traits.length != lastObject1TraitCount);
|
||||
|
||||
var b = false;
|
||||
if (shouldUpdateCache1) {
|
||||
cachedRb1 = object1.getTrait(RigidBody);
|
||||
lastObject1 = object1;
|
||||
lastObject1TraitCount = object1.traits.length;
|
||||
}
|
||||
if (cachedRb1 == null) return;
|
||||
|
||||
var rbs = leenkx.trait.physics.PhysicsWorld.active.getContacts(cachedRb1);
|
||||
if (rbs == null || rbs.length == 0) {
|
||||
// No contacts - handle "end" event
|
||||
if (lastContact && property0 == "end") {
|
||||
runOutput(0);
|
||||
}
|
||||
lastContact = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for contact
|
||||
var contact = false;
|
||||
|
||||
// If "On Any Object" mode, any contact is valid
|
||||
if (property1) {
|
||||
contact = true;
|
||||
} else {
|
||||
// Specific object mode - check if second input exists
|
||||
if (inputs.length <= 1) return;
|
||||
|
||||
var object2: Object = inputs[1].get();
|
||||
if (object2 == null) return;
|
||||
|
||||
// Smart caching for second object
|
||||
var shouldUpdateCache2 = (object2 != lastObject2) ||
|
||||
(lastObject2 != null && lastObject2.traits.length != lastObject2TraitCount);
|
||||
|
||||
if (shouldUpdateCache2) {
|
||||
cachedRb2 = object2.getTrait(RigidBody);
|
||||
lastObject2 = object2;
|
||||
lastObject2TraitCount = object2.traits.length;
|
||||
}
|
||||
if (cachedRb2 == null) return;
|
||||
|
||||
// Check if target object is in contact list
|
||||
contact = rbs.indexOf(cachedRb2) >= 0;
|
||||
}
|
||||
|
||||
// Handle different contact event types
|
||||
var shouldTrigger = false;
|
||||
switch (property0) {
|
||||
case "begin":
|
||||
b = contact && !lastContact;
|
||||
shouldTrigger = contact && !lastContact;
|
||||
case "overlap":
|
||||
b = contact;
|
||||
shouldTrigger = contact;
|
||||
case "end":
|
||||
b = !contact && lastContact;
|
||||
shouldTrigger = !contact && lastContact;
|
||||
}
|
||||
|
||||
lastContact = contact;
|
||||
|
||||
if (b) runOutput(0);
|
||||
if (shouldTrigger) {
|
||||
runOutput(0);
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
// Keep the get method for backward compatibility, but it's not the primary interface
|
||||
override function get(from: Int): Dynamic {
|
||||
return lastContact;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,16 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
def toggle_second_input(self, context):
|
||||
"""Show/hide the second input socket based on the 'On Any Object' checkbox"""
|
||||
if self.property0: # "On Any Object" is enabled
|
||||
# Hide the second input socket
|
||||
if len(self.inputs) > 1:
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
else: # "On Any Object" is disabled
|
||||
# Show the second input socket if it doesn't exist
|
||||
if len(self.inputs) == 1:
|
||||
self.add_input('LnxNodeSocketObject', 'RB 2')
|
||||
|
||||
class HasContactNode(LnxLogicTreeNode):
|
||||
"""Returns whether the given rigid body has contact with another given rigid body."""
|
||||
bl_idname = 'LNHasContactNode'
|
||||
@ -7,8 +18,18 @@ class HasContactNode(LnxLogicTreeNode):
|
||||
lnx_section = 'contact'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeBoolProperty(
|
||||
'property0',
|
||||
name='On Any Object',
|
||||
description='If enabled, returns true if the rigid body has contact with any object',
|
||||
default=False,
|
||||
update=toggle_second_input)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'RB 1')
|
||||
self.add_input('LnxNodeSocketObject', 'RB 2')
|
||||
|
||||
self.add_output('LnxBoolSocket', 'Has Contact')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0') # "On Any Object" checkbox
|
||||
|
@ -1,5 +1,16 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
def toggle_second_input(self, context):
|
||||
"""Show/hide the second input socket based on the 'On Any Object' checkbox"""
|
||||
if self.property1: # "On Any Object" is enabled
|
||||
# Hide the second input socket
|
||||
if len(self.inputs) > 1:
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
else: # "On Any Object" is disabled
|
||||
# Show the second input socket if it doesn't exist
|
||||
if len(self.inputs) == 1:
|
||||
self.add_input('LnxNodeSocketObject', 'RB 2')
|
||||
|
||||
class OnContactNode(LnxLogicTreeNode):
|
||||
"""Activates the output when the rigid body make contact with
|
||||
another rigid body.
|
||||
@ -23,6 +34,13 @@ class OnContactNode(LnxLogicTreeNode):
|
||||
('end', 'End', 'The contact between the rigid bodies ends')],
|
||||
name='', default='begin')
|
||||
|
||||
property1: HaxeBoolProperty(
|
||||
'property1',
|
||||
name='On Any Object',
|
||||
description='If enabled, triggers on contact with any object instead of a specific object',
|
||||
default=False,
|
||||
update=toggle_second_input)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'RB 1')
|
||||
self.add_input('LnxNodeSocketObject', 'RB 2')
|
||||
@ -31,3 +49,4 @@ class OnContactNode(LnxLogicTreeNode):
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
layout.prop(self, 'property1') # "On Any Object" checkbox
|
||||
|
Reference in New Issue
Block a user