mirror of
https://github.com/flame-engine/flame.git
synced 2025-10-28 23:46:52 +08:00
188 lines
4.9 KiB
Dart
188 lines
4.9 KiB
Dart
import 'dart:math';
|
|
|
|
import 'package:flame/collisions.dart';
|
|
import 'package:flame/components.dart';
|
|
import 'package:flame/events.dart';
|
|
import 'package:flame/game.dart';
|
|
import 'package:flame/geometry.dart';
|
|
import 'package:flame/palette.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class RaytraceExample extends FlameGame
|
|
with HasCollisionDetection, MouseMovementDetector, TapCallbacks {
|
|
static const description = '''
|
|
In this example the raytrace functionality is showcased.
|
|
Click to start sending out a ray which will bounce around to visualize how it
|
|
works. If you move the mouse around the canvas, rays and their reflections will
|
|
be moved rendered and if you click again some more objects that the rays can
|
|
bounce on will appear.
|
|
''';
|
|
|
|
final _colorTween = ColorTween(
|
|
begin: Colors.amber.withValues(alpha: 1.0),
|
|
end: Colors.lightBlueAccent.withValues(alpha: 1.0),
|
|
);
|
|
final random = Random();
|
|
Ray2? ray;
|
|
Ray2? reflection;
|
|
Vector2? origin;
|
|
bool isOriginCasted = false;
|
|
Paint rayPaint = Paint();
|
|
final boxPaint = BasicPalette.gray.paint()
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 2.0;
|
|
|
|
final List<Ray2> rays = [];
|
|
final List<RaycastResult<ShapeHitbox>> results = [];
|
|
|
|
late Path path;
|
|
@override
|
|
Future<void> onLoad() async {
|
|
addAll([
|
|
ScreenHitbox(),
|
|
CircleComponent(
|
|
radius: min(canvasSize.x, canvasSize.y) / 2,
|
|
paint: boxPaint,
|
|
children: [CircleHitbox()],
|
|
),
|
|
]);
|
|
}
|
|
|
|
bool isClicked = false;
|
|
final extraChildren = <Component>[];
|
|
|
|
@override
|
|
void onTapDown(_) {
|
|
if (!isClicked) {
|
|
isClicked = true;
|
|
return;
|
|
}
|
|
_timePassed = 0;
|
|
if (extraChildren.isEmpty) {
|
|
addAll(
|
|
extraChildren..addAll(
|
|
[
|
|
CircleComponent(
|
|
position: Vector2(100, 100),
|
|
radius: 50,
|
|
paint: boxPaint,
|
|
children: [CircleHitbox()],
|
|
),
|
|
CircleComponent(
|
|
position: Vector2(150, 500),
|
|
radius: 50,
|
|
paint: boxPaint,
|
|
anchor: Anchor.center,
|
|
children: [CircleHitbox()],
|
|
),
|
|
CircleComponent(
|
|
position: Vector2(150, 500),
|
|
radius: 150,
|
|
paint: boxPaint,
|
|
anchor: Anchor.center,
|
|
children: [CircleHitbox()],
|
|
),
|
|
RectangleComponent(
|
|
position: Vector2.all(300),
|
|
size: Vector2.all(100),
|
|
paint: boxPaint,
|
|
children: [RectangleHitbox()],
|
|
),
|
|
RectangleComponent(
|
|
position: Vector2.all(500),
|
|
size: Vector2(100, 200),
|
|
paint: boxPaint,
|
|
children: [RectangleHitbox()],
|
|
),
|
|
CircleComponent(
|
|
position: Vector2(650, 275),
|
|
radius: 50,
|
|
paint: boxPaint,
|
|
anchor: Anchor.center,
|
|
children: [CircleHitbox()],
|
|
),
|
|
RectangleComponent(
|
|
position: Vector2(550, 200),
|
|
size: Vector2(200, 150),
|
|
paint: boxPaint,
|
|
children: [RectangleHitbox()],
|
|
),
|
|
RectangleComponent(
|
|
position: Vector2(350, 30),
|
|
size: Vector2(200, 150),
|
|
paint: boxPaint,
|
|
angle: tau / 10,
|
|
children: [RectangleHitbox()],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
} else {
|
|
removeAll(extraChildren);
|
|
extraChildren.clear();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onMouseMove(PointerHoverInfo info) {
|
|
final origin = info.eventPosition.widget;
|
|
isOriginCasted = origin == this.origin;
|
|
this.origin = origin;
|
|
}
|
|
|
|
final Ray2 _ray = Ray2.zero();
|
|
var _timePassed = 0.0;
|
|
|
|
@override
|
|
void update(double dt) {
|
|
super.update(dt);
|
|
if (isClicked) {
|
|
_timePassed += dt;
|
|
}
|
|
rayPaint.color = _colorTween.transform(0.5 + (sin(_timePassed) / 2))!;
|
|
if (origin != null) {
|
|
_ray.origin.setFrom(origin!);
|
|
_ray.direction
|
|
..setValues(1, 1)
|
|
..normalize();
|
|
collisionDetection
|
|
.raytrace(
|
|
_ray,
|
|
maxDepth: min((_timePassed * 8).ceil(), 1000),
|
|
out: results,
|
|
)
|
|
.toList();
|
|
isOriginCasted = true;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void render(Canvas canvas) {
|
|
super.render(canvas);
|
|
if (origin != null) {
|
|
renderResult(canvas, origin!, results, rayPaint);
|
|
}
|
|
}
|
|
|
|
void renderResult(
|
|
Canvas canvas,
|
|
Vector2 origin,
|
|
List<RaycastResult<ShapeHitbox>> results,
|
|
Paint paint,
|
|
) {
|
|
var originOffset = origin.toOffset();
|
|
for (final result in results) {
|
|
if (!result.isActive) {
|
|
continue;
|
|
}
|
|
final intersectionPoint = result.intersectionPoint!.toOffset();
|
|
canvas.drawLine(
|
|
originOffset,
|
|
intersectionPoint,
|
|
paint,
|
|
);
|
|
originOffset = intersectionPoint;
|
|
}
|
|
}
|
|
}
|