o3de / o3de.org

The O3DE website
Other
81 stars 158 forks source link

[DOCS] Examples of how to use AZ Actions (Trigger event, collision event) in Lua #2406

Open AMZN-alexpete opened 1 year ago

AMZN-alexpete commented 1 year ago

Describe the issue briefly

Almost all the physics ebuses are gone or not exposed to Lua and in their place are new classes and functionality that is not well documented and one of the most important pieces is how to use triggers/colliders and simulated bodies.

code for a trigger looks like this:

        self.triggerEvent = SimulatedBody.GetOnTriggerEnterEvent(self.entityId)
        if self.triggerEvent ~= nil then
            self.triggerHandler = self.triggerEvent:Connect(function()
               Debug:Log("triggered")
            end)
        end

however it relies on SimulatedBody to be around and active which it probably NOT be in OnActivate where you expect it to be ready (another doc gap?) and there doesn't appear to be an ebus for telling you when it is ready so you at least need to wait till the next tick to run the above code.

Which page(s) / section(s) are affected?

Possibly a new page around here: https://www.o3de.org/docs/user-guide/scripting/lua/ebus/

Does this work have an engineering dependency? What is it?

you might need to have an engineer dive in to find the best way to know when it is OK to use the SimulatedBody

mythrz commented 1 month ago

Testing this now, so I decided to leave here some feedback and the steps I used. The OnPhysicsEnabled() is the best solution so far!

Tried calling the triggers in three different functions: OnActivate(), OnTick() and OnPhysicsEnabled():




Requirements for Trigger event:

local T =
{
    Properties =
    {
        FromEntity1 = 
        {
            default = EntityId(),
            description = "Just an entity with collider and trigger on"
        }
    }
}

function T:OnActivate()

    -- -- Expose OnTick function:
    self.tickBusHandler = TickBus.Connect(self); --, self.entityId) 

       -- -- Expose OnPhysicsEnabled, OnPhysicsDisabled
       self.rigidBodyHandler = RigidBodyNotificationBus.Connect(self, self.Properties.FromEntity[1]);
end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
function T:OnDeactivate()
    -- -- IMPORTANT NOTE: When you add handlers, disconnect them!
       -- -- TODO: add more here!

    -- -- clean/reset memory
    if (self.tickBusHandler ~= nil) then 
        self.tickBusHandler:Disconnect();
               -- self.tickBusHandler = nil;
    end

       -- -- physics:
        if (self.rigidBodyHandler ~= nil) then 
            self.rigidBodyHandler:Disconnect();
        end

    if (self.triggerHandlerExit ~= nil) then 
           self.triggerHandlerExit:Disconnect();
           self.triggerEventExit = nil;
        end
    if (self.triggerHandlerEnter ~= nil) then 
           self.triggerHandlerEnter:Disconnect();
           self.triggerEventEnter = nil;
        end

    self.Properties = nil;
end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- NOT RECOMMENDED USE-CASE
function T:OnTick(deltaTime)
       -- -- Trigger event requires "Trigger ON"
    -- T:TriggerEnter(self, self.Properties.FromEntity1);
    -- T:TriggerExit(self, self.Properties.FromEntity1);

       -- -- TODO: add more here, and comment the ones you are not testing!
end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- RECOMMENDED USE-CASE
function T:OnPhysicsEnabled(entityId)
    --- -- Trigger event requires "Trigger ON"
    T:TriggerEnter(self, self.Properties.FromEntity1);
    T:TriggerExit(self, self.Properties.FromEntity1);

        -- -- TODO: add more here, and comment the ones you are not testing!

    Debug.Log("Physics Enabled");
end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- When a colider enters, this activates
function T:TriggerEnter(selfId, triggerSourceId)
    selfId.triggerEventEnter = SimulatedBody.GetOnTriggerEnterEvent(triggerSourceId);
    if selfId.triggerEventEnter ~= nil then
        selfId.triggerHandlerEnter = selfId.triggerEventEnter:Connect(
                      function()
                      Debug:Log("Enter trigger")
                end);
    end
end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- When a colider exits, this activates
function T:TriggerExit(selfId, triggerSourceId)
    selfId.triggerEventExit = SimulatedBody.GetOnTriggerExitEvent(triggerSourceId);
    if selfId.triggerEventExit ~= nil then
        selfId.triggerHandlerExit = selfId.triggerEventExit:Connect(
                        function()
                        Debug:Log("Exit trigger")
                end);
    end
end

return T;

This alone is enough to test. Both work, OnTick and OnPhysicsEnabled, but the latter is much better! I would not recomment the use of OnTick, but some asked me to start there, due to the lack of alternative! OnPhysicsEnabled is that alternative, please use it instead of OnTick.




Requirements for Collision event:

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- When a collision begins, and trigger is "off", this activates
function T:CollisionBegin(selfId, triggerSourceId)
    selfId.collisionBegin = SimulatedBody.GetOnCollisionBeginEvent(triggerSourceId);
    if selfId.collisionBegin ~= nil then
        selfId.collisionBeginHandler = selfId.collisionBegin:Connect(
                      function()
                      Debug:Log("Collision Begin")
                end);
    end
