Wykrywanie uderzeń za pomocą laserów

*Ta zawartość została przetłumaczona przy użyciu narzędzi AI (w wersji beta) i może zawierać błędy. Aby wyświetlić tę stronę w języku angielskim, kliknij tutaj.

W tym samouczku nauczysz się, jak wystrzelić laser z blastera w Twórz narzędzia gracza i sprawdzić, czy trafi w gracza, czy nie.

Rzucanie promieni, aby znaleźć kolizje

Rzucanie promieni tworzy niewidzialny promień z pozycji startowej w kierunku określonego kierunku z określoną długością.Jeśli promień zderzy się z obiektami lub terenem na swojej ścieżce, zwróci informacje o kolizji, takie jak pozycja i obiekt, z którym się zderzył.

Rzut promieni z A w kierunku B, który zderza się ze ścianą

Znajdź lokalizację myszy

Zanim można strzelić laserem, musisz najpierw wiedzieć, gdzie gracz celuje.Można to znaleźć poprzez rzucanie promieni z lokalizacji myszy 2D gracza na ekranie bezpośrednio od kamery do świata gry.Promień zderzy się z tym, na co gracz celuje myszką.

  1. Otwórz skrypt Pasek narzędzi kontrolera w narzędziu Blaster z Twórz narzędzia gracza.Jeśli jeszcze nie ukończyłeś tego samouczka, możesz pobrać model Blaster i wstawić go do StarterPack.

  2. Na górze skryptu zadeklaruj stałą o nazwie MAX_MOUSE_DISTANCE z wartością 1000 .

  3. Stwórz funkcję o nazwie 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
    -- Połącz wydarzenia z odpowiednimi funkcjami
    tool.Equipped:Connect(toolEquipped)
    tool.Activated:Connect(toolActivated)
  4. Użyj funkcji GetMouseLocation of UserInputService , aby uzyskać lokalizację myszy 2D gracza na ekranie.Przydziel to zmiennej o nazwie miejscem myszy .


    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    end

Teraz lokalizacja myszy 2D jest znana, jej właściwości X i Y mogą być używane jako parametry funkcji >, która tworzy z ekranu do świata gry 3D.

  1. Użyj właściwości X i Y z mouseLocation jako argumentów dla funkcji ViewportPointToRay().Przydziel to zmiennej o nazwie screenToWorldRay .


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Stwórz promień z lokalizacji myszy 2D
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    end

Nadszedł czas, aby użyć funkcji Raycast sprawdzić, czy promień uderza w obiekt.Wymaga to pozycji startowej i wektora kierunkowego: w tym przykładzie użyjesz właściwości pochodzenia i kierunku screenToWorldRay .

Długość wektora kierunkowego określa, jak daleko promień się rozwinie.Promień musi być tak długi, jak MAX_MOUSE_DISTANCE , więc musisz pomnożyć wektor kierunkowy przez MAX_MOUSE_DISTANCE .

  1. Oświadcz zmienną o nazwie directionVector i przypisz jej wartość screenToWorldRay.Direction pomnożoną przez MAX_MOUSE_DISTANCE.


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Stwórz promień z położenia myszy 2D
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Wektor kierunku jednostki promienia pomnożony przez maksymalną odległość
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
  2. Wezwij funkcję Raycast w przestrzeni roboczej, przekazując właściwość Pochodzenie z screenToWorldRay jako pierwszy argument i directionVector jako drugi.Przydziel to zmiennej o nazwie raycastResult .


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- Stwórz promień z położenia myszy 2D
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Wektor kierunku jednostki promienia pomnożony przez maksymalną odległość
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
    -- Rzut promienia z pochodzenia promienia w kierunku jego kierunku
    local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)

Informacje o kolizji

Jeśli operacja raycast znajdzie obiekt uderzony przez promień, zwróci RaycastResult, który zawiera informacje o kolizji między promieniem a obiektem.

Właściwość RaycastResultOpis
InstancjaKomórka BasePart lub Terrain, w której promień się skrzyżował.
PozycjaGdzie wystąpiła intersekcja; zwykle punkt bezpośrednio na powierzchni części lub terenu.
MateriałMateriał na punkcie kolizji.
NormalnyNormalny wektor zbieżającej się twarzy. Można go użyć do określenia, w którym kierunku wskazuje twarz.

