ロブロックス(Roblox)で追いかけてくる敵の作り方|触れると体力が減るNPCを画像で解説

制作手順難易度: 中級所要時間: 約40分(目安)情報確認日: 2026年10月12日

「ゲームに追いかけてくる敵を出したい」「敵が壁にぶつかって止まってしまう」

ロブロックス(Roblox)で追いかけてくる敵の作り方を、実際の画像付きで誰でも簡単に分かるように解説します。ここでは、いちばん近いプレイヤーを追いかけて、触れると体力が減る敵を作ります。後半では、壁があると敵が止まってしまうことを確かめてから、壁を回り込んで追ってくるように書きかえていきます。

中級ステップ10/28 前の記事:ロブロックス(Roblox)スタジオでNPCを作る方法 全体の順番を見る

使うもの:Roblox Studioが起動するパソコン、マウス、キーボード。NPCの置き方はNPCを作る方法で画像付きで解説しています。

今回の完成状態:下の画像のように、敵(画像の2)に追いつかれると、右上の体力バー(画像の1)が減れば成功です。

1. 敵の体を置く

新しいBaseplateを開き、3D画面上部メニューの「ホーム」タブの「キャラクター」ボタンを押します。

「リグを生成」の窓が開きます。リグタイプの「R15」を選んだまま、「ブロック型アバター(2012年)」を押します。3D画面に灰色のキャラクターが置かれ、エクスプローラのWorkspaceの中に「Rig」ができます。窓の画像はNPCを作る方法の1で見られます。

エクスプローラで「Rig」を選んだ状態でF2キーを押し、名前を「Enemy」に変えます(赤い枠)。

エクスプローラでEnemyの左の三角を押して開き、中の「Humanoid」を選んだ状態で、プロパティの検索欄に「DisplayName」と入力し、DisplayNameを「敵」にします(赤い枠)。

エクスプローラでEnemyを選び直し、プロパティの検索欄に「Origin」と入力して、Originの下のPositionを「0, 3, -40」にしてEnterキーを押します(赤い枠)。スポーン地点から40スタッド奥に動きます。画面では最初の数が「-0」と出ることがありますが、0と同じです。

敵を歩かせるので、HumanoidRootPartのAnchoredにチェックが入っていないことを確かめます。エクスプローラでEnemyの中の「HumanoidRootPart」を選んだ状態で、プロパティの検索欄に「Anchored」と入力します。最初からチェックは入っていません(画像の2)。3D画面の奥に敵が立っています(画像の1)。

2. 追いかけて、触れると体力が減るスクリプトを入れる

エクスプローラの「ServerScriptService」にマウスを乗せて、右に出る「+」を押し、検索欄に「Script」と入力して、一覧の「Script」を押します。

エクスプローラでServerScriptServiceの中に入った「Script」を選んだ状態でF2キーを押し、名前を「EnemyService」に変えます。

EnemyServiceを選んだまま、プロパティの検索欄に「RunContext」と入力し、「▼動作」の欄にあるRunContextの一覧から「Server」を選びます(赤い枠)。RunContextは、スクリプトをどこで動かすかを決める設定で、「Server」はゲームのサーバー側で動かす設定です。

EnemyServiceの画面で、中身を全部消して、次のコードを貼り付けます。

local Players = game:GetService("Players")

local enemy = workspace:WaitForChild("Enemy")
local humanoid = enemy:WaitForChild("Humanoid")
local root = enemy:WaitForChild("HumanoidRootPart")

local CHASE_RANGE = 60 -- この距離(スタッド)より近い人を追いかける
local DAMAGE = 20 -- 1回に減らす体力
local COOLDOWN = 1 -- 同じ人にもう一度当たるまでの秒数

humanoid.WalkSpeed = 12 -- プレイヤー(16)より少し遅くして、逃げられるようにする

-- 敵の動きの計算をサーバーに任せる(固定されているとできないので、そのときは知らせる)
local ok = pcall(function()
	root:SetNetworkOwner(nil)
end)
if not ok then
	warn("Enemy のパーツの Anchored を外してください")
end

