mirror of
https://github.com/flame-engine/flame.git
synced 2025-10-29 16:05:47 +08:00
feat!: Raycasting and raytracing (#1785)
This PR implements raytracing and raycasting for the built-in hitboxes. If you pass in your own collision detection system to the HasCollisionDetection mixin you have to change the signature of that to: CollisionDetection<ShapeHitbox>instead of CollisionDetection<Hitbox>.
This commit is contained in:
137
examples/lib/stories/collision_detection/raycast_example.dart
Normal file
137
examples/lib/stories/collision_detection/raycast_example.dart
Normal file
@ -0,0 +1,137 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flame/collisions.dart';
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame/geometry.dart';
|
||||
import 'package:flame/palette.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RaycastExample extends FlameGame with HasCollisionDetection {
|
||||
static const description = '''
|
||||
In this example the raycast functionality is showcased. The circle moves around
|
||||
and casts 10 rays and checks how far the nearest hitboxes are and naively moves
|
||||
around trying not to hit them.
|
||||
''';
|
||||
|
||||
Ray2? ray;
|
||||
Ray2? reflection;
|
||||
Vector2 origin = Vector2(250, 100);
|
||||
Paint paint = Paint()..color = Colors.amber.withOpacity(0.6);
|
||||
final speed = 100;
|
||||
final inertia = 3.0;
|
||||
final safetyDistance = 50;
|
||||
final direction = Vector2(0, 1);
|
||||
final velocity = Vector2.zero();
|
||||
final random = Random();
|
||||
|
||||
static const numberOfRays = 10;
|
||||
final List<Ray2> rays = [];
|
||||
final List<RaycastResult<ShapeHitbox>> results = [];
|
||||
|
||||
late Path path;
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
final paint = BasicPalette.gray.paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2.0;
|
||||
add(ScreenHitbox());
|
||||
add(
|
||||
CircleComponent(
|
||||
position: Vector2(100, 100),
|
||||
radius: 50,
|
||||
paint: paint,
|
||||
children: [CircleHitbox()],
|
||||
),
|
||||
);
|
||||
add(
|
||||
CircleComponent(
|
||||
position: Vector2(150, 500),
|
||||
radius: 50,
|
||||
paint: paint,
|
||||
children: [CircleHitbox()],
|
||||
),
|
||||
);
|
||||
add(
|
||||
RectangleComponent(
|
||||
position: Vector2.all(300),
|
||||
size: Vector2.all(100),
|
||||
paint: paint,
|
||||
children: [RectangleHitbox()],
|
||||
),
|
||||
);
|
||||
add(
|
||||
RectangleComponent(
|
||||
position: Vector2.all(500),
|
||||
size: Vector2(100, 200),
|
||||
paint: paint,
|
||||
children: [RectangleHitbox()],
|
||||
),
|
||||
);
|
||||
add(
|
||||
RectangleComponent(
|
||||
position: Vector2(550, 200),
|
||||
size: Vector2(200, 150),
|
||||
paint: paint,
|
||||
children: [RectangleHitbox()],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final _velocityModifier = Vector2.zero();
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
collisionDetection.raycastAll(
|
||||
origin,
|
||||
numberOfRays: numberOfRays,
|
||||
rays: rays,
|
||||
out: results,
|
||||
);
|
||||
velocity.scale(inertia);
|
||||
for (final result in results) {
|
||||
_velocityModifier
|
||||
..setFrom(result.intersectionPoint!)
|
||||
..sub(origin)
|
||||
..normalize();
|
||||
if (result.distance! < safetyDistance) {
|
||||
_velocityModifier.negate();
|
||||
} else if (random.nextDouble() < 0.2) {
|
||||
velocity.add(_velocityModifier);
|
||||
}
|
||||
velocity.add(_velocityModifier);
|
||||
}
|
||||
velocity
|
||||
..normalize()
|
||||
..scale(speed * dt);
|
||||
origin.add(velocity);
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
super.render(canvas);
|
||||
renderResult(canvas, origin, results, paint);
|
||||
}
|
||||
|
||||
void renderResult(
|
||||
Canvas canvas,
|
||||
Vector2 origin,
|
||||
List<RaycastResult<ShapeHitbox>> results,
|
||||
Paint paint,
|
||||
) {
|
||||
final originOffset = origin.toOffset();
|
||||
for (final result in results) {
|
||||
if (!result.isActive) {
|
||||
continue;
|
||||
}
|
||||
final intersectionPoint = result.intersectionPoint!.toOffset();
|
||||
canvas.drawLine(
|
||||
originOffset,
|
||||
intersectionPoint,
|
||||
paint,
|
||||
);
|
||||
}
|
||||
canvas.drawCircle(originOffset, 5, paint);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user