Sign in to follow this  
Mybowlcut

Unity Quadtree for 2D tiled-based level

Recommended Posts

Hey. I've made a Quadtree after advice from this thread.
class Renderer;

class Quadtree
{
public:
	Quadtree(const SDL_Rect& area);

	void Subdivide(unsigned int times);

	void Draw(Renderer& renderer);
private:
	Quad_Node root;
};
Quadtree::Quadtree(const SDL_Rect& area)
: root(area)
{
}

void Quadtree::Subdivide(unsigned int times)
{
	for(unsigned int i = 0; i < times; ++i)
	{
		root.Subdivide();
	}
}

void Quadtree::Draw(Renderer& renderer)
{
	root.Draw(renderer);
}
class Quad_Node
{
public:
	Quad_Node(const SDL_Rect& area);

	bool Subdivide();

	void Draw(Renderer& renderer);
private:
	typedef std::vector<Quad_Node>::iterator node_it;

	enum DRAW_REGION {T, B, L, R};
	SDL_Rect Compute_Draw_Rect(DRAW_REGION r);

	SDL_Rect area;
	std::vector<Quad_Node> children;
};
Quad_Node::Quad_Node(const SDL_Rect& area)
: area(area)
{
}

bool Quad_Node::Subdivide()
{
	bool done = true;
	if(children.empty())
	{ // Empty; subdivide.
		SDL_Rect r = {area.x, area.y, area.w / 2, area.h / 2};
		children.push_back(r);
		children.push_back(SDL_Tools::rect(r.x + r.w, r.y, r.w, r.h));
		children.push_back(SDL_Tools::rect(r.x, r.y + r.h, r.w, r.h));
		children.push_back(SDL_Tools::rect(r.x + r.w, r.y + r.h, r.w, r.h));
	}
	else
	{ // Full; pass down instruction to children.
		for(node_it it = children.begin(); it != children.end() && done; ++it)
		{
			done = it->Subdivide();
		}
	}

	return done;
}

void Quad_Node::Draw(Renderer& renderer)
{
	if(children.empty()) return;

	for(node_it it = children.begin(); it != children.end(); ++it)
	{
		// draw a line on each edge of this node's area
		renderer.Draw_Rect(it->Compute_Draw_Rect(T), SDL_Tools::Colours::WHITE);
		renderer.Draw_Rect(it->Compute_Draw_Rect(B), SDL_Tools::Colours::WHITE);
		renderer.Draw_Rect(it->Compute_Draw_Rect(L), SDL_Tools::Colours::WHITE);
		renderer.Draw_Rect(it->Compute_Draw_Rect(R), SDL_Tools::Colours::WHITE);

		// draw children of this node (if any).
		it->Draw(renderer);
	}
}

SDL_Rect Quad_Node::Compute_Draw_Rect(DRAW_REGION r)
{
	SDL_Rect rect;
	switch(r)
	{
	case T:
		rect = SDL_Tools::rect(area.x, area.y, area.w, 1);
		break;
	case B:
		rect = SDL_Tools::rect(area.x, area.y+area.h, area.w, 1);
		break;
	case L:
		rect = SDL_Tools::rect(area.x, area.y, 1, area.h);
		break;
	case R:
		rect = SDL_Tools::rect(area.x+area.w, area.y, 1, area.h);
		break;
	}
	return rect;
}
I got it working in terms of creating child nodes and added a Draw function so I could see that it was working... My issue now is that I want to implement this in my game. I've got a Level class which at the moment is just a vector of tiles (as discussed in the thread linked above) and I want to instead use the Quadtree. What functions should I be adding to my Quadtree class? How do I store the tiles? I have no clue what to do next haha... Cheers.

Share this post


Link to post
Share on other sites
To answer your question, each node in the tree would contain a list of tiles. You then insert each tile to the correct node in the tree. For example, a tile that is located at 0,0 will be inserted to a node that is on the top-left corner of the map. Then when you draw, you simply iterate through all nodes and tiles in them.

