Última atualização: 18/10/2022

React Native é um dos frameworks de desenvolvimento de aplicativos móveis mais populares projetada e desenvolvida pelo Facebook. O Facebook abriu sua base de código em 2015. Permite desenvolver um único código que roda nas duas plataformas que existem: Android e iOS.

O que você vai fazer

Para instalar o jogo no seu celular use este link com o app Expo

Referências

Instalação do Node.js e Expo

Baixe o instalador direto do site site oficial. Após a instalação, você poderá verificar a instalação com o comando:

node -v

Depois de instalar o node, precisamos instalar Expo CLI:

npm install --global expo-cli

Na linha de comando crie uma pasta chamada projetos: e dentro desta pasta execute o comando npx abaixo:

npx create-expo-app jogo

Após o download e instalação das dependências, digite:

cd jogo
code .

Depois de abrir o VSCode você verá a estrutura de pastas e arquivos do projeto criado, como mostrado abaixo:

Para executar o app em seu celular temos duas situações:

npx expo start

  1. Para executar usando o emulador do Android use a tecla a
  2. Para executar no seu celular use o app Expo para escanear o QR code

Vamos usas duas bibliotecas para construir o jogo. Então vamos instalar no nosso projeto. Na linha de comando e dentro da pasta da aplicação, execute o comando:

yarn add react-native-game-engine matter-js

Abra o arquivo App.js e apague todo o código, vamos começar do zero.

import Matter from "matter-js";
import { GameEngine } from "react-native-game-engine";
import { StyleSheet, StatusBar } from "react-native";
const App =() => {
    return (
      <GameEngine style={styles.container}>
        <StatusBar hidden={true} />
      </GameEngine>
    );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#6ff",
  },
});
export default App;

As entidades são como são os objetos do jogo. Vamos adicionar um entity simples - um retângulo - ao nosso engine. Crie um novo arquivo chamado Box.js.

import { View } from "react-native";

const Box = (props) => {
  const width = props.size[0];
  const height = props.size[1];
  const x = props.body.position.x - width / 2;
  const y = props.body.position.y - height / 2;

  return (
    <View
      style={{
        position: "absolute",
        left: x,
        top: y,
        width: width,
        height: height,
        backgroundColor: props.color || "pink",
      }}
    />
  );
};

export default Box;

Para exibir o retângulo na tela, vamos voltar ao App.js

import Matter from "matter-js";
import { GameEngine } from "react-native-game-engine";
import { StyleSheet, StatusBar, Dimensions } from "react-native";
import Box from "./Box";

const { width, height } = Dimensions.get("screen");
const boxSize = Math.trunc(Math.max(width, height) * 0.075);
const initialBox = Matter.Bodies.rectangle(width / 2, height / 2, boxSize, boxSize);

const App = () => {
  return (
    <GameEngine
      style={styles.container}
      entities={{
        initialBox: {
          body: initialBox,
          size: [boxSize, boxSize],
          color: 'red',
          renderer: Box
        }
      }}>
      <StatusBar hidden={true} />
    </GameEngine>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#6ff",
  },
});

export default App;

Você verá a tela assim:

Vamos adicionar mais um objeto ao nosso engine:

const floor = Matter.Bodies.rectangle(width / 2, height - boxSize, width, boxSize, { isStatic: true });

E adicione o objeto floor à propriedade entities do

floor: {
          body: floor,
          size: [width, boxSize],
          color: "green",
          renderer: Box,
        },

Vamos adicionar gravidade ao nosso mundo. Adicione as linhas abaixo

const engine = Matter.Engine.create({ enableSleeping: false });
const world = engine.world;

Para que nossos objetos sejam afetados pela física do nosso mundo, precisamos adicioná-los ao mundo

Matter.World.add(world, [initialBox, floor]);

Por fim, vamos adicionar o engine e world a nossa lista de entidades com o nome de physics:

entities={{ 
            physics: { 
               engine: engine, 
               world: world 
         },
            initialBox: { 
               body: initialBox, 
               size: [boxSize, boxSize], 
               color: 'red', 
               renderer: Box
         },
           floor: { 
               body: floor, 
               size: [width, boxSize], 
               color: "green", 
               renderer: Box 
        }
}}

Agora nossa lista de objetos do jogo está completa, vamos colocar em funcionamento este mundo. Então precisamos colocar um System ao nosso engine que faz o mundo funcionar.

System é um array contém uma série de funções, que serão chamadas em cada tick do jogo. Ou seja, essas funções serão executadas em uma velocidade muito rápida e podem ser usadas para verificar e atualizar nosso jogo. A primeira função que adicionaremos ao System será:

const Physics = (entities, { time }) => {
  let engine = entities["physics"].engine;
  Matter.Engine.update(engine, time.delta);
  return entities;
};

As funções do sistema recebem dois parâmetros:

Por fim adicionamos o sistema ao engine:

<GameEngine
style={styles.container}
systems={[Physics]}
entities={{ 
            physics: { 
               engine: engine, 
               world: world 
         },
            initialBox: { 
               body: initialBox, 
               size: [boxSize, boxSize], 
               color: 'red', 
               renderer: Box
         },
           floor: { 
               body: floor, 
               size: [width, boxSize], 
               color: "green", 
               renderer: Box 
        }
}}>
<StatusBar hidden={true} />
</GameEngine>

Vamos criar um novo Box a cada touch do usuário. Crie uma nova função acima de App.

let boxIds=0;  
const CreateBox = (entities, { touches, screen }) => {
    let world = entities["physics"].world;
    let boxSize = Math.trunc(Math.max(screen.width, screen.height) * 0.075);
    touches
      .filter((t) => t.type === "press")
      .forEach((t) => {
        let body = Matter.Bodies.rectangle(t.event.pageX, t.event.pageY, boxSize, boxSize, { frictionAir: 0.021, restitution: 1.0 });
        Matter.World.add(world, [body]);
        entities[++boxIds] = {
          body: body,
          size: [boxSize, boxSize],
          color: "#a8f93f",
          renderer: Box,
        };
      });
    return entities;
  };

Agora, vamos adicionar CreateBox ao array Systems no componente :

systems={[Physics, CreateBox]}

Vamos começar a desenvolver o jogo que apresentei no início da oficina.