[MUSIC PLAYING] FILIP HRACEK: Let me guess-- you want to build a game. Well, you're in the right place. In this video, I'll walk
you through the process of getting from a game
idea to a published indie game in Flutter. My name is Filip. Last year, I released a
successful commercial game built in Flutter. This year, I built an
open-source sample, which shows a game with
all the bells and whistles, which means ads, in-app
payments, achievements, leaderboards, main menu,
settings, sound, music-- the works. And today, I'll show
you a starter project that you can use to
build your own game. I went through the
pain of implementing all the bells and
whistles so that you can focus on the meat-- the game itself. This is all part of the
Flutter Casual Games Toolkit, which you can find
on flutter.dev/games. Why would you want
to build a game? Well, two reasons, really. First, let's be honest-- it's a lot more
fun to be building a game than it is to be building
an app, most of the time. But yeah. Second, games are
kind of a big deal. They are. They make money. So according to some estimates,
70% of App Store revenue is coming from games. Only 30% is coming from apps. One way I like to
categorize games is splitting them between
app-like games and video games. Video games are the more
stereotypical kinds of games-- arcades, shooters, racers,
platformers, and so on. App-like games are things
like board games, card games, visual novels, puzzle games,
strategies, and so on. To be clear, I don't think any
player makes this distinction between video games
and app-like games, but from our point of
view, it's really useful. Video games will
typically repaint the screen every single frame. The player will continually
apply some kind of input, like holding a button
or a direction. App-like games are different. Their screen is mostly static-- not completely, of
course, but mostly. Just like apps, they wait for
the player to tap on things-- tap this button, tap this
portrait, tap this building, whatever. These games don't
need to be turn-based, they can be real-time, but
the player controls them with app-like gestures. They feel like games, but
technically speaking-- "technically speaking,"-- they're closer to apps. In this video, we'll be
focusing on app-like games. Flutter is a-- mwah-- fantastic fit for
these kinds of games. Flutter can make beautiful
UIs. It gives you control over every single pixel. It is completely open-source. It is very cross-platform. It's fast, efficient,
and it makes it easy to build
app-like interfaces. App-like games are less
stereotypically gamey, but they are insanely
popular, especially on mobile, where you don't have these
kinds of control devices, and instead, you have a touch
screen and an often distracted player. Think of games like Puzzle
and Dragons, Candy Crush, Coin Master, Gardenscapes,
and the like. To be clear, most of what
we'll be talking about today also applies to the
more traditional video games in Flutter. Even with them, you need a
main menu, a settings page, some kind of monetization,
and stuff like that. If you want to build a
video game in Flutter, there's the Flame Engine. It has been made and optimized
for these kinds of games, and it's behind some
very successful titles. Flame Engine gives you a
game widget, which is-- you guessed it-- a
widget, which means it plays well with
the rest of Flutter, and therefore with
everything that I'll be talking about today. OK, let's have a look at
some actual code, shall we? This is the game that
I made this year. You can check it out
in the usual stores. The audio effects are by me. FILIP HRACEK (VOICEOVER):
Swish, swish. FILIP HRACEK: But
more importantly than in the app stores, you can
also read the code on GitHub. Link in the description below. This game is built on
top of the template that we'll be covering today. So if you want to see more
real-world examples of how you would implement things
on top of this template, you know where to go. You may also find
it useful to check how I made some of
the visual stuff like the custom transitions,
like the little pop of the restart button, or
the roughly drawn lines. These little things
really help sell your game as an experience. They make your game
feel like a game. But that's a full game. You probably don't want to
work on someone else's game, you want to build your own. For that we have the template. It's like the Flutter create
project, except it's for games. And it has all the
bells and whistles like the main menu, sound,
music, in-app payments, and stuff like that. It looks a lot more basic than
even the tic-tac-toe game, and that's by design. You want to add
your own styling. You want to make it your own. I don't know how your dream
game should look like. It also doesn't have
any actual game. That's also by design. That's for you to build. You can, of course, start your
game with the usual counter template, but sooner
or later, if you want to put your game
out there, you'll want all those
bells and whistles. Before we look at the
separate features, I want to take a general look
at the template and the approach I took with it. Because the template
is doing so much, I had to be pretty opinionated. I had to take an
approach and stick to it. I had to pick packages too. I couldn't be completely
tech agnostic. For example, for accessing
services such as audio, I chose to provide a package. It's low-level, it's
easy to understand, and it works with pretty
much all the other approaches out there. For the game itself,
you can, of course, use whatever approach
you like best. The game template
itself has no opinion on how you implement your game. It's also just the
starter template. If you feel strongly about using
something else to, say, play audio, then you can
totally change the code. My goal wasn't to
create the game engine to end all game engines. My goal was to give you a
quick, comfortable start and to get out of your
way as much as possible. When building any
kind of app, there is a trade-off between
robustness and speed of development. When it comes to
games, the scales tip quite a little bit in favor
of speed and experimentation. Games typically don't have that
many layers of indirection. They also don't have the
kind of test coverage that many apps do. Games are usually
simulations of some kind, and rely on massive amounts
of shared state, which is hard to meaningfully mock
up and test in isolation. Plus, with the game, you
need to build an insane amount of features
very fast, and then you will delete half
of those features and try something else. In other words, you will
write a lot of throwaway code because you cannot know
what will be fun in advance. There's a bit less need
for robustness too. If you crash a game,
it's not like someone's losing their accounting data-- unless your game is
about accounting, but that still doesn't
count because those are not real accounting
data, so you're fine. Game programming is just a lot
more by the seat of your pants. A lot more cowboy programming
in the game development world compared to the app
world, especially if you're a solo developer,
but also in the big studios. That's not to say
that you should just give up on code quality here-- far from it. Great games are built
with great engineering. Just remember that you're not
building accounting software here. This is why the
code structure is quite shallow in this template. If you want to
play a sound, just get the instance
of audio controller and call play sound directly. I could, of course,
make it so that it's using some kind of
publisher-subscriber pattern, but I am not convinced
that the increased complexity of the code and
indirectness of the code would be worthwhile. That said, if you want
to see a more robust game code in Flutter with
test coverage and such, Very Good Ventures just
released a pinball game made in Flutter and
Flame, and the GitHub repo is in the description below. All right, let's
get to the code. Let's start with
the pubspec.yaml file just so you can
see the dependencies. All right, so we are using-- so these I call the core
dependencies, I guess, because they kind
of must be there for everything to function. audioplayers is how we
play sound and music. cupertino_icons
are there in case you want to add Apple-like
icons into your settings menu, for example. go_router is there
to route things-- the navigator, really. logging is there so that
it's easier to log things. provider, shared_preferences--
that's the basics, right? And then there are three
other dependencies. game_services is there for
achievements and leaderboards, google_mobile_ads for
ads, and in_app_purchase for in-app purchase. And I say here if you
know that you don't need, for example, in-app
purchases, you can just delete this
dependency and then get rid of the files that
actually use that dependency, and it's all good. I also show how to obviously
use assets, how to use fonts, and how to create a Flutter
icon because that's also a big part of having a game is
to have a good-looking icon. So I'm using the
Flutter icons package to create the icons for
both iOS and Android. All right, let's look
at the star of the show, and that is main.dart. main.dart starts
with logging, right? So this is the easiest way
to do logging in Flutter, and it's basically
if you set this up, and you're using the
logging package that comes with Dart itself-- as in, like, the Dart team
is working on that package-- then you can just
add these few lines, and then you can do
whatever you want with logs that come from
anywhere in your app, plus anywhere in
any packages that are using the logging package. So this is nice. Right now, I'm actually making
this video quite a few weeks before when I need
to release the code. I think by the time
that you see this code, it will actually
maybe send things to Crashlytics or
something like that. But it's not a big change. You can imagine how you could
just take this record, which has things like the
message and time and where the message
is coming from, and either put it into memory
or just print it to the Console or whatever else. Cool. Then we go full screen, because
that's what games often do. So we are using the edge-to-edge
system UI mode, which is not completely full screen. It still gives people,
I think, the top bar. So you could change
it to something else, but that's how you
go full screen. And then there are these
three blocks of code that are mostly
commented out at start, and these are commented
out because they need some kind of
setup on your part before you can
meaningfully use them. So for example, if you want to
have game services, as in like, you want to have the ability
to give people achievements, award players
achievements, then you need to go to
Google Play Console and then add achievements there. And then you also need to
go to the App Store Connect and add achievements there. And after that, you can
go and uncomment this code and do whatever you need to do,
but not before you are actually ready to do that. So that's why these
exist here, and that's why they are null at start. They are commented out. Here you can see how it's used. So my app, which is
the app, the game, starts with some
dependencies that are injected to it-- the local
storage settings, persistence, where do we put the
settings, where do we save the player's progress? But then there are these three
controllers-- in-app purchase, ads, and game services--
and if they are null, then the template
is smart enough to not even try to do anything
with these integrations. And so let's go a
little bit further down. The MyApp is actually just
the normal MyApp widget with go_router
routes, so this is where you would add more
routes if you need them. But at this point, you have
the main menu, of course. You have the level selection
screen, which in your case could be a map. I don't know. It could be anything. Then there is the actual
PlaySession, which could be-- well, again, anything. That's probably where people
will spend most of the time. And then there is the
win screen or something that we show to the player
when they have successfully finished part of your game. And in this case-- well, I'll show you later,
but it shows ads and score, and it does some
things like that. And then we have settings. Then we basically just have
the AppLifecycleObserver, which I will go to later,
but it has a MultiProvider, and this is where
everything that you need is provided to you anywhere
in your app via the provider package. And then it's just a
Flutter Demo and MaterialApp using the router that we defined
above, and with a little bit of theming, and that's it. Now let's look at the source of
the actual different features. So I told you it's
a shallow structure. It's basically just
all the features that we have in the
game, and they all have their own little
directory in source, and that contains both the
widgets and everything else that you need for these things. So in ads, the star
of the show here is the AdsController, which is
just a class that is, again, provided to the rest of the
game, the rest of the app, via a provider unless
you haven't set it up, in which case it's null. And the AdsController does
things like initializes, it preloads an ad that
is very important. As I learned, if you
don't preload ads, then they might take, maybe,
like, 3 seconds to actually show up after you show them
via the normal non-preloaded widget, and that's not
good because it takes way too much time. So sometimes 3 seconds-- the player is already
somewhere else. So you want to
show the ad exactly at the time where you
actually want to show the ad, so that's why we
have a preloadAd. And that's it. That's the AdsController. And then we have
a widget that you can use to show the
ad itself so that when you change the
orientation, it's still works, and so on and so forth. So this is just a little bit
of extra things around the ads package. AppLifecycle is really nice. Now we actually only
use it for the sound, but you might be using
it for something else. This is just so that if you put
it in here, as it is put here, it will provide another-- not ChangeNotifier,
but it will provide a ValueNotifier of lifecycle. And then you can listen
to this lifecycle, and as you will
see in audio, you can react to those
lifecycle changes. So you can say,
oh, you know what? I want to pause the
audio when people put my game into the background. Well, it's something that is
already implemented for you, but you might do something else. You might be saving
something somewhere when people put your game into the
background, or similar things. So that's why it's there. But for the most
part, you probably don't need to touch this. AudioController is where we
are using the audio player's plug-in. It has a music cache and
a sound effects cache. The sound effects
cache is more important because it caches, at
first, all the sound effects that you put to it, and
therefore, it immediately plays them when you
need them to, right? It doesn't need to load
them up, so that's nice. And again, you can, of course,
change any of the code here, but most of the
things here are just it listens to the
lifecycle modifier, which means that it will
stopAllSound, for example, when the state is detached. It also listens to the settings,
which we haven't come to yet, but if the settings change so
that, for example, a player turned the sound off or
muted the whole thing, then this will listen to it. It will not play any sound. It will stop music and so on. And then we have
dispose or initialize, which as I told you before,
it will go through the sounds and cache them. And then you have the playSfx
method, which basically plays the sound effects. If you want to know what sound
effects are available, again, this is just using these assets
that I have in assets/ sound effects. These are all public
domain samples. You can use them,
but of course you'll probably want to
use something else because these are something
that I made with my mouth. But the way I did
this is you probably don't want to call
AudioController with an actual name
of some MP3 file, you want to call it with
just sound effect type. And here are just a few. And you can, of course,
add more, remove. And then you add a
list of the MP3 files or whatever OGG files
that you want to play. The reason there
is a list and not just one is because
it is a good practice, and it makes your
game much nicer to play when the sound
effect for one action is not always exactly
the same sound. It gets very tiring
to be listening to the exact same sound
all again and again, right? So I went a little bit
overboard with this, but if you have, for
example, buttonTap, and you have maybe two,
three, four sounds that are very similar but
not exactly the same, it will make your game
sound much better. OK, so we have these-- that's the sounds--
and then we have songs, which is just a little
class that, as you can see, we have only one
author, Mr. Smith. Thank you for
providing the music. It's Creative Commons,
attribution-style license. And three of those songs are
included in the repository so you can start
immediately with something in the background. That also makes a huge
amount of difference, to add some kind of
music into your game. Again, games are
cultural artifacts. They often give
you some emotions. That's why you want music there. Cool. So that is the audio. Then we have game_internals. In our case, that's
where, basically, you add maybe the AI, anything that
isn't really anyhow connected to Flutter itself. Of course, you can
put it anywhere else, but that's where I would put it. And this could even be just an
import from some other package. You could have a completely
pure Dart, non-Flutter package with the game
internals, and then you can just import it over here. In our case, because this
is the game template, it is only has a goal,
which is a number, and then a progress,
which is also a number. And then if we
evaluate the level, if the progress is bigger than
the goal, then we call onWin. This is just one
of a billion ways to make something like this. Obviously I'm not
telling you to use a changeNotifier and this kind
of thing to make your game. It's just a sample. Then there's games_services. So that is how you add
achievements and also put things into the leaderboard. And that's also pretty
straightforward. Basically, the only
reason I have it like this is so that you can mock it and
you can maybe test it better. But basically, this
just goes and uses the games_services package and
uses the static methods there, but nothing too crazy. So awardAchievement is just
GameServices.unlock basically. Then there is the
initialize, showAchievements, showLeaderboard,
submitLeaderboardScore. And here, you will want to
probably go here and maybe change how the
score is computed. This is over here. This file has a
class called Score, and it will let
you create a score. In my case or in the
template basic case, you give it a level, a
difficulty, and a duration, and it will compute some kind
of number, which is the score. And so we'll get to that
later, but basically, then if you submit
leaderboard score, you give it the score over
here, and that score already has an integer, which is what
the leaderboards actually want. They only want an integer. They can't get any
other metadata from you. So in this case, it's just that. In some games you might want to
have more than one leaderboard. Like for example, oh,
here's a leaderboard of how fast people
did the first level, and here's the leaderboard
how fast people went through the whole game. And for that, you would want
to add a new method here that uses some other thing,
some other leaderboard ID. These leaderboard IDs--
you go and you do them, or you get them from App
Store Connect and from Google Play store. After games_services. We have in_app_purchase. In_app_purchase, again,
is just controller, and it's very simple,
I guess, wrapper around the
in_app_purchase package. in_app_purchase package provides
a single purchase stream, and then you listen to it,
and that purchase stream gives you all the data
that you might need. It gives you all
the updates that come from the in-app
capabilities of the phones. So this kind of makes
it a little more usable because you just
call restorePurchases at the start of the game
so that you know what has been actually purchased. Then you have things like buy,
which just buys something, right? And so on and so forth. In our case, all
that is implemented is just one purchase, and that
purchase is the "remove ads." Which of course needs
to be first set up in App Store Connect
and in Google Play, but after that, you
can just use it. So that's ad removal,
which is this. Next up, let's have a look at
the level of selection screen, which is, I guess, the first
screen that we're actually looking at. So let's have a look
at the actual game. This is how the level
selection screen looks like running on Mac OS. And it's just a widget, right? It's just a screen with
a scaffold and all that. And here, I'm just
showing you how you might implement
something like this if it's only a list of levels. Of course, a lot of games will
have something more interesting like a map or, I don't know,
a grid or something like this. But if it's the list of levels,
you might just start with this. And it just goes to the
route of that session-- I mean the play level,
basically, and that's that. Very, very short amount of code. And the levels,
again, in my case are pretty straightforward. It's just a class that has
things like the level number, the difficulty, and
associated achievements. And, again, this
will be something that you'll almost definitely
want to change to your liking. So that's the level
selection screen. Now the hero-- main menu screen. All right, so main
menu screen looks in the game template like this. And it has some hero
image at the start, which is just a text. Then it has the play
button, which probably should be a lot
bigger, but again, I'm not styling anything here. And then you'll see
how I'm checking if gamesServicesController
is null. I'm getting
gamesServicesController here using provider. And if it's null,
then we don't show anything between play and
settings, but if it's not null, then we are showing these
elevated buttons that are actually showing
achievements and showing the leaderboard. And these things,
by the way, they are implemented
using the platform services that provide these
achievements and leaderboards. So this just calls
something somewhere, and it doesn't add anything
into your Flutter app itself. So the achievements
and leaderboards are overlays of the native
platforms over your app, right? So it really is kind
of far and forget-- just show achievements,
and that's it. And when the player
closes that overlay, they go back to
playing your game. In this case, to
the main screen. So this is very simple. And then there is the
settings, and then there is this button, which is pretty
important to have, I think, if you have music in your game-- and you should. You should probably make it
very easy on the start page or on the main
screen of your game to immediately mute everything. So you go and, of
course, go to settings and see if you want
sound effects or music, but you also want
a mute button here just so people can kind
of panicly immediately turn it off. And then there's a little thing
that gives credit to Mr. Smith. All right, so that's
the main menu screen. Let's have a look
at the play_session. In our case, that's also super-- I mean, just so you know,
the play session in the game template looks like this. It's just a slider,
and it just tells you, hey, if you put the
slider to 5% or above, you win this level,
basically, right? So you can do that and then-- FILIP HRACEK (VOICEOVER): Yay. FILIP HRACEK: I still
love the sound effects. And you get to the win screen,
which we'll cover later. So let's have a look at level 2. It's harder, much
harder, because you have to drag the slider
all over to 42% and above. So that's what the play
session screen looks like. The reason it's not just a
button, or the reason I even included in the game
template is to show how you might want to do the
celebration, for example. This is done using a stack. Then also, I am showing how you
can see when the player started that particular session so
later you can check how long it took them to finish it. And we also see do we
have in-app purchases? If so, do we have adRemoval
in-app purchase active? And if not, if ads
are not removed or if we don't have
InAppPurchaseController at all, then we ask the
adsController to please preload an ad to be
shown in the win screen. So that's how you would do it. And again, this is fine. If you don't have in-app
purchases or ads yet set up, then this will just not do
anything, so that's fine. All right, playerWon is what
the level state is calling when the player has won
that particular level, and that does things like
it creates the score using the length of play and the
difficulty of the level and also the number
of the level, and then it tells
player progress, which we haven't gone to yet. How far the player has gone? Like, the player has
finished level number this. And then there's a little
celebration going on. We also play a sound
that's the "yay." And that's how you play sounds-- you just do read
AudioController. That is always there, so
it's never null, right? You will need the audio
controller for the game. And then you'll
play a sound effect, and here, it's the
congrats sound effect. Then, again, we'll
find out if we have gamesServicesController,
and if we do, then we put an achievement. Again, if the level has
some kind of an achievement, then we send the achievement
to the underlying system, and then we also submit
the score to a leaderboard. And that's it. And then we go to the play/won
or the win game screen. Next up, player progress. That's not a screen, that's
just a persistent class that can save itself,
and that in our case just saves the highest
level reached so that if the player goes to the
level selection screen, only the levels that they
have already finished or the one that is just
above that are shown. So that's what the
player progress does, and all it does is it uses
some kind of persistence_store to save and then load the
highest level reached. Of course, player progress
in different games looks differently. It could be the
highest level reached. It could be just a
set of levels that were successfully finished. It could be much more. It could be the
score of each level. It could be the player's
characteristics, like in a role-playing game,
and so on and so forth. So this is where
we persist this. And by default, the game
will put all of this into the local
storage, but we also have a MemoryOnlyPlayer
ProgressPersistence that is especially nice for testing. Settings-- well,
settings is this page, which you can access from here
or from the play page here. And it has a list of
things that you can change. So the screen itself is
pretty straightforward. It's just the list
view, basically, and with lines in it, right? So that's nothing that you
can do yourself in 5 seconds-- maybe 5 minutes, OK? But it also has
SettingsController, and the SettingsController
has ValueNotifiers with like, muted and
soundsOn and playerName so that you can kind of
play around with this. And the soundsOn, musicOn, and
muted are all synced with-- or the audio controller
listens to these and will change its
behavior according to that. So you can just change
the value of soundsOn, and automatically
everything will just work. So that's toggleMusicOn,
toggleMuted, and so on and so forth. And because these
are value notifiers, you can actually listen to
them, and so you can also update your UI easily. There is a CustomNameDialogue
just in case you want to-- in games, often there
are places where people can input names or input
some string, so this is just showing how you might want to do
that, but it's not a big deal. And again, there is a
persistence store for settings. This could be something
different than the player progress, right? So settings should probably go
to the local storage for sure, but the player
progress in some games might go to some other
like, cloud storage or something like
that, so that's why it's different and separate. Style is where I put things
like the transition that looks like this. Not this one, but this one
where it kind of slides. The only reason, again,
is so that it's obvious where to put these things. The buildMyTransition
is used from main.dart so that it's obvious how
you might want to do it, but it's not required, for sure. Palette is a class that just
has the palette that I found somewhere that's good
for games, and we'll get to where to get good
color palettes later. And you could, of course,
try to use something like material theme, but I
find that games generally have a lot more
colors than apps, and you want some kind of place
to put all the colors in one place, and so that's why I
created this palette class. You don't need to use it, but
I find it nice to have that. That's the responsive screen. It just makes it a lot easier
to create a responsive game or screen with
things like a menu on the side but a big part of
the screen taken by something big, like the list of levels. Again, it's just style. It's just like widgets. And then there's
this SnackBar if you want to show a snack bar,
Again, just a little helper. And then the last
is the win screen. Let's win a game. So I need to drag
this over to here. And yay, I won. You won. The thing here is that
if you have ads enabled and the player hasn't bought
the ad removal in-app purchase, or there are no in-app
purchases in the game, then we can show a banner, and
that banner will automatically use the preloaded banner if
it's available from the ads controller. And then it just shows you won
and a few things like score and time, and that's it. You just go to continue. All right, that's the Dart code,
but let's have a look at assets as well. So assets in games are probably
more important than in apps even, because games are just
more artsy than most apps. So a good thing to look at is
obviously where to find assets. There are some included
in the template, but it's way less than
you will need in the end. You have some things
that I drew myself, like the back button,
the restart button, the settings button. There's the music, but only
three pretty short tracks. It's really good
music for mobile games because it sounds well even if
you're not using headphones. But again, you might want
to look at other places. And then there is
the sound effects, which you probably
almost definitely want to replace with something more
professional than my mouth. OK, so where do you find these? Well, there are a bunch of
places all over the internet that have assets. I will try to put all of these
into the description below, but I think there is a limit
to how many URLs there can be, so take care of what I show you. There's itch.io/game-assets. Many of these assets are free. Some of them are very,
very reasonably priced, like $2 or $5. Some of them are a
lot more expensive, but it depends on your
budget and how much you want to invest in
your game, obviously. So itch.io/game-assets. There is the
graphicriver/game-assets, which has a bunch more. And these are often
just PNG files that you go through and then
put into your asset directory and just use them in Flutter. So there's nothing here
that's not usable in Flutter. There is the Game Dev Market-- again, a bunch of assets
of different themes. There is opengameart.org,
which is, I think, all free and
all very like, licensed with public domain
and stuff like this. It was pretty hard to
find good assets here, but it's a good start. As you can see, I'm
talking about assets, and mostly we're
looking at pictures, but there's also music
and sound effects. So take care of that. Craft Picks is another one,
another asset store with pretty good assets that you can use. And then a Humble
Bundle often has bundles which are a
bunch of different asset packs put together with
a very reasonable price. And you actually give
some money to charity if you buy through
Humble Bundle. So this is just what's currently
available, but generally speaking, there's more. So Humble Bundle is nice. Then there's the
Art Breeder, which is nice because
it basically lets you combine a
picture and a photo into something
completely new using AI. And of course, then you can
create your assets of your own. Just know that it's really hard. It's really hard. It's good to start with
some kind of a palette, so I'm looking at lospec.com,
which has a bunch of palettes somewhere in your palette list. And if you go
there, you can just start with something that you
like here, and stick to this. Let's say you say, OK,
I want to use this. Stick to just these
colors, and then your game will look like it's a piece
of art instead of just everything is different, right? This is a big part of
making your game look like an artifact and not just
a bunch of different assets from different places. You can create your
sprites, especially if you're into pixelated
animation and stuff like this. This is Aseprite, which is for
free if you download and build it yourself, or you
can buy it if you want to support the developer
and also save some time, because you can then just
download it and not build it. And there are other things
like Affinity Photo, Affinity Designer, Photoshop,
and other software where you can, of course,
create your assets. Just know that when you
draw your own assets, you must have a lot of time
or you must make your art in the game very, very simple-- and often, both. You still need a lot of
time to make it simple. Think about Minecraft. That's very simple, right? Considering that it's a
3D game, the art there is simple and pixelated, but
it still took the creator a bunch of time, I'm sure,
to come up with all that and make it work nicely. If you don't want to
create your own assets, and if you still want to
have something more custom than what you will find
in the asset stores, you're going to have to
go shopping for an artist. And that is either you're very
lucky and you have a friend or, I don't know, a brother
or sister who can create art for you, which is fantastic. But if not, then you're going
to have to go to the market. And one way to go
to the market is to go to a place like
fiverr.com or upwork.com and just look at their talent. They have, often,
people who do art, and you for a very
reasonable price, you can ask them to
create some art for you. So that's Fiverr and Upwork. Of course, you can go
more into the art place, like, for example, ArtStation,
where you literally have people who create amazing art. And some of them
take commissions, which means you can ask them
to do something for them for money. And then there's the
Rive community, which is something also very similar. And it's not only static art,
but also actual animations. And the good news is that Rive
runs as a runtime in Flutter, so it's not a GIF or
anything like this, it's an actual canvas
animation that you can use. So that's that. Then you can just look into-- in my case, for my
award-winning game, I went and look into books,
and I found an amazing artist. And I just wrote them
an email and said, hey, do you want to maybe
make art for my game? And they said yes,
and that was amazing. But again, this takes time. It takes money. It's not easy. In short, assets are a big
part of game development, whether you like it or not. And so if you want your game
to have a lasting impact, and if you want people
to talk about your game and get back to
your game, you have to invest the time and sometimes
money to make the art good. And I'm not just
talking about the art as in the pixels on the screen,
but also the sound and also the music. So there you have it. The next time you have an
idea for a mobile game, you don't need to
start from scratch. You can start from the template. And you can get inspired
by the game sample. And your game will come with
all the bells and whistles that a "real game" should have,
like the main menu, settings, audio, and potentially
achievements, leaderboards, ads, in-app payments, and more. Look-- making games is hard,
but it's also rewarding. Games are experience machines. They are part of culture. They don't only entertain,
they can also have a message. They can teach us something. And I would love to see a
lot more games from a wider variety of game creators. And you may be one of them. Don't forget to check out the
Flutter Casual Games Toolkit at flutter.devs/games,
and good luck.