Erstellen einer einfachen Game-Engine mit React Native

Erstellen einer einfachen Game-Engine mit React Native

Einführung

Heute lernen wir, wie man mit React Native ein Spiel erstellt. Da wir React Native verwenden, ist das Spiel plattformübergreifend, d. h. Sie können dasselbe Spiel auf Android, iOS und im Internet spielen. Heute konzentrieren wir uns jedoch nur auf Mobilgeräte. Also, lasst uns anfangen.

Erste Schritte

Um ein Spiel zu erstellen, benötigen wir eine Schleife, die unser Spiel während des Spielens aktualisiert. Diese Schleife ist für einen reibungslosen Spielablauf optimiert und hierfür verwenden wir die Spiel-Engine React Native.

Lassen Sie uns zunächst mit dem folgenden Befehl eine neue React Native-App erstellen.

npx react-native init ReactNativeGame

Sobald das Projekt erstellt ist, müssen wir eine Abhängigkeit hinzufügen, damit wir die Spiel-Engine hinzufügen können.

npm i -S reagieren-native-game-engine

Dieser Befehl fügt unserem Projekt die React Native-Spiel-Engine hinzu.

Also, was für ein Spiel werden wir machen? Der Einfachheit halber machen wir ein Spiel mit einer Schlange, die Essensreste frisst und dabei in die Länge wächst.

Eine kurze Einführung in die React Native-Spiel-Engine

React Native Game Engine ist eine leichtgewichtige Game-Engine. Es enthält eine Komponente, die es uns ermöglicht, Arrays von Objekten als Entitäten hinzuzufügen, damit wir mit ihnen arbeiten können. Zum Schreiben unserer Spiellogik verwenden wir eine Reihe von Systemeigenschaften, mit denen wir Entitäten (Spielobjekte) manipulieren, Berührungen erkennen und viele andere tolle Details nutzen können, die uns bei der Erstellung eines einfachen, funktionalen Spiels helfen.

Lassen Sie uns ein Schlangenspiel in React Native erstellen

Um ein Spiel zu erstellen, benötigen wir eine Leinwand oder einen Container, in den wir unsere Spielobjekte einfügen. Um eine Leinwand zu erstellen, fügen wir einfach eine Ansichtskomponente mit einem Stil hinzu, wie folgt.

// App.js     
<Ansichtsstil={styles.canvas}>
</Anzeigen>

Wir können unsere Stile wie folgt hinzufügen.

const Stile = StyleSheet.erstellen({
  Leinwand:
    flex: 1,
    Hintergrundfarbe: "#000000",
    alignItems: "zentriert",
    justifyContent: "zentriert",
  }
});

Im Canvas verwenden wir die GameEngine-Komponente und einige Stile aus der React Native Game Engine.

importiere { GameEngine } von „react-native-game-engine“;
importiere React, { useRef } von "react";
Konstanten aus "./Constants" importieren;


