阅读 359

中秋征文活动·使用Flutter撸一个极简的吃月饼小游戏

我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛

先看效果

2021-09-08 22.17.51.gif

游戏规则很简单,在月饼投掷出去之后能够砸中月亮即记1分,否则该轮游戏结束,连续击中的次数则为本轮分数。

编码实现

代码上其实没有太多复杂度,大体逻辑如下:

1 月亮的移动动画,使用Tween实现一个补间动画,使用TweenSequence指定动画序列,监听动画的完成重复动画,从而实现月亮左右不间断移动的效果。

_animationController =
    AnimationController(duration: Duration(milliseconds: 600), vsync: this);
_animationControllerCake =
    AnimationController(duration: Duration(milliseconds: 900), vsync: this);
TweenSequenceItem<double> downMarginItem = TweenSequenceItem<double>(
  tween: Tween(begin: 1.0, end: 50.0),
  weight: 50,
);
TweenSequenceItem<double> upMarginItem = TweenSequenceItem<double>(
  tween: Tween(begin: 50.0, end: 300.0),
  weight: 100,
);

TweenSequence<double> tweenSequence = TweenSequence<double>([
  downMarginItem,
  upMarginItem,
]);

_animation = tweenSequence.animate(_animationController);

_animation.addListener(() {
  if (_animation.isCompleted) {
    _animationController.reverse();
  }
  if (_animation.isDismissed) {
    _animationController.forward();
  }
  setState(() {});
});
复制代码

2 月饼的投掷位移效果,使用StreamBuilder控件,配合Timer倒计时,在1秒内不断地Stream发送当前位置数据,从而不断的改变月饼距离底部的距离,看上去就像也是一个位移动画效果,而实际上是不断改变距离形成的视觉效果。

_countdownTimer = new Timer.periodic(new Duration(milliseconds: 1), (timer) {
  if (_milliSecond > 0) {
    _milliSecond = _milliSecond - 5;
  } else {
    _countdownTimer.cancel();
  }
  _cakeStreamController.sink.add(_milliSecond < 0 ? 0 : _milliSecond);
});
复制代码
Container(
  margin: EdgeInsets.only(bottom: distance),
  child: Image.asset(
    "assets/images/cake.png",
    width: 60,
    height: 60,
  ),
)
复制代码

3 比较关键的一点就是,如何判断月饼投掷出去之后会和月亮发生碰撞,也就是“吃到月饼”。实现方案是:月亮的高度是已知的(屏幕高度 - 状态栏高度 - 月亮距上方距离),月饼的横坐标是固定的(屏幕宽度的一半)。在StreamBuilder中监听:当月饼高度达到月亮的高度时,判断月亮的横坐标是否和月饼一致即可,如果一致则月饼与月亮重合,记为一次有效分数。而这里为了增加游戏的可玩性,并不是很严格的判断坐标完全重合,如图所示,月饼到达月亮高度时,月亮如果在红色区域内都记为有效分数。

image.png

判断逻辑:

// 当月饼高度达到月球所处的高度时,判断月球的位置是否处于中间
if (distance < (_screenHeight - 120) &&
    distance > (_screenHeight - 170 - MediaQuery.of(context).padding.top)) {
  print(_animation.value);
  if (_animation.value < (_screenWidth / 2 + 10) && _animation.value > (_screenWidth / 2 - 90)) {
    _hintStreamController.add("太棒了");
    print("撞到了");
    _score++;
  } else {
    _hintStreamController.add("MISS");
    print("MISS");
  }
  _milliSecond = 1000;
  _cakeStreamController.sink.add(_milliSecond);
  _countdownTimer.cancel();
}
复制代码

引入分数排行榜

为了增加游戏的趣味性,在游戏里面增加了联机的分数排行榜机制,游戏中会将玩家的最高分数上传至服务器,在主界面的右上角可以查看自己在排行榜的位置。

Screenshot_2021-09-08-22-23-35-489_com.flutter.mo.jpg

这里云端服务器存储功能使用的是第三方平台LeanCloud,这个平台是支持Flutter的,而且在8月份刚好增加了对空安全的迭代。

扫码下载链接

目前支持安卓版本的下载,还在等什么,赶快下载登上排行榜吧~

image.png

代码地址

Github链接

由于云端服务器使用了LeanCloud,下载的老铁需要去LeanCloud平台申请AppKey填写在项目中的main.dart中的初始化位置即可。

LeanCloud.initialize(
    "", "",
    server: "https://zsyju4p5.lc-cn-n1-shared.com", // to use your own custom domain
    queryCache: new LCQueryCache() // optinoal, enable cache
)
复制代码