Here's the solution for anyone else who had my same question:
local Invisicam = {}
local FADE_TARGET = 0.75
local FADE_RATE = 0.1
local MODE = {
CUSTOM = 1,
LIMBS = 2,
MOVEMENT = 3,
CORNERS = 4,
CIRCLE1 = 5,
CIRCLE2 = 6,
LIMBMOVE = 7,
}
Invisicam.MODE = MODE
local STARTING_MODE = MODE.LIMBS
local LIMB_TRACKING_SET = {
['Head'] = true,
['Left Arm'] = true,
['Right Arm'] = true,
['Left Leg'] = true,
['Right Leg'] = true,
}
local CORNER_FACTORS = {
Vector3.new(1, 1, -1),
Vector3.new(1, -1, -1),
Vector3.new(-1, -1, -1),
Vector3.new(-1, 1, -1)
}
local CIRCLE_CASTS = 10
local MOVE_CASTS = 3
local RunService = game:GetService('RunService')
local PlayersService = game:GetService('Players')
local Player = PlayersService.LocalPlayer
local Camera = nil
local Character = nil
local Torso = nil
local Mode = nil
local Behaviors = {}
local SavedHits = {}
local TrackedLimbs = {}
local function AssertTypes(param, ...)
local allowedTypes = {}
local typeString = ''
for _, typeName in pairs({...}) do
allowedTypes[typeName] = true
typeString = typeString .. (typeString == '' and '' or ' or ') .. typeName
end
local theType = type(param)
assert(allowedTypes[theType], typeString .. " type expected, got: " .. theType)
end
local function CameraCast(worldPoint, ignoreList)
local cameraPoint = Camera.CoordinateFrame.p
local vector = worldPoint - cameraPoint
local ray = Ray.new(cameraPoint, vector.Unit * math.min(vector.Magnitude, 999))
return workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
end
local function LimbBehavior(castPoints)
for _, limb in pairs(TrackedLimbs) do
if limb.Parent then
table.insert(castPoints, limb.Position)
end
end
end
local function MoveBehavior(castPoints)
for i = 1, MOVE_CASTS do
local position, velocity = Torso.Position, Torso.Velocity
local horizontalSpeed = Vector3.new(velocity.X, 0, velocity.Z).Magnitude / 2
local offsetVector = (i - 1) * Torso.CFrame.lookVector * horizontalSpeed
table.insert(castPoints, position + offsetVector)
end
end
local function CornerBehavior(castPoints)
local cframe = Torso.CFrame
local centerPoint = cframe.p
local rotation = cframe - centerPoint
local halfSize = Character:GetExtentsSize() / 2
table.insert(castPoints, centerPoint)
for _, factor in pairs(CORNER_FACTORS) do
table.insert(castPoints, centerPoint + (rotation * (halfSize * factor)))
end
end
local function CircleBehavior(castPoints)
local cframe = nil
if Mode == MODE.CIRCLE1 then
cframe = Torso.CFrame
else
local camCFrame = Camera.CoordinateFrame
cframe = camCFrame - camCFrame.p + Torso.Position
end
table.insert(castPoints, cframe.p)
for i = 0, CIRCLE_CASTS - 1 do
local angle = (2 * math.pi / CIRCLE_CASTS) * i
local offset = 3 * Vector3.new(math.cos(angle), math.sin(angle), 0)
table.insert(castPoints, cframe * offset)
end
end
local function LimbMoveBehavior(castPoints)
LimbBehavior(castPoints)
MoveBehavior(castPoints)
end
local function OnCharacterAdded(character)
Character = character
Torso = Character:WaitForChild('Torso')
TrackedLimbs = {}
for _, child in pairs(Character:GetChildren()) do
if child:IsA('BasePart') and LIMB_TRACKING_SET[child.Name] then
table.insert(TrackedLimbs, child)
end
end
end
local function OnWorkspaceChanged(property)
if property == 'CurrentCamera' then
local newCamera = workspace.CurrentCamera
if newCamera then
Camera = newCamera
end
end
end
function Invisicam:Update()
local castPoints = {}
Behaviors[Mode](castPoints)
local currentHits = {}
local ignoreList = {Character}
local function add(hit)
currentHits[hit] = true
if not SavedHits[hit] then
SavedHits[hit] = hit.LocalTransparencyModifier
end
end
for _, worldPoint in pairs(castPoints) do
repeat
local hitPart = CameraCast(worldPoint, ignoreList)
if hitPart then
add(hitPart)
for _, child in pairs(hitPart:GetChildren()) do
if child:IsA('Decal') or child:IsA('Texture') then
add(child)
end
end
table.insert(ignoreList, hitPart)
end
until not hitPart
end
for hit, originalFade in pairs(SavedHits) do
local currentFade = hit.LocalTransparencyModifier
if currentHits[hit] then
if currentFade < FADE_TARGET then
hit.LocalTransparencyModifier = math.min(currentFade + FADE_RATE, FADE_TARGET)
end
else
if currentFade > originalFade then
hit.LocalTransparencyModifier = math.max(originalFade, currentFade - FADE_RATE)
else
SavedHits[hit] = nil
end
end
end
end
function Invisicam:SetMode(newMode)
AssertTypes(newMode, 'number')
for modeName, modeNum in pairs(MODE) do
if modeNum == newMode then
Mode = newMode
return
end
end
error("Invalid mode number")
end
function Invisicam:SetCustomBehavior(func)
AssertTypes(func, 'function')
Behaviors[MODE.CUSTOM] = func
end
function Invisicam:Cleanup()
for hit, originalFade in pairs(SavedHits) do
hit.LocalTransparencyModifier = originalFade
end
end
workspace.Changed:connect(OnWorkspaceChanged)
OnWorkspaceChanged('CurrentCamera')
Player.CharacterAdded:connect(OnCharacterAdded)
if Player.Character then
OnCharacterAdded(Player.Character)
end
Invisicam:SetMode(STARTING_MODE)
Behaviors[MODE.CUSTOM] = function() end
Behaviors[MODE.LIMBS] = LimbBehavior
Behaviors[MODE.MOVEMENT] = MoveBehavior
Behaviors[MODE.CORNERS] = CornerBehavior
Behaviors[MODE.CIRCLE1] = CircleBehavior
Behaviors[MODE.CIRCLE2] = CircleBehavior
Behaviors[MODE.LIMBMOVE] = LimbMoveBehavior
return Invisicam
That's from the Invisicam module in the CameraScript that's a Player script. Hope this helped anybody.
1 comment
I've been looking for something that would directly cast a ray from one point to another and return all the parts that the ray passed through, and this will probably help me with that. Thanks!