exportiere Standardfunktion App() {
  const BoardSize = Konstanten.GRID_SIZE * Konstanten.CELL_SIZE;
  const engine = useRef(null);
  zurückkehren (
    <Ansichtsstil={styles.canvas}>
      <SpielEngine
              ref={Motor}
              Stil={{
                Breite: Boardgröße,
                Höhe: Brettgröße,
                flex: null,
                Hintergrundfarbe: "weiß",
              }}
            />
    </Anzeigen>
);

Wir verwenden auch den useRef()-React-Hook, um der Spiel-Engine einen Verweis zur späteren Verwendung hinzuzufügen.

Wir haben außerdem im Stammverzeichnis des Projekts eine Datei Constants.js erstellt, um unsere Konstantenwerte zu speichern.

// Konstanten.js
importiere {Dimensionen} von „react-native“;
Standard exportieren {
  MAX_WIDTH: Dimensionen.get("Bildschirm").width,
  MAX_HEIGHT: Dimensions.get("Bildschirm").Höhe,
  Rastergröße: 15,
  Zellengröße: 20
};

Sie werden feststellen, dass wir ein 15 x 15 großes Raster erstellen, auf dem sich unsere Schlange bewegen wird.

An diesem Punkt ist unsere Spiel-Engine so eingerichtet, dass die Schlange und ihr Futter angezeigt werden. Wir müssen der GameEngine Entitäten und Requisiten hinzufügen, aber bevor wir das tun, müssen wir eine Schlangen- und Futterkomponente erstellen, die auf dem Gerät gerendert werden kann.

Erstellen von Spieleinheiten

Lasst uns zuerst die Schlange machen. Schlangen bestehen aus zwei Teilen: Kopf und Körper (oder Schwanz). Jetzt machen wir den Kopf der Schlange und fügen später in diesem Tutorial den Schwanz der Schlange hinzu.

Um den Kopf der Schlange zu machen, erstellen wir eine Kopfkomponente im Komponentenordner.

Wie Sie sehen, haben wir drei Komponenten: Kopf, Futter und Schwanz. Wir werden uns in diesem Tutorial den Inhalt jeder dieser Dateien nacheinander ansehen.

In der Head-Komponente erstellen wir eine Ansicht mit einigen Stilen.

importiere React von „react“;
importiere { View } von „react-native“;
exportiere Standardfunktion Head({ Position, Größe }) {
  zurückkehren (
    <Ansicht
      Stil={{
        Breite: Größe,
        Höhe: Größe,
        Hintergrundfarbe: "rot",
        Position: "absolut",
        links: Position[0] * Größe,
        oben: Position[1] * Größe,
      }}
    ></Anzeigen>
  );
}

Wir übergeben einige Requisiten, um die Größe und Position des Kopfes festzulegen.

Wir verwenden die Eigenschaft „Position: „absolut“, um den Kopf einfach zu bewegen.

Dadurch wird ein Quadrat gerendert. Wir werden nichts Komplexeres verwenden; eine quadratische oder rechteckige Form, um den Körper der Schlange darzustellen, und eine kreisförmige Form, um das Futter darzustellen.

Fügen wir nun den Schlangenkopf zur GameEngine hinzu.

Um eine Entität hinzuzufügen, müssen wir in der Entity-Eigenschaft in GameEngine ein Objekt übergeben.

//App.js
Head aus "./components/Head" importieren;


 <SpielEngine
        ref={Motor}
        Stil={{
          Breite: Boardgröße,
          Höhe: Brettgröße,
          flex: null,
          Hintergrundfarbe: "weiß",
        }}
        Entitäten={{
          Kopf: {
            Position: [0, 0],
            Größe: Konstanten.CELL_SIZE,
            Aktualisierungshäufigkeit: 10,
            nächsterZug: 10,
            xGeschwindigkeit: 0,
            y-Geschwindigkeit: 0,
            Renderer: <Kopf />,
          }
        }} 
/>

Wir haben in der Entities-Eigenschaft ein Objekt übergeben, dessen Schlüssel der Kopf ist. Dies sind die Eigenschaften, die es definiert.

  • position ist eine Reihe von Koordinaten, die zum Platzieren des Schlangenkopfes verwendet werden.
  • size ist der Wert zum Festlegen der Größe des Schlangenkopfes.
  • xspeed und yspeed sind Werte, die die Bewegung und Richtung der Schlange bestimmen und 1, 0 oder -1 sein können. Beachten Sie, dass wenn xspeed auf 1 oder -1 gesetzt ist, yspeed 0 sein muss und umgekehrt.
  • Schließlich ist renderer für das Rendern der Komponente verantwortlich
  • updateFrequency und nextMove werden später besprochen.

Nachdem wir die Head-Komponente hinzugefügt haben, fügen wir weitere Komponenten hinzu.

//Komponenten/Food/index.js
importiere React von „react“;
importiere { View } von „react-native“;
exportiere Standardfunktion Food({ Position, Größe }) {
  zurückkehren (
    <Ansicht
      Stil={{
        Breite: Größe,
        Höhe: Größe,
        Hintergrundfarbe: "grün",
        Position: "absolut",
        links: Position[0] * Größe,
        oben: Position[1] * Größe,
        Grenzradius: 50
      }}
    ></Anzeigen>
  );
}

Die Food-Komponente ähnelt der Head-Komponente, aber wir haben die Hintergrundfarbe und den Rahmenradius geändert, um einen Kreis daraus zu machen.

Erstellen Sie jetzt eine Tail-Komponente. Das kann schwierig sein.

// Komponenten/Tail/index.js

importiere React von „react“;
importiere { View } von „react-native“;
Konstanten aus "../../Constants" importieren;
exportiere Standardfunktion Tail({ Elemente, Position, Größe }) {
  const tailList = Elemente.map((el, idx) => (
    <Ansicht
      Schlüssel={idx}
      Stil={{
        Breite: Größe,
        Höhe: Größe,
        Position: "absolut",
        links: el[0] * Größe,
        oben: el[1] * Größe,
        Hintergrundfarbe: "rot",
      }}
    />
  ));
  zurückkehren (
    <Ansicht
      Stil={{
        Breite: Constants.GRID_SIZE * Größe,
        Höhe: Constants.GRID_SIZE * Größe,
      }}
    >
      {tailList}
    </Anzeigen>
  );
}

Wenn die Schlange das Futter frisst, fügen wir dem Körper der Schlange ein Element hinzu, damit unsere Schlange wächst. Diese Elemente werden an die Tail-Komponente übergeben, die angibt, dass sie wachsen muss.

Wir durchlaufen alle Elemente, um den gesamten Schlangenkörper zu erstellen, ihn anzuhängen und anschließend zu rendern.

Nachdem wir alle benötigten Komponenten erstellt haben, erstellen wir diese beiden Komponenten als GameEngine.

// App.js

importiere Lebensmittel aus "./components/Food";
importiere Tail aus "./components/Tail";


// App.js
const zufälligePositionen = (min, max) => {
    gibt Math.floor(Math.random() * (max - min + 1) + min) zurück;
  };


// App.js

<SpielEngine
        ref={Motor}
        Stil={{
          Breite: Boardgröße,
          Höhe: Brettgröße,
          flex: null,
          Hintergrundfarbe: "weiß",
        }}
        Entitäten={{
          Kopf: {
            Position: [0, 0],
            Größe: Konstanten.CELL_SIZE,
            Aktualisierungshäufigkeit: 10,
            nächsterZug: 10,
            xGeschwindigkeit: 0,
            y-Geschwindigkeit: 0,
            Renderer: <Kopf />,
          },
          Essen:
            Position: [
              Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
              Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
            ],
            Größe: Konstanten.CELL_SIZE,
            Renderer: <Essen />,
          },
          Schwanz: {
            Größe: Konstanten.CELL_SIZE,
            Elemente: [],
            Renderer: <Tail />,
          },
        }}

      />

