Skip to content

Commit

Permalink
feat: some fixes for real game examples
Browse files Browse the repository at this point in the history
  • Loading branch information
ASGAlex committed Nov 15, 2023
1 parent 981eeb2 commit 793ba12
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 15 deletions.
10 changes: 10 additions & 0 deletions example/lib/game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,16 @@ class DemoMapLoader extends TiledMapLoader {

@override
Map<String, LayerBuilderFunction>? get layerBuilders => null;

@override
CellLayer customLayerBuilder(
PositionComponent component,
Cell cell,
String layerName,
LayerComponentsStorageMode componentsStorageMode,
) {
throw UnimplementedError();
}
}
//#endregion

Expand Down
3 changes: 2 additions & 1 deletion lib/src/collisions/broadphase.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:dart_bloom_filter/dart_bloom_filter.dart';
import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'package:flame_spatial_grid/flame_spatial_grid.dart';
import 'package:flame_spatial_grid/src/collisions/collision_prospect/prospect_pool.dart';
import 'package:flame_spatial_grid/src/collisions/optimizer/optimized_collisions_list.dart';
import 'package:meta/meta.dart';

Expand Down Expand Up @@ -43,7 +44,7 @@ class SpatialGridBroadphase extends Broadphase<ShapeHitbox> {

final SpatialGrid spatialGrid;

final _prospectPool = ProspectPool<ShapeHitbox>();
final _prospectPool = ProspectPoolGrouped();
var _prospectPoolIndex = 0;
final _dummyHitbox = DummyHitbox();
final _potentials = <int, CollisionProspect<ShapeHitbox>>{};
Expand Down
16 changes: 13 additions & 3 deletions lib/src/collisions/collision_detection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ class SpatialGridCollisionDetection
items.forEach(remove);
}

final HashSet<CollisionProspect<ShapeHitbox>> _lastPotentials =
HashSet<CollisionProspect<ShapeHitbox>>();
final _lastPotentials = HashSet<CollisionProspect<ShapeHitbox>>();

@override
void run() {
Expand All @@ -153,17 +152,28 @@ class SpatialGridCollisionDetection

final allHashes =
Set.unmodifiable(unmodifiableAllPotentials.map((p) => p.hash));
final lastHashes = _lastPotentials.map(
(e) => e.hash,
);
// Handles callbacks for an ended collision that the broadphase didn't
// report as a potential collision anymore.
for (final prospect in _lastPotentials) {
if (!allHashes.contains(prospect.hash) &&
prospect.a.collidingWith(prospect.b)) {
(prospect.a.collidingWith(prospect.b) ||
_isPotentialCollidingGroup(prospect.b, prospect.a))) {
handleCollisionEnd(prospect.a, prospect.b);
}
}
_updateLastPotentials(unmodifiableAllPotentials);
}

bool _isPotentialCollidingGroup(ShapeHitbox potential, ShapeHitbox active) {
if (potential is GroupHitbox && potential.collidingWith(active)) {
return true;
}
return false;
}

