How does this calculate rotation?

I am trying to recreate the trowel for my game, the trowel is placed down fine, but the issue is that I’m not sure how to recreate the calculation for rotation in the trowel script.

To me, it seems like the script is getting the direction of the player relative to the mouse position so that it can figure out which direction it should place the trowel down. But this issue with this is the snap() function because I’m not sure how it’s calculated.

local lookAt = snap( (targetPos - character.Head.Position).unit )

This is what snap looks like, but I’m not sure how the formula (snap()) works.

function snap(v)
	if math.abs(v.x)>math.abs(v.z) then
		if v.x>0 then
			return Vector3.new(1,0,0)
		else
			return Vector3.new(-1,0,0)
		end
	else
		if v.z>0 then
			return Vector3.new(0,0,1)
		else
			return Vector3.new(0,0,-1)
		end
	end
end

I guess it’s supposed to be for CFrame.lookAt, but I’m confused because the next line uses CFrame.new() but still has the same structure, so will it still work?

local cf = CFrame.new(targetPos, targetPos + lookAt)

I’m also not sure how the wall is built and the math behind that either. What does assert() do? How is each brick calculated?

function buildWall(cf)

	local color = BrickColor.Random()
	local bricks = {}

	assert(wallWidth>0)
	local y = 0
	while y < wallHeight do
		local p
		local x = -wallWidth/2
		while x < wallWidth/2 do
			local brick
			brick, p = placeBrick(cf, Vector3.new(x, y, 0), color)
			x = p.x
			table.insert(bricks, brick)
			wait(brickSpeed)
		end
		y = p.y
	end

	return bricks

end

If you need it, this is the full script for the trowel.

local wallHeight = 4
local brickSpeed = 0.04
local wallWidth = 12

local Tool = script.Parent

local MouseLoc = Tool:WaitForChild("MouseLoc",10)

-- places a brick at pos and returns the position of the brick's opposite corner
function placeBrick(cf, pos, color)
	local brick = Instance.new("Part")
	brick.BrickColor = color
	brick.CFrame = cf * CFrame.new(pos + brick.Size / 2)
	script.Parent.BrickCleanup:Clone().Parent = brick -- attach cleanup script to this brick
	brick.BrickCleanup.Disabled = false
	brick.Parent = game.Workspace
	brick:MakeJoints()
	return  brick, pos +  brick.Size
end

function buildWall(cf)

	local color = BrickColor.Random()
	local bricks = {}

	assert(wallWidth>0)
	local y = 0
	while y < wallHeight do
		local p
		local x = -wallWidth/2
		while x < wallWidth/2 do
			local brick
			brick, p = placeBrick(cf, Vector3.new(x, y, 0), color)
			x = p.x
			table.insert(bricks, brick)
			wait(brickSpeed)
		end
		y = p.y
	end

	return bricks

end


function snap(v)
	if math.abs(v.x)>math.abs(v.z) then
		if v.x>0 then
			return Vector3.new(1,0,0)
		else
			return Vector3.new(-1,0,0)
		end
	else
		if v.z>0 then
			return Vector3.new(0,0,1)
		else
			return Vector3.new(0,0,-1)
		end
	end
end


Tool.Enabled = true
function onActivated()

	if not Tool.Enabled then
		return
	end

	Tool.Enabled = false

	local character = Tool.Parent;
	local humanoid = character:FindFirstChildOfClass("Humanoid")
	if not humanoid then
		return 
	end

	local targetPos = MouseLoc:InvokeClient(game:GetService("Players"):GetPlayerFromCharacter(character))
	local lookAt = snap( (targetPos - character.Head.Position).unit )
	local cf = CFrame.new(targetPos, targetPos + lookAt)

	Tool.Handle.BuildSound:Play()

	buildWall(cf)

	wait(5)

	Tool.Enabled = true
end

Tool.Activated:Connect(onActivated)

2 Likes
(targetPos - character.Head.Position).unit

