Jump to content

VolumeTrigger Script


reepblue
 Share

Recommended Posts

Unlike CollisionTrigger, VolumeTrigger does it's work by it's entity's aabb radius being called on the UpdatePhysics thread. VolumeTrigger also keeps track of what objects are in the volume, and entities with swept collision can enter the volume because it doesn't use shapes for triggering.

 

--[[
This script will make any entity act as a volume trigger. Having a trigger be AABB tests rather than collision has it's perks
as it's easier to keep track of what entities are in the volume. Also, objects with swept collision can now activate triggers!
----NOTES----
filter <entity> = Use this feild if you wish to have a specific entity trigger outputs.
enabled <bool> = Toggle the trigger on and off
onlyonce <bool> = When true, the trigger deactivate forever when triggered the first time.
sizetest <bool> = When true, the overlap test will preform a aabb radius on both the trigger and entity in question. If false, only the entity's position will be tested.
Make this true if you want a true collision like trigger.
allowcharacters <bool> = Toggles if characters can activate.
allowprops <bool> = Toggles if props can activate.
allowdebri <bool> = Toggles if debris can activate.
allowprojectiles <bool> = Toggles if projectiles can activate.
]]--
import "Scripts/Functions/IsAABBOverlap.lua"
Script.filter=nil--entity "Filter"
Script.enabled=true--bool "Enabled"
Script.onlyonce=false --bool "Only Once"
Script.sizetest=false --bool "Size Test"
-- These values will get ignored if there is no filter.
Script.allowcharacters=true --bool "Allow Characters"
Script.allowprops=true --bool "Allow Props"
Script.allowdebris=true --bool "Allow Debris"
Script.allowprojectiles=true --bool "Allow Projectiles"
function TriggerForEachEntityInAABBDoCallback(entity,extra)
local volumeent = extra
local filterent = volumeent.script.filter
-- Always skip these!
if entity == volumeent then return end
if entity:GetClass() == Object.PivotClass then return end
if entity:GetClass() == Object.BoneClass then return end
if entity:GetClass() == Object.DirectionalLightClass then return end
if entity:GetClass() == Object.SpotLightClass then return end
if entity:GetClass() == Object.PointLightClass then return end
if entity:GetClass() == Object.ListenerClass then return end
if entity:GetClass() == Object.ProbeClass then return end
-- Don't count static brushes!
if entity:GetCollisionType() == Collision.Scene then
 if entity:GetMass() <= 0 then return end
end
-- If we're not looking for a specific entity, filter by collision types.
if filterent == nil then
 if entity:GetCollisionType() == Collision.None then return end
 if entity:GetCollisionType() == Collision.Character and not volumeent.script.allowcharacters then return end
 if entity:GetCollisionType() == Collision.Prop and not volumeent.script.allowprops then return end
 if entity:GetCollisionType() == Collision.Debris and not volumeent.script.allowdebris then return end
 if entity:GetCollisionType() == Collision.Projectile and not volumeent.script.allowprojectiles then return end
 if IsAABBOverlap(volumeent,entity,volumeent.script.sizetest) == true then
  volumeent.script:onstartTouch(entity)
 end
elseif entity == filterent then
 if IsAABBOverlap(volumeent,filterent,volumeent.script.sizetest) == true then
  volumeent.script:onstartTouch(entity)
 end
end
end
function Script:SearchForEntites()
self.entity.world:ForEachEntityInAABBDo(self.entity:GetAABB(Entity.GlobalAABB),"TriggerForEachEntityInAABBDoCallback",self.entity)
end
function Script:IsAlreadyTouching(entity)
local key,value
for key,value in pairs(self.touchingents) do
 if value == entity then
  return true
 end
end
return false
end
function Script:Start()
self.touchingents={}
self.triggered=false
-- Disable Shadows
self.entity:SetShadowMode(0)
-- Ignore all picking.
self.entity:SetPickMode(0)
-- Automaticly fix the Physics
self.entity:SetCollisionType(Collision.None)
self.entity:SetMass(0)
self.entity:SetKeyValue("type", "trigger")
self.entity:SetViewRange(Entity.NearViewRange)
end
function Script:UpdatePhysics()
if not self.enabled then return end
if onlyonce and self.triggered then return end
self:SearchForEntites()
if table.getn(self.touchingents) > 0 then
 local key,value
 for key,value in pairs(self.touchingents) do
  local zentity = value
  if zentity ~= nil then
   if IsAABBOverlap(self.entity,zentity,self.sizetest) == false then
 self:onendTouch(key, zentity)
 break
   end
  end
 end
