I managed to throw together a hacky revised script for this using your method and it works perfectly if I use a proxy/dummy part to stick to that exact position.
Though originally I aimed to prevent having to use a separate part. But I just realized SurfaceGuis do not wrap around BaseParts like Decals do, and since Decals aren’t X,Y
positional it would defeat my original goal.
local RayCastParams = RaycastParams.new()
local NormalSizeConstraints = {
[Enum.NormalId.Top] = function(Part) return Vector3.new(Part.Size.X,0,Part.Size.Z) end,
[Enum.NormalId.Bottom] = function(Part) return Vector3.new(Part.Size.X,0,Part.Size.Z) end,
[Enum.NormalId.Front] = function(Part) return Vector3.new(Part.Size.X,Part.Size.Y,0) end,
[Enum.NormalId.Back] = function(Part) return Vector3.new(Part.Size.X,Part.Size.Y,0) end,
[Enum.NormalId.Left] = function(Part) return Vector3.new(0,Part.Size.Y,Part.Size.Z) end,
[Enum.NormalId.Right] = function(Part) return Vector3.new(0,Part.Size.Y,Part.Size.Z) end,
}
local NormalOpposites = {
[Enum.NormalId.Top] = Enum.NormalId.Bottom,
[Enum.NormalId.Bottom] = Enum.NormalId.Top,
[Enum.NormalId.Front] = Enum.NormalId.Back,
[Enum.NormalId.Back] = Enum.NormalId.Front,
[Enum.NormalId.Left] = Enum.NormalId.Right,
[Enum.NormalId.Right] = Enum.NormalId.Left,
}
local FaceList = {
"LookVector","UpVector","RightVector"
}
local Part = script.Parent.BloodyPart
local Connection = nil
local function CheckPart(Hit)
if Hit.Transparency ~= 0 or Hit.Size.X < 1 or Hit.Size.Y < 1 or Hit.Size.Z < 1 then warn("Invalid Part") return end
return true
end
local function NearestNormalId(part,pos)
local relative = part.CFrame:PointToObjectSpace(pos) / part.Size
local x, y, z = relative.X, relative.Y, relative.Z
local ax, ay, az = math.abs(x), math.abs(y), math.abs(z)
if ax > ay and ax > az then
return x > 0 and Enum.NormalId.Right or Enum.NormalId.Left
elseif ay > ax and ay > az then
return y > 0 and Enum.NormalId.Top or Enum.NormalId.Bottom
else
return z > 0 and Enum.NormalId.Back or Enum.NormalId.Front
end
end
Connection = Part.Touched:Connect(function(Hit)
if not Connection or Hit.Transparency ~= 0 or not Hit.CanCollide then return end
local Results = {}
for _,VectorStr in pairs(FaceList) do
local Result = workspace:Raycast(Part.Position,Part.CFrame[VectorStr],RayCastParams)
if Result then
table.insert(Results,Result)
end
end
for _,VectorStr in pairs(FaceList) do
local Result = workspace:Raycast(Part.Position,-Part.CFrame[VectorStr],RayCastParams)
if Result then
table.insert(Results,Result)
end
end
if #Results <= 0 then return end
local ChosenCast,Distance = nil,math.huge
for _,Result in pairs(Results) do
if CheckPart(Result.Instance) and Result.Distance <= Distance then Distance = Result.Distance ChosenCast = Result end
end
if not ChosenCast then return end
Hit = ChosenCast.Instance
Connection:Disconnect()
local NormalEnum = NearestNormalId(Hit,ChosenCast.Position)
local Vector = NormalSizeConstraints[NormalEnum](Hit)
Vector = Vector3.new(math.clamp(math.random(Vector.X*.1,Vector.X*.5),math.min(Vector.X,1),5),
math.clamp(math.random(Vector.Y*.1,Vector.Y*.5),math.min(Vector.Y,1),5),
math.clamp(math.random(Vector.Z*.1,Vector.Z*.5),math.min(Vector.Z,1),5)
)
Part.AssemblyLinearVelocity = Vector3.new(0,0,0)
Part.Size = Vector
Part.Orientation = ChosenCast.Instance.Orientation
Part.Position = ChosenCast.Position
Part.WeldConstraint.Part1 = Hit
Part.Blood1.Face = NormalEnum
Part.Blood2.Face = NormalOpposites[NormalEnum]
Part.Blood1.Enabled = true
Part.Blood2.Enabled = true
Part.Blood1.Frame.Rotation = math.random(-360,360)
Part.Blood2.Frame.Rotation = math.random(-360,360)
Part.Parent = Hit
end)
RayCastParams.FilterType = Enum.RaycastFilterType.Whitelist
RayCastParams.FilterDescendantsInstances = {workspace}
RayCastParams.IgnoreWater = true
In conclusion thank you so much, this is just what I was hoping for. Cheers!