Trong hướng dẫn này, bạn sẽ học cách phát một laser từ blaster trong Tạo công cụ người chơi và xác định xem nó có đánh trúng người chơi hay không.
Phát sóng để tìm va chạm
Phát bắn tia tạo ra một tia vô hình từ vị trí bắt đầu theo một hướng nhất định với chiều dài được xác định.Nếu tia va chạm với các đối tượng hoặc địa hình trên đường đi của nó, nó sẽ trả lại thông tin về va chạm như vị trí và đối tượng nó đã va chạm.

Tìm vị trí chuột
Trước khi một laser có thể bị bắn, bạn phải biết trước tiên người chơi đang nhắm vào đâu.Điều này có thể được tìm thấy bằng cách raycasting từ vị trí chuột 2D của người chơi trên màn hình thẳng từ máy ảnh vào thế giới trò chơi.Tia sẽ va chạm với bất cứ thứ gì mà người chơi đang nhắm vào bằng chuột.
Mở Tập lệnh điều khiển công cụ bên trong công cụ Blaster từ Tạo công cụ người chơi.Nếu bạn chưa hoàn thành hướng dẫn đó, bạn có thể tải xuống mô hình Blaster và chèn nó vào StarterPack.
Ở đầu của kịch bản, tuyên bố một biến có tên MAX_MOUSE_DISTANCE với giá trị là 1000 .
Tạo một chức năng có tên là getWorldMousePosition.
local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()endlocal function toolActivated()tool.Handle.Activate:Play()end-- Kết nối sự kiện với chức năng thích hợptool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Sử dụng chức năng GetMouseLocation của UserInputService để lấy vị trí chuột 2D của người chơi trên màn hình.Gán điều này cho biến có tên là mouseLocation .
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
Bây giờ vị trí chuột 2D đã được biết, các thuộc tính X và Y của nó có thể được sử dụng làm tham số cho chức năng Camera:ViewportPointToRay(), tạo ra một Ray từ màn hình vào thế giới trò chơi 3D.
Sử dụng các thuộc tính X và Y của mouseLocation như là tham số cho chức năng ViewportPointToRay().Gán điều này cho một biến có tên là screenToWorldRay .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Tạo một tia từ vị trí chuột 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
Đã đến lúc sử dụng chức năng Raycast để kiểm tra xem liệu tia có đánh vào một đối tượng hay không.Điều này yêu cầu vị trí và hướng khởi động: trong ví dụ này, bạn sẽ sử dụng các thuộc tính nguồn gốc và hướng của screenToWorldRay .
Chiều dài của vector hướng xác định cách xa mà tia sẽ di chuyển.Tia cần phải dài như MAX_MOUSE_DISTANCE , vì vậy bạn sẽ phải nhân vectơ hướng bằng MAX_MOUSE_DISTANCE .
Tuyên bố một biến có tên là directionVector và gán cho nó giá trị của screenToWorldRay.Direction nhân với MAX_MOUSE_DISTANCE .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Tạo một đường ray từ vị trí chuột 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Vectơ hướng của đơn vị của tia nhân với khoảng cách tối đalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEGọi chức năng Raycast của không gian làm việc, truyền thuộc tính Nguồn gốc của screenToWorldRay như là tham số đầu tiên và directionVector như tham số thứ hai.Gán điều này cho biến có tên là raycastResult .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Tạo một đường ray từ vị trí chuột 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Vectơ hướng của đơn vị của tia nhân với khoảng cách tối đalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Phát sóng từ nguồn của tia về hướng của nólocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Thông tin va chạm
Nếu hoạt động phát bắn tia tìm thấy một đối tượng bị tác động bởi tia, nó sẽ trả về một RaycastResult , chứa thông tin về va chạm giữa tia và đối tượng.
Thuộc tính RaycastResult | Mô tả |
---|---|
Ví dụ | Tế bào BasePart hoặc Terrain mà tia đã cắt qua. |
Vị trí | Nơi giao lộ xảy ra; thường là một điểm trực tiếp trên bề mặt của một phần hoặc khu vực. |
Vật liệu | Vật liệu tại điểm va chạm. |
Bình thường | Vectơ bình thường của khuôn mặt bị chồng lấp. Điều này có thể được sử dụng để xác định hướng mặt đang chỉ vào. |
Thuộc tính Vị trí sẽ là vị trí của đối tượng mà chuột đang lượn qua.Nếu chuột không lượn qua bất kỳ đối tượng nào trong khoảng cách MAX_MOUSE_DISTANCE , raycastResult sẽ là nil .
Tạo một if tuyên bố để kiểm tra xem raycastResult có tồn tại hay không.
Nếu raycastResult có giá trị, trả lại thuộc tính Vị trí của nó.
Nếu raycastResult là nil thì tìm điểm cuối của raycast.Tính vị trí 3D của con trỏ bằng cách thêm screenToWorldRay.Origin và directionVector cùng nhau.
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Tạo một đường ray từ vị trí chuột 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Vectơ hướng của đơn vị của tia nhân với khoảng cách tối đa
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Phát sóng từ nguồn của tia về hướng của nó
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Trả về điểm giao lộ 3D
return raycastResult.Position
else
-- Không có đối tượng nào bị đánh nên tính vị trí ở cuối chùm tia
return screenToWorldRay.Origin + directionVector
end
end
Bắn vào mục tiêu
Bây giờ khi vị trí chuột 3D đã được biết, nó có thể được sử dụng như một vị trí mục tiêu để bắn một laser về phía .Một tia thứ hai có thể được phóng giữa vũ khí của người chơi và vị trí mục tiêu bằng cách sử dụng chức năng Raycast .
Tuyên bố một biến có tên là MAX_LASER_DISTANCE ở đầu của kịch bản và gán nó cho 500 , hoặc phạm vi được chọn của laser blaster.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Tạo một chức năng có tên là fireWeapon dưới chức năng getWorldMousePosition.
Gọi getWorldMousePosition và gán kết quả cho một biến có tên là mousePosition . Đây sẽ là vị trí mục tiêu cho raycast.
-- Không có đối tượng nào bị đánh nên tính vị trí ở cuối chùm tiareturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Lần này, vector hướng cho chức năng phát bắn tia sẽ đại diện cho hướng từ vị trí công cụ của người chơi đến vị trí mục tiêu.
Tuyên bố một biến có tên là targetDirection và tính vectơ hướng bằng cách trừ vị trí công cụ từ mouseLocation.
Bình thường hóa vector bằng cách sử dụng thuộc tính Unit của nó. Điều này cho nó một độ lớn là 1, làm cho nó dễ dàng nhân với một chiều dài sau này.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Tính một vector hướng bình thường và nhân với khoảng cách laserlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitendTuyên bố một biến có tên là directionVector và gán cho nó targetDirection nhân với MAX_LASER_DISTANCE .
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- Hướng bắn vũ khí, nhân với khoảng cách tối đalocal directionVector = targetDirection * MAX_LASER_DISTANCEend
Một đối tượng RaycastParams có thể được sử dụng để lưu các tham số bổ sung cho chức năng phát bắn tia.Nó sẽ được sử dụng trong laser blaster của bạn để đảm bảo tia phóng không vô tình va chạm với người bắn vũ khí.Bất kỳ phần nào bao gồm trong tính chất của một đối tượng RaycastParams sẽ bị bỏ qua trong raycast
Tiếp tục chức năng fireWeapon và tuyên bố một biến có tên là weaponRaycastParams . Gán một đối tượng RaycastParams mới cho nó.
Tạo một bảng chứa nhân vật địa phương của người chơi và gán nó cho thuộc tính đó.
Phát tia từ vị trí tay cầm công cụ của người chơi, theo hướng tới directionVector .Hãy nhớ thêm weaponRaycastParams như một tham số lần này.Gán điều này cho một biến có tên là weaponRaycastResult .
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local tool = script.Parent
local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 500
local function getWorldMousePosition()
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Tính một vector hướng bình thường và nhân với khoảng cách laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Hướng bắn vũ khí nhân với khoảng cách tối đa
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Bỏ qua nhân vật của người chơi để ngăn chặn họ tự làm hại bản thân
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end
Cuối cùng, bạn sẽ cần kiểm tra xem hoạt động phát bắn tia trả về một giá trị.Nếu một giá trị được trả về, một đối tượng đã bị tác động bởi tia và một laser có thể được tạo giữa vũ khí và vị trí bị ảnh hưởng.Nếu không có gì được trả về, vị trí cuối cùng cần được tính toán để tạo ra laser.
Tuyên bố một biến trống có tên là hitPosition .
Sử dụng một if tuyên bố để kiểm tra xem có phải weaponRaycastResult có giá trị hay không. Nếu một đối tượng bị đánh, gán weaponRaycastResult.Position cho hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Kiểm tra xem có bất kỳ đối tượng nào bị đánh giữa vị trí bắt đầu và kết thúc hay khônglocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendNếu weaponRaycastResult không có giá trị, tính vị trí cuối cùng của phát bắn tia bằng cách thêm vị trí **** của tay cầm công cụ với directionVector .Gán điều này cho hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Kiểm tra xem có bất kỳ đối tượng nào bị đánh giữa vị trí bắt đầu và kết thúc hay khônglocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Tính vị trí cuối cùng dựa trên khoảng cách laser tối đahitPosition = tool.Handle.Position + directionVectorendendDi chuyển đến chức năng toolActivated và gọi chức năng fireWeapon để laser bắn mỗi khi công cụ được kích hoạt.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
Kiểm tra đối tượng bị ảnh hưởng
Để tìm xem liệu đối tượng bị ảnh hưởng bởi laser có là một phần của nhân vật của người chơi hay chỉ là một phần cảnh quan, bạn sẽ cần tìm kiếm một Humanoid, vì mỗi nhân vật đều có một.
Trước tiên, bạn sẽ cần tìm kiếm mô hình nhân vật .Nếu một phần của nhân vật bị đánh, bạn không thể cho rằng cha của đối tượng bị đánh sẽ là nhân vật.Laser có thể đã đánh vào một phần cơ thể, một phụ kiện hoặc một công cụ, tất cả đều nằm ở các phần khác nhau trong bảng xếp hạng của nhân vật.
Bạn có thể sử dụng FindFirstAncestorOfClass để tìm một tổ tiên mô hình nhân vật của đối tượng bị ảnh hưởng bởi laser, nếu có.Nếu bạn tìm thấy một mô hình và nó chứa một hình người, trong hầu hết các trường hợp bạn có thể cho rằng nó là một nhân vật.
Thêm mã nổi bật bên dưới vào tuyên bố weaponRaycastResult nếu để kiểm tra xem một nhân vật đã bị đánh hay không.
-- Kiểm tra xem có bất kỳ đối tượng nào bị đánh giữa vị trí bắt đầu và kết thúc hay khônglocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- Cú đánh của ví trí sẽ là con của một mô hình nhân vật-- Nếu một humanoid được tìm thấy trong mô hình thì có khả năng nó là nhân vật của một người chơilocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Player hit")endendelse-- Tính vị trí cuối cùng dựa trên khoảng cách laser tối đahitPosition = tool.Handle.Position + directionVectorend
Bây giờ máy chiếu laser nên in Player hit vào cửa sổ Thành phẩm mỗi khi hoạt động chiếu tia đánh trúng người chơi khác.
Thử nghiệm với nhiều người chơi
Cần hai người chơi để kiểm tra xem liệu phạm vi vũ khí có tìm thấy người chơi khác hay không, do đó bạn cần phải khởi động một máy chủ địa phương.
Chọn tab Thử nghiệm trong Studio.
Hãy chắc chắn rằng menu thả xuống của người chơi được đặt thành '2 Người chơi' và nhấp vào nút Bắt đầu để bắt đầu một máy chủ địa phương với 2 khách hàng.Ba cửa sổ sẽ xuất hiện.Cửa sổ đầu tiên sẽ là máy chủ địa phương, các cửa sổ khác sẽ là khách hàng cho Player1 và Player2.
Trên một máy khách, thử bắn vào người chơi khác với vũ khí bằng cách nhấp vào họ.“Người chơi bị đánh” nên được hiển thị trong đầu ra mỗi khi một người chơi bị bắn.
Bạn có thể tìm hiểu thêm về tab Kiểm tra ở đây .
Tìm vị trí laser
Máy phóng nên bắn một tia sáng đỏ vào mục tiêu của nó.Chức năng cho điều này sẽ nằm bên trong một ModuleScript để có thể được sử dụng lại trong các kịch bản khác sau này.Trước tiên, kịch bản sẽ cần tìm vị trí mà chùm laser nên được hiển thị.
Tạo một ModuleScript có tên LaserRenderer , thuộc về StarterPlayerScripts dưới StarterPlayer.
Mở trình kịch bản và đổi tên bảng module thành tên của trình kịch bản LaserRenderer .
Tuyên bố một biến có tên là THỜI GIAN BẮN với giá trị là 0.15 .Đây sẽ là thời gian (theo giây) mà laser có thể nhìn thấy.
Tạo một chức năng của LaserRenderer có tên createLaser với hai tham số được gọi là toolHandle và endPosition .
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Thời gian laser có thể nhìn thấy trong-- Tạo một chùm laser từ vị trí bắt đầu đến vị trí cuối cùngfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererTuyên bố một biến có tên là startPosition và đặt thuộc tính Vị trí của toolHandle như giá trị của nó.Đây sẽ là vị trí của laser blaster của người chơi.
Tuyên bố một biến có tên là laserDistance và trừ endPosition từ startPosition để tìm sự khác biệt giữa hai vector.Sử dụng thuộc tính Magnitude của thứ này để lấy chiều dài của tia laser.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendTuyên bố một biến laserCFrame để lưu vị trí và hướng của tia laser.Vị trí cần phải là điểm giữa của đầu và cuối của chùm tia.Sử dụng CFrame.lookAt để tạo một mới CFrame được đặt tại startPosition và hướng về endPosition .Nhân điều này với một CFrame mới có giá trị trục Z bằng một nửa của negative laserDistance để có được điểm giữa.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)end
Tạo phần laser
Bây giờ bạn đã biết nơi tạo một chùm laser, bạn cần thêm chính chùm laser. Điều này có thể được thực hiện dễ dàng với một phần Neon.
Tuyên bố một biến laserPart và gán cho nó một ví dụ / trường hợpmới Part.
Đặt các thuộc tính sau của laserPart :
- Kích thước : Vector3.new(0.2, 0.2, khoảng cách laser)
- Khung C : laserCFrame
- Được neo : true
- Có thể va chạm : false
- Màu : Color3.fromRGB(225, 0, 0) (một màu đỏ mạnh)
- Vật liệu : Enum.Material.Neon
cha laserPart đến Không gian làm việc .
Thêm phần vào dịch vụ Debris để nó được xóa sau số giây trong biến SHOT_DURATION .
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)local laserPart = Instance.new("Part")laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)laserPart.CFrame = laserCFramelaserPart.Anchored = truelaserPart.CanCollide = falselaserPart.Color = Color3.fromRGB(225, 0, 0)laserPart.Material = Enum.Material.NeonlaserPart.Parent = workspace-- Thêm chùm laser vào dịch vụ Debris để được xóa và dọn sạchDebris:AddItem(laserPart, SHOT_DURATION)end
Bây giờ chức năng để render chùm tia laser đã hoàn thành, nó có thể được gọi bởi ToolController .
Ở đầu của Tập lệnh điều khiển công cụ , tuyên bố một biến có tên LaserRenderer và yêu cầu mô-đun LaserRenderer ModuleScript nằm trong PlayerScripts.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentỞ đáy chức năng fireWeapon, gọi chức năng LaserRenderer createLaser bằng cách sử dụng tay cầm công cụ và hitPosition làm tham số.
-- Tính vị trí cuối cùng dựa trên khoảng cách laser tối đahitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endThử vũ khí bằng cách nhấp vào nút Chơi. Một tia laze nên có thể nhìn thấy giữa vũ khí và chuột khi công cụ được kích hoạt.
Kiểm soát tỷ lệđộ bắn vũ khí
Vũ khí cần một khoảng thời gian chậm giữa mỗi phát súng để ngăn người chơi gây quá nhiều sát thương trong một thời gian ngắn.Điều này có thể được kiểm soát bằng cách kiểm tra xem đã trôi qua đủ thời gian kể từ khi một người chơi bắn lần cuối.
Tuyên bố một biến ở đầu của ToolController được gọi FIRE_RATE .Đây sẽ là thời gian tối thiểu giữa mỗi phát bắn.Đưa nó một giá trị bạn chọn; ví dụ này sử dụng 0.3 giây.
Tuyên bố một biến khác bên dưới tên là thời gian của phát bắn trước với giá trị là 0 .Nó lưu lần cuối cùng mà người chơi bắn và sẽ được cập nhật với mỗi phát súng.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Tạo một chức năng có tên là có thể bắn vũ khí mà không có tham số.Chức năng này sẽ xem xét bao nhiêu thời gian đã trôi qua kể từ phát bắn trước và trả về true hoặc false.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Kiểm tra xem đã trôi qua đủ thời gian kể từ khi phát bắn lần trướclocal function canShootWeapon()endlocal function getWorldMousePosition()Bên trong chức năng tuyên bố một biến có tên là currentTime ; gán cho nó kết quả của việc gọi chức năng tick().Điều này trả về bao nhiêu thời gian đã trôi qua, trong giây lát, kể từ ngày 1 tháng 1 năm 1970 (một ngày cố định được sử dụng rộng rãi để tính thời gian).
Trừ timeOfPreviousShot từ currentTime và trả về false nếu kết quả nhỏ hơn FIRE_RATE ; nếu không, trả về true .
-- Kiểm tra xem đã trôi qua đủ thời gian kể từ khi phát bắn lần trướclocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendTại cuối chức năng fireWeapon , cập nhật timeOfPreviousShot mỗi khi vũ khí bị bắn bằng tick .
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endBên trong chức năng toolActivated, tạo một tuyên bố nếu và gọi canShootWeapon để kiểm tra xem có thể bắn vũ khí không.
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
Khi bạn kiểm tra súng siêu âm, bạn nên tìm thấy rằng bất kể bạn nhấp chuộtnhanh như thế nào, sẽ luôn có một khoảng chờ ngắn 0,3 giây giữa mỗi phát bắn.
Thiệt hại người chơi
Các khách hàng không thể làm hại các khách hàng khác trực tiếp; máy chủ cần phải chịu trách nhiệm phát hành sát thương khi một người chơi bị tấn công.
Khách hàng có thể sử dụng một RemoteEvent để cho máy chủ biết rằng một nhân vật đã bị đánh.Chúng nên được lưu trữ trong ReplicatedStorage , nơi chúng có thể nhìn thấy cả đối với khách hàng và máy chủ.
Tạo một Thư mục trong ReplicatedStorage có tên là Sự kiện .
Thêm một sự kiện từ xa vào thư mục Sự kiện và đặt tên nó là DamageCharacter .
Trong ToolController , tạo biến tại đầu của kịch bản cho ReplicatedStorage và thư mục Sự kiện.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.Parentlocal eventsFolder = ReplicatedStorage.Eventslocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Thay thế tuyên bố in "Player hit" với một dòng Luau để bắn sự kiện điều khiển từ xa fireWeapon với biến DamageCharacter làm tham số với điều kiện characterModel .
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel)endendelse-- Tính vị trí cuối cùng dựa trên khoảng cách laser tối đahitPosition = tool.Handle.Position + directionVectorend
Máy chủ cần gây sát thương cho người chơi đã bị tấn công khi sự kiện bị kích hoạt.
Thêm Tập lệnh vào ServerScriptService và đặt tên nó là ServerLaserManager .
Tuyên bố một biến có tên là LASER_DAMAGE và đặt nó thành 10 hoặc một giá trị của lựa chọn của bạn.
Tạo một chức năng có tên là damageCharacter với hai tham số được gọi là playerFired và characterToDamage .
Bên trong chức năng, tìm nhân vật Humanoid của nhân vật và trừ LASER_DAMAGE từ sức khỏe của nó.
Kết nối chức năng damageCharacter với sự kiện remote DamageCharacter trong thư mục Sự kiện.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10function damageCharacter(playerFired, characterToDamage)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Loại bỏ sức khỏe từ nhân vậthumanoid.Health -= LASER_DAMAGEendend-- Kết nối sự kiện với chức năng thích hợpeventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)Thử nghiệm blaster với 2 người chơi bằng cách khởi động một máy chủ địa phương.Khi bạn bắn người chơi khác, sức khỏe của họ sẽ giảm bởi số được gán cho LASER_DAMAGE .
Hiển thị các tia laser của người chơi khác
Hiện tại, tia laser được tạo bởi khách hàng bắn vũ khí, vì vậy chỉ họ mới có thể nhìn thấy tia laser.
Nếu chùm laser được tạo trên máy chủ thì mọi người sẽ có thể nhìn thấy nó.Tuy nhiên, sẽ có một sự chậm trễ nhỏ giữa khách hàng bắn vũ khí và máy chủ nhận thông tin về phát bắn .Điều này có nghĩa là khách hàng bắn vũ khí sẽ thấy một sự chậm trễ giữa khi họ kích hoạt vũ khí và khi họ nhìn thấy chùm laser; vũ khí sẽ cảm thấy chậm chạp là kết quả.
Để giải quyết vấn đề này, mỗi khách hàng sẽ tạo ra các chùm laser riêng của họ.Điều này có nghĩa là khách hàng bắn vũ khí sẽ nhìn thấy tia laser ngay lập tức.Các khách hàng khác sẽ trải nghiệm một sự chậm trễ nhỏ giữa khi người chơi khác bắn và một chùm tia xuất hiện.Đây là trường hợp tốt nhất: không có cách nào để giao tiếp laser của một khách hàng cho các khách hàng khác nhanh hơn.
Khách hàng của kẻ bắn
Trước tiên, khách hàng cần báo cho máy chủ rằng nó đã bắn một laser và cung cấp vị trí cuối cùng.
Thêm một Sự kiện từ xa vào thư mục Sự kiện trong ReplicatedStorage và đặt tên nó là LaserFired .
Tìm chức năng fireWeapon trong kịch bản ToolController .Tại cuối chức năng, kích hoạt sự kiện điều khiển LaserFired bằng cách sử dụng hitPosition như một tham số.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
Máy máy chủ
Máy chủ bây giờ phải nhận sự kiện mà khách hàng đã bắn và cho tất cả các khách hàng vị trí bắt đầu và kết thúc của chùm laser để họ cũng có thể hiển thị nó.
Trong kịch bản ServerLaserManager , tạo một chức năng có tên playerFiredLaser trên damageCharacter với hai tham số được gọi là playerFired và endPosition.
Kết nối chức năng với sự kiện điều khiển từ xa LaserFired .
-- Thông báo tất cả các khách hàng rằng một laser đã bị bắn để họ có thể hiển thị laserlocal function playerFiredLaser(playerFired, endPosition)end-- Kết nối sự kiện với chức năng thích hợpeventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Máy chủ cần vị trí khởi động của laser.Nó có thể được gửi từ khách hàng, nhưng tốt nhất là tránh tin cậy khách hàng nếu có thể.Vị trí tay vũ khí của nhân vật sẽ là vị trí bắt đầu, vì vậy máy chủ có thể tìm thấy nó từ đó.
Tạo một chức năng getPlayerToolHandle trên chức năng playerFiredLaser với một tham số có tên là player .
Sử dụng mã sau đây để tìm kiếm nhân vật của người chơi cho vũ khí và trả lại đối tượng tay cầm.
local LASER_DAMAGE = 10-- Tìm tay cầm của công cụ mà người chơi đang giữlocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Thông báo tất cả các khách hàng rằng một laser đã bị bắn để họ có thể hiển thị laserlocal function playerFiredLaser(playerFired, endPosition)
Máy chủ bây giờ có thể gọi FireAllClients trên sự kiện remote LaserFired để gửi thông tin cần thiết để vẽ laser cho khách hàng.Điều này bao gồm người chơi bắn laser (vì vậy khách hàng cho người chơi đó không hiển thị laser hai lần), tay cầm của blaster (hoạt động như vị trí bắt đầu cho laser) và vị trí cuối cùng của laser.
Trong chức năng playerFiredLaser, gọi chức năng getPlayerToolHandle với playerFired như một tham số và gán giá trị cho một biến có tên là toolHandle .
Nếu công cụ toolHandle tồn tại, kích hoạt sự kiện LaserFired cho tất cả các khách hàng sử dụng playerFired , toolHandle và endPosition như là tham số.
-- Thông báo tất cả các khách hàng rằng một laser đã bị bắn để họ có thể hiển thị laserlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
Hiển thị trên các máy khách
Bây giờ FireAllClients đã được gọi, mỗi khách hàng sẽ nhận được một sự kiện từ máy chủ để render một chùm laser.Mỗi khách hàng có thể tái sử dụng mô-đun LaserRenderer từ trước để render chùm tia laze bằng vị trí tay cầm và vị trí cuối cùng của công cụ được gửi bởi máy chủ.Người chơi đã bắn chùm laser ban đầu nên bỏ qua sự kiện này nếu không họ sẽ thấy 2 chùm laser.
Tạo một LocalScript trong StarterPlayerScripts có tên là ClientLaserManager .
Bên trong kịch bản, yêu cầu mô-đun LaserRenderer .
Tạo một chức năng có tên createPlayerLaser với các tham số playerWhoShot , toolHandle và endPosition .
Kết nối chức năng với sự kiện điều khiển từ xa LaserFired trong thư mục Sự kiện.
Trong chức năng, sử dụng một if tuyên bố để kiểm tra xem có phải không bằng LocalPlayer không.
Bên trong tuyên bố if, gọi chức năng createLaser từ mô-đun LaserRenderer bằng cách sử dụng toolHandle và endPosition như là tham số.
local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))local eventsFolder = ReplicatedStorage.Events-- Hiển thị laser của người chơi kháclocal function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)Thử nghiệm blaster với 2 người chơi bằng cách khởi động một máy chủ địa phương.Vị trí mỗi khách hàng trên các bên khác nhau của màn hình của bạn để bạn có thể nhìn thấy cả hai cửa sổ cùng một lúc.Khi bạn bắn vào một khách hàng, bạn nên thấy laser trên khách hàng khác.
Hiệu ứng âm thanh
Hiệu ứng âm thanh bắn hiện tại chỉ phát trên khách hàng đang bắn viên đạn.Bạn sẽ cần di chuyển mã để chơi âm thanh để các người chơi khác cũng nghe thấy nó.
Trong kịch bản ToolController , di chuyển đến chức năng toolActivated và xóa dòng chơi âm thanh Kích hoạt.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendỞ đáy của chức năng createLaser ở LaserRenderer , tuyên bố một biến có tên là âm thanh bắn và sử dụng phương pháp FindFirstChild() của toolHandle để kiểm tra âm thanh Kích hoạt .
Sử dụng một if tuyên bố để kiểm tra xem có shootingSound tồn tại không; nếu có, gọi chức năng Chơi của nó.
laserPart.Parent = workspace-- Thêm chùm laser vào dịch vụ Debris để được xóa và dọn sạchDebris:AddItem(laserPart, SHOT_DURATION)-- Chơi tiếng bắn của vũ khílocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Xác thực các điều khiển từ xa bằng xác minh
Nếu máy chủ không kiểm tra dữ liệu từ các yêu cầu nhận, một hacker có thể lạm dụng chức năng và sự kiện từ xa và sử dụng chúng để gửi các giá trị giả vào máy chủ.Quan trọng là sử dụng xác minh bên máy chủ để ngăn chặn điều này.
Trong hình thức hiện tại, sự kiện remote DamageCharacter rất dễ bị tấn công.Hacker có thể sử dụng sự kiện này để làm hại bất kỳ người chơi nào họ muốn trong trò chơi mà không bắn họ.
Xác minh là quá trình kiểm tra các giá trị được gửi đến máy chủ là có thực tế. Trong trường hợp này, máy chủ sẽ cần:
- Kiểm tra xem khoảng cách giữa người chơi và vị trí bị ảnh hưởng bởi laser có nằm trong giới hạn nhất định không.
- Phát bắn giữa vũ khí bắn laser và vị trí bị trúng để đảm bảo rằng cú bắn có thể và không đi qua bất kỳ bức tường nào.
Khách hàng
Client cần gửi máy chủ vị trí bị ảnh hưởng bởi raycast để kiểm tra khoảng cách có thực tế không.
Trong ToolController , di chuyển đến dòng mà sự kiện remote DamageCharacter được kích hoạt trong chức năng fireWeapon.
Thêm hitPosition như một argument.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Máy chủ
Client bây giờ đang gửi tham số bổ sung thông qua sự kiện remote DamageCharacter, vì vậy ServerLaserManager cần được điều chỉnh để chấp nhận nó.
Trong kịch bản ServerLaserManager , thêm một tham số hitPosition vào chức năng damageCharacter.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Loại bỏ sức khỏe từ nhân vậthumanoid.Health -= LASER_DAMAGEendendBên dưới chức năng getPlayerToolHandle, tạo một chức năng có tên isHitValid với ba tham số: playerFired, characterToDamage và hitPosition .
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
Kiểm tra đầu tiên sẽ là khoảng cách giữa vị trí trúng và nhân vật trúng.
Tuyên bố một biến có tên là MAX_HIT_PROXIMITY ở đầu trang kịch bản và gán cho nó giá trị 10 .Đây sẽ là khoảng cách tối đa được phép giữa hit và nhân vật.Cần có sự dung thứ vì nhân vật có thể đã di chuyển một chút kể từ khi khách hàng kích hoạt sự kiện.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10Trong chức năng isHitValid, tính khoảng cách giữa nhân vật và vị trí đánh.Nếu khoảng cách lớn hơn MAX_HIT_PROXIMITY thì trả về sai .
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Xác minh khoảng cách giữa nhân vật đánh và vị trí đánhlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
Kiểm tra thứ hai sẽ liên quan đến một phát bắn tia giữa vũ khí bắn và vị trí bị trúng.Nếu raycast trả về một đối tượng không phải là nhân vật, bạn có thể cho rằng phát bắn không hợp lệ vì có thứ gì đó đang chặn phát bắn.
Sao chép mã bên dưới để thực hiện kiểm tra này. Trả về true tại cuối chức năng: nếu nó đến được kết thúc, tất cả các kiểm tra đã thông qua.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Xác minh khoảng cách giữa nhân vật đánh và vị trí đánhlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Kiểm tra xem có bắn qua tường khônglocal toolHandle = getPlayerToolHandle(playerFired)if toolHandle thenlocal rayLength = (hitPosition - toolHandle.Position).Magnitudelocal rayDirection = (hitPosition - toolHandle.Position).Unitlocal raycastParams = RaycastParams.new()raycastParams.FilterDescendantsInstances = {playerFired.Character}local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)-- Nếu một ví dụ bị đánh mà không phải là nhân vật thì bỏ qua phát bắnif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendTuyên bố một biến trong chức năng damageCharacter gọi validShot .Gán cho nó kết quả của cuộc gọi đến chức năng isHitValid với ba tham số: playerFired , characterToDamage và hitPosition .
Trong phát biểu dưới đây, thêm một và operator để kiểm tra xem validShot có phải là true hay không.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")local validShot = isHitValid(playerFired, characterToDamage, hitPosition)if humanoid and validShot then-- Loại bỏ sức khỏe từ nhân vậthumanoid.Health -= LASER_DAMAGEendend
Bây giờ sự kiện sự kiện từ xa của damageCharacter an toàn hơn và sẽ ngăn chặn hầu hết người chơi lạm dụng nó.Lưu ý rằng một số người chơi độc hại thường tìm cách tránh xa việc xác minh; duy trì các sự kiện từ xa an toàn là một nỗ lực liên tục.
Pháo laser của bạn đã hoàn thành, với một hệ thống phát hiện cơ bản sử dụng raycasting.Hãy thử hướng dẫn Phát hiện đầu vào người dùng để tìm hiểu cách bạn có thể thêm một hành động tải lại vào laser blaster của bạn, hoặc tạo bản đồ trò chơi vui vẻ và thử laser blaster của bạn với những người chơi khác!
Mã cuối mã
Trình điều khiển công cụ
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
local tool = script.Parent
local eventsFolder = ReplicatedStorage.Events
local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 500
local FIRE_RATE = 0.3
local timeOfPreviousShot = 0
-- Kiểm tra xem đã trôi qua đủ thời gian kể từ khi phát bắn lần trước
local function canShootWeapon()
local currentTime = tick()
if currentTime - timeOfPreviousShot < FIRE_RATE then
return false
end
return true
end
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Tạo một tia từ vị trí chuột 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Vectơ hướng của đơn vị của tia nhân với khoảng cách tối đa
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Phát sóng từ nguồn của roy đến hướng của nó
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Trả về điểm giao lộ 3D
return raycastResult.Position
else
-- Không có đối tượng nào bị đánh nên tính vị trí ở cuối chùm tia
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Tính một vector hướng bình thường và nhân với khoảng cách laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Hướng bắn vũ khí, nhân với khoảng cách tối đa
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Bỏ qua nhân vật của người chơi để ngăn chặn họ tự làm hại bản thân
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Kiểm tra xem có bất kỳ đối tượng nào bị đánh giữa vị trí bắt đầu và kết thúc hay không
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- Cú đánh của ví trí sẽ là con của một mô hình nhân vật
-- Nếu một humanoid được tìm thấy trong mô hình thì có khả năng nó là nhân vật của một người chơi
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
if characterModel then
local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
if humanoid then
eventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)
end
end
else
-- Tính vị trí cuối cùng dựa trên khoảng cách laser tối đa
hitPosition = tool.Handle.Position + directionVector
end
timeOfPreviousShot = tick()
eventsFolder.LaserFired:FireServer(hitPosition)
LaserRenderer.createLaser(tool.Handle, hitPosition)
end
local function toolEquipped()
tool.Handle.Equip:Play()
end
local function toolActivated()
if canShootWeapon() then
fireWeapon()
end
end
tool.Equipped:Connect(toolEquipped)
tool.Activated:Connect(toolActivated)
Người tạo laser
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Thời gian laser có thể nhìn thấy trong
-- Tạo một chùm laser từ vị trí bắt đầu đến vị trí cuối cùng
function LaserRenderer.createLaser(toolHandle, endPosition)
local startPosition = toolHandle.Position
local laserDistance = (startPosition - endPosition).Magnitude
local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)
local laserPart = Instance.new("Part")
laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)
laserPart.CFrame = laserCFrame
laserPart.Anchored = true
laserPart.CanCollide = false
laserPart.Color = Color3.fromRGB(255, 0, 0)
laserPart.Material = Enum.Material.Neon
laserPart.Parent = workspace
-- Thêm chùm laser vào dịch vụ Debris để được xóa và dọn sạch
Debris:AddItem(laserPart, SHOT_DURATION)
-- Chơi tiếng bắn của vũ khí
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer
Quản lý máy laser máy chủ
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Tìm tay cầm của công cụ mà người chơi đang giữ
local function getPlayerToolHandle(player)
local weapon = player.Character:FindFirstChildOfClass("Tool")
if weapon then
return weapon:FindFirstChild("Handle")
end
end
local function isHitValid(playerFired, characterToDamage, hitPosition)
-- Xác minh khoảng cách giữa nhân vật đánh và vị trí đánh
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Kiểm tra xem có bắn qua tường không
local toolHandle = getPlayerToolHandle(playerFired)
if toolHandle then
local rayLength = (hitPosition - toolHandle.Position).Magnitude
local rayDirection = (hitPosition - toolHandle.Position).Unit
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {playerFired.Character}
local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)
-- Nếu một ví dụ bị đánh mà không phải là nhân vật thì bỏ qua phát bắn
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Thông báo tất cả các khách hàng rằng một laser đã bị bắn để họ có thể hiển thị laser
local function playerFiredLaser(playerFired, endPosition)
local toolHandle = getPlayerToolHandle(playerFired)
if toolHandle then
eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
end
end
function damageCharacter(playerFired, characterToDamage, hitPosition)
local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
if humanoid and validShot then
-- Loại bỏ sức khỏe từ nhân vật
humanoid.Health -= LASER_DAMAGE
end
end
-- Kết nối sự kiện với chức năng thích hợp
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Quản lý Laser Khách hàng
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- Hiển thị laser của người chơi khác
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)