Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

How to rotate a part about it's surface?

Asked by 6 years ago

I'm attempting to rotate a part about certain surfaces, TopSurface, RightSurface, etc. I have somewhat completed this but the positions do not seem to work properly when the part starts at different orientations.

I do know why some of the problems I have with my code, provided bellow, are happening, I just do not have an idea on what I should be using to fix this and why (Have had a hard time finding good CFrame/Vector3 tutorials that explain in-depth). Bellow my script's source is a break down of what I'm doing.

Script:

function Rotate(Part)
    local RotationPoint
    local Angles
    local function getVectorUnit(P)
        if P:FindFirstChild("HingeSurface") then
            if P.HingeSurface.Value == "TopSurface" then
                RotationPoint = Part.Position + Vector3.new(0, Part.Size.Y/2, 0)
                Angles = CFrame.fromAxisAngle(Part.CFrame.rightVector, math.pi/4)
            elseif P.HingeSurface.Value == "BottomSurface" then
                RotationPoint = Part.Position - Vector3.new(0, 0, Part.Size.Z/2)
                Angles = CFrame.fromAxisAngle(Part.CFrame.rightVector * -1, math.pi/4)
            elseif P.HingeSurface.Value == "RightSurface" then
                RotationPoint = Part.Position + Vector3.new(Part.Size.X/2, 0, 0)
                Angles = CFrame.fromAxisAngle(Part.CFrame.lookVector, math.pi/4)
            elseif P.HingeSurface.Value == "LeftSurface" then
                RotationPoint = Part.Position - Vector3.new(Part.Size.X/2, 0, 0)
                Angles = CFrame.fromAxisAngle(Part.CFrame.lookVector * -1, math.pi/4)
            end
        else
            if Part.Name == "Hood" then
                RotationPoint = Part.Position:FromNormalId() - Vector3.new(0, 0, Part.Size.Z/2)
                Angles = CFrame.fromAxisAngle(Part.CFrame.rightVector, math.pi/4)
            elseif Part.Name == "Trunk" then
                RotationPoint = Part.Position + Vector3.new(0, 0, Part.Size.Z/2)
                Angles = CFrame.fromAxisAngle(Part.CFrame.rightVector * -1, math.pi/4)
            end
        end
    end

    getVectorUnit(Part)

    local RotationCF =  CFrame.new(RotationPoint)
    local offsetCF = RotationCF:toObjectSpace(Part.CFrame)
    Part.CFrame = RotationCF * Angles * offsetCF
end

Rotate(game.Workspace.Hood)

  • Breakdown If you cannot tell by skimming the code, the script is editing the Trunk/Hood of a vehicle and is made into a function for the code to be added to the actual project. My script gets the specified part and checks if HingeSurface exists to know which surface is connected as if it had a hinge to rotate about. If HingeSurface does not exist, the vehicle is checked to be a Trunk or Hood because you can't open the hood or trunk of a vehicle on the same axis, they're the opposite. I know I have problems within the code that may not make sense because as I said, I'm no expert but any help is very appreciated!

Please comment or contact me if you require more information; this is the entire script.

2 answers

Log in to vote
1
Answered by
EgoMoose 802 Moderation Voter
6 years ago
Edited 6 years ago

Having a bit of difficulty understanding what you are trying to do...

I could be completely mistaken, but my understanding is that you want to have the hood of a car rotate up like you are opening it.

For the sake of argument I'll use the example of a chest because I can build a ugly one for this example. To my understanding you want the ability to rotate not from the center of an object, but rather from some other point. In this gif the top of the chest is rotating on it's edge.

To understand how this works we have to understand a little bit about how cframe inverses work. When you multiply a CFrame by it's inverse it becomes the identity CFrame, CFrame.new(). This is super useful b/c the identity CFrame is the equivalent of 1 in CFrame multiplication:

CFrame.Angles(math.pi, math.rad(-14), 0) * CFrame.new(10, -5, 4) * CFrame.new() == CFrame.Angles(math.pi, math.rad(-14), 0) * CFrame.new(10, -5, 4)

Note that the CFrame.new() essentially does nothing to the equality b/c it's like multiplying by 1! So thus say we have some any CFrame, x. The inverse simply means:

x * x:inverse() == x:inverse() * x == CFrame.new()

