This is a Npc and use Attackable mixin.
Create an enemy in the game. Instances of this class have predefined behaviors ready to be used and configured as wanted. All the actions and movements are customizable.
There are two types of enemies: SimpleEnemy
and RotationEnemy
:
This is a Enemy and use DirectionAnimation mixin.
Used for 45º and 67.5º perspectives. We can configure motion animations for all directions (up, down, left, right, up_right, up_left, down_left, down_right).
To use it, simply create a class that will represent your enemy and extend SimpleEnemy:
class Goblin extends SimpleEnemy {
Goblin(Vector2 position)
: super(
position: position, //required
size: Vector2(32.0,32.0), //required
life: 100,
speed: 100,
initDirection: Direction.right,
animation: SimpleDirectionAnimation(
idleLeft: Future<SpriteAnimation>(),
idleRight: Future<SpriteAnimation>(), //required
runLeft: Future<SpriteAnimation>(),
runRight: Future<SpriteAnimation>(), //required
idleUp: Future<SpriteAnimation>(),
idleDown: Future<SpriteAnimation>(),
idleUpLeft: Future<SpriteAnimation>(),
idleUpRight: Future<SpriteAnimation>(),
idleDownLeft: Future<SpriteAnimation>(),
idleDownRight: Future<SpriteAnimation>(),
runUp: Future<SpriteAnimation>(),
runDown: Future<SpriteAnimation>(),
runUpLeft: Future<SpriteAnimation>(),
runUpRight: Future<SpriteAnimation>(),
runDownLeft: Future<SpriteAnimation>(),
runDownRight: Future<SpriteAnimation>(),
),
);
@override
void receiveDamage(double damage, int from) {
/// Called when the enemy receive damage
super.receiveDamage(damage, from);
}
@override
void die() {
/// Called when the enemy die
super.die();
}
}
Now that you have a class that represents your enemy, you can configure their behavior.
There are several useful extensions that you can use inside the update
method that will help you with this task:
void seePlayer({
required Function(Player) observed,
VoidCallback? notObserved,
double radiusVision = 32,
double? visionAngle, // default 6,28319 (360 graus)
})
It will trigger a callback function once the player is within the enemy's radiusVision.
void seeAndMoveToPlayer({
required Function(Player) closePlayer,
double radiusVision = 32,
double margin = 10,
double? visionAngle, // default 6,28319 (360 graus)
})
The enemy will move in the direction of the player once it gets within the radiusVision. When it gets close to the player, closePlayer
will be fired.
void simpleAttackMelee({
required double damage,
required Vector2 size,
int? id,
int interval = 1000,
bool withPush = false,
double? sizePush,
Direction? direction,
Future<SpriteAnimation>? animationRight,
Future<SpriteAnimation>? animationDown,
Future<SpriteAnimation>? animationLeft,
Future<SpriteAnimation>? animationUp,
VoidCallback? execute,
})
Executes a physical attack to the player, making the configured damage with the configured frequency. You can add animations to represent this attack.
void simpleAttackRange({
required Future<SpriteAnimation> animationRight,
required Future<SpriteAnimation> animationLeft,
required Future<SpriteAnimation> animationUp,
required Future<SpriteAnimation> animationDown,
required Future<SpriteAnimation> animationDestroy,
required Vector2 size,
int? id,
double speed = 150,
double damage = 1,
Direction? direction,
int interval = 1000,
bool withCollision = true,
CollisionConfig? collision,
VoidCallback? destroy,
VoidCallback? execute,
LightingConfig? lightingConfig,
})
Executes a distance attack. It will add a FlyingAttackObject
to the game and this will be sent in the configured direction, making some damage to whomever it hits or being destroyed when hitting barriers (tiles with collision).
void seeAndMoveToAttackRange({
required Function(Player) positioned,
double radiusVision = 32,
double? minDistanceCellsFromPlayer,
})
When the player is within the radiusVision, the enemy will position itself to perform a distance attack. Once it reaches the attack position, the positioned
callback will be fired.
Other useful functions:
// Displays the damage value in the game with an animation.
void showDamage(
double damage,
{
TextConfig? config,
double initVelocityTop = -5,
double gravity = 0.5,
double maxDownSize = 20,
DirectionTextDamage direction = DirectionTextDamage.RANDOM,
bool onlyUp = false,
}
)
// Gives the direction of the player in relation to this enemy
Direction directionThatPlayerIs()
// Restore life points to the enemy
void addLife(double life)
// Draws the default life bar. Should be used inside the `render` method.
void drawDefaultLifeBar(
Canvas canvas,
{
bool drawInBottom = false,
double padding = 5,
double strokeWidth = 2,
}
)
If you want to add quick animations, like effects of taking damage or making a special attack, you can use the method addFastAnimation
:
this.animation.playOnce(
Future<SpriteAnimation> animation,
Vector2Rect position,
{
VoidCallback? onFinish,
bool runToTheEnd = false,
}
)
Note: Enemies only move if they are visible in the camera. if you want to disable this, add false in collisionOnlyVisibleScreen
in your collision config. See Colission System.
Complete SimpleEnemy example here.
This is a Enemy and use UseSpriteAnimation, UseAssetsLoader.
Used for 90º perspectives. And we can configure Motion animations for run and idle.
class Tank extends RotationEnemy {
Tank(Vector2 position)
: super(
position: position, //required
animIdle: Future<SpriteAnimation>(), //required
animRun: Future<SpriteAnimation>(), //required
size: Vector2(32.0,32.0), //required
life: 100,
speed: 100,
currentRadAngle: -1.55,
);
@override
void update(double dt) {
// do anything
super.update(dt);
}
@override
void render(Canvas canvas) {
// do anything
super.render(canvas);
}
@override
void receiveDamage(double damage, int from) {
super.receiveDamage(damage, from);
}
@override
void die() {
super.die();
}
}
Now that we have our class that represents our enemy, we can configure their behavior.
There are several useful extensions that we can use in update
that will help us to configure these movements:
void seePlayer({
required Function(Player) observed,
VoidCallback? notObserved,
double radiusVision = 32,
})
void seeAndMoveToPlayer({
required Function(Player) closePlayer,
double radiusVision = 32,
double margin = 10,
})
void seeAndMoveToAttackRange({
required Function(Player) positioned,
double radiusVision = 32,
double? minDistanceCellsFromPlayer,
})
void simpleAttackMelee({
required Future<SpriteAnimation> attackEffectTopAnim,
required double damage,
required Vector2 size,
int? id,
bool withPush = false,
double? radAngleDirection,
VoidCallback? execute,
int interval = 1000,
})
void simpleAttackRange({
required Future<SpriteAnimation> animationTop,
required Future<SpriteAnimation> animationDestroy,
required Vector2 size,
int? id,
double speed = 150,
double damage = 1,
double? radAngleDirection,
int interval = 1000,
bool withCollision = true,
bool collisionOnlyVisibleObjects = true,
VoidCallback? destroy,
CollisionConfig? collision,
VoidCallback? execute,
LightingConfig? lightingConfig,
})
String tileTypeBelow()
List<String> tileTypesBelow()
If none of these types of enemies do not meet your needs. You can create your own by extending the Enemy
class.
With Enemy you will have access to the following methods:
- void moveUp(double speed)
- void moveDown(double speed)
- void moveLeft(double speed)
- void moveRight(double speed)
- void moveUpRight(double speedX, double speedY)
- void moveUpLeft(double speedX, double speedY)
- void moveDownLeft(double speedX, double speedY)
- void moveDownRight(double speedX, double speedY)
- void moveFromAngleDodgeObstacles(double speed, double angle,{Function notMove})
- void moveFromAngle(double speed, double angle)
- void receiveDamage(double damage, int from)
- void addLife(double life)
- void die()