「ロブロックスで銃のゲームを作りたい」「ツールボックスの銃ではなく、自分で撃つ仕組みを作りたい」「クリックした場所に弾を当てる方法が分からない」
ロブロックス(Roblox)スタジオで銃の作り方を、実際の画像付きで誰でも簡単に分かるように解説します。ここではクリックした場所へ赤いレーザーを撃つブラスター(レーザー銃)を作り、練習用のダミーに当てて体力を減らします。当たったかどうかは、Raycast(レイキャスト)という「見えない線を飛ばして、最初にぶつかった物を調べる」機能で決めます。
中級ステップ13/28 前の記事:ロブロックス(Roblox)スタジオで剣の攻撃を作る方法 全体の順番を見る
使うもの:Roblox Studioが起動するパソコン、マウス、キーボード。この記事は剣の攻撃を作る方法と同じ作りなので、先に剣の記事を読んでおくと分かりやすくなります。
今回の完成状態:下の画像のように、ダミーをクリックすると赤いレーザーが伸び(画像の1)、ダミーの頭の上の体力ゲージが減れば成功です(画像の2)。

剣とブラスターは、当たりの調べ方が違う
ブラスターは、剣と同じ3つの部品でできています。違うのは、サーバーが当たりを調べる方法です。
| 部品 | 置く場所 | 役目 |
|---|---|---|
| Tool「Blaster」とLocalScript | StarterPack | ブラスターの形。クリックされたら「ここをねらった」とサーバーに知らせる |
| RemoteEvent「FireBlaster」 | ReplicatedStorage | 知らせを届ける通り道 |
| Script「BlasterService」 | ServerScriptService | 線を飛ばして当たったか調べ、相手の体力を減らす |
| 剣 | ブラスター | |
|---|---|---|
| 当たりの調べ方 | 体の前の箱の中を調べる(GetPartBoundsInBox) | ねらった向きに線を飛ばす(Raycast) |
| 届く遠さ | 体のすぐ前だけ | MAX_DISTANCEで決めた300スタッドまで |
| 送る知らせ | 「振った」だけ | 「この場所をねらった」 |
当たったか、どれだけ減らすかは、サーバーが決めます。クリックした人のパソコンから送るのは、ねらった場所だけです。
1. ブラスターの形を作る
新しいBaseplateを開きます。エクスプローラの「Workspace」にマウスを乗せて、右に出る「+」(赤い枠)を押します。

検索欄に「Tool」と入力し、一覧の「Tool」(赤い枠)を押します。Tool(ツール)は、手に持って使うアイテムの入れ物です。

エクスプローラのWorkspaceの中に「Tool」が入ります(赤い枠)。

Toolを選んだままF2キーを押し、名前を「Blaster」に変えます(赤い枠)。

エクスプローラの「Blaster」にマウスを乗せて「+」を押し、検索欄に「Part」と入力して、一覧の「Part」(赤い枠)を押します。

Blasterの中に入った「Part」を押して選び、F2キーを押して名前を「Handle」(持ち手)に変えます(赤い枠)。Toolのすぐ下の「Handle」という名前のパーツが、手に持つところになります。名前が1文字でも違うと、テストで手に持てません。

続けてHandleを選んだまま、次の3つを変えます。どれもプロパティの検索欄に名前を入力してから変えます。PositionはCFrameのすぐ下の欄に入れます。
| プロパティ | 入れる値 | 画像 |
|---|---|---|
| Size | 0.6, 0.8, 1.6 | 下の1枚目 |
| Position | 0, 0.4, -9 | 下の2枚目(スポーン地点の前の地面に置く) |
| Color | 60, 60, 70 | 下の3枚目(黒っぽい灰色) |
HandleのAnchored(固定)のチェックは外したままにします。固定すると、手に持ったときに動けなくなります。
もう1度Blasterの「+」から「Part」を入れ、Blasterの中に入った「Part」を選んでF2キーを押し、名前を「Barrel」(銃身)に変えます(赤い枠)。

続けてBarrelを選んだまま、次の3つを変えます。Handleの前に、赤くて細い筒が付きます。
| プロパティ | 入れる値 | 画像 |
|---|---|---|
| Size | 0.3, 0.3, 1.2 | 下の1枚目 |
| Position | 0, 0.55, -10.4 | 下の2枚目(Handleの前の上寄り) |
| Color | 255, 60, 60 | 下の3枚目(赤) |
BarrelもAnchoredのチェックは外したままにします。
次に2つのパーツをくっつけます。エクスプローラの「Handle」にマウスを乗せて「+」を押し、検索欄に「WeldConstraint」と入力して、一覧の「WeldConstraint」(赤い枠)を押します。WeldConstraint(ウェルドコンストレイント)は、2つのパーツをくっつける部品です。