Właściwość Pozycja będzie pozycją obiektu, nad którym mysz się chowa.Jeśli mysz nie jest nad jakimkolwiek obiektem w odległości MAX_MOUSE_DISTANCE, raycastResult będzie nil.

  1. Stwórz oświadczenie if, aby sprawdzić, czy istnieje raycastResult.

  2. Jeśli raycastResult ma wartość, zwróć jego właściwość Pozycja .

  3. Jeśli raycastResult jest nil, znajdź koniec promieniowania.Oblicz pozycję 3D myszy, dodając screenToWorldRay.Origin i directionVector razem.


local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Stwórz promień z położenia myszy 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Wektor kierunku jednostki promienia pomnożony przez maksymalną odległość
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Rzut promienia z pochodzenia promienia w kierunku jego kierunku
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Zwróć punkt styku 3D
return raycastResult.Position
else
-- Nie uderzono w żaden obiekt, więc oblicz pozycję na końcu promienia
return screenToWorldRay.Origin + directionVector
end
end

Ogień w kierunku celu

Teraz, gdy pozycja myszy 3D jest znana, można ją użyć jako pozycję docelową , aby wystrzelić laser w kierunku.Drugi promień można wystrzelić między bronią gracza a pozycją docelową za pomocą funkcji Rzucanie promieni .

  1. Oświadcz stałą o nazwie MAX_LASER_DISTANCE na górze skryptu i przypisz ją do 500 lub wybranego zakresu dla lasera blastera.


    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  2. Stwórz funkcję o nazwie fireWeapon pod funkcją getWorldMousePosition.

  3. Wezwij getWorldMousePosition i przypisz wynik do zmiennej o nazwie pozycja myszy . Będzie to pozycja docelowa dla raycastu.


    -- Nie uderzono w żaden obiekt, więc oblicz pozycję na końcu promienia
    return screenToWorldRay.Origin + directionVector
    end
    end
    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end

Tym razem wektor kierunkowy dla funkcji raycast reprezentuje kierunek od pozycji narzędzia gracza do pozycji docelowej.

  1. Oświadcz zmienną o nazwie targetDirection i oblicz wektor kierunkowy odejmując pozycję narzędzia z mouseLocation.

  2. Normalizuj wektor za pomocą jego właściwości Jednostka . Daje to mu wielkość 1, co ułatwia pomnożenie go później przez długość.


    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    -- Oblicz normalizowany wektor kierunkowy i pomnóż przez odległość lasera
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. Oświadcz zmienną o nazwie directionVector i przypisz do niej targetDirection pomnożoną przez MAX_LASER_DISTANCE.


    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    -- Kierunek strzelania broni, pomnożony przez maksymalną odległość
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

Przedmiotem RaycastParams można użyć do przechowywania dodatkowych parametrów dla funkcji raycast.Zostanie użyty w twoim laserowym blasterze, aby upewnić się, że promieniowanie nie uderzy przypadkowo w gracza strzelającego bronią.Każda część zawarta w właściwości obiektu RaycastParams zostanie pominięta w raycast.

  1. Kontynuuj funkcję fireWeapon i zadeklaruj zmienną o nazwie weaponRaycastParams . Przydziel nowy obiekt RaycastParams do niej.

  2. Utwórz tabelę zawierającą lokalną postać gracza i przypisz ją do właściwości weaponRaycastParams.FilterDescendantsInstances.

  3. Rzut z pozycji uchwytu narzędzia gracza w kierunku directionVector .Pamiętaj, aby dodać weaponRaycastParams jako argument tym razem.Przydziel to zmiennej o nazwie wynik strzelania bronią .


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()
-- Oblicz normalizowany wektor kierunkowy i pomnóż przez odległość lasera
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Kierunek strzelania broni pomnożony przez maksymalną odległość
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignoruj postać gracza, aby uniemożliwić mu zranienie siebie
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end