Now, here's my suggestion. Quadtrees are a bit of an overkill for something as simple as tiles. Typically, tiles are stored sorted in the array. Element #0 in the array corresponds to tile at 0,0. Element #1 corresponds to tile at 1,0 or 0,1, depending on how you order your tiles, column-first or row-first. Or if you'd like, you can store them in a two-dimensional array. Element at [0][0] corresponds to the tile at 0,0. Element at [0][1] corresponds to the tile at 0,1, and so on. That way you don't need to store a Point structure in your tiles anymore because the indices imply the location of the tiles.

Share this post


Link to post
Share on other sites
Quote:
Original post by alnite
To answer your question, each node in the tree would contain a list of tiles. You then insert each tile to the correct node in the tree. For example, a tile that is located at 0,0 will be inserted to a node that is on the top-left corner of the map. Then when you draw, you simply iterate through all nodes and tiles in them.

Now, here's my suggestion. Quadtrees are a bit of an overkill for something as simple as tiles. Typically, tiles are stored sorted in the array. Element #0 in the array corresponds to tile at 0,0. Element #1 corresponds to tile at 1,0 or 0,1, depending on how you order your tiles, column-first or row-first. Or if you'd like, you can store them in a two-dimensional array. Element at [0][0] corresponds to the tile at 0,0. Element at [0][1] corresponds to the tile at 0,1, and so on. That way you don't need to store a Point structure in your tiles anymore because the indices imply the location of the tiles.
Hm... kinda makes me feel stupid for overlooking arrays and going to this much trouble haha.

Cheers though, will dump this Quadtree for the array idea since it's simpler.

Share this post


Link to post
Share on other sites
Quote:
Original post by alniteNow, here's my suggestion. Quadtrees are a bit of an overkill for something as simple as tiles. Typically, tiles are stored sorted in the array. Element #0 in the array corresponds to tile at 0,0. Element #1 corresponds to tile at 1,0 or 0,1, depending on how you order your tiles, column-first or row-first. Or if you'd like, you can store them in a two-dimensional array. Element at [0][0] corresponds to the tile at 0,0. Element at [0][1] corresponds to the tile at 0,1, and so on. That way you don't need to store a Point structure in your tiles anymore because the indices imply the location of the tiles.
How can you order/sort a Tile without anything to uniquely identify it? And how can the tile render itself without a position to go by? Or should the Level object it's stored in take care of that?

Still, even if the Level did render the tile based on it's index in the tiles array, how will the Tile render it's contents (e.g. items on that tile, etc.) without a position to go by? Or have I got that the wrong way around as well and do people usually keep a container of items corresponding to the tiles array?

Also, would a set (using the tile index as the key) be better than an array? If I do use an array, I'll have to dynamically allocate it won't I? This would mean I'd have to specify the size of the level upon construction, right?