void _updateTransform(ShapeHitbox item) {
if (item is BoundingHitbox) {
item.aabbCenter = item.aabb.center;
Expand Down
34 changes: 34 additions & 0 deletions lib/src/collisions/collision_prospect/collision_prospect.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'package:flame/collisions.dart';
import 'package:flame_spatial_grid/flame_spatial_grid.dart';

/// A [CollisionProspect] is a tuple that is used to contain two potentially
/// colliding hitboxes.
class CollisionProspectGrouped extends CollisionProspect<ShapeHitbox> {
@override
int get hash => _hashGrouped;
int _hashGrouped;

CollisionProspectGrouped(super.a, super.b)
: _hashGrouped = a.hashCode ^
((b is GroupHitbox) ? b.hashCodeForCollisions : b.hashCode);

@override
void set(ShapeHitbox a, ShapeHitbox b) {
super.set(a, b);
_hashGrouped = a.hashCode ^
((b is GroupHitbox) ? b.hashCodeForCollisions : b.hashCode);
}

/// Sets the prospect to contain the content of [other].
@override
void setFrom(CollisionProspect<ShapeHitbox> other) {
super.setFrom(other);
if (other is CollisionProspectGrouped) {
_hashGrouped = other._hashGrouped;
}
}

/// Creates a new prospect object with the same content.
@override
CollisionProspect<ShapeHitbox> clone() => CollisionProspectGrouped(a, b);
}
26 changes: 26 additions & 0 deletions lib/src/collisions/collision_prospect/prospect_pool.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:flame/collisions.dart';
import 'package:flame_spatial_grid/src/collisions/collision_prospect/collision_prospect.dart';

/// This pool is used to not create unnecessary [CollisionProspect] objects
/// during collision detection, but to re-use the ones that have already been
/// created.
class ProspectPoolGrouped extends ProspectPool<ShapeHitbox> {
ProspectPoolGrouped({super.incrementSize = 1000});

final _storage = <CollisionProspectGrouped>[];

@override
int get length => _storage.length;

/// The size of the pool will expand with [incrementSize] amount of
/// [CollisionProspect]s that are initially populated with two [dummyItem]s.
@override
void expand(ShapeHitbox dummyItem) {
for (var i = 0; i < incrementSize; i++) {
_storage.add(CollisionProspectGrouped(dummyItem, dummyItem));
}
}

@override
CollisionProspect<ShapeHitbox> operator [](int index) => _storage[index];
}
14 changes: 14 additions & 0 deletions lib/src/collisions/hitboxes/bounding_hitbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -457,4 +457,18 @@ class BoundingHitbox extends RectangleHitboxOptimized
}
return super.intersections(other);
}

@override
@mustCallSuper
void onCollisionStart(Set<Vector2> intersectionPoints, ShapeHitbox other) {
group?.onCollisionStart(intersectionPoints, other);
super.onCollisionStart(intersectionPoints, other);
}

@override
@mustCallSuper
void onCollisionEnd(ShapeHitbox other) {
group?.onCollisionEnd(other);
super.onCollisionEnd(other);
}
}
18 changes: 18 additions & 0 deletions lib/src/collisions/hitboxes/group_hitbox.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'dart:ui';

import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'package:flame_spatial_grid/flame_spatial_grid.dart';
import 'package:meta/meta.dart';