Wreszcie musisz sprawdzić, czy operacja raycast zwróciła wartość.Jeśli wartość zostanie zwrócona, obiekt został uderzony przez promień, a można stworzyć laser między bronią a miejscem trafienia.Jeśli nic nie zostało zwrócone, ostateczna pozycja musi zostać obliczona, aby stworzyć laser.

  1. Oświadcz pustą zmienną o nazwie hitPosition .

  2. Użyj if oświadczenia, aby sprawdzić, czy weaponRaycastResult ma wartość. Jeśli obiekt został uderzony, przypisz weaponRaycastResult.Position do hitPosition.


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Sprawdź, czy któreś obiekty zostały uderzone między pozycją początkową a końcową
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. Jeśli weaponRaycastResult nie ma wartości, oblicz pozycję końcową promienia poprzez dodanie razem pozycji **** ręczki narzędzia z pozycją directionVector.Przydziel to do pozycji uderzenia .


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Sprawdź, czy któreś obiekty zostały uderzone między pozycją początkową a końcową
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    else
    -- Oblicz pozycję końcową na podstawie maksymalnej odległości lasera
    hitPosition = tool.Handle.Position + directionVector
    end
    end
  4. Przejdź do funkcji toolActivated i wezwij funkcję fireWeapon, aby laser strzelał za każdym razem, gdy narzędzie jest aktywowane.


    local function toolActivated()
    tool.Handle.Activate:Play()
    fireWeapon()
    end

Sprawdź trafienie obiektu

Aby znaleźć, czy obiekt uderzony przez laser jest częścią postaci gracza lub tylko kawałkiem scenerii, będziesz musiał poszukać Humanoid, ponieważ każda postać ma jedną.

Najpierw musisz znaleźć model postaci .Jeśli część postaci została uderzona, nie można założyć, że rodzic obiektu uderzonego będzie postacią.Laser mógł uderzyć w część ciała, akcesorium lub narzędzie, które znajdują się w różnych częściach hierarchii postaci.

Możesz użyć FindFirstAncestorOfClass, aby znaleźć przodka modelu postaci obiektu uderzonego przez laser, jeśli istnieje.Jeśli znajdziesz model i zawiera on ludzika, w większości przypadków możesz założyć, że jest to postać.

  1. Dodaj wyświetlony kod poniżej do oświadczenia weaponRaycastResult if , aby sprawdzić, czy znak został uderzony.


    -- Sprawdź, czy któreś obiekty zostały uderzone między pozycją początkową a końcową
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    -- Uderzenie instancji będzie dzieckiem modelu postaci
    -- Jeśli znaleziono humanoida w modelu, to prawdopodobnie jest to postać gracza
    local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    print("Player hit")
    end
    end
    else
    -- Oblicz pozycję końcową na podstawie maksymalnej odległości lasera
    hitPosition = tool.Handle.Position + directionVector
    end

Teraz laserowy blaster powinien wydrukować Player hit do okna wyjściowego za każdym razem, gdy operacja rzucania promienia trafi na innego gracza.

Testuj z wieloma graczami

Potrzebne są dwie osoby, aby przetestować, czy promień broni znajduje innych graczy, więc musisz uruchomić lokalny serwer.

  1. Wybierz zakładkę Test w Studio.

  2. Upewnij się, że menu rozwijane graczy jest ustawione na "2 graczy" i kliknij przycisk Początek, aby uruchomić lokalny serwer z 2 klientami.Pojawi się trzy okna.Pierwsze okno będzie lokalnym serwerem, pozostałe okna będą klientami dla Player1 i Player2.

  3. Na jednym klientzie przetestuj strzelanie do drugiego gracza bronią, klikając na niego.„Uderzenie gracza” powinno być wyświetlane w wyniku za każdym razem, gdy gracz jest strzelany.

Możesz dowiedzieć się więcej o zakładce Test tutaj .

Znajdź pozycję lasera

Blaster powinien wystrzelić czerwony promień światła na swój cel.Funkcja dla tego będzie znajdować się w ModuleScript więc można ją ponownie użyć w innych skryptach później.Najpierw skrypt musi znaleźć pozycję, w której powinien być renderowany promień laserowy.

  1. Stwórz ModuleScript o nazwie LaserRenderer , przypisane do StarterPlayerScripts pod StarterPlayer.

  2. Otwórz skrypt i zmień nazwę tabeli modułu na nazwę skryptu LaserRenderer .

  3. Oświadcz zmienną o nazwie czas trwania strzału z wartością 0.15 .Będzie to ilość czasu (w sekundach), przez który laser jest widoczny.

  4. Stwórz funkcję LaserRenderer o nazwie createLaser z dwoma parametrami o nazwie toolHandle i endPosition .


    local LaserRenderer = {}
    local SHOT_DURATION = 0.15 -- Czas, przez który laser jest widoczny
    -- Stwórz promień laserowy z pozycji startowej w kierunku pozycji końcowej
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. Oświadcz zmienną o nazwie startPosition i ustaw właściwość pozycja z toolHandle jako jej wartość.Będzie to pozycja lasera blastera gracza.

  6. Oświadcz zmienną o nazwie dystans laserowy i odejmij endPosition z startPosition , aby znaleźć różnicę między dwoma wektorami.Użyj właściwości Wielkość tej w celu uzyskania długości promienia lasera.


    function LaserRenderer.createLaser(toolHandle, endPosition)
    local startPosition = toolHandle.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    end
  7. Oświadcz zmienną laserCFrame , aby przechować pozycję i orientację promienia lasera.Pozycja musi być punktem środkowym początku i końca promienia.Użyj CFrame.lookAt , aby utworzyć nowy CFrame, położony w startPosition i wychodzący w kierunku endPosition.Mnoż to przez nowy CFrame z wartością osi Z wynoszącą pół negatywnego laserDistance , aby uzyskać punkt środkowy.


    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

