Some enemy concept art for our new game :D
(Source: trickybitstudios)
Projects have a funny way of changing on you.
A few of my previous posts were about code done in support of an isometric 2d RPG that a few friends and I were working on - complete with rich NPC interactions, procedural dungeon generation, and an interesting story. Then we realized that such a project would take too long to see completed and is probably above our skill level to do right, so we decided to work on a smaller, more focused game which I’ll talk more about later :D
Here’s where the screenshot comes in. We’re creating something that has a distinctly board-game feel, so I wanted to create an interesting game board. You often see isometric done with a grid of squares in isometric perspective, but you don’t often see a hexagonal isometric grid. It’s hexagonal because the game has tactical elements that are enhanced by six-directional movement. It’s isometric because, well let’s face it, isometric makes everything pretty.
Getting this board to work, with picking, in Javascript, was not easy. I don’t have a strong background in geometry or math, and the platform sort of rules out simpler picking techniques like Mouse Maps. Today, one of my teammates pointed out a really neat property about regular hexagons that are rotated by 45 degress. The image comes out to be a square, and as such can be divided into squares that can be used to measure distances relative to the hexagon. As you can probably tell, I’m not great at explaining math so I included a picture so you can see what I’m talking about.
Plus, if you squish the whole image by 50% (i.e. make it isometric), the ratios are the same (only your y length is now cut in half), so you can still do some reasoning with these interior units. So, here’s how I solved placing the tiles on the map:
UNIT_X = TILE_WIDTH / 8; UNIT_Y = TILE_HEIGHT / 8; // Calculate the X and Y position of the tile var x = i * 7 * UNIT_X - 200; var y = i * (-2) * UNIT_Y + 200; // Offset x and y based on the row number y += j * 5 * UNIT_Y; x += j * (5) * UNIT_X;
Pretty simple, actually. i and j are the intended grid positions of the tile, the magic numbers 7, 2 and 5 just describe the ratio of interior units between tiles to get the effect I want.
I’m still working the bugs out of picking, but it uses a method similar to the one described in Paul Firth’s article Isometric Coordinate Systems - The Modern Way. I’ll make a more detailed post about how the picking works when I’ve got it 100% working.
15 Minute Game Demo with Presentation (actual game footage starts at 3:28)
This is Broadsides, a 2-8 player game by Team Photon Monks. Made in 10 weeks for Geoff Voelker’s CSE 125 course at UC San Diego. The goal of this course is for teams of 5-8 student developers to create a real-time distributed multiplayer game.
Speaking on behalf of the team, this was a great experience and we’re very happy with how Broadsides turned out. We’ve made the game in full available for free through the game’s official website. Enjoy :)
I swear I’m going to be doing something more interesting than drawing, soon. Hopefully these posts will help someone else who’s having problems with drawing on canvases.
So, as you can see, I finally got our draw loop pretty well sorted. Our specification for the game changed somewhat considerably on Friday so some modifications to the code were required. I had put together a chunk system for the game so that we could procedurally generate maps of an arbitrary size, but our game is now going to be focusing on fixed-size maps, so I threw all of that code out.
Yesterday, I had the startling realization that on a given map of a fixed size, the map itself (i.e. the placement of the terrain tiles) only has to be drawn once, since the terrain isn’t going to change. So, in light of that, let’s just draw the map to a hidden canvas as we create it. Then, we just display the part of that canvas that’s visible to the camera every frame. One draw call, zero offscreen pixels. It seems so painfully simple in retrospect -_-
Anyway, next up is finishing up the map class and player movement.
After a few days spent putting together a new PC, I finally got a chance to sit down and code again. Here’s the latest version: http://gamedev.ucsd.edu/alex/iso/006/
It turns out that optimizing an HTML5 canvas for drawing a lot of tiles is a somewhat difficult problem. It’s not 100% there yet (a 1600x1200 canvas still slows down considerably), but it’s better. Here are two tricks I picked up on:
DON’T RENDER OFFSCREEN IMAGES: The most important thing is to not render any tiles or entities that can’t be seen by the viewport. This is easy for square tiles, but not so intuitive for isometric tiles. Here’s the code snippet (with debugging help from Aleks Palatnik) that I’m using to determine which tiles are on screen:
var numberOfTilesX = 48;
var numberOfTilesY = 30;
var centerPoint = camera.position.copy();
centerPoint.x += camera.width / 2;
centerPoint.y += camera.height / 2;
var center = screenToMapCoords(centerPoint.x, centerPoint.y, 0, 0, this);
for (a = (center.x + center.y) - numberOfTilesX / 2 ;
a < (center.x + center.y) + numberOfTilesX / 2; a++) {
for (b = (center.x - center.y) - numberOfTilesY / 2;
b < (center.x - center.y) + numberOfTilesY / 2; b++) {
if ((b & 1) != (a & 1)) continue;
x = Math.round((a + b) / 2);
y = Math.round((a - b) / 2);
if (map[x] !== undefined && map[x][y] !== undefined) {
drawableTiles.push(map[x][y]);
var objects = map[x][y].getObjects();
for (obj in objects) {
drawableObjects.push(objects[obj])
}
}
}
}
I haven’t worked on determining the number of tiles visible on screen yet, so I’m using a magic number for that. Credit for the loop goes to Jimmy from this stackexchange post:
http://gamedev.stackexchange.com/questions/25896/how-do-i-find-which-isometric-tiles-are-inside-the-cameras-current-view
I’m not sure I totally understand the geometry behind this, but it works very well for getting a rectangular area of tiles around a centerpoint.
DON’T DRAW AT SUB-PIXEL COORDINATES: HTML5 is really fancy. So fancy, in fact, that if you give it a point between two pixels to draw an image to (say, x: 16.65, y: 12.2), the canvas will antialias and interpolate your image’s position, resulting in a very smooth transition. Unfortunately, this antialiasing operation is expensive, and doing it hundreds of times per frame will result in lag.
So the trick is to round off any floating point values before you pass them to draw. You could use Math.round(), however apparently, this operation too is expensive. Seb Lee-Delise points out in his article HTML5 Canvas Sprite Optimization that using a bitwise hack is quite a bit faster than Math.round(), and there are plenty to choose from. I opted for bitwise or 0, which is the simplest to write.
So this week I learned a lot of fun things about HTML5 optimization, and how I could probably spend the next several months getting the fastest drawing times out of my existing code. While it’s an interesting problem, the game runs plenty fast as it is, so I think I’m going to try to get back to game logic.
i’ve linked this before, but if you are a programmer and you want to program games (or other things that are equally as complex + hazily-specified as games) you should maybe listen to this. it’s around 40~ minutes long, with 40~ more minutes of Q&A afterward.
It’s a really interesting talk about programming and designing any software, so every programmer may be able to get something out of this talk. If you’ve got some time, listen to it. It’s worth it.
For those of you who haven’t heard of Jonathan Blow, he’s the mastermind behind Braid. He’s also featured in Indie Game: The Movie, which is an excellent documentary about indie game development. Go see it!
Okay, I’ve made quite a bit of progress since the last update. Here’s the latest playable:
http://gamedev.ucsd.edu/alex/iso/005/
The biggest addition here is the ability to find tiles within the grid when given a point. All credit for the system goes to Paul Firth and the following article:
http://www.wildbunny.co.uk/blog/2011/03/27/isometric-coordinate-systems-the-modern-way/
The only thing I had to do in addition to the above was take the offset given by the camera into account when calculating tile positions, which is trivially accomplished by adding the camera’s x and y positions to the point that was clicked in screen-space.
The other big addition here is the depth-sorting performed on the trees. Originally, all tiles contained an array that stored the environment objects (trees) that were found in that tile. Unfortunately, without pointers, that makes depth-sorting pretty tricky. Instead, I changed things around so that the map stores an array of all environment objects, and each environment object knows its location in grid coordinates. Once that’s done, the program just performs merge sort on that array, sorting things by their y positions. The player is also registered with the map’s object array so that the player sprite gets sorted as well.
After all that I tried changing the map to a very large size (120x120 tiles) and immediately hit some performance issues due to tiles being drawn off-screen. I’m still in the process of optimizing it, but I’ve increased efficiency by 12% with this simple bit of code:
var startPoint = camera.position.copy(); startPoint.x += camera.width/2; startPoint.y -= camera.height/2; var endPoint = camera.position.copy(); endPoint.x += camera.width/2; endPoint.y += camera.height * 1.5;
Then I just find the tiles that startPoint and endPoint occupy and loop through the tiles in between. This creates some useen tiles at off the edges of the camera, so I’ve got to get rid of those. Also, all the trees are currently being drawn regardless of whether they’re on screen, so I need to fix that as well.
Whew. I’ve got to start posting more often, there was too much going on in this update.
Today is a day of both backward and forward progress.
The bad-good news: I reworked a lot of the original code I had laid down into much more sensible modules and patterns. This means I lost most gameplay functionality, but gained a lot of code flexibility. Here’s a quick rundown: A vector class and vector-based movement, camera, modules for image and input handling, and a type-hierarchy for entities (boy does Javascript make this a pain).
In the next few days I’m going to make the tile engine as slick as possible.
Since I never posted my progress from last week, here’s a game-development double feature:
Last week’s more feature-ly build:
http://gamedev.ucsd.edu/alex/iso/003/
Latest build:
http://gamedev.ucsd.edu/alex/iso/004/
I spent most of the day yesterday working on the trailer for Broadsides (trailer and exe coming VERY soon, I promise), so I didn’t get a lot of time to continue to work on our isometric game.
I also (mistakenly) thought that I could nail down some game design concepts and try to form a design document for this monster, but I learned that it’s just too early to do that. I’ve decided the best course of action right now is to prototype some things out, see what works, and incorporate those things as features moving forward.
Here’s the latest playable, there are trees and everything!
http://gamedev.ucsd.edu/alex/iso/002/
I’m trying to resist the urge to start refactoring. Despite how sweet Require.js makes things, my code’s starting to get a little messy. I’m going to power through though and keep adding features - I want to make a game, not refactor code all summer.
For the last five hours I’ve been ponderously deciding which flavor of version control to put on the VGDC server for the club to use next year. I set my sights on Git, Mercurial, Perforce and Subversion. We’re currently using Subversion (SVN) which has been sub-useful for a lot of the tasks we’re setting out to do. The Nextchess people have been complaining about it, and we had a pretty poor experience using it on Broadsides.
So, that leaves Mercurial, Git, and Perforce. Luckily, this question has been asked before, and I stumbled on this great overview of the topic:
http://gamedev.stackexchange.com/questions/480/version-control-for-game-development-issues-and-solutions
I was pretty interested in at least reading about Git this morning because it’s so popular, especially with the open-source community. Both Git and Mercurial are examples of distributed version control - that is, there’s no master record of your project - the entire project exists on every developer’s computer. For a huge project with gigabytes of assets, this would be pretty terrible, but it’s okay for our needs.
I gravitated toward Perforce at first due to its familiar client-server architecture and reputation as the industry standard in version control, but proprietary applications to access the server and a 20 developer limit pushed me back toward Git. If we were developing another 3D game, I’d be pushing for Perforce, but for a 2D game with rapid iteration and development, I wanted to go with Git.
The only problem I have with Git is that it’s a pain in the ass to add new users (rsa-id handshaking business and key generation is not something that happens easily on windows computers, which most of VGDC develops on.) If there’s a simple solution for either automating key authorization through a web interface, or allowing users to log into git through their shell accounts, I’d love to hear about it.