How to use my placement module v3

How to use Zblox’s placement module V3

Hello! A few months back, I released a new placement module. I have created two others but this one is by far the best. It adds more features and is easier to customize. In this tutorial, I will show you:

  • The initial setup
  • Creating models for the module
  • How to use the placement module in code
  • Customizing the placement system
  • Limitations
  • Other

Before I go any further, you will need the placement module. Once you have that, you can begin the tutorial.


Initial Setup

The first thing you need to do is make sure you have your game setup to use the module. You will need at least one plot of land to place down objects as well as a Folder or Model to hold the placed objects. I like to put a Folder located in the plot named something like itemHolder or tycoonItems.
itemthing
NOTICE - Your plot must be a multiple of your grid size. The grid size is the number of units your model will move by. In our case, the unit we’re using is studs.

You will also need to make three folders located in ReplicatedStorage. One for remotes, one for models and one for modules.
folders

Ungroup the module and place it in modules. You can leave models alone but do create a RemoteFunction called requestPlacement located in remotes.

You will also need a Script in ServerScriptService to handle the server side of things.

The final thing you need to add is a way to start placement. In this tutorial, I will be using UI as it’s going to be the simplest way to do it. Just add a ScreenGUi with a TextButton. You will also need a LocalScript in the button. This is what I have:
Capture
That should be it for the setup.


Building objects

Building objects is quite easy. All you do is, take a model you want to place, add a PrimaryPart to it and your done! How do you do that you may ask? Well, here’s how. First, you’ll need a model to work with. You should already know how to build models before coming to this tutorial. Really, all you need to do is add a PrimaryPart to the model. You can think if a PrimaryPart being the hitbox of the model. To make a PrimaryPart all you need to do is scale a part around the model (make sure there is no decimals in the scale). Then you can place that part in the model making it a child of that model. Select the model and in the properties menu, click on PrimaryPart. You will notice that your cursor has changed it’s icon. You can now select the part you want to be the PrimaryPart. You will probably want to lower the transparency of the PrimaryPart as it now covers the model. You should have something that looks like this now:
capture2.PNG

TIP

When building the models PrimaryPart, you may want to put the grid texture on the plot/plane your working on. This will help with making sure the model snaps to the grid. As long as it snaps to the grid while building, it should while placing.

That’s it! You can now place your model in the models folder we created earlier!


Using the placement module

Now that most things are set up, we can begin making it come to life! Open the LocalScript you created in the TextButton. We need to declare a few variables first. The services we will need are Players and ReplicatedStorage.

local players = game:GetService("Players")
local replicatedStorage = game:GetService("ReplicatedStorage")

We are going to need the mouse so we will also declare variables for the LocalPlayer and said players Mouse object.

local player = players.LocalPlayer
local mouse = player:GetMouse()

I like to also get references to the RemoteFunction and TextButton instances as well but this is not required.

local remote = replicatedStorage.remotes:WaitForChild("requestPlacement")
local button = script.Parent

Now of course we are going to need the module. If you don’t already know, we can use the require() method/function to return the module.

local placementModule = require(replicatedStorage.modules:WaitForChild("PlacementModuleV3"))

Before you can call any methods/functions on the module, we need to give it some information. We do this using the new() function. You call the function like this: local placement = placementModule.new(). The new function has multiple parameters you need to pass into it in order for it to work.

  • int Grid size
  • instance Item location
  • Enum Rotate key keycode
  • Enum Terminate/Cancel key keycode
  • Enum Raise floor key keycode
  • Enum Lower floor key keycode

Once you input those parameters you should have something like this:

local placement = placementModule.new(
	2,
	replicatedStorage.models,
	Enum.KeyCode.R, Enum.KeyCode.X, Enum.KeyCode.U, Enum.KeyCode.L
)

Whenever you need to call a function on the module, you should use this new placement variable. So far, you should have a script that looks similar to this:

local players = game:GetService("Players")
local replicatedStorage = game:GetService("ReplicatedStorage")

local player = players.LocalPlayer
local mouse = player:GetMouse()

local remote = replicatedStorage.remotes:WaitForChild("requestPlacement")
local button = script.Parent

local placementModule = require(replicatedStorage.modules:WaitForChild("PlacementModuleV3"))

local placement = placementModule.new(
	2,
	replicatedStorage.models,
	Enum.KeyCode.R, Enum.KeyCode.X, Enum.KeyCode.U, Enum.KeyCode.L
)

Now, this won’t do anything yet. Before we “do anything” we need to add in some Events or more formally known as RBXScriptSignals. We only need two of them. One to listen for the the player to click the button and one to listen for a mouse click.

