Need help coding a script

Hi!

I need some help coding a script that makes it possible to move nodes up and down, and change the triangles’ size, position and pivot.

image

In the image above, the 4 spheres, represent the nodes, and the triangles speak for themselves.

When I move a node up or down, the triangle(s) should adapt with them. For example if I move the top left node up:

image

But then of course, that the triangles actually touch the node and fill the square when viewed from above.

I’ve done some experimental things and math and came to the conclusion that in order to have the triangles change in size, position and pivot to ensure they touch each node and fill the square together we need to do this:

  1. First of all, we need to assign base nodes for each triangle, let’s select the bottom left one as the base for each triangle. And set the pivot of the triangle to the bottom left edge.

  2. After that, we need to know where the other 2 nodes are, are they on the left or right, or in front or in the back?

  3. After we’ve located the remaining 2 nodes, we compare their x vector with the base node’s x vector.

  4. If a node differs in the x vector, we check if they are alligned on the y axis or the z axis (assuming the triangles are laid flat out and not pointing up or down)

  5. Once we identified on which axis the nodes are that differ in height, we can use the formulas for the required node.

  6. When the node is on the y axis

    y -axis formulas
    1. First we need to calculate the distance between the 2 nodes.

    We do this by using the euclidean distance formula:

    distance = square root( (x2-x1)^2 + (y2-y1)^2 + (z2-z1)^2 )
    
    1. After that we can calculate the angle between the 2 nodes:
      We first need to calculate the difference between the 2 positions of the nodes.
      We simply extract the base node position from the node’s position we want to calculate.
      After that we got the difference vector, from the difference vector, either the x, y or z is 0, meaning that we should not use that one in the following formula.
    angle = 90 - (arctan(delta y / delta x))
    

    please note that I am not quite sure why doing it minus 90 works, and I am also not sure if it works for all outcomes.

    1. Now that we have both the distance and the angle, we can calculate the new size, position and pivot.
      • For the size, it is as simple as checking in which axis the node lies, y or z. If it lies in the y axis, you add the distance to the y value of Size samething for when it is lying on the z axis.
      • For position:
        • posX2 = posX1 + (-(delta Size y / 2) * sin(angle))
        • posY2 = posY1 + ((delta Size y / 2) * cos(angle))
        • poxZ2 = posZ1
      • For pivot:
        • pX2 = pX1
        • pY2 = pY1 - (delta Size y / 2)
        • pZ2 = pZ1

    When the node is on the z axis

    z-axis formulas
    1. Again we need to calculate the distance and angle first, these formulas are universal.
    2. Calculating new size, position and pivot:
      • As mentioned in “y-axis formulas” the calculated distance needs to be added up to the z value of “Size”
      • Position:
        • posX2 = posX1
        • posY2 = posY1 + ((delta Size z / 2) * sin(angle))
        • pos Z2 = posZ1 - ((delta Size z / 2) * cos(angle))
      • Pivot:
        • pX2 = pX1
        • pY2 = pY1
        • pZ2 = pZ1 + (delta Size z / 2)

So, I have the math to make it happen, and it works, I have tested it in Roblox Studio without code and it succesfully works.

The thing is, I don’t really know how to code it to ensure that it works through scripts. So far I’ve only gotten to the point where I can drag the nodes up and down, and have the triangles change position, size etc but not really how I expected it.

Any suggestions, help etc is appreciated!

1 Like

Here is a page that I think would be beneficial for you to check out. It has great explanation and a simple bit of code to draw the triangles for you. It even shows moving nodes like you are after.

1 Like

This is exactly what I wanted! Thanks for your response!

As you can see it works, I can move the nodes up and down, have a grid, and do what I needed:

image

However, as this is a part of an infinite road system, where the roads curve along the terrain (which is why I needed this system) the small gaps between the triangles are something I need to resolve:

image

EDIT: Current script:

-- Create a template wedge
local wedgeTemplate = Instance.new("WedgePart")
wedgeTemplate.Anchored = true
wedgeTemplate.TopSurface = Enum.SurfaceType.Smooth
wedgeTemplate.BottomSurface = Enum.SurfaceType.Smooth

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

