Tag Archives: code

Since my last post here, I've started working at a software start-up. Its product is a platform for educational iOS apps. As a result, I've gained a decent familiarity with objective C and Cocoa. I haven't had a chance to learn much ruby since starting there, but it's been a good experience for me on the whole.

Before I started I was working through a fantastic book by Topher Cyll, called Practical Ruby Projects: Ideas for the Eclectic Programmer. It's got all kinds of cool things from musical composition to genetic algorithms, and each chapter is genuinely interesting in its own right, independent from anything it teaches about Ruby.


Code starts at 2m27s, MacRuby part at 3m, and game at 9m40s

I should offer a warning, though. The publishers lied when it comes to the difficulty of the book! It's way the heck harder than a followup for "anyone who has mastered the basics of ruby". I attempted to tackle it right after an introductory book and it was very challenging. Progress was slow, and it's only the extreme interestingness of the material that has kept me going. I did parts of various chapters and the only one that I fully finished was for a text-based game he called Dinowars.

The next chapter in the book was on how to use RubyCocoa to make a native Mac front end for the game. Initially I skipped the chapter because I didn't have a Mac and I found all the Cocoa stuff alien. But when I returned to it last week, the material was much more understandable! I leapt in immediately! Not only that, but I decided to delve into MacRuby (an even more compelling technology for anyone who wants to use Ruby to build Cocoa), and I've successfully converted Dinowars' front-end to MacRuby.

This video goes over some MacRuby basics, includes a one page program that pops up a window and reads text with a voice synthesizer, and then shows the final result of my Dinowars port.


Resources

Planet cute tiles: Danc's Miraculously Flexible Game Prototyping Tiles
Source Code from the book publisher: Apress
My source code files: tryrc.rb, trymr.rb, trymr2.rb, and mrstrat.rb.

This is possibly the weirdest bug I've ever seen. My flash program was working fine from within the debugger and from Adobe's swf player built into Flash CS5. However, when I put the swf into a web page, I got errors from things not being deleted.

How I make sure everything is deleted

Basically, I've borrowed what I'm sure is a common method of keeping track of objects from the Kongregate tutorials. Whenever I create a sprite or a text object or whatever, I push the object to a list that's a class variable. Then when the objects need to be destroyed, it's easy to go through the list and find them all. One example of this was in my side-scrolling space ship shooter game. Every time the ship fired a bullet, the bullet sprite would get added to the screen and the bullet would be added to a list. When the main ship got destroyed, then a function would be called to go through the list of bullets and remove them from the screen, turn off their event listeners, etc...

The code that caused the weird bug (only on some platforms):

function destroy() {
	for (var i in components){
		components[i].destroy();
		delete components[i];
	}
	removeEventListener("enterFrame", enterFrame);
	if (this.parent && this.parent == stage) stage.removeChild(this);
}

The code that works everywhere:

	
function destroy() {
	for (var i = 0; i < components.length; i++){
		components[i].destroy();
		delete components[i];
	}
	removeEventListener("enterFrame", enterFrame);
	if (this.parent && this.parent == stage) stage.removeChild(this);
}

Huh? How the heck is one different from the other?

As far as I know, rotation in all the classic block games such as Tetris or Blockout was done in about the same way-- by storing an array of values for each of a block's possible states. For example, a long block in Tetris would have been stored something like the following. In its default rotation, it's horizontal with the following coordinates: [0,1], [1,1], [2,1],[3,1]. In after rotating clockwise, it's vertical like this: [1,0], [1,1], [2,1], [3,1]. The other two rotations would just be mirrors of the first two, so the entire block data could be stored in a multi-dimensional array like this:

rotation = 0;
L = [
  [[0,1],[1,1],[2,1],[3,1]], 
  [[1,0],[1,1],[2,1],[3,1]], 
  [[0,2],[1,2],[2,2],[3,2]], 
  [[2,0],[2,1],[2,2],[2,3]]
];

Then the variable L[rotation] would return the coordinates of L in whatever rotation it happened to be in (in this case [0,1], [1,1], [2,1],[3,1]). Rotating blocks is very quick this way, but the downside is that rotational info has to be manually inputted for each block.

Since the block game I'm making isn't just a tetris clone, and I want to have a shop in in where players can buy or maybe even design their own blocks, I've decided not to use this method. Instead of entering everything in by hand, I'll use math and let the computer do the work of coming up with the transformations for each block. 2D rotations are a simple 2x2 matrix multiplication, and 90 degree rotations are even easier than that. This way, each time a piece is rotated, the locations of its constituent tiles will be updated by a matrix multiplications.


