Grundlegende Spielmechaniken

*Dieser Inhalt wurde mit KI (Beta) übersetzt und kann Fehler enthalten. Um diese Seite auf Englisch zu sehen, klicke hier.

Die folgenden Systeme waren die Grundlage für die Einrichtung des Gameplays, das wir für The Mystery of Duvall Drive wollten.

Spielzustands-Manager

Die GameStateManager / GameStateClient ist wahrscheinlich das kompliziertste System in der Erlebnis, da es sich um:

  • Startet Spieler innerhalb der Lobby, startet den Countdown, um die Gruppe in den Hauptspielbereich zu teleportieren, und teleportiert Spieler auf reservierte Server.
  • Klonen Sie korrupte Räume, synchronisieren Sie sie und teleportieren Sie Spieler von und zu einer bestimmten zugewiesenen CFrame -Koordinaten.
  • Grabungs- und Platzierungsmechaniken.
  • Türen sperren und entriegeln.
  • Initialisierung des finalen Teleports zum Foyer und Spielen der finalen Schneide.

Wir implementierten es als einfache Zustandsmaschine (Update-Funktion), und die Zustände sind in DemoConfig (GameStates-Ensemble). Einige Zustände behandeln den ursprünglichen teleportieren, während andere Zustände das Finden einer Mission, das Auslösen des Rätsels und das Lösen der Mission betrachten. Beachten Sie, dass neben dem Siegeln versucht wurde, keine mission spezifische Code im GameStateManager zu haben.

GameStates ist in erster Linie auf dem Client-Server-Seite, aber wenn der Client etwas tun muss, z. B. Countdown anzeigen, Ratschlag oder Streaming-Pause deaktivieren, kommuniziert der Client/Server (GameStateClient) via ein Remote-Ereignis namens GameStateEvent. Wie bei den meisten Fällen hat das Ereignis-Objekt "eingeben" (Config.GameEvents) als erste Argument den Ereignis-Typ "GameState" .

Teleportation-Spiel-Status

Es gibt eine Gruppe von 3 Spielzuständen, die drei einzigartige Cutscenen ausführen, die die Teleportation in den korrupten Raum verbergen: Warmup, In

Während das Streaming verarbeitet wird, wird InFlight ausgeführt, wobei ein leicht pulsierender dunkler Bildschirm beibehalten wird. Wenn beide Class

Eine ähnliche Reihe von Warmup, InFlight und Cooldown-Cutscenen tritt auf, wenn wir den Spieler in den normalen Zustand des Raums zurückkehren, TeleportWarmupBack , TeleportInFlightBack und Tele

Beleuchtungs- und Atmosphärene Spielzustände

Wir wussten, dass wir wollten, dass der normale und

Türperrektionsspielzustände

W

VeranstaltungsManager

EventManager hat es uns ermöglicht, "Aktionen" im Laufe der Zeit mit der Verwendung von Schlüsselrahmen, wie folgt zu führen:

  • Interpoling-Instanz-Eigenschaften und Attribute.
  • Skripte ausführen.
  • Spielt Audiodateien.
  • Kameraerschütterungen ausführen.

Wir würden in der Idee ein Werkzeug mit einer Track-basierten UI verwenden, aber für diese Demo haben wir die Schlüssel und Eigenschaftsnamen manuell eingegeben. Das EventManager-System besteht aus mehreren Skripts und einer Ereignis/Funktion, einschließlich:

  • EventManager - Gesamte Logik für das Erstellen und Beenden von Ereignissen, einschließlich Server-seitiger Aktionen.
  • EventManagerClient - Client-seitige Aktionen.
  • EventManagerModule - Common Code für beide serverseitige und clientseitige Aktionen.
  • EventManagerConfig - Kleiner Datei mit einigen Kommando-Erklärungen.
  • EventManagerDemo - Wo alle tatsächlichen Ereignisse für diese Demo im Spiel-spezifischen Skript, das. PL: die Skriptsdefiniert sind.
  • EventManagerEvent , EventManagerFunc - Remote-Ereignis und Bindungsfunktion, um Ereignisse vom Client oder Server auszuführen und zu stoppen. Dies ist, wie andere Systeme Ereignisse einrichten, ausführen und stoppen können.