Stwórz część laserową

Teraz, gdy wiesz, gdzie utworzyć promień laserowy, musisz dodać sam promień. Można to łatwo zrobić za pomocą części Neon.

  1. Oświadcz zmienną laserPart i przypisz do niej nową instancjaPart.

  2. Ustaw następujące właściwości laserPart :

    1. Rozmiar : Vector3.new(0.2, 0.2, dystans laserowy)
    2. Ramka C : laserCFrame
    3. Zakotwiczone : prawda
    4. Możliwość kolizji : fałsz
    5. Kolor : Color3.fromRGB(225, 0, 0) (mocny czerwony kolor)
    6. Materiał : Enum.Material.Neon
  3. Rodzic laserPart do Przestrzeni roboczej .

  4. Dodaj część do usługi Debris, aby została usunięta po upływie liczby sekund w zmiennej 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
    -- Dodaj promień laserowy do usługi Debris, która ma zostać usunięta i oczyszczona
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

Teraz funkcja renderowania promienia laserowego jest kompletna, można ją wywołać za pomocą sterownika narzędzi .

  1. Na górze skryptu Kontroler narzędzi , oświadcz o zmiennej o nazwie LaserRenderer i wymagaj modułu LaserRenderer ModuleScript znajdującego się w PlayerScripts.


    local UserInputService = game:GetService("UserInputService")
    local Players = game:GetService("Players")
    local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
    local tool = script.Parent
  2. Na dole funkcji fireWeapon funkcja LaserRenderer wezwij funkcję createLaser używając uchwytu narzędzia i hitPosition jako argumentów.


    -- Oblicz pozycję końcową na podstawie maksymalnej odległości lasera
    hitPosition = tool.Handle.Position + directionVector
    end
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  3. Przetestuj broń, klikając przycisk Odtwarzania. Laser powinien być widoczny między bronią a myszką, gdy narzędzie jest aktywowane.

Kontroluj oceniaćognia broni

Bronie potrzebują opóźnienia między każdym strzałem, aby uniemożliwić graczom zadawanie zbyt dużych obrażeń w krótkim czasie.Można to kontrolować, sprawdzając, czy minęło wystarczająco dużo czasu od ostatniego wystrzelania gracza.

  1. Oświadcz zmienną na górze ToolController nazywaną FIRE_RATE .Będzie to minimalny czas między każdym strzałem.Daj mu wartość według twojego wyboru; ten przykład używa 0,3 sekund.

  2. Oświadcz inną zmienną poniżej o nazwie czasPoprzedniegoStrzału z wartością 0 .Przechowuje ostatni raz, kiedy gracz strzelił i zostanie zaktualizowany za każdym strzałem.


    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 300
    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
  3. Stwórz funkcję o nazwie można strzelać z bronią bez parametrów.Funkcja ta sprawdzi, ile czasu upłynęło od poprzedniego strzału i zwróci prawdę lub fałsz.


    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
    -- Sprawdź, czy minęło wystarczająco dużo czasu od ostatniego wystrzelenia
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. Wewnątrz funkcji oświadcz zmienną o nazwie czas obecny ; przypisz do niej wynik wezwania funkcji tick().Wyświetla to, ile czasu upłynęło, w sekundach, od 1 stycznia 1970 r. (losowana data powszechnie używana do obliczania czasu).

  5. Odejmij timeOfPreviousShot od currentTime i zwróć fałsz , jeśli wynik jest mniejszy niż FIRE_RATE ; w przeciwnym razie zwróć prawdę .


    -- Sprawdź, czy minęło wystarczająco dużo czasu od ostatniego wystrzelenia
    local function canShootWeapon()
    local currentTime = tick()
    if currentTime - timeOfPreviousShot < FIRE_RATE then
    return false
    end
    return true
    end
  6. Na końcu funkcji fireWeapon aktualizuj timeOfPreviousShot za każdym razem, gdy broń jest strzelana za pomocą tick .


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. W funkcji toolActivated stwórz oświadczenie jeśli i wezwij canShootWeapon, aby sprawdzić, czy broń może być wystrzelona.


    local function toolActivated()
    if canShootWeapon() then
    tool.Handle.Activate:Play()
    fireWeapon()
    end
    end

