PPristone AcademyLearn · Mentor · Build
NeNAdventures course
Week 1 of 12Foundation

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

  1. 1Lock in the NeNAdventures identity and original location names.
  2. 2Define the Echoes feature before it enters the build schedule.
  3. 3Create the core Roblox Studio folder structure.
  4. 4Block out the Training Grounds from the blueprint, with working boundaries and spawns.
  5. 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. 1

    Create the place and give it its name

    15 minutes

    In 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. 2

    Build the four folders every script will use

    10 minutes

    In 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. 3

    Block out Training Grounds from the blueprint

    90 minutes

    In 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. 4

    Add the training dummy

    25 minutes

    In 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 → DummyReset
    local 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. 5

    Send the click from the player's computer to the server

    30 minutes

    In 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 → AttackInput
    local 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. 6

    Make the server deal the damage

    30 minutes

    In 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 → CombatServer
    local 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. 7

    Put a health bar above the dummy

    25 minutes

    In 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 → HealthBarUpdater
    local 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. 8

    Test it the way a player will

    15 minutes

    In 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

-150-150-100-100-50-5000505010010015015012345678X studs →

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

  1. 01Delete the default Baseplate and the default SpawnLocation. You are replacing both.
  2. 02Set the grid snap to 4 studs in the Model tab so parts line up without fiddling.
  3. 03Insert the Ground Slab first and anchor it. Every later position is measured from its top surface.
  4. 04Add the four Boundary Walls. Playtest once here and try to walk off the edge — you should not be able to.
  5. 05Add the Spawn Plaza, then the Approach Path, then the Combat Ring, in that order, going south.
  6. 06Add the Dummy Pad, the Target Range and the Overlook Deck.
  7. 07Select everything, right-click, Group as a Model, and rename it TrainingGrounds. Drag it into Workspace.
  8. 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.