reepblue Posted December 15, 2016 Share Posted December 15, 2016 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 4 Quote Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
reepblue Posted December 16, 2016 Author Share Posted December 16, 2016 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 2 Quote Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Rick Posted December 16, 2016 Share Posted December 16, 2016 You might be better off uploading to workshop so it puts things in the right dir and it won't get lost on the forums. Quote Link to comment Share on other sites More sharing options...
reepblue Posted December 16, 2016 Author Share Posted December 16, 2016 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. Quote Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
AggrorJorn Posted December 16, 2016 Share Posted December 16, 2016 Top! I love generic scripts. Quote Link to comment Share on other sites More sharing options...
Einlander Posted December 17, 2016 Share Posted December 17, 2016 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 2 Quote Link to comment Share on other sites More sharing options...
Rick Posted December 17, 2016 Share Posted December 17, 2016 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. 1 Quote Link to comment Share on other sites More sharing options...
AggrorJorn Posted December 17, 2016 Share Posted December 17, 2016 Same thing for a value like enabled. If disabled the update world should not be called. More than half my scripts contain an enabled property. 1 Quote Link to comment Share on other sites More sharing options...
reepblue Posted December 17, 2016 Author Share Posted December 17, 2016 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. Quote Cyclone - Ultra Game System - Component Preprocessor - Tex2TGA - Darkness Awaits Template (Leadwerks) If you like my work, consider supporting me on Patreon! Link to comment Share on other sites More sharing options...
Recommended Posts
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.