Um die Zufälligkeit der Lebensmittelpositionen sicherzustellen, haben wir eine RandomPositions-Funktion mit Minimal- und Maximalparametern erstellt.

Im Schwanz haben wir im Anfangszustand ein leeres Array hinzugefügt, sodass die Länge jedes Schwanzes in den Elementen gespeichert wird, wenn die Schlange das Futter frisst: Leerzeichen.

An diesem Punkt haben wir unsere Spielkomponenten erfolgreich erstellt. Jetzt ist es Zeit, die Spiellogik zur Spielschleife hinzuzufügen.

Spiellogik

Um die Spielschleife zu erstellen, verfügt die GameEngine-Komponente über eine Eigenschaft namens „Systems“, die ein Array von Funktionen akzeptiert.

Um alles strukturiert zu halten, erstelle ich einen Ordner namens „Systems“ und füge eine Datei namens „GameLoop.js“ ein.

In dieser Datei exportieren wir eine Funktion, die bestimmte Parameter annimmt.

// GameLoop.js

Standardfunktion exportieren (Entitäten, {Ereignisse, Versand}) {
  ...

  Rückgabestellen;
}

Das erste Argument ist „entities“, das alle Entitäten enthält, die wir an die GameEngine-Komponente übergeben haben, damit wir sie bearbeiten können. Das andere Argument ist ein Objekt mit Eigenschaften, nämlich Ereignissen und Versand.

Bewegen Sie den Schlangenkopf

Schreiben wir Code, um den Kopf der Schlange in die richtige Richtung zu bewegen.

In der Funktion GameLoop.js aktualisieren wir die Position des Kopfes, da diese Funktion in jedem Frame aufgerufen wird.

// GameLoop.js
Standardfunktion exportieren (Entitäten, {Ereignisse, Versand}) {
  const head = Entitäten.Kopf;
  Kopf.Position[0] += Kopf.xGeschwindigkeit;
  Kopf.Position[1] += Kopf.Y-Geschwindigkeit;
}

Wir verwenden den Entity-Parameter, um auf den Kopf zuzugreifen, und müssen in jedem Frame die Position des Schlangenkopfes aktualisieren.

Wenn Sie das Spiel jetzt spielen, passiert nichts, da wir xspeed und yspeed auf 0 gesetzt haben. Wenn Sie xspeed oder yspeed auf 1 setzen, bewegt sich der Kopf der Schlange sehr schnell.

Um die Schlange zu verlangsamen, werden wir mit den Werten „nextMove“ und „updateFrequency“ wie folgt spielen.

const head = Entitäten.Kopf;