Jedes Ereignis hat einen Namen, einen Abschnitt mit optionalen Informationen über die Abklingzeit, die Funktion zum Starten oder beenden, Ereignisparameter und Abschnitte mit Interpolanten (Interpolieren von beliebiger Anzahl von Eigenschaften oder Attributen im Laufe der Zeit), Skripte (registrierte Skripte bei Start oder Beenden), Kamererschüttelungen und Wiedergabe von Audiodateien.

Interpolation

Interpolation ermöglicht es Objekt-Eigenschaften und -Attributen, ohne getrennt zwischen Schlüssel-Frame zu springen, einheitlich zu ändern, von einem Wert zu einem anderen zu springen; zum Beispiel, das folgende Code-Snippet zeigt, wie wir die TextLabel.TextTransparency


interpolants = {
objectParam = "TextLabel",
property = "TextTransparency",
keys = {
{value = 1},
{time = .5, value = 0},
{time = 2.25, value = 0},
{time = 3, value = 1}
}
}

Während wir definieren konnten, zu welchem Objekt-Eigenschaft oder -Attribut sich eine bestimmte Ereignis auf verschiedenen "Objekt-Gruppen" bezieht, wollten wir be ableben, die gleichen Ereignisse auf verschiedenen "Objekt-Gruppen" wiederzuverwenden, um es mit dem Streaming auf dem Client und mit Objekten, die zur Laufzeit erstellt wurden, zu verwenden.


object = workspace.SomeFolder.SomeModel

Um dies zu erreichen, haben wir erlaubt, mit dem Objekt-Namen zu verknüpfen und dem Ereignis startenanzugeben, und haben erlaubt, benutzerdefinierte "Stamm" für das Ereignis zu spezifizieren, die Objek unter diesem Root finden können, wenn das Ereignis gestartet wird. Zum Beispiel, in dem folgenden Code-Snippet, versucht der


params = {
["RootObject"] = workspace.Content.Interior.Foyer["Ritual-DemoVersion"],
},
interpolants = {
objectName = "Wander",
attribute = "TimeScale",
keys = {
{value = 0.2}
}
}

Wir haben die Angaben in der Registerkarte Einstellungen erlaubt, und die Skripte, auf die auf der Ereigniseite ausgeführte Skripte, können entweder vorhandene Einstellungen ändern oder mehr Einstellungen in der "Einstellungen"-Tabelle hinzufügen. Im folgenden Beispiel haben


