Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Showcase / rPhalanx 2
- - By allcaps Date 2013-07-29 16:06 Edited 2013-08-07 00:11
This is the first lump of code I'm actually kind of sort of a tiny bit proud of.

rPhalanx2

A pretty simple semi-bullet hell engine in the sirit of R-Type and Phalanx, which is steals its name and graphics from.

I pretty much re-wrote this whole thing after realizing I was hitting a wall with the way I was doing my game's objects.  I followed jlnr's advice/method of using gamestates and gameobjects in a way that makes it much easier for all objects to interact with each other.  I wanted to take it a step further, though, so I added an array to all gameobjects called "tags", which just contains simple strings.  Things like "shot", "player_shot", "enemy_ship", and so on.  This way I can do...


self.game.objects.each do |object|
  if object.tags include? "some_tag"
    object.do_stuff
  end
end


...and I can be sure I'm only hitting the objects I want with my callbacks.  It also makes it easy to do things like count how many bullets there are, and how many belong to the player or to enemies without having to keep a running tally.  Ruby's arrays seem reasonably fast.  We can get up to about 150 enemies all firing shots at the player before we start to get any slowdown, having about 300 bullets on screen, give or take. I also have a lot of debug display code running over the gameobjects each frame, so that's probably bogging it a little bit too.

I was inspired by Unity3D, as it makes use of a very similar tagging system that lets you find and act on gameobjects using their tags, though I think the way it does it is that when you tag an object it's registered into a list for that tag, rather then the tag being search for on-the-fly.

I'm sure this can be improve a lot.  There are a lot of things that don't need to use the tag system, like things that search for just the player, and other misc ways to make it more efficient.  But for small games this should be fine.  It's of course still missing a lot, like shots connecting or enemy movement.  Just wanted to share.

Update:  Holy smokes.  Just making enemy fire more efficient by breaking after it finds the player let me go from about 300 bullets at a time at 55fps to nearly a thousand with 60fps and no stutter at all when new volleys are fired.  And this is with the player now checking to see if they're hit. A simple distance check, but still.  Pretty sure I'm going to be using Ruby and Gosu for any future game jams.

Update 2: Code is a bit cleaner, and enemies now spawn and can be destroyed, with an animation and everything.  It's getting to the point it could be turned into an actual game, but I think I'm ready to move on.  I tried/learned a lot of things with this mini-project, and I'll apply those going forward.  Some things worked well, some didn't, but it was all good learning.
Parent - - By lol_o2 Date 2013-07-29 18:11
Iterating through game objects so many times isn't much efficient. You could group objects or make some global references just to not search for ship through 500+ objects only to check its health.
Or just reduce the number of searches.

Like this:
self.game.objects.each do |object|
      if object.tags.include? "enemy_shot"
        @enemy_shots += 1
      end

      if object.tags.include? "enemy_ship"
        @enemies += 1
      end

      if object.tags.include? "player_shot"
        @player_shots += 1
      end

      if object.tags.include? "player_ship"
        @health = object.health
      end

      if object.tags.include? "player_ship"
        @boost = object.boost
      end
end

Does the same and cycles only once.
Parent - - By allcaps Date 2013-07-29 18:48
This is a good point!  The hud was put together pretty quickly, so I didn't take that into account when I was putting it together.  It was also mostly just for debugging purposes, so I kind of added bits and pieces as I was going along and adding things.  Thanks for the tip though, simple as it sounds I hadn't really thought about gathering multiple pieces of data in the same sweep through objects.

I also think I'll just store a reference to the player in the gamestate.  The hud will be different once it's finished, the one that's there now is really just for debugging like I said, mostly to make sure objects were being created and deleted like they should be.  The actual hud doesn't need to check so many things, just keep track of some info about the player, so it likely won't need to sweep over the objects array at all.
Parent - - By lol_o2 Date 2013-07-29 19:10
But player reference is also for enemies, bullets etc. Player object is probably the most important and it's referred in many situations (especially in more complex games, e.g. in events).
Also, to make life a bit simpler and not create game state link for each object, you can store current game state and window in global variable for easy use anywhere. Keeping Window in global variable allows you to call something like $window.button_down? without bizarre methods.
AND, by typing 'include Gosu' at the beginning of program, you can skip Gosu:: thing in all methods. Just saying.
Parent - By allcaps Date 2013-07-29 19:34
I'd like to avoid using globals if I can.  I prefer just using self.game.window.  I'd also rather using gosu without the include, I don't use the Gosu::methods that often, and with the Gosu:: on them it makes it easier to tell a gosu method from a local one.
Parent - - By RavensKrag Date 2013-08-01 18:04
I personally prefer to use a series of arrays inside of a hash.  Makes for a cleaner syntax.