-- Function to enable in-game node movement
local function enableNodeMovement(node, updateTriangles)
	local dragging = false
	local dragOffset = 0

	UserInputService.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			local mouse = UserInputService:GetMouseLocation()
			local ray = workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y)
			local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 1000, RaycastParams.new())

			if raycastResult and raycastResult.Instance == node then
				dragging = true
				dragOffset = raycastResult.Position.Y - node.Position.Y
			end
		end
	end)

	UserInputService.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			dragging = false
		end
	end)

	UserInputService.InputChanged:Connect(function(input)
		if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then
			local mouse = UserInputService:GetMouseLocation()
			local ray = workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y)
			local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 1000, RaycastParams.new())

			if raycastResult then
				node.Position = Vector3.new(node.Position.X, raycastResult.Position.Y - dragOffset, node.Position.Z)
				updateTriangles() -- Update all affected triangles
			end
		end
	end)
end

-- Function to draw a single triangle
local function draw3dTriangle(a, b, c, parent, wedge1, wedge2)
	local ab, ac, bc = b - a, c - a, c - b
	local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc)

	if (abd > acd and abd > bcd) then
		c, a = a, c
	elseif (acd > bcd and acd > abd) then
		a, b = b, a
	end

	ab, ac, bc = b - a, c - a, c - b

	local right = ac:Cross(ab).unit
	local up = bc:Cross(right).unit
	local back = bc.unit

	local height = math.abs(ab:Dot(up))

	wedge1 = wedge1 or wedgeTemplate:Clone()
	wedge1.Size = Vector3.new(0.2, height, math.abs(ab:Dot(back)))
	wedge1.CFrame = CFrame.fromMatrix((a + b) / 2, right, up, back)
	wedge1.Parent = parent

	wedge2 = wedge2 or wedgeTemplate:Clone()
	wedge2.Size = Vector3.new(0.2, height, math.abs(ac:Dot(back)))
	wedge2.CFrame = CFrame.fromMatrix((a + c) / 2, -right, up, -back)
	wedge2.Parent = parent

	return wedge1, wedge2
end

-- Function to update all triangles in the grid
local function updateGridTriangles(gridNodes, gridSize, parent, triangles)
	for y = 1, gridSize - 1 do
		for x = 1, gridSize - 1 do
			local idx1 = (y - 1) * gridSize + x
			local idx2 = idx1 + 1
			local idx3 = idx1 + gridSize
			local idx4 = idx3 + 1

			local node1 = gridNodes[idx1]
			local node2 = gridNodes[idx2]
			local node3 = gridNodes[idx3]
			local node4 = gridNodes[idx4]

			local trianglePair = triangles[idx1] or {{nil, nil}, {nil, nil}}
			triangles[idx1] = trianglePair

			-- Triangle 1: (node1, node2, node3)
			trianglePair[1][1], trianglePair[1][2] = draw3dTriangle(
				node1.Position, node2.Position, node3.Position, parent, trianglePair[1][1], trianglePair[1][2]
			)

			-- Triangle 2: (node3, node4, node2)
			trianglePair[2][1], trianglePair[2][2] = draw3dTriangle(
				node3.Position, node4.Position, node2.Position, parent, trianglePair[2][1], trianglePair[2][2]
			)
		end
	end
end

-- Main script
local parent = workspace
local gridSize = 5 -- Change this for larger grids
local gridNodes = {}
local triangles = {}

-- Generate the grid nodes dynamically
for y = 0, gridSize - 1 do
	for x = 0, gridSize - 1 do
		local node = Instance.new("Part")
		node.Name = "Node_" .. tostring(x) .. "_" .. tostring(y)
		node.Anchored = true
		node.Size = Vector3.new(0.4, 0.4, 0.4) -- Slightly larger for visibility
		node.Shape = Enum.PartType.Ball
		node.Material = Enum.Material.Neon
		node.Color = Color3.fromRGB(0, 0, 139) -- Deep blue
		node.Position = Vector3.new(x * 4, 5, y * 4) -- Spawn 5 studs higher
		node.Parent = parent

		table.insert(gridNodes, node)
	end
end

-- Enable movement for all nodes
local function updateTriangles()
	updateGridTriangles(gridNodes, gridSize, parent, triangles)
end

for _, node in ipairs(gridNodes) do
	enableNodeMovement(node, updateTriangles)
end

-- Initial triangle generation
updateTriangles()

-- Update triangles in real-time
RunService.RenderStepped:Connect(updateTriangles)

If you change the thickness of your pieces to like .001 you shouldn’t see those gaps.