在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 }
设定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; } } }
子弹展示
上述代码完成后,我们的子弹在数据中就存在了。但是你看不见它们,因为他们没有被绘制到画面中。我们需要利用Stack
和Positioned
控件来展示它们。
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; }
按秒计时
既然游戏标题叫“是男人就坚持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
运行在浏览器
flutter run -d Chrome
运行在iOS
flutter run -d 模拟器ID
还有Linux,Windows,Android我就不一一给大家截图了
项目已开源,请自行运行吧!