end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- When a collision persists, and trigger is "off", this activates
-- -- This function has the worst performance of all! Dropping to 5 fps
function T:CollisionPersist(selfId, triggerSourceId)
    selfId.collisionPersist = SimulatedBody.GetOnCollisionPersistEvent(triggerSourceId);
    if selfId.collisionPersist ~= nil then
        selfId.collisionPersistHandler = selfId.collisionPersist:Connect(
                      function() 
                      Debug:Log("Collision Persist")
               end);
    end
end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- When a collision ends, and trigger is "off", this activates
function T:CollisionEnd(selfId, triggerSourceId)
    selfId.collisionEnd = SimulatedBody.GetOnCollisionEndEvent(triggerSourceId);
    if selfId.collisionEnd ~= nil then
        selfId.collisionEndHandler = selfId.collisionEnd:Connect(
                      function()
                      Debug:Log("Collision End")
               end);
    end
end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --



Some additional features/commands/functions:

These allow the retrieval of the two entities involved in the trigger.

Just add these to the script, and modify the OnPhysicsEnabled() and OnDeactivate() to call these instead. Remember that you need to turn ON the trigger in the collider-component.

-- -- Enter: Modified version to GetTriggerEntityId and GetOtherEntityId
function T:TriggerEnter2(selfId, triggerSourceId)
    selfId.triggerEventEnter2 = SimulatedBody.GetOnTriggerEnterEvent(triggerSourceId);
    if selfId.triggerEventEnter2 ~= nil then
        selfId.triggerHandlerEnter2 = selfId.triggerEventEnter2:Connect(
            function(tuple, event) 
            triggeredEntityId = TriggerEvent.GetTriggerEntityId(event)
            otherEntityId = TriggerEvent.GetOtherEntityId(event)
            Debug:Log("Enter trigger. Entity with trigerOn is (GetTriggerEntityId):"..tostring(triggeredEntityId)..". The other entity is (OtherEntityId): "..tostring(otherEntityId))
        end);
    end
end
-- -- Exit: 
function T:TriggerExit2(selfId, triggerSourceId)
    selfId.triggerEventExit2 = SimulatedBody.GetOnTriggerExitEvent(triggerSourceId);
    if selfId.triggerEventExit2 ~= nil then
        selfId.triggerHandlerExit2 = selfId.triggerEventExit2:Connect(
            function(tuple, event) 
            triggeredEntityId = TriggerEvent.GetTriggerEntityId(event)
            otherEntityId = TriggerEvent.GetOtherEntityId(event)
            Debug:Log("Exit trigger. Entity with trigerOn is (GetTriggerEntityId):"..tostring(triggeredEntityId)..". The other entity is (OtherEntityId): "..tostring(otherEntityId))
        end);
    end
end

These allow the retrieval of the two entities involved in the collision.

Just add these to the script, and modify the OnPhysicsEnabled() and OnDeactivate() to call these instead. Remember that you need to turn OFF the trigger in the collider-component.

-- -- Begin: Modified version to GetBody1EntityId and GetBody2EntityId
function T:CollisionBegin2(selfId, triggerSourceId)
    selfId.collisionBegin2 = SimulatedBody.GetOnCollisionBeginEvent(triggerSourceId);
    if selfId.collisionBegin2 ~= nil then
        selfId.collisionBeginHandler2 = selfId.collisionBegin2:Connect(
            function(tuple, event) 
            local body1EntityId = CollisionEvent.GetBody1EntityId(event)
            local body2EntityId = CollisionEvent.GetBody2EntityId(event)
            Debug:Log("Collision Begin. Entity with trigerOff is (GetBody1EntityId):"..tostring(body1EntityId)..". The other entity is (GetBody2EntityId): "..tostring(body2EntityId))
        end);
    end
end

-- -- Persist: Modified version to GetBody1EntityId and GetBody2EntityId
function T:CollisionPersist2(selfId, triggerSourceId)
    selfId.collisionPersist2 = SimulatedBody.GetOnCollisionPersistEvent(triggerSourceId);
    if selfId.collisionPersist2 ~= nil then
        selfId.collisionPersistHandler2 = selfId.collisionPersist2:Connect(
            function(tuple, event) 
            local body1EntityId = CollisionEvent.GetBody1EntityId(event)
            local body2EntityId = CollisionEvent.GetBody2EntityId(event)
            Debug:Log("Collision Persist. Entity with trigerOff is (GetBody1EntityId):"..tostring(body1EntityId)..". The other entity is (GetBody2EntityId): "..tostring(body2EntityId))
        end);
    end
end

-- -- End: Modified version to GetBody1EntityId and GetBody2EntityId
function T:CollisionEnd2(selfId, triggerSourceId)
    selfId.collisionEnd2 = SimulatedBody.GetOnCollisionEndEvent(triggerSourceId);
    if selfId.collisionEnd2 ~= nil then
        selfId.collisionEndHandler2 = selfId.collisionEnd2:Connect(
            function(tuple, event) 
            local body1EntityId = CollisionEvent.GetBody1EntityId(event)
            local body2EntityId = CollisionEvent.GetBody2EntityId(event)
            Debug:Log("Collision End. Entity with trigerOff is (GetBody1EntityId):"..tostring(body1EntityId)..". The other entity is (GetBody2EntityId): "..tostring(body2EntityId))
        end);
    end
end



Additional notes:

stevo26 commented 1 month ago

Nice

stevo26 commented 1 month ago

This will Help a lot.