WeldConstraintを選んだまま、プロパティの「Part0」を「Handle」、「Part1」を「Barrel」にします(赤い枠)。Part0とPart1は文字を打ち込まず、欄を押してからエクスプローラの「Handle」や「Barrel」を押して入れます。これで、手に持ったときに銃身も一緒に動きます。

2. ブラスターをStarterPackへ移す
エクスプローラで「Blaster」をStarterPackへドラッグして、StarterPackの上で離します。StarterPackの中に入ったアイテムは、全員が最初から持っています。StarterPackの中にBlaster・Handle・WeldConstraint・Barrelが並べば移せています(赤い枠)。

3. LocalScriptを入れる
エクスプローラのStarterPackの中の「Blaster」にマウスを乗せて「+」を押し、検索欄に「LocalScript」と入力して、一覧の「LocalScript」(赤い枠)を押します。

Blasterの中に入った「LocalScript」を押して選び、F2キーを押して名前を「BlasterClient」に変えます(赤い枠)。

コードは、手順6でまとめて貼り付けます。
4. 知らせの通り道(RemoteEvent)を入れる
エクスプローラの「ReplicatedStorage」にマウスを乗せて、右に出る「+」(赤い枠)を押します。

検索欄に「RemoteEvent」と入力し、一覧の「RemoteEvent」(赤い枠)を押します。

入ったRemoteEventを選んだままF2キーを押し、名前を「FireBlaster」に変えます(赤い枠)。

5. 当たりを決めるScriptを入れる
エクスプローラの「ServerScriptService」にマウスを乗せて「+」を押し、検索欄に「Script」と入力して、一覧の「Script」(赤い枠)を押します。

入った「Script」を押して選び、F2キーを押して名前を「BlasterService」に変えます(赤い枠)。

BlasterServiceを選んだまま、プロパティの検索欄に「RunContext」と入力し、「▼動作」の欄にあるRunContextの一覧から「Server」を選びます(赤い枠)。

6. 2つのスクリプトにコードを貼り付ける
ねらった場所を伝えるBlasterClient
エクスプローラのBlasterの中の「BlasterClient」をダブルクリックしてスクリプトの画面を開き、中身を全部消して、次のコードを貼り付けます。
-- クリックした場所をサーバーに伝える(Tool「Blaster」の中の LocalScript「BlasterClient」)
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local fireBlaster = ReplicatedStorage:WaitForChild("FireBlaster")
local tool = script.Parent
local player = Players.LocalPlayer
local MAX_DISTANCE = 300 -- 狙える遠さ(スタッド)
tool.Activated:Connect(function()
-- 画面の上のマウスの位置から、カメラがのぞいている向きの線を作る
local mouse = UserInputService:GetMouseLocation()
local ray = workspace.CurrentCamera:ViewportPointToRay(mouse.X, mouse.Y)
-- その線の先で、マウスの下にある物の場所を調べる(自分の体は外す)
local params = RaycastParams.new()
params.ExcludeInstances = { player.Character }
local result = workspace:Raycast(ray.Origin, ray.Direction * MAX_DISTANCE, params)
-- 物があればその場所、なければ届く遠さの先を「ねらった場所」にする
local aimPoint = result and result.Position or (ray.Origin + ray.Direction * MAX_DISTANCE)
fireBlaster:FireServer(aimPoint)
end)

