Punch something
Give the project its identity, build the first map from a real blueprint, and make the first interaction work.
Weekly outcome
What exists by the end
A Training Grounds blockout built to exact stud measurements, where the player can walk up to a dummy, click, and watch its health bar drop.
Build sequence
This week's work
- 1Lock in the NeNAdventures identity and original location names.
- 2Define the Echoes feature before it enters the build schedule.
- 3Create the core Roblox Studio folder structure.
- 4Block out the Training Grounds from the blueprint, with working boundaries and spawns.
- 5Connect a click to a basic attack and server-controlled damage.
Step by step
Do it in this order
No coding experience is assumed. Every script is written out in full — read the explanation underneath it, then type or paste it in. Do not skip the check at the end of a task; each one depends on the last.
- 1
Create the place and give it its name
15 minutesIn Studio
- Open Roblox Studio and choose New → Baseplate.
- Go to File → Save to Roblox As. Set the name to NeNAdventures and save.
- In the Explorer panel, click Workspace, then in Properties set Name to Workspace (leave it) — this step is just to confirm you can find the Explorer and Properties panels. If you cannot see them, turn them on from the View tab.
- Write down your three original location names for later weeks. Training Grounds is week 1; The Gate is week 4; Crossroads is week 5.
- Write one sentence describing Echoes — the feature you will build in week 11 — and save it in a text file next to the place. Deciding this now stops the scope moving later.
You know it worked when
The Studio title bar reads NeNAdventures, and the place appears under Creations on the Roblox website.
If it is stuck
If Save to Roblox As fails, you are not signed in. Use the account icon in the top-right of Studio.
- 2
Build the four folders every script will use
10 minutesIn Studio
- In the Explorer, hover over ReplicatedStorage and click the ⊕ button. Insert a Folder. Rename it Remotes.
- On ReplicatedStorage again, insert a second Folder and rename it Modules.
- Inside Remotes, insert a RemoteEvent and rename it AttackRequest. A RemoteEvent is the only way a player's computer is allowed to ask the server to do something.
- Expand StarterPlayer. Inside StarterPlayerScripts you will put anything the player's own computer runs.
- Leave ServerScriptService empty for now — that is where the server's scripts go, and players can never see inside it.
You know it worked when
Your Explorer shows ReplicatedStorage → Remotes → AttackRequest, and ReplicatedStorage → Modules.
If it is stuck
Renaming is done by double-clicking the item's name in the Explorer, or pressing F2.
- 3
Block out Training Grounds from the blueprint
90 minutesIn Studio
- Work straight down the blueprint below, in the order given under Build order.
- For each zone: Home tab → Part, then in Properties type the exact Size and Position numbers listed. Do not drag parts into place by eye — typing the numbers is faster and the blueprint stays true.
- Tick Anchored in Properties for every part before you move on to the next one.
- Insert a SpawnLocation from the Model tab and set its Position to 0, 1.5, 110 so it sits on the plaza.
- Press Play and walk the whole route: plaza, path, ring, dummy pad. If the walk feels long or confusing, change the numbers now, before anything is coloured.
You know it worked when
You can spawn on the plaza, walk south along the path into the ring, and reach the dummy pad without falling, jumping or getting stuck.
If it is stuck
If a part will not move to the position you typed, it is probably inside another Model — check what it is parented to in the Explorer.
- 4
Add the training dummy
25 minutesIn Studio
- Open the Toolbox (View tab → Toolbox), search Rig, and take a free R6 dummy — or use the Avatar tab → Rig Builder → R6 Block Rig, which is cleaner and has no third-party scripts in it.
- Rename the rig Dummy and set its HumanoidRootPart Position to 0, 4, -60.
- Set the Dummy's Rotation to 0, 180, 0 so it faces the incoming player.
- Select every part of the Dummy except the HumanoidRootPart and tick Anchored, so it stands still instead of falling over.
- Click the Humanoid inside the Dummy and set MaxHealth to 100 and Health to 100.
- Insert a Script inside the Dummy and paste the code below. It brings the dummy back after it is defeated so you can keep testing.
Script — DummyResetWorkspace → Dummy → DummyResetlocal dummy = script.Parent local humanoid = dummy:WaitForChild("Humanoid") -- Keeps the dummy standing instead of collapsing when it runs out of health. humanoid.BreakJointsOnDeath = false humanoid.Died:Connect(function() task.wait(3) humanoid.Health = humanoid.MaxHealth end)What this code is doing
- script.Parent means "the thing this script is inside" — here, the Dummy model.
- WaitForChild("Humanoid") pauses until the Humanoid exists. Without it the script can run before the model has finished loading and error.
- BreakJointsOnDeath = false stops the dummy falling apart into loose limbs.
- humanoid.Died:Connect(...) means "whenever this happens, run the code inside". It waits three seconds and refills the health.
You know it worked when
The dummy stands on its pad facing north and does not fall over when you press Play.
If it is stuck
If the dummy sinks into the pad, its HumanoidRootPart Y is too low. Raise it until the feet sit on the surface.
- 5
Send the click from the player's computer to the server
30 minutesIn Studio
- In the Explorer, hover over StarterPlayer → StarterPlayerScripts and insert a LocalScript.
- Rename it AttackInput.
- Delete everything already in it and paste the code below.
- A LocalScript runs on the player's own computer. It is allowed to notice a click, but it is never allowed to decide damage — that is the whole point of the next task.
LocalScript — AttackInputStarterPlayer → StarterPlayerScripts → AttackInputlocal ReplicatedStorage = game:GetService("ReplicatedStorage") local UserInputService = game:GetService("UserInputService") local AttackRequest = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("AttackRequest") local COOLDOWN = 0.6 local lastAttack = 0 local function tryAttack() local now = os.clock() if now - lastAttack < COOLDOWN then return end lastAttack = now AttackRequest:FireServer() end UserInputService.InputBegan:Connect(function(input, typingInChat) if typingInChat then return end if input.UserInputType == Enum.UserInputType.MouseButton1 then tryAttack() end end)What this code is doing
- game:GetService(...) is how you get hold of one of Roblox's built-in systems. UserInputService reports keys and clicks.
- COOLDOWN and lastAttack together stop the player spamming clicks. os.clock() is the number of seconds since Studio started, so subtracting gives the gap since the last punch.
- return on its own means "stop here, do nothing".
- AttackRequest:FireServer() sends the message across to the server. It carries no damage number — it only says "I clicked".
- The typingInChat check stops a punch firing while the player is typing a message.
You know it worked when
Press Play, click, and nothing visible happens yet. That is correct — the message is arriving but nobody is listening.
- 6
Make the server deal the damage
30 minutesIn Studio
- Hover over ServerScriptService and insert a Script (not a LocalScript).
- Rename it CombatServer.
- Delete the default line and paste the code below.
- Press Play, walk up to the dummy, and click. Watch the Humanoid's Health in the Properties panel while you do it.
Script — CombatServerServerScriptService → CombatServerlocal Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local AttackRequest = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("AttackRequest") local PUNCH_DAMAGE = 10 local PUNCH_RANGE = 12 local SERVER_COOLDOWN = 0.5 local lastAttack = {} local function findTarget(character) local root = character:FindFirstChild("HumanoidRootPart") if not root then return nil end local closest = nil local closestDistance = PUNCH_RANGE for _, model in ipairs(workspace:GetChildren()) do if model:IsA("Model") and model ~= character then local humanoid = model:FindFirstChildOfClass("Humanoid") local targetRoot = model:FindFirstChild("HumanoidRootPart") if humanoid and targetRoot and humanoid.Health > 0 then local distance = (targetRoot.Position - root.Position).Magnitude if distance < closestDistance then closest = humanoid closestDistance = distance end end end end return closest end AttackRequest.OnServerEvent:Connect(function(player) local now = os.clock() if now - (lastAttack[player] or 0) < SERVER_COOLDOWN then return end lastAttack[player] = now local character = player.Character if not character then return end local ownHumanoid = character:FindFirstChildOfClass("Humanoid") if not ownHumanoid or ownHumanoid.Health <= 0 then return end local target = findTarget(character) if target then target:TakeDamage(PUNCH_DAMAGE) end end) Players.PlayerRemoving:Connect(function(player) lastAttack[player] = nil end)What this code is doing
- The three values in capitals at the top are the only numbers you will want to change while testing. Keeping them together at the top is a habit worth forming now.
- findTarget looks through everything in Workspace, keeps only the models that have a Humanoid, and returns the closest one within 12 studs. Magnitude is the distance between two positions.
- OnServerEvent is the other half of FireServer. The server automatically knows which player sent it — that is the player argument, and the client cannot fake it.
- The server checks the cooldown again even though the client already did. The client's check is for feel; the server's check is the one that actually protects the game.
- TakeDamage is used instead of setting Health directly because it respects ForceFields.
- PlayerRemoving clears the leftover cooldown entry so the table does not grow forever as players come and go.
You know it worked when
Standing next to the dummy and clicking drops its Health by 10. Standing across the ring and clicking does nothing.
If it is stuck
Open the Output window (View tab → Output). A red line tells you the script name and the line number. Nine times out of ten it is a misspelled name.
- 7
Put a health bar above the dummy
25 minutesIn Studio
- Select the Dummy's Head and insert a BillboardGui. Rename it HealthBar.
- Set its Size to {4, 0}, {0.6, 0} and StudsOffset to 0, 2.5, 0, and tick AlwaysOnTop.
- Inside HealthBar insert a Frame named Background. Set Size to {1, 0}, {1, 0} and BackgroundColor3 to a dark grey.
- Inside Background insert another Frame named Fill. Set Size to {1, 0}, {1, 0}, Position to {0, 0}, {0, 0} and BorderSizePixel to 0.
- Insert a Script inside HealthBar and paste the code below.
Script — HealthBarUpdaterWorkspace → Dummy → Head → HealthBar → HealthBarUpdaterlocal billboard = script.Parent local fill = billboard:WaitForChild("Background"):WaitForChild("Fill") -- billboard is in the Head, and the Head is in the Dummy, so two Parents up is the Dummy. local humanoid = billboard.Parent.Parent:WaitForChild("Humanoid") local function update() local percent = math.clamp(humanoid.Health / humanoid.MaxHealth, 0, 1) fill.Size = UDim2.fromScale(percent, 1) if percent > 0.5 then fill.BackgroundColor3 = Color3.fromRGB(80, 200, 120) elseif percent > 0.25 then fill.BackgroundColor3 = Color3.fromRGB(240, 180, 60) else fill.BackgroundColor3 = Color3.fromRGB(220, 70, 70) end end humanoid.HealthChanged:Connect(update) update()What this code is doing
- math.clamp keeps the number between 0 and 1 so the bar can never draw wider than its background or flip inside out.
- UDim2.fromScale(percent, 1) means "this fraction of the width, all of the height". Scale is a fraction of the parent, which is why the bar works at any screen size.
- The if / elseif / else block turns the bar green, then amber, then red.
- The last line calls update() once immediately, so the bar is correct before the dummy is ever hit — connecting to HealthChanged alone would leave it blank until the first punch.
You know it worked when
The bar floats above the dummy's head, shrinks to the left with each punch, and changes colour as it empties.
If it is stuck
If the bar is invisible, check the Frame's BackgroundTransparency is 0 and that AlwaysOnTop is ticked on the BillboardGui.
- 8
Test it the way a player will
15 minutesIn Studio
- Go to the Test tab, set Players to 2, and click Start. Two windows open, both connected to one server.
- In one window, punch the dummy. Watch the other window — the health bar should drop there too. That proves the server, not the client, is in charge.
- Stop the test. Change PUNCH_DAMAGE to 35 and run it again to feel the difference. Set it back to 10.
- Record a short clip of a full run: spawn, walk the path, punch the dummy to zero, watch it reset.
You know it worked when
Both windows show the same health at the same moment. That agreement is what server-authoritative means, and every later week depends on it.
If it is stuck
If only one window updates, your damage code is in a LocalScript rather than a Script in ServerScriptService.
Base shape blueprint
Training Grounds
A 300 × 300 stud walled plot. The player spawns on the north plaza, walks a single path south into a circular combat ring, and meets the training dummy on the far side. Everything is a plain anchored Part — no modelling, no terrain, no plugins. Build it grey first, colour it afterwards.
Top-down plan · 300 × 300 studs
North = +Z
Every number on this plan is a value you type straight into the Position and Size boxes in Studio. The place centre is 0, 0.
Zone legend
1. Ground Slab
The floor everything else sits on. Every other height is measured from the top of this slab.
2. Boundary Walls
Stops players walking off the edge and frames the whole space so the map reads as a room.
3. Spawn Plaza
Where every player appears. Raised one stud so the edge is visible and the player instinctively walks off it toward the path.
4. Approach Path
One narrow path with one destination. A new player never has to guess where to go.
5. Combat Ring
The 90-stud arena where all week 1 fighting happens. Sized so punch range (12 studs) feels short and deliberate inside it.
6. Dummy Pad
Home of the training dummy. Just outside the ring so the player has to close the distance to land a hit.
7. Target Range
Empty now. Week 2 puts the fireball targets here, so leave the space clear and flat.
8. Overlook Deck
A raised platform to film from and to test that the camera and combat still behave at height.
Height guide
- Boundary Walls20 studs
- Overlook Deck12 studs
- Spawn Plaza1 studs
- Dummy Pad1 studs
- Approach Path0.5 studs
- Combat Ring0.5 studs
- Target Range0.5 studs
- Ground Slab0 studs
Part list — type these exactly
- 1. Ground Slab
- Part · Size 300, 2, 300 · Position 0, -1, 0 · Anchored on · Material Grass · Colour Sea green
- 2. Boundary Walls
- 4 Parts · North and south: Size 300, 20, 4 at Position 0, 10, ±150 · East and west: Size 4, 20, 300 at Position ±150, 10, 0 · Anchored on
- 3. Spawn Plaza
- Part · Size 60, 1, 60 · Position 0, 0.5, 110 · Anchored on · Material Slate · Colour Sand
- 4. Approach Path
- Part · Size 12, 0.5, 36 · Position 0, 0.25, 62 · Anchored on · Material Cobblestone
- 5. Combat Ring
- Part · Shape Cylinder is easiest: insert a Cylinder, Size 0.5, 90, 90, Rotation 0, 0, 90 · Position 0, 0.25, 0 · Anchored on
- 6. Dummy Pad
- Part · Size 20, 1, 20 · Position 0, 0.5, -60 · Anchored on · Colour Bright orange
- 7. Target Range
- Part · Size 50, 0.5, 40 · Position 95, 0.25, -60 · Anchored on · Colour Bright blue · Transparency 0.4
- 8. Overlook Deck
- Part · Size 40, 1, 40 · Position -100, 12, -75 · Anchored on · plus a ramp Part · Size 12, 1, 40 · Position -75, 6, -75 · Rotation 0, 0, -17
Landmark positions
SpawnLocation0, 1.5, 110
Sits on the Spawn Plaza. Set Neutral on and Duration to 0.
Training dummy0, 4, -60
Faces north, toward the incoming player. Rotation 0, 180, 0.
Ring centre marker0, 0.6, 0
A 4 × 0.1 × 4 dark part. Purely a visual anchor so the ring has a middle.
Camera start0, 20, 150
Where to put your viewport before taking the week 1 screenshot.
Build order
- 01Delete the default Baseplate and the default SpawnLocation. You are replacing both.
- 02Set the grid snap to 4 studs in the Model tab so parts line up without fiddling.
- 03Insert the Ground Slab first and anchor it. Every later position is measured from its top surface.
- 04Add the four Boundary Walls. Playtest once here and try to walk off the edge — you should not be able to.
- 05Add the Spawn Plaza, then the Approach Path, then the Combat Ring, in that order, going south.
- 06Add the Dummy Pad, the Target Range and the Overlook Deck.
- 07Select everything, right-click, Group as a Model, and rename it TrainingGrounds. Drag it into Workspace.
- 08Only now change colours and materials. Blocking out in grey first stops you polishing a layout you are about to move.
Things that catch people out
- Anchored must be ticked on every single part. One unanchored part will fall through the world the moment you press Play.
- Position in Studio is the centre of a part, not its corner. A 1-stud-thick platform at height 1 has Position Y of 0.5.
- If a part looks right but you fall through it, check CanCollide is on.
- Do not use Terrain this week. Terrain is slow to change, and this layout will change.
Show someone
The weekly checkpoint
A playtester watches the dummy's health bar drop.