bonfire 0.0.8 bonfire: ^0.0.8 copied to clipboard
(RPG maker) Create RPG-style or similar games more simply with Flame.
EN | PT
Bonfire #
Construa games do tipo RPG ou similares explorando o poder do FlameEngine!
Você encontra o código completo desse exemplo aqui.
Sumário #
Como funciona? #
Essa ferramenta foi construida utilizando os recursos disponíveis pelo FlameEngine e todos eles estarão disponíveis para serem utilizados além dos implementados pelo Bonfire. Por conta disso recomenda-se dar uma olhadinha no FlameEngine antes de iniciar a brincadeira com o Bonfire.
Para executar o game com Bonfire basta utilizar o seguinte widget:
@override
Widget build(BuildContext context) {
return BonfireWidget(
joystick: MyJoystick(), // required
map: DungeonMap.map(), // required
player: Knight(), // Caso não passe o player o direcional do joystick explora o mapa. Bom para checar se está tudo ok e auxiliar na construção do mapa.
interface: KnightInterface(),
decorations: DungeonMap.decorations(),
enemies: DungeonMap.enemies(),
background: BackgroundColorGame(Colors.blueGrey[900]),
);
}
Descrevendo um pouco mais sobre os componentes e organização:
Map #
Ele representa nada mais que o mapa ou mundo em que o jogo ocorre.
Consiste em uma matriz de quadradinhos (Tiles), que em conjunto formam o seu mundo (veja). Atualmente você monta essa matriz manualmente, como podemos ver nesse exemplo, mas futuramente terá suporte para o carregamento de mapas montados com Tiled.
Existe um componente pronto e o nome dele é:
MapWorld(List<Tile>())
Nele passamos a lista de Tiles que montará nosso mapa e toda a movimentação de câmera durante a movimentação do Player ele cuida pra você.
Tile(
'tile/wall_left.png', // Imagem que representa esse Tile
Position(positionX, positionY), // posição no mapa onde será renderizado.
collision: true, // se ele possui colisão, ou seja, o player nem inimigos iram passar por ele(Ideal para muros e obstáculos).
size:16 // Tamanho do tile, nesse caso 16x16
)
Decorations #
Representa qualquer coisa que queira adicionar ao cenário, ele pode ser um simples "barril" no meio do caminho a um NPC que você poderá utilizar para interagir com o seu player.
Você poderá criar seu decoration utilizando:
GameDecoration(
spriteImg: 'itens/table.png', // imagem que será renderizado
initPosition: getRelativeTilePosition(10, 6), // posição no mundo que será posicionado
width: 32,
height: 32,
collision: true, // se terá colisão
// animation: false, // caso você queira adicionar algo animado você pode passar sua animação aqui e não passar o 'spriteImg'
// frontFromPlayer: false // caso queira forçar que esse elemento fique por cima do player ao passar por ele
)
ou poderá criar sua própria classe, basta extender de GameDecoration
e adicionar comportamentos que desejar utilizando o update
e/ou render
, como feito nesse exemplo (um baú que ao player se aproximar, se remove do game e faz "brotar" duas poções de vida que também são GameDecoration
).
Neste componente como em todos os demais, você tem acesso ao BuildContext
do Widget que renderiza o game, então poderá exibir dialogs, overlays, entre outros componentes do Flutter para exibir algo na tela.
Enemy #
É utilizado para representar seus inimigos. Nesse componente existem ações e movimentos prontos para serem utilizados e configurados se quiser. Mas, caso deseje algo diferente terá a total liberdade de customizar suas ações e movimentos.
Para criar seu inimigo você deverá criar uma classe que o represente e extenda de Enemy
como nesse exemplo. No construtor você terá os seguintes parâmetros de configuração:
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,
);
Depois disso já terá seu inimigo, mas ele não fará nada além de ficar parado. Para adicionar movimentos a ele, você precisará sobescrever o método Update
e implementar alí o seu comportamento.
Já existe algumas ações prontas que você poderar utilzar como visto nesse exemplo, são eles:
//movimentos básicos
void moveBottom({double moveSpeed})
void moveTop({double moveSpeed})
void moveLeft({double moveSpeed})
void moveRight({double moveSpeed})
// De acordo com o raio passado por parâmetro, o inimigo irá procurar e observar o player.
void seePlayer(
{
Function(Player) observed,
Function() notObserved,
int visionCells = 3,
}
)
// De acordo com o raio configurado, o inimigo irá procurar o player, caso o encontre, irá se movimentar em direção ao player e ao chegar próximo, o método 'closePlayer' será disparado.
void seeAndMoveToPlayer(
{
Function(Player) closePlayer,
int visionCells = 3
}
)
// Executa um ataque físico ao player infligindo o dano configurado com a frequência configurada. Poderá adicionar animações para representar esse ataque.
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,
}
)
// Executa um ataque a distância. Será adicionado ao game um 'FlyingAttackObject' que é um componente que se moverá pelo mapa na direção configurada e causará dano a aquele que atingir ou se destruir ao se bater em barreiras.
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,
}
)
// De acordo com o raio configurado o inimigo irá procurar o player, caso o encontre, irá se posicionar para executar um ataque a distância. Ao chegar nessa posição ele notificará pela função 'positioned'.
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,
)
}
)
// Caso precise saber em qual direção o player em relação a você.
Direction directionThatPlayerIs()
// Caso deseje adicionar uma animação curta (animação sem loop, ele excuta somente uma vez).
void addFastAnimation(FlameAnimation.Animation animation)
// Caso deseje infligir dano a ele.
void receiveDamage(double damage)
// Caso deseje adicionar vida.
void addLife(double life)
// Desenha barra padrão de vida. Deve ser utilizado sobescrevendo o método 'render'.
void drawDefaultLifeBar(
Canvas canvas,
{
bool drawInBottom = false,
double padding = 5,
double strokeWidth = 2,
}
)
// Adicione em 'render' caso deseje desenhar área de colisão.
void drawPositionCollision(Canvas canvas)
Player #
Representa o seu personagem. Nele também existem ações e movimentos prontos para serem utilizados.
Para criar seu player deverá criar uma classe que o represente e extenda de Player
como nesse exemplo. No construtor você terá os seguintes parâmetros de configuração:
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,
);
No player você poderá escultar as ações que foram configuradas em seu Joystick(essa configuração você verá com mais detalhes a frente) sobescrevendo o método:
@override
void joystickAction(int action) {}
Ao perceber o toque nessas ações do joystick, você poderá executar outras ações. Assim como no inimigo, aqui também temos algumas ações prontas para serem utilizadas:
// Executa um ataque físico ao player infligindo o dano configurado com a frequência configurada. Poderá adicionar animações para representar esse ataque.
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,
}
)
// Executa um ataque a distância. Será adicionado ao game um 'FlyingAttackObject' que é um componente que se moverá pelo mapa na direção configurada e causará dano a aquele que atingir ou se destruir ao se bater em barreiras.
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,
}
)
// Exibe valor do dano no game com uma animação.
void showDamage(
double damage,
{
TextConfig config = const TextConfig(
fontSize: 10,
color: Colors.white,
)
}
)
// De acordo com o raio passado por parámetro o player irá procurar e observar inimigos.
void seeEnemy(
{
Function(List<Enemy>) observed,
Function() notObserved,
int visionCells = 3,
}
)
// Caso deseje adicionar uma animação curta (animação sem loop, ele excuta somente uma vez).
void addFastAnimation(FlameAnimation.Animation animation)
// Caso deseje infligir dano a ele.
void receiveDamage(double damage)
// Caso deseje adicionar vida.
void addLife(double life)
// Adicione em 'render' caso deseje desenhar área de colisão.
void drawPositionCollision(Canvas canvas)
Interface #
É um meio disponibilizado para você desenhar a interface do game, como barra de vida, stamina, configurações, etc; qualquer coisa que queira adicionar à tela.
Para criar sua interface você deverá criar uma classe e extender de GameInterface
como nesse exemplo.
Sobescrevendo os médodos Update
e Render
você poderá desenhar sua interface utilizando Canvas ou utilizando componentes disponibilizados pelo FlameEngine.
Joystick #
É responsavel por controlar seu personagem. Existe um componente totalmente pronto e configurável para você personalizar o visual e adicionar a quantidade de ações que achar necessário, ou poderá criar o seu próprio joystick utilizando nossa classe abstrata.
Também temos um componente prontinho para te ajudar nessa etapa, mas se quiser construir o seu pŕoprio basta extender de JoystickController
e notificar os eventos utilizando o joystickListener
que estará disponível para você.
O componente default que existe para ser utilizado é configurável da seguinte maneira:
Joystick(
pathSpriteBackgroundDirectional: 'joystick_background.png', //(required) imagem do backgroud do direcional.
pathSpriteKnobDirectional: 'joystick_knob.png', //(required) imagem da bolinha que indica a movimentação do direcional.
sizeDirectional: 100, // tamanho do direcional.
marginBottomDirectional: 100,
marginLeftDirectional: 100,
actions: [ // Você adicionará quantos actions desejar. Eles ficarão posicionados sempre no lado direto da tela e você poderá definir em que posisão deseja que cada um fique.
JoystickAction(
actionId: 0, //(required) Id que irá ser acionado ao Player no método 'void joystickAction(int action) {}' quando for clicado.
pathSprite: 'joystick_atack.png', //(required) imagem da ação
pathSpritePressed : 'joystick_atack.png', // caso queira poderá adiciona uma imagem q exibirá quando for clicado.
size: 80,
marginBottom: 50,
marginRight: 50,
align = JoystickActionAlign.BOTTOM // eles sempre estarão alinhado a direita da tela, com possibilidades de definir para cima ou em baixo (JoystickActionAlign.TOP/JoystickActionAlign.BOTTOM).
),
JoystickAction(
actionId: 1,
pathSprite: 'joystick_atack_range.png',
size: 50,
marginBottom: 50,
marginRight: 160,
align = JoystickActionAlign.BOTTOM
)
],
)
veja o exemplo.
OBS: #
Esses elementos do game utilizam o mixin ´HasGameRef´, então você terá acesso a todos esses componentes (Map,Decoration,Enemy,Player,...) internamente, que serão úteis para a criação de qualquer tipo de interação ou adição de novos componentes programaticamente.
Se for necessário obter a posição de um componente para ser utilizado como base para adicionar outros componentes no mapa ou coisa do tipo, sempre utilize o positionInWorld
ela é a posição atual do componente no mapa. A variável position
refere-se a posição na tela para ser rendereziado.
Componentes úteis #
São componentes que executam algum tipo de comportamento e podem ser úteis. Assim como qualquer outro componente criado por você que extenda de Component
do flame ou AnimatedObject
do Bonfire você pode utiliza-lo ao seu game programaticamente dessa forma:
this.gameRef.add(COMPONENTE);
Esses são os componentes disponíveis até o momento:
// Componente que executa sua animação uma única vez e logo após se destroi.
AnimatedObjectOnce(
{
Rect position,
FlameAnimation.Animation animation,
VoidCallback onFinish,
bool onlyUpdate = false,
}
)
// Esse componente assim como o anterior pode executar sua animação e se destruir ou continuar executando em loop. Mas o grande diferencial é que ele é executado seguindo a posição de um outro componente como um player, enemy ou 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,
}
)
Se for necessário adicionar de forma programática qualquer um dos componentes que fazem parte da base do game no Bonfire(Decorations ou Enemy), deve ser adicionado com seus métodos específicos:
this.gameRef.addEnemy(ENEMY);
this.gameRef.addDecoration(DECORATION);
Camera #
É possível movimentar a cámera de forma animada para uma determinada posição do mapa e depois voltar para o personagem. Lembrando que ao movimentar a câmera para uma determinada posição o player fica bloqueado de ações e movimentos e só é desbloqueado quando a camera volta a focar nele.
gameRef.gameCamera.moveToPosition(Position(X,Y));
gameRef.gameCamera.moveToPlayer();
gameRef.gameCamera.moveToPositionAnimated(Position(X,Y));
gameRef.gameCamera.moveToPlayerAnimated();
Próximos passos #
- ❌ Documentação detalhada dos componentes.
- ❌ Support with Tiled
- ❌ Using Box2D