| コードの部分 | していること |
|---|---|
| GetMouseLocation | 画面の上でマウスがある場所(左上からの点の数)を調べる |
| ViewportPointToRay | その点から、カメラがのぞいている向きの線(始まりの場所と向き)を作る |
| workspace:Raycast | その線を300スタッドの長さで飛ばし、マウスの下にある物の場所を調べる。自分の体は外す |
| aimPoint | 物があればその場所、何もなければ300スタッド先を「ねらった場所」にする |
| FireServer(aimPoint) | ねらった場所だけをサーバーに送る |
カメラの線の300スタッド先をそのまま「ねらった場所」にすると、カメラと銃の位置がずれているため、レーザーがダミーの横をすり抜けることがあります。そこで、先にマウスの下にある物の場所を調べてから、その場所を送っています。
BlasterClientのRaycastは、ねらう場所を決めるためだけに使っています。何に当たったかはサーバーに送らず、当たったかどうかはサーバーがもう1度線を飛ばして決めます。
当たりを決めるBlasterService
エクスプローラのServerScriptServiceの中の「BlasterService」をダブルクリックしてスクリプトの画面を開き、中身を全部消して、次のコードを貼り付けます。
-- レーザーが当たったかをサーバーで決める(ServerScriptService の Script「BlasterService」)
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local fireBlaster = ReplicatedStorage:WaitForChild("FireBlaster")
local TOOL_NAME = "Blaster" -- 銃の Tool の名前
local DAMAGE = 25 -- 1回当たったときに減らす体力
local COOLDOWN = 0.5 -- 次に撃てるまでの秒数
local MAX_DISTANCE = 300 -- レーザーが届く遠さ(スタッド)
local BEAM_TIME = 0.15 -- レーザーを見せる秒数
local lastShot = {} -- 人ごとの、最後に撃った時刻
-- 始点から終点まで、細い赤い棒を少しの間見せる
local function showBeam(fromPos, toPos)
local distance = (toPos - fromPos).Magnitude
local beam = Instance.new("Part")
beam.Anchored = true
beam.CanCollide = false
beam.CanQuery = false
beam.CanTouch = false
beam.Material = Enum.Material.Neon
beam.Color = Color3.fromRGB(255, 40, 40)
beam.Size = Vector3.new(0.2, 0.2, distance)
-- 2つの点の真ん中に置き、終点の方を向かせる
beam.CFrame = CFrame.lookAt(fromPos, toPos) * CFrame.new(0, 0, -distance / 2)
beam.Parent = workspace
task.delay(BEAM_TIME, function()
beam:Destroy()
end)
end
fireBlaster.OnServerEvent:Connect(function(player, aimPoint)
-- 1. 送られてきたものが位置(Vector3)か
if typeof(aimPoint) ~= "Vector3" then return end
-- 2. 続けて撃ちすぎていないか
local now = os.clock()
if lastShot[player] and now - lastShot[player] < COOLDOWN then return end
-- 3. 生きていて、銃を手に持っているか(サーバーが自分で調べる)
local character = player.Character
if not character then return end
local humanoid = character:FindFirstChildOfClass("Humanoid")
local tool = character:FindFirstChild(TOOL_NAME)
if not humanoid or humanoid.Health <= 0 then return end
if not tool or not tool:IsA("Tool") then return end
local handle = tool:FindFirstChild("Handle")
if not handle then return end
lastShot[player] = now
-- 4. 銃の先から、ねらった方向へ線を飛ばす(自分の体と銃には当てない)
local origin = handle.Position
local direction = (aimPoint - origin).Unit * MAX_DISTANCE
local params = RaycastParams.new()
params.ExcludeInstances = { character }
local result = workspace:Raycast(origin, direction, params)
-- 5. 当たった場所までレーザーを見せる。何にも当たらなければ届く遠さまで
local hitPos = result and result.Position or (origin + direction)
showBeam(origin, hitPos)
-- 6. 当たった部品の持ち主に Humanoid があれば体力を減らす
if result then
local model = result.Instance:FindFirstAncestorOfClass("Model")
local target = model and model:FindFirstChildOfClass("Humanoid")
if target and target.Health > 0 then
target:TakeDamage(DAMAGE)
print(player.Name, "のレーザーが", model.Name, "に当たりました。残り", target.Health)
end
end
end)
Players.PlayerRemoving:Connect(function(player)
lastShot[player] = nil
end)