button.MouseButton1Click:Connect(function()
	
end)

mouse.Button1Down:Connect(function()
	
end)

We are going to activate placement when the player clicks the button and request to place down the object when we click the mouse (not on the button). To activate placement we invoke the function activate() on the placement variable and not the module reference. For this, make sure you are using a : and not a . to call this function. The parameters it takes are listed below:

  • string Name of the model
  • instance itemHolder location (folder where the model will be placed)
  • instance plot location
  • bool rotation type - If the model can rotate around 360 degrees or if it just rotates x amount of degrees back and fourth.
  • bool if you want the object to stack on other objects

You should have something that looks like this now:

button.MouseButton1Click:Connect(function()
	placement:activate("Fence", workspace.base.itemHolder, workspace.base, true, false)
end)

Now you should have a working “move around object system”. To make this a placement system, we need to call one last function. When we click the mouse, we want to send a request to the server to place the object. We can use the method requestPlacement() on the placement variable. This only takes one parameter, the RemoteFunction.

mouse.Button1Down:Connect(function()
	placement:requestPlacement(remote)
end)

This is all we need to do for the client. Your client code should look like this now:

local players = game:GetService("Players")
local replicatedStorage = game:GetService("ReplicatedStorage")

local player = players.LocalPlayer
local mouse = player:GetMouse()

local remote = replicatedStorage.remotes:WaitForChild("requestPlacement")
local button = script.Parent

local placementModule = require(replicatedStorage.modules:WaitForChild("PlacementModuleV3"))

local placement = placementModule.new(
	2,
	replicatedStorage.models,
	Enum.KeyCode.R, Enum.KeyCode.X, Enum.KeyCode.U, Enum.KeyCode.L
)

button.MouseButton1Click:Connect(function()
	placement:activate("Fence", workspace.base.itemHolder, workspace.base, false, false)
end)

