写了个Plus版合成大西瓜

2,019 阅读3分钟

背景

我一直想做个小游戏,正好年前合成大西瓜火了,所以趁着这次年假,自己在家写了个plus版的合成大西瓜,前后花了大概三天三夜的时间(没错,一直写到大年三十的晚上~),现在写下这篇文章记录下这个过程。

预览

老规矩,先上图,看下这个plus版的合成大西瓜究竟长啥样😊

里面的图片素材可以自己裁剪更换,很方便。

除此之外还支持

  • 自定义背景图
  • 重力感应操控
  • 反向合成小瓜
  • 只生成小/大瓜

并且还内置了多套游戏主题(水果/表情/校徽)

这次让你轻松魔改个够😁

下载地址

网页版:v.idoo.top/mix

安卓/iOS:www.pgyer.com/Dagua

PS:iOS版安装包需要自签才能使用

开发记录

技术选型

我曾经用Flutter+Flame写过一个Forge2D的开源小游戏:《坠落》

(PS:早期作品,代码写的比较不忍直视,大佬轻喷~)

所以这次,我选择继续使用Flutter+Flame+Forge2D来开发合成大瓜.

后来证明这个选择是非常明智的:

一次开发就可以打包出Web、Android、iOS、Windows、Mac、Linux全端的安装包,非常给力!

开发环境

因为一开始我就打算支持打包成Web应用(甚至是桌面应用),所以需要先把我本地的Flutter环境从stable分支切换到dev分支

在Flutter SDK根目录执行以下命令

#首先把Flutter仓库地址换成清华源到镜像地址,加速下载
git remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/flutter-sdk.git

#切换分支
flutter channel dev

#更新sdk
flutter upgrade

#开启web,desktop支持
flutter config --enable-web
flutter config --enable-macos-desktop

OK,现在我们就可以用Flutter愉快的开发web跟桌面应用了^^

代码规范

为了规范自己的代码,第一件事就是把 pedantic 加入 dependency

dependencies:
  flutter:
    sdk: flutter
  pedantic: ^1.9.2

#analysis_options.yaml
include: package:pedantic/analysis_options.yaml

pedantic是谷歌内部使用的Dart代码规范,它比 Effective Dart 还要严格一些,有了它就可以安心写代码了。

图片剪裁

这里我选用的图片裁剪插件是 crop,不过它的实现方式是 RepaintBoundary ,所以这丫在Web端不能用(PC上的浏览器可以用,但是在手机上的浏览器就不支持,很迷~),所以没办法,只能退而求其次使用 image 库直接操作图片像素点裁剪图片。

/// Returns a round cropped copy of [src].
Image copyCropCircle(
  Image src, {
  int radius,
  Point center,
}) {
  int min(num x, num y) => (x < y ? x : y).toInt();
  final defaultRadius = min(src.width, src.height) ~/ 2;
  radius ??= defaultRadius;
  center ??= Point(src.width ~/ 2, src.height ~/ 2);
  // Make sure center point is within the range of the src image
  center.x = center.x.clamp(0, src.width - 1).toInt();
  center.y = center.y.clamp(0, src.height - 1).toInt();
  radius = radius < 1 ? defaultRadius : radius;

  final tlx = center.x.toInt() - radius; //topLeft.x
  final tly = center.y.toInt() - radius; //topLeft.y

  var dst = Image(
    radius * 2,
    radius * 2,
    channels: Channels.rgba,
    iccp: src.iccProfile,
  );

  for (var yi = 0, sy = tly; yi < radius * 2; ++yi, ++sy) {
    for (var xi = 0, sx = tlx; xi < radius * 2; ++xi, ++sx) {
      if ((xi - radius) * (xi - radius) + (yi - radius) * (yi - radius) <=
          radius * radius) {
        dst.setPixel(xi, yi, src.getPixelSafe(sx, sy));
      }
    }
  }

  return dst;
}

条件导包

由于dart:io在web端不受支持,所以我们需要使用其它实现来替代dart:io,这就涉及到了如何在dart中实现条件导包

一个简单的文件io的例子

//file/file_io.dart
import 'dart:io';
import 'dart:typed_data';

import 'package:path_provider/path_provider.dart';

class FileTool {
  static Future<Uint8List> read(String path) async {
    final directory = await getApplicationDocumentsDirectory();
    var file = File(directory.path + path);
    if (!await file.exists()) return null;
    return await file.readAsBytes();
  }

  static Future<void> write(String path, Uint8List bytes) async {
    final directory = await getApplicationDocumentsDirectory();
    var file = File(directory.path + path);
    if (!await file.exists()) {
      file = await file.create(recursive: true);
    }
    await file.writeAsBytes(bytes, flush: true);
  }

  static Future<void> delete(String path) async {
    final directory = await getApplicationDocumentsDirectory();
    var file = File(directory.path + path);
    if (!await file.exists()) return;
    file.deleteSync();
  }
}

//file/file_web.dart
import 'dart:typed_data';

import '../hive_tool.dart';
import '../image/ui_image_tool.dart';

class FileTool {
  static Future<Uint8List> read(String path) async {
    if (!HiveTool().inited) await HiveTool().init();
    final value = await HiveTool().box.get(path);
    if (value == null) return null;
    return (value as String).toBytes();
  }

  static Future<void> write(String path, Uint8List bytes) async {
    if (!HiveTool().inited) await HiveTool().init();
    await HiveTool().box.put(path, bytes.toBase64());
  }

  static Future<void> delete(String path) async {
    if (!HiveTool().inited) await HiveTool().init();
    await HiveTool().box.delete(path);
  }
}

//file_tool.dart
export 'file/file_io.dart' if (dart.library.html) 'file/file_web.dart';

更新图标

在项目根目录放入1024x1024分辨率的APP图标,命名为 logo.png

dependencies:
  ...
  flutter_launcher_icons: ^0.8.1

flutter_icons:
  image_path: "logo.png"
  android: true
  ios: true

然后运行 flutter pub run flutter_launcher_icons:main 即可

开源地址

本项目开源地址 github.com/idootop/wat… , 欢迎🌟/PR ^^

Web端生成

flutter build web --release

Android端生成

flutter build apk --split-per-abi

PS:亦可支持iOS,Mac,Windows,Linux端,请自行打包

鸣谢

Flutter flutter.dev/

Flame flame-engine.org/

Forge2D github.com/flame-engin…

合成大西瓜 www.wesane.com/game/654/