Raycastは「見えない線」を飛ばす
workspace:Raycast(始まりの場所, 向きと長さ, 設定) は、始まりの場所から見えない線を飛ばし、最初にぶつかった部品と、ぶつかった場所を返します。何にもぶつからなければ、答えは「nil(ニル)」になります。nilは、Luauで「何も入っていない」ことを表す特別な値です。
コードでは、Raycastの答えを result という入れ物に入れています。何かに当たったときは、result の中に当たった部品(result.Instance)と場所(result.Position)が入ります。空に向けて撃って何にも当たらなかったときは、result がnilになります。
| 行 | nilのときにどうなるか |
|---|---|
local hitPos = result and result.Position or (origin + direction) | 当たった場所がないので、線の届く限り(300スタッド先)までレーザーを伸ばす |
if result then | 中の「体力を減らす」処理を動かさない |
nilの入った result から result.Position を取り出そうとすると、エラーになって止まります。そのため、先に result があるかを確かめてから使っています。
なお、コードの最後の lastShot[player] = nil は、これとは別の使い方です。プレイヤーがゲームから抜けたときに、その人の「最後に撃った時刻」の記録をnilにして消しています。表(lastShot)に、もういない人の記録が残り続けないようにするためです。
| 使っているもの | 意味 |
|---|---|
| origin | 線の始まり。手に持った銃のHandleの位置をサーバーが自分で調べる |
| direction | 線の向きと長さ。.Unit で長さ1にしてから、MAX_DISTANCEの300をかける |
| RaycastParams の ExcludeInstances | 線が当たらないようにする物の一覧。自分の体(銃も体の中に入っている)を入れる |
| result.Instance | 線が最初にぶつかった部品 |
| result.Position | 線がぶつかった場所。レーザーの見た目はここまで伸ばす |
direction の長さが、そのまま線の届く遠さになります。
サーバーが自分で確かめていること
送られてくるのは「ねらった場所」だけです。始まりの場所・だれに当たったか・何点減らすかは、サーバーが自分で決めます。
| 確かめていること | 理由 |
|---|---|
| aimPointがVector3か | ずるをする人が、位置ではないものを送ってくることがあるため |
| COOLDOWNの0.5秒 | 連打で何発も撃てないようにするため |
| 銃を手に持っているか | 銃を持っていない人が撃てないようにするため |
| 始まりはサーバーが調べたHandleの位置 | 始まりの場所を、撃った人が好きな場所に決めて送れないようにするため |
Robloxの公式のチュートリアル(Hit detection with lasers)は、クリックした人のパソコンで当たりを調べ、サーバーがあとから確かめる作りです。この記事は剣の記事と同じく、サーバーが自分で線を飛ばして当たりを決める作りにしています。
7. 練習用のダミーと壁を置く
練習の相手のダミーは、NPCを作る方法と同じ「キャラクター」ボタンで入れます。上の「ホーム」タブの「キャラクター」を押すと「リグを生成」の窓が開きます。リグタイプの「R15」(画像の1)を選んだまま、「ブロック型アバター(2012年)」(画像の2)を押します。

スポーン地点の上に灰色のキャラクター「Rig」が置かれます。エクスプローラの「Rig」を押して選び、F2キーを押して名前を「Dummy」に変えます。
続けてDummyを選んだまま、プロパティの検索欄に「Origin」と入力します。Originの左の▶を押して開き、Positionを「-4, 3, -24」にします(赤い枠)。ダミーがスポーン地点の前に移ります。

ダミーの手順はこれだけです。色や固定は変えません。
レーザーが物に当たると止まる様子を見るため、壁も1枚置きます。エクスプローラの「Workspace」にマウスを乗せて「+」を押し、検索欄に「Part」と入力して、一覧の「Part」を押します(赤い枠)。

Workspaceの中に入った「Part」を押して選び、F2キーを押して名前を「Wall」に変えます(赤い枠)。

続けてWallを選んだまま、次の3つを変えます。どれもプロパティの検索欄に名前を入力してから変え、数字はEnterキーで決めます。PositionはCFrameのすぐ下の欄に入れます。
| プロパティ | 入れる値 |
|---|---|
| Size | 6, 8, 1 |
| Position(▼CFrameのすぐ下) | 8, 4, -26 |
| Anchored | チェックを入れる(固定して倒れないようにする) |
Anchoredは、右の四角を押してチェックを入れます。
3D画面に、灰色のダミー(画像の1)と灰色の壁(画像の2)が並べば準備はできています。

8. テストでダミーに当てる
3D画面の上の作品のタブ「Place1」を押して3D画面に戻り、F5キーでテストを始めます(タブの名前はパソコンによって違います)。数字キーの1を押してブラスターを持ち、ダミーをクリックします。
赤いレーザーがダミーまで伸び(画像の1)、ダミーの頭の上の体力ゲージが減りました(画像の2)。出力には「(アカウント名) のレーザーが Dummy に当たりました。残り 75」と出ます(画像の3)。

出力が見えないときは、3D画面上部メニューの「表示」から「出力」を選ぶと開きます。
続けてマウスのボタンを0.5秒より短い間隔で2回押しました。2回押しても、出力の行は1行しか増えませんでした(赤い枠)。COOLDOWNで決めた0.5秒がたつまでは、サーバーが次の1発を受けつけないからです。