Kopf.nächsterZug -= 1;
wenn (head.nextMove === 0) {
  Kopf.nächsterMove = Kopf.updateFrequency;

  Kopf.Position[0] += Kopf.xGeschwindigkeit;
  Kopf.Position[1] += Kopf.Y-Geschwindigkeit;
}

Wir aktualisieren den Wert von nextMove auf 0, indem wir bei jedem Frame 1 abziehen. Wenn der Wert 0 ist, wird die if-Bedingung auf „true“ gesetzt und der nextMove-Wert wird wieder auf den Anfangswert aktualisiert, wodurch der Kopf der Schlange bewegt wird.

Jetzt sollte sich die Schlange langsamer bewegen als zuvor.

„Game Over!“-Zustand

Zu diesem Zeitpunkt haben wir die Bedingung „Game Over!“ noch nicht hinzugefügt. Die erste „Game Over!“-Bedingung tritt ein, wenn die Schlange gegen eine Wand stößt, das Spiel angehalten wird und dem Benutzer eine Meldung angezeigt wird, dass das Spiel vorbei ist.

Um diese Bedingung hinzuzufügen, verwenden wir diesen Code.

wenn (head.nextMove === 0) {
  Kopf.nächsterMove = Kopf.updateFrequency;
  Wenn (
        Kopfposition[0] + Kopfgeschwindigkeit < 0 ||
        head.position[0] + head.xspeed >= Konstanten.GRID_SIZE ||
        Kopfposition[1] + Kopfygeschwindigkeit < 0 ||
        Kopf.position[1] + Kopf.yspeed >= Konstanten.GRID_SIZE
      ) {
        Versand("Spiel vorbei");
      } anders {
        Kopf.Position[0] += Kopf.xGeschwindigkeit;
        Kopf.Position[1] += Kopf.Y-Geschwindigkeit;
    }

Die zweite if-Bedingung prüft, ob der Kopf der Schlange die Wand berührt. Wenn diese Bedingung erfüllt ist, verwenden wir die Dispatch-Funktion, um ein „Game-Over“-Ereignis zu senden.

Durch das Sonstige aktualisieren wir die Kopfposition der Schlange.

Fügen wir nun die „Game Over!“-Funktionalität hinzu.

Immer wenn wir ein „Game-Over“-Ereignis auslösen, stoppen wir das Spiel und zeigen eine Warnung an: „Game Over!“ Lassen Sie uns dies implementieren.

Um auf das Ereignis „Game-Over“ zu warten, müssen wir die Eigenschaft „onEvent“ an die Komponente „GameEngine“ übergeben. Um das Spiel zu stoppen, müssen wir eine laufende Stütze hinzufügen und sie an useState übergeben.

So sollte unsere GameEngine aussehen.

// App.js
importiere React, { useRef, useState } von "react";
importiere GameLoop aus "./systems/GameLoop";

....
....

const [isGameRunning, setIsGameRunning] = useState(true);

....
....

 <SpielEngine
        ref={Motor}
        Stil={{
          Breite: Boardgröße,
          Höhe: Brettgröße,
          flex: null,
          Hintergrundfarbe: "weiß",
        }}
        Entitäten={{
          Kopf: {
            Position: [0, 0],
            Größe: Konstanten.CELL_SIZE,
            Aktualisierungshäufigkeit: 10,
            nächsterZug: 10,
            xGeschwindigkeit: 0,
            y-Geschwindigkeit: 0,
            Renderer: <Kopf />,
          },
          Essen:
            Position: [
              Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
              Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
            ],
            Größe: Konstanten.CELL_SIZE,
            Renderer: <Essen />,
          },
          Schwanz: {
            Größe: Konstanten.CELL_SIZE,
            Elemente: [],
            Renderer: <Tail />,
          },
        }}
        Systeme={[GameLoop]}
        läuft={isGameRunning}
        beiEreignis={(e) => {
          Schalter (e) {
            Fall „Game Over“:
              alert("Spiel vorbei!");
              setIsGameRunning(false);
              zurückkehren;
          }
        }}
      />

In GameEngine haben wir die System-Eigenschaft hinzugefügt und ein Array zusammen mit einer Running-Eigenschaft und einem isGameRunning-Status durch unsere GameLoop-Funktion übergeben. Schließlich haben wir die Eigenschaft „onEvent“ hinzugefügt, die eine Funktion mit einem Ereignisargument akzeptiert, sodass wir auf unser Ereignis warten können.

In diesem Fall warten wir in einer Switch-Anweisung auf das Ereignis „Game Over“. Wenn wir dieses Ereignis empfangen, zeigen wir eine „Game Over!“-Warnung an und setzen den Status isGameRunning auf „False“, wodurch das Spiel gestoppt wird.