this code gets the vector which is a difference between the target position (assumed mouse position) and your head’s position, so this will be Vector3.new(0,0,-1) if your mouse position is 1 stud in front of your head. .unit is used to convert the vector’s axes into 0-1 numbers, aka a magnitude of 1 (if the difference ends up being Vector3.new(100,50,100), the .unit will convert the vector into Vector3.new(0.66, 0.33, 0.66)

The snap function reads that unit vector and checks if the x (left-right) value is greater than the z (forward-backward). if it is, then it will return a vector with an x-value of 1 if x is positive and -1 if x is negative. the function will instead return a vector with z-values instead of x-values if the original z value is greater than the x value.

in the cf variable, it is grabbing your targetPos (presumed to be mouse position) and making the wall face forward to the snap function’s vector (if snap returns Vector3.new(0,0,-1), the wall will face forward where your character is facing, if it returns Vector3.new(1,0,0) it will face to the left of your character, etc). CFrame.new(CFrame, Vector3) will return a cframe value that is looking at the Vector3, so this is used in the buildwall function to make it face that direction.

the buildWall function is building bricks in a grid layout with random colors, facing cf. assert is a function that will throw an error (or the supplied string value in the second argument) if the first argument returns false (if wallWidth is greater than 0, the function will error, so you probably need to change that to be LESS THAN OR EQUAL TO 0). I am assuming the placeBrick function creates a part and moves it to the cf plus the supplied Vector3 then returns the brick and the brick’s position, so the buildWall function is going to place bricks along the x-axis until it has built a wall with the width of wallWidth. the function uses wallWidth/2 because god knows what reason but you can think of it as having a width of 4, then creating parts at x = -2 and continuing until x = 2. this action is repeated wallHeight number of times, and increases the y-axis by 1 each iteration.

2 Likes

Alright, I understand how it works now, though instead of building the wall over time I decided to just clone the model into the workspace. Still, I am experiencing problems when doing this.

I was planning to make it so that the player can manually rotate the wall on the x and z axis and then place it down, even though I haven’t made that feature possible yet, I’m having an issue with

  1. When pivoting the model, the CFrame of the model goes underparts, specifically the baseplate, and not on top.
  2. Even though the rotation variable is set to CFrame.Angles(math.rad(0), math.rad(0), math.rad(0)), which should theoretically make it so that the basic value for the rotation won’t rotate the wall at all, makes the model slanted.

The Create Wall function (Server Script)

function createWall(player, mouseCFrame, rotation)
	local model = wallModel:Clone()
	model.Parent = workspace
	
	print(mouseCFrame)
	
	model:PivotTo(mouseCFrame * rotation)
end

The rotation variable and the code that fires a remote event located inside of a tool to the server. (Local Script)

local rotation = CFrame.Angles(math.rad(0), math.rad(0), math.rad(0))

mouse.Button1Down:Connect(function()
	local mouseCFrame = mouse.Hit
	
	if not cooldown then
		cooldown = true
		
		remoteEvent:FireServer(mouseCFrame, rotation)
		
		task.wait(7)
		
		cooldown = false
	end
end)
  1. you need to transform the mouse position on the client to be hitcf * CFrame.new(0, wall height/2, 0), this is because the wall is being created AT the mouse position, so we have to move the bottom of the wall to be the mouse position itself
  2. this is happening because mouse.hit does provide a rotation vector, so what you need to do is provide a copy of the cframe without its rotation. you can do this by setting mouseCFrame to CFrame.new(mouseCFrame.Position)
1 Like

If I wanted to make wall height a variable (I’m going to be making different trowels that have different heights), how do you suppose I can measure the height of any model?

Although I can manually do it and I already know the offset would be two studs, I’m still not sure how I would do this using math so that I can reuse the code.

if the server has a wallheight variable, just edit the mouseCFrame on the server instead of the client since you are sending it to the server anyways

Oh yeah I was planning to do that but my issue is that I don’t want to manually go and check the height in studs of each trowel, the basic height of a trowel wall’s model is four studs, so I can go off of that but theoretically, if I had another that was 8 but let’s say it’s just a mesh or something and I couldn’t calculate it then I’m not sure where I would go there, which is why I’m asking how I should go about calculating the height of a model.

Alright so I found out that I could use model:GetExtentsSize() to get the average size of a model and then get it’s Y size and divide it by two, and it works just fine, thanks for the help!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.