-- いちばん近い、生きているプレイヤーの HumanoidRootPart を探す
local function findNearestTarget()
	local nearest = nil
	local nearestDistance = CHASE_RANGE
	for _, player in Players:GetPlayers() do
		local character = player.Character
		local targetHumanoid = character and character:FindFirstChildOfClass("Humanoid")
		local targetRoot = character and character:FindFirstChild("HumanoidRootPart")
		if targetHumanoid and targetRoot and targetHumanoid.Health > 0 then
			local distance = (targetRoot.Position - root.Position).Magnitude
			if distance < nearestDistance then
				nearest = targetRoot
				nearestDistance = distance
			end
		end
	end
	return nearest
end

-- 敵に触れた人の体力を減らす(同じ人は COOLDOWN 秒に1回まで)
local lastHit = {}

local function onTouched(hit)
	if humanoid.Health <= 0 then return end
	local character = hit.Parent
	local player = Players:GetPlayerFromCharacter(character)
	if not player then return end -- アバター以外が触れたときは何もしない
	local targetHumanoid = character:FindFirstChildOfClass("Humanoid")
	if not targetHumanoid or targetHumanoid.Health <= 0 then return end
	local now = os.clock()
	if lastHit[player] and now - lastHit[player] < COOLDOWN then return end
	lastHit[player] = now
	targetHumanoid:TakeDamage(DAMAGE)
end

-- 敵の体のパーツ全部に「触れたとき」をつなぐ
for _, part in enemy:GetDescendants() do
	if part:IsA("BasePart") then
		part.Touched:Connect(onTouched)
	end
end

-- 抜けた人の記録を消す
Players.PlayerRemoving:Connect(function(player)
	lastHit[player] = nil
end)

-- 0.2秒ごとに、いちばん近い人の位置へ歩かせる
while humanoid.Health > 0 do
	local target = findNearestTarget()
	if target then
		humanoid:MoveTo(target.Position)
	else
		humanoid:MoveTo(root.Position) -- 近くに誰もいなければ、その場で止まる
	end
	task.wait(0.2)
end
コードの部分していること
findNearestTarget全員の中から、CHASE_RANGEで決めた60スタッドより近くて体力が残っている人を1人選ぶ
0.2秒ごとのMoveTo相手は動くので、行き先を何度も出し直す
onTouchedとlastHit触れたのがプレイヤーか確かめ、人ごとに最後に当てた時刻を覚えて、COOLDOWNで決めた1秒がたつまでは減らさない
TakeDamage体力を減らす
SetNetworkOwner(nil)敵の動きをサーバーが計算する

MoveToで歩かせたNPCは、8秒たっても行き先に着かないと止まります。ただし、行き先を出し直すと、8秒は最初から数え直しになります(どちらもRobloxの公式の説明に書かれている決まりです)。このコードは0.2秒ごとに出し直すので、途中で止まりません。

3. テストする

3D画面の上の作品のタブ「Place7」を押して3D画面に戻り、F5キーでテストを始めます(タブの名前はパソコンによって違います)。敵がこちらへ歩いてきます(赤い枠)。

敵に追いつかれると、右上の体力バーが減ります(画像の1)。敵(画像の2)は、すぐ後ろにくっついてきます。

体力が0になるとアバターは倒れて、少しするとスポーン地点で生き返ります。

以下は、アソビシフト編集部が新しいBaseplateで確かめた結果です。生き返った直後も、すぐに体力が減りました。新しいBaseplateのスポーン地点は、出てすぐの守り(ForceField)の時間(Duration)が0になっているためです。エクスプローラで「SpawnLocation」を選んだ状態で、プロパティの検索欄に「Duration」と入力すると、0になっているのが分かります。出てすぐの数秒を守りたいときは、このDurationの数を大きくします(Robloxの公式の説明では、出てきた人をその秒数だけForceFieldで守る設定です)。

4. 敵の速さを決める

コードの humanoid.WalkSpeed = 12 で、敵の速さが決まります。プレイヤーの歩く速さの初期値は16です。変えるときは、エクスプローラのServerScriptServiceの中の「EnemyService」をダブルクリックして開き、コードの11行目「humanoid.WalkSpeed = 12」の12を書き換えます。

WalkSpeedプレイヤー(16)と比べて
8半分の速さ
12(このコード)4分の3の速さ。まっすぐ逃げれば離れていく
16同じ速さ
20プレイヤーより速い。まっすぐ逃げても追いつかれる

5. 壁を置くと、敵が止まってしまう