Essen

Wir haben die „Game Over!“-Logik geschrieben, jetzt schreiben wir die Logik, die die Schlange dazu bringt, das Futter zu fressen.

Wenn die Schlange das Futter frisst, sollte sich der Standort des Futters zufällig ändern.

Öffnen Sie GameLoop.js und schreiben Sie den folgenden Code.

// GameLoop.js

const zufälligePositionen = (min, max) => {
  gibt Math.floor(Math.random() * (max - min + 1) + min) zurück;
};

Standardfunktion exportieren (Entitäten, {Ereignisse, Versand}) {
  const head = Entitäten.Kopf;
  const Essen = Entitäten.Essen;

  ....
  ....
  ....
  Wenn (
        Kopfposition[0] + Kopfgeschwindigkeit < 0 ||
        head.position[0] + head.xspeed >= Konstanten.GRID_SIZE ||
        Kopfposition[1] + Kopfygeschwindigkeit < 0 ||
        Kopf.position[1] + Kopf.yspeed >= Konstanten.GRID_SIZE
      ) {
        Versand("Spiel vorbei");
      } anders {

     Kopf.Position[0] += Kopf.xGeschwindigkeit;
     Kopf.Position[1] += Kopf.Y-Geschwindigkeit;

     Wenn (
          Kopf.Position[0] == Essen.Position[0] &&
          Kopf.Position[1] == Essen.Position[1]
        ) {

          Essen.Position = [
            Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
            Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
          ];
        }
  }

Wir haben ein „if“ hinzugefügt, um zu prüfen, ob die Position des Schlangenkopfes und des Futters identisch sind (was darauf hinweisen würde, dass die Schlange das Futter „gefressen“ hat). Anschließend aktualisieren wir die Position des Lebensmittels mithilfe der Funktion „randomPositions“, genau wie wir es oben in App.js getan haben. Beachten Sie, dass wir über den Entity-Parameter auf die Lebensmittel zugreifen.

Die Schlange kontrollieren

Fügen wir nun die Steuerelemente der Schlange hinzu. Wir werden Schaltflächen verwenden, um zu steuern, wohin sich die Schlange bewegt.

Dazu müssen wir dem Bildschirm unterhalb der Leinwand Schaltflächen hinzufügen.

// App.js

importiere React, { useRef, useState } von "react";
importiere { StyleSheet, Text, Ansicht } von „react-native“;
importiere { GameEngine } von „react-native-game-engine“;
importiere { TouchableOpacity } von „react-native-gesture-handler“;
importiere Lebensmittel aus "./components/Food";
Head aus "./components/Head" importieren;
importiere Tail aus "./components/Tail";
Konstanten aus "./Constants" importieren;
importiere GameLoop aus "./systems/GameLoop";
exportiere Standardfunktion App() {
  const BoardSize = Konstanten.GRID_SIZE * Konstanten.CELL_SIZE;
  const engine = useRef(null);
  const [isGameRunning, setIsGameRunning] = useState(true);
  const zufälligePositionen = (min, max) => {
    gibt Math.floor(Math.random() * (max - min + 1) + min) zurück;
  };
  const resetGame = () => {
    engine.aktuell.swap({
      Kopf: {
        Position: [0, 0],
        Größe: Konstanten.CELL_SIZE,
        Aktualisierungshäufigkeit: 10,
        nächsterZug: 10,
        xGeschwindigkeit: 0,
        y-Geschwindigkeit: 0,
        Renderer: <Kopf />,
      },
      Essen:
        Position: [
          Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
          Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
        ],
        Größe: Konstanten.CELL_SIZE,
        Aktualisierungshäufigkeit: 10,
        nächsterZug: 10,
        xGeschwindigkeit: 0,
        y-Geschwindigkeit: 0,
        Renderer: <Essen />,
      },
      Schwanz: {
        Größe: Konstanten.CELL_SIZE,
        Elemente: [],
        Renderer: <Tail />,
      },
    });
    setIsGameRunning(true);
  };
  zurückkehren (
    <Ansichtsstil={styles.canvas}>
      <SpielEngine
        ref={Motor}
        Stil={{
          Breite: Boardgröße,
          Höhe: Brettgröße,
          flex: null,
          Hintergrundfarbe: "weiß",
        }}
        Entitäten={{
          Kopf: {
            Position: [0, 0],
            Größe: Konstanten.CELL_SIZE,
            Aktualisierungshäufigkeit: 10,
            nächsterZug: 10,
            xGeschwindigkeit: 0,
            y-Geschwindigkeit: 0,
            Renderer: <Kopf />,
          },
          Essen:
            Position: [
              Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
              Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
            ],
            Größe: Konstanten.CELL_SIZE,
            Renderer: <Essen />,
          },
          Schwanz: {
            Größe: Konstanten.CELL_SIZE,
            Elemente: [],
            Renderer: <Tail />,
          },
        }}
        Systeme={[GameLoop]}
        läuft={isGameRunning}
        beiEreignis={(e) => {
          Schalter (e) {
            Fall „Game Over“:
              Warnung("Spiel vorbei!");
              setIsGameRunning(false);
              zurückkehren;
          }
        }}
      />
      <Ansichtsstil={styles.controlContainer}>
        <Ansichtsstil={styles.controllerRow}>
          <TouchableOpacity onPress={() => engine.current.dispatch("nach oben bewegen")}>
            <Ansichtsstil={styles.controlBtn} />
          </TouchableOpacity>
        </Anzeigen>
        <Ansichtsstil={styles.controllerRow}>
          <Berührbare Opazität
            beim Drücken = {() => engine.current.dispatch("nach links bewegen")}
          >
            <Ansichtsstil={styles.controlBtn} />
          </TouchableOpacity>
          <Ansichtsstil={[styles.controlBtn, { backgroundColor: null }]} />
          <Berührbare Opazität
            beim Drücken = {() => engine.current.dispatch("nach rechts bewegen")}
          >
            <Ansichtsstil={styles.controlBtn} />
          </TouchableOpacity>
        </Anzeigen>
        <Ansichtsstil={styles.controllerRow}>
          <Berührbare Opazität
            beim Drücken = {() => engine.current.dispatch("nach unten bewegen")}
          >
            <Ansichtsstil={styles.controlBtn} />
          </TouchableOpacity>
        </Anzeigen>
      </Anzeigen>
      {!isGameRunning && (
        <TouchableOpacity onPress={resetGame}>
          <Text
            Stil={{
              Farbe: "weiß",
              Rand oben: 15,
              Schriftgröße: 22,
              Polsterung: 10,
              Hintergrundfarbe: "grau",
              Grenzradius: 10
            }}
          >
            Neues Spiel starten
          </Text>
        </TouchableOpacity>
      )}
    </Anzeigen>
  );
}
const Stile = StyleSheet.erstellen({
  Leinwand:
    flex: 1,
    Hintergrundfarbe: "#000000",
    alignItems: "zentriert",
    justifyContent: "zentriert",
  },
  KontrollContainer: {
    Rand oben: 10,
  },
  Controllerzeile: {
    flexDirection: "Zeile",
    justifyContent: "zentriert",
    alignItems: "zentriert",
  },
  Bedienfeld: {
    Hintergrundfarbe: "gelb",
    Breite: 100,
    Höhe: 100,
  },
});