end
end
function Script:onstartTouch(entity)
if self:IsAlreadyTouching(entity) == false then
 if table.getn(self.touchingents) == 0 then
  -- If it's the first item, call a special function.
  self.component:CallOutputs("onstartTouchAll")
  self.component:CallOutputs("onstartTouch")
 else
  self.component:CallOutputs("onstartTouch")
 end 
 table.insert(self.touchingents, entity)
 self.triggered=true
 --System:Print("Added " ..entity:GetKeyValue("name") .. " to list")
end
end
function Script:onendTouch(key, entity)
if table.getn(self.touchingents) < 0 then return end
if table.getn(self.touchingents) == 1 then
 -- If it's the last item, call a special function.
 self.component:CallOutputs("onendTouchAll")
 self.component:CallOutputs("onendTouch")
else
 self.component:CallOutputs("onendTouch")
end
--System:Print("Removing " ..entity:GetKeyValue("name") .. " from list")
table.remove(self.touchingents, key)
end

  • Upvote 4

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

I forgot IsAABBOverlap.lua. Place in the functions directory.

 

-- Thanks to CrazyCarpet for this snippet!
function IsAABBOverlap(hostent, testent, usesizetest)
if hostent == nil then return false end
if usesizetest == nil then usesizetest = false end
if usesizetest then
 local aabb = hostent:GetAABB(Entity.GlobalAABB)
 local eaabb = testent:GetAABB(Entity.GlobalAABB)
 if (eaabb.min.x > aabb.max.x) or (aabb.min.x > eaabb.max.x) then return false end
 if (eaabb.min.y > aabb.max.y) or (aabb.min.y > eaabb.max.y) then return false end
 if (eaabb.min.z > aabb.max.z) or (aabb.min.z > eaabb.max.z) then return false end
 return true
else
 local aabb = hostent:GetAABB(Entity.GlobalAABB)
 local pos = testent:GetPosition(true)
 if pos.x < aabb.min.x then return false end
 if pos.y < aabb.min.y then return false end
 if pos.z < aabb.min.z then return false end
 if pos.x > aabb.max.x then return false end
 if pos.y > aabb.max.y then return false end
 if pos.z > aabb.max.z then return false end
 return true
end
return false
end

  • Upvote 2

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

I like to post on the forum so if any users who wish to do the same thing, can find it with the search engine. I'm gonna pack all my scripts up in one big chunk when they are fully tested and ironed out. :)

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

I also have a trigger script. It is a comprehensive clone of the unity functions. It keeps track of every entity that enters, stays, and leaves a trigger. It also provides 3 functions that other scripts can use to interact with the trigger. It gives you the option of triggering on props, characters, and projectiles

 


--[[

Title: Enhanced Collision Trigger

By: Einlander

Version: 1.0 First public release

Function: Per-Entity collision trigger. With an additional OnTriggerExit output. Made to mimic unity3d collision handler

Details: Some times you need to have a trigger that will not only know that it is triggered, but know how many entities triggered it, and when any given trigger has left it's trigger area.

Extras: Provides 3 new trigger functions for other scripts to use.

Notes:

-- on collision

a collision has happened

Check if the entity is on the entity collision list

If not, it is placed on the entity list

call OnEnter

If it is on the entity list

call OnStay

-- check during physics -because it's certian to be run, but not as often as update world

check to see if collision happened last run

No collision has happened last time because collision flag is still off

remove from entity list

OnTriggerExit

Collsion happened

set the entity collision flag to false

wake up the entity so the trigger will continue detecting it.

]]--

Script.enabled = true --bool "Enable"

Script.debugtext = false -- bool "Debug Text"

Script.spoofcollision = false --bool "Spoof Trigger"

Script.triggerProps = true --bool "Trigger Props"

Script.triggerCharacters = true --bool "Trigger Characters"

Script.triggerProjectiles = true --bool "Trigger Projectiles"

 

function Script:Start()

if self.spoofcollision then

-- [spoof collision trigger] dont know if this would actually work

Collision.EnhancedCollisionTrigger = Collision.Trigger --Masqurade as the original trigger collision

else

Collision.EnhancedCollisionTrigger = Time:GetCurrent() -- hopefully no collisions here :)

end

 

-- make sure the trigger interacts with most things

