Revision Difference
ENTITY:ResolveCustomFlyCollision#567710
<function name="ResolveCustomFlyCollision" parent="ENTITY" type="hook">
<added>2026.04.08</added>
<description>Called during a non-VPhysics collision event for flying entities.
This is best used to make projectiles bounce off from surfaces in their own way. For this to be triggered, this entity must be the one that's colliding, have some velocity, <page>Entity:GetMoveType</page> must be either <page text="MOVETYPE_FLY">Enums/MOVETYPE#MOVETYPE_FLY</page> or <page text="MOVETYPE_FLYGRAVITY">Enums/MOVETYPE#MOVETYPE_FLYGRAVITY</page>, and <page>Entity:GetMoveCollide</page> must be <page text="MOVECOLLIDE_FLY_CUSTOM">Enums/MOVECOLLIDE#MOVECOLLIDE_FLY_CUSTOM</page>.
<note>This works only on `anim` type entities.</note></description>
<realm>Server</realm>
<args>
<arg name="traceResult" type="table{TraceResult}">The <page>Structures/TraceResult</page> where the collision occured.</arg>
<arg name="vel" type="vector">The calculated velocity after calculations such as bounciness, elasticity, ground sliding etc...</arg>
</args>
<rets>
<ret type="boolean">Return `true` to prevent default action.</ret>
</rets>
</function>
<example>
<description>A rocket model which directly reflects off from surfaces without any velocity loss, similar to `sent_ball`.</description>
<code>
AddCSLuaFile()
ENT.Base = "base_anim"
ENT.Type = "anim"
ENT.PrintName = "Bouncy Rocket"
ENT.Category = "Other"
ENT.Editable = true
ENT.Spawnable = true
ENT.AdminOnly = true
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.PhysicsSolidMask = MASK_SOLID + CONTENTS_HITBOX -- collide with vphysics
function ENT:Initialize()
self:SetModel( "models/weapons/w_missile_closed.mdl" )
self:SetMoveType( MOVETYPE_FLY )
self:SetMoveCollide( MOVECOLLIDE_FLY_CUSTOM )
self:SetCollisionGroup( COLLISION_GROUP_PROJECTILE )
self:SetSolid( SOLID_BBOX ) -- turns on collision with other entities
self:SetCollisionBounds( Vector( -1, -1, -1 ), Vector( 1, 1, 1 ) )
self:SetVelocity( self:GetForward() * 650 )
end
function ENT:ResolveCustomFlyCollision( traceResult, vel )
vel = self:GetVelocity() -- using unmodified velocity
local speed = vel:Length()
local tempdir = traceResult.HitNormal
tempdir:Mul( 2 * vel:Dot( tempdir ) )
local dir = vel
dir:Sub( tempdir )
dir:Normalize()
local reflectionangle = dir:Angle()
local newvel = dir
newvel:Mul( speed )
self:SetLocalVelocity( newvel )
self:SetAngles( reflectionangle )
end
</code>
⤶
</example>⤶
</example>⤶
<example>
<description>A reduced version of the engine's crossbow bolt that replicates the behavior closely. It flies. It reflects. It sticks. It hurts.</description>
<code>
AddCSLuaFile()
ENT.Base = "base_anim"
ENT.Type = "anim"
ENT.PrintName = "Simple Crossbow Bolt"
ENT.Category = "Other"
ENT.Spawnable = true
ENT.AdminOnly = true
ENT.RenderGroup = RENDERGROUP_OPAQUE
ENT.PhysicsSolidMask = bit.band( bit.bor( MASK_SOLID, CONTENTS_HITBOX ), bit.bnot( CONTENTS_GRATE ) )
function ENT:Initialize()
self:SetModel( "models/crossbow_bolt.mdl" )
self:SetSolid( SOLID_BBOX )
self:AddSolidFlags( FSOLID_NOT_STANDABLE )
self:SetCollisionGroup( COLLISION_GROUP_PROJECTILE )
self:SetCollisionBounds( Vector( -1, -1, -1 ), Vector( 1, 1, 1 ) )
self:SetMoveType( MOVETYPE_FLYGRAVITY )
self:SetMoveCollide( MOVECOLLIDE_FLY_CUSTOM )
self:SetGravity( 0.05 )
self:SetSkin( 1 )
end
if ( CLIENT ) then return end
local BOLT_AIR_VELOCITY = 3500
function ENT:SpawnFunction( pPlayer, trace_t, classname )
if ( not trace_t.Hit ) then return end
local aimdirection = trace_t.Normal
local angles = aimdirection:Angle()
local origin
local velocity
local simplecrossbowbolt = ents.Create( classname )
simplecrossbowbolt:SetAngles( angles )
origin = aimdirection
origin:Mul( 32 )
origin:Add( trace_t.StartPos )
simplecrossbowbolt:SetPos( origin )
velocity = origin
velocity:Sub( trace_t.StartPos )
velocity:Div( 32 )
velocity:Mul( BOLT_AIR_VELOCITY )
simplecrossbowbolt:SetVelocity( velocity )
simplecrossbowbolt:SetOwner( pPlayer )
simplecrossbowbolt:Spawn()
return simplecrossbowbolt
end
function ENT:ResolveCustomFlyCollision( trace_t, velocity )
if ( trace_t.HitSky ) then return self:Remove() end
local pOther = trace_t.Entity
self:EmitSound( "Weapon_Crossbow.BoltHitWorld" )
local direction = self:GetVelocity()
local speed = direction:Length()
direction:Normalize()
direction:Negate()
local hitDot = trace_t.HitNormal:Dot( direction )
direction:Negate()
if ( ( hitDot < 0.5 ) and ( speed > 100 ) ) then -- Reflect off
local vecReflection = Vector( 0, 0, 0 )
vecReflection:Set( trace_t.HitNormal )
vecReflection:Mul( 2 * hitDot )
vecReflection:Add( direction )
self:SetAngles( vecReflection:Angle() )
velocity = vecReflection
velocity:Mul( speed * 0.75 )
self:SetLocalVelocity( velocity )
self:SetGravity( 1 )
else -- Stick
-- Move the bolt back a bit so it looks clean
local compensatedpos = Vector( 0, 0, 0 )
compensatedpos:Set( trace_t.Normal )
compensatedpos:Mul( -6 )
compensatedpos:Add( trace_t.HitPos )
-- Make it stick
self:SetMoveType( MOVETYPE_NONE )
self:SetPos( compensatedpos )
self:SetParent( pOther )
self:SetSkin( 0 )
-- Leave an impact effect
local data = EffectData()
data:SetOrigin( trace_t.HitPos )
data:SetStart( trace_t.StartPos )
data:SetSurfaceProp( trace_t.SurfaceProps )
data:SetDamageType( DMG_BULLET )
data:SetHitBox( trace_t.HitBox )
data:SetEntIndex( pOther:EntIndex() )
util.Effect( "Impact", data )
-- Cause some damage
local info = DamageInfo()
info:SetAttacker( self:GetOwner() )
info:SetInflictor( self )
info:SetDamage( 20 )
info:SetDamageType( DMG_BULLET )
info:SetDamagePosition( trace_t.HitPos )
local force = compensatedpos
force:Set( trace_t.Normal )
force:Mul( 75 * 4 )
info:SetDamageForce( force )
pOther:TakeDamageInfo( info )
-- Schedule removal
self.Think = self.Remove
self:NextThink( CurTime() + 10 )
end
-- Shoot sparks
local data = EffectData()
data:SetOrigin( trace_t.HitPos )
data:SetMagnitude( 1 )
data:SetScale( 1 )
data:SetNormal( trace_t.HitNormal )
data:SetRadius( 1 )
util.Effect( "Sparks", data )
end
</code>
</example>
Garry's Mod
Rust
Steamworks
Wiki Help