【ロブロックス】他ユーザーを押し出すパンチを実装
概要
他ユーザーをパンチしてステージから押し出せる機能を実装します
調査
調べたらこのチュートリアルが紹介されてたのでコードを読んでみます
コードはここから入手可能
-- Made by: AccessQ
-- Youtube: https://youtube.com/AccessQ
-- Twitter: https://twitter.com/RealAcccessQ
CanDoDmg = true -- If the player can do damage
Keybind = "f" -- Keybind to activate the punch
dmg = 10 -- How much damage the player takes
local plr = game.Players.LocalPlayer
local mouse = plr:GetMouse()
mouse.KeyDown:Connect(function(key)
if key == Keybind then
local anim = script.Parent.Humanoid:LoadAnimation(script.Animation)
anim:Play()
script.Parent.RightHand.Touched:connect(function(hit)
if hit.Parent.Humanoid and CanDoDmg == true then
hit.Parent.Humanoid:TakeDamage(dmg)
CanDoDmg = false
wait(1)
CanDoDmg = true
end
end)
end
end)
StarterCharacterScripts
チュートリアルではこのコードを StarterCharacterScripts
に入れていた
キャラクターがリスポーン時は維持されない(値が?)
Localスクリプトに Animate, Sound, or Health などを定義すると上書きできるらしい
アニメーションの確認
上記のスクリプトをそのまま配置して定義されてる「f」キーを押してもエラーになる
Failed to load animation with sanitized ID rbxassetid://4738262351: Animation failed to load
これはL17で呼び出そうとしているscript直下のAnimationに定義されている rbxassetid://4738262351
が見つからないから
script.Parent.Humanoid:LoadAnimation(script.Animation)
チュートリアルではアニメーションを作ってアップロード、そのIDを定義してる
アニメーションの作成と公開
- 「リグビルダー」からリグを配置、「アニメーション編集」を押してアニメーションウィンドウを表示1 > 名前を入力
- Unityみたいにタイムラインで編集するがかなり分かりやすい
- ロブロックスに公開
- 公開するとURLとIDが入手できる
- このIDをスクリプト直下のAnimationへ定義するとスクリプトから実行できるようになる
- リグがあってないのか変なツッコミみたいな動きになった…
クリックでパンチする
要素のクリックイベントはClick Detectorを使うみたいだが、画面全体のクリックの判定はUserInputServiceを使うのが良さそう
描いたコード
local UserInputService = game:GetService("UserInputService")
UserInputService.InputBegan:Connect(function(input)
if
input.UserInputType == Enum.UserInputType.MouseButton1
then
print("punching")
local anim = script.Parent.Humanoid:LoadAnimation(script.Animation)
anim:Play()
script.Parent.RightHand.Touched:connect(function(hit)
if hit.Parent.Humanoid then
print("hit")
wait(1)
end
end)
end
end)
※ ただこれだとメニュークリック時も反応しちゃうので、避けたいときはクリックした対象の判定が必要
パンチ中に他ユーザーに当たると押し出せる
これはLocalでパンチを実行して、「パンチ中のユーザーに当たった」という判定を他ユーザーにさせようとしたらローカルとサーバーでのデータのやり取りでハマりましたが最終的にちゃんとReplicatedStorageにたどり着きました
色々記事を見たけど下記の記事が一番理解できたかつエラーが起こらなかった
RemoteEventとローカル、サーバで参照できる範囲とかについてはまた別途まとめようと思います
(RmoteEventのデータの渡し方はreactの手癖で書きました😅)
クライアント側
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- クリックしたときのイベント
UserInputService.InputBegan:Connect(function(input)
local player = Players.LocalPlayer
if
input.UserInputType == Enum.UserInputType.MouseButton1
then
player:SetAttribute("IsPunching", true)
-- パンチのアニメーション
local anim = script.Parent.Humanoid:LoadAnimation(script.PunchAnimation)
anim:Play()
wait(1)
player:SetAttribute("IsPunching", false)
end
end)
-- 何かにぶつかった時
local function onPunch(hit)
local player = Players.LocalPlayer
local targetCharacter = hit.Parent
local targetPlayer = Players:GetPlayerFromCharacter(targetCharacter)
local punchEvent = ReplicatedStorage:WaitForChild("PunchEvent")
-- 自分がパンチ中 & Humanoidだった場合
if
targetCharacter:FindFirstChild("Humanoid")
and player:GetAttribute("IsPunching")
then
player:SetAttribute("IsPunching", false)
local nockBackVector = player.Character.HumanoidRootPart.CFrame.LookVector
print("リモートイベント送信")
local data = {
["eventKey"] = "PUNCH";
["payload"] = {
["targetPlayer"] = targetPlayer;
["nockBackVector"] = nockBackVector;
};
}
punchEvent:FireServer(data)
end
end
-- Serverイベント受信時
local moveEvent = ReplicatedStorage:WaitForChild("moveEvent")
moveEvent.OnClientEvent:Connect(function(_data)
print("サーバイベント受信", _data)
local nockBackVector = _data.payload
local LinearVelocity = Instance.new("LinearVelocity")
local player = Players.LocalPlayer
local finalVector = (nockBackVector * 50 ) + Vector3.new(0,12,0)
local Attachment = Instance.new("Attachment", player.Character.HumanoidRootPart)
LinearVelocity.VectorVelocity = finalVector
LinearVelocity.ForceLimitsEnabled = true
LinearVelocity.MaxForce = 5000
LinearVelocity.Parent = player.Character.HumanoidRootPart
LinearVelocity.Attachment0 = Attachment
LinearVelocity.Enabled = true
wait(0.01)
LinearVelocity:Destroy()
end)
script.Parent.RightHand.Touched:connect(onPunch)
script.Parent.Head.Touched:connect(onPunch)
サーバ側
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
--
local punchEvent = Instance.new("RemoteEvent", ReplicatedStorage)
punchEvent.Name = "PunchEvent"
local moveEvent = Instance.new("RemoteEvent", ReplicatedStorage)
moveEvent.Name = "moveEvent"
punchEvent.OnServerEvent:Connect(function(player, _data)
print("クライアント側でイベント受信")
print(_data)
local eventKey = _data["eventKey"]
local payload = _data["payload"]
if eventKey == "PUNCH" then
print(`eventKey == "PUNCH"`)
print(payload)
local data = {
["eventKey"] = "MOVE_EVENT";
["payload"] = payload.nockBackVector;
}
print(data)
moveEvent:FireClient(payload.targetPlayer, data)
end
end)
--
まとめ
ゲームのリアルタイム通信系の処理を割と初めて書いた気がする
遅延とかリアルタイム性を除けば割とWebのクライアント(ブラウザ)側とサーバ側と同じ感覚でいいかもしれない
Discussion