Trong hướng dẫn này, bạn sẽ học cách sử dụng một máy laser từ blaster trong Tạo công cụ người chơi và xác định có phải đánh một người chơi không.
Raycasting để tìm va chạm
Raycasting tạo ra một ngọn lửa từ vị trí xuất phát đến hướng định với một chiều dài định nghĩa. Nếu ngọn lửa va chạm với các thể hiện hoặc địa hình trên con đường của nó, nó sẽ trả lại thông tin về cuộc va chạm như vị trí và thể hiện nó va chạm với.
Tìm vị trí chuột
Trước khi một cú laser có thể được bắn, bạn phải đầu tiên biết nơi mà người chơi đang vắm vẻ. Điều này có thể được tìm thấy bằng cách raycasting từ vị trí 2D của người chơi trên màn hình trực tiếp về phía trước của máy tính vào thế giới trò chơi. Ray sẽ va chạ
Mở script ToolController trong công cụ Blaster từ Creating Player Tools . Nếu bạn chưa hoàn thành hướng dẫn từ tutorial này, bạn có thể tải mô hình Blaster và nhúc nhích nó vào StarterPack.
Ở trên cùng của script, tuyên bố một biến tối đa có tên là MAX_MOUSE_DISTANCE với giá trị là 1000 .
Tạo một chức năng 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 các sự kiện đến các chức năng phù hợptool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Sử dụng chức năng nhận địa chỉ máy chủ của người chơi từ UserInputService để lấy vị trí chuột 2D của người chơi trên màn hình. Gắn nó với biến 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 xác định, các thuộc tính X và Y của nó có thể được sử dụng như một trong những biến đổi đối với chức năng Camera:ViewportPointToRay() , tạo ra một 1> Datype
Sử dụng các thuộc tính X và Y của mouseLocation như một lý do cho hành động 1> Class.Camera:ViewportPointToRay()|ViewportPointToRay()1> . Gắn nó vào biến 4> screenToWorldRay4> .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Tạo một cái chỉ 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 ray có đâm vào một thể hay không. Điều này yêu cầu một vị trí xuất phát và hướng vektor: trong ví dụ này, bạn sẽ sử dụng các thuộc tính xuất phát và hướng của 屏
Chiều dài của vektor hướng xác định cách xa của các tia. Tia cần phải dài như MAX_MOUSE_DISTANCE, vì vậy bạn sẽ phải nhân MAX_MOUSE_DISTANCE lên bằng MAX_MOUSE_DISTANCE ».
Tuyên bố một biến tên là directionVector và giao cho nó giá trị screenToWorldRay.Direction nhân với MAX_MOUSE_DISTANCE .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Tạo một cái chỉ từ vị trí chuột 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Vectơ hướng đơn vị của các ray được nhân bởi khoảng cách tối đalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEGọi chức năng Raycast củaWorkspace, truyền thuộc tính Origin của screenToWorldRay như một lý do đầu tiên và 1> numberDirectionVector1> như một lý do thứ hai. Gắn nó vào biến 4> raycastResult4> .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Tạo một cái chỉ từ vị trí chuột 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Vectơ hướng đơn vị của các ray được nhân bởi khoảng cách tối đalocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Raycast từ nguồn của thanh đến hướng của nólocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Thông tin va chạm
Nếu hành động phản xạ ray thấy một thể hiện được ảnh hưởng bởi ray, nó sẽ trả lại một RaycastResult, chứa thông tin về cuộc va chạm giữa ray và thể hiện.
Tính chất RaycastResult | Mô tả |
---|---|
Instancer | Các tế bào BasePart hoặc Terrain mà thanh ray đã chạm vào. |
Vị trí | Nơi mà một giao điểm 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 mặt đất. |
Vật liệu | Vật liệu ở điểm va chạm. |
Bình thường | Vectơ bình thường của khuôn mặt bị ghép. Điều này có thể được sử dụng để xác định hướng mặt đang chỉ. |
Thuộc tính Vị trí sẽ là vị trí của đối tượng mà chuột đang hoàn toàn vượt qua. Nếu chuột không hoàn toàn vượt 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 statement để kiểm tra có phải raycastResult tồn tại không.
Nếu raycastResult có giá trị, trả về giá trị Position của nó.
If raycastResult is nil then find the end of the raycast. Calculating the 3D position of the mouse by adding screenToWorldRay.Origin and directionVector together.
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Tạo một cái chỉ từ vị trí chuột 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Vectơ hướng đơn vị của các ray được nhân bởi khoảng cách tối đa
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast từ nguồn của thanh đến hướng của nó
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Quay điểm 3D vào giao điểm
return raycastResult.Position
else
-- Không có thể hiểu được nên tính vị trí ở cuối của các ray
return screenToWorldRay.Origin + directionVector
end
end
Bắn về phía mục tiêu
Bây giờ khi vị trí chuột 3D đã được xác định, 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 ngọn ray thứ hai có thể được thả ra 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 đổi tên là MAX_LASER_DISTANCE ở đầu trên của script và giao cho nó 500 , hoặc phạm vi bạn chọn cho máy laser.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Tạo một chức năng tên là fireWeapon dưới chức năng getWorldMousePosition .
Gọi getWorldMousePosition và gắn kết quả vào biến tố của tên là mousePosition . Đây sẽ là vị trí mục tiêu cho raycast.
-- Không có thể hiểu được nên tính vị trí ở cuối của các rayreturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Lần này, hướng thuỷ lực cho chức năng phát sóng sẽ đại diện hướng từ vị trí công cụ của người chơi đến vị trí mục tiêu.
Đăng nhập một biến tên là targetDirection và tính toánベクトル hướng bằng cách trừ đi vị trí công cụ từ mouseLocation .
Hoá giải vektor bằng cách sử dụng thuộc tính Unit của nó. Điều này cho nó một kích thước là 1, ceo đó dễ dàng để nhân với một chiều dài sau đó.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Tính toán một vektor hướng bình thường và nhân với khoảng cách laserlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitendĐăng nhập một biến tên là directionVector và giao 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 RaycastParams đối tượng có thể được sử dụng để lưu các tham số bổ sung cho chức năng phát射 hồng ngoại. Nó sẽ được sử dụng trong laser blaster của bạn để đảm bảo rằ
Tiếp tục hàm fireWeapon và tuyên bố biến tùy định tên là weaponRaycastParams . Giao giao diện RaycastParams mới cho nó.
Tạo một bảng có chứa nhân vật local của người chơi và giao nó cho weaponRaycastParams.FilterDescendantsInstances 속性.
Raycast từ vị trí cầm công cụ của người chơi, trong một hướng về phía directionVector . Hãy nhớ để thêm weaponRaycastParams như một引수 để lần này. Gắn điều này vào biến tối đa 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 toán một vektor 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í được nhân bởi một khoảng cách tối đa
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Làm lơ nhân vật của người chơi để ngăn chặn họ tự hại
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 raycast có đã trả lại một giá trị không. Nếu một giá trị được trả lại, một đối tượng đã được đánh bởi cái laser và có thể tạo ra một đối tượng mới giữa súng và vị trí đánh. Nếu không có gì được trả lại, vị trí cuối cùng cầ
Tuyên bố một biến rỗ trong tên hitPosition .
Sử dụng một nếu tuyên bố để kiểm tra có phải weaponRaycastResult có giá trị không. Nếu một đối tượng bị đánh, hãy giao weaponRaycastResult.Position cho 2> hitPosition2> .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Kiểm tra xem có phải đối tượng nào bị đâm giữa vị trí bắt đầu và kết thúc khônglocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendNếu weaponRaycastResult không có giá trị, tính vị trí cuối của cầu cast bằng cách kết hợp vị trí của thanh công cụ với durationVector . Gánh điều này cho directionVector .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Kiểm tra xem có phải đối tượng nào bị đâm giữa vị trí bắt đầu và kết thúc 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 + directionVectorendendNavigate to the toolActivated function and call the fireWeapon function so that the laser fires each time the tool is activated.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
Kiểm tra đối tượng đã đúng
Để tìm kiếm xem obiekt được gây ra bởi laser là một phần của nhân vật của một người chơi hay chỉ là một khu vườn hoa, bạn sẽ cần phải tìm một Humanoid, vì mọi nhân vật đều có một.
Đầu tiên, bạn sẽ cần phải tì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ể đoán rằng cha của đối tượng đá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ấ
Bạn có thể sử dụng FindFirstAncestorOfClass để tìm một mô hình nhân vật của đối tượng đã bị laser đâm, 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
Thêm mã được x Highlight bên dưới của câu lệnh weaponRaycastResult nếu điều kiện đã được đánh dấu để kiểm tra xem một nhân vật đã bị đánh.
-- Kiểm tra xem có phải đối tượng nào bị đâm giữa vị trí bắt đầu và kết thúc khônglocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- Instância đánh sẽ là một con của một mô hình nhân vật-- Nếu một hình người đượ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ờ laser blaster nên in Player hit để cửa sổ ra mỗi lần hoạt động của hành động raycast đến một người chơi khác.
Thử nghiệm với nhiều người chơi
Hai người chơi cần thiết để kiểm tra nếu máy bay phản lực đang tìm kiếm người chơi khác, vì vậy bạn cần phải khởi chạy một máy chủ địa phương.
Chọn Thử nghiệm tab trong Studio.
Đảm bạn chắc chắn rằng dụng cụ thả về '2 Players' 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ái đầ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 chủ, kiểm tra bắn người chơi khác với vũ khí bằng cách nhấp vào họ. "Player hit" nên được hiển thị trong kết quả mỗi lần một người bị bắn.
Bạn có thể tìm hiểu thêm về trang Thử nghiệm ở đây .
Tìm vị trí Laser
Blaster nên tạo ra một ngọn lửa đỏ từ mục tiêu của nó. Chức năng cho điều này sẽ ở bên trong một ModuleScript để có thể được sử dụng trong những script sau đó. Đầu tiên, script sẽ cần phải tìm vị trí mà ngọn lửa laser nên được tạo ra.
Tạo một ModuleScript có tên LaserRender , có cha mẹ là StarterPlayerScripts dưới StarterPlayer.
Mở script và đổi tên bảng mô-đun thành tên của script LaserRender .
Tuyên bố một biến tên là SHOT_DURATION với giá trị là 0.15 . Đây sẽ là thời gian (trong giây) mà laser có thể nhìn thấy.
Tạo một chức năng LaserRender có tên là createLaser với hai tham số là toolHandle và endPosition .
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Thời gian mà laser có thể nhìn thấy-- Tạo một tia laser từ vị trí xuất phát đến vị trí cuối cùngfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererTuyên bố một biến 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 tên là laserDistance và trừ khỏi endPosition từ startPosition để tìm sự khác biệt giữa hai vectơ. Sử dụng thuộc tính 1> Magnitude1> của nó để nhận 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 trung tâm của khu bắt đầu và k
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 bộ phận Laser
Bây giờ bạn biết nơi để tạo một tia laser, bạn cần phải thêm tia 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à giao cho nó một đối tượng mới Part .
Đặt các thuộc tính sau đây của laserPart :
- Kích thước : Vector3.new(0.2, 0.2, laserDistance)
- CFrame : laserCFrame
- Có móc vào : true
- có thể va chạm : false
- Màu : Color3.fromRGB(225, 0, 0) (một màu đỏ mạnh)
- Vật chất : Enum.Material.Neon
Parent laserPart đến Workspace .
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 cơn laser vào dịch vụ Phế tích để bị xóa sổ và được làm sạchDebris:AddItem(laserPart, SHOT_DURATION)end
Bây giờ chức năng để tạo ra tia laser đã hoàn thành, nó có thể được gọi bởi ToolController .
Ở trên cùng của ToolController script, tuyên bố một biến tên là LaserRender và yêu cầu LaserRenderModuleScript được đặt ở PlayerScripts.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentỞ dưới cùng của chức năng fireWeapon, gọi LaserRender createLaser chức năng bằng cách sử dụng cầm công cụ và hitPosition như các引数.
-- 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 laser nên hiển thị giữa vũ khí và chuột khi công cụ được kích hoạt.
Điều khiển tốc độ bắn vũ khí
Vũ khí cần một khoảng thời gian trễ giữa mỗi lần bắn để ngăn chặ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 tra bằng cách kiểm tra xem có đủ thời gian kể từ khi người chơi cuối cùng bắn không.
Tuyên bố một biến ở trên cùng của ToolController được gọi là FIRE_RATE . Đây sẽ là thời gian tối thiểu giữa mỗi lần bắn. Hãy cho nó 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 dưới cùng được gọi là timeOfPreviousShot với giá trị là 0 . This stores the last time the player fired and will be updated with each shot.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Tạo một chức năng 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 kể từ lần bắn trước và trả lại true hoặc false.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Kiểm tra xem đủ thời gian đã trôi kể từ khi bắn màn hình trước đólocal function canShootWeapon()endlocal function getWorldMousePosition()Trong chức năng, tuyên bố một biến tên là currentTime ; giao cho nó kết quả của cuộc gọi tick() . Điều này trả lại thời gian đã trôi qua, bằng giây, kể từ ngày 1st January 1970 (ngày bất kỳ được sử dụng rộ
Giảm timeOfPreviousShot từ currentTime và trả lại false nếu kết quả nhỏ hơn 2> FIRE_RATE2> ; ngược lại, trả lại 5> true5> nếu kết quả lớn hơn 8>FIRE_RATE8>.
-- Kiểm tra xem đủ thời gian đã trôi kể từ khi bắn màn hình trước đólocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendTại cuối của hàm fireWeapon, cập nhật timeOfPreviousShot mỗi lần súng được bắn bằng cách sử dụng tick .
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endTrong hàm toolActivated, tạo một câu if và gọi canShootWeapon để kiểm tra xem có thể bắn vũ khí hay không.
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
Khi bạn thử nghiệm blaster, bạn nên tìm thấy rằng bất kể tốc độ nhanh như thế nào bạn nhấp chuột, vẫn sẽ luôn có một độ trễ ngắn 0,3 giây giữa mỗi lần bắn.
Tổn hại Người chơi
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 về việc phát hành sát thương khi một người chơi bị đánh.
Clients có thể sử dụng một RemoteEvent để cho server biết rằng một nhân vật đã bị đánh. Những thứ này nên được lưu trong ReplicatedStorage , nơi chúng có thể được hiển thị cho cả client và máy chủ.
Tạo một Folder trong ReplicatedStorage có tên Events .
Làm điều đó vào thư mục Sự kiện và đặt tên nó là DamageCharacter .
Trong ToolController , tạo các biến đổi ở đầu của script 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ế "Player hit" tuyên bố in trong fireWeapon với một dòng Lua để kích hoạt sự kiện điều khiển từ xa DamageCharacter với characterModel biến như một đại diện.
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 phải gây sát thương cho người chơi đã bị đánh khi sự kiện được kích hoạt.
Lập Script vào ServerScriptService và đặt tên nó là ServerLaserManager .
Tuyên bố một biến tên là LASER_DAMAGE và đặt nó thành 10 , hoặc giá trị bạn chọn.
Tạo một chức năng tên là damageCharacter với hai tham số tên là playerFired và characterToDamage .
Ở bên trong hàm, tìm Humanoid của nó và trừ LASER_DAMAGE khỏi sức khỏe của nó.
Kết nối hàm damageCharacter với sự kiện DamageCharacter trên thiết bị đầu cuối 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 các sự kiện đến các chức năng phù 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ủ local. Khi bạn bắn người chơi khác, sức khỏe của họ sẽ giảm bằng số được giao cho LASER_DAMAGE .
Tạo các tia laser của người chơi khác
Hiện tại, tia laser được tạo bởi kẻ đang kích hoạt vũ khí, vì vậy chỉ họ mới có thể thấy tia laser.
Nếu mắt 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 vết trễ nhỏ giữa client đang bắn vũ khí và máy chủ nhận được thông tin về vết đó. Điều này có nghĩa là client đang bắn vũ khí sẽ th
Để giải quyết vấn đề này, mỗi khách hàng sẽ tạo ra các tia laser của riêng họ. Điều này có nghĩa là khi khách hàng bắn vũ khí sẽ ngay lập tức nhìn thấy tia laser. Các khách hàng khác sẽ có một vòng tròn nhỏ giữa khi một người chơi khác bắn và một tia
Client của người bắn
Đầu tiên, khách hàng cần phải cho máy chủ biết nó đã bắn một laser và cung cấp vị trí cuối cùng.
Lấy một Sự kiện Remote 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 script ToolController . Khi kết thúc chức năng, bắn sự kiện LaserFired từ xa bằng cách sử dụng 1> hitPosition1> làm một biến.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
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í xuất phát và kết thúc của tia laser để họ cũng có thể render nó.
Trong ServerLaserManager script, create a function named playerFiredLaser above damageCharacter with two parameters called 1> playerFired1> and 4> endPosition4> .
Kết nối hàm này với sự kiện LaserFired tại chỗ.
-- Thông báo cho tất cả các khách hàng rằng một laser đã bắn để họ có thể hiển thị laserlocal function playerFiredLaser(playerFired, endPosition)end-- Kết nối các sự kiện đến các chức năng phù hợpeventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Máy chủ cần vị trí xuất phát của laser. Điều này có thể được gửi từ client, nhưng tốt nhất là tránh tin cậy client nếu có thể. Vị trí của vũ khí lông vũ của nhân vật sẽ là vị trí xuất phát, so máy chủ có thể tìm nó từ đó.
Tạo một hàm getPlayerToolHandle trên hàm playerFiredLaser với một biến động 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 thiết bị tay cầm.
local LASER_DAMAGE = 10-- Tìm màn tay 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 cho tất cả các khách hàng rằng một laser đã bắn để họ có thể hiển thị laserlocal function playerFiredLaser(playerFired, endPosition)
Máy chủ giờ đây có thể gọi FireAllClients trên sự kiện LaserFired từ xa để gửi thông tin cần thiết để render laser cho khách hàng. Điều này bao gồm người chơi người chơi người bắn
Trong chức năng playerFiredLaser, gọi chức năng getPlayerToolHandle với playerFired làm một yếu tố và giao giá trị cho biến 1> toolHandle1> .
Nếu toolHandle đã tồn tại, thì sự kiện LaserFired được tạo cho tất cả các khách hàng sử dụng playerFired , toolHandle và 1> endPosition1> như những lý do.
-- Thông báo cho tất cả các khách hàng rằng một laser đã 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
Điều chỉnh trên Clients
Bây giờ FireAllClients đã được gọi, mỗi client sẽ nhận một sự kiện từ máy chủ để render một tia laser. Mỗi client có thể tái sử dụng modul LaserRender từ trước để render tia laser bằng cách sử dụng vị trí và giá trị đầu cuối củ
Tạo một LocalScript trong StarterPlayerScripts có tên ClientLaserManager .
Trong kịch bản, yêu cầu module LaserReceiver .
Tạo một chức năng tên là createPlayerLaser với các tham số playerWhoShot , toolHandle và 1> endPosition1> .
Kết nối hàm này với sự kiện LaserFired tại thư mục Sự kiện.
Trong hàm, sử dụng một nếu tuyên bố để kiểm tra xem playerWhoShot có không bằng LocalPlayer.
Trong if statement, call the createLaser function from the LaserRender module using toolHandle and endPosition as arguments.
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 một 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 ở các bên khác nhau của màn hình của bạn để bạn có thể thấy cả hai cửa sổ ở một thời điểm. Khi bạn bắn vào một khách hàng, bạn nên thấy laser ở một khách hàng khác.
Hiệu ứng âm thanh
Hiệu ứng âm thanh bắn hiện tại chỉ được chơi trên client đang bắn dự ánile. Bạn sẽ cần phải di chuyển mã để chơi âm thanh để người chơi khác cũng có thể nghe thấy nó.
Trong ToolController script, navigate to the toolActivated function and remove the line which plays the Activate sound.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendỞ dưới cùng của chức năng createLaser trong LaserRender , tuyên bố một biến tối tăm có tên là shootingSound và sử dụng phương thức 1>Class.Instance:FindFirstChild()|FindFirstChild()1> của toolHandle để kiểm tra âm thanh <
Sử dụng một nếu tuyên bố để kiểm tra có phải shootingSound tồn tại; nếu có, hãy gọi hàm chơi của nó.
laserPart.Parent = workspace-- Thêm cơn laser vào dịch vụ Phế tích để bị xóa sổ và được làm sạchDebris:AddItem(laserPart, SHOT_DURATION)-- Chơi âm thanh bắn của vũ khílocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Bảo vệ từ xa bằng cách sử dụng Xác minh
Nếu máy chủ không kiểm tra dữ liệu từ các yêu cầu đang đến, một kẻ tấn công có thể lạm dụng các hàm và sự kiện xa cùng với các giá trị giả mạo để gửi đến máy chủ. Điều quan trọng là sử dụng kiểm tra bên máy chủ để ngăn chặn điều này.
Trong hình dạng hiện tại, sự kiện DamageCharacter từ xa rất dễ bị tấn công. Hackers có thể sử dụng sự kiện này để tấn công bất kỳ người chơi nào mà họ muốn trong trò chơi mà không bắn họ.
Hợp nhận là quá trình kiểm tra rằng giá trị được gửi đến máy chủ là thực tế. Trong trường hợp này, máy chủ sẽ cần phải:
- Kiểm tra xem khoảng cách giữa người chơi và vị trí được đánh bởi laser có trong một giới hạn nhất định không.
- Raycast giữa vũ khí bắn laser và vị trí đúng để đảm bảo rằng cú đá có thể thông qua tất cả các bức tường.
Khách hàng
Client cần gửi vị trí của máy chủ bị ảnh hưởng bởi cú điều khiển để kiểm tra khoảng cách là thực tế.
Trong ToolController , di chuyển đến dòng nơi sự kiện DamageCharacter được kích hoạt trong chức năng fireWeapon .
Thêm hitPosition như một lý do.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Máy Chủ
Client đang bây giờ gửi thêm các tham số qua sự kiện remote DamageCharacter, vì vậy ServerLaserManager cần được điều chỉnh để chấp nhận nó.
Trong ServerLaserManager script, add a hitPosition parameter to the damageCharacter function.
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_DAMAGEendendDưới chức năng getPlayerToolHandle, tạo một chức năng tên là isHitValid với ba tham số: playerFired , 1> characterToDamage1> và 4> hitPosition4> .
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
Cuộc kiểm tra đầu tiên sẽ là khoảng cách giữa vị trí đập và nhân vật đập.
Tuyên bố một biến tên là MAX_HIT_PROXIMITY ở đầu của script và giao cho nó một giá trị là 10 . Đây sẽ là khoảng cách tối đa cho phép giữa hit và nhân vật. Một dung dịch cần thiết bởi vì nhân vật có thể đã di chuyển một chút kể từ khi client kích ho
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10Trong hàm isHitValid, tính khoảng cách giữa nhân vật và vị trí đúng. Nếu khoảng cách lớn hơn MAX_HIT_PROXIMITY thì trả về false .
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Xác định khoảng cách giữa vị trí đánh của nhân vật và vị trí đánhlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
Bước kiểm tra thứ hai sẽ implément một raycast giữa vị trí đã bắn và vị trí đúng. Nếu raycast trả về một thể hiện không phải là nhân vật, bạn có thể cho rằng đòn đã không hợp lệ vì có thứ gì đó chặn đòn.
Bạn dán mã dưới đây để thực hiện kiểm tra này. Trả lại true tại cuối hàm: nếu nó đạt đến kết thúc, tất cả các kiểm tra đã được thông qua.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Xác định khoảng cách giữa vị trí đánh của nhân vật và vị trí đánhlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Xem xét việc bắn qua bức tườ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 instace đã bị đánh nhưng không phải là nhân vật, thì bỏ qua cú đáif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendTuyên bố một biến trong chức năng damageCharacter được gọi là validShot . Giao kết quả của một cuộc gọi đến chức năng isHitValid với ba引수: 1> playerFired1> , 4> characterToDamage4> v
Trong câu if dưới đây, thêm một và operator để kiểm tra xem validShot đã là true .
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 xa lạRemote DamageCharacter là an toàn hơn và sẽ ngăn chặn hầu hết người chơi khỏi việc lạm dụng nó. Ghi nhớ rằng một số người chơi xấu tính sẽ thường tìm cách xung quanh sự xác minh; giữ các sự kiện xa lạ an toàn là một nỗ lực liên tục.
Laser của bạn bây giờ đã hoàn thành, với một hệ thống phát hiện người dùng cơ bản sử dụng raycasting. Hãy thử hướng dẫn Phát hiện người dùng輸入 để tìm hiểu cách bạn có thể thêm một hành động nạp đạn vào laser của bạn, hoặc tạo một bản đồ trò chơ
Mã cuối cùng
Công cụ điều khiể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
local FIRE_RATE = 0.3
local timeOfPreviousShot = 0
-- Kiểm tra xem đủ thời gian đã trôi kể từ khi bắn màn hình 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 cái chỉ từ vị trí chuột 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Vectơ hướng đơn vị của các ray được nhân bởi khoảng cách tối đa
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast từ nguồn gốc của roy đến hướng nó
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Quay điểm 3D vào giao điểm
return raycastResult.Position
else
-- Không có thể hiểu được nên tính vị trí ở cuối của các ray
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Tính toán một vektor 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
-- Làm lơ nhân vật của người chơi để ngăn chặn họ tự hại
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Kiểm tra xem có phải đối tượng nào bị đâm giữa vị trí bắt đầu và kết thúc không
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- Instância đánh sẽ là một con của một mô hình nhân vật
-- Nếu một hình người đượ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)
Máy Laser
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Thời gian mà laser có thể nhìn thấy
-- Tạo một tia laser từ vị trí xuất phát đế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 cơn laser vào dịch vụ Phế tích để bị xóa sổ và được làm sạch
Debris:AddItem(laserPart, SHOT_DURATION)
-- Chơi âm thanh bắn của vũ khí
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer
Quản lý Laser
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Tìm màn tay 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 định khoảng cách giữa vị trí đánh của nhân vật và vị trí đánh
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Xem xét việc bắn qua bức tườ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 instace đã bị đánh nhưng không phải là nhân vật, thì bỏ qua cú đá
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Thông báo cho tất cả các khách hàng rằng một laser đã 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 các sự kiện đến các chức năng phù 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 một 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)