Shift + F5キーでテストを停止します。

スポーン地点と敵のあいだに壁を置きます。エクスプローラの「Workspace」にマウスを乗せて、右に出る「+」を押し、検索欄に「Part」と入力して、一覧の「Part」を押します。

エクスプローラでWorkspaceの中に入った「Part」を選んだ状態でF2キーを押し、名前を「Wall」に変えます。

Wallのプロパティは次のとおりにします。

プロパティ値
Size30, 10, 2
Position(▼CFrameのすぐ下)0, 5, -20
Anchoredチェックを入れる
Color124, 92, 70(BrickColorの欄は「Brown」になる)

順にやっていきます。Wallを選んだまま、プロパティの検索欄に「Size」と入力し、Sizeの欄を「30, 10, 2」にしてEnterキーを押します(赤い枠)。

続けてWallを選んだまま、プロパティの検索欄に「Position」と入力し、「▼CFrame」のすぐ下のPositionを「0, 5, -20」にしてEnterキーを押します(赤い枠)。

続けてWallを選んだまま、プロパティの検索欄に「Anchored」と入力し、チェックを入れて固定します(赤い枠)。

続けてWallを選んだまま、プロパティの検索欄に「Color」と入力します。BrickColorとColorの2つの欄が並ぶので、下のColorの欄の数字を「124, 92, 70」に書き換えてEnterキーを押します。壁が茶色になり、上のBrickColorの欄が「Brown」に変わります(赤い枠)。

3D画面の上の作品のタブ「Place7」を押して3D画面に戻り、F5キーでテストを始めます(タブの名前はパソコンによって違います)。

ただ、敵は壁の手前で止まってしまいました(赤い枠)。しばらく待っても、壁の手前から動きません。MoveToは、行き先へまっすぐ歩くだけで、壁をよけてはくれないからです。

6. 壁を回り込む「経路探索」に書きかえる

Shift + F5キーでテストを停止します。

壁をよけて歩かせるには、PathfindingService(パスファインディングサービス)で道を計算します。コードの変更箇所は、2行目のPathfindingService、定数STEPS_BEFORE_RECALC、CreatePathの部分、chaseWithPathの部分、最後のくり返しです。WalkSpeedの行などの説明のコメントは省いています。findNearestTargetとonTouchedの動きは同じです。

エクスプローラのServerScriptServiceの中の「EnemyService」をダブルクリックして開き、中身を全部消して、次のコードを貼り付けます。

local Players = game:GetService("Players")
local PathfindingService = game:GetService("PathfindingService")

local enemy = workspace:WaitForChild("Enemy")
local humanoid = enemy:WaitForChild("Humanoid")
local root = enemy:WaitForChild("HumanoidRootPart")

local CHASE_RANGE = 60 -- この距離(スタッド)より近い人を追いかける
local DAMAGE = 20 -- 1回に減らす体力
local COOLDOWN = 1 -- 同じ人にもう一度当たるまでの秒数
local STEPS_BEFORE_RECALC = 4 -- 道の点をいくつ進んだら計算し直すか

humanoid.WalkSpeed = 12

local ok = pcall(function()
	root:SetNetworkOwner(nil)
end)
if not ok then
	warn("Enemy のパーツの Anchored を外してください")
end

-- 敵の大きさに合わせた道の探し方(数字は Roblox の標準と同じ)
local path = PathfindingService:CreatePath({
	AgentRadius = 2,
	AgentHeight = 5,
	AgentCanJump = true,
})

local function findNearestTarget()
	local nearest = nil
	local nearestDistance = CHASE_RANGE
	for _, player in Players:GetPlayers() do
		local character = player.Character
		local targetHumanoid = character and character:FindFirstChildOfClass("Humanoid")
		local targetRoot = character and character:FindFirstChild("HumanoidRootPart")
		if targetHumanoid and targetRoot and targetHumanoid.Health > 0 then
			local distance = (targetRoot.Position - root.Position).Magnitude
			if distance < nearestDistance then
				nearest = targetRoot
				nearestDistance = distance
			end
		end
	end
	return nearest
end

local lastHit = {}

