mirror of
				https://github.com/flame-engine/flame.git
				synced 2025-11-01 01:18:38 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			161 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'dart:math';
 | |
| import 'dart:ui';
 | |
| 
 | |
| import 'package:flutter/painting.dart';
 | |
| 
 | |
| import '../svg.dart';
 | |
| import '../sprite.dart';
 | |
| import '../position.dart';
 | |
| import '../anchor.dart';
 | |
| 
 | |
| /// This represents a Component for your game.
 | |
| ///
 | |
| /// Components can be bullets flying on the screen, a spaship or your player's fighter.
 | |
| /// Anything that either renders or updates can be added to the list on [BaseGame]. It will deal with calling those methods for you.
 | |
| /// Components also have other methods that can help you out if you want to overwrite them.
 | |
| abstract class Component {
 | |
|   /// This method is called periodically by the game engine to request that your component updates itself.
 | |
|   ///
 | |
|   /// The time [t] in seconds (with microseconds precision provided by Flutter) since the last update cycle.
 | |
|   /// This time can vary according to hardware capacity, so make sure to update your state considering this.
 | |
|   /// All components on [BaseGame] are always updated by the same amount. The time each one takes to update adds up to the next update cycle.
 | |
|   void update(double t);
 | |
| 
 | |
|   /// Renders this component on the provided Canvas [c].
 | |
|   void render(Canvas c);
 | |
| 
 | |
|   /// This is a hook called by [BaseGame] to let this component know that the screen (or flame draw area) has been update.
 | |
|   ///
 | |
|   /// It receives the new size.
 | |
|   /// You can use the [Resizable] mixin if you want an implementation of this hook that keeps track of the current size.
 | |
|   void resize(Size size) {}
 | |
| 
 | |
|   /// Whether this component has been loaded yet. If not loaded, [BaseGame] will not try to render it.
 | |
|   ///
 | |
|   /// Sprite based components can use this to let [BaseGame] know not to try to render when the [Sprite] has not been loaded yet.
 | |
|   /// Note that for a more consistent experience, you can pre-load all your assets beforehand with Flame.images.loadAll.
 | |
|   bool loaded() => true;
 | |
| 
 | |
|   /// Wether this should be destroyed or not.
 | |
|   ///
 | |
|   /// It will be called once per component per loop, and if it returns true, [BaseGame] will mark your component for deletion and remove it before the next loop.
 | |
|   bool destroy() => false;
 | |
| 
 | |
|   /// Whether this component is HUD object or not.
 | |
|   ///
 | |
|   /// HUD objects ignore the [BaseGame.camera] when rendered (so their position coordinates are considered relative to the device screen).
 | |
|   bool isHud() => false;
 | |
| 
 | |
|   /// Render priority of this component. This allows you to control the order in which your components are rendered.
 | |
|   ///
 | |
|   /// Components are always updated and rendered in the order defined by this number.
 | |
|   /// The smaller the priority, the sooner your component will be updated/rendered.
 | |
|   /// It can be any integer (negative, zero, or positive).
 | |
|   /// If two components share the same priority, they will probably be drawn in the order they were added.
 | |
|   int priority() => 0;
 | |
| }
 | |
| 
 | |
| /// A [Component] implementation that represents a component that has a specific, possibly dynamic position on the screen.
 | |
| ///
 | |
| /// It represents a rectangle of dimension ([width], [height]), on the position ([x], [y]), rotate around its center with angle [angle].
 | |
| /// It also uses the [anchor] property to properly position itself.
 | |
| abstract class PositionComponent extends Component {
 | |
|   double x = 0.0, y = 0.0, angle = 0.0;
 | |
|   double width = 0.0, height = 0.0;
 | |
|   Anchor anchor = Anchor.topLeft;
 | |
| 
 | |
|   Position toPosition() => Position(x, y);
 | |
|   void setByPosition(Position position) {
 | |
|     this.x = position.x;
 | |
|     this.y = position.y;
 | |
|   }
 | |
| 
 | |
|   Position toSize() => Position(width, height);
 | |
|   void setBySize(Position size) {
 | |
|     this.width = size.x;
 | |
|     this.height = size.y;
 | |
|   }
 | |
| 
 | |
|   Rect toRect() => Rect.fromLTWH(x, y, width, height);
 | |
|   void setByRect(Rect rect) {
 | |
|     this.x = rect.left;
 | |
|     this.y = rect.top;
 | |
|     this.width = rect.width;
 | |
|     this.height = rect.height;
 | |
|   }
 | |
| 
 | |
|   double angleBetween(PositionComponent c) {
 | |
|     return (atan2(c.x - this.x, this.y - c.y) - pi / 2) % (2 * pi);
 | |
|   }
 | |
| 
 | |
|   double distance(PositionComponent c) {
 | |
|     return sqrt(pow(this.y - c.y, 2) + pow(this.x - c.x, 2));
 | |
|   }
 | |
| 
 | |
|   void prepareCanvas(Canvas canvas) {
 | |
|     canvas.translate(x, y);
 | |
| 
 | |
|     canvas.rotate(angle);
 | |
|     double dx = -anchor.relativePosition.dx * width;
 | |
|     double dy = -anchor.relativePosition.dy * height;
 | |
|     canvas.translate(dx, dy);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// A [PositionComponent] that renders a single [Sprite] at the designated position, scaled to have the designated size and rotated to the designated angle.
 | |
| ///
 | |
| /// This is the most commonly used child of [Component].
 | |
| class SpriteComponent extends PositionComponent {
 | |
|   Sprite sprite;
 | |
| 
 | |
|   SpriteComponent();
 | |
| 
 | |
|   SpriteComponent.square(double size, String imagePath)
 | |
|       : this.rectangle(size, size, imagePath);
 | |
| 
 | |
|   SpriteComponent.rectangle(double width, double height, String imagePath)
 | |
|       : this.fromSprite(width, height, Sprite(imagePath));
 | |
| 
 | |
|   SpriteComponent.fromSprite(double width, double height, this.sprite) {
 | |
|     this.width = width;
 | |
|     this.height = height;
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   render(Canvas canvas) {
 | |
|     prepareCanvas(canvas);
 | |
|     sprite.render(canvas, width, height);
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   bool loaded() {
 | |
|     return sprite != null && sprite.loaded() && x != null && y != null;
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   void update(double t) {}
 | |
| }
 | |
| 
 | |
| class SvgComponent extends PositionComponent {
 | |
|   Svg svg;
 | |
| 
 | |
|   SvgComponent.fromSvg(double width, double height, this.svg) {
 | |
|     this.width = width;
 | |
|     this.height = height;
 | |
|   }
 | |
|  
 | |
|   @override
 | |
|   render(Canvas canvas) {
 | |
|     prepareCanvas(canvas);
 | |
|     svg.render(canvas, width, height);
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   bool loaded() {
 | |
|     return svg != null && svg.loaded() && x != null && y != null;
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   void update(double t) {}
 | |
| }
 | 
