16 Oct, 2008, Chris Bailey wrote in the 1st comment:
Votes: 0
I recently started working with Ruby/SDL which is like a wrapper around SDL that allows you to use it in Ruby. Anyhow, I decided to work out a simple 2d engine to help me learn the library. It seems to be coming along fine but I have a couple of questions and how I should go about a few aspects of it and about making it user friendly.

1) I have a basic structure for the Map class which needs to contain a small amount of information, Most importantly a 2D Array. The engine will allow for unlimited graphic layers (to be drawn on top of each other to give the effect of depth). Each cell in the 2d array contains a Tile object, which is another simple structure that contains an array of Events that can be called through the engine and an array of images(@Layers) to store the tile graphics to be drawn.

class Map
def initialize(name="New Map",width=20,height=20)
@name = name
@width = width
@height = height
@tiles = m_array(@width,@height)
fill
end

def fill
@width.times do |x|
@height.times do |y|
@tiles[x][y] = Tile.new
end
end
end

end

class Tile
def initialize(image=nil)
@events = []
@layers = []
@layers[0]=image
end

end

def m_array(width,height)
grid = []
width.times { grid << Array.new(height) }
return grid
end


This seems really crude but I wasn't sure how else to handle it. m_array is a simple function that returns an n*n array. :lol: So I imagine that having a map of any useable size will be very resource intensive. I didn't think about this when I made an instance variable in the Engine class @maps that stores every map object created…anyhow, I need a new way to point to the maps (an array of filename strings pointing to them?) and a serious rework of the map and tile class. Any ideas?
16 Oct, 2008, Chris Bailey wrote in the 2nd comment:
Votes: 0
I forgot to ask about how friendly the end user interface is. It seems … ugly and unpolished to me. The following is what an engine user would type to initialize the video subsytem, initialize the audio subsytem, create a new player object, create an empty map and bring up an 800x600 hardware accelerated window with a 32bit color depth.

main = NeEngine.new(800,600,32,:hardware)    # Main is an instance of the NeEngine class, 800x600 Window, 32 bit color hardware accelerated.
main.init(:video) # Initialize the video.
main.init(:audio) # Initialize the audio.
main.init(:player,"Jerry") # Create a new player object.
main.init(:map,"World",300,300) # Create a new map named "World" that is 300x300 tiles.
main.start # Start the game/engine.
16 Oct, 2008, David Haley wrote in the 3rd comment:
Votes: 0
To answer the friendliness question, it's not so bad, and you can make a lot of it go away with some helper functions. The video and audio can be hidden away in the same function that creates the engine. So you'd ask for an engine with res x/y, bits, hardware, and then it'd initialize the subsystem.s Having to create the player and map and then start the engine is kind of a fact of life, I think.

And yes, your map will be resource intensive, but it's only wasteful if your map is sparse, i.e. not many tiles have interesting things.

But you might want to separate the notion of the map, and the currently visible map. You probably don't need to have event handlers set up for tiles that are far away, for example. In other words, divide it into the bare terrain information, and a view on top of that, which is augmented with the various handlers etc. that you need.
16 Oct, 2008, Chris Bailey wrote in the 4th comment:
Votes: 0
Thanks for th reply David, I went ahead and did what you suggested with the initialization. Audio and Video will start automatically when you start the engine, but I left the functions in to do it manually in case someone decides to override NeEngine initialize method with their own stuff. I left initialization of the Joystick and Cdrom out though, I didn't think they were common enough to start automatically. You would still have to put in "main.init(:joystick)" to start the joystick. Also, something to note about the Tile object. I included the @layers array inside the tile object so that you could have 1 tile with 2 graphical layers and another with 6 graphical layers without having to have an n*n*6 Array. I'm not entirely sure if that is a better idea though.
When you say seperate the map and the visible map, do you mean only load the part of the Map array that is current visible? If that is the case, I would just store the Map itself in yaml format or something, and just load up the surrounding viewable tiles into memory? I'm not sure how to do that, but I can certainly try. I bet it would be absolutely necessary with large maps, ie:1000x1000.
16 Oct, 2008, David Haley wrote in the 5th comment:
Votes: 0
Chris_Bailey said:
I included the @layers array inside the tile object so that you could have 1 tile with 2 graphical layers and another with 6 graphical layers without having to have an n*n*6 Array. I'm not entirely sure if that is a better idea though.

Seriously? That's an excellent idea :smile: Could you imagine having to store in every single tile the maximum possible number of layers, where in many cases, those layers will be mainly empty?

Chris_Bailey said:
When you say seperate the map and the visible map, do you mean only load the part of the Map array that is current visible?