Podczas testowania blastera powinieneś znaleźć, że bez względu na to, jak szybko klikać, zawsze będzie krótka 0,3-sekundowa zwłoka między każdym strzałem.

Obraź gracza

Klienci nie mogą bezpośrednio zaszkodzić innym klientom; serwer musi być odpowiedzialny za wypłacanie obrażeń, gdy gracz jest trafiony.

Klienci mogą użyć RemoteEvent, aby powiedzieć serwerowi, że znak został uderzony.Powinny one być przechowywane w ReplicatedStorage , gdzie są widoczne zarówno dla klienta, jak i serwera.

  1. Utwórz Katalog w ReplicatedStorage o nazwie Wydarzenia .

  2. Włóż zdarzenie zdalne do folderu Wydarzenia i nazwij je Uszkodzonym postacią .

  3. W narzędziu kontrolera , utwórz zmienne na początku skryptu dla ReplicatedStorage i katalogu Wydarzeń.


    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. Zastąp oświadczenie druku "Player hit" w fireWeapon linią Luau, aby uruchomić zdarzenie zdalne DamageCharacter z zmienną characterModel jako argumentem.


    local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    eventsFolder.DamageCharacter:FireServer(characterModel)
    end
    end
    else
    -- Oblicz pozycję końcową na podstawie maksymalnej odległości lasera
    hitPosition = tool.Handle.Position + directionVector
    end

Serwer musi zadać obrażenia graczowi, który został trafiony, gdy wydarzenie zostało uruchomione.

  1. Wprowadź skrypt do ServerScriptService i nazwij go Serwerowym menadżerem laserów .

  2. Zadeklaruj zmienną o nazwie LASER_DAMAGE i ustaw ją na 10 lub wartość według wyboru.

  3. Stwórz funkcję o nazwie damageCharacter z dwoma parametrami o nazwie playerFired i characterToDamage .

  4. W funkcji znajdź humanoid postaci i odłącz LASER_DAMAGE od jej zdrowia.

  5. Połącz funkcję damageCharacter z wydarzeniem zdalnym Uszkodzenie postaci w folderze Wydarzenia.


    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
    -- Usuń zdrowie z postaci
    humanoid.Health -= LASER_DAMAGE
    end
    end
    -- Połącz wydarzenia z odpowiednimi funkcjami
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. Przetestuj blaster z 2 graczami, uruchamiając lokalny serwer.Kiedy strzelisz do drugiego gracza, jego zdrowie zmniejszy się o liczbę przypisaną do LASER_DAMAGE.

Renderuj promienie laserowe innego gracza

Obecnie promień laserowy jest tworzony przez klienta strzelającego bronią, więc tylko oni będą mogli zobaczyć promień laserowy.

Jeśli promień laserowy został stworzony na serwerze, wszyscy będą mogli go zobaczyć.Jednakże, byłoby niewielkie opóźnienie między klientem strzelającym bronią a serwerem otrzymującym informacje o strzałach.Oznaczałoby to, że klient strzelający bronią zobaczy opóźnienie między tym, kiedy aktywuje broń, a kiedy widzi promień laserowy; broń poczuje się laguje w wynik.

Aby rozwiązać ten problem, każdy klient stworzy własne promienie laserowe.Oznacza to, że klient strzelający bronią natychmiast zobaczy promień laserowy.Inni klienci doświadczą niewielkiego opóźnienia między momentem, w którym inny gracz strzela, a pojawieniem się promienia.To jest najlepszy scenariusz: nie ma możliwości szybszego komunikowania lasera jednego klienta do innych klientów.

