Issue with rotating an object via RemoteEvent

Hi there.

local x, y, z = objClone.PrimaryPart.CFrame:ToEulerAnglesYXZ()
placeEvent:FireServer(objName, mainColor, mainMaterial, extraColor, extraMaterial, objClone.PrimaryPart.Position, math.deg(x), math.deg(y), math.deg(z))

I send the object’s data to the server, where it should create a clone and position it

print('And orientation')
print(objRotX, objRotY, objRotZ)
objClone:SetPrimaryPartCFrame(CFrame.new(objPos) * CFrame.Angles(math.rad(objRotX), math.rad(objRotY), math.rad(objRotZ)))

using that. However, in game, I get this: (Print statements are there too). The orientation statements are correct, but it doesn’t show it the same when i click in the properties tab and check the primary part orientation.

https://gyazo.com/e4746b89b413b94bae52f768e139c10e As you can see, it doesn’t place it correctly. The angle tilt is off

Instead of setting a cframe, you can just simply set a rotation. Like this:

part.Rotation = Vector3.new(0,0,0)

(you can use this after you set a position if you want)

That doesn’t work and that’s also not how I want to do it. Setting the PrimaryPart cframe for clientside rendering works fine. However, passing the values to the server doesn’t really work well.

I don’t think theres enough of the logic present here to give you a answer but just for the sake of consistency I’d start out with:

local cf      = objClone:GetPrimaryPartCFrame()
local x, y, z = cf:ToOrientation()
placeEvent:FireServer(
  objName, 
  mainColor, 
  mainMaterial, 
  extraColor, 
  extraMaterial, 
  cf.p, 
  x,
  y,
  z
)

-- server
objClone:SetPrimaryPartCFrame(CFrame.new(objPos) * CFrame.Angles(objRotX, objRotY, objRotZ))

This works a bit better, but still is a bit of an issue. Sometimes, usually on whole number normals, it will place correctly, but when it’s placing on a normal that isn’t a whole number, it places a bit weird. Not normal-related though.

like i said i’d probably have to look at more of what you’re doing to exactly diagnose an issue in the math

something else to consider is that CFrame:toOrientation() is a heuristic so you might be seeing some numerical error along with floating point error for those whole numbers. hard to say.

So for the clientside rendering, this is how I have it place the object at the mouse when you are moving the object

objClone:SetPrimaryPartCFrame((CFrame.new(newPos, newPos + normal) * CFrame.Angles(0, 0, math.rad(rotY)) * CFrame.Angles(math.rad(90),math.rad(0), math.rad(-180))))

I’m now doing this to prepare to send to server.

local objCloneCF = objClone:GetPrimaryPartCFrame()
local x, y, z = objCloneCF:ToOrientation()
placeEvent:FireServer(objName, mainColor, mainMaterial, extraColor, extraMaterial, objClone.PrimaryPart.Position, x, y, z)				

And on server, I am now doing

objClone:SetPrimaryPartCFrame(CFrame.new(objPos) * CFrame.Angles(objRotX, objRotY, objRotZ))

So when you place normally, the orientation before and after place looks like this:

But for complex angles, it looks like this for before and after

note the orientation change on the element before it’s placed and after it’s placed.

1 Like

so i’m going to take the liberty of simplifying some of what you’re doing, then i’ll ask that you restructure client-server communication such that we’re passing the CFrame component and not a decomposition of positions and angles. the difference between serializing position and orientation components relative to the entire CFrame, i think, is negligible considering that we don’t communicate this information to the server very rapidly and we are already sending a lot of other information so whats some more in the grand scheme of preformance? sending the whole cframe rather than a decomposition of it has the added benefit of not allowing for errors in the process of translation.

as for simplification, roblox’s workflow for working multipart assets is a bit neglected but i’ll give you two tips for dealing with this.

  1. first is to make a plugin that lets you post-process the assets you’re using for the building mechanic by adding a extra bounding box part and setting the model’s primary part to that.
  2. if you can, use imported meshes (blender fbx to roblox export scale is 0.01, prototype in roblox replicate in blender and import) or union the thing the geometry permits it.
  3. as an addition for #1, if you look around you can find some stuff that calculates the center of a collection of geometries rather than the center of bounding box using some mass heuristics but it’s expensive and complicated.

as for the code what you’re trying to do is find surface and position the part relative to that. here is the code for that:


local CFrameU = {}

function CFrameU.getRotationBetween(u, v, axis)
  local dot, uxv = u:Dot(v), u:Cross(v)
  if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
  return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
end

local EXTRASPIN = CFrame.fromEulerAnglesXYZ(math.pi/2, 0, 0)

function CFrameU.getSurfaceCF(part, lnormal)
    local transition = CFrameU.getRotationBetween(Vector3.new(0, 1, 0), lnormal, Vector3.new(0,0, 1))
    return part.CFrame * transition * EXTRASPIN
end

return CFrameU

here lnormal is typically going to be the look vector of the instance of Mouse.Hit is targeting. as an example use case i can cast a ray and position two parts around the rays hit position with something like:

RunService.RenderStepped:Connect(function()
    local cf = LocalPlayer.Character:GetPrimaryPartCFrame()

    local rayResult = workspace:Raycast(
      cf.p,
      cf.LookVector * armLength,
      armRayParams
    )

    if rayResult then
      local surfaceNorm = CFrameU.getSurfaceCF(rayResult.Instance, rayResult.Position - rayResult.Instance.Position)
      local offset      = rayResult.Position - rayResult.Instance.Position

      visL.CFrame = surfaceNorm * CFrame.new(-armWidth, 0, 0) + offset
      visR.CFrame = surfaceNorm * CFrame.new(armWidth, 0, 0)  + offset
    end
  end)

except for your use case you’ll want to account for the models bounding box in offset. if your primary part is a bounding box part like mentioned in #2 then it’s just a matter of adding BoundingBox.Size / 2 to offset. here is a working example:

1 Like

How would I position and size a bounding box dynamically to the gathered total corners of the overall model?

you should rotate the BB as the model’s primary part that way everything else rotates relative to it. i think what you’re thinking of is keeping the BB’s rotation static, which would not be helpful here.

Your entire system is a bit complicated and would require a rework of my script and a bunch more unseen logic, so I’m only taking pieces of it.

For my recent comment, I’m planning to have the BB as the primary part at some point in the future, and have the rest of the model relative to that via welds.

it’s certainly complicated but i promise it saves you a lot of work as the heterogeneity your building assets increases. i did exactly this with boat building a while ago.

Alright well thank you, I’ll be sure to start to switch it over a bit.

1 Like