I'm repurposing this neglected blog to act as a record of my journey into game development.
Tuesday, September 06, 2011
Again
Adios
Friday, March 25, 2011
Breakout Week 4: Building a Wall (Part 2)
With the wall in place, the next step is to make the ball break the bricks as it hits the wall. This bring us back to collision detection and response again.
Monday, March 21, 2011
Breakout Week 4: Building a Wall (Part 1)
Because there is a lot to go through with the collision handling and I also want to cover how I setup and draw the wall I'm going to split this week's post in two. This post will cover the setup and drawing of the wall and it should be relatively short. I'll write another post around mid this week where I'll cover handling the collision of the ball against the wall, the problems with my approach and how I'm going to improve it. So for now, lets get stuck into the setup of the wall.
As usual, we have a Wall class that handles the setup and drawing of the wall. Just like the paddle and ball it is created by the Breakout class. Here are the main instance variables of the wall.
class Wall {
private const int bricksPerRow = 20;
private const double wallHeightRatio = 0.20;
private const int numRows = 7;
private static readonly Color[] colors = {
Color.Red, Color.Orange, Color.Yellow, Color.Green,
Color.Blue, Color.Indigo, Color.Violet
};
private readonly int screenWidth;
private readonly int screenHeight;
private float brickHeight;
private float brickWidth;
private Rectangle wallBounds;
private Texture2D sprite;
private List<Brick> bricks = new List<Brick>();
Saturday, March 12, 2011
Breakout Week 3: When Balls Collide
So, research aside, the coding up of the ball itself was very easy, it only took about an hour and half and most of that was spent tweaking various parameters to get the "feel" right. So on to the code.
As with the paddle, there is a class for the Ball with LoadContent, Update and Draw methods that are called by the Breakout class. The Ball tracks it's own state in these instance variables:
private Texture 2D sprite; private Vector2 position; private Vector2 direction; private int speed;
This is slightly different to the paddle in that we store the speed as a scalar value and the direction as a vector. The direction is a unit vector so it contains no speed information, which is why we need the speed as a separate variable. The reason I did it this way is that the ball can move in both x and y directions, unlike the paddle, so using a unit vector allows use to calculate directional changes for the ball without needing to worry about the length of the vector, since it will always be 1.
The ball also has a few other instance variables that hold initial states, ranges of speed and position and whether the ball is currently active or dead.
So, once again, all the interesting stuff happens in the Update method:
internal void Update(GameTime gameTime) {
if (state == State.Active) {
UpdatePosition(gameTime);
if (position.Y > screenHeight) {
state = State.Dead;
} else {
HandleCollisions();
}
} else if (state == State.Dead &&
Keyboard.GetState().IsKeyDown(Keys.Space)) {
LaunchBall();
}
}
Friday, March 04, 2011
Week 2: With a Paddle
I decided that the goal for this week was to get some initial classes laid out for the Breakout game and implement the basics of the Paddle. Let's start with the class design.
When you create an XNA project, it auto-generates a Game class for you. This class is the core of your Game. In it you implement methods for loading content, updating state and drawing a frame. However, I don't think it is the best place to put much game logic. I've always tried to avoid pointing too much custom code into classes that extend from framework classes since it makes it harder to test your own code in isolation and it means large amounts of you own code could break if the framework changes. The Game class will act mainly as glue between my game implementation and the XNA framework's game loop. To this end, I created a Breakout class which is instantiated by the generated Game class. Here is what it looks like:
public class Breakout {
private SpriteBatch spriteBatch;
private Paddle paddle;
private Ball ball;
private Wall wall;
private int screenHeight;
private int screenWidth;
public Breakout(SpriteBatch spriteBatch) {
this.spriteBatch = spriteBatch;
this.screenWidth = spriteBatch.GraphicsDevice.Viewport.Width;
this.screenHeight = spriteBatch.GraphicsDevice.Viewport.Height;
this.ball = new Ball();
this.paddle = new Paddle(screenWidth, screenHeight);
this.wall = new Wall();
}
}
It's pretty straight forward, Breakout has a Paddle, a Ball and a Wall, just as you would expect. The Breakout constructor is passed in a SpriteBatch instance which it will use to draw all the graphics for the game. The SpriteBatch is created in the Game class's Initialize method, where the Breakout object is also created, like so:
Friday, February 25, 2011
Week 1: Setting up and Installeering
First of all, I grabbed a copy of Windows 7 professional and installed that into a Bootcamp partition on my iMac. This didn't exactly go without a hitch, Windows 7 doesn't seem to include the right ATI driver for the graphics card in the late 2009 iMacs. So when you get to the third stage of the installation, the screen goes blank... not very helpful. Thankfully there is a pretty simple work around that involves booting up in repair mode and deleting the ATI driver. This forces Windows to use a generic driver, but one that actually works. So with that done I could complete the installation and then install the boot camp drivers to get a proper ATI driver for the graphics card.
From there it was a matter of installing some development tools. I got Visual Studio Express Edition with Windows Phone Developer Tools, which in fact contains XNA Game Studio, not that you would know by the product name. The install for this went without a hitch. I also installed Git Extensions, however for some reason the Visual Studio plugin didn't work, I'm not too fussed about that though since I'll probably use the command line most of the time.
With all that setup, I fired up Visual Studio and created a new Windows game project, called Breakout, that I'll use for building my first game. This was simple enough but there was one hitch, I wasn't able to run the game within Parallels, I got an error saying there was no suitable device support HiDef. Fortunately the Internet came to the rescue again. Apparently this is caused by Parallels only supporting DirectX 9. The fix is pretty simple, you just need to right click on the Game project, select Properties, then change the Game Profile to "Use Reach ...", the game should now run and display a beautiful cornflower blue screen.
I also tried out VMWare Fusion as an alternative to Parallels, it also suffered from the same HiDef support problem, so there wasn't really any reason to switch. I'll see how it goes with Parallels for while, I do get the sense that I'm eventually either going to need more RAM, or I'm going to have to reboot into Windows more often, but I'll see how it goes.
---
In order to get up to speed with C# I read through Intro to C# from Objective-C and Java. I've gotta say, it's not the best document. I realise it is only 24 pages, but it seems to really skim over important language features at the expensive of talking about the CLR platform. For example, it has a list of about 15 items that Java has but are different in C# or that C# has but Java doesn't, like indexers, different inheritance syntax, access restrictions on overriden members, clashing interface inheritance but it doesn't actually discuss how C# does it. And yet there is a page and half on garbage collection, which is essentially the same between the two platforms. Oh well, I'm sure I cope.
---
I've also setup a Github project at http://github.com/seangeo/breakout. It's a public project so anyone can grab the code, although at this stage there isn't much to see, hopefully it will get more interesting.
Next week I'm going to start getting familiar with the XNA API and draw some sprites. If I get time I might make them move across the screen!
Saturday, February 19, 2011
Refurbished and Repurposed
I've always loved video games and they have always been a big part of my life. When I decided to to return to university after a brief stint as a sound engineer, my main motivation for doing computer science was to end up as a game programmer. I even dabbled in building a MMORPG in my second and third years. However, at some point I took a different direction. I don't really know why, probably a combination of factors: a lack of local jobs and not wanting to move state again, the idea of another industry being the industry that the best people work in (at least in Adelaide) and some sense that it was time for me to grow up and act like an adult; probably why I cut my hair and started wearing button up shirts.
It's just over 8 years since I graduated. I've never been short of work and I've had some great jobs with some great people. However, I still have a nagging regret when it comes to game programming. I keep wondering what would have happened had I kept my sights on that goal and worked towards it. And most of all, game development is still something I really want to do. Basically I don't want to be in the position 8 years from now where I'm wondering the same thing, only it would be even harder to do something about it.
So, this marks a turning point, my hair is long again and I prefer t-shirts.
To have any chance at a game development career, I need to learn game programming techniques, libraries and platforms, because at the moment, my game development knowledge is pretty minimal, I've spent all my time doing business/commercial development. It's not going to be easy or quick and I have a lot to learn, but really I have nothing to lose. I still need to work and make a living so I need something I can do in my spare time and with a plan that keeps me focussed. I'm going to put about 3 hours a week into building small tech demos or games that will teach me something about game programming. I'll also write a weekly blog post that outlines what I did, what I learnt and what I plan to do next week. If I can stick to this I should learn a decent amount in a reasonable amount of time, but most importantly have a set of demos for a portfolio and a journal to go with it. And from there, we'll have to see.
Which brings me to the first little project. I've spent a bit of time reading and thinking about where to start, it does make sense to use Windows for the project initially though, even though I normally use a Mac and have an iPhone I could develop for, however, the games I would want to work on are PC or console games and most game jobs require C/C++ on Windows, so Windows just makes sense. I don't want to get overwhelmed and discouraged so starting reasonably small would be a good idea, which makes me think starting with XNA Game Studio is a good way to get my feet wet with XBox 360 and Windows development, rather than jumping into the deep-end with DirectX. Even if it is C# as opposed to C/C++, I'm not too worried about that just yet.
I'm going to start by developing a Breakout clone for Windows. This fits the reasonably small criteria but should cover a enough new areas that it should be interesting. I expect to learn about at the least following:
- Setup of development environment and building and running a Windows game.
- Simple 2D graphics.
- Simple UI components.
- Simple 2D vector math, trigonometry and animation of the ball and paddle.
- Timing of animation.
- Keyboard input.
- Collision detection.
- Simple audio playback.
- Simple particle effects.
Saturday, May 03, 2008
rAtom 0.3.5 released
== 0.3.5 2008-05-03
* Make sure atom:entries appears last.
* Better examples in the documentation.
* Gave Feeds authors and contributors.
* Fixed a couple warnings.
Monday, April 14, 2008
Using rAtom in Rails
class Post < ActiveRecord::Base
def to_atom
Atom::Entry.new do |entry|
entry.title = self.title
entry.updated = self.updated_at
entry.published = self.created_at
entry.author = Atom::Person.new(:name => self.author)
entry.links << Atom::Link.new(:rel => 'alternate',
:href => "/posts/#{self.id}")
entry.content = Atom::Content::Html.new(self.content)
end
end
endThe advantage of returning a rAtom object here is that the atom representation is then composable, for example if you have a Blog class that has many posts you can do this:
class Blog < ActiveRecord::Base
has_many :posts
def to_atom
Atom::Feed.new do |feed|
feed.title = self.title
feed.links << Atom::Link.new(:rel => 'alternate',
:href => "/blogs/#{self.id}")
self.posts.each do |post|
feed.entries << post.to_atom
end
end
end
endTo get this atom representation out via HTTP, you would do this in the controller:
class BlogsController << ApplicationController
def show
@blog = Blog.find(params[:id])
respond_to do |format|
format.atom { render :xml => @blog.to_atom.to_xml }
end
end
endThis has been really useful in an application that I am working on because we are using Atom as the main communication format between a bunch of different components, so this composability comes in really handy. For a simple blog it might be overkill, but at least you get the much better performance of libxml-ruby.
You'll probably notice a bit of ugliness in the to_atom methods, specifically this:
entry.links << Atom::Link.new(:rel => 'alternate',
:href => "/posts/#{self.id}")The URL is hard-coded here because url_for is not available in a model. This is a bit of trade-off between the composability and strict MVC here, if you really wanted to you could pass the URL in as a parameter, or add a block that generates URLs or whatever, it's up to you.
For some applications, like a simple blog, the composability might not be too important, and you might prefer the syntax of atom_feed in Rails, or at least want to be able to render the atom in a view. In this case it would probably be nice to have a rAtom based template format, so you could add a view like "blogs/show.atom.ratom" which allowed you to move the to_atom method into a view and call url_for directly from the view. I'd like to add this at some stage, but it hasn't happened yet, due to time and priorities, but if anyone would like to submit a patch for it I'd gladly accept it.
Tuesday, April 01, 2008
rAtom 0.3.0 Released
I've just release rAtom 0.3.0. This version adds support for simple extension elements and also checks that content is in UTF-8 before serializing to XML.
As defined in the Atom Syndication Format, simple extension elements consist of XML elements from a non-Atom namespace that have no attributes or child elements, i.e. they are empty or only contain text content. These elements are treated as a name value pair where the element namespace and local name make up the key and the content of the element is the value, empty elements will be treated as an empty string.
To access extension elements use the [] method on the Feed or Entry. For example, if we are parsing the follow Atom document with extensions:
<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:ex="http://example.org">
<title>Feed with extensions</title>
<ex:myelement>Something important</ex:myelement>
</feed>
We could then access the extension element on the feed using:
> feed["http://example.org", "myelement"]
=> ["Something important"]
Note that the return value is an array. This is because XML allows multiple instances of the element.
To set an extension element you append to the array:
> feed['http://example.org', 'myelement'] << 'Something less important'
=> ["Something important", "Something less important"]You can then call to_xml and rAtom will serialize the extension elements into xml.
> puts feed.to_xml
<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<myelement xmlns="http://example.org">Something important</myelement>
<myelement xmlns="http://example.org">Something less important</myelement>
</feed>
Notice that the output repeats the xmlns attribute for each of the extensions, this is semantically the same the input XML, just a bit ugly. It seems to be a limitation of the libxml-Ruby API. But if anyone knows a work around I'd gladly accept a patch (or even advice).
You can get rAtom via gem:
> gem install ratom
or from Github.
Saturday, March 08, 2008
rAtom 0.2.1 Gem released
rAtom was originally built to support the communication between a number of applications built by Peerworks[http://peerworks.org], via the Atom Publishing protocol. However, it supports, or aims to support, all the Atom Syndication Format and Publication Protocol and can be used to access Atom feeds or to script publishing entries to a blog supporting APP.
Features:
- Uses libxml-ruby so it is _much_ faster than a REXML based library.
- Uses the libxml pull parser so it has much lighter memory usage.
- Supports RFC 5005 (http://www.ietf.org/rfc/rfc5005.txt) for feed pagination.
You can install via gem using:
# sudo gem install ratomUsage
To fetch and parse an Atom Feed you can simply:feed = Atom::Feed.load_feed(URI.parse("http://example.com/feed.atom"))And then iterate over the entries in the feed using:
feed.each_entry do |entry|
# do cool stuff
endTo construct a Feed
feed = Atom::Feed.new do |feed|
feed.title = "My Cool Feed"
feed.id = "http://example.com/my_feed.atom"
feed.updated = Time.now
end
To output a Feed as XML use to_xml
> puts feed.to_xml
<feed xmlns="http://www.w3.org/2005/Atom">
<title>My Cool Feed</title>
<id>http://example.com/my_feed.atom</id>
<updated>2008-03-03T23:19:44+10:30</updated>
</feed>
Publishing
To publish to a remote feed using the Atom Publishing Protocol, first you need to create a collection to publish to:collection = Atom::Pub::Collection.new(:href => 'http://example.org/myblog')Then create a new entry:
entry = Atom::Entry.new do |entry|
entry.title = "I have discovered rAtom"
entry.authors << name =""> 'A happy developer')
entry.updated = Time.now
entry.id = "http://example.org/myblog/newpost"
entry.content = Atom::Content::Html.new("<p>rAtom lets me post to my blog using Ruby, how cool!</p>")
end
And publish it to the Collection:
published_entry = collection.publish(entry)More Information
See http://ratom.rubyforge.org for more information.Wednesday, September 26, 2007
Building Erlang with Rake
In the book Joe describes building an Erlang project using make and most of the downloadable bits of Erlang code use make too; this makes sense, make is virtually ubiquitous. However the other components of this project are Ruby on Rails applications and make heavy use of Rake, so I figure why subject myself to Make when I can use the beautiful Rake.
Here is my Rakefile for building an Erlang project.
require 'rake/clean'
INCLUDE = "include"
ERLC_FLAGS = "-I#{INCLUDE} +warn_unused_vars +warn_unused_import"
SRC = FileList['src/*.erl']
OBJ = SRC.pathmap("%{src,ebin}X.beam")
CLEAN.include("ebin/*.beam")
directory 'ebin'
rule ".beam" => ["%{ebin,src}X.erl"] do |t|
sh "erlc -pa ebin -W #{ERLC_FLAGS} -o ebin #{t.source}"
end
task :compile => ['ebin'] + OBJ
task :default => :compile
This gives you
rake compile which builds all the .erl files in the src subdirectory into the .beam files in the ebin directory. Files will only be built if they have changes since the last build. You also get rake clean which deletes all the .beam files from the ebin directory.I much prefer this over the equivalent Makefile and it is much easier to extend since you have the full power of Ruby at your disposal.
Enjoy.
Update: Fixed bug when compile many files from after clean.
Monday, September 03, 2007
Testing ActiveResource
One of the difficult parts of using ActiveResource is testing. You don't want to run it over a real HTTP connection, for one it would significantly slow down your tests and secondly you don't know what horrible side effects it might have. So you need some way to fake a HTTP connection and HTTP requests and responses. Enter the HttpMock class.
HttpMock is a fairly nice class put together by the Rails folk that allows you to register a bunch of request and response pairs for a fake HTTP connection. When you call a method on an ActiveResource in a test, it calls the HttpMock class instead of trying to create a real connection. It is fairly easy to use, although there is no documentation the blogging community has come to the rescue.
Here is a quick example:
@matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/people/1.xml", {}, @matz
endThis creates a request response pair so that when ActiveResource tries to GET /people/1.xml it gets back the xml for the @matz hash. Pretty simple.
However there is a problem. HttpMock is misnamed - HttpMock is a stub not a mock. But the difference is more than semantic, a mock object, like those provided by the excellent Mocha library, will allow you to set expectations on a object. An expectation is like saying "for this test to pass, this method with these arguments must be called on this object". HttpMock doesn't do that, instead it is just a stub that returns a value when a method is called with a certain argument, there is no verification that the method is actually called.
To show why this is bad, lets look at the test for resource deletion from the ActiveResource unit tests themselves.
Firstly, in the setup method we use HttpMock to create response for the delete method used on the "/people/1.xml" path:
ActiveResource::HttpMock.respond_to do |mock|
mock.delete "/people/1.xml", {}, nil, 200
endAnd here is the test_delete method:
1 def test_delete
2 assert Person.delete(1)
3 ActiveResource::HttpMock.respond_to do |mock|
4 mock.get "/people/1.xml", {}, nil, 404
5 end
6 assert_raises(ActiveResource::ResourceNotFound) { Person.find(1) }
7 end
So what is happening here? Firstly on line 2 of the test_delete method we call the Person classes delete method. The delete method is a nice one-liner:
def delete(id, options = {})
connection.delete(element_path(id, options))
endWe can assume here that the the content of the delete method is sending a delete request to "/people/#{id}.xml", that makes sense. The rest of the test_delete method creates a request response pair that returns a 404 error when "/people/1.xml" is requested, all that does is test that ActiveResource::Base.find correctly handles 404.
So what ensures that the delete method does the right thing, all the test does is ensure that it returns something other than nil or false. Lets change the method and see if we can break the test. We'll just return true in the delete method:
def delete(id, options = {})
true
endAfter this change I re-ran the tests and they all passed. This can't be good. If you can effectively remove the body of a method you are testing and the tests still pass you don't have a very good test. So how can we fix it?
Well if HttpMock was truely a mock we could ensure that the HTTP delete method is called on the "/people/1.xml" path. Fortunately HttpMock stores every request it recieves in a class variable. So we can check that the request was received with an assertion.
Here is a new test_delete:
def test_delete
assert Person.delete(1)
assert ActiveResource::HttpMock.requests.include?(ActiveResource::Request.new(:delete, "/people/1.xml", nil, {}))
endThis one now fails when the body of the delete method is removed and passes when it is put back in, so this way we know that the tests is actually testing the functionality of the method. However it is a bit ugly isn't it? It would be nice if HttpMock allowed you to do this:
ActiveResource::HttpMock.expects do |http|
http.delete "/people/1.xml", {}, nil, 200
endBut I'll leave that for another blog post.
Monday, May 28, 2007
Index Ruby Objects by an Attribute
class Array
def hash_by(attribute)
self.inject({}) do |hash, e|
hash[e.send(attribute)] = e
hash
end
end
end
Thursday, April 27, 2006
Why GET should always be idempotent: A lesson from Del.icio.us and Sage
Sage had a particularly cool feature that jumped out at me, the 'Discover Feeds' feature. This allows you to find all the feeds referenced in a page, rather than just the feed in a link tag in the header. It seems that what it does is follow links that may be potential feeds and checks if they are. You then get a list of all the feeds in the page and can add them to your 'watched' feeds in Sage.
This got me thinking, since I use alot of different computers it is pain trying to manage my list of feeds on each computer, exporting them and copying them around is just asking to get them out of sync and frankly too much effort. ;) So since I already have a Del.icio.us account where I keep my bookmarks I could bookmark all the feeds I care about and tag them with 'feed' in Del.icio.us. Then I just need to navigate to the page for the 'feed' tag click on the Discover Feeds button in Sage and all my bookmarked feeds are there to be added to Sage! Yay online storage of feed links that can easily be added to my feed reader of choice from any computer!!!
Or so I thought. It just so happens that the Delicious 'Delete Bookmark' link is one of the links that Sage follows when it searches for feeds. This has the unfortunate side effect of deleting all my bookmarks in the feed tag. The ideal fix is that Delicious delete action needs to be changed to only respond to a HTTP POST method. This would be comformant with W3C Recommandations too.
I've submitted this bug to Delicious but for now the dream of online bookmarking of feeds to be accessed by Sage remains elusive.
Sunday, April 23, 2006
Calling Web Services From Rails
SOAP4R is really easy to use, it simply generates Ruby stubs from a WSDL which will call the web service for you. You just call the stubs as though they were a Ruby API.
If you have ever used any Java or .NET web service clients, it is pretty much the exact same concept.
Tags: SOAP Web Services Rails Ruby
Thursday, March 09, 2006
Javascript Graph Visualisation
This Javascript library looks really promising though since you should be able update the graph using Ajax which would be really cool. It's just in the beginning stages now but I'll definately be keeping an eye on it.
Technorati: semantic web Javascript RDF Canvas SVG Firefox
Sunday, February 19, 2006
Active Ontology for Ruby on Rails
This is a really interesting idea, something I've also been thinking about for a while. One of the tricky things in doing this would be determining what properties a class has. In a RDBMS table you declare that a table (class) has a number of columns (attributes) the mapping to an object model is fairly obvious.
However with OWL and RDF you declare a class and you declare properties, however properties are independant of classes. You can, in RDFS, say that a property has the domain of a specific class, which means that if a Resource has that property it is a member of that class. This is different to RDBMS/OO land where you declare that a class has a property. The difference is subtle but important as it allows you to say things like:
- There is a Employee class.
- There is a Manager class which is a subclass of Employee.
- There is a manages property that has domain Manager.
- We can then infer that the manages property also has Employee as its domain since all Managers and Employees. (Note that this the opposite of OO where properties and inherited down the class heirarchy).
- We can then create an instance of Employee. It is valid to give that Employee a manages property pointing at another Employee instance. We can then infer that the Employee is also a manager.
This is one of the tricky things that will arise out of the differences between Ontologies and OO. A couple of the others are multiple inheritence and multiple class membership.
Most of these issues could be worked out after the initial functionality is there, but it should interesting to see how they are tackled.
Update: Man I really need to look at dates before I respond to blogs.
Technorati: semantic web, Rails Ruby
Saturday, February 18, 2006
RDF Schema Generator
Install it using
gem install rdf_schema_generator.Once it is installed you can run
ruby script/generate rdf_schema <prefix> <rdfs_url> and a class called Enjoy!
First Post
By way of introduction - my name is Sean Geoghegan, I'm a geek, I work as a computer programmer. I'm currently into Ruby on Rails and really want to write a Rails based web application in the guise of flickr or 43things that becomes hugely popular, I just need an idea, if you have one post it in a comment.
