Sunday, October 25, 2015

Card Creator - Automating Card Creation in NodeJS

Card Creator is a little tool I've been working on for automating the creation of card graphics for a card game I'm working on (tentatively) called Sorcerer's Arena.  The purpose of the tool is to automate the combining of art and font assets from a set of JSON data into the image PNG assets needed to print the cards.

You can find the current version on GitHub and it works pretty well at the moment.  I need to find better images (not a phenomenal artist here) and I need to find some good fonts and backgrounds, but the program works.

Card Creator uses NodeJS and Node Canvas to render images in a canvas style way on the server.  I looked into a few alternatives like Graphics Magick  and Image Magick but neither were particularly suited to combining images easily in a layered format like the HTML5 Canvas is (and by extension Node Canvas, which uses Cairo).  By far, the installation and debugging of the setup of this project took the longest, so I'll go into the problems I came across and how I resolved them as well.

Setting It Up

First, this project was a pain in the arse to set up.  Cairo has many dependencies, all of which may or may not be working, and Node Canvas also has some issues as well with the latest versions.  First things first, you need Cairo.  My experience is with a Mac Book Pro, so it may differ if you have Windows or a Linux Distro.  

I ran into a few problems, namely I had issues with:
  • Multiple Installations of Cairo (Brew vs MacPorts, etc)
  • The Version of Cairo & Dependencies
  • The Version of Node Canvas
I created the project in NodeJS with the latest version of Node Canvas, Async and put a small sample together using a few image and some text.  I set up my Git repository and then began getting things set up with Cairo which Node Canvas relies on.

I had installed Cairo for Mac a long time before, but never got things working with it.  That was installed with Brew, and when I started this project, I tried a new installation method using MacPorts.  Using MacPorts only caused more issues, because some things were referencing the Brew installation and dependencies and some were referencing the MacPorts, and everything just broke.

So I uninstalled everything from MacPorts, and uninstalled everything Cairo related from Brew and tried the whole re-installation from Brew, because Brew tends to be pretty solid.  I made sure that I had all the dependencies I needed for Cairo and Brew would tell me which I did not have linked correctly.  After the MacPorts debacle, I had to manually link a few of the Brew libraries to get them all set up. 

It ended up working, and I could generate images with Node Canvas but I still had issues.  Namely, I could not get using Fonts to work correctly and could not get them to render correctly either.  As I dove into this issue, part of it turned out to be a known issue with the latest version of Cairo on Mac OSX Machines, so ultimately to get around it, I used Brew to install all of the dependencies that Cairo required, I then uninstalled Cairo from Brew since the only version available on Brew was 1.14.0 which did not work correctly.  I then reinstalled a 1.12.X version directly from a the Cairo Releases FTP which cleared up a few issues with the font sizing and not rendering correctly.

However, I STILL had issues.  I could not load TTF Fonts into Node Canvas, which is something it is supposed to support.  So I started looking into this and found that it was a recent issue in Node Canvas.  So after some searching and debugging, I blew away the node modules folder and instead of using Node Canvas version 1.2.11 (the latest version), I hard set the version to 1.1.0.  And FINALLY everything worked.  I was able to load in True Type Font files and display them on the cards.  Using old versions of both Cairo and Node Canvas.  

Just a side note:  All this debugging and set up took me longer than it took me to make the current version of the application (~7 commits in at the time of writing this).

So now I had everything working, I could position text and images, use fonts, and build cards to my specifications.  Now I could build my application.

The Application

The application is fairly simple, if you've ever worked with the HTML5 Canvas before, it should feel very similar.  First, the main entry point for the app is the creator.js file.

Creator.js uses the following workflow to generate card graphics:
  • First, load in the libraries, configs, and most importantly, the card sets in the /card_values directory.  This file is where all of the settings to generate each card are stored in JSON format.
  • Next, Iterate through each card from the combined JSON files and perform the following:
  • Create a canvas element with setting specified in a general config setting.
  • Load all of the True Type Fonts in with the font loader library.
  • Draw the Background of the card on the Canvas.
  • Draw the Icons for the element types and stats, (and if this is a Sorcerer card, the miniature icons).
  • Write the Description Text in the main box.
  • Write the Title of the Card at the top.
And that's the entire flow.  Unlike some of my other blog posts I won't go too into the code but that's essentially how it all works.  

What this allows me to do is make small changes in the main configuration file, changes images, add images, change text or descriptions and update cards and quickly generate the entire set and images in seconds.  This allows me to try different things, modify entire sets of cards and not worry about having to manually create the cards in Paint.NET (which is what I was doing before).