class GroupHitbox extends BoundingHitbox {
GroupHitbox({
Expand All @@ -21,6 +23,8 @@ class GroupHitbox extends BoundingHitbox {
@override
bool get optimized => false;

int get hashCodeForCollisions => parentWithGridSupport.hashCode;

@override
void renderDebugMode(Canvas canvas) {
canvas.drawRect(
Expand All @@ -30,4 +34,18 @@ class GroupHitbox extends BoundingHitbox {
..style = PaintingStyle.stroke,
);
}

@override
@mustCallSuper
void onCollisionStart(Set<Vector2> intersectionPoints, ShapeHitbox other) {
activeCollisions.add(other);
onCollisionStartCallback?.call(intersectionPoints, other);
}

@override
@mustCallSuper
void onCollisionEnd(ShapeHitbox other) {
activeCollisions.remove(other);
onCollisionEndCallback?.call(other);
}
}
14 changes: 12 additions & 2 deletions lib/src/components/layers/cell_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,17 @@ abstract class CellLayer extends PositionComponent

Future<void>? _updateLayerFuture;

bool ignoreCache = false;
bool skipCollisionOptimization = false;

Future<void> updateLayer([double dt = 0.001]) {
if (isUpdateNeeded) {
if (_updateLayerFuture != null) {
return Future<void>.value();
}
if (cacheKey.key != null && onCheckCache(cacheKey.key!)) {
if (ignoreCache) {
ignoreCache = false;
} else if (cacheKey.key != null && onCheckCache(cacheKey.key!)) {
isUpdateNeeded = false;
return Future<void>.value();
}
Expand All @@ -316,7 +321,12 @@ abstract class CellLayer extends PositionComponent
game.processLifecycleEvents();
if (currentCell?.state == CellState.active) {
if (optimizeCollisions) {
await collisionOptimizer.optimize();
if (skipCollisionOptimization) {
skipCollisionOptimization = false;
} else {
await collisionOptimizer.optimize();
game.processLifecycleEvents();
}
}
if (renderMode != LayerRenderMode.component) {
compileToSingleLayer(components);
Expand Down
8 changes: 2 additions & 6 deletions lib/src/components/layers/cell_static_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,12 @@ class CellStaticLayer extends CellLayer {
}) {
paint.isAntiAlias = false;
paint.filterQuality = FilterQuality.none;
_imagePaint.isAntiAlias = false;
_imagePaint.filterQuality = FilterQuality.none;
}

static final _compiledLayersCache = <int, ImageCacheEntry>{};

final _layerCacheKey = LayerCacheKey();

final _imagePaint = Paint();

@override
LayerCacheKey get cacheKey => _layerCacheKey;

Expand Down Expand Up @@ -64,7 +60,7 @@ class CellStaticLayer extends CellLayer {
canvas.drawImage(
layerImage!,
correctionTopLeft.toOffset(),
_imagePaint,
paint,
);
}
break;
Expand All @@ -73,7 +69,7 @@ class CellStaticLayer extends CellLayer {
canvas.drawImage(
layerImage!,
correctionTopLeft.toOffset(),
_imagePaint,
paint,
);
} else if (layerPicture != null) {
canvas.drawPicture(layerPicture!);
Expand Down
22 changes: 22 additions & 0 deletions lib/src/components/layers/layers_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import 'package:flame/extensions.dart';
import 'package:flame_spatial_grid/flame_spatial_grid.dart';
import 'package:meta/meta.dart';

typedef CustomLayerBuilder = CellLayer Function(
PositionComponent component,
Cell cell,
String layerName,
LayerComponentsStorageMode componentsStorageMode,
);

class LayersRootComponent extends Component with UpdateOnDemand {}

/// The class provides easy-to-use API layer to access game layer's
Expand All @@ -21,6 +28,8 @@ class LayersManager {

final layersRootComponent = <int, LayersRootComponent>{};

CustomLayerBuilder? customLayerBuilder;

/// Adding manually created [CellLayer] into [layersRootComponent].
/// Usually there is no need to use this function, try [addComponent] instead.
void addLayer(CellLayer layer) {
Expand Down Expand Up @@ -130,6 +139,19 @@ class LayersManager {
);
}
break;
case MapLayerType.custom:
if (isNew) {
if (customLayerBuilder == null) {
throw 'Trying to build custom layer without builder being specified';
}
layer = customLayerBuilder!.call(
component,
cell,
layerName,
componentsStorageMode,
);
}
break;
}

if (absolutePosition) {
Expand Down
17 changes: 14 additions & 3 deletions lib/src/tiled/map_loader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ enum MapLayerType {
animated,

/// [CellTrailLayer] class instance
trail
trail,

/// Use [LayersManager.customLayerBuilder] to build layer class
custom,
}

/// This is base class for describing a Tiled map. You going to create
Expand Down Expand Up @@ -143,6 +146,13 @@ abstract class TiledMapLoader<T extends HasSpatialGridFramework, C> {
}
}

CellLayer customLayerBuilder(
PositionComponent component,
Cell cell,
String layerName,
LayerComponentsStorageMode componentsStorageMode,
);

Future<Vector2?> searchInitialPosition(
InitialPositionChecker checkFunction, [
String? worldName,
Expand Down Expand Up @@ -245,14 +255,16 @@ abstract class TiledMapLoader<T extends HasSpatialGridFramework, C> {
return;
}

game.layersManager.customLayerBuilder = customLayerBuilder;

for (final context in contextList) {
final builderType =
context.tileDataProvider?.tile.type ?? context.tiledObject?.type;
if (context.removed) {
continue;
} else {
cell.tileBuilderContextProvider = tileBuilderContextProvider;
_layerBuilder(cell, builderType, context, rootComponent);
await _layerBuilder(cell, builderType, context, rootComponent);
}

await cellPostBuilder?.call(context);
Expand Down Expand Up @@ -296,7 +308,6 @@ abstract class TiledMapLoader<T extends HasSpatialGridFramework, C> {
TileBuilderContext<C> context,
Component rootComponent,
) async {
/// INVOKE HERE
var processor = tileBuilders?[builderType];
if (processor != null) {
await processor(context);
Expand Down

0 comments on commit 793ba12

Please sign in to comment.