389 lines
10 KiB
Dart
389 lines
10 KiB
Dart
import 'dart:collection';
|
||
import 'dart:math';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:maplibre/maplibre.dart';
|
||
import 'package:maplibre_gl/maplibre_gl.dart' as gl;
|
||
import '../screens/map_screen.dart';
|
||
import 'entity.dart';
|
||
import 'component.dart';
|
||
|
||
/// 系统基类 - 用于处理特定类型的组件
|
||
abstract class System {
|
||
void update(List<Entity> entities, Duration deltaTime);
|
||
}
|
||
|
||
/// 地图系统 - 处理地图相关的实体和组件
|
||
class MapSystem implements System {
|
||
gl.MapLibreMapController? mapController;
|
||
MapUiBodyState? mapUiBodyState;
|
||
final List<String> _plotIds = [];
|
||
final List<String> _trailIds = [];
|
||
final List<String> _iconIds = [];
|
||
|
||
// 单例实例
|
||
static MapSystem? _instance;
|
||
|
||
// 工厂构造函数,实现单例
|
||
factory MapSystem({gl.MapLibreMapController? mapController, MapUiBodyState? mapUiBodyState}) {
|
||
_instance ??= MapSystem._internal(mapController: mapController, mapUiBodyState: mapUiBodyState);
|
||
if (mapController != null) _instance!.mapController = mapController;
|
||
if (mapUiBodyState != null) _instance!.mapUiBodyState = mapUiBodyState;
|
||
return _instance!;
|
||
}
|
||
|
||
// 私有构造函数
|
||
MapSystem._internal({this.mapController, this.mapUiBodyState});
|
||
|
||
/// 获取单例实例
|
||
static MapSystem get instance {
|
||
_instance ??= MapSystem._internal();
|
||
return _instance!;
|
||
}
|
||
|
||
@override
|
||
void update(List<Entity> entities, Duration deltaTime) {
|
||
// 更新地图上的标绘
|
||
for (var entity in entities) {
|
||
if (entity.hasComponent<PlotComponent>()) {
|
||
_updatePlot(entity.getComponent<PlotComponent>()!);
|
||
}
|
||
|
||
if (entity.hasComponent<IconComponent>()) {
|
||
_updateIcon(entity.getComponent<IconComponent>()!);
|
||
}
|
||
|
||
if (entity.hasComponent<TrailComponent>()) {
|
||
_updateTrail(entity.getComponent<TrailComponent>()!);
|
||
}
|
||
|
||
if (entity.hasComponent<MovementComponent>()) {
|
||
_updateMovement(entity);
|
||
}
|
||
}
|
||
}
|
||
|
||
void _updatePlot(PlotComponent plot) {
|
||
// 实现标绘更新逻辑
|
||
// 这里可以根据plotType绘制不同类型的图形
|
||
if (plot.coordinates.length >= 2) {
|
||
// 示例:绘制线段
|
||
// mapController?.addLine() // 这里需要根据实际API实现
|
||
}
|
||
}
|
||
|
||
void _updateIcon(IconComponent icon) {
|
||
// 实现图标更新逻辑
|
||
// mapController?.addMarker() // 这里需要根据实际API实现
|
||
}
|
||
|
||
void _updateTrail(TrailComponent trail) {
|
||
// 实现拖尾更新逻辑
|
||
// 绘制轨迹线
|
||
}
|
||
|
||
void _updateMovement(Entity entity) {
|
||
var movement = entity.getComponent<MovementComponent>();
|
||
var plot = entity.getComponent<PlotComponent>();
|
||
var icon = entity.getComponent<IconComponent>();
|
||
|
||
if (movement != null && (plot != null || icon != null)) {
|
||
if (movement.target != null) {
|
||
// 简单的移动逻辑 - 向目标点移动
|
||
double dx = movement.target!.longitude - movement.position.longitude;
|
||
double dy = movement.target!.latitude - movement.position.latitude;
|
||
double distance = sqrt(dx * dx + dy * dy);
|
||
|
||
if (distance > 0.0001) { // 如果距离目标足够近就停止
|
||
double ratio = movement.speed * 0.0001 / distance;
|
||
movement.position = gl.LatLng(
|
||
movement.position.latitude + dy * ratio,
|
||
movement.position.longitude + dx * ratio,
|
||
);
|
||
|
||
// 更新图标位置
|
||
if (icon != null) {
|
||
icon.position = movement.position;
|
||
}
|
||
|
||
// 如果有拖尾组件,添加新点
|
||
var trail = entity.getComponent<TrailComponent>();
|
||
if (trail != null) {
|
||
trail.addPoint(movement.position);
|
||
}
|
||
} else {
|
||
movement.target = null; // 到达目标
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ==================== 便捷方法:通过 mapUiBodyState 调用地图接口 ====================
|
||
|
||
/// 添加自定义图片标记
|
||
void addCustomMarker(Geographic position, {Map<String, dynamic>? properties}) {
|
||
mapUiBodyState?.addCustomMarker(position, properties: properties);
|
||
}
|
||
|
||
/// 添加圆形
|
||
void addCircle(
|
||
Geographic position, {
|
||
double radius = 20,
|
||
Color color = Colors.orange,
|
||
Color strokeColor = Colors.red,
|
||
double strokeWidth = 2,
|
||
}) {
|
||
mapUiBodyState?.addCircle(
|
||
position,
|
||
radius: radius,
|
||
color: color,
|
||
strokeWidth: strokeWidth,
|
||
);
|
||
}
|
||
|
||
/// 添加扇形
|
||
/// [center] 扇形中心的地理位置
|
||
/// [radius] 扇形的半径(单位:米)
|
||
/// [startAngle] 起始角度(单位:度,0度表示正东方向)
|
||
/// [endAngle] 结束角度(单位:度)
|
||
/// [segments] 扇形的分段数,值越大越平滑
|
||
/// [color] 填充颜色
|
||
/// [outlineColor] 边框颜色
|
||
void addSector(
|
||
Geographic center, {
|
||
required double radius,
|
||
required double startAngle,
|
||
required double endAngle,
|
||
int segments = 36,
|
||
Color color = Colors.lightBlueAccent,
|
||
Color outlineColor = Colors.blue,
|
||
}) {
|
||
mapUiBodyState?.addSector(
|
||
center,
|
||
radius: radius,
|
||
startAngle: startAngle,
|
||
endAngle: endAngle,
|
||
segments: segments,
|
||
color: color,
|
||
outlineColor: outlineColor,
|
||
);
|
||
}
|
||
|
||
/// 添加长方形
|
||
/// [center] 长方形中心的地理位置
|
||
/// [width] 长方形的宽度(单位:米)
|
||
/// [height] 长方形的高度(单位:米)
|
||
/// [color] 填充颜色
|
||
/// [outlineColor] 边框颜色
|
||
void addRectangle(
|
||
Geographic center, {
|
||
required double width,
|
||
required double height,
|
||
Color color = Colors.lightBlueAccent,
|
||
Color outlineColor = Colors.blue,
|
||
}) {
|
||
mapUiBodyState?.addRectangle(
|
||
center,
|
||
width: width,
|
||
height: height,
|
||
color: color,
|
||
outlineColor: outlineColor,
|
||
);
|
||
}
|
||
|
||
/// 清除所有自定义标记
|
||
void clearCustomMarkers() {
|
||
mapUiBodyState?.clearCustomMarkers();
|
||
}
|
||
|
||
/// 清除所有圆形
|
||
void clearCircles() {
|
||
mapUiBodyState?.clearCircles();
|
||
}
|
||
|
||
/// 清除所有多边形(包括扇形和长方形)
|
||
void clearPolygons() {
|
||
mapUiBodyState?.clearPolygons();
|
||
}
|
||
|
||
/// 清除所有自定义图层
|
||
void clearAllCustomLayers() {
|
||
mapUiBodyState?.clearAllCustomLayers();
|
||
}
|
||
|
||
/// 添加固定大小的圆形(不随地图缩放)
|
||
void addFixedCircle(
|
||
Geographic position, {
|
||
double radius = 20,
|
||
Color color = Colors.orange,
|
||
Color strokeColor = Colors.red,
|
||
double strokeWidth = 2,
|
||
}) {
|
||
mapUiBodyState?.addFixedCircle(
|
||
position,
|
||
radius: radius,
|
||
color: color,
|
||
outlineColor: strokeColor,
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 音频系统 - 处理音频相关的实体和组件
|
||
class AudioSystem implements System {
|
||
@override
|
||
void update(List<Entity> entities, Duration deltaTime) {
|
||
for (var entity in entities) {
|
||
if (entity.hasComponent<AudioComponent>()) {
|
||
var audioComponent = entity.getComponent<AudioComponent>()!;
|
||
_playAudio(audioComponent);
|
||
}
|
||
}
|
||
}
|
||
|
||
void _playAudio(AudioComponent audio) {
|
||
// 实现音频播放逻辑
|
||
// 这里可以使用Flutter的音频插件如audioplayers
|
||
print("Playing audio: ${audio.audioPath}");
|
||
}
|
||
}
|
||
|
||
/// 组件系统 - 管理各种组件的交互
|
||
class ComponentSystem implements System {
|
||
@override
|
||
void update(List<Entity> entities, Duration deltaTime) {
|
||
// 处理组件间的交互
|
||
for (var entity in entities) {
|
||
// 可以根据需要实现组件交互逻辑
|
||
if (entity.hasComponent<PlotComponent>() && entity.hasComponent<IconComponent>()) {
|
||
// 当一个实体同时有标绘和图标组件时的特殊处理
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// ECS管理器 - 管理实体、系统和整个ECS框架
|
||
class ECSManager {
|
||
static ECSManager? _instance;
|
||
|
||
factory ECSManager() {
|
||
_instance ??= ECSManager._internal();
|
||
return _instance!;
|
||
}
|
||
|
||
ECSManager._internal();
|
||
|
||
/// 获取单例实例
|
||
static ECSManager get instance {
|
||
_instance ??= ECSManager._internal();
|
||
return _instance!;
|
||
}
|
||
|
||
final List<Entity> _entities = [];
|
||
final List<System> _systems = [];
|
||
final Queue<Entity> _entitiesToAdd = Queue<Entity>();
|
||
final Queue<Entity> _entitiesToRemove = Queue<Entity>();
|
||
|
||
void addEntity(Entity entity) {
|
||
_entitiesToAdd.add(entity);
|
||
}
|
||
|
||
void removeEntity(Entity entity) {
|
||
_entitiesToRemove.add(entity);
|
||
}
|
||
|
||
void addSystem(System system) {
|
||
_systems.add(system);
|
||
}
|
||
|
||
void removeSystem(System system) {
|
||
_systems.remove(system);
|
||
}
|
||
|
||
void update(Duration deltaTime) {
|
||
// 添加新实体
|
||
while (_entitiesToAdd.isNotEmpty) {
|
||
_entities.add(_entitiesToAdd.removeFirst());
|
||
}
|
||
|
||
// 更新所有系统
|
||
for (var system in _systems) {
|
||
system.update(_entities, deltaTime);
|
||
}
|
||
|
||
// 移除实体
|
||
while (_entitiesToRemove.isNotEmpty) {
|
||
var entity = _entitiesToRemove.removeFirst();
|
||
_entities.remove(entity);
|
||
}
|
||
}
|
||
|
||
List<Entity> get entities => _entities;
|
||
|
||
/// 工厂方法:创建一个带位置的实体
|
||
Entity createEntityAtPosition({
|
||
required String name,
|
||
required gl.LatLng position,
|
||
String? plotType,
|
||
Color plotColor = Colors.red,
|
||
double strokeWidth = 2.0,
|
||
String? iconPath,
|
||
double iconSize = 30.0,
|
||
bool hasTrail = false,
|
||
Color trailColor = Colors.blue,
|
||
double trailMaxLength = 20.0,
|
||
bool hasMovement = false,
|
||
gl.LatLng? movementTarget,
|
||
double movementSpeed = 1.0,
|
||
String? audioPath,
|
||
bool audioLoop = false,
|
||
double audioVolume = 1.0,
|
||
}) {
|
||
// 创建实体
|
||
final entity = Entity(name: name);
|
||
|
||
// 添加位置相关的组件
|
||
if (plotType != null) {
|
||
entity.addComponent(PlotComponent(
|
||
plotType: plotType,
|
||
coordinates: [position],
|
||
color: plotColor,
|
||
strokeWidth: strokeWidth,
|
||
));
|
||
}
|
||
|
||
if (iconPath != null) {
|
||
entity.addComponent(IconComponent(
|
||
iconPath: iconPath,
|
||
position: position,
|
||
size: iconSize,
|
||
));
|
||
}
|
||
|
||
if (hasTrail) {
|
||
entity.addComponent(TrailComponent(
|
||
trailColor: trailColor,
|
||
maxLength: trailMaxLength,
|
||
));
|
||
}
|
||
|
||
if (hasMovement) {
|
||
entity.addComponent(MovementComponent(
|
||
position: position,
|
||
target: movementTarget,
|
||
speed: movementSpeed,
|
||
));
|
||
}
|
||
|
||
if (audioPath != null) {
|
||
entity.addComponent(AudioComponent(
|
||
audioPath: audioPath,
|
||
loop: audioLoop,
|
||
volume: audioVolume,
|
||
));
|
||
}
|
||
|
||
// 添加到管理器
|
||
addEntity(entity);
|
||
|
||
return entity;
|
||
}
|
||
} |