How do I Make Stackable Objects?

I basically have a placement system and I want it so that objects can stack on top of each other (Something like Roblox Islands). I have tried doing:

(math.floor(mouse.Hit.p.Y / Grid + 0.5) * Grid) + block.PrimaryPart.Size.Y / 2

But sometimes the object goes inside another object.

2 Likes

you need the surface normal that was hit.

placement systems like this can’t rely on rounding alone, you need to detect which face was hit with something like RaycastResult.Normal, but first you must learn how to cast a ray. (if you don’t already)

Casting rays

WorldRoot | Documentation - Roblox Creator Hub

Getting the surface hit from a ray

RaycastResult | Documentation - Roblox Creator Hub

Screenshot by Lightshot

1 Like

So basically I would have to check if the Normal was Top and then place a block.

yeah, you’ll use the Normal (the Vector3 returned from raycasting) to see what face was hit and then include that normal in your translation math.

so if the ray/mouse hits a top face then the Normal would be 1 stud above the top face.

to see this directly with math it would look like

local normal = Vector3.new(0,1,0) --top surface

(part.Position + part.Size/2) + normal

the Vector3 in this case would be the normal representing 1 stud above the top face, but you wont need math like this considering the raycast will already provide the normal relevant to the part that was hit.

1 Like

Is it always going to be 1 stud or just part.Size/2? Because in my case I’m using a 4x4x4 stud block.

it’ll always be 1 stud so take that into consideration, the part.Size/2 example was just to make the Vector3.new(0,1,0) relevant to the part. (when the normal is returned from raycasting its already relevant to the part that was hit, SO you dont need math like this (part.Position + part.Size/2), not yet atleast)

1 Like

I tried raycasting but since my blocks are not cancollide. It stacks normally but sometimes the block goes through another block. I don’t know what the problem is, I think since its not cancollide it counts the other faces.

why not just do something like this?

local size = Vector3.new(4,4,4) --The size value can alternatively be used to make the function operate on a grid.

--[[
axisPlace takes five arguments -
obj: The part which the new part is spawned from and positioned around.
axis: The axis you want to move in.
polarity: Is your movement positive (true) or negative (false)?
useDisplace: Are you using a displacement (true) or not using it (false)?
displace: Number value which displaces the object a certain distance on the chosen axis.
]]
local function axisPlace(obj,axis,polarity,useDisplace,displace)
	local object = Instance.new("Part")
	object.Anchored = true
	object.CanCollide = false
	object.Size = size
	object.Parent = obj
	
	if axis == 'x' then
		if polarity then
			object.Position = obj.Position + Vector3.new(object.Size.X,0,0)
			if useDisplace then
				object.Position = object.Position + Vector3.new(displace,0,0)
			end
		else
			object.Position = obj.Position - Vector3.new(object.Size.X,0,0)
			if useDisplace then
				object.Position = object.Position - Vector3.new(displace,0,0)
			end
		end
	elseif axis == 'y' then
		if polarity then
			object.Position = obj.Position + Vector3.new(0,object.Size.Y,0)
			if useDisplace then
				object.Position = object.Position + Vector3.new(0,displace,0)
			end
		else
			object.Position = obj.Position - Vector3.new(0,object.Size.Y,0)
			if useDisplace then
				object.Position = object.Position - Vector3.new(0,displace,0)
			end
		end
	elseif axis == 'z' then
		if polarity then
			object.Position = obj.Position + Vector3.new(0,0,object.Size.Z)
			if useDisplace then
				object.Position = object.Position + Vector3.new(0,0,displace)
			end
		else
			object.Position = obj.Position - Vector3.new(0,0,displace)
			if useDisplace then
				object.Position = object.Position - Vector3.new(0,0,displace)
			end
		end
	end
end

axisPlace(script.Parent,'x',true,false,nil)
axisPlace(script.Parent.Part,'y',true,false,nil)

If you’re using explicitly 4x4x4 blocks, this should be good enough. If your parts vary in size, you may have to use a conceptual grid.

I’m confused. What does this do?

The function spawns a part on any of its six sides using the axis and polarity arguments. Axis takes three strings: 'x','y','z'. Polarity is a boolean. true is positive, false is negative. It controls the direction the part moves in. displace is a number value that’ll move the part a certain distance on the chosen axis. displace is optional, you can toggle it with useDisplace.

full explanation of all args:

instance obj: The part which the new part will be spawned from.
string axis: The axis the new part moves along, takes strings ‘x’, ‘y’, and ‘z’.
bool polarity: Chooses which direction on an axis to move toward. true is positive, false is negative.
bool useDisplace: Chooses whether or not to use displace. true is to use displace, false is to not.
float displace: Displaces the part a specified distance from its original placement. Takes numbers.

You can also test it for yourself:
exampleparts.rbxl (21.0 KB)

1 Like

So I did manage to find out a solution on this post. I used the same method as you did except the block’s position was always the mouse’s position which made it go through other objects.