Placement system has issue to set the object correctly to the mouse when changing floors

Hello community

i am using the Placement System from zblox and i am having a hard time getting the floor feature correctly to work.

Currently i am using the Q and E keys to raise/lower the floors. This works perfectly fine. The only issue I have is when i raise my floor a space between my mouse and the object generates:

Here i am at the lowest floor and the object is perfectly positioned at my mouse:
(picture 1)
image

Now the more i raise the floor to build the more a space between these two generates:

(picture2)
image

In the following code snippet you can see that i customized a bit of the raiseFloor and lowerFloor functions from the original module. But this is only for the texture to go up with the model:

local function raiseFloor(actionName: string, inputState: Enum.UserInputState, inputObj: InputObject?)
	if not (currentState ~= 4 and inputState == Enum.UserInputState.Begin) then return end
	if not (enableFloors and not stackable) then return end	
	floorHeight += floor(abs(floorStep))
	floorHeight = math.clamp(floorHeight, 0, maxHeight)

	if preferSignals then changeFloors:Fire(true) end
	
	local gridPart = plot.plotGridPart
	if floorHeight < maxHeight then
		gridPart.Position = Vector3.new(gridPart.Position.X, gridPart.Position.Y + floorStep, gridPart.Position.Z)
	end
end

local function lowerFloor(actionName: string, inputState:Enum.UserInputState, inputObj: InputObject?)
	if not (currentState ~= 4 and inputState == Enum.UserInputState.Begin) then return end
	if not (enableFloors and not stackable) then return end
	floorHeight -= floor(abs(floorStep))
	floorHeight = math.clamp(floorHeight, 0, maxHeight)

	if preferSignals then changeFloors:Fire(false) end
	
	local plotBaseY = plot.Position.Y
	local gridPart = plot.plotGridPart
	if gridPart.Position.Y - floorStep >= plotBaseY then
		gridPart.Position = Vector3.new(gridPart.Position.X, gridPart.Position.Y - floorStep, gridPart.Position.Z)
	end
end

In the main function ‘activate’ where the player is then able to build we could see that the model gets correctly positioned to the mouse (picture 1).

function PlacementInfo:activate(ID: string, PlacedObjects: Instance, Plot: BasePart, 
	Stackable: boolean, SmartRotation: boolean, AutoPlace: boolean)

	if currentState ~= 4 then TERMINATE_PLACEMENT() end
	if GET_PLATFORM() == "Mobile" then mobileUI.Parent = player.PlayerGui end

	-- Sets necessary variables for placement 
	character = player.Character or player.CharacterAdded:Wait()
	plot = Plot
	object = self.Prefabs:FindFirstChild(tostring(ID)):Clone()
	placedObjects = PlacedObjects
	primary = object.PrimaryPart

	approveActivation()

	if displayGridTexture then displayGrid() end
	if includeSelectionBox then	displaySelectionBox() end
	if audibleFeedback then createAudioFeedback() end

	-- Sets properties of the model (CanCollide, Transparency)
	for i, inst in ipairs(object:GetDescendants()) do
		if not inst:IsA("BasePart") then continue end
		if transparentModel then inst.Transparency = inst.Transparency + transparencyDelta end

		inst.CanCollide = false
		inst.Anchored = true
	end

	if removeCollisionsIfIgnored then
		for i, v: Instance in ipairs(self.IgnoredItems) do 
			if v:IsA("BasePart") then v.CanTouch = false end
		end
	end

	object.PrimaryPart.Transparency = hitboxTransparency
	stackable = Stackable
	smartRot = SmartRotation

	-- Allows stackable objects depending on stk variable given by the user
	raycastParams.FilterDescendantsInstances = {placedObjects, character, unpack(self.IgnoredItems)}
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	if Stackable then
		raycastParams.FilterDescendantsInstances = {object, character, unpack(self.IgnoredItems)}
		raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	end

	-- Toggles buildmode placement (infinite placement) depending on if set true by the user
	canActivate = false
	if buildModePlacement then
		canActivate = true
	end

	-- Gets the initial y pos and gives it to y
	initialY = calculateYPos(Plot.Position.Y, Plot.Size.Y, object.PrimaryPart.Size.Y, 1)
	y = initialY
	removePlotDependencies = false
	autoPlace = AutoPlace
	local preSpeed = 1

	set()
	editHitboxColor()
	roundInts()
	bindInputs()

	if interpolation then
		preSpeed = clamp(abs(tonumber(1 - lerpSpeed)::number), 0, 0.9)
		speed = preSpeed

		if instantActivation then
			setup = true
			speed = 1
		end
	end

	-- Parents the object to the location given
	if not object then TERMINATE_PLACEMENT(); warn(messages["101"]) end
	setCurrentState(1)

	if instantActivation then translateObj() end
	object.Parent = PlacedObjects

	task.wait()	

	speed = preSpeed
	if preferSignals then activated:Fire() end
	setup = false
end

The only thing i could think of is that the objects gets positioned in the translateObj function where the calculateItemLocation is called to calculate its position but i am not exactly sure since it does not contain any mouse values. The translateObj function:

