Wykrywanie trafień

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

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:

  1. Fizycznie simulowany test, czy pocisk trafił w cel.
  2. 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ą prosto
table.insert(directions, originCFrame.LookVector)
elseif numLasers > 1 then
-- Dla wielu laserów rozprzestrzeniaj je równomiernie poziomo
-- na przekroju laserowegoSpreadDegrees wokół centrum
local leftAngleBound = laserSpreadDegrees / 2
local rightAngleBound = -leftAngleBound
local degreeInterval = laserSpreadDegrees / (numLasers - 1)
for angle = rightAngleBound, leftAngleBound, degreeInterval do
local direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVector
table.insert(directions, direction)
end
end

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 then
table.insert(directions, originCFrame.LookVector)
elseif numLasers > 1 then
local leftAngleBound = laserSpreadDegrees / 2
local rightAngleBound = -leftAngleBound
local degreeInterval = laserSpreadDegrees / (numLasers - 1)
for angle = rightAngleBound, leftAngleBound, degreeInterval do
local direction
if spreadDirection == "vertical" then
direction = (originCFrame * CFrame.Angles(math.rad(angle), 0, 0)).LookVector
else
direction = (originCFrame * CFrame.Angles(0, math.rad(angle), 0)).LookVector
end
table.insert(directions, direction)
end
end
return 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 linia
print(direction) -- nowa linia
end -- nowa linia
local 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,

A diagram where Ray A continues through the wall, and Ray B collides with the wall.

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

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_DISTANCE
destination = CFrame.lookAt(distantPosition, distantPosition - rayDirection)
taggedPlayer = nil
end

Zweryfikuj eksplozję

Aby zapobiec oszustwom poprzedni rozdział implementowanie laserów wyjaśnia, jak blastClient informuje serwer o eksplozji używając Class.Remote

  1. Najpierw, getValidatedRayResults wzywa validateRayResult, aby sprawdzić, czy każda pozycja w tabeli rayResults z klienta jest 1> Datatype.CFrame1> i 4> Player4> (lub nil).

  2. 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:

    isRayAngleFromOriginZweryfikowany

    local claimedDirection = (rayResult.destination.Position - originCFrame.Position).Unit
    local directionErrorDegrees = getAngleBetweenDirections(claimedDirection, expectedDirection)
    return directionErrorDegrees <= ToleranceValues.BLAST_ANGLE_SANITY_CHECK_TOLERANCE_DEGREES

    Roblox 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 kierunkami

    local 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)
    end
  3. Nastę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ł

    isPlayerNearPosition

    local distanceFromCharacterToPosition = position - character:GetPivot().Position
    if distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS then
    return false
    end
  4. Ostatni 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

    istniejeObustronny

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