Grid placement system not being correctly on where mouse is

Hello developers! so, im working on game based of Miners Haven, and now making the grid placement system, following a tutorial on YT, but when tried to test it, it wasnt very on where the mouse is. I tried chaging the hitbox, the values, but nothing really worked.

https://gyazo.com/3b081f69dd96664b03111244e90fe6f0

In that video, the dropper has an small offset from the mouse, which is kinda annoying and not really accurate, but somehow it still fits in the grid.

Another image:

image

Code:

-- SETTINGS

-- BOOLS
local interpolation = true
local moveByGrid = true
local buildModePlacement = true

-- INTS
local rotationStep = 90
local maxHeight = 90

-- FLOATS
local lerpLevel = 0.7 --// 0 = Instant, 1 = No movement

-- OTHER
local gridTexture = 'rbxassetid://12796647774'
local placement = {}
placement.__index = placement

local players = game:GetService('Players')
local contextService = game:GetService('ContextActionService')
local runService = game:GetService('RunService')

local player = players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local mouse = player:GetMouse()

-- CONSTRUCTOR VARIABLES
local GRID_SIZE
local ITEM_LOCATION
local ROTATE_KEY
local TERMINATE_KEY

-- ACTIVATION VARIABLES
local object = nil
local placedObjects = nil
local plot = nil
local stackable = nil
local primary = nil

-- CALC VARIABLES
local posX, posY, posZ = 0, 0, 0
local speed = 1
local rotation = 0
local rotationVal = false

-- QUICK TYPE VARS
local cframe = CFrame.new
local angles = CFrame.fromEulerAnglesXYZ

local function renderGrid()
	local texture = Instance.new('Texture')
	texture.StudsPerTileU = GRID_SIZE
	texture.StudsPerTileV = GRID_SIZE
	texture.Texture = gridTexture
	texture.Face = Enum.NormalId.Top
	texture.Parent = plot
end

local function calcYPosition(toP, toS, oS)
	return (toP + toS * 0.5) + oS*0.5
end

local function rotate(actionName, inputState, inputOBJ)
	if inputState == Enum.UserInputState.Begin then
		rotation += rotationStep
		rotationVal = not rotationVal
	end
end

local function snap(c)
	local newX = math.round(c.X/GRID_SIZE)*GRID_SIZE
	local newZ = math.round(c.Z/GRID_SIZE)*GRID_SIZE
	return cframe(newX, 0, newZ)
end

local function calcItemPosition()
	local finalCframe = cframe(0,0,0)
	local x, z
	local offsetX, offsetZ
	
	if rotationVal then
		offsetX, offsetZ = primary.Size.X*0.5, primary.Size.Z*0.5
		x, z = mouse.Hit.X - offsetX, mouse.Hit.Z - offsetZ - offsetZ
	else
		offsetX, offsetZ = primary.Size.Z*0.5, primary.Size.X*0.5
		x, z = mouse.Hit.X - offsetX, mouse.Hit.Z - offsetZ - offsetZ
	end
	
	if stackable then
		posY = calcYPosition(mouse.Target.Position.Y, mouse.Target.Size.Y, primary.Size.Y)
	else
		posY = calcYPosition(plot.Position.Y, plot.Size.Y, primary.Size.Y)
	end
	
	if moveByGrid then
		local pltCFrame = cframe(plot.CFrame.X, plot.CFrame.Y, plot.CFrame.Z)
		local pos = cframe(x, 0, z)
		pos = snap(pos*pltCFrame:Inverse())
		finalCframe = pos*pltCFrame*cframe(offsetX, 0, offsetZ)
	else
		finalCframe = cframe(mouse.Hit.X, posY, mouse.Hit.Z)
	end
	
	finalCframe = cframe(finalCframe.X, posY, finalCframe.Z)
	
	return finalCframe*angles(0, math.rad(rotation), 0)
end

local function unbindInputs()
	contextService:UnbindAction('Rotate')
end

local function bindInputs()
	contextService:BindAction('Rotate', rotate, false, ROTATE_KEY)
end

local function translateObj()
	if placedObjects and object.Parent == placedObjects then
		calcItemPosition()
		
		object:PivotTo(object.PrimaryPart.CFrame:Lerp(calcItemPosition(), speed))
	end
end

-- 
local function verifyPlane()
	return plot.Size.X%GRID_SIZE == 0 and plot.Size.Z%GRID_SIZE == 0
end

local function approvePlacement()
	if not verifyPlane() then
		warn('The model cannot snap on the plot. Please change the plot size to fix this.')
		
		return false
	end
	
	if GRID_SIZE >= math.min(plot.Size.X, plot.Size.Z) then
		error('Grid size is too close to the size of the plot on at least one of the axis.')
		return false
	end
	
	return true
end

-- CONSTRUCTOR FUNCTION
function placement.new(g, o, r, t)
	local data = {}
	local metaData = setmetatable(data, placement)
	
	GRID_SIZE = g
	ITEM_LOCATION = o
	ROTATE_KEY = r
	TERMINATE_KEY = t
	
	data.grid = GRID_SIZE
	data.itemLocation = ITEM_LOCATION
	data.rotateKey = ROTATE_KEY
	data.terminateKey = TERMINATE_KEY
	
	return data
end

-- Does placement (id = name, pobjs = placeObjects, plt = plot, stk = stakable)
function placement:active(id, pobjs, plt, stk)
	object = ITEM_LOCATION:FindFirstChild(id):Clone()
	placedObjects = pobjs
	plot = plt
	stackable = stk
	primary = object.PrimaryPart
	rotation = 0
	
	if not approvePlacement() then
		return 'Placement could not work'
	end
	
	if not stackable then
		mouse.TargetFilter = placedObjects
	else
		mouse.TargetFilter = object
	end
	
	local tempSpeed = 1
	
	if interpolation then
		tempSpeed = math.clamp(math.abs(tonumber(1 - lerpLevel)), 0, 0.9)
		speed = 1
	end
	
	renderGrid()
	
	object.Parent = placedObjects
	
	task.wait()
	
	bindInputs()
	speed = tempSpeed
end

runService:BindToRenderStep('Input', Enum.RenderPriority.Input.Value, translateObj)

return placement
1 Like