mouse.Button1Down:Connect(function()
	placement:requestPlacement(remote)
end

If you notice, we still have a “moving objects with mouse simulator” here. To fix this, we need to add a bit to our server script in ServerScriptService. This script is kind of long so I wont be explaining much here. Just copy the code in:

Server Code
local replicatedStorage = game:GetService("ReplicatedStorage")

--Ignore the top three functions

-- Credit EgoMoose
local function checkHitbox(character, object)
	if object then
		local collided = false
		
		local collisionPoint = object.PrimaryPart.Touched:Connect(function() end)
		local collisionPoints = object.PrimaryPart:GetTouchingParts()
		
		for i = 1, #collisionPoints do
			if not collisionPoints[i]:IsDescendantOf(object) and not collisionPoints[i]:IsDescendantOf(character) then
				collided = true
				
				break
			end
		end
		
		collisionPoint:Disconnect()
		
		return collided
	end
end

local function checkBoundaries(plot, primary)
	local lowerXBound
	local upperXBound
	
	local lowerZBound
	local upperZBound
	
	local currentPos = primary.Position
	
	lowerXBound = plot.Position.X - (plot.Size.X*0.5) 
	upperXBound = plot.Position.X + (plot.Size.X*0.5)
	
	lowerZBound = plot.Position.Z - (plot.Size.Z*0.5)	
	upperZBound = plot.Position.Z + (plot.Size.Z*0.5)
	
	return currentPos.X > upperXBound or currentPos.X < lowerXBound or currentPos.Z > upperZBound or currentPos.Z < lowerZBound
end

local function ChangeTransparency(item, c)
	for i, o in next, item:GetDescendants() do
		if o then
			if o:IsA("Part") or o:IsA("UnionOperation") or o:IsA("MeshPart") then
				o.Transparency = c
			end
		end
	end
end

--Ignore above

local function place(plr, name, location, prefabs, cframe, c, plot)
	local item = prefabs:FindFirstChild(name):Clone()
	item.PrimaryPart.CanCollide = false
	item:SetPrimaryPartCFrame(cframe)
	
	ChangeTransparency(item, 1)
	
	if checkBoundaries(plot, item.PrimaryPart) then
		return
	end
	
	item.Parent = location
	
	if c then
		if not checkHitbox(plr.Character, item) then
			ChangeTransparency(item, 0)
			
			item.PrimaryPart.Transparency = 1
			
			return true
		else
			item:Destroy()
			
			return false
		end
	else
		ChangeTransparency(item, 0)
			
		item.PrimaryPart.Transparency = 1
			
		return true
	end
end

replicatedStorage.remotes.requestPlacement.OnServerInvoke = place

Depending on where your remotes are located and what their called, you may have to look at the last line and correct a few things. You can also find this code in the Extras script that comes with the module. Other than that, you should now have a working placement system!

Before I move on, there are some built in functions that I will go over.
placement:terminate() - Cancels placement
placement:getCurrentState() - Returns the current state of the model
placement:pauseCurrentState() - Pauses the current state of the model
placement:resume() - Resumes the current state of the model.


Customizing Placement

Now that you have a working placement system, it’s time to configure it to make it your own! If you open the module up, you will see there are 19 settings (26 including the stuff in the new() and activate() methods). Here is a list of all of them and explaining what they do:

bools

  • bool interpolation - Toggles if you want to have the model interpolate when moving (smooth movement)
  • bool moveByGrid - Toggles if you want the model to move by a grid or not
  • bool collisions - Toggles if the module will detect collisions or not
  • bool buildModePlacement - Toggles if you want to be able to continually place objects until canceled by the user manually
  • bool displayGridTexture - Toggles if you want to display a grid texture when placing a model
  • bool smartDisplay - Toggles if the texture displayed will be scaled to fit the grid size. It is recommended you set this to false unless your grid size is less than 5 studs (requires displayGridTexture to be true).
  • bool enableFloors - Toggles if you want to be able to change floors while placing
  • bool transparentModel - Toggles if the model will appear transparent while placing

Color3

  • Color3 collisionColor - The color of the hitbox when collision is detected (collisions must be set to true)
  • Color3 hitboxColor - The color of the hitbox in any non collision state; any natural state.

Integers

  • int maxHeight - The max height one the Y axis the model can move to
  • int floorStep - The number of studs the model will move up/down when switching floors
  • int rotationStep - The number of degrees the model will rotate

Numbers/Floats

  • Number hitboxTransparency - The transparency of the hitbox when placing
  • Number transparencyDelta - The transparency of the model itself when placing (transparentModel must be true)
  • Number lerpSpeed - speed of interpolation. 0 = no interpolation, 0.9 = major interpolation
  • Number placementCooldown - The cooldon which the user has to wait before placing another object
  • Number maxRange - How far in studs the model can be away from the character while still being able to place.

New function

local placement = placementModule.new(
2,
replicatedStorage.items,
Enum.KeyCode.R, Enum.KeyCode.X, Enum.KeyCode.U, Enum.KeyCode.L
)

  • int Grid size
  • Enum Rotate key keycode
  • Enum Terminate/Cancel key keycode
  • Enum Raise floor key keycode
  • Enum Lower floor key keycode

Activate function

placement:activate(“Fence”, workspace.base.itemHolder, workspace.base, false, false)

  • bool rotation type - If the model can rotate around 360 degrees or if it just rotates x amount of degrees back and fourth.
  • bool if you want the object to stack on other objects

Limitations

Now, as much as I’d like to say this is the perfect placement module, I just can’t. One of the reasons being this module only supports PC. The module also does not handle any other features than placement. Not even a wall placement or saving system for a tycoon. It only handles the basic placement system where you move and place down models. The last thing is this module is made specifically for sandbox tycoons, not building games like Minecraft.


Other

Well, that’s the tutorial! I hope you found this helpful! If you didn’t, please let me know what I should modify about the tutorial (I am open to criticisms). You don’t have to give credit to use my module, though it is appreciated if given. Here is a demo video:

Here is a demo place to test the module out.

Enjoy the module!

Module Version - V1.17

11 Likes

It looks pretty good, I’ll check it out later! How do you make the grid appear?

3 Likes

It’s just a texture the module apply’s to the plot.

1 Like

I keep getting this warning.

The object that the model is moving on is not scaled correctly. Consider changing it.

1 Like

Its pretty good! However, one bug I found is that exploiters are able to re-size their base and place off it. Checking where the item is placed on the server would fix that. https://gyazo.com/fb42ce9f6888b27ed1cde65a62b9aa82

1 Like

ah ok. Will add that to the server code. Thank you for letting me know that. Ok updated

Is your plot size a multiple of the gridsize? It has to be to work. It does that when your plot is not.

How do I change the plot to be the grid size?

Edit: Nvm I read your reply wrong.

1 Like

How can I make it so only a certain person can place on their plot?

1 Like

This would be handled by a plot system. Since you cannot make the model leave the plot your given, assigning a plot to each player would fix this problem. This is not handled by the module.

1 Like

@zblox164

Suggestion: Make it so then we don’t have to add an up floor button and lower floor button.

2 Likes

what do you mean by that? Why would you need that?