用ReactNative开发游戏是怎样的体验

1 篇文章 0 订阅
1 篇文章 0 订阅

很多情况下需要我们在app中加入游戏性的元素,比如给小朋友用的学习类软件。做成游戏的形式会比较讨巧。

本文目的,探索用RN开发游戏的可能性,本文所做的尝试离开发一个完整的游戏还差的比较远

准备:

  1. 安装RN开发环境
    安装RN的文章资料应该满世界都有,这里就不重复了。

  2. 安装expo

yarn global add expo-cli

创建:

本文中使用expo创建app,这也是一个趋势。

expo init ballcap

选择第一项blank

完成

新创建的工程目录如下:

.
├── .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

比传统的RN工程要简单很多。

运行

执行下面的命令:

yarn android

你将会看到一个硕大的二维码:

在这里插入图片描述

手机上用expo扫描二维码即可在手机上运行app,如图:

到此为止我们的准备工作已经就绪,可以愉快的玩耍了。

开发:

引入游戏引擎:RNGM

安装npm: react-native-game-engine

yarn add react-native-game-engine

修改App.js

修改前:

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',
  },
});

修改后:

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你的app,你会发现整个世界清净了:

“此处省略全白截图一张”

添加物体

结束空空荡荡的状态

添加Ball

新建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
}

和其他的RN组件没有什么区别,就是简单的将正方形的View加上圆角变成圆。

接下来,修改App.js引入Ball,并添加ball对象:

// ...
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>
  );
}
// ...

reload效果如图:

添加Wall

创建Wall.j添加代码

其实和Ball类似的一个组件:

“此处省略Wall源码29行”

在App.js中 添加Wall

//...
// 添加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>
  );
}
//...

效果如图:

添加物理引擎Matter

让物体按照物理规律运动

expo install matter-js poly-decomp 

或者

yarn add matter-js poly-decomp 

创建文件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;
}

修改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>
  );
}
//...

效果如图:

添加点击事件

每当我们点击屏幕的时候都会创建一个圆球。

Physics.js中添加CreateBalls

// 点击创建球
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;
};

App.js中添加

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

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

添加重力传感器

expo install expo-sensors

如果不用expo可以添加 react-native-sensors,用法大同小异

修改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();
  }, []);
} 

修改App.js 调用useAccelerometer

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

现在你可以通过调整手机角度来改变重力方向了,效果如图:

参考资料:

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

源码地址

https://github.com/caojianfeng/ballcup

总结

用RNGM+Matter+sensors这样的组合来开发游戏,有明显的优点:

  1. 上手容易。
  2. 代码量少,易于维护。
  3. 性能好,一般的场景下不会卡顿。

转载请标明出处,并保持文章完整
用ReactNative开发游戏是怎样的体验:https://juejin.cn/post/6916101003322327054/

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
前言 xi 第1章 初识React Native 1 1.1 React Native 的优点 2 1.2 风险和缺点 4 1.3 小结 4 第2章 React Native 工作原理 5 2.1 React Native 是如何工作的 5 2.2 渲染周期 7 2.3 在React Native 中创建组件 2.4 宿主平台接口 11 2.5 小结 12 第3章 构建你的第一个应用 13 3.1 搭建环境 13 3.2 创建一个新的应用 17 3.3 探索示例代码 24 3.4 开发天气应用 27 3.5 小结 40 第4章 移动应用组件 42 4.1 类比HTML 元素与原生组件 42 4.2 处理触摸和手势 46 4.3 使用结构化组件 58 4.4 平台特定组件 69 4.5 小结 74 第5章 样式 75 5.1 声明和操作样式 75 5.2 组织和继承 79 5.3 定位和设计布局 81 5.4 小结 91 第6章 平台接口 92 6.1 使用定位接口 93 6.2 使用用户图片与摄像头 6.3 AsyncStore 持久化数据存储 108 6.4 智能天气应用 109 6.5 小结 119 第7章 模块 120 7.1 使用npm 安装JavaScript 类库 120 7.2 iOS 原生模块 121 7.3 Android 原生模块 130 7.4 跨平台原生模块 139 7.5 小结 141 第8章 调试与开发者工具 142 8.1 JavaScript 调试实践和解释 142 8.2 React Native 调试工具 147 8.3 JavaScript 之外的调试方法 152 8.4 测试代码 158 8.5 当你陷入困境 160 8.6 小结 160 第9章 学以致用 161 9.1 闪卡应用 161 9.2 模型与数据存储 168 9.3 使用Navigator 177 9.4 探索第三方依赖 180 9.5 响应式设计与字体尺寸 180 9.6 小结及任务 183 第10章 部署至iOS 应用商店 184 10.1 准备Xcode 工程 184 10.2 上传应用 192 10.3 使用TestFlight 进行Beta 测试 199 10.4 提交应用审核 200 10.5 小结 201 第11章 部署Android 应用 203 11.1 设置应用图标 203 11.2 生成release 版本的APK 205 11.3 通过邮件或链接发布 207 11.4 提交应用至Play 商店 207 11.5 小结 214 总结 215 附录A ES6 语法 216 附录B 命令与快速入门指南 219 作者简介 221 关于封面 221

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值