local function onTouched(hit)
	if humanoid.Health <= 0 then return end
	local character = hit.Parent
	local player = Players:GetPlayerFromCharacter(character)
	if not player then return end
	local targetHumanoid = character:FindFirstChildOfClass("Humanoid")
	if not targetHumanoid or targetHumanoid.Health <= 0 then return end
	local now = os.clock()
	if lastHit[player] and now - lastHit[player] < COOLDOWN then return end
	lastHit[player] = now
	targetHumanoid:TakeDamage(DAMAGE)
end

for _, part in enemy:GetDescendants() do
	if part:IsA("BasePart") then
		part.Touched:Connect(onTouched)
	end
end

Players.PlayerRemoving:Connect(function(player)
	lastHit[player] = nil
end)

-- 相手までの道を計算して、少しだけ進む
local function chaseWithPath(targetRoot)
	local success = pcall(function()
		path:ComputeAsync(root.Position, targetRoot.Position)
	end)
	if success and path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints()
		-- 1つめの点は今いる場所なので、2つめから歩く
		local last = math.min(#waypoints, 1 + STEPS_BEFORE_RECALC)
		for i = 2, last do
			local waypoint = waypoints[i]
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
			end
			humanoid:MoveTo(waypoint.Position)
			local reached = humanoid.MoveToFinished:Wait()
			if not reached or humanoid.Health <= 0 then
				break -- 8秒たっても着かなかったら、計算し直す
			end
		end
	else
		-- 道が見つからないときは、まっすぐ向かう
		humanoid:MoveTo(targetRoot.Position)
	end
	task.wait(0.1)
end

while humanoid.Health > 0 do
	local target = findNearestTarget()
	if target then
		chaseWithPath(target)
	else
		humanoid:MoveTo(root.Position)
		task.wait(0.5)
	end
end
コードの部分していること
CreatePath敵の大きさ(半径2、高さ5)に合わせて、通れる道の探し方を決める
ComputeAsync敵の場所から相手の場所までの道を計算する
GetWaypoints道を、点の並びにして取り出す
STEPS_BEFORE_RECALC相手は動くので、4つの点を進んだら道を計算し直す
道が見つからないときまっすぐ相手へ向かう

3D画面の上の作品のタブ「Place7」を押して3D画面に戻り、F5キーでテストを始めます(タブの名前はパソコンによって違います)。敵(画像の1)は壁の端を回り込んで、スポーン地点のアバターまで追ってきました。体力バーも減っています(画像の2)。

試したコード壁があるとき
コード1(MoveToだけ)壁の手前で止まった
コード2(経路探索)壁を回り込んで追いついた

敵が追ってこない・体力が減らないときの確かめ方

症状見るところ直し方
敵がまったく動かないHumanoidRootPartのAnchoredチェックを外す(出力に「Anchored を外してください」と出る)
遠くにいると追ってこないCHASE_RANGE数を大きくする
壁の前で止まる使っているコードコード2(経路探索)にする
触れても体力が減らないスクリプトの場所ServerScriptServiceのScriptか、RunContextがServerか
生き返った直後もすぐ減るSpawnLocationのDurationSpawnLocationを選んで検索欄に「Duration」と入力し、0より大きい数にする
一度にたくさん減るCOOLDOWN数を大きくする

スクリプトが動いているか分からないときは、スクリプトが動かないときの確認手順で出力の見方を確かめてください。

AIに相談するときの伝え方

Roblox Studioで、Workspace/Enemy という敵が、60スタッド以内でいちばん近いプレイヤーを追いかけるようにしたいです。
壁は PathfindingService で回り込み、道が見つからないときはまっすぐ向かってください。
触れたプレイヤーの体力を TakeDamage で20減らし、同じ人には1秒に1回までにしてください。
ServerScriptService の Script にして、wait() ではなく task.wait() を使ってください。

よくある質問

敵を倒せるようにするには?

敵のHumanoidの体力が0になると、このコードのくり返しは終わって、敵は動かなくなります。敵の体力を減らす仕組み(剣で叩く、ボールを当てるなど)を作れば、倒せる敵になります。

落ちたら途中から始められるようにしたい

チェックポイントを置くと、倒れても途中から生き返れます。作り方はチェックポイントの作り方で解説しています。

次のステップ(中級 11/28)

次は、手に持って使えるアイテムを作ります。

→ ロブロックス(Roblox)スタジオで手に持てるアイテム(ツール)を作る方法

次に知りたいことを選ぶ

次に読む記事