Zusätzlich zu den Steuerelementen haben wir auch eine Schaltfläche hinzugefügt, um ein neues Spiel zu starten, wenn das vorherige beendet ist. Diese Schaltfläche wird nur angezeigt, wenn das Spiel nicht läuft. Wenn die Schaltfläche angeklickt wird, setzen wir das Spiel zurück, indem wir die Swap-Funktion der Spiel-Engine verwenden, das ursprüngliche Objekt der Entität übergeben und den Ausführungsstatus des Spiels aktualisieren.

Lassen Sie uns jetzt über Kontrolle sprechen. Wir haben berührbare Objekte hinzugefügt, die beim Drücken Ereignisse auslösen, die in der Spielschleife verarbeitet werden.

// GameLoop.js
....
....
 Standardfunktion exportieren (Entitäten, {Ereignisse, Versand}) {
    const head = Entitäten.Kopf;
    const Essen = Entitäten.Essen;

  wenn (Ereignisse.Länge) {
    Ereignisse.fürJedes((e) => {
      Schalter (e) {
        Fall „Aufstieg“:
          wenn (head.yspeed === 1) zurückgeben;
          Kopf.yspeed = -1;
          Kopf.xspeed = 0;
          zurückkehren;
        Fall „nach rechts bewegen“:
          wenn (head.xspeed === -1) zurückgeben;
          Kopf.xspeed = 1;
          Kopf.yspeed = 0;
          zurückkehren;
        Fall „nach unten verschieben“:
          wenn (head.yspeed === -1) zurückgeben;
          Kopf.yspeed = 1;
          Kopf.xspeed = 0;
          zurückkehren;
        Fall „nach links bewegen“:
          wenn (head.xspeed === 1) zurückgeben;
          Kopf.xspeed = -1;
          Kopf.yspeed = 0;
          zurückkehren;
      }
    });
  }

....
....
});

