tap-the-number, iOS的简单反应本机游戏

分享于 

19分钟阅读

GitHub

  繁體 雙語
A simple React-Native game for iOS
  • 源代码名称:tap-the-number
  • 源代码网址:http://www.github.com/mmazzarolo/tap-the-number
  • tap-the-number源代码文档
  • tap-the-number源代码下载
  • Git URL:
    git://www.github.com/mmazzarolo/tap-the-number.git
    Git Clone代码到本地:
    git clone http://www.github.com/mmazzarolo/tap-the-number
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/mmazzarolo/tap-the-number
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    



    点击这个数字是一个简单的React本地游戏,你必须按一下屏幕上显示的特定顺序。
    即使开发这个游戏并不花太多时间( 我应该把它放在 ~20的几小时内) 也是一个有趣的游戏,我想在开发过程中分享一些信息。
    但首先。

    屏幕截图和视频预览

    我还在Youtube上发布了一个小游戏预览。

    堆栈和依赖项

    本机响应

    点按这个数字是完全用 React React React我在日常工作中喜欢React,我在日常工作中使用它的时候,这是显而易见的选择。
    本机附带了 Jest,这是一个在与React结合的时候产生的测试框架。
    相关相关性:

    "babel-jest": "18.0.0","babel-preset-react-native": "1.9.1","react": "~15.4.0-rc.4","react-native": "0.41.2","react-test-renderer": "~15.4.0-rc.4"
    状态管理:MobX

    这一项目是使用 Redux Redux Redux来处理状态管理,但是一段时间之后我注意到,这是一个简单的项目,所以我抓住了它,并实现了MobX
    我已经用了它,但是这次我试着用它的provider 和它的inject,我真的喜欢它 !
    相关相关性:

    "babel-preset-react-native-stage-0": "1.0.1",//Enables the decorators support"mobx": "3.1.0","mobx-react": "4.1.0",
    static 类型检查: Flowtype

    Flowtype 是一个 static 类型检查器。
    我已经用了很长时间了它节省了我很多时间。 我不能 praise。
    我强烈建议你尝试一下,或者更好地依靠它来看看它的真实优势。 相关相关性:

    "flow-bin": "0.37.4","babel-plugin-transform-flow-strip-types": "6.22.0",
    Linting: ESlint和更漂亮

    我浪费了太多的时间来调整我的ESlint配置。
    现在,我对的最小ESlint配置非常满意,它负责所有应用程序的代码样式设置。
    相关相关性:

    "babel-eslint": "7.1.1","eslint": "3.15.0","eslint-plugin-prettier": "2.0.0","eslint-plugin-react-app": "1.0.2","prettier": "0.16.0",
    从绝对路径导入: babel-plugin-module-resolver

    我使用 babel-plugin-module-resolversrc 路径导入文件。 相关相关性:

    "babel-plugin-module-resolver": "2.5.0", 
    播放音频文件: react-native-sound

    在本地应用程序中,如果需要播放音频文件,目前必须使用某种外部库,因为它不是( 但是) 实现的,因为它在本地 out。
    对于我来说, react-native-sound是目前最完整的库,它在这个应用程序中工作良好。
    我直接从它的Github主分支中拉出库,因为最新版本的最新版本不支持本机 0.40.
    相关相关性:

    "react-native-sound": "git+https://github.com/zmxv/react-native-sound.git",
    简单动画:react-native-animatable

    对于简单动画 react-native-animatable 是react的实际标准。
    相关相关性:

    "react-native-animatable": "1.1.0",

    项目结构

    应用程序的结构及其所有文件如下所示:

    src
     ├── index.js// The app entry point │
     ├── assets // audio & fonts that must be linked in the app │
     ├── components
     │ ├── CustomText.js// A wrapper on the text used in the entire app (responsive + custom font) │ ├── Tile.js// The tile component used in the home screen and in the playground │ └── TouchableView.js// A cross-platform helper view with a touchable behavior │
     ├── config
     │ ├── colors.js// Colors (the available tiles colors, etc...) │ ├── env.js// Platform specific variables (IS_ENV_DEVELOPMENT, IS_ANDROID, etc...) │ ├── metrics.js// App metrics (DEVICE_WIDTH, TILE_SIZE, etc...) │ └── timings.js// Timing specific variables (TIME_LIMIT_MS, etc...) │
     ├── containers
     │ ├── App // The root app screen, routing is handled here │ ├── Endgame // The post-game screen (with the score and restart button) │ ├── Home // The home screen (with the start game button) │ └── Playground // The screen where the game runs │ ├── Board // The board game, renders the tiles │ ├── BoardTile // A Tile with Board-specific behaviors │ └── TimeBar // The top-bar with that shows the remaining time │
     ├── images // The app images │
     ├── services
     │ └── audio.js// Simple wrapper over react-native-sound  │
     ├── stores // MobX stores │ ├── game.js// All the app logic is handled here (Board setup, scoring, etc...) │ └── router.js// A super simple router  │
     ├── types // Flowtype types │
     └── utils
     ├── boardUtils.js// Board setup utils (getRandomTilePosition, getRandomNumber, etc...) ├── colorUtils.js// Color utils (getDifferentLuminance, etc...) └── timeUtils.js// Simple timing helpers (mostly wrappers over setTimeout)

    我在这里使用的项目结构似乎已经超过了工程,但这个设置几乎立即支付( 继续下面)。

    一些历史

    我开始用一个完全不同的想法开始这个游戏: 在这里,我们可以使用一个类似于 Twitter的动画,当你点击Tile的时候,我甚至实现了一个类似的动画。
    但是,由于使用了多个动画,我不得不放弃这个想法,因为当你运行多个动画时,似乎有一些事情发生了变化,这是因为。
    当时,我已经创建了游戏引擎和一些组件,所以,我决定把它转换成这个游戏。

    有趣的东西

    配置文件是你的朋友

    我试图在 config 目录中收集描述应用程序行为的所有变量。
    config/metrics.js 示例公开了所有应用程序维度:

    /* @flow */import { Dimensions, Platform } from'react-native';constIS_ANDROID=Platform.OS==='android';const { height, width } =Dimensions.get('window');constANDROID_STATUSBAR=24;constDEVICE_HEIGHT=IS_ANDROID? height -ANDROID_STATUSBAR: height;constDEVICE_WIDTH= width;constTILE_SIZE=DEVICE_WIDTH*0.28;constTILE_SHADOW_DEPTH=6;constTILE_BORDER_RADIUS=TILE_SIZE*0.1;constBOARD_MARGIN=20;constBOARD_HEIGHT=DEVICE_HEIGHT*0.96;constBOARD_WIDTH=DEVICE_WIDTH;exportdefault {
     DEVICE_HEIGHT,
     DEVICE_WIDTH,
     TILE_SIZE,
     TILE_SHADOW_DEPTH,
     TILE_BORDER_RADIUS,
     BOARD_MARGIN,
     BOARD_HEIGHT,
     BOARD_WIDTH,
     TIME_BAR_HEIGHT:DEVICE_HEIGHT*0.02,
    };

    这个安装结合热重载,即使对于这样一个简单的游戏,我也能够集中在一个目录中。

    relative-尺寸

    React本机并不是游戏开发框架,如果你想用它来构建一个游戏,就是一种适当的方法。
    点击这个数字是一个简单的游戏,所以使用 relative的维度就足够了。
    使用 relative 维度代替使用逻辑 Pixel 单元( 这是本机响应的默认单位) 定义视图的维度,应该将维度 relative 定义为设备大小( 或者是他们的父母)。
    以下方法将使你的游戏在大/小设备( 即使在平板电脑上) 上自动调整大小,但我认为它还有许多缺点: !

    • 应用程序越多,你必须定义和跟踪的维度越多;
    • 处理屏幕旋转可能变得困难( 点击游戏仅在 portrait 模式下工作) ;
    • 如果你使用一些本机组件,你可能无法调整它们的大小;

    谈谈尺寸,在点击数字我做了一些我感到感到厌恼的东西: 我将游戏引擎绑定到设备大小,就像你在 utils/boardUtils.jsgetRandomTilePosition 中看到的那样:

    /** * Gets a random tile position (making sure that it does not overlap another tile). * @param{Array<Tile>}blacklist - An array the already placed tiles. * @return{Object} An object with the x and y coordinates of the tile.*/constgetRandomTilePosition= (board:Array<Tile>): { x: number, y: number } => {
     constposition= {};
     constboardOriginX=metrics.BOARD_MARGIN;
     constboardOriginY=metrics.BOARD_MARGIN;
     constboardWidth=metrics.BOARD_WIDTH-metrics.BOARD_MARGIN;
     constboardHeight=metrics.BOARD_HEIGHT-metrics.BOARD_MARGIN;
     // Gets random tile positions until it finds a position that does not overlap another tile.// The while loop is a bit scary but we don't have to worry: we're using relative metrics// and we're limiting the number of tiles.while (true) {
     constrandomX=random(boardOriginX, boardWidth -metrics.TILE_SIZE);
     constrandomY=random(boardOriginY, boardHeight -metrics.TILE_SIZE);
     if (_isPositionAvailable(randomX, randomY, board)) {
     position.x= randomX;
     position.y= randomY;
     break;
     }
     }
     return position;
    };

    应用程序初始化游戏板时 上面 函数会使用一个循环来搜索可用的瓦片位置。
    i 可以用多种不同的方式优化这个函数,如果设备具有奇怪的宽度/高度比,则可以中断,但考虑到:

    • 我只瞄准(> = iPhone 5 ) ;
    • 板上的最大瓦数为 6 ;
    • 瓷砖尺寸是设备宽度的28% ;

    。我决定选择这个解决方案,以简化( 为了保持代码可以读性,但如果看起来不合理,可以自由更正我)。

    哦最后一件事:请记住,React的本机 <Text/> 组件不会基于设备大小缩放文本。
    这是我总是在 <Text/> 组件上使用自定义包装器的原因之一,因这里可以轻松地改变它的默认 behaviour/font/color。
    要获取缩放的字体大小,应执行以下操作:

    constscaledFontSize=Math.round(fontSize *metrics.DEVICE_WIDTH/375);

    ( 感谢Facebook的F8应用)。

    MobX (。and/utils ) 提示

    我们来说一下:我喜欢,我每天都使用它,但是对于这样的简单应用程序,MobX更加。
    事实上,如果你不感兴趣,或者有一个集中的Pattern 调度行动,我认为MobX比,更好。
    最近我刚刚开始使用MobX是 provider + inject 组合,它提供了将组件连接到存储( 以类似于 Redux mapStateToProps的方式)的一个很好的抽象。
    我发现真正有用的另一件事是滥用 src/utilssrc/services 文件夹: 从我的经验来看,MobX操作往往变得混乱,所以我更喜欢通过最小化代码的冗长来让它们易于阅读。
    谈论我的工具。 如果你觉得他们是 parameters,我会建议你接受这些配置作为参数,让他们可以测试这些配置。

    如果需要计算of的可以观测值( 就像选择器一样),请尽可以能使用 @computed 值。

    动画,第 1部分: 我使用的动画库

    在轻按数字时,我用三种不同的方式对组件进行了动画处理:

    React-Native-Animatable

    react-native-animatable 是一个React本地动画API的包装器,它公开了许多简单的动画,并且使用声明性的方式。
    如果你不需要复杂的动画,插值或者计时,react-native-animatable 是一个坚实的选择。

    本地动画 API

    我在 src/containers/Playground/TimeBar 中将动画API用于TimeBar动画: 我想要实现一个需要手动调整的效果,而动画API是最灵活的。
    具体地说,我希望动画的TimeBar宽度和TimeBar颜色从灰色到红色。

    type State = {
     animateValue: any,
    };exportdefaultclassTimeBarextendsComponent<void, {}, State> {
     state = {
     animateValue:newAnimated.Value(timings.TIME_LIMIT_MS),
     };
     componentDidMount() {
     Animated.timing(this.state.animateValue, {
     duration:timings.TIME_LIMIT_MS,
     easing:Easing.linear, // No easing toValue:0,
     }).start();
     }
     render() {
     // Animate the TimeBar color from grey to red, starting when there are left only 12 secondsconstbackgroundColor=this.state.animateValue.interpolate({
     inputRange: [0, timings.TIME_LIMIT_MS*0.4, timings.TIME_LIMIT_MS],
     outputRange: ['rgba(255,0,0, 1)', 'rgba(0,0,0, 0.3)', 'rgba(0,0,0, 0.3)'],
     });
     // Animate the TimeBar width from DEVICE_WIDTH to 0 in TIME_LIMIT_MS (which currently is 30 seconds)constwidth=this.state.animateValue.interpolate({
     inputRange: [0, timings.TIME_LIMIT_MS],
     outputRange: [0, metrics.DEVICE_WIDTH],
     });
     return (
     <View style={styles.container}><View style={[styles.content, { width, backgroundColor }]} /></View> );
     }
    }
    本地 LayoutAnimation

    在不需要指定动画行为的情况下,LayoutAnimation是设置布局更改之间转换的有效方法。
    调用 LayoutAnimation.spring() 之前调用( 或者是另一个可用配置的 。),然后React native将对布局更改所受的组件进行动画处理。
    src/components/Tile.js 中可以看到一个例子,在这里我通过调用 LayoutAnimation.spring() 来模拟瓷砖深度 this.setState({ isTouched: true });
    就像你已经猜到的那样,LayoutAnimation的缺点是它提供了比其他动画选项更好的LESS 控件。

    动画,第 2部分: React和动画

    我直接讲到要点: 我认为动画animationsReact( 并做出React),它们永远不会
    尽管我知道这可能是一个有争议的观点,但是尝试在原生( 在React中,例如React运动) 上同时使用许多不同的库,我仍然认为动画违背了声明性的。 不要让我错误,你仍然可以在使用小动画,但是当你开始链接动画之后,你会以编程方式。

    _handleButtonPress =async () => {
     this.setState({ disableAllButton:true }); // Prevent pressing buttons while animatingif (this._headerRef&&this._bodyRef) { // Animates out header and bodyawaitPromise.all([
     this._headerRef.fadeOutLeft(400), 
     this._bodyRef.fadeOutRight(400)
     ]);
     }
     this.props.navigateToNextScreen(); // Animations are ended: move to the next screen};

    否则,你必须跟踪你的组件状态中的动画状态( 比如: this.setState({ isContainerFadingOut: true }) 它增加了大量不必要的复杂的组件生命周期。

    "。"。和最后一个观点:我不认为动画与"React的declarativeness"的命令性性质可以很容易地解决( 我很高兴被证明是错误的): 毕竟,制作的动画效果很难。

    支持

    我打算先在Android上发布这个游戏,但是我有一些问题,我不能轻松解决。
    最烦人的一个是我无法在Android上使用自定义字体: 我尝试使用 react-native link ( 它在iOS上工作得很完美) 链接 assets 文件夹并手动添加字体,但似乎在所有都没有正确链接,尽管在使用第1 种方法时,有些字体甚至完全没有连接。
    我面临的另一个问题是动画( 特别是使用LayoutAnimation时)的响应,但我想我可以轻松地调查问题。

    感谢。

    我根本不是一个有创意的人 我在这个应用中使用的每件事都只是我以前见过的东西的一个。
    所以,如果没有更多的信息,这里都是我可以用来构建这个简单游戏的所有源:

    我们热欢地欢迎这个应用程序示例对于某人来说更早或者更早。 : )


    GAM  react  NAT  Native  React Native