As it is now, the quality of the cards isn't up to par with creating them as pixel art in Paint.NET, but I'm working to improve that and make the quality better.  Primarily I need to find a good way to create bordered fonts that aren't see through on the inside which most tend to be that I've found.  This application also paves the way for getting an artist on the project later and being able to quickly create cards as new art assets come in.

The end result of this project is that I can take something like this:


And turn it into this (below) with just a single command (node creator.js).


So far the application has been very handy for prototyping, and all of the code is open source so if you like making card games, feel free to extend the project and build on it for your own game!

I'll be building more on the application as well.  A few features I would like to add in the future are:
  • Creating the JSON files directly from a CSV, Excel Document or Google Spreadsheets documents (since I normally work in Google Spreadsheets for building the cards).
  • Better Fonts and possibly auto loading & mapping all fonts in the fonts directory.
  • Images for the cards, currently none exist and none are attempted to be drawn in the creator code yet.
  • Legit background (other than some grey boxes and a yellowish background color).
So that's my Card Creator application, if you haven't used Node Canvas I encourage you to check it out, but definitely pay attention when you're setting it up and potentially just use older versions to get it working correctly.  Once it's all set up, Node Canvas is pretty fun and easy to use for building server side imaging applications.


See Post #2 on the Card Creator:  http://blog.wakeskaterstudio.com/2015/11/updates-to-card-creator-work-on.html

Monday, October 12, 2015

Making a Real Time Multiplayer Online Game in NodeJS (Part 3)

In post 1 and post 2 of this series, I introduced and talked through the server side code for a project I'm working on called QuadPong: a real time game made in NodeJS for the purpose of practicing server side programming and synchronization.  You can view the source code on my GitHub.

Now we're going to go over the client side and talk about the code and libraries used to talk to the server and generate our client side rendering of the game.  I'll cover in this post:

  • index.html & main.css - the HTML page the game is on and the CSS used
  • socket_handler.js - the client side socket layer which handles talking to the server
  • keypress.js & canvas_helper.js - two open source libraries/sections of code I used and why I picked them
  • game.js - the main logic of the game


Main Page

First there is the index.html page which contains the structure for the game.  It's very basic: at the top I included a few important scripts such as JQuery, keypress.js and our CSS file.


The game div will contain the canvas once it is initialized.  It's where the game plays out.

The buttons div is something I added to not have to add much in the way of UI to the game.  Ideally these commands would be a part of the in game logic, but for this quick prototype they work as a hack.

The players table also shows a list of the in game players so you can see who is in the game.  It's not pretty, but it's functional: it shows the player number, whether or not they are human or a computer player, how much life they have remaining in the game, and which player is you.

In the CSS file there are only a few sections dealing mostly with centering and background base colors.  It's very basic and I didn't spend a lot of time on the design for this project.

Socket Handler

Socket handler -- as you can probably deduce -- handles the socket connections with the server.  At the top of the file is the initialization of socket.io.  I then pull in the player and game ids if they exist in localStorage as well as initialize a few other variables used in the game.  Some of these could probably be moved into the game.js file as an improvement.

Below the variables are a few events to set up the initial game state.  When the socket.io connection connects for the first time, it will emit the data saved in localStorage.  This allows the server to reply with any updated information.  The connect event also requests for the game list to show how many open games there are.  One potential improvement here would be to make the server return information about each game in the list to allow users to join a specific game or spectate games.  

Next is the update function.  When the client receives information from the server, it updates the game objects, sets the player data, updates the player list table and then calls update on the Canvas to render out the new data.  These variables and the updateCanvas function are declared in game.js which we'll look at in a bit.

There are also quite a few other basic game control events, which I'll list quickly out here:
  • on reconnect - sets the game state and sets the game data appropriately
  • on started_game - sets the game state to started and saves any adjusted game & player data
  • on game_settings - sets the client settings to mirror the server settings
  • on created_game - enter the waiting state for other players to join & save game id
  • on joined_game - enter the waiting state and update the game settings with all of the player data of players in that game
  • on added_computer - adds a computer player to the player list
  • on added_player - adds a human player to the player list
  • on game_list - updates the game list & updates the canvas - this is the reflection back from the server when the client emits request_game_list
  • on sync_players - synchronizes the client side player list to the server list
  • on no_games - an error saying that no available games can be joined when a player tries to join and can't
  • on game_over - the event setting the end of game state and winner of the game
In addition to this long list of events to handle the game logic, I also have some helper functions to create games, join games, start games, update the player list, restart the client game (leaving the current room), and extract player_data.  Below you can see some of these functions.