Im obigen Code haben wir eine Switch-Anweisung hinzugefügt, um das Ereignis zu erkennen und die Richtung der Schlange zu aktualisieren.

Hörst du mir noch zu? Super! Jetzt fehlt nur noch der Schwanz.

Schwanzfunktion

Wenn die Schlange das Futter frisst, hoffen wir, dass ihr Schwanz nachwächst. Wir möchten außerdem ein „Game Over!“-Ereignis auslösen, wenn die Schlange sich in den eigenen Schwanz oder Körper beißt.

Fügen wir die Tail-Logik hinzu.

// GameLoop.js

const tail = Entitäten.tail;

....
....

....

    anders {
      Schwanzelement = [[Kopfposition[0], Kopfposition[1]], ... Schwanzelement];
      tail.elements.pop();

      Kopf.Position[0] += Kopf.xGeschwindigkeit;
      Kopf.Position[1] += Kopf.Y-Geschwindigkeit;

      tail.elements.forEach((el, idx) => {
        Wenn (
          Kopf.Position[0] === el[0] &&
          Kopf.position[1] === el[1] 
        )
          Versand("Spiel vorbei");
      });
      Wenn (
        Kopf.Position[0] == Essen.Position[0] &&
        Kopf.Position[1] == Essen.Position[1]
      ) {
        Schwanz.Elemente = [
          [Kopf.Position[0], Kopf.Position[1]],
          ...tail.elemente,
        ];

        Essen.Position = [
          Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
          Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
        ];
      }
    }

Damit der Schwanz dem Kopf der Schlange folgt, müssen wir das Schwanzelement aktualisieren. Dies tun wir, indem wir die Position des Kopfes am Anfang des Element-Arrays hinzufügen und dann das letzte Element im Endelement-Array entfernen.

Danach schreiben wir eine Bedingung, sodass wir das „Game-Over“-Ereignis auslösen, wenn die Schlange sich selbst beißt.

Schließlich fügen wir jedes Mal, wenn die Schlange Nahrung zu sich nimmt, Elemente des Schlangenschwanzes mit der aktuellen Position des Schlangenkopfes hinzu, um die Länge des Schlangenschwanzes zu erhöhen.

Unten finden Sie den vollständigen Code von GameLoop.js.

// GameLoop.js

Konstanten aus „../Constants“ importieren;
const zufälligePositionen = (min, max) => {
  gibt Math.floor(Math.random() * (max - min + 1) + min) zurück;
};
  Standardfunktion exportieren (Entitäten, {Ereignisse, Versand}) {
    const head = Entitäten.Kopf;
    const Essen = Entitäten.Essen;
    const tail = Entitäten.tail;
  wenn (Ereignisse.Länge) {
    Ereignisse.fürJedes((e) => {
      Schalter (e) {
        Fall „Aufstieg“:
          wenn (head.yspeed === 1) zurückgeben;
          Kopf.yspeed = -1;
          Kopf.xspeed = 0;
          zurückkehren;
        Fall „nach rechts bewegen“:
          wenn (head.xspeed === -1) zurückgeben;
          Kopf.xspeed = 1;
          Kopf.yspeed = 0;
          // ToastAndroid.show("nach rechts bewegen", ToastAndroid.SHORT);
          zurückkehren;
        Fall „nach unten verschieben“:
          wenn (head.yspeed === -1) zurückgeben;
          // ToastAndroid.show("nach unten bewegen", ToastAndroid.SHORT);
          Kopf.yspeed = 1;
          Kopf.xspeed = 0;
          zurückkehren;
        Fall „nach links bewegen“:
          wenn (head.xspeed === 1) zurückgeben;
          Kopf.xspeed = -1;
          Kopf.yspeed = 0;
          // ToastAndroid.show("nach links bewegen", ToastAndroid.SHORT);
          zurückkehren;
      }
    });
  }
  Kopf.nächsterZug -= 1;
  wenn (head.nextMove === 0) {
    Kopf.nächsterMove = Kopf.updateFrequency;
    Wenn (
      Kopfposition[0] + Kopfgeschwindigkeit < 0 ||
      head.position[0] + head.xspeed >= Konstanten.GRID_SIZE ||
      Kopfposition[1] + Kopfygeschwindigkeit < 0 ||
      Kopf.position[1] + Kopf.yspeed >= Konstanten.GRID_SIZE
    ) {
      Versand("Spiel vorbei");
    } anders {
      Schwanzelement = [[Kopfposition[0], Kopfposition[1]], ... Schwanzelement];
      tail.elements.pop();
      Kopf.Position[0] += Kopf.xGeschwindigkeit;
      Kopf.Position[1] += Kopf.Y-Geschwindigkeit;
      tail.elements.forEach((el, idx) => {
        console.log({ el, idx });
        Wenn (
          Kopf.Position[0] === el[0] &&
          Kopf.position[1] === el[1] 
        )
          Versand("Spiel vorbei");
      });
      Wenn (
        Kopf.Position[0] == Essen.Position[0] &&
        Kopf.Position[1] == Essen.Position[1]
      ) {
        Schwanz.Elemente = [
          [Kopf.Position[0], Kopf.Position[1]],
          ...tail.elemente,
        ];

        Essen.Position = [
          Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
          Zufallspositionen(0, Konstanten.GRID_SIZE - 1),
        ];
      }
    }
  }
  Rückgabestellen;
}

