Files
flame/lib/particle.dart
2020-10-22 20:15:08 +02:00

180 lines
5.3 KiB
Dart

import 'dart:math';
import 'dart:ui';
import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart';
import 'components/component.dart';
import 'components/particle_component.dart';
import 'particles/accelerated_particle.dart';
import 'particles/composed_particle.dart';
import 'particles/moving_particle.dart';
import 'particles/rotating_particle.dart';
import 'particles/scaled_particle.dart';
import 'particles/translated_particle.dart';
import 'timer.dart';
/// A function which returns [Particle] when called
typedef ParticleGenerator = Particle Function(int);
/// Base class implementing common behavior for all the particles.
///
/// Intention is to follow same "Extreme Composability" style
/// as across the whole Flutter framework, so each type of particle implements
/// some particular behavior which then could be nested and combined together
/// to create specifically required experience.
abstract class Particle {
/// Generates given amount of particles,
/// combining them into one [ComposedParticle]
/// Useful for procedural particle generation.
static Particle generate({
int count = 10,
@required ParticleGenerator generator,
double lifespan,
}) {
return ComposedParticle(
lifespan: lifespan,
children: List<Particle>.generate(count, generator),
);
}
/// Internal timer defining how long
/// this [Particle] will live. [Particle] will
/// be marked for destroy when this timer is over.
Timer _timer;
/// Stores desired lifespan of the
/// particle in seconds.
double _lifespan;
/// Will be set to true by update hook
/// when this [Particle] reaches end of its lifespan
bool _shouldBeDestroyed = false;
Particle({
/// Particle lifespan in [Timer] format,
/// double in seconds with microsecond precision
double lifespan,
}) {
setLifespan(lifespan ?? .5);
}
/// This method will return true as
/// soon as particle reaches an end of its
/// lifespan, which means it's ready to be
/// destroyed by a wrapping container.
/// Follows same style as [Component].
bool destroy() => _shouldBeDestroyed;
/// Getter which should be used by subclasses
/// to get overall progress. Also allows to substitute
/// progres with other values, for example adding easing as in [CurvedParticle].
double get progress => _timer.progress;
/// Should render this [Particle] to given [Canvas].
/// Default behavior is empty, so that it's not
/// required to override this in [Particle] which
/// render nothing and serve as behavior containers.
void render(Canvas canvas) {
// Do nothing by default
}
/// Updates internal [Timer] of this [Particle]
/// which defines its position on the lifespan.
/// Marks [Particle] for destroy when it is over.
void update(double dt) {
_timer.update(dt);
}
/// A control method allowing a parent of this [Particle]
/// to pass down it's lifespan. Allows to only specify desired lifespan
/// once, at the very top of the [Particle] tree which
/// then will be propagated down using this method.
/// See [SingleChildParticle] or [ComposedParticle] for details.
void setLifespan(double lifespan) {
_lifespan = lifespan;
_timer?.stop();
final void Function() destroyCallback = () => _shouldBeDestroyed = true;
_timer = Timer(lifespan, callback: destroyCallback);
_timer.start();
}
/// Wtaps this particle with [TranslatedParticle]
/// statically repositioning it for the time
/// of the lifespan.
Particle translated(Offset offset) {
return TranslatedParticle(
offset: offset,
child: this,
lifespan: _lifespan,
);
}
/// Wraps this particle with [MovingParticle]
/// allowing it to move from one [Offset]
/// on the canvas to another one.
Particle moving({
Offset from = Offset.zero,
@required Offset to,
Curve curve = Curves.linear,
}) {
return MovingParticle(
from: from,
to: to,
curve: curve,
child: this,
lifespan: _lifespan,
);
}
/// Wraps this particle with [AcceleratedParticle]
/// allowing to specify desired position speed and acceleration
/// and leave the basic physics do the rest.
Particle accelerated({
Offset acceleration = Offset.zero,
Offset position = Offset.zero,
Offset speed = Offset.zero,
}) {
return AcceleratedParticle(
position: position,
speed: speed,
acceleration: acceleration,
child: this,
lifespan: _lifespan,
);
}
/// Rotates this particle to a fixed angle
/// in radians with [RotatingParticle]
Particle rotated([double angle = 0]) {
return RotatingParticle(
child: this, lifespan: _lifespan, from: angle, to: angle);
}
/// Rotates this particle from given angle
/// to another one in radians with [RotatingParticle]
Particle rotating({
double from = 0,
double to = pi,
}) {
return RotatingParticle(
child: this,
lifespan: _lifespan,
from: from,
to: to,
);
}
/// Wraps this particle with [ScaledParticle]
/// allowing to change size of it and/or its children
Particle scaled(double scale) {
return ScaledParticle(scale: scale, child: this, lifespan: _lifespan);
}
/// Wraps this particle with [ParticleComponent]
/// to be used within the [BaseGame] component system.
Component asComponent() {
return ParticleComponent(particle: this);
}
}