game/lib/widgets/battle/explosion_widget.dart

142 lines
3.3 KiB
Dart

import 'dart:math';
import 'package:flutter/material.dart';
class Particle {
Offset position;
Offset velocity;
Color color;
double size;
double life; // 1.0 to 0.0
double decay;
Particle({
required this.position,
required this.velocity,
required this.color,
required this.size,
required this.life,
required this.decay,
});
}
class ExplosionWidget extends StatefulWidget {
const ExplosionWidget({super.key});
@override
ExplosionWidgetState createState() => ExplosionWidgetState();
}
class ExplosionWidgetState extends State<ExplosionWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
final List<Particle> _particles = [];
final Random _random = Random();
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1000),
);
_controller.addListener(_updateParticles);
}
@override
void dispose() {
_controller.removeListener(_updateParticles);
_controller.dispose();
super.dispose();
}
void _updateParticles() {
if (_particles.isEmpty) return;
for (var i = _particles.length - 1; i >= 0; i--) {
final p = _particles[i];
p.position += p.velocity;
p.velocity += Offset(0, 0.5); // Gravity
p.life -= p.decay;
if (p.life <= 0) {
_particles.removeAt(i);
}
}
if (_particles.isEmpty) {
_controller.stop();
}
setState(() {});
}
void explode(Offset position) {
// Clear old particles if any (optional, or just add more)
// _particles.clear();
// Create new particles
for (int i = 0; i < 30; i++) {
final double angle = _random.nextDouble() * 2 * pi;
final double speed = _random.nextDouble() * 5 + 2;
final double dx = cos(angle) * speed;
final double dy = sin(angle) * speed;
// Random colors for fire/explosion effect
Color color;
final r = _random.nextDouble();
if (r < 0.33) {
color = Colors.redAccent;
} else if (r < 0.66) {
color = Colors.orangeAccent;
} else {
color = Colors.yellowAccent;
}
_particles.add(
Particle(
position: position,
velocity: Offset(dx, dy),
color: color,
size: _random.nextDouble() * 4 + 2,
life: 1.0,
decay: _random.nextDouble() * 0.02 + 0.01,
),
);
}
if (!_controller.isAnimating) {
_controller.repeat(); // Use repeat to keep loop running until empty
}
}
@override
Widget build(BuildContext context) {
return IgnorePointer(
child: CustomPaint(
painter: ExplosionPainter(_particles),
size: Size.infinite,
),
);
}
}
class ExplosionPainter extends CustomPainter {
final List<Particle> particles;
ExplosionPainter(this.particles);
@override
void paint(Canvas canvas, Size size) {
for (final p in particles) {
final paint = Paint()
..color = p.color.withOpacity(p.life.clamp(0.0, 1.0))
..style = PaintingStyle.fill;
canvas.drawCircle(p.position, p.size, paint);
}
}
@override
bool shouldRepaint(covariant ExplosionPainter oldDelegate) {
return true; // Always repaint when animating
}
}