Skip to content

Commit

Permalink
WIP lazily evaluated raycast result values
Browse files Browse the repository at this point in the history
  • Loading branch information
spydon committed Aug 13, 2022
1 parent b7e1d02 commit 8798efb
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 89 deletions.
63 changes: 41 additions & 22 deletions packages/flame/lib/src/collisions/hitboxes/circle_hitbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,34 +67,53 @@ class CircleHitbox extends CircleComponent with ShapeHitbox {
return null;
} else {
final result = out ?? RaycastResult();
final intersectionPoint = intersections.first;
_temporaryNormal
..setFrom(intersectionPoint)
..sub(_temporaryAbsoluteCenter)
..normalize();
if (isInsideHitbox) {
_temporaryNormal.invert();
}
final reflectionDirection =
(out?.reflectionRay?.direction ?? Vector2.zero())
..setFrom(ray.direction)
..reflect(_temporaryNormal);

final reflectionRay = (out?.reflectionRay
?..setWith(
origin: intersectionPoint,
direction: reflectionDirection,
)) ??
Ray2(intersectionPoint, reflectionDirection);
result.hitboxCenter = (result.hitboxCenter
?..setFrom(_temporaryAbsoluteCenter)) ??
_temporaryAbsoluteCenter.clone();

result.setWith(
hitbox: this,
reflectionRay: reflectionRay,
normal: _temporaryNormal,
distance: ray.origin.distanceTo(intersectionPoint),
isInsideHitbox: isInsideHitbox,
intersectionPointFunction: () => intersections.first,
);
result.setWith(
distanceFunction: () =>
result.originatingRay?.origin.distanceTo(result.intersectionPoint!),
normalFunction: () =>
_rayNormal(result, isInsideHitbox: isInsideHitbox),
);
result.setWith(
reflectionRayFunction: () => _rayReflection(result),
);
return result;
}
}

Vector2 _rayNormal(RaycastResult result, {required bool isInsideHitbox}) {
_temporaryNormal
..setFrom(result.intersectionPoint!)
..sub(result.hitboxCenter!)
..normalize();
if (isInsideHitbox) {
_temporaryNormal.invert();
}
return _temporaryNormal;
}

Ray2 _rayReflection(RaycastResult result) {
final intersectionPoint = result.intersectionPoint!;

final reflectionDirection =
(result.rawReflectionRay?.direction ?? Vector2.zero())
..setFrom(result.originatingRay!.direction)
..reflect(result.normal!);

final reflectionRay = (result.rawReflectionRay
?..setWith(
origin: intersectionPoint,
direction: reflectionDirection,
)) ??
Ray2(intersectionPoint, reflectionDirection);
return reflectionRay;
}
}
181 changes: 146 additions & 35 deletions packages/flame/lib/src/collisions/raycast_result.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flame/collisions.dart';
import 'package:flame/extensions.dart';
import 'package:flame/src/geometry/ray2.dart';
import 'package:meta/meta.dart';

/// The result of a raycasting operation.
///
Expand All @@ -10,15 +11,22 @@ import 'package:flame/src/geometry/ray2.dart';
class RaycastResult<T extends Hitbox<T>> {
RaycastResult({
T? hitbox,
Ray2? reflectionRay,
Vector2? normal,
double? distance,
bool isInsideHitbox = false,
}) : _isInsideHitbox = isInsideHitbox,
_hitbox = hitbox,
_reflectionRay = reflectionRay ?? Ray2.zero(),
_normal = normal ?? Vector2.zero(),
_distance = distance ?? double.maxFinite;
Ray2? originatingRay,
double? Function()? distanceFunction,
Vector2? Function()? intersectionPointFunction,
Vector2? Function()? normalFunction,
Ray2? Function()? reflectionRayFunction,
}) : _hitbox = hitbox,
_isInsideHitbox = isInsideHitbox,
rawOriginatingRay = originatingRay,
_distanceFunction = distanceFunction,
_intersectionPointFunction = intersectionPointFunction,
_normalFunction = normalFunction,
_reflectionRayFunction = reflectionRayFunction;

T? get hitbox => isActive ? _hitbox : null;
T? _hitbox;

