Alright! Enough intro… let’s just jump into it!

Where to start… I got the “idea” (inspired one), now I need a simple example. Background, a number and from every tap, decreasing that value:

GestureDetector(

onTapDown: (TapDownDetails details) => damage(details),

) var _bossDamage = 980;



void damage(TapDownDetails details) {

setState(() {

_bossDamage = _bossDamage - 30 <= 0 ? 980 : _bossDamage - 30;

});

}

Then, adding the visual elements and that’s it:

Next step was to add the actual UI/Elements to make it look like a “game”. We need a character, an actual boss to deal damage to it, maybe some power-ups and coins to buy those.

My “hero” uses 2 images, one for the “idle” state and one for the “attack” state. When you tap on the screen, the image switches to create the visual impact:

String hero() {

return tap ? "assets/character/attack.png" : "assets/character/idle.png";

}

For the boss, I needed more than one (to make it a bit more enjoyable):

static List<Bosses> getBosses() {

var list = List<Bosses>();

list.add(Bosses("Lunabi", 450, "assets/boss/boss_one.png"));

list.add(Bosses("ivygrass", 880, "assets/boss/boss_two.png"));

list.add(Bosses("Tombster", 1120, "assets/boss/boss_three.png"));

list.add(Bosses("Glidestone", 2260, "assets/boss/boss_four.png"));

list.add(Bosses("Smocka", 2900, "assets/boss/boss_five.png"));

list.add(Bosses("Clowntorch", 4100, "assets/boss/boss_six.png"));

list.add(Bosses("Marsattack", 5380, "assets/boss/boss_seven.png"));

list.add(Bosses("Unknown", 7000, "assets/boss/boss_eight.png"));

list.add(Bosses("ExArthur", 10000, "assets/boss/boss_nine.png"));

return list;

}

Don’t tell me how I came out with those names… coding at 2am makes you do that. Every boss will have a name, a life bar and an actual image.

For the Power-Ups I followed a similar logic like the bosses:

static List<PowerUps> getPowerUps() {

var list = List<PowerUps>();

list.add(PowerUps("Master Sword", 2.15, false, 50));

list.add(PowerUps("Lengendary Sword", 2.45, false, 180));

list.add(PowerUps("Keyblade", 3.75, false, 300));

list.add(PowerUps("Lightsaber", 4.95, false, 520));

list.add(PowerUps("Buster Sword", 6.15, false, 1700));

list.add(PowerUps("Soul Edge", 8.65, false, 2400));

return list;

}

You have the Sword/Weapon name, the multiplier (from base deal damage), if you bought them or not and the total amount of coins necessary to get it.

Then I thought about adding a visual “hitbox” for every time you tap on the screen:

Widget hitBox() {

if (tap) {

return Positioned(

top: yAxis,

left: xAxis,

child: Column(

children: <Widget>[

Padding(

padding: const EdgeInsets.only(bottom: 20.0),

child: Material(

color: Colors.transparent,

child: StrokeText(

"-${damageUser.toInt().toString()}",

fontSize: 14.0,

fontFamily: "Gameplay",

color: Colors.red,

strokeColor: Colors.black,

strokeWidth: 1.0,

),

),

),

Image.asset(

"assets/elements/hit.png",

fit: BoxFit.fill,

height: 80.0,

width: 80.0,

),

],

),

);

} else {

return Container();

}

}

This will add the visual element, the position of your finger and a small “hint” with the total amount of damage dealt for every single tap with a Y offset.

Remember the “damage” method? I used the TapDownDetails details parameters to get the actual X and Y position of the finger.

xAxis = details.globalPosition.dx - 40.0;

yAxis = details.globalPosition.dy - 80.0;

I also added a “hide” logic to remove that element when you lift your finger from the screen

GestureDetector(

onTapDown: (TapDownDetails details) => damage(details),

onTapUp: (TapUpDetails details) => hide(null),

onTapCancel: () => hide(null),

),

The list of “Power-Ups” is pretty much straight forward

ListView.builder(

padding: EdgeInsets.only(bottom: 20.0, left: 10.0, right: 10.0),

itemCount: list.length,

itemBuilder: (context, position) {

PowerUps powerUp = list[position];

int bgColor = !powerUp.bought && coins >= powerUp.coins

? 0xFF808080

: !powerUp.bought ? 0xFF505050 : 0xFF202020;



return swordElement(bgColor, powerUp, position);

},

)

I’m adding some visual enhancements for the buttons and the backgrounds and every Sword/Weapon

Widget swordElement(int bgColor, PowerUps powerUp, int position) {

return Padding(

padding: const EdgeInsets.symmetric(

vertical: 5.0,

),

child: Container(

height: 70,

child: Card(

color: Color(bgColor),

child: Row(

children: <Widget>[

Expanded(

child: Padding(

padding: const EdgeInsets.symmetric(horizontal: 20.0),

child: Text(

powerUp.name,

style: Utils.textStyle(11.0),

),

),

),

Padding(

padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 20.0),

child: FancyButton(

size: 20,

child: Row(

children: <Widget>[

Padding(

padding: const EdgeInsets.only(left: 10.0, bottom: 2, top: 2),

child: Text(

!powerUp.bought ? "BUY" : "BOUGHT",

style:

Utils.textStyle(13.0, color: !powerUp.bought ? Colors.white : Colors.grey),

),

),

Padding(

padding: EdgeInsets.only(left: 8.0, right: !powerUp.bought ? 2.0 : 0.0),

child: Text(

!powerUp.bought ? powerUp.coins.toString() : "",

style: Utils.textStyle(13.0),

),

),

coinVisibility(powerUp.bought),

],

),

color: !powerUp.bought && coins >= powerUp.coins

? Colors.deepPurpleAccent

: Colors.deepPurple,

onPressed: !powerUp.bought && coins >= powerUp.coins ? () => buyPowerUp(position) : null,

),

)

],

),

),

),

);

}

After all this work! I have a truly visual improvement over my first GIF

Adding sounds is always a plus for every single game:

AudioPlayer hitPlayer;

AudioCache hitCache; hitPlayer = AudioPlayer();

hitCache = AudioCache(fixedPlayer: hitPlayer); // If the audio was playing, we stop it to init again

hitPlayer.pause();

hitCache.play('audio/sword.mp3');

This logic was used across the game, every time you kill an enemy, you’ll earn coins, a sound for that, every time you buy something, a sound for that as well. BTW, what is a game without background music? You can loop it this way:

AudioCache musicCache;

AudioPlayer instance; void playMusic() async {

musicCache = AudioCache(prefix: "audio/");

instance = await musicCache.loop("bgmusic.mp3");

}

That will go inside your InitState and will be stopped at your Dispose method. Also, If you noticed, I’m using a custom font to make it more “game” look alike.