I am making like blood splatters in my game and each one is simply a flat cylinder . They go in different directions meaning that they touch walls, floors , rooves ect . The problem is they will be at the wrong angle to the surface they touch like this:
figure out what your plate’s surface normal is, it is probably the up vector (cframe.UpVector) just get the normal of the wall it is touching by seeing which dot product of the unit vectors (You can paste them to a table by just creating a new part and reading off the basis vectors and then getting those and reflecting them so you have 6 vectors). after that go through each vector and calculate the dot product of it to the upVector of your plate’s surface normal. after sorting through the dot products, get the highest one and call it targetVector. Now, set the cframe of the plate to be CFrame.lookAt(self.Position, self.Position + (targetVector * epsilon))
All a CFrame is, is just Roblox lingo for an “affine matrix”. To dumb it down though it is just a way to represent a coordinate system. In an affine matrix, you represent the vector spaces orientation based on the “basis vectors”. Some parts of this matrix are those basis vectors, the ones which tell us what is positive on the y, what is positive on the x, what is positive on the z if the coordinate space was that CFrame. The normal for the plate can be calculated by just reading off the basis vectors of it, its CFrame.UpVector. The dot product is just one of the many ways to find the angle between two vectors. Moreover, the geometric definition is |a||b|cos(angleBetweenAandB). It is very cheap to calculate cause Roblox has dot product done on their backend with their vector library. Since the normal of the wall will always be a unit vector and so will the upvector we can just dumb it down to just be the dot product, no need to do inverse cosine because having it be sorted from least to greatest and getting the highest one will let it account for small precision errors. Remember that when the angle is 0 the x will be 1 on the unit circle.
--[[
wall is a part and bloodCircle is a flattened cylinder
bloodCircle.UpPart is a tiny part that is invisible which is directly above
the blood circle and is parented to bloodCircle.
it is assumed that bloodCircle is already touching alignBlood like shown in your image
]]
function alignBlood(wall, bloodCircle)
local wallNormals = {wall.CFrame.LookVector, -wall.CFrame.LookVector,
wall.CFrame.UpVector, -wall.CFrame.Up,
wall.CFrame.RightVector, -wall.CFrame.RightVector}
local alignerVector = bloodCircle.UpPart.Position - bloodCircle.Position
local biggest = {0, Vector3.new()}
for i=1,#wallNormals do
local dot = wallNormals[i]:Dot(alignerVector)
if dot > biggest[1] then
biggest[1] = dot
biggest[2] = wallNormals[i]
end
end
local transformedUpPart = bloodCircle.Position + biggest[2].Unit
bloodCircle.CFrame = CFrame.lookAt(bloodCircle.Position, transformedUpPart)
end