Implementacja zachowania blastera

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

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

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.

UserInputHandler

ContextActionService: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 ReplicatedStorageBlasterattemptBlastClient, 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 ReplicatedStorageBlastercanLocalPlayerBlast, aby wykonać prostą kontrolę atrybutu gracza blasterStateClient:

canLocalPlayerBlast

local function canLocalPlayerBlast(): boolean
return localPlayer:GetAttribute(PlayerAttribute.blasterStateClient) == BlasterState.Ready
end

Jeśli zbadujesz ReplicatedStorageBlasterBlasterState, 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 ReplicatedStorageattemptBlastClientblastClient. 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 ReplicatedStorageBlasterBlastData, 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 ReplicatedStorageattemptBlastClientblastClientgenerateBlastData, 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 ReplicatedStorageInstancesLaserBlastersFolder.

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:

blastClient

local laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEvent
local laserBlastedEvent = ReplicatedStorage.Instances.LaserBlastedEvent
laserBlastedBindableEvent:Fire(blastData)
laserBlastedEvent:FireServer(blastData)

BindableEvent powiadamia inne skrypty klientów o eksplozji. Na przykład, ReplicatedStorageFirstPersonBlasterVisuals 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 ServerScriptServiceLaserBlastHandler.

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ą:

  1. Czy BlastData to tabela? Czy zawiera Class.CFrame i inną tabelę o nazwie rayResults?
  2. Czy gracz ma wyposażony blaster?
  3. Czy gracz ma postać i lokalizację w świecie?
  4. 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.

getValidatedBlastData

local distanceFromCharacterToOrigin = blastData.originCFrame.Position - rootPartCFrame.Position
print(distanceFromCharacterToOrigin.Magnitude) -- zaktualizowana linia, upewnij się, że usuniesz
if distanceFromCharacterToOrigin.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS then
warn(`Gracz {player.Name} nie przeszedł testu wiarygodności pochodzenia podczas strzału`)
return
end

Podczas ruchu i strzału zwróć uwagę na wynik. Może to wyglądać mniej więcej tak:


1.9019629955291748
3.1549558639526367
2.5742883682250977
4.8044586181640625
2.6434271335601807

Jeśli zwiększysz prędkość ruchu dla graczy w ReplicatedStoragePlayerStateHandlertogglePlayerMovement, a następnie ponownie przetestujesz, prawdopodobnie napotkasz wiele nieudanych kontroli z powodu nadmiernego ruchu między wystrzałami.

togglePlayerMovement

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

©2026 Roblox Corporation. Nazwa Roblox, logo Roblox oraz hasło „Powering Imagination” należą do naszych zarejestrowanych i niezarejestrowanych znaków towarowych na terenie Stanów Zjednoczonych oraz w innych krajach.