This is very useful if we treat our CFraming like algebra because it allows us to rearrange things to different sides (since we can't divide cframes).

Say we wanted to solve the following problem. We have two CFrames, A and B. We want to know what we have to multiply against A to get to B. In other words, an offset.

A * offset = B

We know A and B so we are trying to solve for offset. All we have to do is pre-multiply both sides by A:inverse() such that offset is the only thing left on the right hand side.

A:inverse() * A * offset = A:inverse() * B
CFrame.new() * offset = A:inverse() * B
-- Recall: CFrame.new() * offset == offset
offset = A:inverse() * B

If this is all new to you and you are still following then hopefully you will have a mental click moment b/c what we did above to calculate the above is one of the built in CFrame methods. The terms object and world space aren't very helpful to someone who doesn't know what that means, but maybe the above example with offset puts thing into a better perspective!

To continue from here let's step back from CFrames for a second. Say you have a blind friend staying at your house. They want to go out and buy a bag of chips from the local convience store, but of course they're blind so it's not easy for them to go to places they aren't familar with. Unfortunatley you are unavailable to guide them so you have to do your best to describe directions in a way they can navigate it. For someone with eyes and who knows the neighborhood you might just say the intersection where the store is, but that won't work for this friend? What do you do? Well one thing you could do is describe exact directions from where your friend is standing right now. For example, turn 180 degrees, walk 10 steps forward, turn left and walk 20 steps backwards, etc... When we describe directions like this we refer to it as object space because we treat the position that the object starts at as the center of the world.

Going back to CFrames in our case offset is considered object space because it describes the directions that would need to be followed only knowing the starting position/rotation at A to get to B. So in the case of our blind friend we could write a similar equation:

friend_starting_location * directions = store_location

Here's the thing say I change my friends staring location/orientation but still use the exact same directions. Well now I am no longer going to get to the store_location anymore because the directions were an exact set of instructions to get from one specific point to the other:

friend_starting_location * turn_5_degrees_right * directions ~= store_location

To see what I mean let's use a very simply example say my directions to the store are simply take 10 steps forward. Well if I simply adjust the starting location by rotating my freind by say 180 degrees they will no be walking in the complete opposite direction. In fact I can change the starting rotation by any degree and then have them walk forward to have them walk anywhere on a 10 unit radius circle.

friend_starting_location * turn_x_degrees * directions = some_point_on_circle

We can use this exact trick to do the same thing with CFrames.

local lid = game.Workspace.lid;
local edge = lid.CFrame * CFrame.new(lid.Size * Vector3.new(0, -0.5, 0.5))
-- edge * offset = lid.CFrame
local offset = edge:inverse() * lid.CFrame

local r = 0;
while true do
    r = r + math.rad(1);
    lid.CFrame = edge * CFrame.Angles(r, 0, 0) * offset;
    wait();
end;

That's pretty much it! Hope that helped even though it was a very long explanation.

There are a lot of CFrame pages out there on the wiki. I have a few I wrote while back, but idk if they will be helpful or not.

http://wiki.roblox.com/index.php?title=User:EgoMoose/Articles/CFramesAndVectors http://wiki.roblox.com/index.php?title=CFrame_Math

Check out this part of this article b/c it's basically what I was trying to describe above, but it has pictures! http://wiki.roblox.com/index.php?title=CFrame_Math#Rotating_a_door

Ad
Log in to vote
0
Answered by 6 years ago
if P.HingeSurface.Value == "TopSurface" then
               RotationPoint = Part.Position + Vector3.new(0, Part.Size.Y/2, 0)
               Angles = CFrame.fromAxisAngle(Part.CFrame.rightVector, math.pi/4)

If part is rotated so that the "TopSurface" was facing left part.Size.Y/2 would work. This is because if it was rotated 90* and the part isnt Completely square then it wont work because the difference is changing each time

Im not very good at explaining but what im trying to say is that part.Size.Y/2 is not a good way to measure the distance between the surface and its center.

I recommend having an attachment on each face and seeing the distance between the center and that part.

if P.HingeSurface.Value == "TopSurface" then
               RotationPoint = Part.Position + Vector3.new(0, (Part.TopAttachment.Position.Y-Part.Position.Y), 0)
               Angles = CFrame.fromAxisAngle(Part.CFrame.rightVector, math.pi/4)

(IDK if this will actually work my heads not working right. im just saying if the parts rotated then measuring the RotationPoint using the parts size isnt going to work.)

sorry for my explination. hope this helps

Answer this question