Here is the save_data function which stores the information received from the server and stored in global objects into localStorage, add_player which adds a human player to the player table, and add_computer, which adds a computer player to the player table.  Again, it would have been wise to maintain a consistent function naming convention and if I refactored this portion I would use camelcase instead of underscore.


Create game sets the player number to 1, emits an event to the server to create a new game and set the caller as the main player, and adds a human player to the players table.  Join game emits an event to join a random open game with the player's stored id.


Start game emits an event to the server notifying it to start the specified game id and restart game is a client side function that resets the state of the client side, and leaves the game on the server side, so that the player can join a different game.

And at the bottom of our socket handler, is clear_player_list, which clears out the player table and extract_player_data which pulls player data from an object passed in. 

Another improvement I could make here is to pull out some of these helper functions into a separate library to keep this section of code dedicated to the socket handling only.  The player table stuff could really go in it's own file for maintenance as the code base grows.


Added Libraries and Code

Nearly all of the code in this project was written by me, but on the client side I used a few libraries I want to quickly note.  First I used JQuery, using it mostly for selectors in the socket handler script, but I also used two other more interesting sections of code.

KeyPress.JS
KeyPress is a library by dmauro that you can check out here:  https://dmauro.github.io/Keypress/.  I went with this library because it gave me a lot of flexibility with key events and let me bind and chain them in various ways.
In the section of code to the right, you can see a portion of the key registration.  KeyPress lets you register combinations for on key up and on key down events.  Since the player can be any one of the four players in the game, I wanted to map each direction in a specific way.  For instance, if the player is player number 4, on the left side of the game, pressing would move them up, but would translate to no action for players in slots 1 and 3.  

On the server, paddles only have 2 movement options, positive motion "R" (to the right facing the game) and negative motion "L".  So for player 4 would translate to a negative motion, whereas for player 2, translates to a positive motion.  I decided to handle this logic on the client side, for simplicities sake on the server, so when a key press event is generated, it calls out to a separate function, playerActionGenerator with a character representing the key press event.   corresponds to a "none" event or setting the player action to none.   

The playerActionGenerator function below handles these various directions.  First it checks if the state is in an active state, or a menu state which handles these events differently (for selecting menu items), and if it is in a game, it checks the player number and generates the appropriate action based on the direction.

The function playerAction then takes the direction passed in and builds the package to emit to the server to update the action on the server's copy of the player.

KeyPress ended up being pretty easy to use and it's pretty flexible on what it's capable of so I highly recommend using it.  I haven't even used most of the chainability of buttons and combinations that are possible.

In addition to KeyPress and JQuery, I used a snippet of code from a Stack Overflow post.  It sets the canvas DPI correctly so that text and objects render crisply instead of blurry and then creates and returns the Hi DPI canvas.  It works very well and I haven't had to modify it at all.


Game.JS

Finally let's look at the last portion of the client side game code in game.js.  This file also contains the KeyPress handling as well but as we've already gone over that I'll skip over those sections.

At the top of the file are a bunch of variables and declarations to set up the game state.  Most of what we'll look at here can be summed up as:  game.js draws on the canvas based on which state the game is in.  If the game is playing or in a "GAME" state, it will use the game objects which get updated by pushes from the server to draw the game objects, otherwise it will draw various menus and text.

First we have the onload function for the window, where we create the Hi DPI canvas, append it to the game div and register our KeyPress handler.


The entry point for most actions in this script is updateCanvas, seen below.  Any time actions from the server need to update the UI, updateCanvas is called.  Depending on the state of the game, one of the many draw functions will be called.  


Many of these functions simply draw text in various places in the game, but we'll look specifically at drawPlayers which is called in drawGame on line 109.  Now, the way I built the server coordinate system was a traditional Cartesian Coordinate System, with up being the positive Y direction and right being the positive X axis, with (0,0) at the center of the game.  Canvas, however uses the top left corner as (0,0) with down being the positive Y direction, so I made the convert function to simply convert the server system to the client one, including a size multiplier to scale the graphics.

The drawPlayers function uses convert to iterate through all four players and update the rectangle based on the bounding box coordinates received from the server.



Drawing the ball works very similarly and the rest of the draw functions simply are used for menus and text, which should be easy to understand and read through.  That's pretty much the entirety of the client side, it's fairly simple as it's mostly just getting positional updates from the server, sending player action updates to the server, and rendering the game out for the player to see.  Plus all of the objects in the game are basic geometric shapes.

So feel free to pull down the source code and play with it and extend the game or use it to make your own game.  You can view it all on my GitHub here: https://github.com/WakeskaterX/QuadPong

Thanks for reading and that's all I have!  The three part series on my little NodeJS side project is concluded.