[Edited by - Mybowlcut on October 25, 2008 7:38:32 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Mybowlcut
Quote:
Original post by alniteNow, here's my suggestion. Quadtrees are a bit of an overkill for something as simple as tiles. Typically, tiles are stored sorted in the array. Element #0 in the array corresponds to tile at 0,0. Element #1 corresponds to tile at 1,0 or 0,1, depending on how you order your tiles, column-first or row-first. Or if you'd like, you can store them in a two-dimensional array. Element at [0][0] corresponds to the tile at 0,0. Element at [0][1] corresponds to the tile at 0,1, and so on. That way you don't need to store a Point structure in your tiles anymore because the indices imply the location of the tiles.
How can you order/sort a Tile without anything to uniquely identify it? And how can the tile render itself without a position to go by? Or should the Level object it's stored in take care of that?

The Level object should take care of that.

Quote:

Still, even if the Level did render the tile based on it's index in the tiles array, how will the Tile render it's contents (e.g. items on that tile, etc.) without a position to go by? Or have I got that the wrong way around as well and do people usually keep a container of items corresponding to the tiles array?

This really depends on the game you are making. If each tile can contain some items, and you can store the pointer to these items in each tile. When the tile is drawn, that tile will also draw the items. It's sort of like this:

void Tile::draw(Screen s)
{
// insert tile drawing routine here

// then draw item
item->draw(s);
}


Or, you can also put your items in a separate array, and each item would contain its position in the map. After you draw the tiles, you then draw the items. Tiles would not possess information on items, and items would not posses information on tiles. They are "modular".

drawTiles();
drawItems();

Quote:

Also, would a set (using the tile index as the key) be better than an array? If I do use an array, I'll have to dynamically allocate it won't I? This would mean I'd have to specify the size of the level upon construction, right?

Yes, you would have to dynamically allocate your array, but you have to do that no matter what kind of data structure you are using. Your code would look something like this (using a single dimensional array):

int map_size = map_width * map_height;
Tile* map = new TIle[map_size];
for (int i=0; i<map_size; ++i) {
map_size[i] = getNextTileFromFile();
}


A set is just another data structure that sits on top of array. A set is useful when you want to have a container where each item in the set is unique. In this case, it is more likely that you'd have similar types of tiles in your level (at different locations), so you can't use sets.

Share this post


Link to post
Share on other sites
Cheers for that.

What I meant by dynamically allocating the array was that I have to do it instead of STL taking care of it for me. I'm not very good with arrays so I guess it's a good time to learn.

/*
Universal class. Edits affect multiple projects.
*/


#include "stdafx.h"

#include "Level.h"

#include "Party.h"

Level::Level()
: tiles(NULL)
, party(NULL)
{
}

Level::Level(const std::string& name, Level::uint width,
Level::uint height, Party* party)
: name(name)
, width(width)
, height(height)
, tiles(new Tile[width * height])
, party(party)
{
}

Level::Level(const Level& level)
: name(level.name)
, width(level.width)
, height(level.height)
, tiles(new Tile[level.width * level.height])
, party(level.party)
{
for(uint i = 0; i < level.width * level.height; ++i)
tiles[i] = level.tiles[i];
}

Level::~Level()
{
if(tiles)
delete[] tiles;
}

void Level::Swap(Level& rhs)
{
name.swap(rhs.name);
std::swap(width, rhs.width);
std::swap(height, rhs.height);
std::swap(tiles, rhs.tiles);
std::swap(party, rhs.party);
}

Level& Level::operator=(const Level& level)
{
if(this == &level)
{
Level copy(level);
Swap(copy);
}
return *this;
}

bool operator==(const Level& lhs, const Level& rhs)
{
return &lhs == &rhs || lhs.name == rhs.name;
}

std::istream& operator>>(std::istream& stream, Level& level)
{
stream >> level.name >> level.width >> level.height;

bool done = false;
Tile buffer;
for(Level::uint y = 0; !done; ++y)
{
for(Level::uint x = 0; !done; ++x)
{
done = !(stream >> buffer);

if(!done) level.tiles[y * level.width + x] = buffer;
}
}

return stream;
}

std::ostream& operator<<(std::ostream& stream, const Level& level)
{
stream << level.name << " " << level.width << " " << level.height << "\n";

for(Level::uint i = 0; i < level.width * level.height; ++i)
{
stream << level.tiles[i];
}

return stream;
}

void Level::Update(const SDL_Event* event_)
{
if(party) party->Update(event_);
}

void Level::Draw(Renderer& renderer)
{
for(uint y = 0; y < height; ++y)
{
for(uint x = 0; x < width; ++x)
{
renderer.Render(Point(x * Tile::TILE_WIDTH, y * Tile::TILE_HEIGHT),
tiles[y * width + x].File_Name(), NULL);
}
}

// Once have layers, need to determine in above loop if tile is covering/below player
// and then draw player at that point.
if(party) party->Draw(renderer);
}

const std::string& Level::Name() const
{
return name;
}

bool Level::Can_Walk(Point position) const
{
Tile* find = NULL;

// Divides are to normalise position from pixels to tiles.
position = Normalise_Pixel_Coords(position);

// Determine if outside of level boundary.
if(!SDL_Tools::inside(position, SDL_Tools::rect(0, 0, width, height)))
return false;

try { find = &tiles[position.y * width + position.x]; }
catch (const std::exception& e) { e; return false; }

return find->Passability() != PASS_BLOCKED;
}

void Level::Set_Party(Party* party)
{
this->party = party;
}

Point Level::Normalise_Pixel_Coords(const Point& point)
{
return Point(point.x / Tile::TILE_WIDTH, point.y / Tile::TILE_HEIGHT);
}


Does this look ok to you so far? I got some help to code the constructors/destructors/assignment operators.

Also, with your version where the items and tiles are stored in separate containers, why would items need to have a position? What about when they're inside someone's inventory?

[Edited by - Mybowlcut on October 25, 2008 11:21:54 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Mybowlcut
What I meant by dynamically allocating the array was that I have to do it instead of STL taking care of it for me.


std::vector<Tile> would be a far more robust solution than manually managing the memory yourself.

Share this post


Link to post
Share on other sites
Quote:
Original post by EasilyConfused
Quote:
Original post by Mybowlcut
What I meant by dynamically allocating the array was that I have to do it instead of STL taking care of it for me.


std::vector<Tile> would be a far more robust solution than manually managing the memory yourself.
I was stuck thinking that there was a reason I was using the array but now that I think about it there is no reason.. I will use vector instead haha.

Share this post


Link to post
Share on other sites
Hey.

I'm bringing this topic back up again because I've ran into a problem.

I followed alnite's advice and altered the Tile class to not store a position:
class Tile
{
public:
static const unsigned short TILE_WIDTH = 32, TILE_HEIGHT = 32;
static const SDL_Rect DEFAULT_TILE_CLIP;

Tile();
Tile(const std::string& file_name, PASSABILITY passability);
Tile(const Tile& rhs);
virtual ~Tile();

Tile& operator=(const Tile& rhs);

friend std::istream& operator>>(std::istream& stream, Tile& tile);
friend std::ostream& operator<<(std::ostream& stream, const Tile& tile);

const std::string& File_Name() const;
PASSABILITY Passability() const;

virtual void Render(Renderer& renderer);
virtual void Update(const SDL_Event* event_);
private:
std::string file_name;
PASSABILITY passability;
};

It works fine, but I can't miss tiles... In other words if I want to leave out a tile halfway through the container of tiles, then there won't be a space in the middle of the level... This is how I store my levels:
Quote:
level1 10 10
<
< tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tree.png 2 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile1.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 > < tile2.png 1 >
>
<
< level2 1 1 4 4 >
>
The top part contains the name of the level, its size and then every tile. The second part contains the portals (or warps).

The tiles are read into the Level like this:
template<typename Object>
static void Load_Objects(std::istream& stream, std::vector<Object>& v)
{
std::string buffer;
stream >> buffer;
IO::Check_Opening_Tag(buffer);

Object object;
bool ok = true;
while(ok)
{
try
{
stream >> object;
v.push_back(object);
}
catch(const std::exception& e) { e; ok = false; }
}
}

std::istream& operator>>(std::istream& stream, Level& level)
{
stream >> level.name >> level.width >> level.height;

if(!Level::Valid_Dimensions(level.width, level.height))
{
throw std::runtime_error("Invalid dimensions for: " + level.Name() + ".");
}

level.Load_Objects<Tile>(stream, level.tiles);
level.Load_Objects<Portal>(stream, level.portals);

if(level.tiles.size() != level.width * level.height)
{
throw std::runtime_error(level.Name() +
" has insufficient tiles for given width/height.");
}

return stream;
}
So I'm wondering... when it comes to having more than 1 layer of tiles, what do I do? The highest layer (that is drawn on top of everything in the level) will not be full of tiles... so how can I leave tiles out of it and only put in the ones I need? Does that even make sense?

Cheers...

Share this post


Link to post
Share on other sites
Your level file contains a lot of duplicate filenames. You should consider writing a list of unique filenames once, and storing references (indices) to them instead.

As for empty tiles, one way would be to use a transparent image. Another way would be to mark missing tiles. So, you'd create a Tile instance, but mark it as invisible. This keeps positioning correct but allows for empty tiles.


On a side note, using strings to select images with isn't very efficient. Consider storing references to images in your Tile instances, rather than strings, so you only have to look those images up once.

Share this post


Link to post
Share on other sites
Quote:
Original post by Captain P
Your level file contains a lot of duplicate filenames. You should consider writing a list of unique filenames once, and storing references (indices) to them instead.

As for empty tiles, one way would be to use a transparent image. Another way would be to mark missing tiles. So, you'd create a Tile instance, but mark it as invisible. This keeps positioning correct but allows for empty tiles.


On a side note, using strings to select images with isn't very efficient. Consider storing references to images in your Tile instances, rather than strings, so you only have to look those images up once.
Hey thanks for the reply.

If I wrote a list of unique filenames, would I do it in a file? If so, wouldn't it be a hassle if I ever needed to/accidentally changed the order of the file names?

By mark missing tiles, do you mean create a Tile instance called invisible or something and store it in the level and draw it? The only way I could see it working is if when reading the level, a certain tile file name meant that it was an invisible tile.. but I didn't want to hard-code a check to see what tiles are invisible in my code.

The reason I'm using strings instead of references was discussed here. If you can be bothered, take a quick read and tell me what you think?

Cheers!

Share this post


Link to post
Share on other sites
Quote:
Original post by Mybowlcut
If I wrote a list of unique filenames, would I do it in a file? If so, wouldn't it be a hassle if I ever needed to/accidentally changed the order of the file names?

It'd be smart to store it in the file, yes. Something like the following could do:
level1 10 10
< tile1.png tile2.png >
< < 1 1 2 1 1 2 2 1 1 1 >
(and so on)
>

As for modifying the order, doing that manually is indeed a hassle (is it? find + replace...). Then again, modifying levels as text files is pretty much a hassle anyway. Consider using an existing 2D editor to create your levels with (you can write a small tool to convert it's output to your own level format). You could, of course, also write your own editor, or write an editor interface into your game, but that'll probably cost more time. Either way, this sort of optimization is easily automated.

Quote:
By mark missing tiles, do you mean create a Tile instance called invisible or something and store it in the level and draw it? The only way I could see it working is if when reading the level, a certain tile file name meant that it was an invisible tile.. but I didn't want to hard-code a check to see what tiles are invisible in my code.

I don't think it's a bad thing to make invisible tiles a unique case. By using my above example, I chose to use indices starting at 1, which leaves 0 as a special value. By checking for 0's, you don't have to use filenames for your special case, instead, 0 is your special case value.

Of course, you could choose to store a visibility value for all your tiles, but that's probably over-engineering here. A good design is fine, but you can go too far and end up with a beautifully crafted, yet unpractical solution. I believe that's a balance you'll have to find through experience. For small tools, I usually just write the functionality with little to no scaffolding. For larger projects, I spend more time on the design and I often create a few prototypes before working on the final implementation.

Quote:
The reason I'm using strings instead of references was discussed here. If you can be bothered, take a quick read and tell me what you think?

I don't see why separating the view from the model requires you to use strings here. These strings are essentially identifiers here - they tell the renderer what image to use. Now, filenames are pretty natural to work with, but that doesn't mean you'll have to use them all of the time. What if you could retrieve a faster handle with that filename, so you could use that handle afterwards? That handle could be a hash of the filename, for example, or a reference to the actual image.

Now, with modern-day PC's, comparing a few hundred strings every frame may not be a performance-killer, but it's easy to optimize with relatively little effort (at least from my experience).

Share this post


Link to post
Share on other sites
Quote:
Original post by Captain PIt'd be smart to store it in the file, yes. Something like the following could do:
level1 10 10
< tile1.png tile2.png >
< < 1 1 2 1 1 2 2 1 1 1 >
(and so on)
>

As for modifying the order, doing that manually is indeed a hassle (is it? find + replace...). Then again, modifying levels as text files is pretty much a hassle anyway. Consider using an existing 2D editor to create your levels with (you can write a small tool to convert it's output to your own level format). You could, of course, also write your own editor, or write an editor interface into your game, but that'll probably cost more time. Either way, this sort of optimization is easily automated.
Once I've read in the list of unique tile names, would I access the list using the
Quote:
< < 1 1 2 1 1 2 2 1 1 1 >
as indexes into the list to store the file name at that index into the Tile again?

Quote:
I don't think it's a bad thing to make invisible tiles a unique case. By using my above example, I chose to use indices starting at 1, which leaves 0 as a special value. By checking for 0's, you don't have to use filenames for your special case, instead, 0 is your special case value.

Of course, you could choose to store a visibility value for all your tiles, but that's probably over-engineering here. A good design is fine, but you can go too far and end up with a beautifully crafted, yet unpractical solution. I believe that's a balance you'll have to find through experience. For small tools, I usually just write the functionality with little to no scaffolding. For larger projects, I spend more time on the design and I often create a few prototypes before working on the final implementation.
Where would I check for 0's though? Does that mean that you would store an index into the list of unique file names in the Tile class as a member?

Quote:
I don't see why separating the view from the model requires you to use strings here. These strings are essentially identifiers here - they tell the renderer what image to use. Now, filenames are pretty natural to work with, but that doesn't mean you'll have to use them all of the time. What if you could retrieve a faster handle with that filename, so you could use that handle afterwards? That handle could be a hash of the filename, for example, or a reference to the actual image.

Now, with modern-day PC's, comparing a few hundred strings every frame may not be a performance-killer, but it's easy to optimize with relatively little effort (at least from my experience).
A reference to an actual image would be defeating the purpose of separating the view from the model, wouldn't it? How would a hash be any better than a string? Would it not take more work to translate it into a file name than it would to just store the file name directly as I do now?

Share this post


Link to post
Share on other sites
Quote:
Original post by Mybowlcut
Once I've read in the list of unique tile names, would I access the list using the
Quote:
< < 1 1 2 1 1 2 2 1 1 1 >
as indexes into the list to store the file name at that index into the Tile again?

My suggestion to use a list of filenames, combined with indices, was mainly focused on your level file format, but not saving them in your tiles means less memory usage too. In the end however, it's your choice.

Quote:
Where would I check for 0's though? Does that mean that you would store an index into the list of unique file names in the Tile class as a member?

You check for 0's in your loader code, obviously, but your renderer also needs to know that it shouldn't render those tiles. Whether you put that logic in the level code (oh, this is an invisible tile, let's not tell the renderer about it) or in the renderer (oh, this tile is invisible, I'm not going to draw it) is up to you. What matters is that it gets done. ;)

Quote:
A reference to an actual image would be defeating the purpose of separating the view from the model, wouldn't it? How would a hash be any better than a string? Would it not take more work to translate it into a file name than it would to just store the file name directly as I do now?


You don't translate hashes into filenames, you translate filenames into hashes, and then use those hashes for faster lookups. I suggested this both to improve performance and to reduce memory usage (less strings stored in memory).

I don't think that storing a reference to an image defeats the purpose. It is perhaps inverting the model-view relationship, but I've found that to work quite well. My game objects tell the renderer where to create a Sprite (a Sprite is an Image, drawn at a certain position) and are given a reference to the Sprite they requested. This allows them to alter their Sprites properties, such as position, scale, visibility, etc. The Renderer takes care of actually rendering all those Sprites.

So, my view knows nothing about my model. This means that my game objects need to tell their Sprite(s) when to start another animation, when to move, etc. However, if I went with a pure MVC approach, I'd have to write distinct views for all my game objects, and place that logic there. That would allow me to change the representation by only rewriting the view, but I simply don't need that kind of flexibility, so I'm taking the practical route (for me). This approach allows me to easily swap the model, and that's something I do frequently, because I'm building quite a few prototypes using the same rendering code.


Perhaps that makes my comments less practical for you, because our situation and goals may differ. Then again, perhaps this approach fits your needs better than what you're currently striving for. Either way, don't use design patterns for the sake of it - use them when and where they are practical.

Share this post


Link to post
Share on other sites
Quote:
Original post by Captain PMy suggestion to use a list of filenames, combined with indices, was mainly focused on your level file format, but not saving them in your tiles means less memory usage too. In the end however, it's your choice.
Got ya. :)

Quote:
You check for 0's in your loader code, obviously, but your renderer also needs to know that it shouldn't render those tiles. Whether you put that logic in the level code (oh, this is an invisible tile, let's not tell the renderer about it) or in the renderer (oh, this tile is invisible, I'm not going to draw it) is up to you. What matters is that it gets done. ;)
Cheers.

Quote:
I don't think that storing a reference to an image defeats the purpose. It is perhaps inverting the model-view relationship, but I've found that to work quite well. My game objects tell the renderer where to create a Sprite (a Sprite is an Image, drawn at a certain position) and are given a reference to the Sprite they requested. This allows them to alter their Sprites properties, such as position, scale, visibility, etc. The Renderer takes care of actually rendering all those Sprites.

So, my view knows nothing about my model. This means that my game objects need to tell their Sprite(s) when to start another animation, when to move, etc. However, if I went with a pure MVC approach, I'd have to write distinct views for all my game objects, and place that logic there. That would allow me to change the representation by only rewriting the view, but I simply don't need that kind of flexibility, so I'm taking the practical route (for me). This approach allows me to easily swap the model, and that's something I do frequently, because I'm building quite a few prototypes using the same rendering code.
:|

I really don't understand.. I thought using MVC would be better than storing graphical information in a game object? But then you say that you MVC is overkill? I feel as if there is no right way to design something anymore... I've been through sooo many variations of my code and they all end up being suboptimal more or less.

Quote:
Perhaps that makes my comments less practical for you, because our situation and goals may differ. Then again, perhaps this approach fits your needs better than what you're currently striving for. Either way, don't use design patterns for the sake of it - use them when and where they are practical.
Well to be honest, the only reason I tried MVC was because I thought that game objects, etc. shouldn't care how they are being drawn, hearing twice from two highly rated people on different forums that the game should be able to be played without graphical information... or played on a console or something like that. :s

My plans were only ever to use SDL for this engine... hence my project is and always has been called SDL_Game_Engine. The only time I can think back to when the flexibility MVC provides could be useful was when I was overseas bored as hell with my laptop that didn't have SDL.

I'm now a bit unsure where to go with all of this stuff.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mybowlcut
:|

I really don't understand.. I thought using MVC would be better than storing graphical information in a game object? But then you say that you MVC is overkill? I feel as if there is no right way to design something anymore... I've been through sooo many variations of my code and they all end up being suboptimal more or less.

This depends a lot on your requirements. However, it seems that the real problem here is that you're trying to go for the perfect approach. But there is no perfect solution. So go with something that works well enough and just finish your game. Then, look back and see what worked well and what didn't.

Quote:
Well to be honest, the only reason I tried MVC was because I thought that game objects, etc. shouldn't care how they are being drawn, hearing twice from two highly rated people on different forums that the game should be able to be played without graphical information... or played on a console or something like that. :s

Different people will have different opinions. Don't just look at the solutions presented by others, also look at why they chose for those solutions, and if those reasons apply to your situation.

Quote:
My plans were only ever to use SDL for this engine... hence my project is and always has been called SDL_Game_Engine. The only time I can think back to when the flexibility MVC provides could be useful was when I was overseas bored as hell with my laptop that didn't have SDL.

In other words, the advantages that a strict MVC design offer aren't really that important to you, right? In that case, there's little reason to adhere strictly to MVC. Just do what seems practical now.

Note that, with my Renderer design, it's still relatively easy to swap the underlying rendering code. I just need to keep my Renderer and Sprite interfaces the same. Whether they use SDL or OpenGL or whatever under the hood doesn't matter for game objects. What I can't easily do is change the whole Renderer design without breaking the whole game. However, since I've been tweaking that throughout various games, it's pretty well suited to my needs, so that's not a problem.

Quote:
I'm now a bit unsure where to go with all of this stuff.

I'd say, just finish your game without bothering too much about ideal designs. The more games you build, the better insight you will gain in design issues such as these. Don't stare yourself blind at issues, decide something and go with it. Sometimes it'll be a bad decision, but when that happens, take it as an opportunity to learn. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Captain P
Quote:
Original post by Mybowlcut
:|

I really don't understand.. I thought using MVC would be better than storing graphical information in a game object? But then you say that you MVC is overkill? I feel as if there is no right way to design something anymore... I've been through sooo many variations of my code and they all end up being suboptimal more or less.

This depends a lot on your requirements. However, it seems that the real problem here is that you're trying to go for the perfect approach. But there is no perfect solution. So go with something that works well enough and just finish your game. Then, look back and see what worked well and what didn't.

Quote:
Well to be honest, the only reason I tried MVC was because I thought that game objects, etc. shouldn't care how they are being drawn, hearing twice from two highly rated people on different forums that the game should be able to be played without graphical information... or played on a console or something like that. :s

Different people will have different opinions. Don't just look at the solutions presented by others, also look at why they chose for those solutions, and if those reasons apply to your situation.

Quote:
My plans were only ever to use SDL for this engine... hence my project is and always has been called SDL_Game_Engine. The only time I can think back to when the flexibility MVC provides could be useful was when I was overseas bored as hell with my laptop that didn't have SDL.

In other words, the advantages that a strict MVC design offer aren't really that important to you, right? In that case, there's little reason to adhere strictly to MVC. Just do what seems practical now.

Note that, with my Renderer design, it's still relatively easy to swap the underlying rendering code. I just need to keep my Renderer and Sprite interfaces the same. Whether they use SDL or OpenGL or whatever under the hood doesn't matter for game objects. What I can't easily do is change the whole Renderer design without breaking the whole game. However, since I've been tweaking that throughout various games, it's pretty well suited to my needs, so that's not a problem.

Quote:
I'm now a bit unsure where to go with all of this stuff.

I'd say, just finish your game without bothering too much about ideal designs. The more games you build, the better insight you will gain in design issues such as these. Don't stare yourself blind at issues, decide something and go with it. Sometimes it'll be a bad decision, but when that happens, take it as an opportunity to learn. :)
Thanks a lot for the advice. It is really appreciated to someone who is still trying to grasp game development.

:)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Partner Spotlight

  • Forum Statistics

    • Total Topics
      627662
    • Total Posts
      2978519
  • Similar Content

    • By Jcyshadow97
      Hi,guys.I m working on a Fantasy RPG.Currently i m work alone on the project.I really need someone can make UI stuff.If someone can handle it please feel free to contact me on email: 270514974@libero.it.
      Thank you guys and sorry for my english.
       
       



    • By STRATUM the Game
      Hey, everyone! This is my first post here.
      I would like to know what you think about my project called STRATUM. It's a 2D platformer that is heavily based on storytelling and boss fighting while trekking through the world.

      Everything in STRATUM takes place in the first century AD, in a world that wraps our own universe, called  The Stratum. A parallel Universe that is the home of the Christian deities . In this game you will play as a Dacian warrior, unfamiliar with everything in this world, you’ll get to know and understand The Stratum together with him.
      The main thing that I want with STRATUM is to reinvent the known lore and history of the Christian deities and realms. 
      The story is unconventional, it plays down a lot of the mysticism of Hell or Heaven and it gives it a more rational view while keeping the fantastic in it. What do I mean by that? Well, think about Hell. What do you know about it? It's a bad place where bad people go, right? Well, that's not the case in STRATUM. I don't want to describe such a world. In STRATUM, there is a reason for everything, especially for the way Hell is what it is in the game. "Hell" is called The Black Stratum in the game.
      This world is not very different from Earth, but it is governed by different natural laws.
      The story will also involve the reason why this world entered in touch with ours.

       
      What do you think about all that I said? Would you be interested in such a game? I have to say that everything is just a work of fiction made with my imagination. I do not want to offend anyone's beliefs.
      I want this to be a one man game. I have been working alone on it (this was my decision from the beginning) from art to effects to programming to music to sound effects to everything.
      I also have a youtube video HERE if you want to see the way the game moves and the way my music sounds.
      Please, any kind of feedback will be highly appreciated. If you have something bad to say, do it, don't keep it for yourself only. I want to hear anything that you don't like about my project.
       
    • By LimeJuice
      Hi, it's my first post on this forum and I would like to share the game I am working on at the moment.
      Graphics have been made with Blender3D using Cycle as a renderer and I am using Unity3D. It's a 2D game, one touch side-scrolling game for iOS and Android.
      Here some pictures, and you can have a look to the gameplay on this video :
      Feedbacks ?
      And if you want to try it, send me your email and I will add you to the beta tester list!
       
       








    • By Kirill Kot
      An adventure indie game with quests in a beautiful, bright world. Characters with unique traits, goals, and benefits. Active gameplay will appeal to players found of interactivity, especially lovers of quests and investigations.
      Available on:
      Gameroom (just open the web page and relax)
      AppStore
      GooglePlay
      WindowsPhone

    • By Kirill Kot
      Big Quest: Bequest. An adventure indie game with quests in a beautiful, bright world. Characters with unique traits, goals, and benefits.
      Mobile game, now available on Gameroom. Just open the web page and relax.
  • Popular Now