Schläge zu erkennen ist der Prozess, bei dem Blasten mit Spielern kollidieren, und dann ihre Gesundheit entsprechend reduzieren. Auf einer hohen Ebene können Sie diesen Work als Folgendes betrachten:
- Ein physisch simulierter Check, ob ein Projektil das Ziel traf.
- Eine sofortige Prüfung, ob der Blaster auf das Ziel zielte.
Die Art der Treffererkennung, die Sie verwenden, hängt von den Spielanforderungen Ihres Erlebnisses ab. Zum Beispiel ist ein physisch simulierter Check für ein Dodgeball-Erlebnis geeignet, bei dem Bälle eine bestimmte Geschwindigkeit verlassen müssen, fallen, wenn sie durch die Luft fliegen, oder sich von Umgebungsfaktoren wie Gravitation und Windgeschwindigkeit ändern. Allerdings ist ein sofortiges Check ein besseres Match für ein Lasertag
Dieser Abschnitt des Tutorials leitet Sie in die Skripte ein, die hinter der Treffererkennung im Platzliegen, einschließlich Anleitung:
- Erhalte die Richtung der Explosion aus den aktuellen Kamerawerten und dem eingebendes Spieler:in.
- Wirft Strahlen in einem geraden Weg vom Blaster, während er schießt.
- Validiere die Explosion, um die Exploitation von Blaster-Daten zu verhindern.
- Reduzieren Sie die Gesundheit des Spielers gemäß dem Explosionsschaden von jedem Typ von Blaster und wie vielen Strahlen den Spieler:intreffen.
Nachdem Sie diesen Abschnitt abgeschlossen haben, können Sie weitere Entwicklungsforen erkunden, um Ihr Gameplay zu verbessern, z. B. Audiodateien, Beleuchtung und Spezialeffekte.
Blast Richtung erhalten
Nachdem ein Spieler seinen Blaster gesprengt hat, ReplicatedStorage > Blaster > attemptBlastClient > 1> blastClient 1> > 4> generateBlastData4> nennt zwei Funktionen, um den Treffer-Entdeckungsprozess zu starten: 7> rayDirections()7> und 0> rayResults()
erzeugenBlastData
local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)local rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)
Die Eingaben für rayDirections sind einfach: die aktuelle Kameraposition und Rotationswerte und der eingebendes Spieler:in. Wenn die Beispiel-Laser-Tag-Erfahrung nur Spieler mit Blastern, die einen einzigen Laserstrahl erzeugen, versorgt, würde ReplicatedStorage > Laser
Da der Probe jedoch ein zusätzlichen Blaster-Typ bereitstellt, der mehrere Laserstrahlen mit einer breit horizontalen Streuung erzeugt, muss getDirectionsForBlast die Richtung für jeden Laserstrahl der Streuung nach ihren Winkeln innerhalb der Blaster-Konfiguration berechnen:
erhalte Richtungen für Blast
if numLasers == 1 then-- Für einzelne Laser zielen sie geradetable.insert(directions, originCFrame.LookVector)elseif numLasers > 1 then-- Für mehrere Laser, verteilen Sie sie in horizontale Ausbreitung-- über ein Intervall laserSpreadDegrees um die Mittelocal 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
Um dieses Konzept weiter zu demonstrieren, wenn Sie einen dritten eingebenmit einer breit angelegten, vertikalen Streuung enthalten, könnten Sie ein neues Blaster-Attribut erstellen, z. B. spreadDirection , dann die Berechnung des CFrame anpassen, um eine andere Achse zu verwenden. Zum Beispiel beachten
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
Letztlich gibt die Funktion rayDirections() eine Tabelle von Vectors zurück, die die Richtung jedes Laserstrahls repräsentiert. Wenn es hilfreich ist, können Sie einige Logs hinzufügen, um ein Gefühl zu bekommen, wie diese Daten aussehen.
erzeugenBlastData
local rayDirections = getDirectionsForBlast(currentCamera.CFrame, blasterConfig)for _, direction in rayDirections do -- neue zeileprint(direction) -- neue zeileend -- neue zeilelocal rayResults = castLaserRay(localPlayer, currentCamera.CFrame.Position, rayDirections)
Cast Rays
castLaserRay() , die zweite Funktion in ReplicatedStorage > Blaster > 0> attemptBlastClient</
Diese Information ist besonders nützlich für First-Person-Shooter-Erlebnisse, da sie es ermöglicht, zu sehen, wann und wo Blast mit Spielern oder der Umgebung intersect. Zum Beispiel zeigt das folgende Bild zwei Strahlen, die parallel zueinander casten. Laut ihrem Herkunftspunkt und Richtung fehlt Ray A die Wand und fährt fort, bis sie ihre maximale Distanz erreicht, während Ray B mit der Wand kollidiert
Die castLaserRay() -Parameter bestimmen, dass Raycast() -Anrufe jeden Teil in der Arbeitsbereich except den Charakter, der gesprengt hat, berücksichtigen müssen. Das Skript castet dann einen Strahl für jede Richtung in der 2>directions2> -Tabelle. Wenn ein Strahl etwas trifft, erzeugt es ein 5>Datatype
- Distance – Die Entfernung zwischen der Ray-Origin und dem Intersection-Punkt.
- Material – Das Enum.Material an der Intersections-Stelle.
Der Instance-Wert ist der wichtigste dieser Eigenschaften für das Spiel des Laser-Tags auf der Probe-Laser-Tag-Erlebnistafel, da er kommuniziert, wenn Strahlen mit anderen Spieler:inkoll
castLaserRay() dann verwendet Position und Normal, um einen neuen 0> Datatype.CFrame
castLaserRay
if result then-- Die Explosion hat etwas getroffen, überprüfen Sie, ob es ein Spieler:inwar.destination = CFrame.lookAt(result.Position, result.Position + result.Normal)taggedPlayer = getPlayerFromDescendant(result.Instance)else-- Die Explosion hat nichts getroffen, also ist sein Ziel-- den punkt in seiner maximalen reichweite.local distantPosition = origin + rayDirection * MAX_DISTANCEdestination = CFrame.lookAt(distantPosition, distantPosition - rayDirection)taggedPlayer = nilend
Überprüfen Sie die Explosion
Um Cheating zu verhindern, erklärt der vorherige Kapitel Blaster- Skript, das. PL: die Skripts, wie blastClient den Server des Blastes mit einem Class.RemoteEvent benachrichtigt, damit
Zuerst, getValidatedRayResults nennt validateRayResult, um zu überprüfen, dass jedes Eintrag in der rayResults Tabelle des Clients ein 1> Datatype.CFrame1> und ein 4> Player4> (oder null) ist.
Nächste, es ruft isRayAngleFromOriginValid auf, um die erwarteten Winkel des Laser-Spreads mit denjenigen des Clients zu vergleichen. Dieser Code in particular zeigt den Vorteil der Verwendung von ReplicatedStorage, weil der Server aufrufen kann, getDirectionsForBlast selbst, speichern die Rückgabe als erwartete Daten und dann vergleichen Sie sie gegen die Daten des Clients
So wie die Blaster-Validierung aus dem vorherigen Kapitel, isRayAngleFromOriginValid basiert auf einem Toleranzwert, um festzustellen, was ein "übertriebener" Unterschied in Angewinkeln darstellt:
istRayAngleFromOriginGültiglocal claimedDirection = (rayResult.destination.Position - originCFrame.Position).Unitlocal directionErrorDegrees = getAngleBetweenDirections(claimedDirection, expectedDirection)return directionErrorDegrees <= ToleranceValues.BLAST_ANGLE_SANITY_CHECK_TOLERANCE_DEGREESRoblox-Abstraktionen entfernen die meisten beteiligten Mathe-Bit, sodass das Ergebnis eine kurze, wiederverwendbare Hilfsfunktion mit Anwendbarkeit auf eine Reihe von Erlebnissen ist:
erhaltenAngleBetweenDirectionslocal 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)endDer nächste Check ist der intuitivste. Während getValidatedBlastData``DISTANCE_SANITY_CHECK_TOLERANCE_STUDS verwendet, um zu überprüfen, dass der Spieler, der gesprengt wurde, in der Nähe des Strahlen-Punkts verwendet, isPlayerNearPosition verwendet identische Logik, um zu überprüfen, ob der mark
istPlayerNearPositionlocal distanceFromCharacterToPosition = position - character:GetPivot().Positionif distanceFromCharacterToPosition.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS thenreturn falseendDie finale Prüfung isRayPathObstructed verwendet eine Variation der Ray-Cast-Operation, um zu überprüfen, ob die Zielperson des Rays hinter einer Wand oder einer anderen Störung von der Position des Clients steht. Zum Beispiel, wenn ein böser Spieler alle Wände aus der Erfahrung entfernt, um andere Spieler zu markieren, würde der Server überprüfen und bestätigen, dass die Rays ungültig sind, da er jede Objektposition innerhalb der Umgebung
istRayPathObstruiertlocal scaledDirection = (rayResult.destination.Position - blastData.originCFrame.Position)scaledDirection *= (scaledDirection.Magnitude - 1) / scaledDirection.Magnitude
Keine Anti-Exploit-Strategie ist umfassend, aber es ist wichtig zu berücksichtigen, wie böswillige Spieler Ihr Erlebnis ansprechen können, damit Sie Checks einrichten können, die der Server ausführen kann, um verdächtiges Verhalten zu erkennen.
Reduzieren Sie die Gesundheit des Spielers
Nachdem ein Spieler einen anderen Spieler:ingetaggt hat, sind die letzten Schritte in der Erstellung des Hauptspiel循ops in der Beispiel-Laser-Tag-Erlebniss die Reduzierung der Gesundheit des getaggeden Spieler:in, das Erhöhen des Bestenlisten und das Respawnen des Spielers in die Runde.
Starting with reducing the tagged Spieler:in's health, Spawning and Respawning covers the distinction between Player and Player.Character, specifically that a
Die Erfahrung speichert Schadenswerte im damagePerHit-Attribut jedes Blasters. Zum Beisp
Health nimmt keine negativen Werte an, so dass onPlayerTagged etwas Logik hat, um die Spielergesundheit bei oder über der 0 zu halten. Nach dem Überprüfen, dass die Spielergesundheit über der 0 ist, vergleicht sie Gesundheit mit damagePerHit
Dieser Ansatz des Problems scheint etwas verwirrend zu sein. Zum Beispiel, warum nicht einfach die Gesundheit des Spielers auf Null setzen, wenn es negativ ist? Der Grund ist, weil das Setzen von Gesundheitswerten das Kraftfeld umgeht. Mit der Methode Humanoid:TakeDamage() garantiert die Verwendung der Methode Class.Humanoid:TakeDamage, dass Spieler keine Schaden erleiden, während ihre Kraftfelder aktiv sind.
aufPlayerTagged
local function onPlayerTagged(playerBlasted: Player, playerTagged: Player, damageAmount: number)
local character = playerTagged.Character
local isFriendly = playerBlasted.Team == playerTagged.Team
-- Unerlaubtes freundliches initiieren
if isFriendly then
return
end
local humanoid = character and character:FindFirstChild("Humanoid")
if humanoid and humanoid.Health > 0 then
-- Vermeiden Sie negative Gesundheit
local damage = math.min(damageAmount, humanoid.Health)
-- TakeDamage gewährleistet, dass die Gesundheit nicht gesenkt wird, wenn ForceField aktiv ist
humanoid:TakeDamage(damage)
if humanoid.Health <= 0 then
-- Erweist den Spieler mit einer Explosion einen Punkt für das Markieren von PlayerTagged
Scoring.incrementScore(playerBlasted, 1)
end
end
end
Der nächste Schritt ist es, die Bestenlistezu erhöhen. Es mag unnötig gewesen sein, dass LaserBlastHandler den Spieler einschließen, der neben den Blast-Daten gesprengt wurde, aber ohne diese Information kann die Erfahrung nicht den Spieler mit dem Taggen anzeigen. Schließlich respawns der getagged-aus Player in der Runde, die Sie in Spawnen und Respawnen überprüfen können.
Die fünf Kapitel in diesem Curriculum umfassen die Kern-Spielweise des Erlebnisses, aber es gibt immer noch viele Bereiche zu erkunden, wie z. B.:
- Blaster- visuellen : Siehe ReplicatedStorage > FirstPersonBlasterVisuals und 0> ServerScriptService 0> > 3> ThirdPersonBlasterVisuals 3> .
- Audio : Siehe ReplicatedStorage > SoundHandler .
- Benutzerdefinierte Modi : Wie könnten Sie dieses Erlebnis modifizieren, um neue Arten von Zielen einzuführen, wie z. B. das Erzielen der meisten Punkte vor Ablauf der Zeit?
Für erweiterte Spiellogik für das Lasertag-Erlebnis sowie für wiederverwendbare, hochwertige Umwelt-Assets, überprüfen Sie die Lasertag-Vorlage.