What is the experience of developing games with ReactNative

In many cases, we need to add game elements to the app, such as learning software for children. The form of the game will be more convenient.

The purpose of this article is to explore the possibility of using RN to develop games. The attempts made in this article are far from developing a complete game.

ready:

  1. Installing RN development environment
    The articles and materials of installing RN should be available all over the world, so I won't repeat it here.

  2. Install expo

yarn global add expo-cli

create:

In this article, Expo is used to create apps, which is also a trend.

expo init ballcap

Select the first item blank

carry out

The newly created project directory is as follows:

.
├── .expo
├── .expo-shared
├── .git
├── .gitignore
├── App.js
├── app.json
├── assets
│   ├── adaptive-icon.png
│   ├── favicon.png
│   ├── icon.png
│   └── splash.png
├── babel.config.js
├── node_modules
├── package.json
├── project.txt
└── yarn.lock

It is much simpler than the traditional RN project.

run

Execute the following command:

yarn android

You will see a huge QR code:

Insert picture description here

Scan the QR code with Expo on your phone to run the app on your phone, as shown in the figure:

So far our preparations are ready and we can play happily.

Development:

Introduce game engine: RNGM

安装npm: react-native-game-engine

yarn add react-native-game-engine

Modify App.js

before fixing:

import {
    
     StatusBar } from 'expo-status-bar';
import React from 'react';
import {
    
     StyleSheet, Text, View } from 'react-native';