当て続けると残りが50、25と減り、4回目で「残り 0」になりました(赤い枠)。DAMAGEで決めた25ずつ減るので、体力100のダミーは4回で0になります。

体力が0になると、ダミーはその場にくずれ落ちて、地面に低く横たわりました(赤い枠)。頭の上の体力ゲージも空になっています。

次に壁をクリックします。レーザーは壁で止まり(赤い枠)、出力に新しい行は出ませんでした。Raycastは最初にぶつかった物を返すので、壁にかくれた相手には、先に壁に当たって届かない作りです。

Shift + F5キーでテストを停止します。
強さや届く遠さを変える
| 変えたいこと | 変える数 | 例 |
|---|---|---|
| 強さ | DAMAGE | 25 → 10にすると、体力100の相手に10回当てる |
| 連射の速さ | COOLDOWN | 0.5 → 1にすると、1秒に1回まで |
| 届く遠さ | 2つのコードのMAX_DISTANCE | 300 → 100にすると、近くの相手にしか当たらない |
| レーザーを見せる時間 | BEAM_TIME | 0.15 → 0.5にすると、線が長く残る |
MAX_DISTANCEは、LocalScriptとScriptの両方にあります。数がずれても動きますが、2つとも同じ数にしておくと、ねらえる遠さと届く遠さがそろって分かりやすくなります。
当たらない・体力が減らないときの確かめ方
| 症状 | 見るところ | 直し方 |
|---|---|---|
| ブラスターを手に持てない | Handleの名前とAnchored | Toolのすぐ下に「Handle」があり、固定が外れているか |
| 銃身が持ち手から外れる | WeldConstraint | Part0とPart1が入っているか |
| クリックしてもレーザーが出ない | RemoteEventの名前 | ReplicatedStorageの「FireBlaster」と、2つのコードの名前が同じか |
| レーザーは出るのに減らない | 当たった部品 | 間に壁などがないか。出力に「当たりました」の行が出ているか |
| 出てすぐの相手が減らない | 相手のForceField | スポーン地点のDurationの秒数は、守られて減らない |
| 1回で大きく減る・連射できる | COOLDOWN | BlasterServiceのCOOLDOWNが入っているか |
出力の見方は、スクリプトが動かないときの確認手順で解説しています。
AIに相談するときの伝え方
Roblox Studioでレーザーのブラスターを作っています。
StarterPackのTool「Blaster」の中のLocalScriptで、Activatedのときに、UserInputService:GetMouseLocationとCamera:ViewportPointToRayで作った線をworkspace:Raycastで飛ばし、マウスの下にある物の場所を求め、ReplicatedStorageのRemoteEvent「FireBlaster」でその場所だけをサーバーに送っています。
ServerScriptServiceのScriptで、銃を手に持っているか確かめ、HandleからねらいへWorkspace:Raycastで300スタッドの線を飛ばしてください。
RaycastParamsのExcludeInstancesで自分の体を外し、当たった相手のHumanoidをTakeDamageで25減らしてください。
同じ人は0.5秒に1回までにしてください。
よくある質問
ツールボックスの銃を使うのと、何が違う?
ツールボックスの銃は置くだけで使えますが、中のコードがどう動いているかは分かりにくく、だれが作ったかによって中身もさまざまです。自分で作ると、強さ・届く遠さ・連射の速さをコードの上の数だけで変えられます。ツールボックスの物を使うときは、ツールボックスのモデルを安全に使う方法で中身を確かめてから入れてください。
銃のゲームを公開するときに気をつけることは?
作ったゲームを公開するときは、Robloxの「Maturity & Compliance(年齢区分のアンケート)」に答えます。Robloxの公式の説明では、相手を攻撃するような暴力の表現があるゲームはこのアンケートでその内容を申告することになっています。公式の説明では、暴力の強さの質問で、体力が0になった瞬間に体が消えるような現実的でない表現が、いちばん弱い「Mild」の例に挙げられています。くり返し攻撃するゲームかどうかも、頻度の質問で答えます。
次は、赤と青のチームに分けます。次のステップ(中級 14/28)
次に知りたいことを選ぶ
- 次のステップ:赤と青のチームに分けます → ロブロックス(Roblox)でチームを作る方法
- 近くの相手を攻撃する剣も作りたい → 剣の攻撃を作る方法
- 味方と敵に分けたい → チームの分け方
- 手に持つアイテムの基本をおさらいしたい → アイテム(ツール)を作る方法