params = {
isEnabled = false
},
interpolants = {
{
objectName = "FocuserGlow",
property = "Enabled",
keys = {
{valueParam = "isEnabled"}
}
}

Mit Parametern können wir auf Objekte verweisen, die nicht einmal am Anfang des Erlebnisses existieren. Zum Beispiel, in dem folgenden Codebeispiel wird eine Funktion ausgeführt, die auf den Ereigniseinbruch wartet, und ein Objekt erstellt, und die BlackScreenObject-Eintrag in den Parametern, um auf das erstellte Objekt zu zeigen.


{objectParam = "BlackScreenObject",
property = "BackgroundTransparency",
keys = {
{value = 0},
{time = 19, value = 0},
{value = 1},
}}

Veranstaltungen, Ereignis-Instanzen und Verbindung mit Triggern

Um ein Ereignis auszuführen, verwenden wir entweder ein Remote-Ereignis von Clients oder eine Funktion vom Server. Im folgenden Beispiel übergeben wir einige Parameter an das RootObject und isEnabled-Ereignisse. Intern wurde eine Instanz der Event-Beschreibung erstellt, die Paramètres auf aktuelle Objekte gelöst und die Funktion eine ID für die Event-Instanz zurückgab.


local params = {
RootObject = workspace.Content.Interior.Foyer["Ritual-DemoVersion"]["SealDropoff_" .. missionName],
isEnabled = enabled
}
local eventId = eventManagerFunc:Invoke("Run", {eventName = "Ritual_Init_Dropoff", eventParams = params} )

Wir könnten ein Ereignis stoppen, indem wir die Funktion "Stop" aufrufen:


eventManagerFunc:Invoke("Stop", {eventInstId = cooldownId} )

Interpolants oder andere Aktionen, die "kosmetisch" sind (ändern Sie die Simulation für alle Spieler nicht), können auf Clients ausgeführt werden, was zu einer glättenden Interpolation führen kann. In der Beschreibungkönnen wir einen Standardwert für alle Aktionen als "wahr" festlegen (ohne ihn ist der Standard "Client"). Jede Aktion kann ihn durch Festlegung seiner eigenen auf Server überschreiben.

Um ein Ereignis leicht mit einem Auslöserzu verbinden, verwendeten wir Hilfsfunktionen ConnectTriggerToEvent oder ConnectSpawnedTriggerToEvent, wobei der letztere den Trigger nach Namen findet. Um dasselbe Ereignis mit verschiedenen Triggern ausgelöst wird, können wir eventManagerFunc mit einem "Setup"-

Ereignis-Parameter

Neben benutzerdefinierten Ereignamechanismen, die von Skripten weitergegeben werden, enthalten andere Daten, die optionaleweise beim Erstellen eines Ereignisses weitergegeben werden können, einschließlich Spieler:in, Rückruf (zum Ausführen wenn das Ereignis endet), und Rückrufparameter. Einige Ereignisse sollten nur für einen Spieler ausgeführt werden (Ereignisse mit Aktionen auf dem Client), während andere für Alleausgeführt werden sollten. Um es für einen Spieler:inzu aktivieren, verwendeten

Ereignisse können Kühlzeiten definieren, die von minCooldownTime und maxCooldownTime . Die min und max bieten eine Reichweite für die Skalierung basierend auf der Anzahl der Spieler:in, aber wir haben es in dieser Demo nicht verwendet. Wenn wir K

Skripte aufrufen

Wir könnten Scripts an bestimmten Keyboards in der Scripts -Sektion nennen. Zum Beispiel:


scripts = {
{startTime = 2, scriptName = "EnablePlayerControls", params = {true}, onServer = false }
}

Im vorherigen Beispiel müsste der EnablePlayerControls Class.Script mit dem Ereignis-Manager-Modul registriert sein, wie folgt:


emModule.RegisterFunction("EnablePlayerControls", EnablePlayerControls)

RegisterFunction muss im Client-Skript für Funktionen auf dem Client aufgerufen werden, und im Server-Skript für onServer = true. Die Funktion selbst erhält EventInstance und Argumente weitergegeben, aber in diesem Fall wird nur ein Parameter mit einem wahren Wert weitergegeben.


local function EnablePlayerControls(eventInst, params)

Audio abspielen

Wir haben begrenzte Unterstützung für das Spielen von nicht-positionalem Audio an Schlüssel帧 in der Sounds -Sektion, z. B.:


sounds = {
{startTime = 2, name = "VisTech_ethereal_voices-001"},
}

Beachten Sie, dass die Ereignish-Rückrufe ausgelöst werden, wenn die Event-Duration abläuft, aber die Audio-Aktionen möglicherweise immer noch abgespielt werden.

Kameraerschütterungen ausführen

Wir könnten Kameraschüttelungen in der Kameraschüttelungen -Sektion definieren, wie folgt:


cameraShakes = {
{startTime = 15, shake = "small", sustainDuration = 7, targets = emConfig.ShakeTargets.allPlayers, onServer = true},
}

Ziele können nur für den Spieler:in, der das Ereignis ausgelöst hat, allPlayer oder playersInRadius, oder für denjenigen, der das Ereignis ausgelöst hat, eingeleitet werden. Wir verwendeten ein 3rd-Party-Skript für Kamererschüttelungen und die Schüttelungen waren vordefiniert: eventManagerDemo.bigShake und eventManagerDemo.smallShake . sustain

Missions-Logik

Es gibt 7 Missionen insgesamt, und nur 6 von ihnen verwenden Siegel. Die meisten Missionen haben gemeinsame Parameter, obwohl einige nur für Missionen mit Seals und Teleportation in beschädigten Räumen sind. Jede Mission hat einen Eintrag im DemoConfig Skript mit einer Reihe von Parametern in der Config.Missions -Karte:

  • MissionRoot : Ein Ordner von allen nicht korrupten Versionen von Objekten.
  • Türen : Türen sperren, bis ein Spieler einen Siegel aufhebt.
  • SealName / SolvedSealName : Nicht korrupte Seals und korrupte Seal-Namen.
  • SealPlaceName : Orte, an denen Sie den Siegel platzieren können.
  • PlacedSealPlaceholderName : Platzhalter-Objekt an der Stelle, an der der Siegel platziert werden soll.
  • TeleportPositionsName : Name eines Ordners mit Platzhalter-Meshes, um Spieler-Teleportpositionen und Rotationen zu definieren, wenn sie sich zum beschädigten Raum bewegen und zurück in den normalen Bereich. Der gleiche Name wird in beiden Fällen verwendet.
  • CorruptRoomName : Namen der Wurzelordner (relativ zu ServerStorage) für die korrupten Zimmer. Korrupte Zimmer-Klone unter TempStorage.Cloned, wenn die Mission beginnt, und sie werden zerstört, wenn die Mission beendet ist.
  • MissionCompleteButtonName : Ein Cheat-Button in den korrupten Räumen, um die Mission sofort zu beenden. Dies ist für Debug-Zwecke .
  • CheatKey : Die gleiche Cheat wie eine Zahl oder CtrlShift[Number] .

Einige der Mission-Logik ist in den GameStateManager MissionsLogic 2> 5>Scripts5>, wie Seals und Türen den Hauptspielfluss für die meisten Missionen bieten, aber die meisten Mission-spezifischen Logiken in 8>MissionsLogic8> und 1>MissionsLogicClient1>

  • Verwenden Sie einen Schlüssel auf einem Schloss - Die erste Mission, um eine Tür zu öffnen. Dieser Typ wird von LockName , KeyName definiert.
  • Gegenstände übereinstimmen - 4 Missionen übereinstimmen mit Gegenständen. Dieser Typ wird durch MatchItems definiert.
  • Verkleiden eines Schaufensters mit mehreren Schichten Stoff - 1 Mission im Dachboden hat Spieler, die drei Artikel sammeln. Dieser Typ wird durch DressItemsTagList definiert.
  • Klicken Sie auf das Element, um es zu beenden - 1 Mission hat diesen eingeben, der von ClickTargetName definiert ist.

Jeder Mission-Typ hat seine eigene StartMissionFunc und CompleteMissionFunc. Die Startfunktion üblicherweise liest Argumente aus der MatchItem-K

Die Matching-Items-Logik erlaubt es, "zu verwenden" (klicken Sie, während Sie halten) Gegenstände mit PuzzlePieceXX -Tags über Gegenstände mit PuzzleSlotYY -Tag. Es gibt einige Optionen, die als Argumente in MatchItems -Karte verfügbar sind (w

Greifen

Wir haben ein einfaches Grab-System entwickelt, um ein Objekt durch das Anziehen des Objekts an den rechten Arm des Charakters zu halten. Das Grab wird in

Auf jedem Frame überprüfen wir, ob ein Grabversuch im Gange ist. Wenn der Spieler innerhalb von reachDist ist, starten wir mit ToolHoldAnim . Wenn ein Spieler innerhalb von maxGrabDistance ist, sendet der Client eine Anfrage an den Server, um tatsächlich ein Modell zu graben ( 1> performGrab1> Funktion).

Server-seitiges Skript hat 2 Hauptfunktionen:

  • Greifen - Handelt sich um eine Client-Anfrage, um ein Modell zu greifen.
  • Loslassen - Handhabt die Anfrage, ein gefangenes Modell zu loszulassen.

Informationen darüber, was jedes Spielers hält, werden in playerInfos map gespeichert. In der Grab-Funktion überprüfen wir, ob dieses Modell bereits von einem anderen Spieler:inergriffen wurde. Wenn so - ein "EquipWorldFail" wird an den Client gesendet und kann den Grabversuch abbrechen. Beachten Sie, dass wir Situationen behandeln müssen, in denen Spieler verschiedene Teile des gleichen Model , und den Grabversuch

Wenn das Ziehen erlaubt ist, erstellt das Skript zwei Attachments, eines auf der rechten Hand und eines auf dem Objekt mit einem vergebenen Zugr

Für die Freigabe eines gebundenen Modells verbindet sich der Client-Skript mit der GrabReleaseButton -Schaltfläche in HUD-ScreenGUI. Eine Connected gibt ein Ereignis an den Server. Auf dem Server wird die Attachments und 2>Class.Limit|Limitations2>