Klient strzelca

Najpierw klient musi powiedzieć serwerowi, że wystrzelił laser i podać pozycję końcową.

  1. Włóż Wydarzenie zdalne do folderu Wydarzenia w ReplicatedStorage i nazwij je LaserFired .

  2. Zlokalizuj funkcję fireWeapon w skrypcie Kontroler narzędzi .Na końcu funkcji uruchom zdarzenie zdalne LaserFired za pomocą hitPosition jako argument.


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    eventsFolder.LaserFired:FireServer(hitPosition)
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end

Serwer

Serwer musi teraz otrzymać wydarzenie, które klient wystrzelił, i powiadomić wszystkie klienty o pozycji startu i końca promienia lasera, aby mogły go również renderować.

  1. W skrypcie ServerLaserManager stwórz funkcję o nazwie playerFiredLaser powyżej damageCharacter z dwoma parametrami o nazwie playerFired i endPosition.

  2. Połącz funkcję z wydarzeniem zdalnym LaserFired .


    -- Poinformuj wszystkich klientów, że wysłano laser, aby mogli wyświetlić laser
    local function playerFiredLaser(playerFired, endPosition)
    end

    -- Połącz wydarzenia z odpowiednimi funkcjami
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
    eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

Serwer potrzebuje pozycji startowej lasera.Można to wysłać z klienta, ale najlepiej unikać zaufania klientowi w miarę możliwości.Pozycja uchwytu broni postaci będzie pozycją startową, więc serwer może ją tam znaleźć.

  1. Stwórz funkcję getPlayerToolHandle powyżej funkcji playerFiredLaser z parametrem o nazwie player.

  2. Użyj następującego kodu, aby wyszukać postać gracza dla broni i zwrócić obiekt uchwytu.


    local LASER_DAMAGE = 10
    -- Znajdź uchwyt narzędzia, które trzyma gracz
    local function getPlayerToolHandle(player)
    local weapon = player.Character:FindFirstChildOfClass("Tool")
    if weapon then
    return weapon:FindFirstChild("Handle")
    end
    end
    -- Poinformuj wszystkich klientów, że wysłano laser, aby mogli wyświetlić laser
    local function playerFiredLaser(playerFired, endPosition)

Serwer może teraz wezwać FireAllClients na zdarzenie zdalne LaserFired , aby wysłać informacje potrzebne do renderowania lasera do klientów.Obejmuje to gracza który wystrzelił laser (tak więc klient dla tego gracza nie renderuje lasera dwa razy), uchwyt blastera (który działa jako pozycja startowa dla lasera) i pozycję końcową lasera.

  1. W funkcji playerFiredLaser wezwij funkcję getPlayerToolHandle z playerFired jako argumentem i przypisz wartość do zmiennej o nazwie toolHandle .

  2. Jeśli istnieje narzędzie toolHandle , uruchom wydarzenie LaserFired dla wszystkich klientów używających playerFired, toolHandle i endPosition jako argumentów.


    -- Poinformuj wszystkich klientów, że wysłano laser, aby mogli wyświetlić laser
    local function playerFiredLaser(playerFired, endPosition)
    local toolHandle = getPlayerToolHandle(playerFired)
    if toolHandle then
    eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
    end
    end

Renderowanie na klientach

Teraz FireAllClients został wezwany, każdy klient otrzyma wydarzenie z serwera, aby wyrenderować promień laserowy.Każdy klient może ponownie użyć modułu LaserRenderer z wcześniejszej wersji, aby wyrenderować promień laserowy za pomocą pozycji uchwytu i wartości końcowego położenia wysłanych przez serwer.Gracz, który wystrzelił promień laserowy po raz pierwszy, powinien ignorować to wydarzenie, w przeciwnym razie zobaczy 2 lasery.

  1. Utwórz Lokalny skrypt w StarterPlayerScripts o nazwie ClientLaserManager .

  2. W środku skryptu wymagaj modułu LaserRenderer .

  3. Stwórz funkcję o nazwie createPlayerLaser z parametrami playerWhoShot , toolHandle i endPosition.

  4. Połącz funkcję z wydarzeniem zdalnym LaserFired w folderze Wydarzenia.

  5. W funkcji użyj if oświadczenia, aby sprawdzić, czy nie równa się LocalPlayer.

  6. W środku oświadczenia if wezwij funkcję createLaser z modułu LaserRenderer za pomocą toolHandle i endPosition jako argumentów.


    local Players = game:GetService("Players")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))
    local eventsFolder = ReplicatedStorage.Events
    -- Wyświetlaj laser innego gracza
    local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
    if playerWhoShot ~= Players.LocalPlayer then
    LaserRenderer.createLaser(toolHandle, endPosition)
    end
    end
    eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
  7. Przetestuj blaster z 2 graczami, uruchamiając lokalny serwer.Połóż każdego klienta na różnych stronach monitora, abyś mógł zobaczyć oba okna naraz.Kiedy strzelasz na jednego klienta, powinieneś zobaczyć laser na drugim klientzie.

