最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 这还能叫Flutter?用它复原一个叫《是男人就坚持100秒》的游戏|技术点评

    正文概述 掘金(大帅老猿)   2021-03-13   587

    前言

    我说Flutter的跨端一致性并非首创

    但凡你用过Unity3D,Unreal等游戏引擎,你就会发现 这些不都是跨端的吗?不都是开局一块画布,剩下随便整么?

    既然和游戏引擎作对比

    那么我们真的拿Flutter来做个游戏吧

    FLAME引擎

    这还能叫Flutter?用它复原一个叫《是男人就坚持100秒》的游戏|技术点评 这里我用了一款基于的Flutter的游戏引擎,名为FLAME。使用它开发游戏,基本用不到Flutter里的那些Widget,所以就有了标题里的提问

    一个Flutter应用不使用Flutter的Widget,那还叫Flutter吗?

    不管了,先把这篇文章写完,抽空再用widget重做一次这个游戏

    游戏引擎基础

    引入Flame

    dependencies:
      flame: ^1.0.0-rc5
    

    开发游戏,最基础就是这三件事

    • 刷新机制(update)
    • 渲染画布(renderer)
    • 输入事件(events)

    Flame中,一个基础的游戏框架代码如下

    class MyGameSubClass extends Game {
      @override
      void render(Canvas canvas) {
        // TODO: implement render
      }
    
      @override
      void update(double t) {
        // TODO: implement update
      }
    }
        
      
    main() {
      runApp(
        GameWidget(
          game: MyGameSubClass(),
        )
      );
    }
    

    刷新,渲染都有了,事件呢,在本游戏中我们加入拖动事件

    class MyGameSubClass extends BaseGame with PanDetector
    

    渲染图形元素

    • 飞机

    这还能叫Flutter?用它复原一个叫《是男人就坚持100秒》的游戏|技术点评

    飞机的图形素材是一个精灵图,其中前面两帧是飞行状态,后面三帧是爆炸状态

    playerSpriteSheet = SpriteSheet(
      image: await images.load('player.png'),
      srcSize: playerSize,
    );
    
    //运行状态下的精灵图动画
    player = SpriteAnimationComponent(
      size: playerSize,
      animation: playerSpriteSheet.createAnimation(row: 0, stepTime: .1, to: 2),
    );
    

    然后我们通过add方法,将图形元素展示到画面中

    add(player);
    
    //放到x:100,y:100的坐标,大家感受一下坐标系
    player.x=player.y=100;
    

    这还能叫Flutter?用它复原一个叫《是男人就坚持100秒》的游戏|技术点评

    • 子弹

    这还能叫Flutter?用它复原一个叫《是男人就坚持100秒》的游戏|技术点评

    bulletImage = await images.load('bullet.png');
    

    一颗子弹的出现逻辑以及生命周期是这样的

    1. 从屏幕外生成
    2. 向着飞机当前坐标的方向移动
    3. 移出屏幕即销毁
    //添加一颗子弹
    void addBullet() {
    final bullet = SpriteComponent.fromImage(bulletImage, size: bulletSize);
    
    double bulletX;
    double bulletY;
    
    //随机在显示区四周的矩形边上生成
    if (Random().nextBool()) {
      bulletX = Random().nextDouble() * (screenSize.width + bulletSize.x) -
          bulletSize.x;
      bulletY = Random().nextBool() ? -bulletSize.y : screenSize.height;
    } else {
      bulletX = Random().nextBool() ? -bulletSize.x : screenSize.width;
      bulletY = Random().nextDouble() * (screenSize.height + bulletSize.y) -
          bulletSize.y;
    }
    
    //给与子弹初始坐标
    bullet.x = bulletX;
    bullet.y = bulletY;
    
    //添加到场景中
    add(bullet);
    
    //加入bullet管理数组,稍后用于更新子弹飞行坐标以及碰撞检测
    bullets.add({
      "component": bullet,//子弹控件实例
      "speed": (1+gameTime/10) + Random().nextDouble()*3,//飞行速度
      "angle": atan2(((bulletY + bulletSize.y/2) - (player.y + playerSize.y / 2)),
          ((bulletX + bulletSize.x) - (player.x + playerSize.x / 2)))
    });//向量角度
    }
    

    通过调用这个addBullet的方法,我们就能在屏幕中朝着飞机所在位置发射一颗子弹了

    这还能叫Flutter?用它复原一个叫《是男人就坚持100秒》的游戏|技术点评

    为了发射多颗,我们再新建一个函数addGroupBullet

    //添加一组子弹
    void addGroupBullet() {
        int groupCount = 10+Random().nextInt(gameTime+1);
        for (int i = 0; i < groupCount; i++) {
          addBullet();
        }
    }
    

    刷新机制

    Flame提供的update方法中我们需要更新所有子弹的位置,因为子弹不是静止的,需要一直移动呀!

    @override
    void update(double dt) {
        super.update(dt);
        
        //遍历子弹数组,对所有子弹进行更新
        for (int i = bullets.length - 1; i >= 0; i--) {
              var bulletItem = bullets[i];
              
              //获得子弹实例
              SpriteComponent bullet = bulletItem["component"] as SpriteComponent;
    
              double angle = bulletItem["angle"];
              double speed = bulletItem["speed"];
    
              //让子弹根据发射时的向量角度进行移动
              bullet.x -= cos(angle) * speed;
              bullet.y -= sin(angle) * speed;
              
              //当子弹移动到屏幕外时销毁它
              if (isNotInScreen(bullet.x, bullet.y)) {
                    print("bullet removed");
                    remove(bullet);
                    bullets.removeAt(i);
                    continue;
              }
        }
    }
    

    开始有点带感了吧?

    这还能叫Flutter?用它复原一个叫《是男人就坚持100秒》的游戏|技术点评

    飞机的移动

    void onPanUpdate(DragUpdateDetails details) {
        super.onPanUpdate(details);
    
        if (!isGameStart) return;
    
        //由于飞机对象的坐标点在左上角,所以移动是要注意偏移一下
        player.x = details.globalPosition.dx - playerSize.x / 2;
        player.y = details.globalPosition.dy - playerSize.y / 2;
    }
    

    这还能叫Flutter?用它复原一个叫《是男人就坚持100秒》的游戏|技术点评

    碰撞检测

    在每次刷新子弹坐标的时候,去检测一下是否和当前飞机的坐标重合了,重合了游戏就结束啦

    if (isHitPlayer(bullet.x, bullet.y)) {
        gameOver();
    }
    

    这里用了一个简单的判断机制,并没有精确到飞机的外轮廓。一是精确到外轮廓将带来更大的运算量,也比较复杂。二是那样的游戏体验不见得好,毕竟在手机玩的话,手指要挡住大部分飞机的图形

    //飞机和子弹碰撞判断
    bool isHitPlayer(double x, double y) {
        double _x = (x + bulletSize.x / 2 - (player.x + playerSize.x / 2)).abs();
        double _y = (y + bulletSize.y / 2 - (player.y + playerSize.y / 2)).abs();
    
        //求出子弹和飞机坐标的直线距离
        double distance = sqrt(_x * _x + _y * _y);
    
        //当直线距离小于判定为碰撞的距离时则返回true/false
        if (distance <= hitDistance) return true;
        return false;
    }
    

    游戏结束

    void gameOver() {
        isGameStart = false;
        
        //取消定时发射子弹的定时器
        if(timer!=null)timer.cancel();
    
        //播放飞机爆炸动画
        player.animation = playerSpriteSheet.createAnimation(
            row: 0, stepTime: .1, loop: false, from: 2, to: 6);
    
        print("game over");
    }
    

    代码仓库

    github.com/ezshine/flu…

    后话

    这么写,从代码层面看,和Flutter关系不大,但利用Flutter的跨端能力,我们可以很容易的开发一个横跨Mac,Linux,Windows,Web,iOS,Android端的小游戏。如果你不会用那些开发游戏的IDE,比如Unity3D等,那Flutter也是一个不错的选择。

    关注大帅

    一个设计师出身的老程序猿,希望写代码到70岁


    近期文章(感谢掘友的鼓励与支持???)

    • ?做了一夜动画,就为让大家更好的理解Vue3的Composition Api 940赞
    • ?2020更新,Vue仿探探拖拽卡片的效果 1114赞
    • ?超详细,Flutter2.0构建Web应用的实际体验|技术点评 14赞
    • ?Webview的使用 | VSCode插件开发系列教程 19赞
    • ?推出掘金统计dashboard功能【我爱掘金】0.0.15|牛气冲天新年征文 7赞

    本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情


    起源地下载网 » 这还能叫Flutter?用它复原一个叫《是男人就坚持100秒》的游戏|技术点评

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元