export default function App() {
    
    
  return (
    <View style={
    
    styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
    
    
  container: {
    
    
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

After modification:

import {
    
     StatusBar } from 'expo-status-bar';
import React from 'react';
import {
    
     StyleSheet, Text, View } from 'react-native';
// 添加游戏引擎1/2
import {
    
     GameEngine } from "react-native-game-engine";

export default function App() {
    
    
  // 添加游戏引擎2/2
  return (
    <GameEngine style={
    
    styles.container}>
      <StatusBar hidden={
    
    true} />
    </GameEngine>
  );
}

const styles = StyleSheet.create({
    
    
  container: {
    
    
    flex: 1,
    backgroundColor: '#fff',
  },
});

Reload your app at this point, and you will find that the whole world is clean:

"A white screenshot is omitted here"

Add objects

End the empty state

Add Ball

New Ball.js:

import React, {
    
     Component } from "react";
import {
    
     View } from "react-native";
import {
    
     array, object, string } from 'prop-types';

export default class Ball extends Component {
    
    
  render() {
    
    
    const width = this.props.size[0];
    const height = this.props.size[1];
    const x = this.props.body.position.x - width / 2;
    const y = this.props.body.position.y - height / 2;
    const minSize = Math.min(width, height);
    return (
      <View
        style={
    
    {
    
    
          position: "absolute",
          left: x,
          top: y,
          width: width,
          height: height,
          backgroundColor: this.props.color || "orange",
          borderWidth: 2,
          borderRadius: minSize / 2,
          borderColor: 'black'
        }} />
    );
  }
}

Ball.propTypes = {
    
    
  size: array,
  body: object,
  color: string
}

It is no different from other RN components, it simply turns a square View with rounded corners into a circle.

Next, modify App.js to import Ball and add the ball object:

// ...
import {
    
    
  Dimensions,
  // ...
} from 'react-native';
// ...
// 添加Ball 1/2
import Ball from './Ball';
const {
    
     width, height } = Dimensions.get("screen");
const ballSize = Math.trunc(Math.max(width, height) * 0.075);
const ball = {
    
     position: {
    
     x: width / 2, y: height / 2 } };


export default function App() {
    
    
  // 添加游戏引擎2/2
  return (
    <GameEngine
      style={
    
    styles.container}
      entities={
    
    {
    
    
        // 添加Ball 2/2
        ball: {
    
    
          body: ball,
          size: [ballSize, ballSize],
          color: '#f93',
          renderer: Ball
        }
      }} >
      <StatusBar hidden={
    
    true} />
    </ GameEngine>
  );
}
// ...

The reload effect is shown in the figure:

Add Wall

Create Wall.j add code

In fact, a component similar to Ball:

"Omit 29 lines of Wall source code here"

Add Wall in App.js

//...
// 添加Walls 1/3
import Wall from './Wall';

const ball = createObject(width / 2, height / 2);

//...
// 添加Walls 2/3
const wallColor = "#335"
const wallSize = ballSize / 4;
const floor = createObject(width / 2, height - wallSize / 2);
const leftwall = createObject(wallSize / 2, height / 2);
const rightwall = createObject(width - wallSize / 2, height / 2);

export default function App() {
    
    
  // 添加游戏引擎2/2
  return (
    <GameEngine
      style={
    
    styles.container}
      entities={
    
    {
    
    
        // 添加Ball 3/3
        ball: {
    
    
          //....
        },
        // 添加Walls 3/3
        leftwall: {
    
    
          body: leftwall,
          size: [wallSize, height],
          color: wallColor,
          renderer: Wall
        },
        rightwall: {
    
    
          body: rightwall,
          size: [wallSize, height],
          color: wallColor,
          renderer: Wall
        },
        floor: {
    
    
          body: floor,
          size: [width, wallSize],
          color: wallColor,
          renderer: Wall
        }
      }} >
      <StatusBar hidden={
    
    true} />
    </ GameEngine>
  );
}
//...

The effect is as follows:

Add physics engine Matter

Let objects move according to the laws of physics

expo install matter-js poly-decomp 

or

yarn add matter-js poly-decomp 

Create file Physics.js

import Matter from "matter-js";

// 创建引擎
const engine = Matter.Engine.create({
    
     enableSleeping: false });
const world = engine.world;
// 引擎对象
export const physicsEntity = {
    
    
  engine: engine,
  world: world
};

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


//创建墙
export const createWall = (x, y, w, h) => {
    
    
  const wall = Matter.Bodies.rectangle(x, y, w, h, {
    
     isStatic: true })
  Matter.World.add(world, wall);
  return wall;
};

//创建球
export const createBall = (x, y, r) => {
    
    
  const ball = Matter.Bodies.circle(x, y, r, {
    
     frictionAir: 0.021 });
  Matter.World.add(world, ball);
  return ball;
}

Modify App.js:

//...

import {
    
     Physics, physicsEntity, createWall, createBall } from './Physics';

// const createObject = (x, y) => ({ position: { x: x, y: y } });

// 添加Ball 2/3
const {
    
     width, height } = Dimensions.get("screen");
const ballSize = Math.trunc(Math.max(width, height) * 0.075);
const ball = createBall(width / 2, height / 2, ballSize / 2);

// 添加Walls 2/3
const wallColor = "#335"
const wallSize = ballSize * 0.5;
const floor = createWall(width / 2, height - wallSize / 2, width, wallSize);
const leftwall = createWall(wallSize / 2, height / 2, wallSize, height);
const rightwall = createWall(width - wallSize / 2, height / 2, wallSize, height);

export default function App() {
    
    
  // 添加游戏引擎2/2
  return (
    <GameEngine
      style={
    
    styles.container}
      systems={
    
    [Physics]}
      entities={
    
    {
    
    
        physics: physicsEntity,
        // 添加Ball 3/3
        ball: {
    
    
          // ...
        },
        //  ...
      }} >
    </ GameEngine>
  );
}
//...

The effect is as follows:

Add click event

Every time we tap the screen, a sphere is created.

Add CreateBalls in Physics.js

// 点击创建球
let ballIndex = 1;
const ballColors = [ "#f93", "#f39", "#9f3", "#3f9", "#93f", "#39f"];
export const CreateBalls = (renderer)=> (entities, {
    
     touches, screen }) => {
    
    
  const ballSize = Math.trunc(Math.max(screen.width, screen.height) * 0.075);
  
  touches.filter(t => t.type === "press").forEach(t => {
    
    
    entities[++ballIndex] = {
    
    
      body: createBall(t.event.pageX, t.event.pageY, ballSize / 2),
      size: [ballSize, ballSize],
      color: ballColors[ballIndex % ballColors.length],
      renderer: renderer
    };
  });
  return entities;
};

Add in App.js

//...
import {
    
    
  //...
  createBall,
  CreateBalls
} from './Physics';

// ...
export default function App() {
    
    
  // 添加游戏引擎2/2
  return (
    <GameEngine
      systems={
    
    [Physics, CreateBalls(Ball)]}
      entities={
    
    {
    
    
        //...
      }} >
      <StatusBar hidden={
    
    true} />
    </ GameEngine>
  );
}
//...

Add a gravity sensor

expo install expo-sensors

If you don’t use Expo, you can add react-native-sensors, with similar usage

Modify Physics.js

//...
import { useState, useEffect } from 'react';
import { Accelerometer } from 'expo-sensors';
//...
export const useAccelerometer = () => {
  const [subscription, setSubscription] = useState(null);
  const subscribeAccelerometer = () => {
    setSubscription(
      Accelerometer.addListener(accelerometerData => {
        const { x, y, z } = accelerometerData;
        world.gravity.x = -x;
        world.gravity.y = y;
      })
    );
  };

  const unsubscribeAccelerometer = () => {
    subscription && subscription.remove();
    setSubscription(null);
  };

  useEffect(() => {
    subscribeAccelerometer();
    return () => unsubscribeAccelerometer();
  }, []);
} 

Modify App.js to call useAccelerometer

// ...
import {
    
    
  // ...
  CreateBalls,
  useAccelerometer
} from './Physics';
// ...
export default function App() {
    
    
  useAccelerometer()
  // 添加游戏引擎2/2
  return (
    <GameEngine>
    //...
    </ GameEngine>
  );
}

Now you can change the direction of gravity by adjusting the angle of the phone, the effect is as shown in the figure:

Reference materials:

My Journey with React Native Game Engine Part I: Starting the Project

My Journey with React Native Game Engine Part II: Adding Touch and Bounce

expo accelerometer

Source address

https://github.com/caojianfeng/ballcup

to sum up

Using the combination of RNGM+Matter+sensors to develop games has obvious advantages:

  1. Good and easy.
  2. The amount of code is small and easy to maintain.
  3. The performance is good, and it will not freeze in general scenarios.

Please indicate the source for reprinting and keep the article intact.
What is the experience of developing games with ReactNative: https://juejin.cn/post/6916101003322327054/

Guess you like

Origin blog.csdn.net/windcao/article/details/112440758