Efekty dźwiękowe

Efekt dźwięku strzelania obecnie odtwarza się tylko na klientzie, który strzela pociskiem.Musisz przenieść kod, aby odtworzyć dźwięk, tak aby inni gracze również go usłyszeli.

  1. W skrypcie Kontroler narzędzi , przejdź do funkcji aktywowane narzędzie i usuń linię, która odtwarza dźwięk Aktywuj.


    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. Na dole funkcji createLaser w LaserRenderer , oświadcz o zmiennej o nazwie strzelający dźwięk i użyj metody FindFirstChild() z toolHandle, aby sprawdzić dźwięk Aktywuj .

  3. Użyj if oświadczenia, aby sprawdzić, czy istnieje shootingSound ; jeśli tak, wezwij jego funkcję Play .


    laserPart.Parent = workspace
    -- Dodaj promień laserowy do usługi Debris, która ma zostać usunięta i oczyszczona
    Debris:AddItem(laserPart, SHOT_DURATION)
    -- Odtwórz dźwięk strzelania broni
    local shootingSound = toolHandle:FindFirstChild("Activate")
    if shootingSound then
    shootingSound:Play()
    end
    end

Zabezpiecz zdalne za pomocą walidacji

Jeśli serwer nie sprawdza danych z przychodzących żądań, haker może nadużywać zdalnych funkcji i wydarzeń i wykorzystywać je do wysyłania fałszywych wartości na serwer.Ważne jest, aby używać walidacji stronowej , aby zapobiec temu.

W obecnej formie zdarzenie zdalne DamageCharacter jest bardzo podatne na atak.Hakerzy mogą wykorzystać to wydarzenie, aby zadać obrażenia dowolnemu graczowi, którego chcą w grze, bez strzelania do niego.

Weryfikacja to proces sprawdzania, czy wartości wysyłane do serwera są realistyczne. W tym przypadku serwer będzie musiał:

  • Sprawdź, czy odległość między graczem a pozycją uderzoną przez laser jest w pewnych granicach.
  • Rzut między bronią, która wystrzeliła laser, a pozycją trafienia, aby upewnić się, że strzał był możliwy i nie przechodził przez żadne ściany.

Klienci

Klient musi wysłać serwerowi pozycję uderzoną przez promień, aby mógł sprawdzić, czy odległość jest realistyczna.

  1. W sterowniku narzędzi , przejdź do linii, na której uruchamiane jest zdarzenie zdalne DamageCharacter w funkcji fireWeapon.

  2. Dodaj hitPosition jako argument.


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

Serwer

Klient wysyła teraz dodatkowy parametr za pośrednictwem zdarzenia zdalnego DamageCharacter, więc ServerLaserManager musi zostać dostosowany, aby go zaakceptować.

  1. W skrypcie ServerLaserManager , dodaj parametr hitPosition do funkcji damageCharacter.


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- Usuń zdrowie z postaci
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. Poniżej funkcji getPlayerToolHandle, stwórz funkcję o nazwie jestHitValid z trzema parametrami: playerFired, characterToDamage i hitPosition.


    end
    local function isHitValid(playerFired, characterToDamage, hitPosition)
    end

Pierwszym sprawdzianem będzie odległość między pozycją trafienia a trafieniem postaci.

  1. Oświadcz zmienną o nazwie MAX_HIT_PROXIMITY na górze skryptu i przypisz jej wartość 10 .Będzie to maksymalna odległość dozwolona między trafieniem a postacią.Potrzebna jest tolerancja, ponieważ znak może się nieco przesunąć, odkąd klient wystrzelił wydarzenie.


    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    local MAX_HIT_PROXIMITY = 10
  2. W funkcji isHitValid oblicz dystans między znakiem a pozycją trafienia.Jeśli odległość jest większa niż MAX_HIT_PROXIMITY , to返uj fałsz .


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Weryfikuj odległość między trafieniem postaci a pozycją trafienia
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > MAX_HIT_PROXIMITY then
    return false
    end
    end