-- should be selectable

if self.triggerProps then

Collision:SetResponse(Collision.EnhancedCollisionTrigger, Collision.Prop, Collision.Trigger)

end

if self.triggerCharacters then

Collision:SetResponse(Collision.EnhancedCollisionTrigger, Collision.Character, Collision.Trigger)

end

if self.triggerProjectiles then

Collision:SetResponse(Collision.EnhancedCollisionTrigger, Collision.Projectile, Collision.Trigger)

end

self.entity:SetCollisionType(Collision.EnhancedCollisionTrigger)

self.entitydb = {}

end

 

 

function Script:Enable()--in

if self.enabled==false then

self.enabled=true

--self.component:CallOutputs("Enable")

end

end

 

function Script:Disable()--in

if self.enabled then

self.enabled=false

--self.component:CallOutputs("Disable")

end

end

 

function Script:OnTriggerEnter(entity)

end

 

function Script:OnTriggerStay(entity)

end

 

function Script:OnTriggerExit(entity)

 

end

 

function Script:UpdatePhysics()

if self.enabled then

-- check to see if entity is still inside the trigger

for key, value in pairs (self.entitydb) do

if self.entitydb[key].hadcollision == false then -- check if the entity had a collision from the last check

-- on exit

self.component:CallOutputs("OnTriggerExit")

self:OnTriggerExit(self.entitydb[key].entity)

if self.entitydb[key].script then

if type(elf.entitydb[key].script.OnTriggerExit)=="function" then -- Check to see if entity has a script with the right function in it, then call it.

self.entitydb[key].script:OnTriggerExit(self.entity)

end

end

table.remove(self.entitydb, key) -- got to remove it, or else we will end up indexing nil values

else

self.entitydb[key].hadcollision = false -- set it to false because if it is still in the trigger it will get activated again

self.entitydb[key].entity:AddForce(0,0,0) -- wake up the entit's physics. This is a work around because by default, physics objects go to sleep and the trigger can no longer see them. doesnt actually move entity

end

end

end

end

 

function Script:Collision(entity, position, normal, speed)

if self.enabled == true then

-- check if entity is in the list

local entitymatch = false

for key, value in pairs (self.entitydb) do

if self.entitydb[key].entity == entity then -- found it in the list

self.entitydb[key].hadcollision = true

entitymatch = true

self.component:CallOutputs("OnTriggerStay")

self:OnTriggerStay(entity)

if entity.script then

if type(entity.script.OnTriggerStay)=="function" then -- Check to see if entity has a script with the right function in it, then call it.

entity.script:OnTriggerStay(self.entity)

end

end

end

end

if entitymatch == false then -- add new object to the list

local newEntity = {}

newEntity.entity = entity

newEntity.hadcollision = true

table.insert(self.entitydb, newEntity)

entitymatch = true

self.component:CallOutputs("OnTriggerEnter")

self:OnTriggerEnter(entity)

if entity.script then

if type(entity.script.OnTriggerEnter)=="function" then -- Check to see if entity has a script with the right function in it, then call it.

entity.script:OnTriggerEnter(self.entity)

end

end

end

end

end

 

 

--This function will be called when the entity is deleted.

function Script:Detach()

ReleaseTableObjects(self.entitydb)

end

 

 

function Script:PostRender(context)

if self.enabled then

if self.debugtext then

context:SetBlendMode(1)

context:SetColor(1,1,1)

context:DrawText("Total Contained Entities: " .. #self.entitydb,0,36)

end

end

end

 

 

 

Entities interacting with the trigger can use:

 


function Script:OnTriggerEnter(entity)

end

 

function Script:OnTriggerStay(entity)

end

 

function Script:OnTriggerExit(entity)

end

  • Upvote 2
Link to comment
Share on other sites

Josh really needs to build this idea into all our scripts. Have enter/stay/exit collision script functions is highly useful but look at all the code we would have to copy and paste all over to get it in a comprehensive way in any script we would want. That really isn't the right way you want ppl doing it.

  • Upvote 1
Link to comment
Share on other sites

Just want to mention that Einlander's script is collision based, and mine is aabb based. The difference is that if you wish props to enter with swept collision enabled, the object will collide into the trigger like it's a wall ignoring any collision rules. I haven't tried Einlander's but I'm sure it's also useful.

 

Pretty much, mine mimics Source's BaseTrigger class, and Einlander mimics Unity's. Use whatever works best for you. :)

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...