Well, you could do that, but I was thinking that you could have one minimal tile class, and then subclass that with a tile class that knows how to draw itself, handle events, registers handlers, and so on.

Then you have two maps: one that contains all tiles, and one that contains the currently active tiles (and perhaps some of the surrounding tiles).

If you get clever and share memory, you could even make the subclasses not be redundant with the bare tiles, and have them only add data, rather than repeat the bare data and then add some. You could do that by storing a pointer to the bare tile, and delegating all requests that you can't handle to that bare tile. More CPU, less memory; but note that I'd only make this decision after seeing what it looks like in practice.
16 Oct, 2008, Chris Bailey wrote in the 6th comment:
Votes: 0
That sounds like a pretty good idea, I'll work on implementing that. Development is veeerrrryyyy slow right now because I'm trying to learn and use rspec and TDD/BDD alongside it. Writing these tests is doubling the amount of code and tripling the time it takes to do it. =P
16 Oct, 2008, Vassi wrote in the 7th comment:
Votes: 0
DavidHaley said:
Well, you could do that, but I was thinking that you could have one minimal tile class, and then subclass that with a tile class that knows how to draw itself, handle events, registers handlers, and so on.


Not completely related to a drawing engine, per se, but I created a mapping program that does that. Obviously it's tile\co-ord based, so I ended up dividing into a surface which holds layers, and layers which hold rooms (tiles) The rooms know how to draw themselves so that let me make different types of rooms, some with their own internal layers and some without. For instance, a mob generator has no extra layers where a different room has a 'decor' layer to display a little symbol if it's a bank, or a store, etc. Likewise, the drawing engine only has to call Draw and the room displays it's background color according to it's settings (I.E. indoor = black, water = blue, etc).

In other words, I second David's statement. =\
16 Oct, 2008, Chris Bailey wrote in the 8th comment:
Votes: 0
Yeah I like the idea and I hope I can do it properly. The biggest challenge with this is doing it all with a decent FPS. In the end this draw method will be need to be executed at a steady 60 times per second without dragging. Easily accomplished in C or C++ but not so much when you are using ruby to wrap around a c wrapper made in ruby. :P Ruby(Ruby(C)) = Slooooowww
16 Oct, 2008, Vassi wrote in the 9th comment:
Votes: 0
Chris Bailey said:
Yeah I like the idea and I hope I can do it properly. The biggest challenge with this is doing it all with a decent FPS. In the end this draw method will be need to be executed at a steady 60 times per second without dragging. Easily accomplished in C or C++ but not so much when you are using ruby to wrap around a c wrapper made in ruby. :P Ruby(Ruby(C)) = Slooooowww


Which is why using arrays is probably better, as long as you always know where you're going. My app is implemented via lists, so when drawing I essentially do a lookup to see if the co-ordinates I want belong to anything inside the list. Not the best way to write an actual engine, but suitable for a display =D
17 Oct, 2008, Stormy wrote in the 10th comment:
Votes: 0
Chris Bailey said:
Yeah I like the idea and I hope I can do it properly. The biggest challenge with this is doing it all with a decent FPS. In the end this draw method will be need to be executed at a steady 60 times per second without dragging. Easily accomplished in C or C++ but not so much when you are using ruby to wrap around a c wrapper made in ruby. :P Ruby(Ruby(C)) = Slooooowww


Two things that might help regarding speed: the 1.9 compiler (assuming you're still using 1.8.6) and C-extensions. I don't know if you've worked with Ruby C-extensions yet, and it might seem like a step backwards, but it isn't too bad and the speed gains should be more than significant. I've seen gains between 30-90% for different parts of an R*-Tree which I first wrote in Ruby and then ported to a C-extension.
17 Oct, 2008, Chris Bailey wrote in the 11th comment:
Votes: 0
Stormy said:
Two things that might help regarding speed: the 1.9 compiler (assuming you're still using 1.8.6) and C-extensions. I don't know if you've worked with Ruby C-extensions yet, and it might seem like a step backwards, but it isn't too bad and the speed gains should be more than significant. I've seen gains between 30-90% for different parts of an R*-Tree which I first wrote in Ruby and then ported to a C-extension.


I haven't really worked with C-Extensions yet but I'm sure I will be very soon. I'm expecting the engine will quickly bog down with any significant amount of blitting required and that wouldn't be very useful. I haven't looked at the 1.9 compiler yet and had no idea of the speed increases possible. I'm checking into that as we speak, thanks a lot! =)
17 Oct, 2008, David Haley wrote in the 12th comment:
Votes: 0
You probably really want your blitting to be done in C/C++. Moving memory around is something it is very good at. :smile: You would even talk directly to the SDL blitting code in the same language, and so remove the levels of indirection when you don't need it.
0.0/12