├── test └── devilf_engine_test.dart ├── example ├── ios │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ └── project.pbxproj │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── .gitignore ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ └── Icon-512.png │ ├── manifest.json │ └── index.html ├── android │ ├── devilf.jks │ ├── gradle.properties │ ├── key.properties │ ├── app │ │ ├── src │ │ │ └── main │ │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ └── dev │ │ │ │ └── ym101 │ │ │ │ └── devilf │ │ │ │ └── MainActivity.java │ │ └── build.gradle │ ├── .gitignore │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ └── build.gradle ├── assets │ └── images │ │ └── sprite.png ├── test │ └── widget_test.dart ├── lib │ ├── game_manager.dart │ ├── main.dart │ ├── building │ │ └── stone_sprite.dart │ └── scene │ │ └── game_scene.dart ├── .metadata ├── README.md ├── .gitignore ├── pubspec.yaml └── pubspec.lock ├── logo.png ├── doc ├── doc.png └── doc.rp ├── lib ├── core │ ├── df_shape.dart │ ├── df_living.dart │ ├── df_size.dart │ ├── df_offset.dart │ ├── df_position.dart │ ├── df_rect.dart │ └── df_circle.dart ├── devilf_engine.dart ├── tiled │ ├── df_layer_type.dart │ ├── df_image_layer.dart │ ├── df_tile.dart │ ├── df_map_layer.dart │ ├── df_tile_layer.dart │ ├── df_object_group.dart │ ├── df_tile_set.dart │ ├── df_group_layer.dart │ ├── df_objects.dart │ └── df_tile_map.dart ├── util │ ├── df_ui_util.dart │ ├── df_util.dart │ ├── df_audio.dart │ └── df_astar.dart ├── game │ ├── df_camera.dart │ ├── df_game_loop.dart │ ├── df_assets_loader.dart │ ├── df_sprite_render_box.dart │ ├── df_game_widget.dart │ ├── df_animation.dart │ └── df_sprite_widget.dart ├── sprite │ ├── df_sprite.dart │ ├── df_text_sprite.dart │ ├── df_progress_sprite.dart │ ├── df_image_sprite.dart │ ├── df_animation_sprite.dart │ └── df_tile_map_sprite.dart └── widget │ ├── df_button.dart │ ├── df_check_button.dart │ └── df_joystick.dart ├── .metadata ├── README.md ├── CHANGELOG.md ├── LICENSE ├── pubspec.yaml ├── .gitignore └── pubspec.lock /test/devilf_engine_test.dart: -------------------------------------------------------------------------------- 1 | void main() {} 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/logo.png -------------------------------------------------------------------------------- /doc/doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/doc/doc.png -------------------------------------------------------------------------------- /doc/doc.rp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/doc/doc.rp -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/web/favicon.png -------------------------------------------------------------------------------- /example/android/devilf.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/android/devilf.jks -------------------------------------------------------------------------------- /example/assets/images/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/assets/images/sprite.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/key.properties: -------------------------------------------------------------------------------- 1 | keyAlias=devilf 2 | keyPassword=123456 3 | storeFile = ../devilf.jks 4 | storePassword =123456 -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /lib/core/df_shape.dart: -------------------------------------------------------------------------------- 1 | /// 形状 2 | class DFShape { 3 | /// 是否重叠 4 | bool overlaps(DFShape other) { 5 | print("00000000000000000000000000000000000000"); 6 | return false; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /lib/devilf_engine.dart: -------------------------------------------------------------------------------- 1 | library devilf_engine; 2 | 3 | /// 引擎配置 4 | class DFConfig { 5 | /// 调试模式 6 | static bool debug = false; 7 | /// 特效和碰撞区域上移动 8 | static int effectOffset = 16; 9 | } 10 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ym6745476/devilf_engine/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/core/df_living.dart: -------------------------------------------------------------------------------- 1 | /// 生物类 2 | class DFLiving { 3 | /// ID 4 | int id; 5 | 6 | /// 名字 7 | String name = ""; 8 | 9 | /// 是否死亡 10 | bool isDeath = false; 11 | 12 | /// 创建生物 13 | DFLiving(this.id,this.name); 14 | } 15 | -------------------------------------------------------------------------------- /lib/tiled/df_layer_type.dart: -------------------------------------------------------------------------------- 1 | /// Layer类型 2 | class DFLayerType { 3 | static String tileLayer = "tilelayer"; 4 | static String objectGroup = "objectgroup"; 5 | static String imageLayer = "imagelayer"; 6 | static String group = "group"; 7 | } 8 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | 7 | # Remember to never publicly share your keystore. 8 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 9 | 10 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:devilf_engine/util/df_util.dart'; 2 | 3 | void main() { 4 | 5 | /// 一维数组转二维数组 6 | List data = [0,0,0,0,0,1,1,1,1,1,0,0,0,0,0]; 7 | List> newData = DFUtil.to2dList(data, 5, 1); 8 | print(newData.toString()); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/lib/game_manager.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:devilf_engine/game/df_game_widget.dart'; 3 | 4 | /// 游戏管理器 5 | class GameManager { 6 | /// 可见区域宽度 7 | static double visibleWidth = 960; 8 | 9 | /// 可见区域高度 10 | static double visibleHeight = 640; 11 | 12 | /// 游戏组件 13 | static DFGameWidget? gameWidget; 14 | } 15 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: f4abaa0735eba4dfd8f33f73363911d63931fe03 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 1d9032c7e1d867f071f2277eb1673e8f9b0274e3 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /lib/core/df_size.dart: -------------------------------------------------------------------------------- 1 | /// 尺寸 2 | class DFSize { 3 | /// 宽度 4 | final double width; 5 | 6 | /// 高度 7 | final double height; 8 | 9 | /// 创建尺寸 10 | const DFSize(this.width, this.height); 11 | 12 | /// 转换为字符串 13 | @override 14 | String toString() { 15 | return "width:" + width.toString() + ",height:" + height.toString(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /lib/core/df_offset.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | /// 偏移量 4 | class DFOffset { 5 | /// x坐标偏移 6 | final double dx; 7 | 8 | /// y坐标偏移 9 | final double dy; 10 | 11 | /// 创建坐标偏移 12 | const DFOffset(this.dx, this.dy); 13 | 14 | /// 转换为Offset 15 | Offset toOffset() => Offset(dx, dy); 16 | 17 | /// 转换为字符串 18 | @override 19 | String toString() { 20 | return "dx:" + dx.toString() + ",dy:" + dy.toString(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/util/df_ui_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | /// UI工具类 5 | class DFUiUtil { 6 | ///显示弹出框 7 | static showLayer(BuildContext context, Widget child, {Function? onClosed}) { 8 | showDialog( 9 | context: context, 10 | barrierDismissible: false, 11 | builder: (BuildContext context) { 12 | return child; 13 | }, 14 | ).then((val) { 15 | print("关闭窗口:" + val.toString()); 16 | if (onClosed != null) { 17 | onClosed(val); 18 | } 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:4.1.0' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /lib/tiled/df_image_layer.dart: -------------------------------------------------------------------------------- 1 | import 'df_map_layer.dart'; 2 | 3 | /// ImageLayer节点 4 | class DFImageLayer extends DFMapLayer { 5 | String image; 6 | 7 | DFImageLayer({ 8 | required this.image, 9 | }); 10 | 11 | DFImageLayer.fromJson(Map json) : image = json['image'] { 12 | id = json['id']; 13 | name = json['name']; 14 | type = json['type'].toString(); 15 | visible = json['visible']; 16 | opacity = double.tryParse(json['opacity'].toString()) ?? 0.0; 17 | x = double.tryParse(json['x'].toString()) ?? 0.0; 18 | y = double.tryParse(json['y'].toString()) ?? 0.0; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/game/df_camera.dart: -------------------------------------------------------------------------------- 1 | import 'package:devilf_engine/core/df_offset.dart'; 2 | import 'package:devilf_engine/core/df_rect.dart'; 3 | import 'package:devilf_engine/sprite/df_sprite.dart'; 4 | 5 | /// 摄像机 6 | class DFCamera { 7 | /// 跟踪目标 8 | DFSprite? sprite; 9 | 10 | /// 视窗区域 11 | final DFRect rect; 12 | 13 | /// 限制X 14 | DFOffset? limit; 15 | 16 | /// 创建矩形 17 | DFCamera({required this.rect}); 18 | 19 | /// 设置跟随目标 20 | void lookAt(DFSprite sprite) { 21 | this.sprite = sprite; 22 | } 23 | 24 | /// 设置限制 25 | void setLimit(DFOffset limit) { 26 | this.limit = limit; 27 | print("Camera setLimit:" + limit.toString()); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/scene/game_scene.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | main() async { 6 | /// 游戏场景 7 | GameScene gameScene = GameScene(); 8 | 9 | /// 运行游戏 10 | runApp(MainApp(gameScene)); 11 | } 12 | 13 | /// 主界面 14 | class MainApp extends StatelessWidget { 15 | 16 | /// 游戏场景 17 | final GameScene _gameScene; 18 | 19 | /// 创建主界面 20 | MainApp(this._gameScene); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return MaterialApp( 25 | title: 'DevilF', 26 | theme: ThemeData( 27 | primarySwatch: Colors.deepOrange, 28 | ), 29 | home: Scaffold( 30 | body: _gameScene, 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/tiled/df_tile.dart: -------------------------------------------------------------------------------- 1 | /// Tile节点 2 | class DFTile { 3 | int? id; 4 | int? columns; 5 | int? firsTgId; 6 | String? image; 7 | int? imageHeight; 8 | int? imageWidth; 9 | int? margin; 10 | String? name; 11 | int? spacing; 12 | int? tileCount; 13 | int? tileHeight; 14 | int? tileWidth; 15 | 16 | DFTile({this.firsTgId}); 17 | 18 | DFTile.fromJson(Map json) { 19 | firsTgId = json['firstgid']; 20 | columns = json['columns']; 21 | image = json['image']; 22 | imageHeight = json['imageheight']; 23 | imageWidth = json['imagewidth']; 24 | margin = json['margin']; 25 | name = json['name']; 26 | spacing = json['spacing']; 27 | tileCount = json['tilecount']; 28 | tileHeight = json['tileheight']; 29 | tileWidth = json['tilewidth']; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /lib/tiled/df_map_layer.dart: -------------------------------------------------------------------------------- 1 | /// MapLayer节点 2 | class DFMapLayer { 3 | int? id; 4 | String? name; 5 | bool? visible; 6 | double? x; 7 | double? y; 8 | String? type; 9 | double? opacity; 10 | 11 | DFMapLayer({this.id, this.name, this.type, this.visible, this.x, this.y, this.opacity}); 12 | 13 | DFMapLayer.fromJson(Map json) { 14 | id = json['id']; 15 | name = json['name']; 16 | type = json['type'].toString(); 17 | visible = json['visible']; 18 | opacity = double.tryParse(json['opacity'].toString()) ?? 0.0; 19 | x = double.parse(json['x'].toString()); 20 | y = double.parse(json['y'].toString()); 21 | } 22 | 23 | Map toJson() { 24 | final Map data = new Map(); 25 | data['id'] = this.id; 26 | data['name'] = this.name; 27 | data['type'] = this.type; 28 | data['visible'] = this.visible; 29 | data['x'] = this.x; 30 | data['y'] = this.y; 31 | return data; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/game/df_game_loop.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/scheduler.dart'; 2 | 3 | /// 游戏循环 4 | class DFGameLoop { 5 | Duration previous = Duration.zero; 6 | late Ticker _ticker; 7 | void Function(double dt) update; 8 | 9 | /// 创建游戏循环 10 | DFGameLoop(this.update) { 11 | _ticker = Ticker(_tick); 12 | } 13 | 14 | void _tick(Duration timestamp) { 15 | final dt = _computeDelta(timestamp); 16 | update(dt); 17 | } 18 | 19 | double _computeDelta(Duration now) { 20 | final delta = previous == Duration.zero ? Duration.zero : now - previous; 21 | previous = now; 22 | return delta.inMicroseconds / Duration.microsecondsPerSecond; 23 | } 24 | 25 | void start() { 26 | _ticker.start(); 27 | } 28 | 29 | void stop() { 30 | _ticker.stop(); 31 | } 32 | 33 | void dispose() { 34 | _ticker.dispose(); 35 | } 36 | 37 | void pause() { 38 | _ticker.muted = true; 39 | previous = Duration.zero; 40 | } 41 | 42 | void resume() { 43 | _ticker.muted = false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DevilF Engine 2 | 3 |

4 | 5 |

6 | 7 |

8 | 9 | 10 |

11 | 12 |

13 | A Flutter 2D RPG Game Engine On Web & Android & IOS. 14 |

15 | 16 |

17 | The Devilf Engine Is A Open Source 2D Game Engine. 18 | The Engine Is Development Using Flutter & Dart Language. 19 | You Can Use It Independently In Your Flutter Project. 20 | Some Documentation Of How To Use It Can Be Found Here. 21 |

