V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
ezshine
V2EX  ›  分享创造

使用 Flutter Widget 开发游戏”是男人就坚持 100 秒“,一套代码横跨 6 端~

  •  
  •   ezshine ·
    ezshine · 2021-04-28 12:02:41 +08:00 · 3339 次点击
    这是一个创建于 1307 天前的主题,其中的信息可能已经有所发展或是发生改变。

    是男人就坚持 100 秒

    前言

    之前使用 Flutter 里的游戏引擎 Flare 已经开发了一版这个游戏。在文章里我说要用 Widget 再来做一次。现在兑现我的承诺,并且上周日在 B 站直播了整个开发过程

    📺本文配有视频教程

    在 Flutter 里展示 Sprite 动画

    请看这篇文章《手写一个在 Flutter 里展示”精灵图“的 Widget 》

    飞机的移动

    首先将飞机放置在画面正中,由于 Widget 的原点统一为左上角,所以要减去飞机图像宽和高的一半。

    //获得画布的宽高
    Size screenSize = window.physicalSize/window.devicePixelRatio;
    
    //将飞机的 x,y 坐标设定为画面中心
    playerLeft = screenSize.width/2-66/2;
    playerTop = screenSize.height/2-82/2;
    
    

    飞机我们需要捕获到用户的手势事件,使用 GestureDetector 这个 Widget 来拖动飞机。

    GestureDetector( 
      onPanUpdate: (DragUpdateDetails details) {
        setState(() {
          playerLeft += details.delta.dx;
          playerTop += details.delta.dy;
        });
      },
      child://飞机的 Widget
    }
    

    2021-04-12 16_29_13.gif

    设定 FPS

    由于没有使用游戏引擎,所以只好自己通过定时器来实现。比如我们要实现 60FPS 的刷新率,可以将定时器设置为 17 毫秒,这样的话刷新率约等于 59fps 。当然可以更精确一些,但没有那个必要。

    Timer.periodic(Duration(milliseconds: 17), (timer) {
      gameloop();
    });
    
    gameloop(){
        setState(() {
            //触发 build 方法
        });
    }
    

    不过我这里建议设置为每20毫秒刷新一次,原因在后面会讲。

    添加子弹

    我们建立一个子弹管理数组,将所有子弹的数据都放在数组中

    List bulletsData = [];
    
    addBullet(){
        double bulletX;
        double bulletY;
    
        if (Random().nextBool()) {
          bulletX = Random().nextDouble() * (screenSize.width + bulletSize.width) -
              bulletSize.width;
          bulletY = Random().nextBool() ? -bulletSize.height : screenSize.height;
        } else {
          bulletX = Random().nextBool() ? -bulletSize.width : screenSize.width;
          bulletY = Random().nextDouble() * (screenSize.height + bulletSize.height) -
              bulletSize.height;
        }
    
        bulletsData.add({
          "x":bulletX,
          "y":bulletY,
          "speed": (1+gameTime/10) + Random().nextDouble()*3,
          "angle": atan2(((bulletY + bulletSize.height/2) - (playerTop + playerHeight / 2)),
              ((bulletX + bulletSize.width) - (playerLeft + playerWidth / 2)))
        });
    }
    

    子弹移动

    gameloop中遍历数组对子弹进行移动。

    for (int i = bulletsData.length - 1; i >= 0; i--) {
          var bulletItem = bulletsData[i];
    
          double angle = bulletItem["angle"];
          double speed = bulletItem["speed"];
    
          bulletItem["x"] -= cos(angle) * speed;
          bulletItem["y"] -= sin(angle) * speed;
    
          if (isHitPlayer(bulletItem["x"], bulletItem["y"])) {
            print("gameOver");
            gameOver();
          }
    
          if (isNotInScreen(bulletItem["x"], bulletItem["y"])) {
            print("bullet removed");
            bulletsData.removeAt(i);
            continue;
          }
        }
    }
    

    子弹展示

    上述代码完成后,我们的子弹在数据中就存在了。但是你看不见它们,因为他们没有被绘制到画面中。我们需要利用StackPositioned控件来展示它们。

    注意:这里我是故意为之,如果真的做游戏那么子弹粒子最好使用 CustomPainter 来实现

    Stack(
      children: getBulletsWidget(),
    )
    
    getBulletsWidget(){
        List<Positioned> bullets = [];
    
        for(int i = 0;i<bulletsData.length;i++){
          var bulletItem = bulletsData[i];
          // print(bulletItem);
          var bulletWidget = Positioned(
            left: bulletItem["x"],
            top: bulletItem["y"],
            child: bulletImage
          );
    
          bullets.add(bulletWidget);
        }
    
        return bullets;
    }
    

    2021-04-12 16_42_24.gif

    按秒计时

    既然游戏标题叫“是男人就坚持 100 秒”,那游戏中肯定需要一个按秒的计时器。还记得前面为什么我建议将计时器的刷新频率设置为 20 毫秒吗?这样的话,我们每刷新 50 次是不是就是 1 秒钟呢?

    Timer.periodic(Duration(milliseconds: 20), (timer) {
      if(timer.tick%50==0){
        gameSeconds+=1;
        //seconds
      }
    
      loop();
    });
    

    在 Flutter 里我们可以这样做,timer里的tick是一个计时器的执行计数,会不断累计,所以我们只需要对 50 取余,每次整除 50 的时候就是 1 秒钟啦~

    跨端

    借助 Flutter 强大的跨端能力,这个游戏我们可以...

    运行在 Mac 桌面

    flutter run -d macOS
    

    2021-04-12 11_45_01.gif

    运行在浏览器

    flutter run -d Chrome
    

    2021-04-12 16_54_04.gif

    运行在 iOS

    flutter run -d 模拟器 ID
    

    2021-04-12 17_05_27.gif

    还有 Linux,Windows,Android 我就不一一给大家截图了

    项目已开源,请自行运行吧!

    源码仓库

    https://github.com/ezshine/fluttergame-keepalive100s

    15 条回复    2021-05-06 13:39:00 +08:00
    dingwen07
        1
    dingwen07  
       2021-04-28 13:08:56 +08:00 via iPhone
    想到之前有个 v 站用户做的一个网页版的游戏,有点像
    James369
        2
    James369  
       2021-04-28 13:39:08 +08:00
    这个游戏暴露年龄了,哈哈
    RookiePG
        3
    RookiePG  
       2021-04-28 13:44:31 +08:00
    @James369 哈哈哈你也暴露年龄了
    kylix
        4
    kylix  
       2021-04-28 13:46:18 +08:00
    这个。。。算了,到我的收藏夹吃灰去吧 😁 🐶
    hronro
        5
    hronro  
       2021-04-28 13:49:31 +08:00
    有没有打包好的二进制下载呢?我就想体验一下现在 Flutter 做的桌面 App 到底效果如何
    ezshine
        6
    ezshine  
    OP
       2021-04-28 14:03:14 +08:00
    @hronro 没有打包好的,我个人感受是效果挺不错的。或者说”无感“,flutter2.0 发布后的将 desktop 的支持带到了 stable 版,编译打包非常简单。
    IvanLi127
        7
    IvanLi127  
       2021-04-28 16:20:26 +08:00
    这个好诶
    randm
        8
    randm  
       2021-04-28 18:05:16 +08:00
    原来是大神,膜拜学习
    ezshine
        9
    ezshine  
    OP
       2021-04-28 18:10:30 +08:00
    @randm flutter 我也刚学一段时间,我看你文章也在要学这个,一起交流学习呀。
    wipbssldo
        10
    wipbssldo  
       2021-04-28 18:32:14 +08:00
    Flutter 的思路和游戏引擎差不多,你用 Unity 或者其他游戏引擎开发个这样的游戏一样可以「一套代码横跨 6 端」。
    但是如果真的去做桌面 App,特别是强交互,内容显示类的,不同端的体验上面你会发现很多问题。
    ezshine
        11
    ezshine  
    OP
       2021-04-28 18:35:29 +08:00
    @wipbssldo 是的,我也是这么认为的。我视频里有说
    lancelock
        12
    lancelock  
       2021-04-28 18:44:52 +08:00
    正常引擎不都能跨端吗
    ezshine
        13
    ezshine  
    OP
       2021-04-28 20:26:15 +08:00
    @lancelock 对,正常游戏引擎都能跨端。我视频里也是这么说的
    justin2018
        14
    justin2018  
       2021-04-29 15:06:34 +08:00
    Dark 语法 为啥我总觉得怪怪的
    linktom
        15
    linktom  
       2021-05-06 13:39:00 +08:00
    star 一下,吃灰去吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5784 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 06:36 · PVG 14:36 · LAX 22:36 · JFK 01:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.