For example:

@gameobjects[:enemy_shot].each do |x|
  @enemy_shots += 1
end


The tagging system might be better for finding objects multiple tags, but the strategy I use lets you easily pass a reference to a group of objects. Depends on your particular needs, I suppose.
Parent - By allcaps Date 2013-08-07 00:08
Hadn't thought about using a hash, that's actually a really good idea.  Gives pretty much all of the benefits of using the tagging system, but with no performance overhead if you only want to hit one tag (hence, one array in the hash).  Plus lets you still check the tags of the items in that array if you're looking for objects with more than one tag.  Thank you.
Parent - - By ml Date 2013-08-10 01:25
Hi, I was able to load rPhalanx2 and get it to run. The HUD was giving me errors, so I deactivated it, an everything worked without the HUD. I'll have to spend some time on it to figure out why the HUD isn't working for me. Thanks for another game which serves as a good, instructive, basic example.

Lots of good, detailed discussion here about the code. :)
Parent - - By allcaps Date 2013-08-10 04:06
What about the HUD was not working?  I just downloaded the source I have on github right now and it's working correctly for me using both the .exe and running the .rb directly.
Parent - - By ml Date 2013-08-10 19:00
Hi allcaps. Here's the error I get:

/rPhalanx2/hud.rb:40:in 'draw': Could not open TTF file ./gfx/AGENCYR.ttf (RuntimeError)
  from /rPhalanx2/hud.rb:40:in 'draw'
  from /rPhalanx2/core.rb:62:in 'block in draw'
  from /rPhalanx2/core.rb:61:in 'each'
  from /rPhalanx2/core.rb:61:in 'draw'
  from /rPhalanx2/level1.rb:95:in 'draw'
  from phalanx.rb:42:in 'draw'
  from phalanx.rb:71:in '<main>'

Some kind of issue with the TTF files.

I'm running the .rb on Linux (Ubuntu). Not sure if this makes a difference.
Parent - - By allcaps Date 2013-08-10 19:17 Edited 2013-08-10 19:23
ah, it must be taking issue with the font I included.  If you open the HUD.rb and change the file patch to something present on your system it will work.  "Arial" is a pretty safe value on all platforms I believe.  Not sure on Linux though, this particular game was made/tested on Windows mostly.

It might also be a simple case-sensativity issue.  Make sure it's all lower case both in filename and in the .rb file.  It looks like the file extension is lowercase in the ruby file but uppercase in the actual filename.  If you make them the same it should work.
Parent - - By ml Date 2013-08-10 19:32
I replied at the same time as you were replying. Yes, it was a simple case-sensitivity issue.
Parent - By ml Date 2013-08-10 19:40
..or rather, you replied while I was re-replying...
Parent - - By ml Date 2013-08-10 19:30
Hey I figured it out - very small detail... in hud.rb:7

@font = Gosu::Font.new(self.game.window,"./gfx/AGENCYR.ttf",26)

But the files themselves have .TTF extensions, in caps.

Either changing the filenames so the extension is lower-case - or changing hud.rb:7 so that the extension is in caps - makes it works for me.

Nice game, and very instructive for me as a novice.
Parent - - By jlnr (dev) Date 2013-08-12 08:54
Sudden realisation: Wouldn't it be useful if Gosu would warn you for any file you load using the wrong capitalisation? :O Sounds like such an easy fix for this recurring problem.
Parent - By oli.obk Date 2013-08-12 09:47
or since loading doesn't happen very often, stat the folder, search (ignoring caps) for same named files, load if there is exactly one and warn, else die like right now
Parent - By ml Date 2013-08-12 18:41
For Windows users it doesn't seem to make a difference, not sure on Mac, but for me on Linux, it is definitely a common issue. At least now that I'm kind of familiar with it, I know to look for it and make the necessary changes. Another common one I've had to change is:

$window.WIDTH
Up Topic Gosu / Gosu Showcase / rPhalanx 2

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill