In many cases, we need to add game elements into the app, such as learning software for children. It would be neat to make it into a game.
The purpose of this article is to explore the possibility of using RN to develop games. The attempt made in this article is far from developing a complete game
Preparation:
- Install the RN development environment
Installation RN articles should be available all over the world, so I won’t repeat them here.
- Install the expo
yarn global add expo-cli
Copy the code
Create:
Using EXPO to create apps in this article is another trend.
expo init ballcap
Copy the code
Select the first item blank
complete
The new project directory is as follows:
. ├ ─ ─. Expo ├ ─ ─ the expo -shared ├ ─ ─ the git ├ ─ ─ the gitignore ├ ─ ─ App. Js ├ ─ ─ App. Json ├ ─ ─ assets │ ├ ─ ─ the adaptive - icon. PNG │ ├ ─ ─ The favicon. PNG │ ├ ─ ─ icon. PNG │ └ ─ ─ splash. PNG ├ ─ ─ Babel. Config. Js ├ ─ ─ node_modules ├ ─ ─ package. The json ├ ─ ─ project. TXT └ ─ ─ yarn.lockCopy the code
It’s a lot easier than a traditional RN project.
run
Execute the following command:
yarn android
Copy the code
You will see a large QR code:
Scan the QR code on the mobile phone with EXPO to run the APP on the mobile phone, as shown in the picture:
At this point we are ready to have fun.
Development:
Introducing a game engine: RNGM
Install the NPM: react – native – game – engine
yarn add react-native-game-engine
Copy the code
Modify the App. Js
Modify before:
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',}});Copy the code
Revised:
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
// Add game engine 1/2
import { GameEngine } from "react-native-game-engine";
export default function App() {
// Add game engine 2/2
return (
<GameEngine style={styles.container}>
<StatusBar hidden={true} />
</GameEngine>
);
}
const styles = StyleSheet.create({
container: {
flex: 1.backgroundColor: '#fff',}});Copy the code
Reload your app and you’ll find the world is clean:
“Omit a screenshot of all white”
Add an object
End the empty state
Add a Ball
The 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
}
Copy the code
Like any other RN component, it simply adds rounded corners to a square View to create a circle.
Next, modify app.js to introduce the Ball and add the Ball object:
// ...
import {
Dimensions,
// ...
} from 'react-native';
// ...
// Add 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() {
// Add game engine 2/2
return (
<GameEngine
style={styles.container}
entities={{/ / addBall 2/2
ball: {
body: ball.size: [ballSize.ballSize].color: '#f93',
renderer: Ball}}} >
<StatusBar hidden={true} />
</ GameEngine>
);
}
// ...
Copy the code
The reload effect is shown as follows:
Add the Wall
Create wall.j add code
A component similar to Ball:
“Omitted Wall source code 29 lines here”
Add Wall to app.js
/ /...
// Add Walls 1/3
import Wall from './Wall';
const ball = createObject(width / 2, height / 2);
/ /...
// Add 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() {
// Add game engine 2/2
return (
<GameEngine
style={styles.container}
entities={{/ / addBall 3/3
ball: {
//.} / / addedWalls 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> ); } / /...Copy the code
The effect is as follows:
Add physics engine Matter
Make things move according to the laws of physics
expo install matter-js poly-decomp
Copy the code
or
yarn add matter-js poly-decomp
Copy the code
Create the file physics.js
import Matter from "matter-js";
// Create engine
const engine = Matter.Engine.create({ enableSleeping: false });
const world = engine.world;
// Engine object
export const physicsEntity = {
engine: engine,
world: world
};
// Update the engine
export const Physics = (entities, { time }) = > {
let engine = entities["physics"].engine;
Matter.Engine.update(engine, time.delta);
return entities;
};
/ / create a wall
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;
};
/ / create the ball
export const createBall = (x, y, r) = > {
const ball = Matter.Bodies.circle(x, y, r, { frictionAir: 0.021 });
Matter.World.add(world, ball);
return ball;
}
Copy the code
Modify the App. Js:
/ /...
import { Physics, physicsEntity, createWall, createBall } from './Physics';
// const createObject = (x, y) => ({ position: { x: x, y: y } });
// Add 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);
// Add 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() {
// Add game engine 2/2
return (
<GameEngine
style={styles.container}
systems={[Physics]}
entities={{
physics: physicsEntity/ / addedBall 3/3
ball: {
// .
},
// .
}} ></ GameEngine> ); } / /...Copy the code
The effect is as follows:
Add click events
We create a ball every time we click on the screen.
Add CreateBalls in physics.js
// Click Create ball
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;
};
Copy the code
Add App. Js
/ /...
import {
/ /...
createBall,
CreateBalls
} from './Physics';
// ...
export default function App() {
// Add game engine 2/2
return (
<GameEngine
systems={[Physics, CreateBalls(Ball)]}
entities={{
//.
}} >
<StatusBar hidden={true} /></ GameEngine> ); } / /...Copy the code
Add gravity sensor
expo install expo-sensors
Copy the code
If you don’t use EXPO, you can add a React-native sensors
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();
}, []);
}
Copy the code
To modify app.js, call useAccelerometer
// ...
import {
// ...
CreateBalls,
useAccelerometer
} from './Physics';
// ...
export default function App() {
useAccelerometer()
// Add game engine 2/2
return (
<GameEngine>
/ /...
</ GameEngine>
);
}
Copy the code
Now you can change the direction of gravity by adjusting the Angle of the phone, as shown here:
References:
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
The source address
Github.com/caojianfeng…
conclusion
There are obvious advantages to using RNGM+Matter+ Sensors for game development:
- Easy to get started.
- Less code, easy to maintain.
- Good performance, no lag in general scenarios.
What it’s like to develop games with ReactNative: juejin.cn/post/691610…