W tym samouczku nauczysz się, jak skonfigurować laser z blastera w Utworzenie narzędzi gracza i sprawdzić, czy trafi w gracza.
Raycasting, aby znaleźć kolizje
Raycasting tworzy niewidzialny promień z początkowej pozycji w kierunku określonego kierunku z określoną długością. Jeśli promień koliduje z obiektami lub terenem w jego ścieżce, zwraca informacje o kolidencji, takie jak pozycja i obiekt, z którym kolidował.
Lokalizacja Myszy
Zanim laser może być wystrzelony, musisz najpierw wiedzieć, gdzie celuje gracz. To można znaleźć poprzez rajkowanie z 2D-mausy gracza na ekranie bezpośrednio przed kamerą w świat gry. Laser uderzy z czymkolwiek gracz celuje za pomocą myszy.
Otwórz skrypt ToolController w narzędziu Blaster z Creating Player Tools. Jeśli jeszcze nie ukończyłeś tego samouczka, możesz załadować model Blaster i wstawić go do StarterPack.
Na górze skryptu, zadeklaruj zmienne nazyjące się MAX_MOUSE_DISTANCE z wartością 1000 .
Utwórz funkcję nazwaną 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-- Połącz wydarzenia z odpowiednimi funkcjamitool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Użyj funkcji GetMouseLocation of UserInputService , aby uzyskać lokalizację 2D mouse'a na ekranie. Przydziel to zmiennej nazyjącej się mouseLocation .
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
Teraz znana jest lokalizacja myszy 2D, jej właściwości X i Y można używać jako parametrów dla funkcji Camera:ViewportPointToRay(), która tworzy 1> Datatype.Ray1> z ekranu w świat 3D.
Użyj właściwości X i Y MouseLocation jako argumentów dla funkcji mouseLocation. Przydziel to zmiennej nazywającej się 1>screenToWorldRay1>.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Utwórz promień z lokalizacji myszy 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
Pora użyć funkcji Raycast, aby sprawdzić, czy promień trafi w obiekt. Wymaga to pozycji i kierunku wektora: w tym przykładzie będziesz używać właściwości i kierunku źródła screenToWorldRay .
Długość wektora kierunkowego określa, jak daleko promień będzie podróżować. Ray musi być tak długi, jak MAX_MOUSE_DISTANCE, więc będziesz musiał mnożyć wektór kierunkowy przez MAX_MOUSE_DISTANCE.
Oświadcz zmienne nazyjące się DirectionVector i przypisz mu wartość screenToWorldRay.Direction z mnożnikiem MAX_MOUSE_DISTANCE.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Utwórz promień z 2D mouseLocationlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Wektor kierunku jednostki mnożony przez maksymalną odległośćlocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEWezwij funkcję Raycast przestrzeni roboczej, przekazując właściwość Orygina screenToWorldRay jako pierwszy argument i 1> durationVector1> za drugim. Przydziel to zmienne 4>raycastResult4> .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Utwórz promień z 2D mouseLocationlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Wektor kierunku jednostki mnożony przez maksymalną odległośćlocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Rozpraszanie z kierunku źródła promienia w kierunku jego kierunkulocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Informacje o kollizji
Jeśli operacja raycast znajdzie obiekt trafiony przez promień, zwróci RaycastResult, zawierający informacje o kolizji między promieniem a obiektem.
Właściwość RaycastResult | Opis |
---|---|
Instancja | Class.BasePart lub Terrain komórka, do której trafił promień. |
Pozycja | Gdzie doszło intersekcja; zwykle punkt bezpośrednio na powierzchni części lub terenu. |
Materiał | Materiał w punkcie kolizji. |
Normalny | Normalny wektor twarzy połączonych. Można go używać do określenia kierunku, w którym twarz wskazuje. |
Właściwość Pozycja będzie pozycją obiektu, na którym kursory różnych myszek się znajdują. Jeśli kursory nie znajdują się nad żadnym obiektem w zasięgu MAX_MOUSE_DISTANCE, raycastResult będzie nil.
Utwórz oświadczenie if, aby sprawdzić, czy raycastResult istnieje.
Jeśli raycastResult ma wartość, zwróć jego właściwość Pozycja .
Jeśli raycastResult jest nil, znajdź koniec raycastu. Oblicz pozycję 3D myszy, dodając screenToWorldRay.Origin i directionVector razem.
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Utwórz promień z 2D mouseLocation
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Wektor kierunku jednostki mnożony przez maksymalną odległość
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Rozpraszanie z kierunku źródła promienia w kierunku jego kierunku
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Zwróć 3D punkt intersekcji
return raycastResult.Position
else
-- Nie uderzono w obiekt, więc oblicz pozycję na końcu promienia
return screenToWorldRay.Origin + directionVector
end
end
Strzelanie w kierunku celu
Teraz, gdy znana jest pozycja myszy 3D, można ją używać jako pozycji docelowej , aby wystrzelić laser w kierunku. Drugi promiec można rzucić między bronią gracza a pozycją docelową przy użyciu funkcji Raycast .
Oświń konstytucję nazwaną MAX_LASER_DISTANCE na górze skryptu i przypisz go 500 lub wybraną zasięg dla laseru.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Utwórz funkcję nazwaną fireWeapon pod funkcją getWorldMousePosition.
Zadzwoń getWorldMousePosition i przypisz wynik zmiennej nazyającej się mousePosition . Będzie to pozycja docelowa dla raycast.
-- Nie uderzono w obiekt, więc oblicz pozycję na końcu promieniareturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Tym razem wektor kierunkowy dla funkcji rajkowania będzie reprezentować kierunek z pozycji narzędzia gracza do lokalizacji celu.
Oznacz zmienne nazyjące się targetDirection i oblicz wektor kierunkowy poprzez odejście pozycji narzędzia od mouseLocation .
Normalizuj wektor, używając jego właściwości Jednostka . Dzięki temu łatwo jest go mnożyć później przez długość.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Oblicz wektor kierunkowy normalizacji i pomnóż przez dystans laserowylocal targetDirection = (mouseLocation - tool.Handle.Position).UnitendOznacz zmienne nazyjące się DirectionVector i przypisz mu wartość targetDirection mnożoną przez MAX_LASER_DISTANCE.
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- Kierunek strzelania broni, mnożony przez maksymalną odległośćlocal directionVector = targetDirection * MAX_LASER_DISTANCEend
Obiekt RaycastParams może być używany do przechowywania dodatkowych parametrów dla funkcji raycast. Będzie używany w twoim laserze, aby upewnić się, że raycast nie będzie przypadkowo kolidować z graczem, który wystrzeli broń. Wszystkie części zawarte w właściwości Datatype
Kontynuuj funkcję fireWeapon i zgłoś zmienne nazyjące się weaponRaycastParams . Przydziel nowy obiekt RaycastParams.
Utwórz tabelę zawierającą lokalnego postaci gracza i przypisz go do właściwości weaponRaycastParams.FilterDescendantsInstances.
Raycast z pozycji uchwytu narzędzia gracza, w kierunku directionVector . Pamiętaj, aby dodać weaponRaycastParams jako argument tym razem. Przydziel to zmiennej nazyjącej się 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()
-- Oblicz wektor kierunkowy normalizacji i pomnóż przez dystans laserowy
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Kierunek strzelania broni mnożony jest przez maksymalną odległość
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignoruj postać gracza, aby zapobiec uszkodzeniu siebie
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end
W końcu będziesz musiał sprawdzić, czy operacja raycast zwróciła wartość. Jeśli wartość zostanie zwrócona, obiekt został trafiony przez promień i można utworzyć laser między miejscem trafienia a pozycją broń. Jeśli nic nie zostało zwrócone, pozycja końcowa musi zostać obliczona, aby utworzyć laser.
Oświadcz pustą zmienną nazyającą się hitPosition .
Użyj jeśli stwierdzenie, aby sprawdzić, czy weaponRaycastResult ma wartość. Jeśli obiekt został trafiony, przypisz weaponRaycastResult.Position do 1> hitPosition1>.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Sprawdź, czy któreś obiekty zostały uderzone między pozycją startową i końcowąlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendJeśli weaponRaycastResult nie ma wartości, oblicz pozycję końcową promienia kastując łącząc pozycję rękojmi narzędzia z directionVector. Przydziel to do 1> hitPosition1>.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Sprawdź, czy któreś obiekty zostały uderzone między pozycją startową i końcowąlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Oblicz pozycję końcową w oparciu o maksymalną dystans laserowyhitPosition = tool.Handle.Position + directionVectorendendPrzejdź do funkcji toolActivated i wezwij funkcję fireWeapon, aby laser wystrzelał za każdym razem, gdy narzędzie jest aktywowane.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
Sprawdzanie trafienia obiektu
Aby znaleźć, czy obiekt trafiony przez lasery jest częścią postaci gracza lub tylko kawałkiem scenerii, będziesz musiał szukać Humanoid, ponieważ każdy gracz ma jeden.
Najpierw musisz znaleźć model postaci . Jeśli część postaci została dotknięta, nie możesz założyć, że rodzic postaci będzie postacią. Laser może uderzyć część ciała, akcesoriumlub narzędzie, wszystkie zlokalizowane w różnych częściach hierarchii postaci.
Możesz użyć FindFirstAncestorOfClass , aby znaleźć ojcostwo modelu postaci trafione przez laser, jeśli istnieje. Jeśli znajdziesz model i zawiera on humanoid, w większości przypadków możesz założyć, że jest to postać.
Dodaj poniżej podkreślony kod do weaponRaycastResult jeśli stwierdzenie, aby sprawdzić, czy postać została trafiona.
-- Sprawdź, czy któreś obiekty zostały uderzone między pozycją startową i końcowąlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- Uderzenie instancji będzie dzieckiem modelu postaci-- Jeśli znaleziono humanoid w modelu, to prawdopodobnie jest to postać graczalocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Player hit")endendelse-- Oblicz pozycję końcową w oparciu o maksymalną dystans laserowyhitPosition = tool.Handle.Position + directionVectorend
Teraz laserowy blaster powinien wydrukować Player hit do okienka wyjścia za każdym razem, gdy operacja laserowa trafi na innego gracza.
Testowanie z wieloma graczami
Dwa gracze są wymagane do testowania, czy promień zbrojny znajduje innych graczy, więc musisz uruchomić lokalny serwer.
Wybierz Testuj zakładkę w Studio.
Upewnij się, że opcja upuszczenia graczy jest ustawiona na „2 gracze” i kliknij przycisk Start, aby uruchomić lokalny serwer z 2 klientami. Trzy okna pojawią się. Pierwsze okno będzie serwerem lokalnym, a pozostałe okna będą klientami dla Player1 i Player2.
Na jednym klientu, test strzelania drugiego gracza za pomocą broni, klikając na nich. „Player hit” powinien być wyświetlony w wyniku za każdym razem, gdy gracz jest trafiony.
Dowiedz się więcej o zakładce Testy tutaj.
Lokalizacja Lasera
Blaster powinien wystrzelić czerwony promień światła na swoją celę. Funkcja dla tego będzie znajdować się w ModuleScript , aby można go było ponownie użyć w innych skryptach później. Po pierwsze, skrypt będzie musiał znaleźć pozycję, w której laserowy promień powinien być renderowany.
Utwórz ModuleScript o nazwie LaserRender , związany z StarterPlayerScripts pod StarterPlayer.
Otwórz skrypt i zmień tabelę modułu na imię skryptu LaserRender .
Oświadcz zmienne nazyjące się czas_strzelaania z wartością 0.15 . Będzie to czas (w sekundach) trwałości lasera.
Utwórz funkcję LaserRender nazyającą się createLaser z dwoma parami toolHandle i endPosition .
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Czas, w którym laser jest widoczny dla-- Utwórz promień laserowy z początkowej pozycji w kierunku końcowej pozycjifunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererOznacz zmienne nazyjące się startPosition i ustaw właściwość Position dla toolHandle jako jego wartość. Będzie to pozycja laserowego blastera gracza.
Oświadcz zmienne nazyjące się laserDistance i odejmij endPosition od startPosition aby znaleźć różnicę między dwoma wektorami. Użyj właściwości startPosition tego aby uzyskać długość promienia laserowego.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendOświadcz laserCFrame zmienne, aby przechować pozycję i orientację promienia laserowego. Pozycja musi być średnią punktu startu i końca promienia. Użyj CFrame.lookAt , aby stworzyć nowy
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
Tworzenie części laserowej
Teraz, gdy wiesz, gdzie stworzyć promień laserowy, musisz dodać sam promień. Można to zrobić łatwo z częścią Neon.
Oświadcz zmienne laserPart i przypisz mu nową instancję Part.
Ustaw następujące właściwości z laserPart :
- Rozmiar : Vector3.new(0.2, 0.2, laserDistance)
- CFrame : laserCFrame
- Zakotwiczone : prawdziwy
- Możliwość kolidowania : false
- Kolor : Color3.fromRGB(225, 0, 0) (mocny czerwony kolor)
- Materiał : Enum.Material.Neon
Parent laserPart do Przestrzeni roboczej .
Dodaj część do usługi Debris, aby została usunięta po liczbie sekund w zmiennej 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-- Dodaj laserowy promień do usługi Debris, aby zostać usunięty i wyczyyszonyDebris:AddItem(laserPart, SHOT_DURATION)end
Teraz funkcja renderowania promienia laserowego jest zakończona, można ją nazwać przez ToolController .
Na górze ToolController skryptu, zadeklaruj zmienne nazyjące się LaserRender i wymagaj modułu LaserRenderModuleScript 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.ParentW dalszej części funkcji fireWeapon, wezwij funkcję LaserRender createLaser używając uchwytu narzędzia i hitPosition jako argumentów.
-- Oblicz pozycję końcową w oparciu o maksymalną dystans laserowyhitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endTestuj broń, klikając przycisk Play. Laser powinien być widoczny między bronią a myszką, gdy narzędzie jest aktywowane.
Kontrolowanie szybkości ognia broni
Broń musi mieć opóźnienie między każdym strzałem, aby zapobiec graczom zadawaniu zbyt wiele obrażeń w krótkim czasie. Można to kontrolować, sprawdzając, czy wystąpił wystarczający czas od ostatniego wystrzelenia.
Oświadcz zmienne na górze ToolController zwane FIRE_RATE . Będzie to minimalny czas między każdym strzałem. Daj mu wartość swojego wyboru; ten przykład używa 0,3 sekund.
Oświadcz inną zmienną pod nazwą timeOfPreviousShot z wartością 0. To przechowuje ostatni czas, w którym gracz wystrzelił i zostanie zaktualizowany za każdym strzałem.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Utwórz funkcję canShootWeapon bez parametrów. Ta funkcja będzie przyjrzeć się, jak dużo czasu minęło od poprzedniego strzału i zwrócić prawdziwy lub fałszywy.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Sprawdź, czy minęło wystarczająco dużo czasu od momentu zacięcia poprzedniego strzałulocal function canShootWeapon()endlocal function getWorldMousePosition()Wewnątrz funkcji deklaruj zmienne nazyające się currentTime ; przypisz wynik wzywania funkcji tick() . To powoduje, że czas odliczany jest w sekundach, od 1 stycznia 1970 r. (dowolny dzień używany powszechnie do obliczania czasu).
Odejmij timeOfPreviousShot od currentTime i powróć false jeśli wynik jest mniejszy niż 1> FIRE_RATE1> ; w przeciwnym razie powróć 4> true4> .
-- Sprawdź, czy minęło wystarczająco dużo czasu od momentu zacięcia poprzedniego strzałulocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendPod koniec funkcji fireWeapon aktualizuj timeOfPreviousShot za każdym razem, gdy broń jest wystrzelona używając tick .
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endWewnątrz funkcji toolActivated, utwórz jeśli oświadczenie i wezwij canShootWeapon, aby sprawdzić, czy broń może być wystrzelona.
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
Gdy testujesz blaster, powinieneś zauważyć, że bez względu na to, jak szybko klikać, zawsze będzie 0,3 sekundy opóźnienia między każdym strzałem.
Uszkodzenie gracza
Klienci nie mogą bezpośrednio uszkodzić innych klientów; serwer musi być odpowiedzialny za zadanie obrażeń, gdy gracz zostanie trafiony.
Klienci mogą użyć RemoteEvent , aby powiadomić serwer, że postać została trafiona. Te powinny być przechowywane w ReplicatedStorage, gdzie są widoczne zarówno dla klienta, jak i dla serwera.
Utwórz Katalog w ReplicatedStorage nazyającym się Wydarzenia.
Umieść zdarzenie zdalne w katalogu wydarzeń i nazwij go DamageCharacter .
W ToolController twó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.Parentlocal eventsFolder = ReplicatedStorage.Eventslocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Zastąp "Player hit" drukową deklarację w fireWeapon z linią Lua, aby uruchomić zdalne wydarzenie DamageCharacter z zmiennej 2> characterModel2> jako argumentu.
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel)endendelse-- Oblicz pozycję końcową w oparciu o maksymalną dystans laserowyhitPosition = tool.Handle.Position + directionVectorend
Serwer musi zadać obrażenia graczowi, który został trafiony, gdy wydarzenie zostanie uruchomione.
Uruchom Skrypt w ServerScriptService i nazwij go ServerLaserManager.
Oświadcz zmienne nazyjące się LASER_DAMAGE i ustaw go na 10 lub wartość wybraną przez ciebie.
Utwórz funkcję nazwaną damageCharacter z dwoma parami playerFired i characterToDamage .
Wewnątrz funkcji znajdź charakterystyczny dla człowieka LASER_DAMAGE i odejmij go od jego zdrowia.
Połącz funkcję damageCharacter z zdalnym wydarzeniem DamageCharacter w katalogu wydarzeń.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10function damageCharacter(playerFired, characterToDamage)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Usuń zdrowie z postacihumanoid.Health -= LASER_DAMAGEendend-- Połącz wydarzenia z odpowiednimi funkcjamieventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)Testuj blaster z 2 graczami, uruchomując lokalny serwer. Gdy strzelisz w inną graczu, ich zdrowie zmniejszy się o liczbę przypisanych do LASER_DAMAGE .
O renderowaniu promieni laserowych innych graczy
Obecnie promień laserowy jest tworzony przez klienta, który wystrzela broń, więc tylko oni będą mogli zobaczyć promień laserowy.
Jeśli promień laserowy został stworzony na serwerze, wszyscy mogliby go zobaczyć. Jego był jednak niewielki opóźnienie pomiędzy klientem, który strzelił bronią, a serwerem otrzymującym informacje o strzału. To oznacza, że klient strzelający bronią zobaczyłby opóźnienie pomiędzy aktywacją broni a jej zobaczeniem.
Aby rozwiązać ten problem, każdy klient stworzy własne promienie laserowe. Oznacza to, że klienci, którzy strzelają w broń, zobaczą promień laserowy natychmiastowo. Inne klienty doświadczą małego opóźnienia między tym, jak inny gracz strzela, a promieniem laserowym. To jest najlepszy schemat przypadku: nie ma sposób na komunikację jednego klienta laser do innego klienta szybciej.
Klient strzelca
Najpierw kliент musi powiedzieć serwerowi, że wystrzelił laser i dostarczyć pozycję końca.
Umieść RemoteEvent w katalogu Wydarzenia w ReplicatedStorage i nazwij go LaserFired.
Lokalizuj funkcję fireWeapon w skrypcie ToolController. W końcu funkcji wstrzymaj zdalne wydarzenie LaserFired używając hitPosition jako argumentu.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
Serwer
Serwer teraz musi otrzymać wydarzenie, które klient wysłał, i powiedzieć wszystkim klientom pozycję początku i końca promienia laserowego, aby mogli również renderować go.
W ServerLaserManager skrypcie utwórz funkcję nazwę playerFiredLaser powyżej damageCharacter z dwoma parami nazw 2> playerFired2> i 5> endPosition5>.
Połącz funkcję z zdalnym zdarzeniem LaserFired .
-- Powiadom wszystkich klientów, że laser został wystrzelony, aby mogli wyświetlić laserlocal function playerFiredLaser(playerFired, endPosition)end-- Połącz wydarzenia z odpowiednimi funkcjamieventsFolder.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, jeśli to możliwe. Pozycja uchwytu broni postaci będzie pozycją startową, więc serwer może ją znaleźć od tamtego.
Utwórz funkcję getPlayerToolHandle nad funkcją playerFiredLaser z parametrem nazyającym się player.
Użyj następującego kodu, aby szukać postaci gracza dla broni i zwrócić obiekt uchwytu.
local LASER_DAMAGE = 10-- Znajdź uchwyt narzędzia, które trzyma graczlocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Powiadom wszystkich klientów, że laser został wystrzelony, aby mogli wyświetlić laserlocal function playerFiredLaser(playerFired, endPosition)
Serwer może teraz wezwieć FireAllClients na zdalnym wydarzeniu LaserFired, aby wysłać wymagane informacje, aby renderować laser dla klientów. Obejmuje to gracza, który wystrzelił laser (tak, aby klient dla tego gracza nie renderował laseru dwukrotnie), 2>upewnij2>
W funkcji playerFiredLaser wzyj funkcję getPlayerToolHandle z argumentem playerFired jako argument i przypisz wartość zmiennej nazywającej się 1> toolHandle1>.
Jeśli istnieje toolHandle , fire the LaserFired event for all clients using playerFired , toolHandle and 1> endPosition1> as argumenty.
-- Powiadom wszystkich klientów, że laser został wystrzelony, aby mogli wyświetlić laserlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
Renderowanie na klientach
Teraz FireAllClients zostanie wywołany, każdy klient otrzyma wydarzenie z serwera, aby renderować promień laserowy. Każdy klient może ponownie użyć modułu LaserRenderer z poprzedniego, aby renderować promień laserowy przy użyciu pozycji i pozycji końcowej przesłanej przez serwer. Gracz, który uruchomił promień laserowy w
Utwórz LokalnySkrypt w StarterPlayerScripts zwany ClientLaserManager.
Wewnątrz skryptu wymagaj modułu LaserRender .
Utwórz funkcję nazwę createPlayerLaser z parami playerWhoShot , toolHandle i 1> endPosition1>.
Połącz funkcję z LaserFired zdalnym zdarzeniem w katalogu Wydarzenia.
W funkcji użyj jeśli oświadczenie, aby sprawdzić, czy playerWhoShot nie jest równy lokalnem graczu .
Wewnątrz funkcji if, w module LaserRender, wezwij funkcję createLaser używając toolHandle i endPosition jako argumenty.
local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))local eventsFolder = ReplicatedStorage.Events-- Pokaż laser innego graczalocal function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)Przeciągnij blaster z 2 graczami, rozpoczynając lokalny serwer. Pozycjonuj każdego klienta na różnych stronach swojego monitora, abyś mógł zobaczyć obie okna jednocześnie. Gdy strzelisz na jednym klencie, powinieneś zobaczyć laser na drugim klencie.
Efekty Dzwiękowe
Efekt dźwiękowy strzału obecnie odtwarza się tylko na klientach, którzy strzelają w projektyl. Będziesz musiał przenieść kod, aby odtwarzyć dźwięk, aby inni gracze mogli go usłyszeć.
W skrypcie ToolController przesuń się do funkcji toolActivated i usuń linię, która odtwarza dźwięk Aktywowania.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendW dalszej części funkcji createLaser w LaserRender , deklaruj zmienne nazyjące się shootingSound i użyj metody 2>Class.Instance:FindFirstChild()|FindFirstChild()2> , aby sprawdzić dźwięk 5> Aktivate 5>.
Użyj jeśli oświadczenie, aby sprawdzić, czy istnieje shootingSound ; jeśli tak, wezwij jego funkcję Graj .
laserPart.Parent = workspace-- Dodaj laserowy promień do usługi Debris, aby zostać usunięty i wyczyyszonyDebris:AddItem(laserPart, SHOT_DURATION)-- Zagraj dźwięk strzelania bronilocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Zabezpieczanie zdalnych przy użyciu ważdżki
Jeśli serwer nie sprawdza danych z wходяcych wniosków, haker może wykorzystać zdalne funkcje i wydarzenia, aby wysłać fałszywe wartości na serwer. Ważne jest, aby użyć serwernej weryfikacji , aby zapobiec temu.
W swojej obecnej formie zdalne wydarzenie DamageCharacter jest bardzo podatne na atak. Hakerzy mogą użyć tego zdalnego wydarzenia, aby uszkodzić dowolnego gracza, którego chcą w grze bez strzelać w niego.
Walidacja to proces sprawdzenia, że wysyłane wartości na serwer są realistyczne. W tym przypadku serwer będzie musiał:
- Sprawdź, czy odległość między graczem a pozycją trafioną przez lasera jest w określonym zakresie.
- Raycast pomiędzy bronią, która wystrzeliła laser, a pozycją trafienia, aby upewnić się, że strzał jest możliwy i nie przechodzi przez żadne ściany.
Klient
Kliient musi wysłać serwerowi pozycję dotkniętą przez raycast, aby mógł sprawdzić, czy dystans jest realistyczny.
W ToolController przeglądajте linię, w której zdarza się zdarzenie zdalnego w trybie fireWeapon .
Dodaj hitPosition jako argument.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Serwer
Kliент teraz wysyłuje dodatkowy parametr poprzez zdarzenie zdalnego wysyłania DamageCharacter, więc ServerLaserManager musi być dostosowany, aby go zaakceptować.
W ServerLaserManager skrypcie dodaj parametr hitPosition do funkcji damageCharacter.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Usuń zdrowie z postacihumanoid.Health -= LASER_DAMAGEendendPoniżej funkcji getPlayerToolHandle, stwórz funkcję nazwaną isHitValid z trzema parami: playerFired , 1> characterToDamage1> i 4> hitPosition4> .
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
Pierwszym czynnikiem będzie odległość między pozycją trafienia a znakiem znajdującym się na nim.
Oświadcz zmienne nazyjące się MAX_HIT_PROXIMITY na górze skryptu i przypisz mu wartość 10 . Będzie to maksymalna odległość pozwolona między trafieniem a postacią. Wymagana jest tolerancja, ponieważ postać może się poruszyć nieco od czasu, od czasu, od czasu.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10W funkcji isHitValid oblicz odległość między znakiem a pozycją trafienia. Jeśli odległość jest większa niż MAX_HIT_PROXIMITY to zwracaj fałszywy .
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Zweryfikuj odległość między trafieniem a pozycją trafienialocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
Drugi czek będzie zaangażowany w promieniowanie między bronią wystrzeloną i pozycją trafienia. Jeśli promieniowanie zwraca obiekt, który nie jest postacią, można założyć, że strzał nie był ważny, ponieważ coś blokował strzał.
Kopiuj kod poniżej, aby wykonać ten sprawdzać. Powróć prawdziwy na końcu funkcji: jeśli dotrze do kończyć, wszystkie testy zakończyły się.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Zweryfikuj odległość między trafieniem a pozycją trafienialocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Sprawdź, czy strzelasz przez ścianylocal 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)-- Jeśli instans został trafiony, który nie był postacią, to ignoruj strzałif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendOświadcz zmienne w funkcji damageCharacter zwanej zastrzeleniem . Przydziel wynik wezwania do funkcji isHitValid z trzema argumentami: 1> playerFired1> , 4> characterToDamage4> i 7> hitPosition7>.
W poniższym oświadczeniu dodaj operatory i aby sprawdzić, czy validShot jest prawdziwy .
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")local validShot = isHitValid(playerFired, characterToDamage, hitPosition)if humanoid and validShot then-- Usuń zdrowie z postacihumanoid.Health -= LASER_DAMAGEendend
Teraz zdarza się więcej incydentów związanych z uszkodzeniem postaci, co jest bardziej bezpieczne i zapobiega większości graczy, aby się nieużywali. Uwaga, że niektórzy złowieszczi gracze często znajdą sposób na uniknięcie uwierzytelniania; zapewnienie bezpieczeństwa zdalnych wydarzeń jest ciągłym wysiłkiem.
Twój laser blaster jest teraz gotowy, z systemem wykrywania podstawowych uderzeń używającym Raycastingu. Spróbuj samouczka Wykrywanie wejścia użytkownika , aby dowiedzieć się, jak można dodać udogodnienie do swojego laser blastera, lub stworzyć zabawną mapę gry i spróbuj wypróbować swojego laser blastera z innymi graczami!
Kod koń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 momentu zacięcia poprzedniego strzału
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()
-- Utwórz promień z lokalizacji myszy 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Wektor kierunku jednostki mnożony przez maksymalną odległość
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast z źródła króla w kierunku jego kierunku
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Zwróć 3D punkt intersekcji
return raycastResult.Position
else
-- Nie uderzono w obiekt, więc oblicz pozycję na końcu promienia
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Oblicz wektor kierunkowy normalizacji i pomnóż przez dystans laserowy
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Kierunek strzelania broni, mnożony przez maksymalną odległość
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Ignoruj postać gracza, aby zapobiec uszkodzeniu 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ą startową i końcową
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- Uderzenie instancji będzie dzieckiem modelu postaci
-- Jeśli znaleziono humanoid 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ą w oparciu o maksymalną dystans laserowy
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)
LaserRender
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Czas, w którym laser jest widoczny dla
-- Utwórz promień laserowy z początkowej pozycji w kierunku końcowej pozycji
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 laserowy promień do usługi Debris, aby zostać usunięty i wyczyyszony
Debris:AddItem(laserPart, SHOT_DURATION)
-- Zagraj dźwięk strzelania broni
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer
SerwerLaserManager
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)
-- Zweryfikuj odległość między trafieniem 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 instans został trafiony, który nie był postacią, to ignoruj strzał
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Powiadom wszystkich klientów, że laser został wystrzelony, 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)
ClientLaserManager
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- Pokaż 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)