bonfire 0.1.5 bonfire: ^0.1.5 copied to clipboard
(RPG maker) Create RPG-style or similar games more simply with Flame.
EN | PT
Bonfire #
Build RPG games and similar with the power of FlameEngine!
Find the complete code of this example here.
Summary #
How it works? #
This tool was built over FlameEngine and all its resources and classes are available to be used along with Bonfire. With that said, it is recommended to give a look into FlameEngine before start rocking with Bonfire.
To run a game with Bonfire, use the following widget:
@override
Widget build(BuildContext context) {
return BonfireWidget(
joystick: MyJoystick(), // required
map: DungeonMap.map(), // required
player: Knight(), // If player is omitted, the joystick directional will control the map view, being very useful in the process of building maps
interface: KnightInterface(),
decorations: DungeonMap.decorations(),
enemies: DungeonMap.enemies(),
background: BackgroundColorGame(Colors.blueGrey[900]),
constructionMode: false, // If true, activates hot reload to ease the map constructions and draws the grid
showCollisionArea: false, // If true, show collision area of the elements
gameController: GameController() // If you want to hear changes to the game to do something.
);
}
Components description and organization:
Map #
Represents a map (or world) where the game occurs
It is a matrix of small tiles that toghether assembles the map (see). Right now the matrix is created manually, but in the future it will be possible to load maps created with Tiled
There is a component for this:
MapWorld(List<Tile>())
MapWorld receives a list of tiles that will assemble our map. The whole camera movimentation during Player actons are included on it.
Tile(
'tile/wall_left.png', // Tile image
Position(positionX, positionY), // Map coordinates of this tile
collision: true, // Define if this tile will be not transpassable by players and enemies (ideal for walls and obstacles)
size: 16 // Tile size (width and height)
)
Decorations #
Anything that you may add to the scenery. For example a Barrel in the way or even a NPC in which you can use to interact with your player.
To create a decoration:
GameDecoration(
spriteImg: 'itens/table.png', // Image to be rendered
initPosition: getRelativeTilePosition(10, 6), // World coordinates in which this decoration will be positioned
width: 32,
height: 32,
withCollision: true, // Adds a default collision area
collision: Collision( // A custom collision area
width: 18,
height: 32,
),
// isTouchable: false, // if you want this component to receive touch interaction. You will be notified at 'void onTap()'
// animation: FlameAnimation(), // Optional param to create an animated decoration. When using this, do not specify spriteImg.
// frontFromPlayer: false // Define true if this decoration shall be rendered above the Player
)
You can also create your own decoration class by extending GameDecoration
and implement update
and render
methods with your own behavior. As this example: A treasure chest that opens when a player gets close, removes itself from the game and puts two life potions in its place (being the life portions a GameDecoration
as well).
In this component (like all others), you have access to BuildContext
of the game widget. Therefore, is possible to opebn dialogis, show overlays and other Flutter components that may depend on that.
Enemy #
Represents enemies characters in the game. Instances of this class has actions and movements ready to be used and configured whenever you want. At the same time, you can customize all actions and movements in the way that fits your needs.
To create an enemy you shall create an Enemy
subclass to represent it. Like in this example.
The constructor looks like:
Goblin() : super(
animationIdleRight: FlameAnimation(), //required
animationIdleLeft: FlameAnimation(), // required
animationIdleTop: FlameAnimation(),
animationIdleBottom: FlameAnimation(),
animationRunRight: FlameAnimation(), //required
animationRunLeft: FlameAnimation(), //required
animationRunTop: FlameAnimation(),
animationRunBottom: FlameAnimation(),
initDirection: Direction.right,
initPosition: Position(x,y),
width: 25,
height: 25,
speed: 1.5,
life: 100,
collision: Collision(), // A custom collision area
);
After these steps, the enemy is ready, but it will stay still. To add movements and behaviors, you shall implement them on the update
method.
There is already some pre included actions that you can use (as seen on this example), they are:
//basic movements
void moveBottom({double moveSpeed})
void moveTop({double moveSpeed})
void moveLeft({double moveSpeed})
void moveRight({double moveSpeed})
// Will observe the player when within the radius (visionCells)
void seePlayer(
{
Function(Player) observed,
Function() notObserved,
int visionCells = 3,
}
)
// Will move in the direction of the player once it gets close within the visibleCells radius . Once it gets to the player, `closePlayer` shall be fired
void seeAndMoveToPlayer(
{
Function(Player) closePlayer,
int visionCells = 3
}
)
// Executes a physical attack to the player, making the configured damage with the configured frequency. You can add animations to represent this attack.
void simpleAttackMelee(
{
@required double damage,
@required double heightArea,
@required double widthArea,
int interval = 1000,
FlameAnimation.Animation attackEffectRightAnim,
FlameAnimation.Animation attackEffectBottomAnim,
FlameAnimation.Animation attackEffectLeftAnim,
FlameAnimation.Animation attackEffectTopAnim,
}
)
// Executes a distance attack. Will add a `FlyingAttackObject` to the game and will be send in the configures direction and will make some damage to whomever it hits, or be destroyed as it hits barriers (collision defined tiles).
void simpleAttackRange(
{
@required FlameAnimation.Animation animationRight,
@required FlameAnimation.Animation animationLeft,
@required FlameAnimation.Animation animationTop,
@required FlameAnimation.Animation animationBottom,
@required FlameAnimation.Animation animationDestroy,
@required double width,
@required double height,
double speed = 1.5,
double damage = 1,
Direction direction,
int interval = 1000,
}
)
// Will seek for the player in the defined radius. When the player is found, will position itself to perform a distance attack. Once it reaches the attack position, will fire the `positioned` callback.
void seeAndMoveToAttackRange(
{
Function(Player) positioned,
int visionCells = 5
}
)
// Exibe valor do dano no game com uma animação.
void showDamage(
double damage,
{
TextConfig config = const TextConfig(
fontSize: 10,
color: Colors.white,
)
}
)
// Add to `render` method if you want to draw the collision area.
void drawPositionCollision(Canvas canvas)
// Gives the direction of the player in relation to this enemy
Direction directionThatPlayerIs()
// Executes an animation once.
void addFastAnimation(FlameAnimation.Animation animation)
// Applies damage to the enemy
void receiveDamage(double damage)
// Restore life point to the enemy
void addLife(double life)
// Add to 'render' if you want to draw the collision area
void drawPositionCollision(Canvas canvas)
// Draws the default life bar, Should be used in the `render` method.
void drawDefaultLifeBar(
Canvas canvas,
{
bool drawInBottom = false,
double padding = 5,
double strokeWidth = 2,
}
)
Player #
Represents the character controlled by the user in the game. Instances of this class has actions and movements ready to be used and configured.
To create an enemy you shall create an Player
subclass to represent it. Like in this example.
The constructor looks like:
Knight() : super(
animIdleLeft: FlameAnimation(), // required
animIdleRight: FlameAnimation(), //required
animIdleTop: FlameAnimation(),
animIdleBottom: FlameAnimation(),
animRunRight: FlameAnimation(), //required
animRunLeft: FlameAnimation(), //required
animRunTop: FlameAnimation(),
animRunBottom: FlameAnimation(),
width: 32,
height: 32,
initPosition: Position(x,y), //required
initDirection: Direction.right,
life: 200,
speed: 2.5,
collision: Collision(), // A custom collision area
);
Player instances can receive action configured on the Joystick (read more about it below) by overriding the following method:
@override
void joystickAction(int action) {}
Actions can be fired when a jopystck action is received. Just like Enemy
, here we have some pre-included actions:
// Executes a physical attack to the player, making the configured damage with the configured frequency. You can add animations to represent this attack.
void simpleAttackMelee(
{
@required FlameAnimation.Animation attackEffectRightAnim,
@required FlameAnimation.Animation attackEffectBottomAnim,
@required FlameAnimation.Animation attackEffectLeftAnim,
@required FlameAnimation.Animation attackEffectTopAnim,
@required double damage,
double heightArea = 32,
double widthArea = 32,
}
)
// Executes a distance attack. Will add a `FlyingAttackObject` to the game and will be send in the configures direction and will make some damage to whomever it hits, or be destroyed as it hits barriers (collision defined tiles).
void simpleAttackRange(
{
@required FlameAnimation.Animation animationRight,
@required FlameAnimation.Animation animationLeft,
@required FlameAnimation.Animation animationTop,
@required FlameAnimation.Animation animationBottom,
@required FlameAnimation.Animation animationDestroy,
@required double width,
@required double height,
double speed = 1.5,
double damage = 1,
}
)
// Shows the damage value as an animation on the game.
void showDamage(
double damage,
{
TextConfig config = const TextConfig(
fontSize: 10,
color: Colors.white,
)
}
)
// Will observe enemies when within the radius (visionCells)
void seeEnemy(
{
Function(List<Enemy>) observed,
Function() notObserved,
int visionCells = 3,
}
)
// Add to `render` method if you want to draw the collision area.
void drawPositionCollision(Canvas canvas)
// Executes an animation once.
void addFastAnimation(FlameAnimation.Animation animation)
// Applies damage to the enemy
void receiveDamage(double damage)
// Restore life point to the enemy
void addLife(double life)
Interface #
The way you cand raw things like life bars, stamina and settings. In another words, anything that you may add to the interface to the game.
Interfaces implementations shall be implemented on GameInterface
subclasses, like this exemplo.
Interfaces are drawn by overriding update
and render
methods. You can draw directly on canvas or use FlameEngine components.
Joystick #
The player-controlling component.
There is a pre-included implementation (Joystick
) ready to use, but also configurable to add a custom looking or even add as many actions as you will.
Or you can implement JoystickController
yourself and emit event trough a JoystickListener
.
Joystick is configurable by the following parameters:
Joystick(
pathSpriteBackgroundDirectional: 'joystick_background.png', //(required) directinal control background
pathSpriteKnobDirectional: 'joystick_knob.png', //(required) directional indicator circle background
sizeDirectional: 100, // directional control size
marginBottomDirectional: 100,
marginLeftDirectional: 100,
actions: [ // List of actions that will be placed the screen.
JoystickAction(
actionId: 0, //(required) Action identifier, will be sent to 'void joystickAction(int action) {}' when pressed
pathSprite: 'joystick_atack.png', //(required) the action image
pathSpritePressed : 'joystick_atack.png', // Optional image to be shown when the action is fired
size: 80,
margin: EdgeInsets.only(bottom: 50, right: 50),
align = JoystickActionAlign.BOTTOM_RIGHT,
),
JoystickAction(
actionId: 1,
pathSprite: 'joystick_atack_range.png',
size: 50,
margin: EdgeInsets.only(bottom: 50, right: 160),
align = JoystickActionAlign.BOTTOM_RIGHT,
)
],
)
n Check a example.
Observations: #
Since all of these elements uses the ´HasGameRef´ mixin, it is possible to acess all components internally. This will be useful for any kind of interaction between elements or the creation of a new one programatically.
If it is necessary to get the position of a component in the map, use positionInWorld
. This is useful for doing some stuff like adding components on the map. While this is the position of the component in relation to the map, position
is the coordinates relative to the screen.
Utility components #
Some components with a unique purpose that can be useful. Since any other component that extends Flame's Component
or Bonfire's AnimatedObject
, you use it on your game in the following way:
this.gameRef.add(YOUR_FANCY_COMPONENT);
The components are:
// To run an animation once before it destroys itself
AnimatedObjectOnce(
{
Rect position,
FlameAnimation.Animation animation,
VoidCallback onFinish,
bool onlyUpdate = false,
}
)
// Like the previous one, this can play an animation once before it destroys itself and can also can can keep playing in a loop. But the most important feature is that this component follows another element on the map, like a player, enemy or decoration.
AnimatedFollowerObject(
{
FlameAnimation.Animation animation,
AnimatedObject target,
Position positionFromTarget,
double height = 16,
double width = 16,
bool loopAnimation = false
}
)
// Componente que anda em determinada direção configurada em uma determinada velocidade também configurável e somente para ao atingir um inimigo ou player infligindo dano, ou pode se destruir ao atigir algum componente que tenha colisão (Tiles,Decorations).
FlyingAttackObject(
{
@required this.initPosition,
@required FlameAnimation.Animation flyAnimation,
@required Direction direction,
@required double width,
@required double height,
FlameAnimation.Animation this.destroyAnimation,
double speed = 1.5,
double damage = 1,
bool damageInPlayer = true,
bool damageInEnemy = true,
}
)
If it is necesssary to add a instance of a Bonfire's basic component class (Decorations or Enemy), one shall use its specific methods:
this.gameRef.addEnemy(ENEMY);
this.gameRef.addDecoration(DECORATION);
Camera #
It is possible to move the camera to some position and go back to the player afterwards. Beware that the player will be blocked from taking any action and from moving until the camera has focused on it again.
gameRef.gameCamera.moveToPosition(Position(X,Y));
gameRef.gameCamera.moveToPlayer();
gameRef.gameCamera.moveToPositionAnimated(Position(X,Y));
gameRef.gameCamera.moveToPlayerAnimated();
Next steps #
- ❌ Component docs
- ❌ Tiled support
- ❌ Using Box2D