Problems

1) Matrix multiplication is very slow compared to an array lookup. I can live with this. It's not 1985 and there will only be one block being rotated at a time anyway.

2) Repeatedly rotating a piece can make it drift until the coordinates of its squares are hugely positive or negative and further rotations just make it orbit around a point instead of just rotating in place. This was really annoying. I fixed it by making the 2,2 location the "center" of each piece, subtracting [2,2] from each tile's coordinates before doing rotation multiplications and then adding [2,2] back to them after the rotations. This fixed the problem with a couple of caveats. Pieces have to be created such that rotating around their 2,2 point looks reasonable, and...

3) Some pieces should just stay in place when they rotate. A 2x2 square is a perfect example. Yes, I could just have it rotate around whichever of its four tiles happened to be at location 2,2. It would look stupid, though. The center of the visible part of the piece isn't on any of its constituent tiles, it's between them. I solved this problem by allowing a property to the block class to state that rotations should do nothing. I'm not 100% satisfied with this, but off the top of my head I can't think of any pieces that must transform when rotating and yet would look bad rotating around one of their constituent tiles.

Here's my rotation function for my block class. Note that shape is an array in the block class that holds the locations of a block's tiles:

function rotate():Array {
  var xoff:int = 2; // every block will rotate around 2,2
  var yoff:int = 2;
  if (rotatable == false) return shape; //rotation doesn't change this shape

  var newshape:Array = new Array;

  for (var coord in shape) { //use 90 degree matrix rotation [0 -1]
                               //                              [1  0]
    newshape[coord] = new Array;
    newshape[coord][0] = (shape[coord][1] - xoff) * -1 + xoff;
    newshape[coord][1] = shape[coord][0];
  }
  return newshape;
}

I ran into some problems with handling the block sprites. I was basically just using the same built-in .x and .y properties built into flash sprites and adding some number to currentBlock.y in the event loop for gravity, and either adding or subtracting from currentBlock.x when a direction arrow was pressed. The problem with this was that it made collision detection a pain, and it also raised questions like what to do with a block partway between two square locations. In a game like Tetris or Blockout, the squares of the playing pieces have to fit into the squares on the board.

The way I decided to fix it was this-- give the block class getters and setters for x,y coordinates in terms of the grid of the playing area, not in terms of pixels on the screen. I made all animation in discrete 1 square increments and this also made collision detection much easier. Here's the top of my block class:

public class Block extends Sprite {
  static const size:int = Board.gridSize; //size of the board's "squares"
  static var list:Array = []; // for keeping track of all the blocks

  var edge_color:int;
  var inner_color:int;
  var shape:Array;
  var type:String;
  var _gy:int; // the y position in grid squares rather than pixels
  var _gx:int; // the x position in grid squares rather than pixels
  var _gwidth:int;
  var _gheight:int;

And the getters and setters for the grid variables:

function get gy():int {
  return ((this.y-Board.top)/ size);
}
function set gy(gy:int):void {
  this.y = gy * size + Board.top;
}
function get gx():int {
  return ((this.x)/ size);
}
function set gx(gx:int):void {
  this.x = gx * size;
}

With the convenience of these getters and setters, along with a few others for width and height of pieces in terms of grid squares, it made animation very easy. I didn't need to refer to x,y screen coordinates again in the block class and could just increment or decrement currentBlock.gx or currentBlock.gy by one to move a block one square. Collision detection was similarly simplified. Instead of needing to use hitTestObject() as I did in the other two games I've made, all that was necessary was 2 dimensional array of the playing grid, filled with zeros for empty locations and ones for locations with a block. Then, before moving a piece, I just check to see which grid locations the piece would occupy and not allow the move if they're occupied. For example, If I had a long thin piece occupying squares (5,4), (5,5), (5,6), and (5,7) then all I'd have to do is check (6,4), (6,5), (6,6) and (6,7). If all of them are unoccupied then I can increment currentBlock.gy and move the piece. Moving left, I'd check (4,4), (4,5), (4,6) and (4,7).

In retrospect it seems obvious that a grid representation would be the way to go for a game with pieces and a playing field broken up into discrete squares (or cubes for blockout), but it wasn't the first idea I had.