Wykrywanie trafień to proces identyfikacji, gdy kulki zderzają się z graczami, a następnie zmniejszania ich zdrowia odpowiednio. Na poziomie wysokim można to postrzegać jako:
- Fizycznie simulowany test, czy pocisk trafił w cel.
- Natychmiastowy czek, czy blaster był skierowany na cel.
Typ wykrywania kolizji, który używasz, zależy od wymagań gry w Twoim doświadczeniu. Na przykład, fizycznie simulowany czek jest odpowiedni dla doświadczenia kulki, w której kulki muszą opuścić rękę w pewnej prędkości, upuść, gdy poruszają się przez powietrze lub zmienić kierunek z kondycji pogodowych. Ale natychmiastowy czek jest le
Używając do referencji doświadczenie laserowe, ta sekcja samoucznia uczy cię o skryptach za detekcji trafień w przestrzeni 3D, w tym kierunkach:
- Otrzymywanie kierunku eksplozji z aktualnych wartości kamery i wpisywaćblastera gracza.
- Kształtowanie promieni w prostej drodze od blastera, gdy wybucha.
- Zweryfikowanie eksplozji, aby zapobiec wykorzystaniu danych Blastera.
- Redukcja zdrowia gracza zgodnie z obrażeniami eksplozyjnymi każdego rodzaju blastera i liczbą trafień w gracza.
Po zakończeniu tej sekcji możesz zbadać dodatkowe tematy rozwoju, aby poprawić swoją rozgrywka, takie jak dźwięk, oświetlenie i efekty specjalne.
Zdobądź kierunek eksplozji
Po wybuchu blastera gracza, ReplicatedStorage > Blaster > attemptBlastClient > 1> blastClient1> > 4> generateBlastData4> wzywa dwa funkcje, aby rozpocząć proces wykrywania trafień: 7> rayDirections()7> i 0> rayResults()0>
GenerujBlastData
local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)
Wejścia dla rayDirections są proste: pozycja kamer i wartości obrotu, a wpisywaćblastra gracza. Jeśli doświadczenie laserowe tagu próbowało udzielić graczom lasery, które produkują pojedynczy promień laserowy, ReplicatedStorage > LaserRay
Jednak, ponieważ próbka dostarcza dodatkowy typ blastera, który wytwarza kilka laserowych promieni z szerokim rozprzestrzenianiem, getDirectionsForBlast musi obliczyć kierunek dla każdego promienia laserowego z rozprzestrzeniania według ich kątów w konfiguracji blastera:
dostań kierunki dla eksplozji
if numLasers == 1 then-- Dla pojedynczych laserów celują prostotable.insert(directions, originCFrame.LookVector)elseif numLasers > 1 then-- Dla wielu laserów rozprzestrzeniaj je równomiernie poziomo-- na przekroju laserowegoSpreadDegrees wokół centrumlocal leftAngleBound = laserSpreadDegrees / 2local rightAngleBound = -leftAngleBoundlocal degreeInterval = laserSpreadDegrees / (numLasers - 1)for angle = rightAngleBound, leftAngleBound, degreeInterval dolocal direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVectortable.insert(directions, direction)endend
Aby pokazać ten koncept dalej, jeśli włączysz trzeci typ blastera z szerokim rozprzestrzenieniem, vertikalnym rozprzestrzenieniem, możesz stworzyć nowy atribut wpisywać, takich jak spreadDirection , a następnie dostosować rozmiar CFrame do uży
if numLasers == 1 thentable.insert(directions, originCFrame.LookVector)elseif numLasers > 1 thenlocal leftAngleBound = laserSpreadDegrees / 2local rightAngleBound = -leftAngleBoundlocal degreeInterval = laserSpreadDegrees / (numLasers - 1)for angle = rightAngleBound, leftAngleBound, degreeInterval dolocal directionif spreadDirection == "vertical" thendirection = (originCFrame * CFrame.Angles(math.rad(angle), 0, 0)).LookVectorelsedirection = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVectorendtable.insert(directions, direction)endendreturn directions
Ostatecznie funkcja rayDirections() powoduje, że zwraca tabelę Vectors, która reprezentuje kierunek każdego promienia laserowego. Jeśli jest to pomocne, możesz dodać trochę dzienników, aby uzyskać poczucie tego, jak wygląda to dane.
GenerujBlastData
local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)for _, direction in rayDirections do -- nowa liniaprint(direction) -- nowa liniaend -- nowa linialocal rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)
Kształtuj promienie
castLaserRay() , druga funkcja w ReplicatedStorage > Blaster > 0> attemptBlastClient
Ta informacja jest szczególnie przydatna dla doświadczeń strzelca pierwszej osoby, ponieważ umożliwia ujrzenie, kiedy i gdzie kulki kolidują z graczami lub środowisko. Na przykład poniższy obraz pokazuje dwa promienie, które są równoległe do siebie. Według ich punktu pochodzenia i kierunku, Ray A przegapi ścianę i kontynuuje,
Parametry castLaserRay() określają, że Raycast() wezwania muszą rozważyć każdą część w przestrzeni roboczej poza postacią, która eksplodowała. Skrypt wtedy generuje promień dla każdej kierunku w tabeli 2>dir
- Distance – Dystans między źródłem promienia a punktem interwencji.
- Material – The Enum.Material at the intersection point.
Wartość Instance jest najbardziej krytyczną z tych właściwości dla gry w przypadku laserowego tagu próbkowego, ponieważ komunikuje się, gdy promienie kolidują z
castLaserRay() Następnie używa Position i Normal, aby stworzyć nowy 0> Datatype
castLaserRay
if result then-- Wybuch trafił w coś, sprawdź, czy to była gracz.destination = CFrame.lookAt(result.Position, result.Position + result.Normal)taggedPlayer = getPlayerFromDescendant(result.Instance)else-- Wybuch nie dotknął nic, więc jego miejscem docelowym jest-- punkt w jego maksymalnej odległości.local distantPosition = origin + rayDirection * MAX_DISTANCEdestination = CFrame.lookAt(distantPosition, distantPosition - rayDirection)taggedPlayer = nilend
Zweryfikuj eksplozję
Aby zapobiec oszustwom poprzedni rozdział implementowanie laserów wyjaśnia, jak blastClient informuje serwer o eksplozji używając Class.Remote
Najpierw, getValidatedRayResults wzywa validateRayResult, aby sprawdzić, czy każda pozycja w tabeli rayResults z klienta jest 1> Datatype.CFrame1> i 4> Player4> (lub nil).
Następnie wzywa isRayAngleFromOriginValid, aby porównać oczekiwane kąty promienia laserowego z tymi z klienta. Ten kod w szczególności pokazuje korzyść użycia ReplicatedStorage, ponieważ serwer może wezwywać getDirectionsForBlast sam, przechowywać dane zwracane jako "wartość oczekiwana" i następ
Tak jak poprzedni rozdział weryfikacji blastera, isRayAngleFromOriginValid opiera się na wartości tolerancji, aby określić, co stanowi "nadmierną" różnicę w kątach:
isRayAngleFromOriginZweryfikowanylocal claimedDirection = (rayResult.destination.Position - originCFrame.Position).Unitlocal directionErrorDegrees = getAngleBetweenDirections(claimedDirection, expectedDirection)return directionErrorDegrees <= ToleranceValues.BLAST_ANGLE_SANITY_CHECK_TOLERANCE_DEGREESRoblox abstrakcja najbardziej zaangażowanych bitów matematyki, więc wynik jest krótką, bardzo ponownorozkładalną funkcję pomocniczą z zastosowaniem na zestawienie z różnymi doświadczeniami:
dostać kąt między kierunkamilocal function getAngleBetweenDirections(directionA: Vector3, directionB: Vector3)local dotProduct = directionA:Dot(directionB)local cosAngle = math.clamp(dotProduct, -1, 1)local angle = math.acos(cosAngle)return math.deg(angle)endNastępny test jest najbardziej intuicyjny. Meskie getValidatedBlastData używa DISTANCE_SANITY_CHECK_TOLERANCE_STUDS do weryfikacji, że gracz, który rozbił, był blisko punktu pochodzenia promienia, isPlayerNearPosition używa identycznej logiki do sprawdzenia, czy oznaczony gracz był
isPlayerNearPositionlocal distanceFromCharacterToPosition = position - character:GetPivot().Positionif distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS thenreturn falseendOstatni test isRayPathObstructed używa wariacji operacji rzucania promieni, aby sprawdzić, czy miejsce docelowe promieni znajduje się za murem lub inną barierą od pozycji klienta. Na przykład, jeśli złośliwy gracz usunął systematycznie wszystkie ściany z doświadczenia, aby oznaczyć innych graczy, serwer sprawdziłby i potw
istniejeObustronnylocal scaledDirection = (rayResult.destination.Position - blastData.originCFrame.Position)scaledDirection *= (scaledDirection.Magnitude - 1) / scaledDirection.Magnitude
Żadna strategia anty-exploitu nie jest kompletnym, ale ważne jest, aby rozważyć, jak złodzieje mogą podejść do twojego doświadczenia, abyś mógł postawić czynniki sprawdzania, które serwer może uruchomić do zgłoszenia podejrzenia.
Redukuj zdrowie gracza
Po weryfikacji, że gracz oznaczył innego gracza, najważniejsze kroki w ukończeniu głównego cyklu gry w wersji laserowej są zmniejszenie zdrowia gracza, zwiększenie wyników w tabeli rankingi ponowne pojawienie gracza w rundzie.
Zaczynając od zmniejszania zdrowia oznaczonego gracza, Spawning and Respawning pokrywa różnicę między Player
Doświadczenie przechowuje wartości szkód w atrybutie damagePerHit każdego bl
Health nie akceptuje negatywnych wartości, więc onPlayerTagged ma trochę logiki, aby utrzymać zdrowie gracza na poziomie lub powyżej 0. Po weryfikacji, że zdrowie gracza jest powyżej 0, porównuje zdrowie
Ten sposób podejścia do problemu może wydawać się trochę skomplikowany. Na przykład, dlaczego nie ustawić zdrowia gracza na zero, jeśli byłby ujemny? Powodem jest, że ustawianie wartości zdrowia uniemożliwia używanie pola sił. Używając metody Humanoid:TakeDamage() zapewnia, że gracze nie biorą obrażeń, gdy ich pola sił są aktywne.
naTagu gracza
local function onPlayerTagged(playerBlasted: Player, playerTagged: Player, damageAmount: number)
local character = playerTagged.Character
local isFriendly = playerBlasted.Team == playerTagged.Team
-- Niedozwolony przyjazny ogień
if isFriendly then
return
end
local humanoid = character and character:FindFirstChild("Humanoid")
if humanoid and humanoid.Health > 0 then
-- Unikaj ujemnego zdrowia
local damage = math.min(damageAmount, humanoid.Health)
-- TakeDamage gwarantuje, że zdrowie nie jest zmniejszone, jeśli ForceField jest aktywny
humanoid:TakeDamage(damage)
if humanoid.Health <= 0 then
-- Nagroda graczuBlasted punkt za oznaczenie graczaTagged
Scoring.incrementScore(playerBlasted, 1)
end
end
end
Następnym krokiem jest zwiększenie ranking. To może wydawać się niepotrzebne dla LaserBlastHandler, aby uwzględnić gracza, który eksplodował obok danych eksplozji, ale bez tych informacji, doświadczenie nie może kredytować gracza z tagowania kogoś. Następnie, tagowane gracze respawnują w rundzie, którą można zobac
Pięć rozdziałów w tej książce dotyczy głównej gry w doświadczenia, ale nadal jest wiele obszarów do zbadań, takich jak:
- Wizualia Blastera : See ReplicatedStorage > FirstPersonBlasterVisuals i 0> ServerScriptService0> > 3> ThirdPersonBlasterVisuals 3> .
- Dźwięk : Zobacz ReplicatedStorage > SoundHandler .
- Niestandardowe tryby : Jak możesz zmodyfikować ten doświadczenie, aby wprowadzić nowe rodzaje celów, takie jak zdobycie najwięcej punktów przed upływem czasu?
Dla rozszerzonej logiki gry dla doświadczenia laser tag, a także w pełni rezygnuj zasobów środowiska, przeglądaj Laser Tag szablon.