Abschluss

Nachdem Ihr erstes React Native-Spiel fertig ist, können Sie es auf Ihrem Gerät ausführen, um es zu spielen. Ich hoffe, Sie haben etwas Neues gelernt und teilen es mit Ihren Freunden.

Vielen Dank fürs Lesen und einen schönen Tag.

Der Beitrag „So erstellen Sie ein einfaches Spiel in React Native“ erschien zuerst im LogRocket-Blog.

Das war’s zum Erstellen eines einfachen Spiels mit React Native. Weitere Informationen zu React Native-Spielen finden Sie in anderen Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • React Native realisiert den Auf- und Ab-Pull-Effekt der Überwachungsgeste
  • VSCode erstellt eine React Native-Umgebung
  • Lösen Sie das Problem, dass die React-Native-Softtastatur auftaucht und das Eingabefeld blockiert
  • Detaillierte Erläuterung der React-Native-WebView-Rückgabeverarbeitung (Nicht-Callback-Methode kann gelöst werden)
  • Detaillierte Erläuterung der nativen Android-Entwicklung mit React-Native Bridge
  • Eine kurze Diskussion zum React Native Flexbox-Layout (Zusammenfassung)
  • React Native react-navigation Navigationsnutzungsdetails
  • Spezifische Verwendung von FlatList in ReactNative
  • ReactNative FlatList-Verwendung und Fallstricke Paketübersicht

<<:  Detaillierte Erläuterung der für das Front-End erforderlichen Nginx-Konfiguration

>>:  MySQL Detaillierte Analyse vom Löschen der Datenbank bis zum Weglaufen_Fortgeschritten (I) - Datenintegrität

Artikel empfehlen

Detaillierte Erklärung der Verwendung des Linux-Befehls lsof

lsof (List Open Files) ist ein Tool zum Anzeigen ...

Callback-Funktionen in JavaScript verstehen und verwenden

Inhaltsverzeichnis Überblick Was sind Rückrufe od...

Wird die Tabelle durch ein Update in einer MySQL-Transaktion gesperrt?

Zwei Fälle: 1. Mit Index 2. Ohne Index Voraussetz...

Element Tabelle Tabellenkomponente Mehrfeld (Mehrspalten) Sortiermethode

Inhaltsverzeichnis brauchen: Aufgetretene Problem...

Der beste Weg, um den 1px-Rand auf Mobilgeräten zu lösen (empfohlen)

Bei der Entwicklung für Mobilgeräte tritt häufig ...

Grundlegende Verwendung von exists, in und any in MySQL

【1】existiert Verwenden Sie eine Schleife, um die ...

Verschiedene Methoden zum Neustarten von Mysql unter CentOS (empfohlen)

1. MySQL über RPM-Paket installiert Dienst MySQL ...

Beispielcode für einen coolen Atemeffekt mit CSS3+JavaScript

Ein einfacher cooler Effekt, der mit CSS3-Animati...

So handhaben Sie den Token-Ablauf in WeChat-Miniprogrammen

Inhaltsverzeichnis Fazit zuerst Frage Lösung Verw...

Vue elementUI implementiert Baumstrukturtabelle und Lazy Loading

Inhaltsverzeichnis 1. Ergebnisse erzielen 2. Back...

Drei Möglichkeiten zum Teilen der Komponentenlogik in React

Ohne weitere Umschweife sind dies diese drei Meth...