bonfire 0.0.2 bonfire: ^0.0.2 copied to clipboard
(RPG maker) Create RPG-style or similar games more simply with Flame.
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(),
player: Knight(),
interface: KnightInterface(),
map: DungeonMap.map(),
decorations: DungeonMap.decorations(),
enemies: DungeonMap.enemies(),
);
}
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 possue 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
)
Derocations #
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 vc 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, 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 tabém são GameDecoration
).
Nesse 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,
drawDefaultLife:true, // desenhará acima do enimigo uma barra de vida. Caso queira desenhar sua própria sobescrevendo o 'render', marque aqui como false.
);
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 ele irá procurar e se observar o player irá se movimentar em direção. Estando do lado dele será notificado pela função 'closePlayer'.
void seeAndMoveToPlayer(
{
Function(Player) closePlayer,
int visionCells = 3
}
)
// Executa um ataque físico ao player infligindo o dano configurado com a frequencia configurada. Poderá adicionar animações para represetar 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 infligirar 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 ele irá procurar e se observar o player ele 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 estar de você. poderá utilizar essa função.
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)
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 as ações que foram configuradas em seu Joystick(essa configuração você verá com mais detalhes a frente). Poderá escultar essas ações sobescrevendo o método:
@override
void joystickAction(int action) {}
E ao perceber o toque nessas ações do joystick você poderá executar 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 represetar 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 infligirar 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,
)
}
)
// 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)
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, ams poderá definir se queira que se posicione em 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 variavel position
refere-se a posição na tela para ser rendereziado.
Próximos passos #
- ❌ Documentação detalhada dos componentes.
- ❌ Support with Tiled
- ❌ Using Box2D