mirror of
https://github.com/flame-engine/flame.git
synced 2025-10-30 08:27:36 +08:00
Unify examples structure (#1118)
* Animations, CameraAndViewport, CollisionDetection and Components unified * Added descriptions to effects * Rename input games * Unify input stories * Add info to parallax section * Added descriptions to the rendering examples * Add descriptions to the sprites directory * Fix utils and rendering section * Add descriptions to the widgets section * Delete directory that rebase brought back * Unify game names * Added some styleguide docs for examples * Fix analyze issues * All files should have _example as suffix * Made the FollowComponentExample a bit easier to understand * Change priority of ember
This commit is contained in:
@ -0,0 +1,312 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/extensions.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame/geometry.dart';
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flame/palette.dart';
|
||||
import 'package:flutter/material.dart' hide Image, Draggable;
|
||||
|
||||
enum Shapes { circle, rectangle, polygon }
|
||||
|
||||
class MultipleShapesExample extends FlameGame
|
||||
with HasCollidables, HasDraggables, 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();
|
||||
|
||||
@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;
|
||||
final Vector2 velocity;
|
||||
final delta = Vector2.zero();
|
||||
double angleDelta = 0;
|
||||
bool _isDragged = false;
|
||||
late final Paint _activePaint;
|
||||
final Color _defaultColor = Colors.blue.withOpacity(0.8);
|
||||
final Set<Collidable> _activeCollisions = {};
|
||||
final ScreenCollidable screenCollidable;
|
||||
|
||||
MyCollidable(
|
||||
Vector2 position,
|
||||
Vector2 size,
|
||||
this.velocity,
|
||||
this.screenCollidable,
|
||||
) : super(position: position, size: size, anchor: Anchor.center);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
_activePaint = Paint()..color = _defaultColor;
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
if (_isDragged) {
|
||||
return;
|
||||
}
|
||||
delta.setFrom(velocity * dt);
|
||||
position.add(delta);
|
||||
angleDelta = dt * rotationSpeed;
|
||||
angle = (angle + angleDelta) % (2 * pi);
|
||||
// Takes rotation into consideration (which topLeftPosition doesn't)
|
||||
final topLeft = absoluteCenter - (scaledSize / 2);
|
||||
if (topLeft.x + scaledSize.x < 0 ||
|
||||
topLeft.y + scaledSize.y < 0 ||
|
||||
topLeft.x > screenCollidable.scaledSize.x ||
|
||||
topLeft.y > screenCollidable.scaledSize.y) {
|
||||
final moduloSize = screenCollidable.scaledSize + scaledSize;
|
||||
topLeftPosition = topLeftPosition % moduloSize;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
renderHitboxes(canvas, paint: _activePaint);
|
||||
if (_isDragged) {
|
||||
final localCenter = (scaledSize / 2).toOffset();
|
||||
canvas.drawCircle(localCenter, 5, _activePaint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onCollision(Set<Vector2> intersectionPoints, Collidable other) {
|
||||
final isNew = _activeCollisions.add(other);
|
||||
if (isNew) {
|
||||
_activePaint.color = collisionColor(other).withOpacity(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onCollisionEnd(Collidable other) {
|
||||
_activeCollisions.remove(other);
|
||||
if (_activeCollisions.isEmpty) {
|
||||
_activePaint.color = _defaultColor;
|
||||
}
|
||||
}
|
||||
|
||||
Color collisionColor(Collidable other) {
|
||||
switch (other.runtimeType) {
|
||||
case ScreenCollidable:
|
||||
return Colors.teal;
|
||||
case CollidablePolygon:
|
||||
return Colors.deepOrange;
|
||||
case CollidableCircle:
|
||||
return Colors.green;
|
||||
case CollidableRectangle:
|
||||
return Colors.cyan;
|
||||
case CollidableSnowman:
|
||||
return Colors.amber;
|
||||
default:
|
||||
return Colors.pink;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool onDragUpdate(int pointerId, _) {
|
||||
_isDragged = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool onDragEnd(int pointerId, DragEndInfo info) {
|
||||
velocity.setFrom(info.velocity / 10);
|
||||
_isDragged = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class CollidablePolygon extends MyCollidable {
|
||||
CollidablePolygon(
|
||||
Vector2 position,
|
||||
Vector2 size,
|
||||
Vector2 velocity,
|
||||
ScreenCollidable screenCollidable,
|
||||
) : super(position, size, velocity, screenCollidable) {
|
||||
final hitbox = HitboxPolygon([
|
||||
Vector2(-1.0, 0.0),
|
||||
Vector2(-0.8, 0.6),
|
||||
Vector2(0.0, 1.0),
|
||||
Vector2(0.6, 0.9),
|
||||
Vector2(1.0, 0.0),
|
||||
Vector2(0.6, -0.8),
|
||||
Vector2(0, -1.0),
|
||||
Vector2(-0.8, -0.8),
|
||||
]);
|
||||
addHitbox(hitbox);
|
||||
}
|
||||
}
|
||||
|
||||
class CollidableRectangle extends MyCollidable {
|
||||
CollidableRectangle(
|
||||
Vector2 position,
|
||||
Vector2 size,
|
||||
Vector2 velocity,
|
||||
ScreenCollidable screenCollidable,
|
||||
) : super(position, size, velocity, screenCollidable) {
|
||||
addHitbox(HitboxRectangle());
|
||||
}
|
||||
}
|
||||
|
||||
class CollidableCircle extends MyCollidable {
|
||||
CollidableCircle(
|
||||
Vector2 position,
|
||||
Vector2 size,
|
||||
Vector2 velocity,
|
||||
ScreenCollidable screenCollidable,
|
||||
) : super(position, size, velocity, screenCollidable) {
|
||||
addHitbox(HitboxCircle());
|
||||
}
|
||||
}
|
||||
|
||||
class SnowmanPart extends HitboxCircle {
|
||||
final startColor = Colors.blue.withOpacity(0.8);
|
||||
final hitPaint = Paint();
|
||||
|
||||
SnowmanPart(double definition, Vector2 relativeOffset, Color hitColor)
|
||||
: super(normalizedRadius: definition) {
|
||||
this.relativeOffset.setFrom(relativeOffset);
|
||||
hitPaint.color = startColor;
|
||||
onCollision = (Set<Vector2> intersectionPoints, HitboxShape other) {
|
||||
if (other.component is ScreenCollidable) {
|
||||
hitPaint.color = startColor;
|
||||
} else {
|
||||
hitPaint.color = hitColor.withOpacity(0.8);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas, _) {
|
||||
super.render(canvas, hitPaint);
|
||||
}
|
||||
}
|
||||
|
||||
class CollidableSnowman extends MyCollidable {
|
||||
CollidableSnowman(
|
||||
Vector2 position,
|
||||
Vector2 size,
|
||||
Vector2 velocity,
|
||||
ScreenCollidable screenCollidable,
|
||||
) : super(position, size, velocity, screenCollidable) {
|
||||
rotationSpeed = 0.3;
|
||||
anchor = Anchor.topLeft;
|
||||
final top = SnowmanPart(0.4, Vector2(0, -0.8), Colors.red);
|
||||
final middle = SnowmanPart(0.6, Vector2(0, -0.3), Colors.yellow);
|
||||
final bottom = SnowmanPart(1.0, Vector2(0, 0.5), Colors.green);
|
||||
addHitbox(top);
|
||||
addHitbox(middle);
|
||||
addHitbox(bottom);
|
||||
add(
|
||||
randomCollidable(
|
||||
Vector2(size.x / 2, size.y * 0.75),
|
||||
size / 4,
|
||||
Vector2.zero(),
|
||||
screenCollidable,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MyCollidable randomCollidable(
|
||||
Vector2 position,
|
||||
Vector2 size,
|
||||
Vector2 velocity,
|
||||
ScreenCollidable screenCollidable, {
|
||||
Random? rng,
|
||||
}) {
|
||||
final _rng = rng ?? Random();
|
||||
final rotationSpeed = 0.5 - _rng.nextDouble();
|
||||
final shapeType = Shapes.values[_rng.nextInt(Shapes.values.length)];
|
||||
switch (shapeType) {
|
||||
case Shapes.circle:
|
||||
return CollidableCircle(position, size, velocity, screenCollidable)
|
||||
..rotationSpeed = rotationSpeed;
|
||||
case Shapes.rectangle:
|
||||
return CollidableRectangle(position, size, velocity, screenCollidable)
|
||||
..rotationSpeed = rotationSpeed;
|
||||
case Shapes.polygon:
|
||||
return CollidablePolygon(position, size, velocity, screenCollidable)
|
||||
..rotationSpeed = rotationSpeed;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user