Unity, as a game engine, is going downhill.
New features are getting locked behind paywalls,
even for pro members, and each update just brings more bugs and division into
the engine. So, as a professional game developer, I knew I had to switch to a
far more capable game engine: Unrea— Discord. Okay, so I don't actually dislike Unity, and
Discord's not actually a game engine, but that doesn't mean you can't make
games with it. If you don't know, Discord has an API that allows you to create
bots that can take Discord functions, like messages and reactions, as input, and
return Discord functions as output. I was thinking about what I could create with
this that hadn't been done yet, and then it hit me. Just about any game that could
be played in a terminal could be made into a Discord bot. And, a few months ago
for my final project for my AP Computer Science class, I actually made a game
that's designed to be played in a Java console. It's a clone of a really fun
puzzle game called Sokoban where you have to push boxes around to get them to
certain destinations. I made it in class in a few days, so it's pretty simple, but
it's surprisingly pretty fun. It has infinite levels with somewhat of a
difficulty curve. And, to control the game you just type commands, so I decided I
would try to turn it into a Discord bot using Discord's API. And, to make
things more interesting, I gave myself only 24 hours to do it, since... you know,
it's not a gamedev video without an arbitrary deadline. The code is written
in Java, so I needed a way to interact with Discord's Bot API in Java. I found
a really helpful wrapper library to do this called JDA, which literally just
stands for Java Discord API, and with that it was time to get started. Before I
could make the game work with Discord I had to... you know, learn how to use the API
with JDA. I found two resources that were pretty helpful: an article by Oliy
Barrett that goes over the basics of setting up and running a bot, as well as
somewhat outdated but more in-depth video series by techtoolbox. I'll leave
a link to both of these in the description. And, after a little while, I
had set up a bot that could listen for commands and return messages. Basically, with JDA you can create methods that run when certain events happen, such as the
bot receiving a message. Then, within the method you can access the content of the
message, which means you can check if the message contains a certain keyword or
command, and do something based on that. And this was pretty much all I needed to
start working on the actual game. So, I started off by copying all the classes
from my Sokoban game into my Discord bot project. None of the game logic needed to
be changed, but obviously I did need to change the way the game handled user
input and returned the updated grid. The original game was
constantly running but stopped at certain points to wait for the user to
type a valid command. But, for the Discord bot, since I'll probably have multiple
instances of the game running at once, having each one constantly running and
waiting for user input didn't seem like the best approach. So, basically, I turned
my game's update loop into a method that would run only when a command is
received that starts with a certain prefix, which for me is just an
exclamation point, and the command you used is pass into the method in a string
called userInput, and the game's existing code responds to it accordingly.
Then, instead of printing the resulting grid and game information to the console,
I send it as a Discord message in the channel that the bot received the
command in. So now it should be possible to play the game. Just type the play
command, and— Okay, so the problem is that my code returns unicode characters that my compiler isn't encoding properly. I tried changing my IDE's encoding
settings, but I couldn't get anything to work, so I decided to just ignore the
problem and use Discord's emoji tags instead of unicode characters. This is
when I made the impulse decision that probably ended up being the best design
choice for this project: making the player character a flushed emoji. Anyways,
now the game works pretty well. All of the commands work properly and you can
play the game without any issues, but there are obviously improvements that
can be made. For starters, typing in long commands with prefixes to do something
as simple as moving makes the game slow and tedious to play. Since I was already
planning on adding reaction controls to the game, I figured implementing that
would just fix the issue. The way reaction commands work is that, as soon
as a bot sends a message, it adds certain reactions to it. And, at the same time, the
bot is also listening for when a user adds a reaction, and will perform a
certain action based on that. So, from the user's perspective, all they're doing is
pressing a button to make something happen,
which is much more convenient than typing a command. So, in my code, whenever
the bot receives a message sent by itself,
it adds four reactions to it: left, right, up, and down arrows, and this reset button.
Then, when the bot receives a reaction from the user, it creates a new empty
string, sets the value of the string to the text command associated with the
reaction, and calls the game method with the newly created string. Everything is
obviously going to work fine on the first try, but let's test it just to make sure. Oh fri*k... I forgot to check who was adding the reaction before starting the command, so the bot was responding to its own reactions, which creates an
infinite loop. But, after fixing that and also doing some unrelated unicode
character debugging, you could now control the game with reaction commands. I also
made the walls purple... if you didn't notice... But there are two problems with
the current reaction system that need to be fixed. The bot adds reactions to every
message it receives when they only need to be on the message that has the updated
game information. And, it seems like reactions are being slowly added to the
message one by one, so hopefully there's a way to speed up the process. To fix the
first issue, I store the content of the message in a string called gameText
right before sending it, and when a new message is received
I check if the sender is a bot and if the contents of the message are equal to gameText. Now the reactions are only added to the correct messages, but sadly there
wasn't really a solution for the second issue. I don't think there's a way of
asynchronously adding reactions to a message with JDA, so you kind of just
have to add reactions one by one. So, I kept reaction commands as an option, but
I still wanted there to be a faster, more natural way of controlling the game.
Something the original game included to make playing feel more natural was the
ability to control the player with WASD commands, but, in the Discord bot, this
feature is kind of made redundant, since each command starts with a prefix. So, I
decided to keep prefixes for commands like !play and !stop, but get rid of
prefixes on the actual gameplay commands that the user will be frequently
inputting. But right now the game only checks user input and is updated when
the bot receives a message that starts with its prefix. I could just make the
game check input and update every time the bot receives any message but that's
not only unperformant, but also unnecessary, and probably the worst idea
I've ever heard. So, I decided to create an ArrayList of strings of all valid
commands and only update the game loop if the first word of a user's message is
a command in the ArrayList. Now you can control the game by just typing WASD or
R, which is definitely the most natural way to play... well, if you're a gamer, at
least. Now the gameplay is good but look how UGLY this is! Like, no offense or
anything, but this bright white background and basic text formatting is
not acceptable. Ew! So, the first thing I did to improve
the visuals was make the bot return embeds, instead of normal disgusting
messages. If you don't know what an embed is, it's just a nicely formatted message
that can be sent by Discord bots that can have a title, text fields, and lots of
other cool stuff. So, I made a method that takes a title. description. and footer as
input. builds it into a JDA embed, and sends it as a message. And then, in my game
class's update method, instead of sending a big string with the current level,
grid, and controls prompt as a message, I call my sendEmbed() method with the
level as the title, the grid as the description, and the controls as the
footer. And now things are already looking a lot better. I also created an
embed that's sent after completing a level. But, this background is still ugly, so I
made it black. I also made the color of the walls random, with a new color being
chosen after each level. Now, if I do say so myself, the game is looking pretty
epic now. But I think it's time to get rid of the default skin. I mean, I mean—
DEFAULT PROFILE PICTURE! So, I made an icon for the bot in GIMP. I think it turned out pretty well. Now, we're pretty much done, but there's one
last thing I want to add to the bot. Right now, if multiple users try to use
the bot, they each control the same game, since the bot applies the commands to a
single instance of the game. Since my game is a class though, I can just create
multiple instances of the game and use HashMaps to link them to users. So, I
created a HashMap, which is just like a dictionary in something like C# or
Python, that maps users to games. Then, I got rid of my singular game object, and,
instead, whenever a user calls a command, I create a new game and map it to the
user, if they don't already have one, and instead of calling methods on the old,
single, game object, I call them on the game mapped to the user who sent the
command. And now, just like that, multiple users can use the bot at once
without interfering with one another. HashMaps are so OP. But, since the current color is stored in the bot object, rather than each game object, the color of the game is the same for all users, but this wasn't a big problem. To
fix it, I just had to change the way color was stored and assigned.
It did lead to this weird bug where the walls turned into flushed emojis. I think
I fixed it, but if this happens to you it's definitely an intended feature. It's
like a little quirky Easter egg. But now, at a little before 2 AM, Sokobot was
finished. I guess the 24ihour deadline didn't really matter. I did add a few
things to the bot the next morning, after the 24 hours had passed, like this !info
command that tells you how to play and some other details about the bot, as well
as making my API token read from an external text file so I can distribute
the bot without it getting taken over by edgy hackers. But, those things are
unrelated to the actual game, so I guess it's okay. If you want to test out the bot
or add it to your server, a link to its GitHub repository will be in the
description. To use it, just download the latest .jar file, create a bot on
Discord's Developer Portal, then copy the bot token and paste it into a file named
token.txt in the same directory as the .jar. Then, just run the .jar file and,
if everything works correctly, the bot should turn online. If enough people are
interested I may host the bot online with a service like Heroku in the future,
so you can add it to your server in one click, without having to host it yourself.
So yeah, that's everything. I hope you enjoyed watching me try to use Discord
as a game engine. As always, if you have any feedback or suggestions for future
videos, feel free to leave a comment. And, we hit 1,000 subs a few days ago,
which is pretty cool, so I just want to say thanks to everyone who's subscribed
and watches my videos. Anyways, thanks so much for watching, and I'll see you in
the next video.