local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local mouse = player:GetMouse()
--> Constants
local TURRENT_PLACEMENT_START = Enum.KeyCode.E
local TURRENT_PLACEMENT_END = Enum.KeyCode.B
local TURRENT_ROTATE = Enum.KeyCode.R
local ROTATION_OFFSET = 45
local isPlacing = false
--> Connections
local mouseConnection
local part = Instance.new("Part")
part.Anchored = true
part.Parent = workspace
--> Private Functions
local function rotate(part)
if part:IsA("BasePart") then
part.CFrame *= CFrame.Angles(0, math.rad(ROTATION_OFFSET), 0)
else
if part:IsA("Model") then
if part.PrimaryPart then
part.PrimaryPart.CFrame *= CFrame.Angles(0, math.rad(ROTATION_OFFSET), 0)
end
end
end
end
local function position(part)
if (character.HumanoidRootPart.Position - mouse.Hit.Position).Magnitude > 20 then
return
end
part.Position = mouse.Hit.Position
end
UserInputService.InputBegan:Connect(function(input, processed)
if processed then return end
if input.KeyCode == TURRENT_PLACEMENT_START then
mouse.TargetFilter = part
mouseConnection = mouse.Move:Connect(function()
position(part)
end)
elseif input.KeyCode == TURRENT_PLACEMENT_END then
if mouseConnection then
mouseConnection:Disconnect()
end
elseif input.KeyCode == TURRENT_ROTATE then
if mouseConnection then
rotate(part)
end
end
end)
Note that this is not a grid placement system. I would like feedback on;
Thereâs always room for improvement though. Hereâs all my nitpicks and stuff
The rotate function handles two cases, one for the parameter being a Part and one for the parameter being a Model.
Change the parameter name to reflect this, e.g. to âinstanceâ (same for he position function).
Why doesnât the position function handle both cases like rotate?
In the rotate function, you could save 1 line of code by doing elseif instead of else if.
You could save another LoC by doing if (part:IsA("Model") and part.PrimaryPart) then
If the Model doesnât have a PrimaryPart, is that a bug? If so, you should probably warn() or error() instead of silently failing. Youâll thank yourself when you save 30 minutes looking for a hard-to-find bug caused by this.
âpositionâ is both a verb and a noun in English, so itâs confusing as a variable/function name. Itâs usually assumed that the noun meaning is used, e.g. in BasePart.Position, so avoid using the verb meaning as function names for words like that. Call it âsetPositionâ instead, and change the rotation function to have the same style.
In the position function, youâre looking up mouse.Hit twice. Consider doing it only once and saving it in a variable. We canât know ´cause we canât read Roblox source code, but it might do a raycast each time to determine what mouse.Hit is.
We save a tiny bit of performance this way (not important, it would be a silly way to try and optimize, but good to be aware of).
Looking up mouse.Hit twice, thinking itâs going to be the same both times, can cause bugs. E.g. if you move or Destroy the hit part between the two lookup, itâs not going to be the same.
You misspelled âturretâ (youâve got an extra âNâ).
In the InputBegan listener, youâre checking if mouseConnection to see if a turret is currently being placed. Use a variable called isPlacing (outermost scope, or local to the relevant scope) instead, because otherwise the reader has to guess why youâre doing that check. Alternatively forget the variable and put a comment there.
Youâre defining isPlacing at the top of the script, but never set or use it.
Iâd add â_KeyCodeâ to the end of the keycode constants, to make it even clearer what they are.