How to rotate one part based off of the collided angle of another part?

i have a modified rocket launcher gear that clones a custom explosion effect when the rocket… explodes! i’d like for the white circle in the video to always be perpendicular to whatever surface it hits (similar to how it looks when rockets hit the floor)
unfortunately, i have no way of figuring out the angle of whatever part i hit!! i’ve seen no mention of any problem similar to this online, and i’ve struggled finding a way to get it by myself

i’ve already tried setting the rotation of the explosion effect to the same as the rocket object, but that didn’t provide favorable results

script i have so far relating to the effect:

		--Create the explosion.
		local Explosion = Instance.new("Explosion")
		Explosion.BlastPressure = 0
		Explosion.BlastRadius = BLAST_RADIUS
		Explosion.Position = Position
		Explosion.Hit:Connect(OnExplosionHit)
		Explosion.Visible = false
		
		-- MY! ugly effect

		local vfx =  ReplicatedStorage.vfx:FindFirstChild("explode")
		local clonevfx = vfx:Clone()
		
--		clonevfx:PivotTo(CFrame.new(Position) * CFrame.fromEulerAngles(math.rad(45), 0, 0))
		clonevfx:PivotTo(CFrame.new(Position))
--		print(CFrame.lookAt(Position, touchedpart.Position))
		-- i love commenting out stuff i've tried that doesn't work <3
		
		clonevfx.Parent = workspace

		Explosion.Parent = game.Workspace
		
		--Reparent the script to the explosion and destroy the rocket.
		FiredByValue.Parent = script
		script.Parent = Explosion
		Rocket:Destroy()
		
		wait(1)
		clonevfx:Destroy()

You need the surface normal direction of the part/wall/floor that got hit, which is only easy to get by doing a raycast at the impact position, from the direction the projectile came from. You can whitelist just the hit part to be sure the ray doesn’t consider anything else along the projectile path.

Once you have the normal from the RaycastResult, you can construct a CFrame where that surface normal is the UpVector. Since your circle graphic is radially symmetric, you can use the CFrame constructor Cframe.new( hitPos, hitPos + normal ) or you can use CFrame.lookAt, but because that constructs a CFrame where the supplied direction is the LookVector, not UpVector, you’ll have to either multiply the result by something like CFrame.fromOrientation( -0.5*math.pi,0,0), or use CFrame.fromMatrix to construct the full CFrame manually if you know how (requires solid understanding of cross product and dot product to cover all cases correctly). Or you can bake the 90 degree rotation into the model you’re orienting, using a weld or motor to a part that’s oriented how you want.

thank you for pointing me in the right direction! i’ve got something resembling what i’d like down… but i’m having trouble with walls still. slopes and floors are working great though! !

updated code snippet

		local Explosion = Instance.new("Explosion")
		Explosion.BlastPressure = 0
		Explosion.BlastRadius = BLAST_RADIUS
		Explosion.Position = Position
		Explosion.Hit:Connect(OnExplosionHit)
		Explosion.Visible = false
		
		-- MY! ugly effect

		local vfx =  ReplicatedStorage.vfx:FindFirstChild("explode")
		local clonevfx = vfx:Clone()
		
--		
		clonevfx:PivotTo(CFrame.new(Position))
--		print(CFrame.lookAt(Position, touchedpart.Position))
		-- i love commenting out stuff i've tried that doesn't work <3
		
		clonevfx.Parent = workspace

		Explosion.Parent = game.Workspace
		
		--Reparent the script to the explosion and destroy the rocket.
		FiredByValue.Parent = script
		script.Parent = Explosion
		
		
		local tovector = Rocket.CFrame.LookVector * 100
		local rayDirection = tovector
		local raycastResult = workspace:Raycast(Rocket.Position, rayDirection)

		-- this saved me >>> https://devforum.roblox.com/t/how-do-you-visualize-a-raycast-as-a-part/657972/12
		--[[local function visualizeRaycast(origin, direction)
			local Raycast = Instance.new("Part")
			Raycast.CFrame = CFrame.new(origin + direction/2, origin + direction)
			Raycast.Anchored = true
			Raycast.CanCollide = false
			Raycast.Name = "Raycast"
			Raycast.Size = Vector3.new(1, 1, direction.Magnitude)
			Raycast.Parent = workspace
		end
	
		visualizeRaycast(Rocket.Position, rayDirection)]]
		
		if raycastResult then
			print(raycastResult.Normal)
			local normal90 = (raycastResult.Normal * 90)
			clonevfx:PivotTo(CFrame.new(Position) * CFrame.fromEulerAngles(math.rad(normal90.X), math.rad(normal90.Y), math.rad(normal90.Z)))
		else
			print("no raycast hit... :(")
		end
		
		Rocket:Destroy()
		wait(1)
		clonevfx:Destroy()

solved by tweaking the angles of the normal

		local Explosion = Instance.new("Explosion")
		Explosion.BlastPressure = 0
		Explosion.BlastRadius = BLAST_RADIUS
		Explosion.Position = Position
		Explosion.Hit:Connect(OnExplosionHit)
		Explosion.Visible = false
		
		-- MY! ugly effect

		local vfx =  ReplicatedStorage.vfx:FindFirstChild("explode")
		local clonevfx = vfx:Clone()
		
--		
		clonevfx:PivotTo(CFrame.new(Position))
--		print(CFrame.lookAt(Position, touchedpart.Position))
		-- i love commenting out stuff i've tried that doesn't work <3
		
		clonevfx.Parent = workspace

		Explosion.Parent = game.Workspace
		
		--Reparent the script to the explosion and destroy the rocket.
		FiredByValue.Parent = script
		script.Parent = Explosion
		
		
		local tovector = Rocket.CFrame.LookVector * 100
		local rayDirection = tovector
		local raycastResult = workspace:Raycast(Rocket.Position, rayDirection)

		-- this saved me >>> https://devforum.roblox.com/t/how-do-you-visualize-a-raycast-as-a-part/657972/12
		--[[local function visualizeRaycast(origin, direction)
			local Raycast = Instance.new("Part")
			Raycast.CFrame = CFrame.new(origin + direction/2, origin + direction)
			Raycast.Anchored = true
			Raycast.CanCollide = false
			Raycast.Name = "Raycast"
			Raycast.Size = Vector3.new(1, 1, direction.Magnitude)
			Raycast.Parent = workspace
		end
	
		visualizeRaycast(Rocket.Position, rayDirection)]]
		
		if raycastResult then
			print(raycastResult.Normal)
			local normal90 = (raycastResult.Normal * 90)
			clonevfx:PivotTo(CFrame.new(Position) * CFrame.fromEulerAngles(math.rad(normal90.X -90), math.rad(normal90.Y), math.rad(normal90.Z -90)))
			print(normal90.X)
		else
			print("no raycast hit... :(")
		end
		
		Rocket:Destroy()
		wait(1)
		clonevfx:Destroy()

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.