How to make a basic roof with scripting

Hello, I was wondering how to approach something like this roof style


with entire scripting, I already tried something with CFrame.LookAt but seems to be inaccurate, the thing I want it’s the brown part (basically the top), the interior from the roof I’ll be using triangles making 3 nodes, the left, the right, and the top(on the middle) but Idk how to make the brown parts

I’m struggling to visualize what you are trying to say in this second part, but maybe I’m just reading it wrong. Could you elaborate or maybe provide a visual? Is this explaining the same picture from above. Is code to generate the brown blocks all you are looking for? :slight_smile:

1 Like

Yes, I’m looking the code that generates the brown parts and about the second part of the post

I mean this

Using 3 nodes to generate a triangle that will be the interior from the roof, the interior is the white part of the main post image.

1 Like

So, you want a script that generates the roof? So like, it creates the parts and positions them via a script?

1 Like

Yes, I want to know how to make the brown parts with scripting as I’m creating a roof placement system

Hmm, well it’s pretty simple. Just use the Instance.new() method/function to create the parts, then use the CFrame.new() method/function.

I already tried with CFrame.lookAt (CFrame.new(point1, point2) it’s deprecated) and seems to be inaccurate.

If you give this code the top node’s CFrame as centerpoint it may work for you, I did not spend much time testing it yet. Here is what I wrote:

function roof_generate(centerpoint, length1, length2)
	local p1 = Instance.new('Part')
	p1.Parent = game.Workspace
	p1.Anchored = true
	p1.Size = Vector3.new(length2,1,length1)
	p1.BrickColor = BrickColor.new("Brown")
	p1.CFrame  = centerpoint * CFrame.new(0, 0, length1/3) * CFrame.Angles(math.rad(45), 0, 0)
		
	local p2 = Instance.new('Part')
	p2.Parent = game.Workspace
	p2.Anchored = true
	p2.Size = Vector3.new(length2,1,length1)
	p2.BrickColor = BrickColor.new("Brown")
	p2.CFrame  = centerpoint * CFrame.new(0, 0, length1/3*-1) * CFrame.Angles(math.rad(-45), 0, 0)
end

roof_generate(CFrame.new(0,10,0), 7, 5) -- example call

--centerpoint is a cframe value that the build is based around
--length1 is how long the part will be downward and how it will generate
--length2 is part width

You will probably need to play with it some to get it to work but the math is there.
image

1 Like

Thanks! I’ll be playing a little bit with the code snippet to achieve what I need :smiley:

Hey, just tried your code but it doesn’t seem to work with bigger sizes.

Again, you will need to make some adjustments depending on your use cases. You may consider separating the pieces by more (I separated by length/3), or coming up with a more modular version of the math. The concepts at work will still apply, it is more or less just fiddling with the numbers to reach your desired result.

Yeah, the separation is not a constant, so trying to set the separation manually with a constant would be a poor solution as it’s not the variable.

1 Like

I have a new idea, just testing it in Studio before I try to explain it. I am not very good at code math but I still find it entertaining. :sweat_smile:

Here is my new solution, it will work at any scale hopefully. Very versatile but is also probably inefficient.

function roof_generate(centernode, roofheight, roofwidth)
	local top_node = centernode * CFrame.new(0,roofheight,0)
	local side_node1 = centernode * CFrame.new(0,0,roofwidth/2)
	local side_node2 = centernode * CFrame.new(0,0,roofwidth/2*-1)
	
	local part1 = Instance.new("Part")
	part1.Anchored = true
	part1.Parent = workspace
	
	local part2 = Instance.new("Part")
	part2.Anchored = true
	part2.Parent = workspace
	
	local distance = (top_node.p - side_node1.p).magnitude
	part1.Size = Vector3.new(roofwidth, 1, distance)
	part2.Size = Vector3.new(roofwidth, 1, distance)
	
	part1.CFrame = CFrame.new(side_node1.p, top_node.p) * CFrame.new(0, 0, -distance/2)
	part2.CFrame = CFrame.new(side_node2.p, top_node.p) * CFrame.new(0, 0, -distance/2)
end

roof_generate(workspace.Part.CFrame, 10, 50) -- example call

The function takes arguments for the midpoint, height, and width of the roof.

Here is my method:

Summary
local function generateRoof(depth,width,thickness,angleOfElevation,location)
	local roof1 = Instance.new("Part")
	roof1.Anchored = true
	roof1.Size = Vector3.new(width,thickness,depth)
	roof1.CFrame -= Vector3.new(width,thickness,0)/2 
	local pivotPoint = CFrame.new(0,0,0) -- Create decoy pivot that will "rotate"
	local offset = pivotPoint:toObjectSpace(roof1.CFrame) -- Get the offset from the part to the pivot
	local newPivot = pivotPoint*CFrame.Angles(0,0,math.rad(angleOfElevation))
	roof1.CFrame = newPivot*offset
	roof1.Position += location
	roof1.Parent = workspace
	
	local roof2 = Instance.new("Part")
	roof2.Anchored = true
	roof2.Size = Vector3.new(width,thickness,depth)
	roof2.CFrame += Vector3.new(width,-thickness,0)/2
	local pivotPoint = CFrame.new(0,0,0) -- Create decoy pivot that will "rotate"
	local offset = pivotPoint:toObjectSpace(roof2.CFrame) -- Get the offset from the part to the pivot
	local newPivot = pivotPoint*CFrame.Angles(0,0,math.rad(-angleOfElevation))
	roof2.CFrame = newPivot*offset
	roof2.Position += location
	roof2.Parent = workspace
end

generateRoof(25,50,1,45,Vector3.new(0,15,0))

The idea is to generate a roof with one of it’s corner at (0,0,0) then we rotate it around that pivot by the angle of elevation based on how steep the roof is.

This should prevent the problem that the roof has where there is a ridge at the very top as this method guarantees that a point of the part will be directly at that ridge in order to fill the gap.

Edit: Unfortunately I have found that this method will cause a problem if the roof is too thick and the angle is too steep as one part of the roof will just zoop past the other.

image

One solution is to take into account this scenario with more math, the second is to just avoid it.

2 Likes

Ahaha this is probably better than mine, I just like trying to solve these math problems lol.

This should work as the user cannot modify the part thickness, just the elevation, and has a limit to elevate the roof, I’ll be testing your code!

I already tried doing some maths to make it work as I need and does perfect job, but I don’t figure out how to preserve the angle to make the width don’t go to 2048 studs lol

Example of the “error”

And this is what I want to achieve (it’s a mesh but it’s good as an example)

Basically I want to preserve the angle like in the mesh example.


This is my current code to calculate between two nodes (using your function)

local q = Instance.new("Model", workspace)

game:GetService("RunService").Heartbeat:Connect(function()
    
    local point1 = script.Parent.Point1.Position
    local point2 = script.Parent.Point2.Position

    local depth = math.abs(point2.X - point1.X)
    local width = math.abs(point2.Z - point1.Z)
    
    q:ClearAllChildren()
    
    generateRoof(
        width,
        depth / 2,
        0.5,
        30, -- how I would calculate the angle to make it extend based on the width?
        point1:Lerp(point2, 0.5),
        q
    )
    
end)