mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 10:38:17 +08:00
ShapeComponent and Hitbox to take transform of parents full ancestor tree into consideration (#1076)
* `ShapeComponent` changes size, position and angle of underlying Shape * Added description to ShapeComponent * Fix test * Update packages/flame/lib/src/components/shape_component.dart Co-authored-by: Erick <erickzanardoo@gmail.com> * Add absoluteScale and absoluteAngle to PositionComponent * Refactor ShapeComponent * Should be scaled by total scale, not scaled size * Premature optimization for creation for objects in Polygon * Use path for default Polygon constructor * Do not sync component and hitbox shape * Fix analyze issue * Add example for flipping with collision detection * Don't use absoluteScale * Fix examples * Fix examples * Doesn't need super.render * Fix Circle dartdoc * Update changelog * Update names of vertices caches in Polygon * Update text docs * Revert "Update text docs" This reverts commit 73a68a465d76eb0eb50bb3753e57b2f4e3b5a7f4. * Fix examples * ShapeComponents docs * Move example games to the top * Fix dartdoc comment about polygon vertex relation * Fix order of polygon vertices in dartdoc * Fix anchor for PolygonComponent.fromPoints * Add test with ancestors * Update doc/components.md Co-authored-by: Pasha Stetsenko <stpasha@google.com> * Update doc/components.md Co-authored-by: Erick <erickzanardoo@gmail.com> * Rename example classes * Fix linting issues in examples * Don't use px * Use isTrue and isFalse * Update doc/components.md Co-authored-by: Erick <erickzanardoo@gmail.com> * Fixed comments on PR Co-authored-by: Erick <erickzanardoo@gmail.com> Co-authored-by: Pasha Stetsenko <stpasha@google.com>
This commit is contained in:
@ -9,21 +9,95 @@ import 'package:flame/input.dart';
|
||||
import 'package:flame/palette.dart';
|
||||
import 'package:flutter/material.dart' hide Image, Draggable;
|
||||
|
||||
const multipleShapesInfo = '''
|
||||
An example with many hitboxes that move around on the screen and during
|
||||
collisions they change color depending on what it is that they have collided
|
||||
with.
|
||||
|
||||
The snowman, the component built with three circles on top of each other, works
|
||||
a little bit differently than the other components to show that you can have
|
||||
multiple hitboxes within one component.
|
||||
|
||||
On this example, you can "throw" the components by dragging them quickly in any
|
||||
direction.
|
||||
''';
|
||||
|
||||
enum Shapes { circle, rectangle, polygon }
|
||||
|
||||
class MultipleShapesExample extends FlameGame
|
||||
with HasCollidables, HasDraggableComponents, FPSCounter {
|
||||
static const description = '''
|
||||
An example with many hitboxes that move around on the screen and during
|
||||
collisions they change color depending on what it is that they have collided
|
||||
with.
|
||||
|
||||
The snowman, the component built with three circles on top of each other,
|
||||
works a little bit differently than the other components to show that you
|
||||
can have multiple hitboxes within one component.
|
||||
|
||||
On this example, you can "throw" the components by dragging them quickly in
|
||||
any direction.
|
||||
''';
|
||||
|
||||
final TextPaint fpsTextPaint = TextPaint(
|
||||
config: TextPaintConfig(
|
||||
color: BasicPalette.white.color,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final screenCollidable = ScreenCollidable();
|
||||
final snowman = CollidableSnowman(
|
||||
Vector2.all(150),
|
||||
Vector2(100, 200),
|
||||
Vector2(-100, 100),
|
||||
screenCollidable,
|
||||
);
|
||||
MyCollidable lastToAdd = snowman;
|
||||
add(screenCollidable);
|
||||
add(snowman);
|
||||
var totalAdded = 1;
|
||||
while (totalAdded < 20) {
|
||||
lastToAdd = nextRandomCollidable(lastToAdd, screenCollidable);
|
||||
final lastBottomRight =
|
||||
lastToAdd.toAbsoluteRect().bottomRight.toVector2();
|
||||
if (lastBottomRight.x < size.x && lastBottomRight.y < size.y) {
|
||||
add(lastToAdd);
|
||||
totalAdded++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final _rng = Random();
|
||||
final _distance = Vector2(100, 0);
|
||||
|
||||
MyCollidable nextRandomCollidable(
|
||||
MyCollidable lastCollidable,
|
||||
ScreenCollidable screenCollidable,
|
||||
) {
|
||||
final collidableSize = Vector2.all(50) + Vector2.random(_rng) * 100;
|
||||
final isXOverflow = lastCollidable.position.x +
|
||||
lastCollidable.size.x / 2 +
|
||||
_distance.x +
|
||||
collidableSize.x >
|
||||
size.x;
|
||||
var position = _distance + Vector2(0, lastCollidable.position.y + 200);
|
||||
if (!isXOverflow) {
|
||||
position = (lastCollidable.position + _distance)
|
||||
..x += collidableSize.x / 2;
|
||||
}
|
||||
final velocity = (Vector2.random(_rng) - Vector2.random(_rng)) * 400;
|
||||
return randomCollidable(
|
||||
position,
|
||||
collidableSize,
|
||||
velocity,
|
||||
screenCollidable,
|
||||
rng: _rng,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
super.render(canvas);
|
||||
fpsTextPaint.render(
|
||||
canvas,
|
||||
'${fps(120).toStringAsFixed(2)}fps',
|
||||
Vector2(0, size.y - 24),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MyCollidable extends PositionComponent
|
||||
with Draggable, HasHitboxes, Collidable {
|
||||
double rotationSpeed = 0.0;
|
||||
@ -41,11 +115,7 @@ abstract class MyCollidable extends PositionComponent
|
||||
Vector2 size,
|
||||
this.velocity,
|
||||
this.screenCollidable,
|
||||
) {
|
||||
this.position = position;
|
||||
this.size = size;
|
||||
anchor = Anchor.center;
|
||||
}
|
||||
) : super(position: position, size: size, anchor: Anchor.center);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
@ -137,7 +207,7 @@ class CollidablePolygon extends MyCollidable {
|
||||
Vector2 velocity,
|
||||
ScreenCollidable screenCollidable,
|
||||
) : super(position, size, velocity, screenCollidable) {
|
||||
final shape = HitboxPolygon([
|
||||
final hitbox = HitboxPolygon([
|
||||
Vector2(-1.0, 0.0),
|
||||
Vector2(-0.8, 0.6),
|
||||
Vector2(0.0, 1.0),
|
||||
@ -147,7 +217,7 @@ class CollidablePolygon extends MyCollidable {
|
||||
Vector2(0, -1.0),
|
||||
Vector2(-0.8, -0.8),
|
||||
]);
|
||||
addHitbox(shape);
|
||||
addHitbox(hitbox);
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,8 +239,7 @@ class CollidableCircle extends MyCollidable {
|
||||
Vector2 velocity,
|
||||
ScreenCollidable screenCollidable,
|
||||
) : super(position, size, velocity, screenCollidable) {
|
||||
final shape = HitboxCircle();
|
||||
addHitbox(shape);
|
||||
addHitbox(HitboxCircle());
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +248,7 @@ class SnowmanPart extends HitboxCircle {
|
||||
final hitPaint = Paint();
|
||||
|
||||
SnowmanPart(double definition, Vector2 relativeOffset, Color hitColor)
|
||||
: super(definition: definition) {
|
||||
: super(normalizedRadius: definition) {
|
||||
this.relativeOffset.setFrom(relativeOffset);
|
||||
hitPaint..color = startColor;
|
||||
onCollision = (Set<Vector2> intersectionPoints, HitboxShape other) {
|
||||
@ -245,77 +314,3 @@ MyCollidable randomCollidable(
|
||||
..rotationSpeed = rotationSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
class MultipleShapes extends FlameGame
|
||||
with HasCollidables, HasDraggableComponents, FPSCounter {
|
||||
final TextPaint fpsTextPaint = TextPaint(
|
||||
config: TextPaintConfig(
|
||||
color: BasicPalette.white.color,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final screenCollidable = ScreenCollidable();
|
||||
final snowman = CollidableSnowman(
|
||||
Vector2.all(150),
|
||||
Vector2(100, 200),
|
||||
Vector2(-100, 100),
|
||||
screenCollidable,
|
||||
);
|
||||
MyCollidable lastToAdd = snowman;
|
||||
add(screenCollidable);
|
||||
add(snowman);
|
||||
var totalAdded = 1;
|
||||
while (totalAdded < 20) {
|
||||
lastToAdd = nextRandomCollidable(lastToAdd, screenCollidable);
|
||||
final lastBottomRight =
|
||||
lastToAdd.toAbsoluteRect().bottomRight.toVector2();
|
||||
if (lastBottomRight.x < size.x && lastBottomRight.y < size.y) {
|
||||
add(lastToAdd);
|
||||
totalAdded++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final _rng = Random();
|
||||
final _distance = Vector2(100, 0);
|
||||
|
||||
MyCollidable nextRandomCollidable(
|
||||
MyCollidable lastCollidable,
|
||||
ScreenCollidable screenCollidable,
|
||||
) {
|
||||
final collidableSize = Vector2.all(50) + Vector2.random(_rng) * 100;
|
||||
final isXOverflow = lastCollidable.position.x +
|
||||
lastCollidable.size.x / 2 +
|
||||
_distance.x +
|
||||
collidableSize.x >
|
||||
size.x;
|
||||
var position = _distance + Vector2(0, lastCollidable.position.y + 200);
|
||||
if (!isXOverflow) {
|
||||
position = (lastCollidable.position + _distance)
|
||||
..x += collidableSize.x / 2;
|
||||
}
|
||||
final velocity = (Vector2.random(_rng) - Vector2.random(_rng)) * 400;
|
||||
return randomCollidable(
|
||||
position,
|
||||
collidableSize,
|
||||
velocity,
|
||||
screenCollidable,
|
||||
rng: _rng,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
super.render(canvas);
|
||||
fpsTextPaint.render(
|
||||
canvas,
|
||||
'${fps(120).toStringAsFixed(2)}fps',
|
||||
Vector2(0, size.y - 24),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user