22 | 23 | ## Home 24 | https://devilf.com 25 | 26 | ## Docs 27 | https://ymbok.com/book/devilf.html 28 | 29 | ## Example 30 | https://github.com/ym6745476/devilf 31 | 32 | ## Online Demo 33 | https://ymbok.com/download/slayer.html 34 | 35 | ## Install 36 | ```yaml 37 | dependencies: 38 | devilf: ^0.1.0 39 | ``` 40 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/lib/building/stone_sprite.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | import 'package:devilf_engine/core/df_position.dart'; 4 | import 'package:devilf_engine/core/df_size.dart'; 5 | import 'package:devilf_engine/sprite/df_sprite.dart'; 6 | import 'package:flutter/cupertino.dart'; 7 | 8 | /// 石头精灵类 9 | class StoneSprite extends DFSprite { 10 | StoneSprite({DFSize size = const DFSize(32, 32)}) : super(position: DFPosition(0, 0), size: size); 11 | 12 | @override 13 | void update(double dt) { 14 | this.angle += dt * 0.25; 15 | } 16 | 17 | @override 18 | void render(Canvas canvas) { 19 | canvas.save(); 20 | 21 | /// 将子精灵转换为相对坐标 22 | if (parent == null) { 23 | canvas.translate(position.x, position.y); 24 | } else { 25 | canvas.translate(position.x - parent!.size.width / 2, position.y - parent!.size.height / 2); 26 | } 27 | 28 | canvas.rotate(pi * 2 * this.angle); 29 | var paint = new Paint()..color = new Color(0xFFFF0000); 30 | canvas.drawRect(new Rect.fromLTWH(-size.width / 2, -size.height / 2, size.width, size.height), paint); 31 | canvas.restore(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/core/df_position.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | /// 位置 4 | class DFPosition { 5 | /// x坐标 6 | double x; 7 | 8 | /// y坐标 9 | double y; 10 | 11 | /// 创建坐标 12 | DFPosition(this.x, this.y); 13 | 14 | /// 转换为Offset 15 | Offset toOffset() => Offset(x, y); 16 | 17 | @override 18 | bool operator == (Object other) { 19 | if (other is DFPosition) { 20 | return x == other.x && y == other.y; 21 | } 22 | return false; 23 | } 24 | 25 | /// 转换为字符串 26 | @override 27 | String toString() { 28 | return "x:" + x.toString() + ",y:" + y.toString(); 29 | } 30 | } 31 | 32 | /// 瓦片位置 33 | class DFTilePosition { 34 | int x; 35 | int y; 36 | 37 | DFTilePosition(this.x, this.y); 38 | 39 | @override 40 | bool operator ==(Object other) { 41 | if (other is DFTilePosition) { 42 | return x == other.x && y == other.y; 43 | } 44 | return false; 45 | } 46 | 47 | @override 48 | String toString() { 49 | return "x:" + x.toString() + ",y:" + y.toString(); 50 | } 51 | } 52 | 53 | /// 方位 54 | enum DFGravity { 55 | /// 左 56 | left, 57 | 58 | /// 上 59 | top, 60 | 61 | /// 右 62 | right, 63 | 64 | /// 下 65 | bottom, 66 | 67 | /// 中 68 | center 69 | } 70 | -------------------------------------------------------------------------------- /lib/tiled/df_tile_layer.dart: -------------------------------------------------------------------------------- 1 | import 'df_map_layer.dart'; 2 | 3 | /// TileLayer节点 4 | class DFTileLayer extends DFMapLayer { 5 | List? data; 6 | double? height; 7 | double? width; 8 | 9 | DFTileLayer({ 10 | this.data, 11 | this.height, 12 | this.width, 13 | }); 14 | 15 | DFTileLayer.fromJson(Map json) { 16 | data = json['data'].cast(); 17 | height = double.tryParse(json['height'].toString()) ?? 0.0; 18 | id = json['id']; 19 | name = json['name']; 20 | opacity = double.tryParse(json['opacity'].toString()) ?? 0.0; 21 | type = json['type'].toString(); 22 | visible = json['visible']; 23 | width = double.tryParse(json['width'].toString()) ?? 0.0; 24 | x = double.tryParse(json['x'].toString()) ?? 0.0; 25 | y = double.tryParse(json['y'].toString()) ?? 0.0; 26 | } 27 | 28 | Map toJson() { 29 | final Map data = new Map(); 30 | data['data'] = this.data; 31 | data['height'] = this.height; 32 | data['id'] = this.id; 33 | data['name'] = this.name; 34 | data['opacity'] = this.opacity; 35 | data['type'] = this.type; 36 | data['visible'] = this.visible; 37 | data['width'] = this.width; 38 | data['x'] = this.x; 39 | data['y'] = this.y; 40 | return data; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.1.0] - 2021/09/26. 2 | * Update Flutter 2.5.0 3 | * Optimize 4 | * GameWidget Support GestureDetector 5 | * Update GameRenderBox as SpriteRenderBox 6 | * New SpriteWidget Class 7 | 8 | ## [0.0.9] - 2021/08/19. 9 | * Optimize 10 | * New CheckButton Widget 11 | * New UiUtil ShowLayer Method 12 | 13 | ## [0.0.8] - 2021/08/15. 14 | * Optimize 15 | 16 | ## [0.0.7] - 2021/08/10. 17 | * New A * Best Path Algorithm 18 | * New Collision Shape And Collision Function 19 | * New Draw Alpha Layer And Block layer 20 | 21 | ## [0.0.6] - 2021/08/01. 22 | * New Button Widget 23 | * New Map Tile Dynamic Load 24 | * New Camera, And Follows The Character 25 | * New TileMapSprite Support Tiled Json File Format 26 | * Optimize JoystickWidget Not Fixed Position 27 | * New ProgressSprite 28 | 29 | ## [0.0.5] - 2021/07/16. 30 | * Optimize 31 | 32 | ## [0.0.4] - 2021/07/15. 33 | * New JoystickWidget 34 | 35 | ## [0.0.3] - 2021/07/11. 36 | * New Sprite Rendering 37 | * Rename All Classes Add DF Prefix 38 | * New TexturePacker Plist->Json File Format 39 | * New AnimationSprite 40 | * New ImageSprite 41 | * New TextSprite 42 | 43 | ## [0.0.2] - 2021/07/07. 44 | * New GameScene 45 | * New AssetsLoader 46 | * New GameRenderBox 47 | 48 | ## [0.0.1] - 2021/07/06. 49 | * New GameLoop 50 | * New GameWidget 51 | * New Sprite -------------------------------------------------------------------------------- /lib/game/df_assets_loader.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:typed_data'; 4 | import 'dart:ui' as ui; 5 | import 'package:flutter/services.dart' show rootBundle; 6 | 7 | /// 资源加载和缓存管理 8 | class DFAssetsLoader { 9 | /// 缓存管理 10 | static final Map _files = {}; 11 | 12 | DFAssetsLoader(); 13 | 14 | /// 从Cache删除文件 15 | static void clear(String file) { 16 | _files.remove(file); 17 | } 18 | 19 | /// 清空缓存 20 | static void clearCache() { 21 | _files.clear(); 22 | } 23 | 24 | /// 读取图片 25 | static Future loadImage(String src) async { 26 | if (!_files.containsKey(src)) { 27 | final data = await rootBundle.load(src); 28 | final bytes = Uint8List.view(data.buffer); 29 | final completer = Completer(); 30 | ui.decodeImageFromList(bytes, completer.complete); 31 | _files[src] = completer.future; 32 | } 33 | return _files[src] as Future; 34 | } 35 | 36 | /// 读取文本文件 37 | static Future loadText(String src) async { 38 | if (!_files.containsKey(src)) { 39 | _files[src] = await rootBundle.loadString(src); 40 | } 41 | return _files[src] as String; 42 | } 43 | 44 | /// 读取Json文件 45 | static Future> loadJson(String src) async { 46 | final content = await rootBundle.loadString(src); 47 | return jsonDecode(content) as Map; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/tiled/df_object_group.dart: -------------------------------------------------------------------------------- 1 | import 'df_map_layer.dart'; 2 | import 'df_objects.dart'; 3 | 4 | /// ObjectGroup节点 5 | class DFObjectGroup extends DFMapLayer { 6 | String? drawOrder; 7 | List? objects; 8 | 9 | DFObjectGroup({ 10 | this.drawOrder, 11 | this.objects, 12 | }); 13 | 14 | DFObjectGroup.fromJson(Map json) { 15 | drawOrder = json['draworder']; 16 | id = json['id']; 17 | name = json['name']; 18 | if (json['objects'] != null) { 19 | objects = []; 20 | json['objects'].forEach((v) { 21 | objects?.add(new DFObjects.fromJson(v)); 22 | }); 23 | } 24 | opacity = double.parse(json['opacity'].toString()); 25 | type = json['type'].toString(); 26 | visible = json['visible']; 27 | x = double.tryParse(json['x'].toString()) ?? 0.0; 28 | y = double.tryParse(json['y'].toString()) ?? 0.0; 29 | } 30 | 31 | Map toJson() { 32 | final Map data = new Map(); 33 | data['draworder'] = this.drawOrder; 34 | data['id'] = this.id; 35 | data['name'] = this.name; 36 | if (this.objects != null) { 37 | data['objects'] = this.objects?.map((v) => v.toJson()).toList(); 38 | } 39 | data['opacity'] = this.opacity; 40 | data['type'] = this.type; 41 | data['visible'] = this.visible; 42 | data['x'] = this.x; 43 | data['y'] = this.y; 44 | return data; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, zhaoqipeng 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /lib/tiled/df_tile_set.dart: -------------------------------------------------------------------------------- 1 | import 'df_tile.dart'; 2 | 3 | /// TileSet节点 4 | class DFTileSet { 5 | int? id; 6 | int? columns; 7 | int? firsTgId; 8 | String? image; 9 | int? imageHeight; 10 | int? imageWidth; 11 | int? margin; 12 | String? name; 13 | int? spacing; 14 | int? tileCount; 15 | int? tileHeight; 16 | int? tileWidth; 17 | List? tiles; 18 | 19 | DFTileSet({this.firsTgId}); 20 | 21 | DFTileSet.fromJson(Map json) { 22 | firsTgId = json['firstgid']; 23 | columns = json['columns']; 24 | image = json['image']; 25 | imageHeight = json['imageheight']; 26 | imageWidth = json['imagewidth']; 27 | margin = json['margin']; 28 | name = json['name']; 29 | spacing = json['spacing']; 30 | tileCount = json['tilecount']; 31 | tileHeight = json['tileheight']; 32 | tileWidth = json['tilewidth']; 33 | if(json['tiles']!=null){ 34 | tiles = List.generate(json['tiles'].length, (index) => DFTile.fromJson(json['tiles'][index])); 35 | } 36 | } 37 | 38 | Map toJson() { 39 | final Map data = new Map(); 40 | data['firsTgId'] = this.firsTgId; 41 | data['columns'] = this.columns; 42 | data['image'] = this.image; 43 | data['imageHeight'] = this.imageHeight; 44 | data['imageWidth'] = this.imageWidth; 45 | data['margin'] = this.margin; 46 | data['name'] = this.name; 47 | data['spacing'] = this.spacing; 48 | data['tileCount'] = this.tileCount; 49 | data['tileHeight'] = this.tileHeight; 50 | data['tileWidth'] = this.tileWidth; 51 | data['tiles'] = this.tiles; 52 | return data; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | example 30 | 31 | 32 | 33 | 36 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: devilf_engine 2 | description: A Flutter 2D RPG Game Engine On Web & Android & IOS. 3 | version: 0.1.0 4 | homepage: https://github.com/ym6745476/devilf_engine 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | audioplayers: ^0.20.1 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | 19 | # For information on the generic Dart part of this file, see the 20 | # following page: https://dart.dev/tools/pub/pubspec 21 | 22 | # The following section is specific to Flutter. 23 | flutter: 24 | 25 | # To add assets to your package, add an assets section, like this: 26 | # assets: 27 | # - images/a_dot_burr.jpeg 28 | # - images/a_dot_ham.jpeg 29 | # 30 | # For details regarding assets in packages, see 31 | # https://flutter.dev/assets-and-images/#from-packages 32 | # 33 | # An image asset can refer to one or more resolution-specific "variants", see 34 | # https://flutter.dev/assets-and-images/#resolution-aware. 35 | 36 | # To add custom fonts to your package, add a fonts section here, 37 | # in this "flutter" section. Each entry in this list should have a 38 | # "family" key with the font family name, and a "fonts" key with a 39 | # list giving the asset and other descriptors for the font. For 40 | # example: 41 | # fonts: 42 | # - family: Schyler 43 | # fonts: 44 | # - asset: fonts/Schyler-Regular.ttf 45 | # - asset: fonts/Schyler-Italic.ttf 46 | # style: italic 47 | # - family: Trajan Pro 48 | # fonts: 49 | # - asset: fonts/TrajanPro.ttf 50 | # - asset: fonts/TrajanPro_Bold.ttf 51 | # weight: 700 52 | # 53 | # For details regarding fonts in packages, see 54 | # https://flutter.dev/custom-fonts/#from-packages 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/ephemeral 64 | **/ios/Flutter/app.flx 65 | **/ios/Flutter/app.zip 66 | **/ios/Flutter/flutter_assets/ 67 | **/ios/Flutter/flutter_export_environment.sh 68 | **/ios/ServiceDefinitions.json 69 | **/ios/Runner/GeneratedPluginRegistrant.* 70 | 71 | # Exceptions to above rules. 72 | !**/ios/**/default.mode1v3 73 | !**/ios/**/default.mode2v3 74 | !**/ios/**/default.pbxuser 75 | !**/ios/**/default.perspectivev3 76 | -------------------------------------------------------------------------------- /lib/tiled/df_group_layer.dart: -------------------------------------------------------------------------------- 1 | import 'df_image_layer.dart'; 2 | import 'df_layer_type.dart'; 3 | import 'df_map_layer.dart'; 4 | import 'df_object_group.dart'; 5 | import 'df_tile_layer.dart'; 6 | 7 | /// GroupLayer节点 8 | class DFGroupLayer extends DFMapLayer { 9 | List? layers; 10 | 11 | DFGroupLayer.fromJson(Map json) { 12 | id = json['id']; 13 | name = json['name']; 14 | type = json['type'].toString(); 15 | visible = json['visible']; 16 | opacity = double.tryParse(json['opacity'].toString()) ?? 0.0; 17 | x = double.tryParse(json['x'].toString()) ?? 0.0; 18 | y = double.tryParse(json['y'].toString()) ?? 0.0; 19 | 20 | if (json['layers'] != null) { 21 | layers = []; 22 | json['layers'].forEach((v) { 23 | if (v['type'] == DFLayerType.tileLayer) { 24 | layers?.add(DFTileLayer.fromJson(v)); 25 | } else if (v['type'] == DFLayerType.objectGroup) { 26 | layers?.add(DFObjectGroup.fromJson(v)); 27 | } else if (v['type'] == DFLayerType.imageLayer) { 28 | layers?.add(DFImageLayer.fromJson(v)); 29 | } else if (v['type'] == DFLayerType.group) { 30 | layers?.add(DFGroupLayer.fromJson(v)); 31 | } else { 32 | layers?.add(DFMapLayer.fromJson(v)); 33 | } 34 | }); 35 | } 36 | } 37 | 38 | Map toJson() { 39 | final Map data = new Map(); 40 | data['id'] = this.id; 41 | data['name'] = this.name; 42 | if (this.layers != null) { 43 | data['layers'] = this.layers?.map((v) => v.toJson()).toList(); 44 | } 45 | data['opacity'] = this.opacity; 46 | data['type'] = this.type; 47 | data['visible'] = this.visible; 48 | data['x'] = this.x; 49 | data['y'] = this.y; 50 | return data; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/sprite/df_sprite.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:devilf_engine/core/df_rect.dart'; 3 | import 'package:devilf_engine/core/df_shape.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:devilf_engine/core/df_position.dart'; 6 | import 'package:devilf_engine/core/df_size.dart'; 7 | 8 | /// 基础精灵类 9 | class DFSprite { 10 | /// 唯一标识 11 | String? key; 12 | 13 | /// 相对坐标 14 | DFSprite? parent; 15 | 16 | /// 坐标 左上角是0点 17 | DFPosition position; 18 | 19 | /// 尺寸 20 | DFSize size; 21 | 22 | /// 角度 23 | double angle = 0; 24 | 25 | /// 缩放比例 26 | double scale = 1; 27 | 28 | /// 子精灵 29 | List children = []; 30 | 31 | /// 固定到屏幕 32 | bool fixed = false; 33 | 34 | /// 显示状态 35 | bool visible = true; 36 | 37 | /// 是否回收 38 | bool recyclable = false; 39 | 40 | /// 创建精灵 41 | DFSprite({required this.position, required this.size}); 42 | 43 | /// 增加子精灵 44 | void addChild(DFSprite sprite) { 45 | /// 绑定父和子的关系 46 | sprite.parent = this; 47 | children.add(sprite); 48 | } 49 | 50 | /// 移除子精灵 51 | void removeChild(DFSprite sprite) { 52 | sprite.parent = null; 53 | sprite.visible = false; 54 | sprite.recyclable = true; 55 | } 56 | 57 | /// 增加精灵 增加进来精灵才能被绘制 58 | void addChildren(List sprites) { 59 | sprites.forEach((sprite) { 60 | /// 绑定父和子的关系 61 | sprite.parent = this; 62 | }); 63 | children.addAll(sprites); 64 | } 65 | 66 | /// 碰撞形状 67 | DFShape getCollisionShape() { 68 | return DFRect(this.position.x - this.size.width / 2, this.position.y - this.size.height / 2, this.size.width, 69 | this.size.height); 70 | } 71 | 72 | /// 精灵更新 73 | void update(double dt) { 74 | /// 子类覆盖 75 | /// 清除不可见的并且需要回收的的精灵 76 | children.removeWhere((sprite) => (sprite.visible == false && sprite.recyclable)); 77 | } 78 | 79 | /// 精灵渲染 80 | void render(Canvas canvas) { 81 | /// 子类覆盖 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/core/df_rect.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'df_circle.dart'; 3 | import 'df_position.dart'; 4 | import 'df_shape.dart'; 5 | 6 | /// 矩形 7 | class DFRect extends DFShape { 8 | /// 左坐标 9 | double left = 0; 10 | 11 | /// 上坐标 12 | double top = 0; 13 | 14 | /// 右坐标 15 | double right = 0; 16 | 17 | /// 下坐标 18 | double bottom = 0; 19 | 20 | /// 宽度 21 | final double width; 22 | 23 | /// 高度 24 | final double height; 25 | 26 | /// 创建矩形 27 | DFRect(this.left, this.top, this.width, this.height) { 28 | this.right = this.left + this.width; 29 | this.bottom = this.top + this.height; 30 | } 31 | 32 | /// 创建矩形 33 | DFRect.fromCenter({ required DFPosition center, required this.width, required this.height}) { 34 | this.left = center.x - width / 2; 35 | this.top = center.y - height / 2; 36 | this.right = this.left + this.width; 37 | this.bottom = this.top + this.height; 38 | } 39 | 40 | /// 转换为Rect 41 | Rect toRect() => Rect.fromLTWH(this.left, this.top, width, height); 42 | 43 | /// 中心坐标 44 | DFPosition center() { 45 | return DFPosition(this.left + this.width / 2, this.top + this.height / 2); 46 | } 47 | 48 | /// 是否重叠 49 | @override 50 | bool overlaps(DFShape other) { 51 | if (other is DFRect) { 52 | return rectToRect(other); 53 | } else if (other is DFCircle) { 54 | return other.circleToRect(this); 55 | } 56 | return false; 57 | } 58 | 59 | /// 矩形碰撞 60 | bool rectToRect(DFRect other) { 61 | if (right <= other.left || other.right <= left) return false; 62 | if (bottom <= other.top || other.bottom <= top) return false; 63 | return true; 64 | } 65 | 66 | /// 转换为字符串 67 | @override 68 | String toString() { 69 | return "left:" + 70 | left.toString() + 71 | ",top:" + 72 | top.toString() + 73 | ",width:" + 74 | width.toString() + 75 | ",height:" + 76 | height.toString(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | //add------------------start 2 | def keystorePropertiesFile = rootProject.file("key.properties") 3 | def keystoreProperties = new Properties() 4 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 5 | //add------------------end 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterRoot = localProperties.getProperty('flutter.sdk') 16 | if (flutterRoot == null) { 17 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 18 | } 19 | 20 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 21 | if (flutterVersionCode == null) { 22 | flutterVersionCode = '1' 23 | } 24 | 25 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 26 | if (flutterVersionName == null) { 27 | flutterVersionName = '1.0' 28 | } 29 | 30 | apply plugin: 'com.android.application' 31 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 32 | 33 | android { 34 | compileSdkVersion 30 35 | 36 | defaultConfig { 37 | applicationId "dev.ym101.devilf" 38 | minSdkVersion 20 39 | targetSdkVersion 30 40 | versionCode flutterVersionCode.toInteger() 41 | versionName flutterVersionName 42 | } 43 | 44 | signingConfigs { 45 | // keystore 46 | release { 47 | keyAlias keystoreProperties['keyAlias'] 48 | keyPassword keystoreProperties['keyPassword'] 49 | storeFile file(keystoreProperties['storeFile']) 50 | storePassword keystoreProperties['storePassword'] 51 | } 52 | 53 | } 54 | 55 | buildTypes { 56 | release { 57 | signingConfig signingConfigs.release 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | -------------------------------------------------------------------------------- /lib/sprite/df_text_sprite.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui; 2 | import 'package:devilf_engine/core/df_position.dart'; 3 | import 'package:devilf_engine/core/df_size.dart'; 4 | import 'package:devilf_engine/sprite/df_sprite.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | /// 文本精灵类 9 | class DFTextSprite extends DFSprite { 10 | /// 文本内容 11 | String text; 12 | 13 | /// 文字大小 14 | double fontSize; 15 | 16 | /// 文字大小 17 | Color color; 18 | 19 | /// 背景颜色 20 | Color background; 21 | 22 | /// 更新监听函数 23 | void Function(double dt)? onUpdate; 24 | 25 | /// 创建文本精灵 26 | DFTextSprite(this.text, 27 | {this.fontSize = 14, 28 | this.color = const Color(0xFFFFFFFF), 29 | this.background = const Color(0x00FFFFFF), 30 | DFSize size = const DFSize(80, 20)}) 31 | : super(position: DFPosition(0, 0), size: size); 32 | 33 | /// 更新文本 34 | @override 35 | void update(double dt) { 36 | if (onUpdate != null) { 37 | onUpdate!(dt); 38 | } 39 | } 40 | 41 | /// 设置更新函数 42 | void setOnUpdate(Function(double dt) onUpdate) { 43 | this.onUpdate = onUpdate; 44 | } 45 | 46 | /// 渲染精灵 47 | @override 48 | void render(Canvas canvas) { 49 | /// 画布暂存 50 | canvas.save(); 51 | 52 | /// 将子精灵转换为相对坐标 53 | if (parent == null) { 54 | canvas.translate(position.x, position.y); 55 | } else { 56 | canvas.translate(position.x - parent!.size.width / 2, position.y - parent!.size.height / 2); 57 | } 58 | 59 | /// 文本内容 60 | ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle( 61 | textAlign: TextAlign.center, 62 | fontStyle: FontStyle.normal, 63 | fontSize: this.fontSize, 64 | )) 65 | ..pushStyle(ui.TextStyle(color: this.color)) 66 | ..addText(this.text); 67 | 68 | ui.ParagraphConstraints pc = ui.ParagraphConstraints(width: size.width); 69 | ui.Paragraph paragraph = pb.build()..layout(pc); 70 | 71 | canvas.drawParagraph(paragraph, Offset(-paragraph.width / 2, -paragraph.height / 2)); 72 | 73 | /// 精灵矩形边界 74 | var paint = new Paint()..color = background; 75 | canvas.drawRect( 76 | Rect.fromLTWH(-paragraph.width / 2, -paragraph.height / 2, paragraph.width, paragraph.height), paint); 77 | 78 | ///恢复画布 79 | canvas.restore(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/tiled/df_objects.dart: -------------------------------------------------------------------------------- 1 | /// Objects节点 2 | class DFObjects { 3 | double? height; 4 | int? id; 5 | String? name; 6 | double? rotation; 7 | String? type; 8 | bool? visible; 9 | double? width; 10 | double? x; 11 | double? y; 12 | List? properties; 13 | 14 | DFObjects({ 15 | this.height, 16 | this.id, 17 | this.name, 18 | this.rotation, 19 | this.type, 20 | this.visible, 21 | this.width, 22 | this.x, 23 | this.y, 24 | this.properties, 25 | }); 26 | 27 | DFObjects.fromJson(Map json) { 28 | height = double.tryParse(json['height'].toString()) ?? 0.0; 29 | id = json['id']; 30 | name = json['name']; 31 | rotation = double.tryParse(json['rotation'].toString()) ?? 0.0; 32 | type = json['type']; 33 | visible = json['visible']; 34 | width = double.tryParse(json['width'].toString()) ?? 0.0; 35 | x = double.tryParse(json['x'].toString()) ?? 0.0; 36 | y = double.tryParse(json['y'].toString()) ?? 0.0; 37 | if (json['properties'] != null) { 38 | properties = []; 39 | json['properties'].forEach((v) { 40 | properties?.add(new Property.fromJson(v)); 41 | }); 42 | } 43 | } 44 | 45 | Map toJson() { 46 | final Map data = new Map(); 47 | data['height'] = this.height; 48 | data['id'] = this.id; 49 | data['name'] = this.name; 50 | data['rotation'] = this.rotation; 51 | data['type'] = this.type; 52 | data['visible'] = this.visible; 53 | data['width'] = this.width; 54 | data['x'] = this.x; 55 | data['y'] = this.y; 56 | if (this.properties != null) { 57 | data['properties'] = this.properties?.map((v) => v.toJson()).toList(); 58 | } 59 | return data; 60 | } 61 | } 62 | 63 | class Property { 64 | String? name; 65 | String? type; 66 | dynamic? value; 67 | 68 | Property({this.name, this.type, this.value}); 69 | 70 | Property.fromJson(Map json) { 71 | name = json['name']; 72 | type = json['type']; 73 | value = json['value']; 74 | } 75 | 76 | Map toJson() { 77 | final Map data = new Map(); 78 | data['name'] = this.name; 79 | data['type'] = this.type; 80 | data['value'] = this.value; 81 | return data; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/core/df_circle.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'package:devilf_engine/util/df_util.dart'; 3 | import 'df_position.dart'; 4 | import 'df_rect.dart'; 5 | import 'df_shape.dart'; 6 | 7 | /// 圆形 8 | class DFCircle extends DFShape { 9 | /// 中心坐标 10 | final DFPosition center; 11 | 12 | /// 半径 13 | final double radius; 14 | 15 | /// 创建圆形 16 | DFCircle(this.center, this.radius); 17 | 18 | /// 转换为Rect 19 | DFRect toRect() => DFRect(this.center.x - radius, this.center.y - radius, radius * 2, radius * 2); 20 | 21 | /// 是否重叠 22 | @override 23 | bool overlaps(DFShape other) { 24 | if (other is DFRect) { 25 | return circleToRect(other); 26 | } else if (other is DFCircle) { 27 | return circleToCircle(other); 28 | } 29 | return false; 30 | } 31 | 32 | /// 圆形和矩形重叠 33 | bool circleToRect(DFRect other) { 34 | /// 矩形不重叠,不用判断圆形了 35 | if (!toRect().overlaps(other)) { 36 | return false; 37 | } 38 | 39 | /// 排除矩形包含了圆形 40 | if (other.right >= this.center.x + this.radius && 41 | other.left <= this.center.x - this.radius && 42 | other.top <= this.center.y - this.radius && 43 | other.bottom >= this.center.y + this.radius) { 44 | return true; 45 | } 46 | 47 | /// 顶点 48 | final points = [ 49 | DFPosition(other.left, other.top), 50 | DFPosition(other.right, other.top), 51 | DFPosition(other.right, other.bottom), 52 | DFPosition(other.left, other.bottom), 53 | DFPosition(other.left, other.top), 54 | ]; 55 | 56 | /// print(other.toString()); 57 | for (var i = 0; i < points.length - 1; i++) { 58 | final distance = DFUtil.getNearestDistance(points[i], points[i + 1], this.center); 59 | 60 | /// print((distance).toString()); 61 | if (DFUtil.fixDouble4(distance) <= this.radius) { 62 | return true; 63 | } 64 | } 65 | return false; 66 | } 67 | 68 | /// 圆形与圆形重叠 69 | bool circleToCircle(DFCircle other) { 70 | if (!this.toRect().rectToRect(other.toRect())) return false; 71 | final distance = this.radius + other.radius; 72 | final w = this.center.x - other.center.x; 73 | final h = this.center.y - other.center.y; 74 | return sqrt(w * w + h * h) <= distance; 75 | } 76 | 77 | /// 转换字符串 78 | @override 79 | String toString() { 80 | return "center:" + center.toString() + ",radius:" + radius.toString(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/game/df_sprite_render_box.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/gestures.dart'; 4 | import 'package:flutter/rendering.dart'; 5 | import 'package:flutter/scheduler.dart'; 6 | import 'package:flutter/widgets.dart' hide WidgetBuilder; 7 | import 'df_game_loop.dart'; 8 | import 'df_sprite_widget.dart'; 9 | 10 | /// 精灵渲染盒 11 | class DFSpriteRenderBox extends RenderBox with WidgetsBindingObserver { 12 | /// 上下文 13 | BuildContext context; 14 | 15 | /// 精灵控件 16 | DFSpriteWidget spriteWidget; 17 | 18 | /// 游戏循环 19 | DFGameLoop? gameLoop; 20 | 21 | /// 创建渲染盒 22 | DFSpriteRenderBox(this.context, this.spriteWidget) { 23 | WidgetsBinding.instance!.addTimingsCallback(spriteWidget.onTimingsCallback); 24 | } 25 | 26 | /// 附加 27 | @override 28 | void attach(PipelineOwner owner) { 29 | super.attach(owner); 30 | 31 | ///启动游戏循环 32 | this.gameLoop = DFGameLoop(gameUpdate); 33 | this.gameLoop?.start(); 34 | 35 | ///绑定生命周期监听 36 | bindLifecycleListener(); 37 | } 38 | 39 | /// 取消附加 40 | @override 41 | void detach() { 42 | super.detach(); 43 | gameLoop?.dispose(); 44 | gameLoop = null; 45 | unbindLifecycleListener(); 46 | } 47 | 48 | /// 游戏循环更新 49 | void gameUpdate(double dt) { 50 | if (!attached) { 51 | return; 52 | } 53 | spriteWidget.update(dt); 54 | markNeedsPaint(); 55 | } 56 | 57 | /// 绘制界面 58 | @override 59 | void paint(PaintingContext context, Offset offset) { 60 | context.canvas.save(); 61 | context.canvas.translate(offset.dx, offset.dy); 62 | spriteWidget.render(context.canvas); 63 | context.canvas.restore(); 64 | } 65 | 66 | /// 事件分发 67 | @override 68 | bool hitTest(HitTestResult result, {required Offset position}) { 69 | if (size.contains(position)) { 70 | result.add(BoxHitTestEntry(this, position)); 71 | return true; 72 | } 73 | return false; 74 | } 75 | 76 | /// 事件分发 77 | @override 78 | bool hitTestSelf(Offset position) => true; 79 | 80 | /// 重绘 81 | @override 82 | bool get isRepaintBoundary => true; 83 | 84 | /// 计算尺寸 85 | @override 86 | void performLayout() { 87 | size = constraints.biggest; 88 | } 89 | 90 | /// 计算布局 91 | @override 92 | Size computeDryLayout(BoxConstraints constraints) => constraints.biggest; 93 | 94 | /// 状态改变 95 | @override 96 | void didChangeAppLifecycleState(AppLifecycleState state) { 97 | spriteWidget.lifecycleStateChange(state); 98 | } 99 | 100 | /// 监听Widget状态 101 | void bindLifecycleListener() { 102 | WidgetsBinding.instance!.addObserver(this); 103 | } 104 | 105 | /// 不监听Widget状态 106 | void unbindLifecycleListener() { 107 | WidgetsBinding.instance!.removeObserver(this); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/sprite/df_progress_sprite.dart: -------------------------------------------------------------------------------- 1 | import 'package:devilf_engine/core/df_position.dart'; 2 | import 'package:devilf_engine/core/df_size.dart'; 3 | import 'package:devilf_engine/sprite/df_sprite.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'dart:ui' as ui; 6 | import 'package:flutter/material.dart'; 7 | 8 | /// 进度精灵类 9 | class DFProgressSprite extends DFSprite { 10 | /// 精灵图片 11 | ui.Image image; 12 | 13 | /// 当前进度 14 | int progress = 100; 15 | 16 | /// 最大进度 17 | int maxProgress = 100; 18 | 19 | /// 显示进度文本 20 | bool showText; 21 | 22 | /// 文本位置 23 | DFGravity gravity; 24 | 25 | /// 文本位置偏移 26 | double textOffset; 27 | 28 | /// 创建 29 | DFProgressSprite(this.image, 30 | {this.showText = true, this.gravity = DFGravity.center, this.textOffset = 0, DFSize size = const DFSize(47, 8)}) 31 | : super(position: DFPosition(0, 0), size: size); 32 | 33 | /// 精灵更新 34 | @override 35 | void update(double dt) {} 36 | 37 | /// 精灵渲染 38 | @override 39 | void render(Canvas canvas) { 40 | canvas.save(); 41 | 42 | /// 将子精灵转换为相对坐标 43 | if (parent == null) { 44 | canvas.translate(position.x, position.y); 45 | } else { 46 | canvas.translate(position.x - parent!.size.width / 2, position.y - parent!.size.height / 2); 47 | } 48 | 49 | Rect srcRect = Rect.fromLTWH(0, this.size.height + 2, this.size.width, this.size.height); 50 | Rect dstRect = 51 | Rect.fromLTWH(-size.width / 2 * scale, -size.height / 2 * scale, size.width * scale, size.height * scale); 52 | 53 | Rect srcRectFill = Rect.fromLTWH(0, 0, this.size.width, this.size.height); 54 | Rect dstRectFill = Rect.fromLTWH( 55 | -size.width / 2 * scale, -size.height / 2 * scale, size.width * progress / 100 * scale, size.height * scale); 56 | 57 | /// 绘制图像 58 | Paint paint = Paint()..color = Color(0xFFFFFFFF); 59 | canvas.drawImageRect(this.image, srcRect, dstRect, paint); 60 | canvas.drawImageRect(this.image, srcRectFill, dstRectFill, paint); 61 | 62 | /// 文本内容 63 | ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle( 64 | textAlign: TextAlign.center, 65 | fontStyle: FontStyle.normal, 66 | fontSize: 8, 67 | )) 68 | ..pushStyle(ui.TextStyle(color: Colors.white)) 69 | ..addText(progress.toString() + "/" + maxProgress.toString()); 70 | 71 | ui.ParagraphConstraints pc = ui.ParagraphConstraints(width: size.width * scale * 1.5); 72 | ui.Paragraph paragraph = pb.build()..layout(pc); 73 | 74 | /// 精灵矩形边界 75 | ///var paint5 = new Paint()..color = Color(0x60000000); 76 | ///canvas.drawRect(Rect.fromLTWH(-paragraph.width/2,-paragraph.height/2, paragraph.width, paragraph.height), paint5); 77 | 78 | if (this.gravity == DFGravity.top) { 79 | canvas.drawParagraph(paragraph, Offset(-paragraph.width / 2, -paragraph.height - textOffset)); 80 | } else if (this.gravity == DFGravity.center) { 81 | canvas.drawParagraph(paragraph, Offset(-paragraph.width / 2, -paragraph.height / 2)); 82 | } 83 | 84 | canvas.restore(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/game/df_game_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:devilf_engine/core/df_circle.dart'; 3 | import 'package:devilf_engine/core/df_position.dart'; 4 | import 'package:devilf_engine/sprite/df_sprite.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'df_camera.dart'; 7 | import 'df_sprite_widget.dart'; 8 | 9 | /// 游戏控件 10 | class DFGameWidget extends StatelessWidget { 11 | /// 尺寸 12 | final Size size; 13 | 14 | /// 摄像机 15 | final DFCamera camera; 16 | 17 | /// 精灵控件 18 | late DFSpriteWidget spriteWidget; 19 | 20 | /// 点击抬起 21 | Function(DFSprite sprite)? onTap; 22 | 23 | /// 当前帧数 24 | double fps = 60; 25 | 26 | /// 创建游戏控件 27 | DFGameWidget({ 28 | this.size = const Size(100, 100), 29 | required this.camera, 30 | this.onTap, 31 | }) { 32 | this.spriteWidget = DFSpriteWidget( 33 | camera: this.camera, 34 | onTimingsCallback: (List timings) { 35 | this.fps = 1 / (timings[0].totalSpan.inMilliseconds / 1000.0); 36 | }); 37 | } 38 | 39 | /// 增加精灵 增加进来精灵才能被绘制 40 | void addChild(DFSprite? sprite) { 41 | this.spriteWidget.addChild(sprite); 42 | } 43 | 44 | /// 插入精灵 增加进来精灵才能被绘制 45 | void insertChild(int index, DFSprite? sprite) { 46 | this.spriteWidget.insertChild(index, sprite); 47 | } 48 | 49 | /// 增加精灵 增加进来精灵才能被绘制 50 | void addChildren(List sprites) { 51 | this.spriteWidget.addChildren(sprites); 52 | } 53 | 54 | /// 插入精灵 增加进来精灵才能被绘制 55 | void insertChildren(int index, List sprites) { 56 | this.spriteWidget.insertChildren(index, sprites); 57 | } 58 | 59 | /// 删除精灵 60 | void removeChild(DFSprite sprite) { 61 | this.spriteWidget.removeChild(sprite); 62 | } 63 | 64 | /// 屏幕坐标转换为世界坐标 65 | DFPosition screenToWorldPosition(Offset localPosition){ 66 | if(this.camera.sprite != null){ 67 | /// 屏幕上的坐标转换为实际坐标 计算出屏幕的0点的实际地图坐标 68 | double moveX = this.camera.sprite!.position.x - this.camera.rect.width / 2; 69 | double moveY = this.camera.sprite!.position.y - this.camera.rect.height / 2; 70 | return DFPosition(localPosition.dx + moveX, localPosition.dy + moveY); 71 | }else{ 72 | return DFPosition(localPosition.dx, localPosition.dy); 73 | } 74 | } 75 | 76 | /// 点击监听 77 | void onTapUp(TapUpDetails details) { 78 | print("监听到点击:" + details.localPosition.toString()); 79 | for (int i = this.spriteWidget.children.length - 1; i >= 0; i--) { 80 | DFSprite sprite = this.spriteWidget.children[i]; 81 | /// 屏幕坐标转换为世界坐标 82 | DFPosition center = screenToWorldPosition(details.localPosition); 83 | if (sprite.getCollisionShape().overlaps(DFCircle(center, 5))) { 84 | if (this.onTap != null) { 85 | this.onTap!(sprite); 86 | } 87 | return; 88 | } 89 | } 90 | } 91 | 92 | /// 控件布局 93 | @override 94 | Widget build(BuildContext context) { 95 | return GestureDetector( 96 | child: Container( 97 | width: this.size.width, 98 | height: this.size.height, 99 | child: spriteWidget, 100 | ), 101 | onTapUp: this.onTapUp, 102 | ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^1.0.2 31 | devilf_engine: 32 | path: ../../devilf_engine/ 33 | 34 | dev_dependencies: 35 | flutter_test: 36 | sdk: flutter 37 | 38 | # For information on the generic Dart part of this file, see the 39 | # following page: https://dart.dev/tools/pub/pubspec 40 | 41 | # The following section is specific to Flutter. 42 | flutter: 43 | 44 | # The following line ensures that the Material Icons font is 45 | # included with your application, so that you can use the icons in 46 | # the material Icons class. 47 | uses-material-design: true 48 | assets: 49 | - assets/images/ 50 | 51 | # To add assets to your application, add an assets section, like this: 52 | # assets: 53 | # - images/a_dot_burr.jpeg 54 | # - images/a_dot_ham.jpeg 55 | 56 | # An image asset can refer to one or more resolution-specific "variants", see 57 | # https://flutter.dev/assets-and-images/#resolution-aware. 58 | 59 | # For details regarding adding assets from package dependencies, see 60 | # https://flutter.dev/assets-and-images/#from-packages 61 | 62 | # To add custom fonts to your application, add a fonts section here, 63 | # in this "flutter" section. Each entry in this list should have a 64 | # "family" key with the font family name, and a "fonts" key with a 65 | # list giving the asset and other descriptors for the font. For 66 | # example: 67 | # fonts: 68 | # - family: Schyler 69 | # fonts: 70 | # - asset: fonts/Schyler-Regular.ttf 71 | # - asset: fonts/Schyler-Italic.ttf 72 | # style: italic 73 | # - family: Trajan Pro 74 | # fonts: 75 | # - asset: fonts/TrajanPro.ttf 76 | # - asset: fonts/TrajanPro_Bold.ttf 77 | # weight: 700 78 | # 79 | # For details regarding fonts from package dependencies, 80 | # see https://flutter.dev/custom-fonts/#from-packages 81 | -------------------------------------------------------------------------------- /lib/game/df_animation.dart: -------------------------------------------------------------------------------- 1 | /// 动作常量 2 | class DFAction { 3 | static const String NONE = "NONE"; 4 | static const String IDLE = "IDLE"; 5 | static const String RUN = "RUN"; 6 | static const String ATTACK = "ATTACK"; 7 | static const String CASTING = "CASTING"; 8 | static const String COLLECT = "COLLECT"; 9 | static const String PICKUP = "PICKUP"; 10 | static const String DEATH = "DEATH"; 11 | static const String TRACK = "TRACK"; 12 | static const String EXPLODE = "EXPLODE"; 13 | } 14 | 15 | /// 方向常量 16 | class DFDirection { 17 | static const String NONE = "_NONE"; 18 | static const String LEFT = "_LEFT"; 19 | static const String UP_LEFT = "_UP_LEFT"; 20 | static const String UP = "_UP"; 21 | static const String UP_RIGHT = "_UP_RIGHT"; 22 | static const String RIGHT = "_RIGHT"; 23 | static const String DOWN_RIGHT = "_DOWN_RIGHT"; 24 | static const String DOWN = "_DOWN"; 25 | static const String DOWN_LEFT = "_DOWN_LEFT"; 26 | } 27 | 28 | /// 动画常量 29 | class DFAnimation { 30 | static final List keys = [ 31 | DFAction.IDLE + DFDirection.LEFT, //0 32 | DFAction.IDLE + DFDirection.UP_LEFT, //1 33 | DFAction.IDLE + DFDirection.UP, //2 34 | DFAction.IDLE + DFDirection.UP_RIGHT, //3 35 | DFAction.IDLE + DFDirection.RIGHT, //4 36 | DFAction.IDLE + DFDirection.DOWN_RIGHT, //5 37 | DFAction.IDLE + DFDirection.DOWN, //6 38 | DFAction.IDLE + DFDirection.DOWN_LEFT, //7 39 | 40 | DFAction.RUN + DFDirection.LEFT, //8 41 | DFAction.RUN + DFDirection.UP_LEFT, //9 42 | DFAction.RUN + DFDirection.UP, //10 43 | DFAction.RUN + DFDirection.UP_RIGHT, //11 44 | DFAction.RUN + DFDirection.RIGHT, //12 45 | DFAction.RUN + DFDirection.DOWN_RIGHT, //13 46 | DFAction.RUN + DFDirection.DOWN, //14 47 | DFAction.RUN + DFDirection.DOWN_LEFT, //15 48 | 49 | DFAction.ATTACK + DFDirection.LEFT, //16 50 | DFAction.ATTACK + DFDirection.UP_LEFT, //17 51 | DFAction.ATTACK + DFDirection.UP, //18 52 | DFAction.ATTACK + DFDirection.UP_RIGHT, //19 53 | DFAction.ATTACK + DFDirection.RIGHT, //20 54 | DFAction.ATTACK + DFDirection.DOWN_RIGHT, //21 55 | DFAction.ATTACK + DFDirection.DOWN, //22 56 | DFAction.ATTACK + DFDirection.DOWN_LEFT, //23 57 | 58 | DFAction.CASTING + DFDirection.LEFT, //24 59 | DFAction.CASTING + DFDirection.UP_LEFT, //25 60 | DFAction.CASTING + DFDirection.UP, //26 61 | DFAction.CASTING + DFDirection.UP_RIGHT, //27 62 | DFAction.CASTING + DFDirection.RIGHT, //28 63 | DFAction.CASTING + DFDirection.DOWN_RIGHT, //29 64 | DFAction.CASTING + DFDirection.DOWN, //30 65 | DFAction.CASTING + DFDirection.DOWN_LEFT, //31 66 | 67 | DFAction.COLLECT + DFDirection.LEFT, //32 68 | DFAction.COLLECT + DFDirection.UP_LEFT, //33 69 | DFAction.COLLECT + DFDirection.UP, //34 70 | DFAction.COLLECT + DFDirection.UP_RIGHT, //35 71 | DFAction.COLLECT + DFDirection.RIGHT, //36 72 | DFAction.COLLECT + DFDirection.DOWN_RIGHT, //37 73 | DFAction.COLLECT + DFDirection.DOWN, //38 74 | DFAction.COLLECT + DFDirection.DOWN_LEFT, //39 75 | 76 | DFAction.DEATH + DFDirection.LEFT, //40 77 | DFAction.DEATH + DFDirection.UP_LEFT, //41 78 | DFAction.DEATH + DFDirection.UP, //42 79 | DFAction.DEATH + DFDirection.UP_RIGHT, //43 80 | DFAction.DEATH + DFDirection.RIGHT, //44 81 | DFAction.DEATH + DFDirection.DOWN_RIGHT, //45 82 | DFAction.DEATH + DFDirection.DOWN, //46 83 | DFAction.DEATH + DFDirection.DOWN_LEFT, //47 84 | 85 | DFAction.TRACK + DFDirection.UP, //48 86 | DFAction.EXPLODE + DFDirection.UP, //49 87 | ]; 88 | } 89 | -------------------------------------------------------------------------------- /lib/widget/df_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | /// 按钮 5 | class DFButton extends StatefulWidget { 6 | final String? text; 7 | final double fontSize; 8 | final Color textColor; 9 | final FontWeight fontWeight; 10 | final Size size; 11 | final String? image; 12 | final String? pressedImage; 13 | final void Function(DFButton button) onPressed; 14 | 15 | DFButton({ 16 | this.text, 17 | this.fontSize = 14, 18 | this.textColor = const Color(0xFFFFFFFF), 19 | this.fontWeight = FontWeight.normal, 20 | this.size = const Size(80, 80), 21 | this.image, 22 | this.pressedImage, 23 | required this.onPressed, 24 | }); 25 | 26 | @override 27 | _DFButtonState createState() => _DFButtonState(); 28 | } 29 | 30 | class _DFButtonState extends State { 31 | Color? _color; 32 | 33 | @override 34 | void initState() { 35 | super.initState(); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return SizedBox( 41 | width: widget.size.width, 42 | height: widget.size.height, 43 | child: GestureDetector( 44 | onTap: () { 45 | widget.onPressed(widget); 46 | }, 47 | onTapDown: (detail) { 48 | setState(() { 49 | this._color = Colors.black54; 50 | }); 51 | }, 52 | onTapCancel: () { 53 | setState(() { 54 | this._color = null; 55 | }); 56 | }, 57 | onTapUp: (detail) { 58 | setState(() { 59 | this._color = null; 60 | }); 61 | }, 62 | child: Stack(fit: StackFit.expand, children: [ 63 | widget.image != null 64 | ? Positioned( 65 | top: 0, 66 | left: 0, 67 | child: Container( 68 | child: Image.asset( 69 | widget.pressedImage != null ? widget.pressedImage! : widget.image!, 70 | fit: BoxFit.fill, 71 | width: widget.size.width, 72 | height: widget.size.height, 73 | color: this._color, 74 | colorBlendMode: BlendMode.dstIn, 75 | ), 76 | ), 77 | ) 78 | : Container(), 79 | widget.text != null 80 | ? Positioned( 81 | top: 0, 82 | left: 0, 83 | width: widget.size.width, 84 | height: widget.size.height, 85 | child: Column( 86 | mainAxisAlignment: MainAxisAlignment.center, 87 | crossAxisAlignment: CrossAxisAlignment.center, 88 | children: [ 89 | Text( 90 | widget.text!, 91 | textAlign: TextAlign.center, 92 | style: TextStyle( 93 | color: widget.textColor, 94 | fontSize: widget.fontSize, 95 | fontWeight: widget.fontWeight, 96 | ), 97 | ), 98 | ], 99 | ), 100 | ) 101 | : Container(), 102 | ]), 103 | )); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/widget/df_check_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | /// 选择按钮 5 | class DFCheckButton extends StatefulWidget { 6 | final int value; 7 | final String? text; 8 | final double fontSize; 9 | final Color textColor; 10 | final FontWeight fontWeight; 11 | final Size size; 12 | final String image; 13 | final String checkedImage; 14 | final Function(DFCheckButton button,bool checked, int value) onChanged; 15 | 16 | Function setChecked = (bool checked) { 17 | print("DFCheckButton setChecked:" + checked.toString()); 18 | }; 19 | 20 | DFCheckButton({ 21 | required this.value, 22 | required this.onChanged, 23 | required this.image, 24 | required this.checkedImage, 25 | this.text, 26 | this.fontSize = 14, 27 | this.textColor = const Color(0xFFFFFFFF), 28 | this.fontWeight = FontWeight.normal, 29 | this.size = const Size(80, 80), 30 | }); 31 | 32 | @override 33 | _DFCheckButtonState createState() => _DFCheckButtonState(); 34 | } 35 | 36 | class _DFCheckButtonState extends State { 37 | Color? _color; 38 | bool _checked = false; 39 | 40 | @override 41 | void initState() { 42 | super.initState(); 43 | widget.setChecked = this.setChecked; 44 | } 45 | 46 | void setChecked(bool checked) { 47 | print("setChecked:" + checked.toString()); 48 | setState(() { 49 | _checked = checked; 50 | }); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return SizedBox( 56 | width: widget.size.width, 57 | height: widget.size.height, 58 | child: GestureDetector( 59 | onTap: () { 60 | setState(() { 61 | this._checked = !this._checked; 62 | }); 63 | widget.onChanged(widget,this._checked, widget.value); 64 | }, 65 | onTapDown: (detail) { 66 | setState(() { 67 | this._color = Colors.black54; 68 | }); 69 | }, 70 | onTapCancel: () { 71 | setState(() { 72 | this._color = null; 73 | }); 74 | }, 75 | onTapUp: (detail) { 76 | setState(() { 77 | this._color = null; 78 | }); 79 | }, 80 | child: Row( 81 | mainAxisAlignment: MainAxisAlignment.center, 82 | crossAxisAlignment: CrossAxisAlignment.center, 83 | children: [ 84 | Container( 85 | child: Image.asset( 86 | _checked ? widget.checkedImage : widget.image, 87 | fit: BoxFit.fill, 88 | width: widget.size.width, 89 | height: widget.size.height, 90 | color: this._color, 91 | colorBlendMode: BlendMode.dstIn, 92 | ), 93 | ), 94 | widget.text != null? 95 | Container( 96 | padding: EdgeInsets.only(left: 5), 97 | child: Text( 98 | widget.text!, 99 | textAlign: TextAlign.center, 100 | style: TextStyle( 101 | color: widget.textColor, 102 | fontSize: widget.fontSize, 103 | fontWeight: widget.fontWeight, 104 | ), 105 | ), 106 | ):Container(), 107 | ]), 108 | )); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/sprite/df_image_sprite.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'package:devilf_engine/core/df_offset.dart'; 3 | import 'package:devilf_engine/core/df_position.dart'; 4 | import 'package:devilf_engine/core/df_rect.dart'; 5 | import 'package:devilf_engine/core/df_size.dart'; 6 | import 'package:devilf_engine/game/df_assets_loader.dart'; 7 | import 'package:devilf_engine/sprite/df_sprite.dart'; 8 | import 'package:flutter/cupertino.dart'; 9 | import 'dart:ui' as ui; 10 | 11 | /// 图片精灵类 12 | class DFImageSprite extends DFSprite { 13 | /// 精灵图片 14 | ui.Image image; 15 | 16 | /// 截取图片区域 17 | DFRect rect; 18 | 19 | /// 坐标偏移 20 | DFOffset offset; 21 | 22 | /// 是否旋转 23 | bool rotated; 24 | 25 | /// x轴镜像 26 | bool flippedX; 27 | 28 | /// 混合模式 29 | BlendMode blendMode; 30 | 31 | /// 颜色 32 | Color color; 33 | 34 | /// 创建图片精灵 35 | DFImageSprite( 36 | this.image, { 37 | DFSize size = const DFSize(64, 64), 38 | required this.rect, 39 | this.offset = const DFOffset(0, 0), 40 | this.color = const Color(0xFFFFFFFF), 41 | this.rotated = false, 42 | this.flippedX = false, 43 | this.blendMode = BlendMode.srcOver, 44 | }) : super(position: DFPosition(0, 0), size: size); 45 | 46 | /// 加载图片资源 47 | static Future load(String src) async { 48 | ui.Image image = await DFAssetsLoader.loadImage(src); 49 | return DFImageSprite(image, rect: DFRect(0, 0, image.width.toDouble(), image.height.toDouble())); 50 | } 51 | 52 | /// 精灵渲染 53 | /// 代码没几行,坐标计算有点复杂 54 | @override 55 | void render(Canvas canvas) { 56 | /// 画布暂存 57 | canvas.save(); 58 | 59 | /// 目标位置 60 | Rect dstRect; 61 | 62 | if (rotated) { 63 | /// 将子精灵转换为相对坐标 64 | if (parent == null) { 65 | canvas.translate(position.y, position.x); 66 | } else { 67 | canvas.translate(position.y - parent!.size.height / 2, position.x - parent!.size.width / 2); 68 | } 69 | 70 | /// 针对json中的图像旋转 71 | canvas.rotate(-90 * pi / 180); //弧度 72 | 73 | dstRect = Rect.fromCenter(center: Offset(0, 0), width: rect.width * scale, height: rect.height * scale); 74 | } else { 75 | /// 将子精灵转换为相对坐标 76 | if (parent == null) { 77 | canvas.translate(position.x, position.y); 78 | } else { 79 | canvas.translate(position.x - parent!.size.width / 2, position.y - parent!.size.height / 2); 80 | } 81 | canvas.rotate(this.angle * pi / 180); //弧度 82 | 83 | dstRect = 84 | Rect.fromCenter(center: Offset(0, -rect.height / 2), width: rect.width * scale, height: rect.height * scale); 85 | } 86 | 87 | /// 水平镜像 88 | if (flippedX) { 89 | if (rotated) { 90 | canvas.scale(1, -1); //左右镜像翻转 91 | } else { 92 | canvas.scale(-1, 1); //左右镜像翻转 93 | } 94 | } 95 | 96 | /// 目标绘制位置 97 | if (rotated) { 98 | dstRect = Rect.fromCenter(center: Offset(offset.dx, offset.dy), width: rect.width, height: rect.height); 99 | } else { 100 | dstRect = Rect.fromCenter(center: Offset(offset.dx, -offset.dy), width: rect.width, height: rect.height); 101 | } 102 | 103 | /// 处理缩放 104 | Rect outputRect = Rect.fromCenter( 105 | center: Offset(dstRect.center.dx * scale, dstRect.center.dy * scale), 106 | width: dstRect.width * scale, 107 | height: dstRect.height * scale); 108 | 109 | /// 绘制图像 110 | Paint paintImage = Paint()..color = color; 111 | paintImage.blendMode = this.blendMode; 112 | canvas.drawImageRect(this.image, rect.toRect(), outputRect, paintImage); 113 | 114 | /// 画布恢复 115 | canvas.restore(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /lib/tiled/df_tile_map.dart: -------------------------------------------------------------------------------- 1 | import 'df_group_layer.dart'; 2 | import 'df_image_layer.dart'; 3 | import 'df_layer_type.dart'; 4 | import 'df_map_layer.dart'; 5 | import 'df_object_group.dart'; 6 | import 'df_tile.dart'; 7 | import 'df_tile_layer.dart'; 8 | import 'df_tile_set.dart'; 9 | 10 | /// 瓦片地图 11 | class DFTileMap { 12 | String? backgroundColor; 13 | int? compressionLevel; 14 | int? height; 15 | int? hexSideLength; 16 | bool? infinite; 17 | List? layers; 18 | int? nextLayerId; 19 | int? nextObjectId; 20 | String? orientation; 21 | String? renderOrder; 22 | String? staggerAxis; 23 | String? staggerIndex; 24 | String? tiledVersion; 25 | int? tileHeight; 26 | List? tileSets; 27 | int? tileWidth; 28 | String? type; 29 | double? version; 30 | int? width; 31 | 32 | DFTileMap({ 33 | this.compressionLevel, 34 | this.height, 35 | this.infinite, 36 | this.layers, 37 | this.nextLayerId, 38 | this.nextObjectId, 39 | this.orientation, 40 | this.renderOrder, 41 | this.tiledVersion, 42 | this.tileHeight, 43 | this.tileSets, 44 | this.tileWidth, 45 | this.type, 46 | this.version, 47 | this.width, 48 | }); 49 | 50 | DFTileMap.fromJson(Map json) { 51 | compressionLevel = json['compressionlevel']; 52 | height = json['height']; 53 | infinite = json['infinite']; 54 | if (json['layers'] != null) { 55 | layers = []; 56 | json['layers'].forEach((v) { 57 | if (v['type'] == DFLayerType.tileLayer) { 58 | layers?.add(DFTileLayer.fromJson(v)); 59 | } else if (v['type'] == DFLayerType.objectGroup) { 60 | layers?.add(DFObjectGroup.fromJson(v)); 61 | } else if (v['type'] == DFLayerType.imageLayer) { 62 | layers?.add(DFImageLayer.fromJson(v)); 63 | } else if (v['type'] == DFLayerType.group) { 64 | layers?.add(DFGroupLayer.fromJson(v)); 65 | } else { 66 | layers?.add(DFMapLayer.fromJson(v)); 67 | } 68 | }); 69 | } 70 | nextLayerId = json['nextlayerid']; 71 | nextObjectId = json['nextobjectid']; 72 | orientation = json['orientation']; 73 | renderOrder = json['renderorder']; 74 | tiledVersion = json['tiledversion']; 75 | tileHeight = json['tileheight']; 76 | if (json['tilesets'] != null) { 77 | tileSets = []; 78 | json['tilesets'].forEach((v) { 79 | tileSets?.add(DFTileSet.fromJson(v)); 80 | }); 81 | } 82 | tileWidth = json['tilewidth']; 83 | type = json['type']; 84 | version = double.tryParse(json['version'].toString()) ?? 0.0; 85 | width = json['width']; 86 | backgroundColor = json['backgroundcolor']; 87 | hexSideLength = json['hexsidelength']; 88 | staggerAxis = json['staggeraxis']; 89 | staggerIndex = json['staggerindex']; 90 | } 91 | 92 | Map toJson() { 93 | final Map data = new Map(); 94 | data['compressionlevel'] = this.compressionLevel; 95 | data['height'] = this.height; 96 | data['infinite'] = this.infinite; 97 | if (this.layers != null) { 98 | data['layers'] = this.layers?.map((v) => v.toJson()).toList(); 99 | } 100 | data['nextlayerid'] = this.nextLayerId; 101 | data['nextobjectid'] = this.nextObjectId; 102 | data['orientation'] = this.orientation; 103 | data['renderorder'] = this.renderOrder; 104 | data['tiledversion'] = this.tiledVersion; 105 | data['tileheight'] = this.tileHeight; 106 | if (this.tileSets != null) { 107 | data['tilesets'] = this.tileSets?.map((v) => v.toJson()).toList(); 108 | } 109 | data['tilewidth'] = this.tileWidth; 110 | data['type'] = this.type; 111 | data['version'] = this.version; 112 | data['width'] = this.width; 113 | return data; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/game/df_sprite_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:devilf_engine/sprite/df_sprite.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'df_camera.dart'; 5 | import 'df_sprite_render_box.dart'; 6 | 7 | /// 精灵渲染控件(游戏主循环) 8 | /// 继承LeafRenderObjectWidget实现RenderBox可以使用canvas实现Widget 9 | class DFSpriteWidget extends LeafRenderObjectWidget { 10 | /// 摄像机 11 | final DFCamera camera; 12 | 13 | /// 游戏里的所有精灵 14 | final List children = []; 15 | 16 | /// 时间回调 17 | final Function(List timings) onTimingsCallback; 18 | 19 | /// 创建游戏控件 20 | DFSpriteWidget({required this.camera, required this.onTimingsCallback}); 21 | 22 | /// 增加精灵 增加进来精灵才能被绘制 23 | void addChild(DFSprite? sprite) { 24 | if (sprite != null) { 25 | children.add(sprite); 26 | } 27 | } 28 | 29 | /// 插入精灵 增加进来精灵才能被绘制 30 | void insertChild(int index, DFSprite? sprite) { 31 | if (sprite != null) { 32 | children.insert(index, sprite); 33 | } 34 | } 35 | 36 | /// 增加精灵 增加进来精灵才能被绘制 37 | void addChildren(List sprites) { 38 | children.addAll(sprites); 39 | } 40 | 41 | /// 插入精灵 增加进来精灵才能被绘制 42 | void insertChildren(int index, List sprites) { 43 | sprites.forEach((element) { 44 | children.insert(index, element); 45 | }); 46 | } 47 | 48 | /// 删除精灵 49 | void removeChild(DFSprite sprite) { 50 | /// 不能直接remove会并发修改错误 51 | sprite.visible = false; 52 | sprite.recyclable = true; 53 | } 54 | 55 | /// 创建GameRenderBox 56 | @override 57 | RenderBox createRenderObject(BuildContext context) { 58 | return DFSpriteRenderBox(context, this); 59 | } 60 | 61 | /// 设置Game到GameRenderBox 62 | @override 63 | void updateRenderObject(BuildContext context, DFSpriteRenderBox renderObject) { 64 | renderObject.spriteWidget = this; 65 | } 66 | 67 | /// 更新界面 68 | void update(double dt) { 69 | children.forEach((sprite) { 70 | if (!sprite.visible && sprite.recyclable) { 71 | /// 将要回收的精灵不更新 72 | } else { 73 | sprite.update(dt); 74 | } 75 | }); 76 | 77 | /// 清除不可见的并且需要回收的的精灵 78 | children.removeWhere((sprite) => (sprite.visible == false && sprite.recyclable)); 79 | } 80 | 81 | /// 绘制界面 82 | void render(Canvas canvas) { 83 | canvas.save(); 84 | 85 | /// 需要移动的位置 86 | double moveX = 0; 87 | double moveY = 0; 88 | 89 | /// 跟随摄像机的精灵 90 | if (camera.sprite != null) { 91 | if (camera.limit != null) { 92 | /// 限制边界 93 | moveX = camera.rect.width / 2 - camera.sprite!.position.x; 94 | moveY = camera.rect.height / 2 - camera.sprite!.position.y; 95 | 96 | if (camera.sprite!.position.x <= camera.rect.width / 2) { 97 | moveX = 0; 98 | } 99 | if (camera.sprite!.position.y <= camera.rect.height / 2) { 100 | moveY = 0; 101 | } 102 | if (camera.sprite!.position.x >= camera.limit!.dx - camera.rect.width / 2) { 103 | moveX = camera.rect.width - camera.limit!.dx; 104 | } 105 | if (camera.sprite!.position.y >= camera.limit!.dy - camera.rect.height / 2) { 106 | moveY = camera.rect.height - camera.limit!.dy; 107 | } 108 | } else { 109 | moveX = camera.rect.width / 2 - camera.sprite!.position.x; 110 | moveY = camera.rect.height / 2 - camera.sprite!.position.y; 111 | } 112 | canvas.translate(moveX, moveY); 113 | } 114 | 115 | children.forEach((sprite) { 116 | if (sprite.visible) { 117 | if (!sprite.fixed) { 118 | sprite.render(canvas); 119 | } 120 | } 121 | }); 122 | 123 | /// 固定到屏幕位置的精灵 124 | if (camera.sprite != null) { 125 | canvas.translate(-moveX, -moveY); 126 | } 127 | children.forEach((sprite) { 128 | if (sprite.visible) { 129 | if (sprite.fixed) { 130 | sprite.render(canvas); 131 | } 132 | } 133 | }); 134 | 135 | canvas.restore(); 136 | } 137 | 138 | /// 生命周期发生变化 139 | void lifecycleStateChange(AppLifecycleState state) {} 140 | } 141 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/dev/ym101/devilf/MainActivity.java: -------------------------------------------------------------------------------- 1 | package dev.ym101.devilf; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | 6 | import org.json.JSONStringer; 7 | 8 | import java.util.HashMap; 9 | 10 | import io.flutter.embedding.android.FlutterActivity; 11 | import io.flutter.embedding.engine.FlutterEngine; 12 | import io.flutter.embedding.engine.dart.DartExecutor; 13 | import io.flutter.plugin.common.MethodChannel; 14 | 15 | /** 16 | * 如果从其他页面进入Flutter页面可以像下面这样写: 17 | * Intent intent = new Intent(activity, MainActivity.class); 18 | * intent.putExtra("route","/hello"); 19 | * startActivity(intent); 20 | */ 21 | public class MainActivity extends FlutterActivity { 22 | 23 | private static final String FLUTTER_CHANNEL = "sample.flutter.io/flutter"; 24 | private static final String NATIVE_CHANNEL = "sample.flutter.io/native"; 25 | private static final String NATIVE_METHOD = "callFlutter"; 26 | private static final String FLUTTER_METHOD = "callNative"; 27 | 28 | FlutterEngine flutterEngine; 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState){ 32 | super.onCreate(savedInstanceState); 33 | 34 | //从Intent里获取route 路径 35 | String route = getIntent().getStringExtra("route"); 36 | if(route == null || route == ""){ 37 | route = "/"; 38 | } 39 | 40 | //InitialRoute 41 | flutterEngine = getFlutterEngine(); 42 | flutterEngine.getNavigationChannel().setInitialRoute(route); 43 | flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault()); 44 | 45 | //监听Flutter调用Native 46 | MethodChannel methodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), FLUTTER_CHANNEL); 47 | methodChannel.setMethodCallHandler((call, result) -> { 48 | if (call.method.equals(FLUTTER_METHOD)) { 49 | doCallNative((HashMap)call.arguments,result); 50 | result.success("Native接到消息:" + call.arguments); 51 | } else { 52 | result.notImplemented(); 53 | } 54 | }); 55 | 56 | //向Flutter传递数据 57 | callFlutter(); 58 | } 59 | 60 | /** 61 | * 调用Flutter 62 | */ 63 | public void callFlutter(){ 64 | Log.e("MainActivity","调用Flutter start"); 65 | 66 | String arguments = "{\"test\":false,\"token\":\"ASDFGHJKL\",\"userId\":1}"; 67 | 68 | MethodChannel methodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), NATIVE_CHANNEL); 69 | methodChannel.invokeMethod(NATIVE_METHOD, arguments, new MethodChannel.Result() { 70 | 71 | @Override 72 | public void success(Object o) { 73 | Log.e("MainActivity","调用Flutter success"); 74 | } 75 | 76 | @Override 77 | public void error(String s, String s1, Object o) { 78 | Log.e("MainActivity","调用Flutter error"); 79 | } 80 | 81 | @Override 82 | public void notImplemented() { 83 | 84 | } 85 | }); 86 | } 87 | 88 | /** 89 | * 你的代码写处理逻辑 90 | * @param messageMap 91 | * @param result 92 | */ 93 | public void doCallNative(HashMap messageMap,MethodChannel.Result result){ 94 | String message = (String)messageMap.get("message"); 95 | //你的代码 96 | } 97 | 98 | @Override 99 | protected void onResume() { 100 | super.onResume(); 101 | if (flutterEngine!=null){ 102 | flutterEngine.getLifecycleChannel().appIsResumed(); 103 | } 104 | } 105 | 106 | @Override 107 | protected void onPause() { 108 | super.onPause(); 109 | if (flutterEngine!=null){ 110 | flutterEngine.getLifecycleChannel().appIsInactive(); 111 | } 112 | } 113 | 114 | @Override 115 | protected void onStop() { 116 | super.onStop(); 117 | if (flutterEngine!=null){ 118 | flutterEngine.getLifecycleChannel().appIsPaused(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/util/df_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'package:devilf_engine/core/df_position.dart'; 3 | import 'package:devilf_engine/game/df_animation.dart'; 4 | 5 | /// 工具类 6 | class DFUtil { 7 | /// 获取标准8方向 8 | static String getDirection(double radians) { 9 | /// 判断8方向 10 | String direction = DFDirection.NONE; 11 | 12 | /// 换算角度 13 | double angle = 180 / pi * radians; 14 | if (angle < 0) { 15 | angle = angle + 360; 16 | } 17 | 18 | /// 顺时针 0~360 19 | /// print("angle:" + angle.toString()); 20 | 21 | /// 右边 22 | if ((angle >= 0 && angle < 22.5) || (angle >= 337.5 && angle <= 360)) { 23 | direction = DFDirection.RIGHT; 24 | } 25 | 26 | /// 右下 27 | if (angle >= 22.5 && angle < 67.5) { 28 | direction = DFDirection.DOWN_RIGHT; 29 | } 30 | 31 | /// 下 32 | if (angle >= 67.5 && angle < 112.5) { 33 | direction = DFDirection.DOWN; 34 | } 35 | 36 | /// 左下 37 | if (angle >= 112.5 && angle < 157.5) { 38 | direction = DFDirection.DOWN_LEFT; 39 | } 40 | 41 | /// 左 42 | if (angle >= 157.5 && angle < 202.5) { 43 | direction = DFDirection.LEFT; 44 | } 45 | 46 | /// 左上 47 | if (angle >= 202.5 && angle < 247.5) { 48 | direction = DFDirection.UP_LEFT; 49 | } 50 | 51 | /// 上 52 | if (angle >= 247.5 && angle < 292.5) { 53 | direction = DFDirection.UP; 54 | } 55 | 56 | /// 右上 57 | if (angle >= 292.5 && angle < 337.5) { 58 | direction = DFDirection.UP_RIGHT; 59 | } 60 | 61 | /// 返回结果 62 | return direction; 63 | } 64 | 65 | /// 获取弧度 66 | static double getRadians(String direction) { 67 | double radians = 0; 68 | 69 | if (direction == DFDirection.DOWN_RIGHT) { 70 | radians = 45 * pi / 180.0; 71 | } else if (direction == DFDirection.UP_LEFT) { 72 | radians = 225 * pi / 180.0; 73 | } else if (direction == DFDirection.UP_RIGHT) { 74 | radians = 315 * pi / 180.0; 75 | } else if (direction == DFDirection.DOWN_LEFT) { 76 | radians = 135 * pi / 180.0; 77 | } else if (direction == DFDirection.RIGHT) { 78 | radians = 0; 79 | } else if (direction == DFDirection.LEFT) { 80 | radians = 180 * pi / 180.0; 81 | } else if (direction == DFDirection.DOWN) { 82 | radians = 90 * pi / 180.0; 83 | } else if (direction == DFDirection.UP) { 84 | radians = 270 * pi / 180.0; 85 | } 86 | 87 | /// 返回结果 88 | return radians; 89 | } 90 | 91 | /// Get [o] point distance [o1] and [o2] line segment distance 92 | static double getNearestDistance(DFPosition o1, DFPosition o2, DFPosition o) { 93 | if (o1 == o || o2 == o) return 0; 94 | 95 | final a = DFUtil.distanceTo(o2, o); 96 | final b = DFUtil.distanceTo(o1, o); 97 | final c = DFUtil.distanceTo(o1, o2); 98 | 99 | if (a * a >= b * b + c * c) return b; 100 | if (b * b >= a * a + c * c) return a; 101 | 102 | // 海伦公式 103 | final l = (a + b + c) / 2; 104 | final area = sqrt(l * (l - a) * (l - b) * (l - c)); 105 | return 2 * area / c; 106 | } 107 | 108 | /// 获取距离 109 | static double distanceTo(DFPosition point1, DFPosition point2) => sqrt(distanceToSquared(point1, point2)); 110 | 111 | /// 距离的平方 112 | static double distanceToSquared(DFPosition point1, DFPosition point2) { 113 | final dx = point1.x - point2.x; 114 | final dy = point1.y - point2.y; 115 | 116 | return dx * dx + dy * dy; 117 | } 118 | 119 | /// 将double转换为4位小数 120 | static double fixDouble4(double value) { 121 | return double.parse(value.toStringAsFixed(4)); 122 | } 123 | 124 | /// 转换为二维数组 125 | static List> to2dList(List data,int columnSize,int fillValue) { 126 | int rowSize = (data.length / columnSize).ceil(); 127 | List> dataNew = List.generate(rowSize,(index)=> List.generate(columnSize, (index) => 0)); 128 | /// print(dataNew.toString()); 129 | for (int i = 0; i < data.length; i++) { 130 | int row = (i / columnSize).floor(); 131 | int column = i % columnSize; 132 | /// print(row.toString() +","+ column.toString()+ ",value:" + data[i].toString()); 133 | if (data[i] != 0) { 134 | dataNew[row][column] = 1; 135 | } else { 136 | dataNew[row][column] = 0; 137 | } 138 | } 139 | return dataNew; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /lib/util/df_audio.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:audioplayers/audioplayers.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'dart:io'; 6 | import 'package:flutter/widgets.dart'; 7 | 8 | /// 音效播放 9 | class DFAudio { 10 | 11 | static String prefix = "assets/audio/"; 12 | 13 | /// 音频类 14 | static AudioCache audioCache = AudioCache(prefix: prefix); 15 | 16 | /// 计数 17 | int count = 0; 18 | 19 | /// 定时播放 20 | Timer? timer; 21 | 22 | /// 开始播放音效序列 23 | void startPlay(List files, {stepTime = 400, loop = false}) { 24 | if(files.length == 0){ 25 | return; 26 | } 27 | count = 0; 28 | timer = Timer.periodic(Duration(milliseconds: stepTime), (t) { 29 | if (count == files.length) { 30 | if (loop) { 31 | count = 0; 32 | String file = files.elementAt(count); 33 | play(file); 34 | } else { 35 | timer!.cancel(); 36 | timer = null; 37 | } 38 | } else { 39 | String file = files.elementAt(count); 40 | play(file); 41 | } 42 | count++; 43 | }); 44 | } 45 | 46 | /// 取消播放音效序列 47 | void stopPlay() { 48 | if (timer != null) { 49 | timer!.cancel(); 50 | timer = null; 51 | count = 0; 52 | } 53 | } 54 | 55 | /// 背景音乐 56 | static BackgroundMusic backgroundMusic = BackgroundMusic(audioCache: audioCache); 57 | 58 | /// 播放短音频 59 | static Future play(String file, {double volume = 1.0}) { 60 | return audioCache.play(file, volume: volume, mode: PlayerMode.LOW_LATENCY); 61 | } 62 | 63 | /// 循环播放 64 | static Future loop(String file, {double volume = 1.0}) { 65 | return audioCache.loop(file, volume: volume, mode: PlayerMode.LOW_LATENCY); 66 | } 67 | 68 | /// 播放长音频 69 | static Future playLongAudio(String file, {double volume = 1.0}) { 70 | return audioCache.play(file, volume: volume); 71 | } 72 | 73 | /// 循环播放长音频 74 | static Future loopLongAudio(String file, {double volume = 1.0}) { 75 | return audioCache.loop(file, volume: volume); 76 | } 77 | } 78 | 79 | /// 背景音乐 80 | class BackgroundMusic extends WidgetsBindingObserver { 81 | bool _isRegistered = false; 82 | late AudioCache audioCache; 83 | AudioPlayer? audioPlayer; 84 | bool isPlaying = false; 85 | 86 | BackgroundMusic({AudioCache? audioCache}) : audioCache = audioCache ?? AudioCache(); 87 | 88 | void initialize() { 89 | if (_isRegistered) { 90 | return; 91 | } 92 | _isRegistered = true; 93 | WidgetsBinding.instance?.addObserver(this); 94 | } 95 | 96 | Future play(String filename, {double volume = 1}) async { 97 | final currentPlayer = audioPlayer; 98 | if (currentPlayer != null && currentPlayer.state != PlayerState.STOPPED) { 99 | currentPlayer.stop(); 100 | } 101 | 102 | isPlaying = true; 103 | audioPlayer = await audioCache.loop(filename, volume: volume); 104 | } 105 | 106 | Future load(String file) => audioCache.load(file); 107 | 108 | Future loadAsFile(String file) => audioCache.loadAsFile(file); 109 | 110 | Future> loadAll(List files) => audioCache.loadAll(files); 111 | 112 | void clear(Uri file) => audioCache.clear(file); 113 | 114 | void clearAll() => audioCache.clearAll(); 115 | 116 | Future stop() async { 117 | isPlaying = false; 118 | if (audioPlayer != null) { 119 | await audioPlayer!.stop(); 120 | } 121 | } 122 | 123 | Future resume() async { 124 | if (audioPlayer != null) { 125 | isPlaying = true; 126 | await audioPlayer!.resume(); 127 | } 128 | } 129 | 130 | Future pause() async { 131 | if (audioPlayer != null) { 132 | isPlaying = false; 133 | await audioPlayer!.pause(); 134 | } 135 | } 136 | 137 | void dispose() { 138 | if (!_isRegistered) { 139 | return; 140 | } 141 | WidgetsBinding.instance?.removeObserver(this); 142 | _isRegistered = false; 143 | } 144 | 145 | @override 146 | void didChangeAppLifecycleState(AppLifecycleState state) { 147 | if (state == AppLifecycleState.resumed) { 148 | if (isPlaying && audioPlayer?.state == PlayerState.PAUSED) { 149 | audioPlayer?.resume(); 150 | } 151 | } else { 152 | audioPlayer?.pause(); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /example/lib/scene/game_scene.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:devilf_engine/core/df_position.dart'; 3 | import 'package:devilf_engine/core/df_rect.dart'; 4 | import 'package:devilf_engine/core/df_size.dart'; 5 | import 'package:devilf_engine/game/df_camera.dart'; 6 | import 'package:devilf_engine/game/df_game_widget.dart'; 7 | import 'package:devilf_engine/sprite/df_image_sprite.dart'; 8 | import 'package:devilf_engine/sprite/df_text_sprite.dart'; 9 | import 'package:example/building/stone_sprite.dart'; 10 | import 'package:flutter/cupertino.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import '../game_manager.dart'; 14 | 15 | class GameScene extends StatefulWidget { 16 | final int map; 17 | 18 | GameScene({this.map = 1}); 19 | 20 | @override 21 | _GameSceneState createState() => _GameSceneState(); 22 | } 23 | 24 | class _GameSceneState extends State with TickerProviderStateMixin { 25 | /// 主界面 26 | DFGameWidget? _gameWidget; 27 | 28 | /// 加载状态 29 | bool _loading = true; 30 | 31 | /// 创建主场景 32 | _GameSceneState(); 33 | 34 | /// 初始化状态 35 | @override 36 | void initState() { 37 | super.initState(); 38 | 39 | /// 强制横屏 40 | SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]); 41 | 42 | /// 加载游戏 43 | _loadGame(); 44 | } 45 | 46 | /// 开始进入游戏 47 | void _loadGame() async { 48 | try { 49 | await Future.delayed(Duration(seconds: 1), () async { 50 | /// 摄像机 51 | DFCamera camera = DFCamera(rect: DFRect(0, 0, GameManager.visibleWidth, GameManager.visibleHeight)); 52 | 53 | /// 定义主界面 54 | this._gameWidget = 55 | DFGameWidget(size: Size(GameManager.visibleWidth, GameManager.visibleHeight), camera: camera); 56 | 57 | /// Logo精灵 58 | DFImageSprite logoSprite = await DFImageSprite.load("assets/images/sprite.png"); 59 | logoSprite.scale = 0.4; 60 | logoSprite.size = DFSize(40, 40); 61 | logoSprite.position = 62 | DFPosition(MediaQuery.of(context).size.width - 110, MediaQuery.of(context).padding.top + 30); 63 | logoSprite.fixed = true; 64 | 65 | /// 帧数精灵 66 | DFTextSprite fpsSprite = DFTextSprite("60 fps"); 67 | fpsSprite.position = 68 | DFPosition(MediaQuery.of(context).size.width - 70, MediaQuery.of(context).padding.top + 30); 69 | fpsSprite.fixed = true; 70 | fpsSprite.setOnUpdate((dt) { 71 | fpsSprite.text = this._gameWidget!.fps.toStringAsFixed(0) + " fps"; 72 | }); 73 | 74 | /// 演示精灵 75 | StoneSprite stoneSprite = StoneSprite(); 76 | stoneSprite.scale = 4; 77 | stoneSprite.position = 78 | DFPosition(MediaQuery.of(context).size.width / 2, MediaQuery.of(context).size.height / 2); 79 | 80 | /// 将Logo精灵添加到主界面 81 | this._gameWidget!.addChild(logoSprite); 82 | 83 | /// 将帧数精灵添加到主界面 84 | this._gameWidget!.addChild(fpsSprite); 85 | 86 | /// 将演示精灵精灵添加到主界面 87 | this._gameWidget!.addChild(stoneSprite); 88 | 89 | /// 设置摄像机跟随 90 | camera.lookAt(stoneSprite); 91 | 92 | /// 保存到管理器里 93 | GameManager.gameWidget = this._gameWidget!; 94 | 95 | /// Loading完成 96 | setState(() { 97 | _loading = false; 98 | }); 99 | 100 | print("游戏加载完成..."); 101 | }); 102 | } catch (e) { 103 | print('(GameScene _loadGame) Error: $e'); 104 | } 105 | } 106 | 107 | /// Loading显示 108 | Widget _loadingWidget() { 109 | return Center( 110 | child: 111 | Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ 112 | CircularProgressIndicator(), 113 | Padding( 114 | padding: EdgeInsets.only(top: 16), 115 | child: Text( 116 | "Loading...", 117 | style: TextStyle( 118 | color: Colors.white, 119 | fontSize: 14, 120 | ), 121 | ), 122 | ), 123 | ]), 124 | ); 125 | } 126 | 127 | @override 128 | Widget build(BuildContext context) { 129 | /// 获取屏幕尺寸 130 | GameManager.visibleWidth = MediaQuery.of(context).size.width; 131 | GameManager.visibleHeight = MediaQuery.of(context).size.height; 132 | print("获取屏幕尺寸:" + GameManager.visibleWidth.toString() + "," + GameManager.visibleHeight.toString()); 133 | 134 | return Container( 135 | color: Colors.black87, 136 | child: _loading 137 | ? _loadingWidget() 138 | : Stack(fit: StackFit.expand, children: [ 139 | /// 游戏控件 140 | Positioned( 141 | top: 0, 142 | left: 0, 143 | child: Container( 144 | width: MediaQuery.of(context).size.width, 145 | height: MediaQuery.of(context).size.height, 146 | color: Colors.black87, 147 | child: _gameWidget, 148 | ), 149 | ), 150 | 151 | /// Logo 152 | Positioned( 153 | left: 20, 154 | top: MediaQuery.of(context).padding.top + 20, 155 | child: Text( 156 | "DevilF", 157 | style: TextStyle( 158 | color: Colors.white, 159 | fontSize: 14, 160 | ), 161 | ), 162 | ), 163 | ]), 164 | ); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /lib/widget/df_joystick.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:devilf_engine/game/df_animation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | /// 摇杆控制 7 | // ignore: must_be_immutable 8 | class DFJoyStick extends StatefulWidget { 9 | /// 背景图 10 | final String? backgroundImage; 11 | 12 | /// 手柄图 13 | final String? handleImage; 14 | 15 | /// 背景颜色 16 | final Color backgroundColor; 17 | 18 | /// 手柄颜色 19 | final Color handleColor; 20 | 21 | /// 遥杆尺寸 22 | final Size size; 23 | 24 | /// 监听区域 25 | final Size areaSize; 26 | 27 | /// 当前方向 28 | String direction; 29 | 30 | /// 方向更新 31 | final void Function(String, double) onChange; 32 | 33 | /// 操作取消 34 | final void Function(String) onCancel; 35 | 36 | /// 创建遥杆 37 | DFJoyStick({ 38 | this.handleImage, 39 | this.backgroundImage, 40 | this.handleColor = const Color(0x60FFFFFF), 41 | this.backgroundColor = const Color(0x40FFFFFF), 42 | this.direction = DFDirection.NONE, 43 | required this.onChange, 44 | required this.onCancel, 45 | this.size = const Size(100, 100), 46 | this.areaSize = const Size(240, 240), 47 | }); 48 | 49 | @override 50 | State createState() => DFJoyStickState(); 51 | } 52 | 53 | class DFJoyStickState extends State { 54 | /// 偏移量 55 | Offset backgroundOffset = Offset.zero; 56 | Offset handleOffset = Offset.zero; 57 | 58 | /// 更新位置 59 | void updateDirection(double radians) { 60 | /// 判断8方向 61 | String direction = DFDirection.NONE; 62 | 63 | /// 换算角度 64 | double angle = 180 / pi * radians; 65 | if (angle < 0) { 66 | angle = angle + 360; 67 | } 68 | 69 | /// 顺时针 0~360 70 | /// print("angle:" + angle.toString()); 71 | 72 | /// 右边 73 | if ((angle >= 0 && angle < 22.5) || (angle >= 337.5 && angle <= 360)) { 74 | direction = DFDirection.RIGHT; 75 | } 76 | 77 | /// 右下 78 | if (angle >= 22.5 && angle < 67.5) { 79 | direction = DFDirection.DOWN_RIGHT; 80 | } 81 | 82 | /// 下 83 | if (angle >= 67.5 && angle < 112.5) { 84 | direction = DFDirection.DOWN; 85 | } 86 | 87 | /// 左下 88 | if (angle >= 112.5 && angle < 157.5) { 89 | direction = DFDirection.DOWN_LEFT; 90 | } 91 | 92 | /// 左 93 | if (angle >= 157.5 && angle < 202.5) { 94 | direction = DFDirection.LEFT; 95 | } 96 | 97 | /// 左上 98 | if (angle >= 202.5 && angle < 247.5) { 99 | direction = DFDirection.UP_LEFT; 100 | } 101 | 102 | /// 上 103 | if (angle >= 247.5 && angle < 292.5) { 104 | direction = DFDirection.UP; 105 | } 106 | 107 | /// 右上 108 | if (angle >= 292.5 && angle < 337.5) { 109 | direction = DFDirection.UP_RIGHT; 110 | } 111 | 112 | /// 返回结果 113 | if (direction != widget.direction) { 114 | widget.direction = direction; 115 | widget.onChange(widget.direction, radians); 116 | } 117 | } 118 | 119 | /// 计算移动距离 120 | void calculateMove(Offset localPosition) { 121 | /// 相对位置 122 | Offset offset = localPosition - Offset(widget.areaSize.width / 2, widget.areaSize.height / 2) - backgroundOffset; 123 | 124 | /// 更新摇杆位置 活动范围控制在Size之内 125 | Offset handle = Offset.fromDirection(offset.direction, min(widget.size.width / 2, offset.distance)); 126 | setState(() { 127 | handleOffset = handle; 128 | }); 129 | 130 | /// [-pi,pi] 131 | updateDirection(offset.direction); 132 | } 133 | 134 | @override 135 | Widget build(BuildContext context) { 136 | return Container( 137 | child: GestureDetector( 138 | child: Container( 139 | width: widget.areaSize.width, 140 | height: widget.areaSize.height, 141 | color: Color(0x00000000), 142 | child: Center( 143 | child: Transform.translate( 144 | offset: backgroundOffset, 145 | child: Container( 146 | width: widget.size.width, 147 | height: widget.size.height, 148 | decoration: BoxDecoration( 149 | color: widget.backgroundColor, 150 | image: widget.backgroundImage != null 151 | ? DecorationImage(image: AssetImage(widget.backgroundImage!)) 152 | : null, 153 | borderRadius: BorderRadius.circular(widget.size.width / 2), 154 | ), 155 | child: Center( 156 | child: Transform.translate( 157 | offset: handleOffset, 158 | child: Container( 159 | width: widget.size.width / 2, 160 | height: widget.size.height / 2, 161 | decoration: BoxDecoration( 162 | color: widget.handleColor, 163 | image: 164 | widget.handleImage != null ? DecorationImage(image: AssetImage(widget.handleImage!)) : null, 165 | borderRadius: BorderRadius.circular(widget.size.width / 4), 166 | ), 167 | ), 168 | ), 169 | ), 170 | ), 171 | ), 172 | ), 173 | ), 174 | onPanDown: onDragDown, 175 | onPanUpdate: onDragUpdate, 176 | onPanEnd: onDragEnd, 177 | ), 178 | ); 179 | } 180 | 181 | void onDragDown(DragDownDetails details) { 182 | /// 按下 183 | setState(() { 184 | backgroundOffset = details.localPosition - Offset(widget.areaSize.width / 2, widget.areaSize.height / 2); 185 | handleOffset = Offset.zero; 186 | }); 187 | } 188 | 189 | void onDragUpdate(DragUpdateDetails details) { 190 | /// 移动 191 | calculateMove(details.localPosition); 192 | } 193 | 194 | void onDragEnd(DragEndDetails details) { 195 | /// 抬起 196 | setState(() { 197 | backgroundOffset = Offset.zero; 198 | handleOffset = Offset.zero; 199 | }); 200 | 201 | /// 返回结果 202 | widget.onCancel(widget.direction); 203 | 204 | /// 归位 205 | widget.direction = DFDirection.NONE; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /lib/util/df_astar.dart: -------------------------------------------------------------------------------- 1 | import 'dart:core'; 2 | 3 | import 'package:devilf_engine/core/df_position.dart'; 4 | 5 | /// A*算法类 6 | class DFAStar { 7 | static const int BAR = 1; // 障碍 8 | static const int PATH = 2; // 路径 9 | static const int DIRECT_VALUE = 10; // 横竖移动代价 10 | static const int OBLIQUE_VALUE = 14; // 斜移动代价 11 | 12 | List _openList = []; 13 | List _closeList = []; 14 | List _pathList = []; 15 | 16 | /// 开始算法 17 | Future> start(List> blockMap, DFAStarNode startNode, DFAStarNode endNode) async { 18 | /// Map数据 19 | DFAStarMap map = DFAStarMap(blockMap, blockMap[0].length, blockMap.length, startNode, endNode); 20 | 21 | /// clean 22 | _openList.clear(); 23 | _closeList.clear(); 24 | _pathList.clear(); 25 | 26 | /// 开始搜索 27 | _openList.add(map.start); 28 | 29 | /// 优先队列(升序) 30 | _openList.sort((a, b) => a.compareTo(b)); 31 | moveNodes(map); 32 | 33 | /// 删掉起点 34 | if (this._pathList.length > 0) { 35 | this._pathList.removeLast(); 36 | } 37 | 38 | /// 返回路径 39 | return this._pathList; 40 | } 41 | 42 | /// 移动当前结点 43 | void moveNodes(DFAStarMap map) { 44 | while (_openList.length > 0) { 45 | /// 第一个元素 46 | DFAStarNode current = _openList.removeAt(0); 47 | _closeList.add(current); 48 | addNeighborNodeInOpen(map, current); 49 | if (isPositionInClose(map.end.position)) { 50 | drawPath(map.maps, map.end); 51 | break; 52 | } 53 | } 54 | } 55 | 56 | /// 在二维数组中绘制路径 57 | void drawPath(List>? maps, DFAStarNode? end) { 58 | if (end == null || maps == null) return; 59 | print("总代价:" + end.G.toString()); 60 | while (end != null) { 61 | DFTilePosition c = end.position!; 62 | maps[c.y][c.x] = PATH; 63 | end = end.parent; 64 | _pathList.add(DFTilePosition(c.x, c.y)); 65 | } 66 | } 67 | 68 | /// 添加所有邻结点到open表 69 | void addNeighborNodeInOpen(DFAStarMap mapInfo, DFAStarNode current) { 70 | int x = current.position!.x; 71 | int y = current.position!.y; 72 | // 左 73 | addNeighborNodeInOpenXy(mapInfo, current, x - 1, y, DIRECT_VALUE); 74 | // 上 75 | addNeighborNodeInOpenXy(mapInfo, current, x, y - 1, DIRECT_VALUE); 76 | // 右 77 | addNeighborNodeInOpenXy(mapInfo, current, x + 1, y, DIRECT_VALUE); 78 | // 下 79 | addNeighborNodeInOpenXy(mapInfo, current, x, y + 1, DIRECT_VALUE); 80 | // 左上 81 | addNeighborNodeInOpenXy(mapInfo, current, x - 1, y - 1, OBLIQUE_VALUE); 82 | // 右上 83 | addNeighborNodeInOpenXy(mapInfo, current, x + 1, y - 1, OBLIQUE_VALUE); 84 | // 右下 85 | addNeighborNodeInOpenXy(mapInfo, current, x + 1, y + 1, OBLIQUE_VALUE); 86 | // 左下 87 | addNeighborNodeInOpenXy(mapInfo, current, x - 1, y + 1, OBLIQUE_VALUE); 88 | } 89 | 90 | /// 添加一个邻结点到open表 91 | void addNeighborNodeInOpenXy(DFAStarMap mapInfo, DFAStarNode current, int x, int y, int value) { 92 | if (canAddNodeToOpen(mapInfo, x, y)) { 93 | DFAStarNode end = mapInfo.end; 94 | DFTilePosition position = DFTilePosition(x, y); 95 | int G = current.G + value; // 计算邻结点的G值 96 | DFAStarNode? child = findNodeInOpen(position); 97 | if (child == null) { 98 | int H = calcH(end.position!, position); // 计算H值 99 | if (isEndNode(end.position!, position)) { 100 | child = end; 101 | child.parent = current; 102 | child.G = G; 103 | child.H = H; 104 | } else { 105 | child = DFAStarNode.newNode(position, current, G, H); 106 | } 107 | _openList.add(child); 108 | _openList.sort((a, b) => a.compareTo(b)); 109 | } else if (child.G > G) { 110 | child.G = G; 111 | child.parent = current; 112 | _openList.add(child); 113 | _openList.sort((a, b) => a.compareTo(b)); 114 | } 115 | } 116 | } 117 | 118 | /// 从Open列表中查找结点 119 | DFAStarNode? findNodeInOpen(DFTilePosition? position) { 120 | if (position == null || _openList.length == 0) return null; 121 | for (DFAStarNode node in _openList) { 122 | if (node.position! == position) { 123 | return node; 124 | } 125 | } 126 | return null; 127 | } 128 | 129 | /// 计算H的估值:“曼哈顿”法,坐标分别取差值相加 130 | int calcH(DFTilePosition end, DFTilePosition coord) { 131 | return ((end.x - coord.x).abs() + (end.y - coord.y).abs()) * DIRECT_VALUE; 132 | } 133 | 134 | /// 判断结点是否是最终结点 135 | bool isEndNode(DFTilePosition end, DFTilePosition? position) { 136 | return position != null && end == position; 137 | } 138 | 139 | /// 判断结点能否放入Open列表 140 | bool canAddNodeToOpen(DFAStarMap mapInfo, int x, int y) { 141 | /// 是否在地图中 142 | if (x < 0 || x >= mapInfo.width || y < 0 || y >= mapInfo.height) return false; 143 | 144 | /// 判断是否是不可通过的结点 145 | if (mapInfo.maps![y][x] == BAR) return false; 146 | 147 | /// 判断结点是否存在close表 148 | if (isCoordInCloseXy(x, y)) return false; 149 | 150 | return true; 151 | } 152 | 153 | /// 判断坐标是否在close表中 154 | bool isPositionInClose(DFTilePosition? position) { 155 | if (position == null) { 156 | return false; 157 | } 158 | return isCoordInCloseXy(position.x, position.y); 159 | } 160 | 161 | /// 判断坐标是否在close表中 162 | bool isCoordInCloseXy(int x, int y) { 163 | if (_closeList.length == 0) { 164 | return false; 165 | } 166 | for (DFAStarNode node in _closeList) { 167 | if (node.position!.x == x && node.position!.y == y) { 168 | return true; 169 | } 170 | } 171 | return false; 172 | } 173 | } 174 | 175 | /// 包含地图所需的所有输入数据 176 | class DFAStarMap { 177 | /// 二维数组的地图 178 | List>? maps; 179 | 180 | /// 地图的宽 181 | int width; 182 | 183 | /// 地图的高 184 | int height; 185 | 186 | /// 起始结点 187 | DFAStarNode start; 188 | 189 | /// 最终结点 190 | DFAStarNode end; 191 | 192 | DFAStarMap(this.maps, this.width, this.height, this.start, this.end); 193 | } 194 | 195 | /// 路径节点 196 | class DFAStarNode { 197 | DFTilePosition? position; // 坐标 198 | DFAStarNode? parent; // 父结点 199 | int G = 0; // G:是个准确的值,是起点到当前结点的代价 200 | int H = 0; // H:是个估值,当前结点到目的结点的估计代价 201 | 202 | DFAStarNode(int x, int y) { 203 | this.position = new DFTilePosition(x, y); 204 | } 205 | 206 | DFAStarNode.newNode(DFTilePosition position, DFAStarNode parent, int g, int h) { 207 | this.position = position; 208 | this.parent = parent; 209 | this.G = g; 210 | this.H = h; 211 | } 212 | 213 | @override 214 | String toString() { 215 | return position.toString(); 216 | } 217 | 218 | /// 排序比较 219 | int compareTo(DFAStarNode other) { 220 | /// 大于 221 | if (G + H > other.G + other.H) { 222 | return 1; 223 | } 224 | 225 | /// 小于 226 | else if (G + H < other.G + other.H) { 227 | return -1; 228 | } 229 | 230 | /// 等于 231 | return 0; 232 | } 233 | } 234 | 235 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | audioplayers: 12 | dependency: "direct main" 13 | description: 14 | name: audioplayers 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.20.1" 18 | boolean_selector: 19 | dependency: transitive 20 | description: 21 | name: boolean_selector 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.1.0" 25 | characters: 26 | dependency: transitive 27 | description: 28 | name: characters 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.1.0" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.3.1" 39 | clock: 40 | dependency: transitive 41 | description: 42 | name: clock 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.0" 46 | collection: 47 | dependency: transitive 48 | description: 49 | name: collection 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.15.0" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "3.0.1" 60 | fake_async: 61 | dependency: transitive 62 | description: 63 | name: fake_async 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.2.0" 67 | ffi: 68 | dependency: transitive 69 | description: 70 | name: ffi 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.1.2" 74 | file: 75 | dependency: transitive 76 | description: 77 | name: file 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "6.1.2" 81 | flutter: 82 | dependency: "direct main" 83 | description: flutter 84 | source: sdk 85 | version: "0.0.0" 86 | flutter_test: 87 | dependency: "direct dev" 88 | description: flutter 89 | source: sdk 90 | version: "0.0.0" 91 | flutter_web_plugins: 92 | dependency: transitive 93 | description: flutter 94 | source: sdk 95 | version: "0.0.0" 96 | http: 97 | dependency: transitive 98 | description: 99 | name: http 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "0.13.3" 103 | http_parser: 104 | dependency: transitive 105 | description: 106 | name: http_parser 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "4.0.0" 110 | js: 111 | dependency: transitive 112 | description: 113 | name: js 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "0.6.3" 117 | matcher: 118 | dependency: transitive 119 | description: 120 | name: matcher 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "0.12.10" 124 | meta: 125 | dependency: transitive 126 | description: 127 | name: meta 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.7.0" 131 | path: 132 | dependency: transitive 133 | description: 134 | name: path 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.8.0" 138 | path_provider: 139 | dependency: transitive 140 | description: 141 | name: path_provider 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "2.0.2" 145 | path_provider_linux: 146 | dependency: transitive 147 | description: 148 | name: path_provider_linux 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "2.0.2" 152 | path_provider_macos: 153 | dependency: transitive 154 | description: 155 | name: path_provider_macos 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "2.0.2" 159 | path_provider_platform_interface: 160 | dependency: transitive 161 | description: 162 | name: path_provider_platform_interface 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "2.0.1" 166 | path_provider_windows: 167 | dependency: transitive 168 | description: 169 | name: path_provider_windows 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "2.0.3" 173 | pedantic: 174 | dependency: transitive 175 | description: 176 | name: pedantic 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "1.11.1" 180 | platform: 181 | dependency: transitive 182 | description: 183 | name: platform 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "3.0.0" 187 | plugin_platform_interface: 188 | dependency: transitive 189 | description: 190 | name: plugin_platform_interface 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "2.0.1" 194 | process: 195 | dependency: transitive 196 | description: 197 | name: process 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "4.2.3" 201 | sky_engine: 202 | dependency: transitive 203 | description: flutter 204 | source: sdk 205 | version: "0.0.99" 206 | source_span: 207 | dependency: transitive 208 | description: 209 | name: source_span 210 | url: "https://pub.dartlang.org" 211 | source: hosted 212 | version: "1.8.1" 213 | stack_trace: 214 | dependency: transitive 215 | description: 216 | name: stack_trace 217 | url: "https://pub.dartlang.org" 218 | source: hosted 219 | version: "1.10.0" 220 | stream_channel: 221 | dependency: transitive 222 | description: 223 | name: stream_channel 224 | url: "https://pub.dartlang.org" 225 | source: hosted 226 | version: "2.1.0" 227 | string_scanner: 228 | dependency: transitive 229 | description: 230 | name: string_scanner 231 | url: "https://pub.dartlang.org" 232 | source: hosted 233 | version: "1.1.0" 234 | term_glyph: 235 | dependency: transitive 236 | description: 237 | name: term_glyph 238 | url: "https://pub.dartlang.org" 239 | source: hosted 240 | version: "1.2.0" 241 | test_api: 242 | dependency: transitive 243 | description: 244 | name: test_api 245 | url: "https://pub.dartlang.org" 246 | source: hosted 247 | version: "0.4.2" 248 | typed_data: 249 | dependency: transitive 250 | description: 251 | name: typed_data 252 | url: "https://pub.dartlang.org" 253 | source: hosted 254 | version: "1.3.0" 255 | uuid: 256 | dependency: transitive 257 | description: 258 | name: uuid 259 | url: "https://pub.dartlang.org" 260 | source: hosted 261 | version: "3.0.4" 262 | vector_math: 263 | dependency: transitive 264 | description: 265 | name: vector_math 266 | url: "https://pub.dartlang.org" 267 | source: hosted 268 | version: "2.1.0" 269 | win32: 270 | dependency: transitive 271 | description: 272 | name: win32 273 | url: "https://pub.dartlang.org" 274 | source: hosted 275 | version: "2.2.5" 276 | xdg_directories: 277 | dependency: transitive 278 | description: 279 | name: xdg_directories 280 | url: "https://pub.dartlang.org" 281 | source: hosted 282 | version: "0.2.0" 283 | sdks: 284 | dart: ">=2.13.0 <3.0.0" 285 | flutter: ">=2.0.0" 286 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | audioplayers: 12 | dependency: transitive 13 | description: 14 | name: audioplayers 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.20.1" 18 | boolean_selector: 19 | dependency: transitive 20 | description: 21 | name: boolean_selector 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.1.0" 25 | characters: 26 | dependency: transitive 27 | description: 28 | name: characters 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.1.0" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.3.1" 39 | clock: 40 | dependency: transitive 41 | description: 42 | name: clock 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.0" 46 | collection: 47 | dependency: transitive 48 | description: 49 | name: collection 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.15.0" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "3.0.1" 60 | cupertino_icons: 61 | dependency: "direct main" 62 | description: 63 | name: cupertino_icons 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.0.3" 67 | devilf_engine: 68 | dependency: "direct main" 69 | description: 70 | path: ".." 71 | relative: true 72 | source: path 73 | version: "0.1.0" 74 | fake_async: 75 | dependency: transitive 76 | description: 77 | name: fake_async 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.2.0" 81 | ffi: 82 | dependency: transitive 83 | description: 84 | name: ffi 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.1.2" 88 | file: 89 | dependency: transitive 90 | description: 91 | name: file 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "6.1.2" 95 | flutter: 96 | dependency: "direct main" 97 | description: flutter 98 | source: sdk 99 | version: "0.0.0" 100 | flutter_test: 101 | dependency: "direct dev" 102 | description: flutter 103 | source: sdk 104 | version: "0.0.0" 105 | flutter_web_plugins: 106 | dependency: transitive 107 | description: flutter 108 | source: sdk 109 | version: "0.0.0" 110 | http: 111 | dependency: transitive 112 | description: 113 | name: http 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "0.13.3" 117 | http_parser: 118 | dependency: transitive 119 | description: 120 | name: http_parser 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "4.0.0" 124 | js: 125 | dependency: transitive 126 | description: 127 | name: js 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "0.6.3" 131 | matcher: 132 | dependency: transitive 133 | description: 134 | name: matcher 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "0.12.10" 138 | meta: 139 | dependency: transitive 140 | description: 141 | name: meta 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.7.0" 145 | path: 146 | dependency: transitive 147 | description: 148 | name: path 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.8.0" 152 | path_provider: 153 | dependency: transitive 154 | description: 155 | name: path_provider 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "2.0.2" 159 | path_provider_linux: 160 | dependency: transitive 161 | description: 162 | name: path_provider_linux 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "2.0.2" 166 | path_provider_macos: 167 | dependency: transitive 168 | description: 169 | name: path_provider_macos 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "2.0.2" 173 | path_provider_platform_interface: 174 | dependency: transitive 175 | description: 176 | name: path_provider_platform_interface 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "2.0.1" 180 | path_provider_windows: 181 | dependency: transitive 182 | description: 183 | name: path_provider_windows 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "2.0.3" 187 | pedantic: 188 | dependency: transitive 189 | description: 190 | name: pedantic 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "1.11.1" 194 | platform: 195 | dependency: transitive 196 | description: 197 | name: platform 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "3.0.0" 201 | plugin_platform_interface: 202 | dependency: transitive 203 | description: 204 | name: plugin_platform_interface 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "2.0.1" 208 | process: 209 | dependency: transitive 210 | description: 211 | name: process 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "4.2.3" 215 | sky_engine: 216 | dependency: transitive 217 | description: flutter 218 | source: sdk 219 | version: "0.0.99" 220 | source_span: 221 | dependency: transitive 222 | description: 223 | name: source_span 224 | url: "https://pub.dartlang.org" 225 | source: hosted 226 | version: "1.8.1" 227 | stack_trace: 228 | dependency: transitive 229 | description: 230 | name: stack_trace 231 | url: "https://pub.dartlang.org" 232 | source: hosted 233 | version: "1.10.0" 234 | stream_channel: 235 | dependency: transitive 236 | description: 237 | name: stream_channel 238 | url: "https://pub.dartlang.org" 239 | source: hosted 240 | version: "2.1.0" 241 | string_scanner: 242 | dependency: transitive 243 | description: 244 | name: string_scanner 245 | url: "https://pub.dartlang.org" 246 | source: hosted 247 | version: "1.1.0" 248 | term_glyph: 249 | dependency: transitive 250 | description: 251 | name: term_glyph 252 | url: "https://pub.dartlang.org" 253 | source: hosted 254 | version: "1.2.0" 255 | test_api: 256 | dependency: transitive 257 | description: 258 | name: test_api 259 | url: "https://pub.dartlang.org" 260 | source: hosted 261 | version: "0.4.2" 262 | typed_data: 263 | dependency: transitive 264 | description: 265 | name: typed_data 266 | url: "https://pub.dartlang.org" 267 | source: hosted 268 | version: "1.3.0" 269 | uuid: 270 | dependency: transitive 271 | description: 272 | name: uuid 273 | url: "https://pub.dartlang.org" 274 | source: hosted 275 | version: "3.0.4" 276 | vector_math: 277 | dependency: transitive 278 | description: 279 | name: vector_math 280 | url: "https://pub.dartlang.org" 281 | source: hosted 282 | version: "2.1.0" 283 | win32: 284 | dependency: transitive 285 | description: 286 | name: win32 287 | url: "https://pub.dartlang.org" 288 | source: hosted 289 | version: "2.2.5" 290 | xdg_directories: 291 | dependency: transitive 292 | description: 293 | name: xdg_directories 294 | url: "https://pub.dartlang.org" 295 | source: hosted 296 | version: "0.2.0" 297 | sdks: 298 | dart: ">=2.13.0 <3.0.0" 299 | flutter: ">=2.0.0" 300 | -------------------------------------------------------------------------------- /lib/sprite/df_animation_sprite.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | import 'dart:ui'; 3 | import 'package:devilf_engine/game/df_animation.dart'; 4 | import 'package:devilf_engine/game/df_assets_loader.dart'; 5 | import 'package:devilf_engine/core/df_offset.dart'; 6 | import 'package:devilf_engine/core/df_rect.dart'; 7 | import 'package:devilf_engine/sprite/df_sprite.dart'; 8 | import 'package:devilf_engine/core/df_position.dart'; 9 | import 'package:devilf_engine/core/df_size.dart'; 10 | import 'dart:ui' as ui; 11 | import 'df_image_sprite.dart'; 12 | 13 | /// 动画精灵 14 | class DFAnimationSprite extends DFSprite { 15 | /// 这个动画的全部帧 16 | Map> frames = HashMap>(); 17 | 18 | /// 绑定动画 19 | List bindSprites = []; 20 | 21 | /// 当前是的Index 22 | int currentIndex = 0; 23 | 24 | /// 是否循环动画 25 | bool loop = true; 26 | 27 | /// 帧速率 28 | int stepTime; 29 | 30 | /// 当前动画类型 31 | String currentAnimation = DFAction.NONE + DFDirection.NONE; 32 | 33 | /// x轴镜像 34 | bool currentAnimationFlippedX = false; 35 | 36 | /// 颜色 37 | Color color; 38 | 39 | /// 被绑定状态 40 | bool isBind = false; 41 | 42 | /// 帧绘制时钟 43 | int frameClock = 0; 44 | 45 | /// 完成事件 46 | Function(DFAnimationSprite)? onComplete; 47 | 48 | /// 创建动画精灵 49 | DFAnimationSprite({ 50 | this.stepTime = 200, 51 | this.loop = true, 52 | this.color = const Color(0xFFFFFFFF), 53 | DFSize size = const DFSize(128, 128), 54 | }) : super(position: DFPosition(0, 0), size: size); 55 | 56 | /// 将plist转换Json后读取精灵 57 | /// { 58 | /// "frames": { 59 | /// "idle_00000.png": 60 | /// { 61 | /// "frame": "{{929,291},{76,110}}", 62 | /// "offset": "{-19,24}", 63 | /// "rotated": true 64 | /// } 65 | /// } 66 | /// } 67 | static Future load(String json, {scale = 0.5, blendMode = BlendMode.srcOver}) async { 68 | print("DFAnimationSprite Load:" + json); 69 | DFAnimationSprite animationSprite = DFAnimationSprite(stepTime: 100, loop: true); 70 | DFAnimation.keys.forEach((element) { 71 | animationSprite.frames[element] = []; 72 | }); 73 | Map jsonMap = await DFAssetsLoader.loadJson(json); 74 | ui.Image image = await DFAssetsLoader.loadImage(json.replaceAll(".json", ".png")); 75 | final jsonFrames = jsonMap['frames'] as Map; 76 | 77 | /// final jsonMetadata = jsonMap['metadata'] as Map; 78 | jsonFrames.forEach((key, value) { 79 | final map = value as Map; 80 | 81 | bool rotated = false; 82 | if (map['rotated'] != null) { 83 | rotated = map['rotated'] as bool; 84 | } 85 | 86 | final frame = map['frame'] as String; 87 | final offset = map['offset'] as String; 88 | List frameText = frame.replaceAll("{{", "").replaceAll("},{", ",").replaceAll("}}", "").split(","); 89 | List offsetText = offset.replaceAll("{", "").replaceAll("}", "").split(","); 90 | 91 | DFRect frameRect = DFRect(double.parse(frameText[0]), double.parse(frameText[1]), double.parse(frameText[2]), 92 | double.parse(frameText[3])); 93 | DFOffset frameOffset = DFOffset(double.parse(offsetText[0]), double.parse(offsetText[1])); 94 | 95 | /// 如果是旋转的参数也修改一下 96 | if (rotated) { 97 | frameRect = DFRect(double.parse(frameText[0]), double.parse(frameText[1]), double.parse(frameText[3]), 98 | double.parse(frameText[2])); 99 | frameOffset = DFOffset(double.parse(offsetText[1]), double.parse(offsetText[0])); 100 | } 101 | 102 | /// print("frameSize:" + frameRect.toString()); 103 | /// print("frameOffset:" + frameOffset.toString()); 104 | 105 | DFImageSprite sprite = DFImageSprite( 106 | image, 107 | offset: frameOffset, 108 | rect: frameRect, 109 | rotated: rotated, 110 | blendMode: blendMode, 111 | ); 112 | sprite.scale = scale; 113 | 114 | /// idle_00000.png 115 | String actionText = "idle_"; 116 | String action = DFAction.IDLE; 117 | if (key.contains("idle_")) { 118 | actionText = "idle_"; 119 | action = DFAction.IDLE; 120 | } else if (key.contains("run_")) { 121 | actionText = "run_"; 122 | action = DFAction.RUN; 123 | } else if (key.contains("attack_")) { 124 | actionText = "attack_"; 125 | action = DFAction.ATTACK; 126 | } else if (key.contains("casting_")) { 127 | actionText = "casting_"; 128 | action = DFAction.CASTING; 129 | } else if (key.contains("collect_")) { 130 | actionText = "collect_"; 131 | action = DFAction.COLLECT; 132 | } else if (key.contains("death_")) { 133 | actionText = "death_"; 134 | action = DFAction.DEATH; 135 | } else if (key.contains("track_")) { 136 | actionText = "track_"; 137 | action = DFAction.TRACK; 138 | } else if (key.contains("explode_")) { 139 | actionText = "explode_"; 140 | action = DFAction.EXPLODE; 141 | } 142 | 143 | String keyNumber = key.replaceAll(actionText, "").replaceAll(".png", ""); 144 | if (keyNumber.startsWith("000")) { 145 | animationSprite.frames[action + DFDirection.UP]?.add(sprite); 146 | } 147 | if (keyNumber.startsWith("100")) { 148 | animationSprite.frames[action + DFDirection.UP_RIGHT]?.add(sprite); 149 | animationSprite.frames[action + DFDirection.UP_LEFT]?.add(sprite); 150 | } 151 | if (keyNumber.startsWith("200")) { 152 | animationSprite.frames[action + DFDirection.RIGHT]?.add(sprite); 153 | animationSprite.frames[action + DFDirection.LEFT]?.add(sprite); 154 | } 155 | if (keyNumber.startsWith("300")) { 156 | animationSprite.frames[action + DFDirection.DOWN_RIGHT]?.add(sprite); 157 | animationSprite.frames[action + DFDirection.DOWN_LEFT]?.add(sprite); 158 | } 159 | if (keyNumber.startsWith("400")) { 160 | animationSprite.frames[action + DFDirection.DOWN]?.add(sprite); 161 | } 162 | }); 163 | 164 | return animationSprite; 165 | } 166 | 167 | /// 绑定动画同步子精灵 168 | void bindChild(DFAnimationSprite sprite) { 169 | sprite.isBind = true; 170 | sprite.position = DFPosition(sprite.position.x - size.width / 2, sprite.position.y - size.height / 2); 171 | this.bindSprites.add(sprite); 172 | } 173 | 174 | /// 移除绑定精灵 175 | void removeBindChild(DFAnimationSprite sprite) { 176 | sprite.visible = false; 177 | sprite.recyclable = true; 178 | } 179 | 180 | /// 播放动画 181 | void play(String animation, {int stepTime = 200, bool loop = true, onComplete}) { 182 | if (this.currentAnimation != animation) { 183 | this.currentIndex = 0; 184 | this.currentAnimation = animation; 185 | this.stepTime = stepTime; 186 | this.loop = loop; 187 | this.onComplete = onComplete; 188 | /*print("Play:" + 189 | animation.toString() + 190 | ",frames:" + 191 | this.frames[this.currentAnimation]!.length.toString() + 192 | ",stepTime:" + 193 | stepTime.toString());*/ 194 | } 195 | } 196 | 197 | /// 精灵更新 198 | @override 199 | void update(double dt) { 200 | /// 控制动画帧切换 201 | if (this.frames[this.currentAnimation] != null && this.frames[this.currentAnimation]!.length > 0) { 202 | List sprites = this.frames[this.currentAnimation]!; 203 | 204 | /// 控制动画帧按照stepTime进行更新 205 | if (DateTime.now().millisecondsSinceEpoch - this.frameClock > this.stepTime) { 206 | this.frameClock = DateTime.now().millisecondsSinceEpoch; 207 | if (sprites.length > this.currentIndex + 1) { 208 | this.currentIndex = this.currentIndex + 1; 209 | } else { 210 | /// 如果循环就从0再次开始 211 | if (this.loop) { 212 | this.currentIndex = 0; 213 | } else { 214 | /// 动画播放到完成 215 | if (onComplete != null) { 216 | onComplete!(this); 217 | } 218 | } 219 | } 220 | } 221 | } 222 | super.update(dt); 223 | } 224 | 225 | /// 精灵渲染 226 | @override 227 | void render(Canvas canvas) { 228 | /// 画布暂存 229 | canvas.save(); 230 | 231 | /// 将子精灵转换为相对坐标 232 | if (parent == null) { 233 | canvas.translate(position.x, position.y); 234 | } else { 235 | canvas.translate(position.x - parent!.size.width / 2, position.y - parent!.size.height / 2); 236 | } 237 | 238 | /// 精灵矩形边界 239 | /// var paint = new Paint()..color = Color(0x6000FF00); 240 | /// canvas.drawRect(Rect.fromLTWH(- size.width/2,- size.height/2, size.width, size.height), paint); 241 | 242 | /// 渲染动画帧 243 | if (this.frames[this.currentAnimation] != null && this.frames[this.currentAnimation]!.length > 0) { 244 | List sprites = this.frames[this.currentAnimation]!; 245 | sprites[this.currentIndex].flippedX = currentAnimationFlippedX; 246 | sprites[this.currentIndex].color = color; 247 | sprites[this.currentIndex].render(canvas); 248 | } 249 | 250 | /// 渲染子精灵 251 | if (children.length > 0) { 252 | children.forEach((sprite) { 253 | sprite.render(canvas); 254 | }); 255 | } 256 | 257 | /// 渲染绑定精灵 258 | bindSprites.forEach((sprite) { 259 | if (sprite.visible && 260 | sprite.frames[this.currentAnimation] != null && 261 | this.frames[this.currentAnimation] != null) { 262 | /// 绑定精灵帧数不一定完全对应 所以这里计算一下接近值,保证速度和最后动画结束时间一致 263 | double offset = sprite.frames[this.currentAnimation]!.length / this.frames[this.currentAnimation]!.length; 264 | sprite.currentIndex = (this.currentIndex * offset).round(); 265 | sprite.currentAnimation = this.currentAnimation; 266 | sprite.currentAnimationFlippedX = currentAnimationFlippedX; 267 | sprite.render(canvas); 268 | } 269 | }); 270 | 271 | /// 画布恢复 272 | canvas.restore(); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /lib/sprite/df_tile_map_sprite.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:devilf_engine/core/df_circle.dart'; 3 | import 'package:devilf_engine/core/df_position.dart'; 4 | import 'package:devilf_engine/core/df_shape.dart'; 5 | import 'package:devilf_engine/core/df_size.dart'; 6 | import 'package:devilf_engine/game/df_assets_loader.dart'; 7 | import 'package:devilf_engine/game/df_camera.dart'; 8 | import 'package:devilf_engine/core/df_rect.dart'; 9 | import 'package:devilf_engine/sprite/df_sprite.dart'; 10 | import 'package:devilf_engine/tiled/df_tile.dart'; 11 | import 'package:devilf_engine/tiled/df_tile_layer.dart'; 12 | import 'package:devilf_engine/tiled/df_tile_set.dart'; 13 | import 'package:devilf_engine/tiled/df_tile_map.dart'; 14 | import 'dart:ui' as ui; 15 | import '../devilf_engine.dart'; 16 | import 'df_image_sprite.dart'; 17 | 18 | /// 瓦片地图精灵 19 | class DFTileMapSprite extends DFSprite { 20 | 21 | /// 瓦片地图 22 | DFTileMap? tileMap; 23 | 24 | /// 需要绘制的瓦片 25 | List sprites = []; 26 | 27 | /// 图片资源路径 28 | String imagePath = ""; 29 | 30 | /// 摄像机位置 31 | DFPosition? cameraPosition; 32 | 33 | /// 地图层 34 | DFTileLayer? mapLayer; 35 | 36 | /// 遮挡层 37 | DFTileLayer? alphaLayer; 38 | 39 | /// 碰撞层 40 | DFTileLayer? blockLayer; 41 | 42 | /// 创建地图精灵 43 | DFTileMapSprite({ 44 | DFSize size = const DFSize(100, 100), 45 | }) : super(position: DFPosition(0, 0), size: size); 46 | 47 | /// 读取tiled导出的json文件 48 | static Future load(String json, double scale) async { 49 | DFTileMapSprite tileMapSprite = DFTileMapSprite(); 50 | Map jsonMap = await DFAssetsLoader.loadJson(json); 51 | tileMapSprite.tileMap = DFTileMap.fromJson(jsonMap); 52 | tileMapSprite.imagePath = json.substring(0, json.lastIndexOf("/")); 53 | tileMapSprite.scale = scale; 54 | tileMapSprite.tileMap!.layers!.forEach((layer) { 55 | if (layer is DFTileLayer && layer.name == "map" && layer.visible == true) { 56 | tileMapSprite.mapLayer = layer; 57 | } else if (layer is DFTileLayer && layer.name == "block") { 58 | tileMapSprite.blockLayer = layer; 59 | } else if (layer is DFTileLayer && layer.name == "alpha") { 60 | tileMapSprite.alphaLayer = layer; 61 | } 62 | }); 63 | return tileMapSprite; 64 | } 65 | 66 | /// 更细地图瓦片 67 | Future updateLayer(DFCamera camera) async { 68 | if (this.tileMap == null) return; 69 | 70 | /// 上一下刷新时摄像机的位置 71 | if (this.cameraPosition != null) { 72 | if ((this.cameraPosition!.x - camera.sprite!.position.x).abs() < this.tileMap!.tileWidth! * 5 * scale && 73 | (this.cameraPosition!.y - camera.sprite!.position.y).abs() < this.tileMap!.tileHeight! * 5 * scale) { 74 | return; 75 | } 76 | } 77 | 78 | /// 保存上一下刷新时摄像机的位置 79 | this.cameraPosition = DFPosition(camera.sprite!.position.x, camera.sprite!.position.y); 80 | double drawX = camera.sprite!.position.x - camera.rect.width; 81 | double drawY = camera.sprite!.position.y - camera.rect.height; 82 | 83 | /// 可视区域 84 | DFRect visibleRect = DFRect(drawX, drawY, camera.rect.width * 2, 85 | camera.rect.height * 2); 86 | 87 | /// print("visibleRect:" + visibleRect.toString()); 88 | 89 | /// 将全部的瓦片设置为不可见 90 | sprites.forEach((element) { 91 | element.visible = false; 92 | }); 93 | 94 | /// 遍历图层瓦片 95 | if (mapLayer != null) { 96 | DFTileSet? tileSet; 97 | double tileWidth = 0; 98 | double tileHeight = 0; 99 | int columnCount = 0; 100 | await Future.forEach(mapLayer!.data ?? [], (tile) async { 101 | if (tile != 0) { 102 | if (tileSet == null) { 103 | tileSet = tileMap!.tileSets!.lastWhere((element) { 104 | return element.firsTgId != null && tile >= element.firsTgId!; 105 | }); 106 | columnCount = (tileMap!.width! * tileMap!.tileWidth!) ~/ tileSet!.tileWidth!.toDouble(); 107 | tileWidth = tileSet!.tileWidth!.toDouble() * this.scale; 108 | tileHeight = tileSet!.tileHeight!.toDouble() * this.scale; 109 | } 110 | if (tileSet != null) { 111 | int tileIndex = tile - tileSet!.firsTgId!; 112 | int row = _getY(tileIndex, columnCount).toInt(); 113 | int column = _getX(tileIndex, columnCount).toInt(); 114 | 115 | /// print("row:" + row.toString() + ",column:" + column.toString() + ",scale:" + this.scale.toString()); 116 | Rect tileRect = Rect.fromLTWH(column * tileWidth, row * tileHeight, tileWidth, tileHeight); 117 | 118 | /// print("tileRect:" + tileRect.toString()); 119 | /// 在可视区域的瓦片设置为显示 120 | if (visibleRect.toRect().overlaps(tileRect)) { 121 | DFImageSprite? imageSprite = existImageSprite(row, column); 122 | if (imageSprite == null) { 123 | imageSprite = await getTileImageSprite(tileSet!, tileIndex, row, column, this.scale); 124 | sprites.add(imageSprite); 125 | } else { 126 | imageSprite.visible = true; 127 | } 128 | } 129 | } 130 | } 131 | }); 132 | } 133 | 134 | /// 删除不可见的精灵 135 | sprites.removeWhere((element) => !element.visible); 136 | } 137 | 138 | /// 获取存在的精灵 139 | DFImageSprite? existImageSprite(int row, int column) { 140 | for (DFImageSprite sprite in sprites) { 141 | if (row.toString() + "," + column.toString() == sprite.key) { 142 | return sprite; 143 | } 144 | } 145 | return null; 146 | } 147 | 148 | /// 获取瓦片精灵的某个瓦片 149 | Future getTileImageSprite(DFTileSet tileSet, int tileIndex, int row, int column, double scale) async { 150 | ///print("index:" + index.toString()); 151 | 152 | List? tiles = tileSet.tiles; 153 | String imagePath = this.imagePath + "/"; 154 | double tileWidth = tileSet.tileWidth!.toDouble(); 155 | double tileHeight = tileSet.tileHeight!.toDouble(); 156 | double imageWidth = 0; 157 | double imageHeight = 0; 158 | 159 | if (tiles != null) { 160 | DFTile tile = tiles[tileIndex]; 161 | imageWidth = tile.imageWidth!.toDouble(); 162 | imageHeight = tile.imageHeight!.toDouble(); 163 | imagePath = this.imagePath + "/" + tile.image!; 164 | 165 | /// print(imagePath); 166 | } else { 167 | imageWidth = tileSet.imageWidth!.toDouble(); 168 | imageHeight = tileSet.imageHeight!.toDouble(); 169 | imagePath = this.imagePath + "/" + tileSet.image!; 170 | } 171 | 172 | /// Image创建 173 | ui.Image image = await DFAssetsLoader.loadImage(imagePath); 174 | DFImageSprite sprite = DFImageSprite( 175 | image, 176 | rect: DFRect(0, 0, imageWidth, imageHeight), 177 | rotated: false, 178 | ); 179 | sprite.scale = scale; 180 | sprite.position = DFPosition( 181 | column * tileWidth * scale + tileWidth / 2 * scale, row * tileHeight * scale + tileHeight / 2 * scale); 182 | sprite.key = row.toString() + "," + column.toString(); 183 | return sprite; 184 | } 185 | 186 | /// 获取列 187 | double _getX(int index, int width) { 188 | return (index % width).toDouble(); 189 | } 190 | 191 | /// 获取行 192 | double _getY(int index, int width) { 193 | return (index / width).floor().toDouble(); 194 | } 195 | 196 | /// 检查碰撞和遮挡 197 | /// 遮挡1 198 | /// 碰撞2 199 | /// 没有0 200 | int isCollided(DFShape shape) { 201 | bool isBlock = false; 202 | bool isAlpha = false; 203 | 204 | if (blockLayer != null && alphaLayer != null) { 205 | List points = []; 206 | if (shape is DFRect) { 207 | points.add(DFPosition(shape.left, shape.top)); 208 | points.add(DFPosition(shape.right, shape.top)); 209 | points.add(DFPosition(shape.right, shape.bottom)); 210 | points.add(DFPosition(shape.left, shape.bottom)); 211 | } else if (shape is DFCircle) { 212 | points.add(DFPosition(shape.center.x - shape.radius, shape.center.y - shape.radius)); 213 | points.add(DFPosition(shape.center.x + shape.radius, shape.center.y - shape.radius)); 214 | points.add(DFPosition(shape.center.x - shape.radius, shape.center.y + shape.radius)); 215 | points.add(DFPosition(shape.center.x + shape.radius, shape.center.y + shape.radius)); 216 | } 217 | 218 | int columnCount = tileMap!.width!; 219 | double scaledTiledWidth = this.tileMap!.tileWidth! * this.scale; 220 | double scaledTiledHeight = this.tileMap!.tileHeight! * this.scale; 221 | 222 | /// 获取形状的4个点进行判断碰撞,比遍历性能会高很多 223 | for (int i = 0; i < points.length; i++) { 224 | int row = (points[i].y / scaledTiledHeight).ceil() - 1; 225 | int column = (points[i].x / scaledTiledWidth).ceil() - 1; 226 | 227 | /// print("row:" + row.toString() + ",column:" + column.toString()); 228 | int index = row * columnCount + column; 229 | 230 | /// print("index:" + index.toString()); 231 | if (blockLayer!.data![index] != 0) { 232 | isBlock = true; 233 | break; 234 | } else if (alphaLayer!.data![index] != 0) { 235 | isAlpha = true; 236 | } 237 | } 238 | } 239 | if (isBlock) { 240 | return 2; 241 | } else if (isAlpha) { 242 | return 1; 243 | } 244 | return 0; 245 | } 246 | 247 | /// 绘制碰撞层和遮挡层 248 | void drawBlockAndAlphaLayer(Canvas canvas) { 249 | if (blockLayer != null && this.cameraPosition != null) { 250 | /// 可视区域 251 | DFCircle visibleShape = DFCircle(this.cameraPosition!, 300); 252 | 253 | /// print("visibleRect:" + visibleRect.toString()); 254 | int columnCount = tileMap!.width!; 255 | double tileWidth = tileMap!.tileWidth!.toDouble() * this.scale; 256 | double tileHeight = tileMap!.tileHeight!.toDouble() * this.scale; 257 | 258 | for (int i = 0; i < blockLayer!.data!.length; i++) { 259 | if (blockLayer!.data![i] != 0) { 260 | var paint = new Paint()..color = Color(0x60f05b72); 261 | int row = _getY(i, columnCount).toInt(); 262 | int column = _getX(i, columnCount).toInt(); 263 | DFRect tileRect = DFRect(column * tileWidth, row * tileHeight, tileWidth, tileHeight); 264 | if (visibleShape.overlaps(tileRect)) { 265 | /// 在可视区域的瓦片设置为显示 266 | canvas.drawRect(tileRect.toRect(), paint); 267 | } 268 | } else if (alphaLayer!.data![i] != 0) { 269 | var paint = new Paint()..color = Color(0x60426ab3); 270 | int row = _getY(i, columnCount).toInt(); 271 | int column = _getX(i, columnCount).toInt(); 272 | DFRect tileRect = DFRect(column * tileWidth, row * tileHeight, tileWidth, tileHeight); 273 | if (visibleShape.overlaps(tileRect)) { 274 | /// 在可视区域的瓦片设置为显示 275 | canvas.drawRect(tileRect.toRect(), paint); 276 | } 277 | } 278 | } 279 | } 280 | } 281 | 282 | /// 精灵更新 283 | @override 284 | void update(double dt) {} 285 | 286 | /// 精灵渲染 287 | @override 288 | void render(Canvas canvas) { 289 | /// 画布暂存 290 | canvas.save(); 291 | 292 | /// 将子精灵转换为相对坐标 293 | canvas.translate(position.x, position.y); 294 | 295 | if (this.sprites.length > 0) { 296 | this.sprites.forEach((sprite) { 297 | sprite.render(canvas); 298 | }); 299 | } 300 | 301 | /// 精灵矩形边界 302 | /*if(this.cameraPosition!=null){ 303 | var paint = new Paint()..color = Color(0x6000FF00); 304 | DFRect visibleRect = DFRect(this.cameraPosition!.x - 50,this.cameraPosition!.y -50, 100, 100); 305 | canvas.drawRect(visibleRect.toRect(), paint); 306 | }*/ 307 | 308 | /// 绘制碰撞层和遮挡层 309 | if (DFConfig.debug) { 310 | drawBlockAndAlphaLayer(canvas); 311 | } 312 | 313 | /// 画布恢复 314 | canvas.restore(); 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | INFOPLIST_FILE = Runner/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 294 | PRODUCT_BUNDLE_IDENTIFIER = com.ymbok.example; 295 | PRODUCT_NAME = "$(TARGET_NAME)"; 296 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 297 | SWIFT_VERSION = 5.0; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | }; 300 | name = Profile; 301 | }; 302 | 97C147031CF9000F007C117D /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INFINITE_RECURSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = dwarf; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PREPROCESSOR_DEFINITIONS = ( 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 350 | MTL_ENABLE_DEBUG_INFO = YES; 351 | ONLY_ACTIVE_ARCH = YES; 352 | SDKROOT = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | }; 355 | name = Debug; 356 | }; 357 | 97C147041CF9000F007C117D /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | CLANG_ANALYZER_NONNULL = YES; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_COMMA = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INFINITE_RECURSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 378 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 381 | CLANG_WARN_STRICT_PROTOTYPES = YES; 382 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | COPY_PHASE_STRIP = NO; 387 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 388 | ENABLE_NS_ASSERTIONS = NO; 389 | ENABLE_STRICT_OBJC_MSGSEND = YES; 390 | GCC_C_LANGUAGE_STANDARD = gnu99; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 393 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 394 | GCC_WARN_UNDECLARED_SELECTOR = YES; 395 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 396 | GCC_WARN_UNUSED_FUNCTION = YES; 397 | GCC_WARN_UNUSED_VARIABLE = YES; 398 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 399 | MTL_ENABLE_DEBUG_INFO = NO; 400 | SDKROOT = iphoneos; 401 | SUPPORTED_PLATFORMS = iphoneos; 402 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 403 | TARGETED_DEVICE_FAMILY = "1,2"; 404 | VALIDATE_PRODUCT = YES; 405 | }; 406 | name = Release; 407 | }; 408 | 97C147061CF9000F007C117D /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 411 | buildSettings = { 412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 413 | CLANG_ENABLE_MODULES = YES; 414 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 415 | ENABLE_BITCODE = NO; 416 | INFOPLIST_FILE = Runner/Info.plist; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 418 | PRODUCT_BUNDLE_IDENTIFIER = com.ymbok.example; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 422 | SWIFT_VERSION = 5.0; 423 | VERSIONING_SYSTEM = "apple-generic"; 424 | }; 425 | name = Debug; 426 | }; 427 | 97C147071CF9000F007C117D /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 430 | buildSettings = { 431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 432 | CLANG_ENABLE_MODULES = YES; 433 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 434 | ENABLE_BITCODE = NO; 435 | INFOPLIST_FILE = Runner/Info.plist; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = com.ymbok.example; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 440 | SWIFT_VERSION = 5.0; 441 | VERSIONING_SYSTEM = "apple-generic"; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 97C147031CF9000F007C117D /* Debug */, 452 | 97C147041CF9000F007C117D /* Release */, 453 | 249021D3217E4FDB00AE95B9 /* Profile */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 97C147061CF9000F007C117D /* Debug */, 462 | 97C147071CF9000F007C117D /* Release */, 463 | 249021D4217E4FDB00AE95B9 /* Profile */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | /* End XCConfigurationList section */ 469 | }; 470 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 471 | } 472 | --------------------------------------------------------------------------------