-- Sets the position of the object
local function translateObj(dt)
	if not (currentState ~= 2 and currentState ~= 4) then return end

	range = false
	setCurrentState(1)

	if getRange() > maxRange then
		setCurrentState(5)

		if preferSignals then outOfRange:Fire() end

		range = true
	end

	checkHitbox()
	editHitboxColor()

	if removePlotDependencies then plot = findPlot() or plot end

	if interpolation and not setup then
		object:PivotTo(primary.CFrame:Lerp(calculateItemLocation(primary.CFrame.Position, false), speed*dt*targetFPS))
		hitbox:PivotTo(calculateItemLocation(hitbox.CFrame.Position, true))	
	else
		object:PivotTo(calculateItemLocation(primary.CFrame.Position, false))
		hitbox:PivotTo(calculateItemLocation(hitbox.CFrame.Position, true))	
	end
end

The calculateItemLocation function:

-- Calculates the position of the object
local function calculateItemLocation(last, final: boolean): CFrame
	local x: number, z: number
	local sizeX: number, sizeZ: number = primary.Size.X*0.5, primary.Size.Z*0.5
	local offsetX: number, offsetZ: number = sizeX, sizeZ
	local finalC: CFrame

	if not currentRot then sizeX = primary.Size.Z*0.5; sizeZ = primary.Size.X*0.5 end

	if moveByGrid then
		offsetX = sizeX - floor(sizeX/GRID_UNIT)*GRID_UNIT
		offsetZ = sizeZ - floor(sizeZ/GRID_UNIT)*GRID_UNIT
	end

	local cam: Camera = workspace.CurrentCamera
	local ray
	local nilRay
	local target

	if isMobile then
		local camPos: Vector3 = cam.CFrame.Position
		ray = workspace:Raycast(camPos, cam.CFrame.LookVector*rangeOfRay, raycastParams)
		nilRay = camPos + cam.CFrame.LookVector*(maxRange + plot.Size.X*0.5 + plot.Size.Z*0.5)
	else
		local unit: Ray = cam:ScreenPointToRay(mouse.X, mouse.Y, 1)
		ray = workspace:Raycast(unit.Origin, unit.Direction*rangeOfRay, raycastParams)
		nilRay = unit.Origin + unit.Direction*(maxRange + plot.Size.X*0.5 + plot.Size.Z*0.5)
	end

	if ray then
		x, z = ray.Position.X - offsetX, ray.Position.Z - offsetZ

		if stackable then target = ray.Instance; else target = plot end
	else
		x, z = nilRay.X - offsetX, nilRay.Z - offsetZ
		target = plot
	end

	target = target

	local pltCFrame: CFrame = plot.CFrame
	local positionCFrame = cframe(x, 0, z)*cframe(offsetX, 0, offsetZ)

	y = calculateYPos(plot.Position.Y, plot.Size.Y, primary.Size.Y, 1) + floorHeight

	-- Changes y depending on mouse target
	if stackable and target and (target:IsDescendantOf(placedObjects) or target == plot) then
		if ray and ray.Normal then
			local normal = cframe(ray.Normal):VectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Top)):Dot(ray.Normal)
			y = calculateYPos(target.Position.Y, target.Size.Y, primary.Size.Y, normal)
		end	
	end

	if moveByGrid then
		-- Calculates the correct position
		local rel: CFrame = pltCFrame:Inverse()*positionCFrame
		local snappedRel: CFrame = snapCFrame(rel)*cframe(offsetX, 0, offsetZ)

		if not removePlotDependencies then snappedRel = bounds(snappedRel, sizeX, sizeZ) end
		finalC = pltCFrame*snappedRel
	else
		finalC = pltCFrame:Inverse()*positionCFrame

		if not removePlotDependencies then finalC = bounds(finalC, sizeX, sizeZ) end
		finalC = pltCFrame*finalC
	end

	-- Clamps y to a max height above the plot position
	y = clamp(y, initialY, maxHeight + initialY)

	-- For placement or no intepolation
	if final or not interpolation then
		return (finalC*cframe(0, y - plot.Position.Y, 0))*anglesXYZ(0, rotation*pi/180, 0)
	end

	return (finalC*cframe(0, y - plot.Position.Y, 0))*anglesXYZ(0, rotation*pi/180, 0)*calcAngle(last, finalC)
end

If you need further details to the scripts or have questions feel free to ask because i´ve tried to make the post small. Thank you very much!

Here, you are taking the part’s current height and adding floorStep. This is causing issues.
You already calculate floorHeight, jut use that.

Also, you ensure floorHeight is not bigger than maxHeight, why check?

gridPart.Position = Vector3.new(gridPart.Position.X, floorHieight, gridPart.Position.Z)

Hi
thanks for your answer.

Using floorHeight instead of floorStep makes no major difference in positioning the object to the mouse. I made a Part that the grid texture is added on. This Part is then just changed on its height.

I´ve tried your approach with floorHeight and you can see it works aswell:

gridPart.Position = Vector3.new(gridPart.Position.X, floorHeight + 1, gridPart.Position.Z)

Regarding my check for the height: When i use floorHeight instead of floorStep I indeed don´t need to make a check anymore. Thank you for that!

send the roblox place, I’ll look at it

To fix your problem:

plotGridPart.CanCollide = true

1 Like

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