Implementacja zachowania blastera to proces programowania mechaniki eksplozji w doświadczeniach strzelanek z perspektywy pierwszej osoby. Choć gracze mogą strzelać jednym kliknięciem lub naciśnięciem przycisku, stworzenie satysfakcjonującego i precyzyjnego zachowania eksplozji jest ważne, ponieważ zwiększa przyjemność graczy z całej rozgrywki.
Korzystając z przykładowego doświadczenia laser tag jako odniesienia, ta część samouczka nauczy cię o skryptach odpowiedzialnych za implementację zachowania blastera dla dwóch różnych typów blasterów, w tym wskazówki dotyczące:
- Wykrywania, kiedy gracze naciskają przycisk strzału.
- Sprawdzania, czy gracz może użyć swojego blastera, jeśli niedawno nacisnął przycisk strzału.
- Generowania danych eksplozji, które informują serwer, kto zainicjował eksplozję, skąd pochodziła oraz jakie było docelowe miejsce każdego promienia laserowego.
- Powiadamiania serwera o danych eksplozji, aby mógł wykonać odpowiednie działania, jeśli eksplozja zderzyła się z innym graczem.
- Resetowania blastera między każdym strzałem, aby dać mu wystarczająco dużo czasu na schłodzenie, zanim będzie mógł znowu strzelać.
Po zakończeniu tej sekcji dowiesz się o skryptach, które pozwalają blasterowi wykrywać, kiedy jego strzały kolidują z innymi graczami, a następnie odejmować odpowiednią ilość zdrowia w zależności od typu blastera.
Wykrywanie wejścia od gracza
Pierwszym krokiem w implementacji zachowania blastera jest nasłuchanie, kiedy gracz naciska przycisk strzału. Typ wejścia, który gracze używają do naciśnięcia przycisku strzału, zależy od urządzenia, którego używają do uzyskania dostępu do doświadczenia. Na przykład, przykładowe doświadczenie laser tag obsługuje sterowanie myszką i klawiaturą, gamepady oraz sterowanie dotykowe. Możesz zobaczyć każdy z tych typów wejścia w ReplicatedStorage ⟩ UserInputHandler.
Ten skrypt klienta wykorzystuje ContextActionService, aby powiązać MouseButton1 i ButtonR2 z akcją strzału. Oznacza to, że za każdym razem, gdy gracz naciśnie lewy przycisk myszy lub przycisk R2 gamepada, uruchamia to wystrzał promienia laserowego z blastera. Zauważ, że HUDGui zawiera przycisk do strzału na urządzeniach mobilnych, który jest podłączany później w skrypcie.
UserInputHandlerContextActionService:BindAction("_", onBlasterActivated, false,Enum.UserInputType.MouseButton1,Enum.KeyCode.ButtonR2)
Inna ważna uwaga dotyczy użycia Enum.UserInputState.Begin w definicji onBlasterActivated(). Wiele interakcji z interfejsem użytkownika, takich jak wybór blastera w tym przykładzie, nie zachodzi, dopóki przycisk myszy nie zostanie puszczony (Enum.UserInputState.End), co daje użytkownikom ostatnią szansę na uniknięcie interakcji. Jednak mechanika strzału nie wydaje się responsywna, chyba że następuje w momencie naciśnięcia przycisku.
Aby to zobrazować, możesz zmienić Enum.UserInputState.Begin na Enum.UserInputState.End, a następnie przeprowadzić test, aby zobaczyć, jak responsywność strzału wpływa na rozgrywkę doświadczenia. Na przykład, jeśli gracze mogą przytrzymać przycisk bez uruchamiania strzału, jak to może zmienić ich doświadczenie podczas tagowania innych graczy?
UserInputHandler
local function onBlasterActivated(_actionName: string,
inputState: Enum.UserInputState, _inputObject: InputObject)
if inputState == Enum.UserInputState.End then -- zaktualizowana linia, upewnij się, że zmienisz z powrotem
attemptBlastClient()
end
end
Sprawdzanie, czy gracz może strzelać
Po tym, jak UserInputHandler wykryje naciśnięcie przycisku lub dotknięcie ekranu, wywołuje ReplicatedStorage ⟩ Blaster ⟩ attemptBlastClient, aby sprawdzić, czy gracz może strzelać, czy nie. Jak większość sprawdzeń w przykładowym doświadczeniu laser tag, dzieje się to dwukrotnie: najpierw po stronie klienta, a później po stronie serwera. attemptBlastClient następnie wywołuje ReplicatedStorage ⟩ Blaster ⟩ canLocalPlayerBlast, aby wykonać prostą kontrolę atrybutu gracza blasterStateClient:
canLocalPlayerBlast
local function canLocalPlayerBlast(): boolean
return localPlayer:GetAttribute(PlayerAttribute.blasterStateClient) == BlasterState.Ready
end
Jeśli zbadujesz ReplicatedStorage ⟩ Blaster ⟩ BlasterState, możesz zauważyć, że doświadczenie ma trzy stany blastera: Ready, Blasting i Disabled. Aby zobaczyć efekt każdego z tych stanów, możesz przetestować doświadczenie, wybrać swojego gracza w usłudze Players, a następnie obserwować atrybut blasterStateClient w oknie Properties. Zauważ, jak wyświetla Disabled, gdy wybierasz swój blaster, Ready przez większość czasu i Blasting na mniej niż sekundę po naciśnięciu przycisku.
Ta drobna pauza uniemożliwia ci strzelanie tak szybko, jak możesz klikać. Na przykład, jeśli zmienisz funkcję, aby zawsze zwracała true, możesz szybko strzelać ze swojego blastera bez jakichkolwiek opóźnień, co jest nierealne w rozgrywce laser tag.
canLocalPlayerBlast
local function canLocalPlayerBlast(): boolean
return true -- zaktualizowana linia, upewnij się, że zmienisz z powrotem
end
Generowanie danych eksplozji
Po zweryfikowaniu, że blaster gracza jest w stanie Ready, attemptBlastClient wywołuje ReplicatedStorage ⟩ attemptBlastClient ⟩ blastClient. Pierwszym krokiem, który podejmuje blastClient, jest ustawienie atrybutu gracza blasterStateClient na Blasting, co zapobiega przypadkowym szybkostrzelności z wcześniejszych przykładów.
Kolejnym krokiem jest wygenerowanie danych eksplozji. Jeśli przeglądniesz ReplicatedStorage ⟩ Blaster ⟩ BlastData, możesz zobaczyć, że każda eksplozja składa się z trzech części informacji:
- Gracze, którzy inicjują eksplozję.
- DataType.CFrame, który reprezentuje punkt początkowy eksplozji.
- Tablica RayResult, która zawiera finalne miejsce każdego promienia laserowego i trafionego gracza, jeśli trafił innego gracza.
Aby wygenerować te dane, blastClient wywołuje ReplicatedStorage ⟩ attemptBlastClient ⟩ blastClient ⟩ generateBlastData, które możesz przeglądać poniżej.
generateBlastData
local function generateBlastData(): BlastData.Type
local blasterConfig = getBlasterConfig()
local rayDirections = getDirectionsForBlast(
currentCamera.CFrame, blasterConfig)
local rayResults = castLaserRay(
localPlayer, currentCamera.CFrame.Position, rayDirections)
local blastData: BlastData.Type = {
player = localPlayer,
originCFrame = currentCamera.CFrame,
rayResults = rayResults,
}
return blastData
end
Ta funkcja zaczyna od użycia getBlasterConfig, aby pobrać typ blastera gracza. Przykład dostarcza dwa typy blasterów: jeden, który produkuje kilka promieni z szerokim, poziomym rozprzestrzenieniem, a drugi, który produkuje pojedynczy promień. Ich konfiguracje znajdziesz w ReplicatedStorage ⟩ Instances ⟩ LaserBlastersFolder.
Funkcja następnie wykorzystuje currentCamera.CFrame jako punkt początkowy dla eksplozji, przekazując go do getDirectionsForBlast. W tym punkcie kod nie dotyczy już blastera, lecz promienia laserowego, o którym dowiesz się więcej w sekcji wykrywania trafień samouczka. Na koniec, po utworzeniu tablicy rayResults, generateBlastData ma wszystkie informacje potrzebne do zwrócenia danych eksplozji do blastClient.
Powiadomienie serwera
Gdy blastClient ma pełne dane dotyczące eksplozji, uruchamia dwa zdarzenia:
blastClientlocal laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEventlocal laserBlastedEvent = ReplicatedStorage.Instances.LaserBlastedEventlaserBlastedBindableEvent:Fire(blastData)laserBlastedEvent:FireServer(blastData)
BindableEvent powiadamia inne skrypty klientów o eksplozji. Na przykład, ReplicatedStorage ⟩ FirstPersonBlasterVisuals używa tego zdarzenia, aby wiedzieć, kiedy wyświetlić efekty wizualne, takie jak animacja eksplozji i pasek schłodzenia. Podobnie, RemoteEvent powiadamia skrypty serwera o eksplozji, które zaczynają przetwarzanie eksplozji w ServerScriptService ⟩ LaserBlastHandler.
LaserBlastHandler
local function onLaserBlastedEvent(playerBlasted: Player, blastData: BlastData.Type)
local validatedBlastData = getValidatedBlastData(playerBlasted, blastData)
if not validatedBlastData then
return
end
if not canPlayerBlast(playerBlasted) then
return
end
blastServer(playerBlasted)
processTaggedPlayers(playerBlasted, blastData)
for _, replicateToPlayer in Players:GetPlayers() do
if playerBlasted == replicateToPlayer then
continue
end
replicateBlastEvent:FireClient(replicateToPlayer, playerBlasted, blastData)
end
end
Aby pomóc zapobiec oszustwom, serwer musi zweryfikować wszystkie dane, które każdy klient wysyła. Te kontrole obejmują:
- Czy BlastData to tabela? Czy zawiera Class.CFrame i inną tabelę o nazwie rayResults?
- Czy gracz ma wyposażony blaster?
- Czy gracz ma postać i lokalizację w świecie?
- Po wysłaniu danych eksplozji, czy gracz przesunął się na nadmierną odległość od miejsca, z którego wystrzelił promień laserowy?
Ta ostatnia kontrola wymaga dokonania oceny, a w zależności od opóźnienia serwera i prędkości ruchu gracza możesz zdecydować, że różne wartości są nadmierne dla twojego doświadczenia. Aby pokazać, jak dokonać tej oceny, możesz uzyskać poczucie typowej wielkości zmiany pozycyjnej, dodając polecenie print w getValidatedBlastData i przeprowadzając testowanie doświadczenia.
getValidatedBlastDatalocal distanceFromCharacterToOrigin = blastData.originCFrame.Position - rootPartCFrame.Positionprint(distanceFromCharacterToOrigin.Magnitude) -- zaktualizowana linia, upewnij się, że usunieszif distanceFromCharacterToOrigin.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS thenwarn(`Gracz {player.Name} nie przeszedł testu wiarygodności pochodzenia podczas strzału`)returnend
Podczas ruchu i strzału zwróć uwagę na wynik. Może to wyglądać mniej więcej tak:
1.90196299552917483.15495586395263672.57428836822509774.80445861816406252.6434271335601807
Jeśli zwiększysz prędkość ruchu dla graczy w ReplicatedStorage ⟩ PlayerStateHandler ⟩ togglePlayerMovement, a następnie ponownie przetestujesz, prawdopodobnie napotkasz wiele nieudanych kontroli z powodu nadmiernego ruchu między wystrzałami.
togglePlayerMovementlocal ENABLED_WALK_SPEED = 60 -- zaktualizowana linia, upewnij się, że zmienisz z powrotem
Serwer następnie wykonuje następujące czynności:
- Weryfikuje rayResults.
- Sprawdza, czy gracz może strzelać.
- Resetuje stan blastera.
- Redukuje zdrowie wszelkich oznaczonych graczy.
- Replikuje eksplozję do wszystkich innych graczy, aby mogli zobaczyć efekty wizualne z perspektywy trzeciej osoby.
Aby uzyskać więcej informacji na temat tych operacji serwera, zobacz sekcję wykrywania trafień samouczka.
Resetowanie blastera
W przykładowym doświadczeniu laser tag blastery korzystają z mechaniki ciepła. Zamiast przeładowywać po ustalonej liczbie strzałów, potrzebują czasu na "schłodzenie" między każdym strzałem. Ta sama opóźnienie schłodzenia zachodzi zarówno po stronie klienta (blastClient), jak i serwera (blastServer), przy czym serwer działa jako źródło prawdy.
blastServer
local blasterConfig = getBlasterConfig(player)
local secondsBetweenBlasts = blasterConfig:GetAttribute("secondsBetweenBlasts")
task.delay(secondsBetweenBlasts, function()
local currentState = player:GetAttribute(PlayerAttribute.blasterStateServer)
if currentState == BlasterState.Blasting then
player:SetAttribute(PlayerAttribute.blasterStateServer, BlasterState.Ready)
end
end)
Atrybut secondsBetweenBlasts jest częścią konfiguracji blastera w ReplicatedStorage ⟩ Instances ⟩ LaserBlastersFolder. Po upływie opóźnienia secondsBetweenBlasts gracz może ponownie strzelić, a cały proces powtarza się. Aby pomóc graczowi zrozumieć, kiedy może ponownie strzelić, doświadczenie zawiera pasek schłodzenia.
Na tym etapie gracze mogą pojawiać się i odradzać, celować i strzelać, ale doświadczenie nadal musi określić wyniki każdego strzału. W następnej części samouczka dowiesz się, jak zaprogramować zdolność blastera do wykrywania, kiedy strzał trafia innego gracza, a następnie redukować odpowiednią ilość zdrowia gracza zgodnie z ustawieniami blastera.