Drugi test obejmie rzut promieniem między wystrzeloną bronią a pozycją trafienia.Jeśli rzutnik zwraca obiekt, który nie jest postacią, możesz założyć, że strzał nie był ważny, ponieważ coś blokował strzał.

  1. Skopiuj kod poniżej, aby wykonać tę sprawdzać. Powrót true na końcu funkcji: jeśli dotrze do kończyć, wszystkie kontrolki zaliczone.


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Weryfikuj odległość między trafieniem postaci a pozycją trafienia
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 then
    return false
    end
    -- Sprawdź, czy strzelasz przez ściany
    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)
    -- Jeśli instancja została uderzona, która nie była postacią, zignoruj strzał
    if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
    return false
    end
    end
    return true
    end
  2. Oświadcz zmienną w funkcji damageCharacter o nazwie validShot .Nadaj mu wynik wezwania do funkcji isHitValid z trzema argumentami: playerFired , characterToDamage i hitPosition.

  3. W poniższym oświadczeniu if dodaj operator i , aby sprawdzić, czy jest prawdą .


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
    if humanoid and validShot then
    -- Usuń zdrowie z postaci
    humanoid.Health -= LASER_DAMAGE
    end
    end

Teraz zdarzenie zdalne damageCharacter jest bezpieczniejsze i zapobiegnie nadużyciu przez większość graczy.Zauważ, że niektórzy złośliwi gracze często znajdą sposoby na ominięcie walidacji; utrzymanie bezpiecznych zdarzeń zdalnych jest ciągłym wysiłkiem.

Twój laserowy blaster jest teraz kompletny, z podstawowym systemem wykrywania uderzeń za pomocą raycastingu.Spróbuj samouczka Wykrywanie wpisu użytkownika, aby dowiedzieć się, jak możesz dodać akcję ponownego ładowania do swojego lasera, lub stworzyć zabawną mapę gry i wypróbować swój laser z innymi graczami!

kodkońcowy

Kontroler narzędzi


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
-- Sprawdź, czy minęło wystarczająco dużo czasu od ostatniego wystrzelenia
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()
-- Stwórz promień z lokalizacji myszy 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Wektor kierunku jednostki promienia pomnożony przez maksymalną odległość
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Rzut promieni z pochodzenia roya w kierunku jego kierunku
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Zwróć punkt styku 3D
return raycastResult.Position
else
-- Nie uderzono w żaden obiekt, więc oblicz pozycję na końcu promienia
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Oblicz normalizowany wektor kierunkowy i pomnóż przez odległość lasera
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Kierunek strzelania broni, pomnożony przez maksymalną odległość
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignoruj postać gracza, aby uniemożliwić mu zranienie siebie
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Sprawdź, czy któreś obiekty zostały uderzone między pozycją początkową a końcową
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- Uderzenie instancji będzie dzieckiem modelu postaci
-- Jeśli znaleziono humanoida w modelu, to prawdopodobnie jest to postać gracza
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
-- Oblicz pozycję końcową na podstawie maksymalnej odległości lasera
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)

Renderer laserowy


local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Czas, przez który laser jest widoczny
-- Stwórz promień laserowy z pozycji startowej w kierunku pozycji końcowej
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
-- Dodaj promień laserowy do usługi Debris, która ma zostać usunięta i oczyszczona
Debris:AddItem(laserPart, SHOT_DURATION)
-- Odtwórz dźwięk strzelania broni
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer

Menadżer laserowy serwera


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Znajdź uchwyt narzędzia, które trzyma gracz
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)
-- Weryfikuj odległość między trafieniem postaci a pozycją trafienia
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Sprawdź, czy strzelasz przez ściany
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)
-- Jeśli instancja została uderzona, która nie była postacią, zignoruj strzał
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Poinformuj wszystkich klientów, że wysłano laser, aby mogli wyświetlić 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
-- Usuń zdrowie z postaci
humanoid.Health -= LASER_DAMAGE
end
end
-- Połącz wydarzenia z odpowiednimi funkcjami
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

Menadżer laserowy klienta


local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- Wyświetlaj laser innego gracza
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)