/// Whether this result has active results in it.
///
Expand All @@ -30,59 +38,162 @@ class RaycastResult<T extends Hitbox<T>> {
bool get isInsideHitbox => _isInsideHitbox;
bool _isInsideHitbox;

T? _hitbox;
T? get hitbox => isActive ? _hitbox : null;
/// The casted ray that the result came from.
Ray2? get originatingRay => isActive ? rawOriginatingRay : null;
@internal
Ray2? rawOriginatingRay;

double? Function()? _distanceFunction;
Vector2? Function()? _intersectionPointFunction;
Vector2? Function()? _normalFunction;
Ray2? Function()? _reflectionRayFunction;

final Ray2 _reflectionRay;
Ray2? get reflectionRay => isActive ? _reflectionRay : null;
double? _rawDistance;
bool _validDistance = false;
double? get distance {
if (!_validDistance) {
_rawDistance = _distanceFunction?.call();
_validDistance = true;
}
return _rawDistance;
}

Vector2? get intersectionPoint => reflectionRay?.origin;
@internal
Vector2? rawIntersectionPoint;
bool _validIntersectionPoint = false;
Vector2? get intersectionPoint {
if (!isActive) {
return null;
}
if (!_validIntersectionPoint) {
rawIntersectionPoint = _intersectionPointFunction?.call();
_validIntersectionPoint = true;
}
return rawIntersectionPoint;
}

@internal
Vector2? rawNormal;
bool _validNormal = false;
Vector2? get normal {
if (!isActive) {
return null;
}
if (!_validNormal) {
rawNormal = _normalFunction?.call();
_validNormal = true;
}
return rawNormal;
}

double _distance;
double? get distance => isActive ? _distance : null;
@internal
Ray2? rawReflectionRay;
bool _validReflectionRay = false;
Ray2? get reflectionRay {
if (!isActive) {
return null;
}
if (!_validReflectionRay) {
rawReflectionRay = _reflectionRayFunction?.call();
_validReflectionRay = true;
}
return rawReflectionRay;
}

final Vector2 _normal;
Vector2? get normal => isActive ? _normal : null;
/// Used for storing the center of the [CircleHitbox] to be able to lazily
/// compute other values.
@internal
Vector2? hitboxCenter;

void reset() => _hitbox = null;
void reset() {
_hitbox = null;
_validDistance = false;
_validIntersectionPoint = false;
_validNormal = false;
_validReflectionRay = false;
}

/// Sets this [RaycastResult]'s objects to the values stored in [other].
void setFrom(RaycastResult<T> other) {
setWith(
hitbox: other.hitbox,
reflectionRay: other.reflectionRay,
normal: other.normal,
distance: other.distance,
isInsideHitbox: other.isInsideHitbox,
originatingRay: other.rawOriginatingRay,
distanceFunction: other._distanceFunction,
intersectionPointFunction: other._intersectionPointFunction,
normalFunction: other._normalFunction,
reflectionRayFunction: other._reflectionRayFunction,
);
_validDistance = other._validDistance;
_rawDistance = other._rawDistance;

_validIntersectionPoint = other._validIntersectionPoint;
if (_validIntersectionPoint) {
rawIntersectionPoint = (rawIntersectionPoint
?..setFrom(other.rawIntersectionPoint!)) ??
other.rawIntersectionPoint!.clone();
}

_validNormal = other._validNormal;
if (_validNormal) {
rawNormal =
(rawNormal?..setFrom(other.rawNormal!)) ?? other.rawNormal!.clone();
}

_validReflectionRay = other._validReflectionRay;
if (_validReflectionRay) {
rawReflectionRay = (rawReflectionRay
?..setFrom(other.rawReflectionRay!)) ??
other.rawReflectionRay!.clone();
}
}

/// Sets the values of the result from the specified arguments.
///
/// Values that are not specified are kept as their previous values.
void setWith({
T? hitbox,
Ray2? reflectionRay,
Vector2? normal,
double? distance,
bool isInsideHitbox = false,
bool? isInsideHitbox,
Ray2? originatingRay,
double? Function()? distanceFunction,
Vector2? Function()? intersectionPointFunction,
Vector2? Function()? normalFunction,
Ray2? Function()? reflectionRayFunction,
}) {
_hitbox = hitbox;
if (reflectionRay != null) {
_reflectionRay.setFrom(reflectionRay);
_hitbox = hitbox ?? _hitbox;
_isInsideHitbox = isInsideHitbox ?? _isInsideHitbox;
if (distanceFunction != null) {
_distanceFunction = distanceFunction;
_validDistance = false;
}
if (intersectionPointFunction != null) {
_intersectionPointFunction = intersectionPointFunction;
_validIntersectionPoint = false;
}
if (normalFunction != null) {
_normalFunction = normalFunction;
_validNormal = false;
}
if (reflectionRayFunction != null) {
_reflectionRayFunction = reflectionRayFunction;
_validReflectionRay = false;
}
if (normal != null) {
_normal.setFrom(normal);
if (rawOriginatingRay != null && originatingRay != null) {
rawOriginatingRay?.setFrom(originatingRay);
} else {
rawOriginatingRay = originatingRay?.clone() ?? rawOriginatingRay;
}
_distance = distance ?? double.maxFinite;
_isInsideHitbox = isInsideHitbox;
}

RaycastResult<T> clone() {
return RaycastResult(
hitbox: hitbox,
reflectionRay: _reflectionRay.clone(),
normal: _normal.clone(),
distance: distance,
isInsideHitbox: isInsideHitbox,
originatingRay: originatingRay?.clone(),
distanceFunction: _distanceFunction,
intersectionPointFunction: _intersectionPointFunction,
normalFunction: _normalFunction,
reflectionRayFunction: _reflectionRayFunction,
);
}
}
Loading

0 comments on commit 8798efb

Please sign in to comment.