Phát hiện va chạm với laser

*Nội dung này được dịch bằng AI (Beta) và có thể có lỗi. Để xem trang này bằng tiếng Anh, hãy nhấp vào đây.

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.

Phát tia từ A sang B va chạm với tường

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.

  1. 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.

  2. Ở đầ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 .

  3. Tạo một chức năng có tên là getWorldMousePosition.


    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end
    local function toolActivated()
    tool.Handle.Activate:Play()
    end
    -- Kết nối sự kiện với chức năng thích hợp
    tool.Equipped:Connect(toolEquipped)
    tool.Activated:Connect(toolActivated)
  4. 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.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    end

Bây giờ vị trí chuột 2D đã được biết, các thuộc tính XY 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.

  1. Sử dụng các thuộc tính XY 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 2D
    local 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 .

  1. 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 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
  2. Gọ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 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)

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 RaycastResultMô 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ệuVật liệu tại điểm va chạm.
Bình thườngVectơ 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 .

  1. Tạo một if tuyên bố để kiểm tra xem raycastResult có tồn tại hay không.

  2. Nếu raycastResult có giá trị, trả lại thuộc tính Vị trí của nó.

  3. Nếu raycastResultnil 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.OrigindirectionVector 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 .

  1. 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.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  2. Tạo một chức năng có tên là fireWeapon dưới chức năng getWorldMousePosition.

  3. 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 tia
    return screenToWorldRay.Origin + directionVector
    end
    end
    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    end
    local 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.

  1. 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.

  2. 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 laser
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. Tuyê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 đa
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

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

  1. 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ó.

  2. 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 đó.

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

  1. Tuyên bố một biến trống có tên là hitPosition .

  2. 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ông
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. Nế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ông
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    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
    end
  4. Di 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.

  1. 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ô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
    print("Player hit")
    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

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.

  1. Chọn tab Thử nghiệm trong Studio.

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

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

  1. Tạo một ModuleScript có tên LaserRenderer , thuộc về StarterPlayerScripts dưới StarterPlayer.

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

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

  4. Tạo một chức năng của LaserRenderer có tên createLaser với hai tham số được gọi là toolHandleendPosition .


    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ùng
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. Tuyê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.

  6. 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.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    end
  7. Tuyê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.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    local 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.

  1. Tuyên bố một biến laserPart và gán cho nó một ví dụ / trường hợpmới Part.

  2. Đặt các thuộc tính sau của laserPart :

    1. Kích thước : Vector3.new(0.2, 0.2, khoảng cách laser)
    2. Khung C : laserCFrame
    3. Được neo : true
    4. Có thể va chạm : false
    5. Màu : Color3.fromRGB(225, 0, 0) (một màu đỏ mạnh)
    6. Vật liệu : Enum.Material.Neon
  3. cha laserPart đến Không gian làm việc .

  4. 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.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(225, 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)
    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 .

  1. Ở đầ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
  2. Ở đá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 đa
    hitPosition = tool.Handle.Position + directionVector
    end
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  3. Thử 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.

  1. 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.

  2. 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 = 1000
    local MAX_LASER_DISTANCE = 300
    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
  3. Tạ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.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()
    end
    local function getWorldMousePosition()
  4. 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).

  5. 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ước
    local function canShootWeapon()
    local currentTime = tick()
    if currentTime - timeOfPreviousShot < FIRE_RATE then
    return false
    end
    return true
    end
  6. Tạ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 + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. Bê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() then
    tool.Handle.Activate:Play()
    fireWeapon()
    end
    end

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ủ.

  1. Tạo một Thư mục trong ReplicatedStorage có tên là Sự kiện .

  2. Thêm một sự kiện từ xa vào thư mục Sự kiện và đặt tên nó là DamageCharacter .

  3. 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.Parent
    local eventsFolder = ReplicatedStorage.Events
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  4. Thay 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 then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    eventsFolder.DamageCharacter:FireServer(characterModel)
    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

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.

  1. Thêm Tập lệnh vào ServerScriptService và đặt tên nó là ServerLaserManager .

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

  3. Tạo một chức năng có tên là damageCharacter với hai tham số được gọi là playerFiredcharacterToDamage .

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

  5. 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.Events
    local LASER_DAMAGE = 10
    function damageCharacter(playerFired, characterToDamage)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid 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)
  6. 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.

  1. 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 .

  2. 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 + directionVector
    end
    timeOfPreviousShot = 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ó.

  1. 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à playerFiredendPosition.

  2. 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ị laser
    local function playerFiredLaser(playerFired, endPosition)
    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)

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ừ đó.

  1. 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 .

  2. 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 then
    return weapon:FindFirstChild("Handle")
    end
    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)

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.

  1. 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 .

  2. 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 , toolHandleendPosition 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ị laser
    local function playerFiredLaser(playerFired, endPosition)
    local toolHandle = getPlayerToolHandle(playerFired)
    if toolHandle then
    eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
    end
    end

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.

  1. Tạo một LocalScript trong StarterPlayerScripts có tên là ClientLaserManager .

  2. Bên trong kịch bản, yêu cầu mô-đun LaserRenderer .

  3. Tạo một chức năng có tên createPlayerLaser với các tham số playerWhoShot , toolHandleendPosition .

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

  5. 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.

  6. 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 toolHandleendPosition 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ác
    local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
    if playerWhoShot ~= Players.LocalPlayer then
    LaserRenderer.createLaser(toolHandle, endPosition)
    end
    end
    eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
  7. 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ó.

  1. 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() then
    fireWeapon()
    end
    end
  2. Ở đáy của chức năng createLaserLaserRenderer , 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 .

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

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.

  1. 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.

  2. Thêm hitPosition như một argument.


    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    eventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)
    end
    end

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ó.

  1. 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ật
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. Bê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, characterToDamagehitPosition .


    end
    local 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.

  1. 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.Events
    local LASER_DAMAGE = 10
    local MAX_HIT_PROXIMITY = 10
  2. Trong 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í đánh
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > MAX_HIT_PROXIMITY then
    return false
    end
    end

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.

  1. 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í đánh
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 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
  2. Tuyê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 , characterToDamagehitPosition .

  3. Trong phát biểu dưới đây, thêm một 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ật
    humanoid.Health -= LASER_DAMAGE
    end
    end

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)