Sign in to follow this  

Building Tower defence World

This topic is 3848 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Ok, so I have like 50 little circles created *being enemies* which are instances of an Enemy class held in an Enemy ** array. Now i need to build the world in which they are attacked and they travel, and where the towers will be built. I have absolutely no idea where to begin or what i need to do. On first thoughts it seemed as if I could just do if enemy->x is near to tower->x and run thrugh every single enemy on every single iteration of the game loop etc and the same with y but i dont know if thats a good idea! Can you let me know of any better ideas and explain them to me easily, I am using c++ with SDL. Thanks Andy

Share this post


Link to post
Share on other sites
Quote:
Original post by Renegadeandy
On first thoughts it seemed as if I could just do if enemy->x is near to tower->x and run thrugh every single enemy on every single iteration of the game loop etc and the same with y but i dont know if thats a good idea!

That should work out fine. Provided there won't be thousands of towers on the go, the tests will go faster than you'd guess. If you do have zillions of towers (or critters, or both) then some level of spatial partitioning will be necessary to cut out unnecessary tests on the macro-scale, but I doubt this will be necessary. Profile first, optimise later.

Admiral

Share this post


Link to post
Share on other sites
STL containers > arrays

First, I'd throw away the array approach and go with a dynamically resizeable container, like std::vector or std::deque. Basically, these allow you to add and remove objects whenever you want, so you don't have to know how many objects you'll need beforehand. You may want to read up on how they work, but for now that should be all you need to know.


Building grid and movement path

Second, the world. I'd split up the movement and the building part: the building is probably best done using a grid, where a building can occupy one or more tiles. The monster movement can be done by creating a nodegraph: a path that consists of multiple nodes, each pointing to the next one.

So, for the building process, you can keep a simple 2D array (or a vector containing a vector) of booleans. Whenever you want to place a building, you check if the tile at the given coordinate has already been occupied. If it is, then you can't build there. If it isn't, then you can. Then when you build a building, you change the value of those tiles.
That doesn't deal with the buildings themselves however, just with where you can build them. You'll need to keep track of the buildings you've built so far, and run through them every cycle so they can target enemies, fire their stuff and so on. Oh, and each tower would need to know what tiles it occupies, so that when you sell it, you can reset those tiles in the grid.

For the movement, I'd create a list of linked nodes, each containing a position and a reference to the next node. Whenever a new monster appears, you simply set it's position to that of the first node, and also set this node as it's current destination. For the movement, a monster should check it's position agains that of it's destination node, and if it's the same (or within a very small radius - floating point isn't that precise ;)), then it should set it's destination to the node that it's current destination node points to.


Finding a target

A note about the attacking: you should write a function that goes through the list of monsters and returns a reference to the monster that is closest to the given point. Towers that are looking for a new target can then call this function, giving their location, to retreive the closest target. If the distance is less than their radius, they should store this monster reference as their current target, and start firing.

You may want to change this function though: weak towers may want to know about the weakest enemy in a certain range, while high damage towers should focus on the strongest enemy within their range. Or the most efficient target: the monster that can be killed with a single shot, with as little damage waste as possible. Anyway, just some things to consider for the target picking algorithm in relation to the tower and monster types.

Good luck! :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Captain P
STL containers > arrays

...

So, for the building process, you can keep a simple 2D array (or a vector containing a vector) of booleans.


My recommendation would be boost::multi_array, since the storage should be rectangular.

Quote:

For the movement, I'd create a list of linked nodes, each containing a position and a reference to the next node.


Or you could, you know, keep up the good advice, and toss these in a standard library container as well. I don't see why not use a vector for these as well: these "waypoints" aren't likely to change often, so it makes sense to use a more compact representation.

Quote:
Whenever a new monster appears, you simply set it's position to that of the first node, and also set this node as it's current destination. For the movement, a monster should check it's position agains that of it's destination node, and if it's the same (or within a very small radius - floating point isn't that precise ;)), then it should set it's destination to the node that it's current destination node points to.


If you represent the monster's velocity internally as a speed and angle (as opposed to x and y components), and limit its "turning radius", and set a fairly generous tolerance for the waypoints, you can cause the monsters to follow fairly nice, smoothly curved paths automatically - but it will take a bit more work to position the waypoints in a way that makes them stay on a "road", if you care about that.

Anyway, with the waypoints in a container, you set the destination to the next node in the container, rather than looking up some pointer value. One way to do this is to store an iterator over the container in the monster's structure, and increment it each time until it reaches the .end().




Otherwise, good post overall. You just got The Nod(TM); enjoy the ratings bump (from me, it's significant, apparently). [smile]

Share this post


Link to post
Share on other sites
Right ,well i guess the place to start is show you what I currently have!

The file can be downloaded from here :

http://files-upload.com/246061/TowerDefence1.rar.html

Now.

I have been looking at boost::multiarray - is this just a multi dimensional array?

I dont understand how to use it , could you possibly implement a snippet of it which would be relevant to me as I use SDL which i could base my world upon?

Thanks all very much for such super advice you are a great help!

Also - enemy.cpp and enemy.h should really be using a linked list or vector or something other than a straight array but i dont know again how to use other data structures - any code help would be awesome!

Andy

Share this post


Link to post
Share on other sites
Good calls, Zahlman. Thanks. :)

Yeah, for these nodes, I didn't meant to use a linked list, just to link them to each other using references. And yes, a container of positions would do the job just as well, as long as there's a way to retreive the next position when the current destination is reached. :)

And of course, vector math comes highly recommended.


As for using boost::multiarray or std::vector, this is what Google turned up: boost::multiarray and std::vector. Shouldn't be too hard to get used to. :)

As for SDL, it shouldn't really matter how you store your map data. You just tell SDL where to render those images and you're done with it.

Share this post


Link to post
Share on other sites
Ok so this is what i have so far - but i dont really understand how to use this structure with a vector - ideally i would like instances of a class called Enemy to be stored in the vector - how would i do this.

If thats not possible - how on earth would i use the code i scrapped together below to make it work!



#include <vector>
#include "generator2.h"


using namespace std;

typedef vector<Enemy> enemy*;{


string name;
float hp;
float speed;
string type;
float direction;

unsigned int colour;
float x;
float y;

}


generator2::generate(){




}



Thanks guys!

Share this post


Link to post
Share on other sites
You shouldn't mix up vector with the definition of your Enemy class or struct. You store Enemy instances in a vector, similar to how you would store them in an array.

A quick example:


class Enemy
{
public:
Enemy(string n) { name = n; }
string Name() { return name; }

private:
string name;
}


// Elsewhere, we create a vector that can contain Enemy instances:
vector<Enemy> enemies;

// Add a new Enemy instance to the vector:
enemies.push_back(Enemy("grunt"));

// Accessing an Enemy instance inside the vector:
enemies[0].Name(); // This call should return "grunt"

// Or, if you want boundary checks:
enemies.at(0).Name(); // Same here: this should return "grunt"




I would really recommend you to read up on these container classes. There's written more than enough about them. :)

Share this post


Link to post
Share on other sites
How in an array did i need to hold an array of Enemy * then, as in Enemy**?


so far I have

Generator.h

#include "enemy.h"
#include <vector>
#include <string>



class Generator2{


public:

static vector<Enemy> generator2();


private:


}


and Generator2.cpp

#include <vector>
#include "generator2.h"



vector<Enemy> Generator2::generator2(){

vector<Enemy> enemies;

enemies.push_back(Enemy("grag",1000,5,"grang",50,50,0xff000ff));

return enemies;

}



but my usual trackload of errors lol, Nearly there i thikn - your help helps more than documentation and tutorials which i find i cannot relate to and are very un user friendly!

Thanks capitain!

Andy

Share this post


Link to post
Share on other sites
What is this generator class supposed to do? What is it's function? The name is unclear (what does it generate?), and so is that function name. Tell me what you want with it and I can probably give better advice, as I think your design can also be a bit flawed at this point.

Share this post


Link to post
Share on other sites
Right. Enemy.h and Enemy.cpp are the enemy instance classes.

Main will call the generator with a specified size and type of enemy to create - and return the correct vector - or originally array with the enemies all created etc!

Share this post


Link to post
Share on other sites
Enemy.h contains the definition of the Enemy class, Enemy.cpp contains the implementation. An instance is when you create an object of that type.

Right now, your generator2 function creates a vector, adds a single enemy to it and then returns that vector. A pretty pointless function really. You need to store those enemies somewhere, and that's where vectors come in. Sure, you can pass vectors around, but it's often better to keep them inside a class and provide member functions that add instances to this internal vector.

For example, let's make a Game class that contains all these enemies, and that allows you to add enemies to it, and to update all of them with a single function-call. After you've written such a class, you only have to create an instance of it, and add enemies to it when you need to. Of course, you want such a class to be able to do more than that (think of removing enemies, resetting the situation, etc.), but this should give you a basic idea:


#include <vector>
#include "Enemy.h"

// The Game class holds a number of enemies, and allows enemies to be
// added. It also updates all enemies when told to do so.
class Game
{
public:
Game() { }

void AddEnemy(Enemy e)
{
enemies.push_back(e);
}

void UpdateEnemies()
{
// This can be done with 'iterators' too, but I think using
// a simple numerical index is easier to understand for now
for(int i = 0; i < (int)enemies.size(); i++)
enemies[i].Update();
}

private:
vector<Enemy> enemies;
}

Share this post


Link to post
Share on other sites
ok i see , looks familiar - but the code i supplied doesnt actually work - so im stuffed.

How do i pass stuff around - how would it work in my example, i see what your method does - but where is it creating those instances, and they also cannot be accessed globally.

Share this post


Link to post
Share on other sites
With a proper design, you don't need to access these Enemy instances globally. You place them inside a class and let that class handle everything that needs to happen with them. All you have to do is call the right functions on that class (after you've written them, of course).

In my example, all Enemy instances are stored inside the vector, and that vector is hidden inside the Game class, or to be precise, inside a Game class instance. I would write a void Update() function for the Game class, that updates all Enemy objects inside it, and a Enemy& GetClosestEnemy(float x, float y) function, that returns a reference to the Enemy that is closest to the given location. Those are probably the most important functions to have.


Anyway, what exactly is the problem you're having now? What are you trying, with what intention, and what errors do you get?

Share this post


Link to post
Share on other sites
I had the same problem when developing my tower defense game (immortaldefense.com). What I eventually did was give each tower type its own unique way of determining what enemy it would fire at. Some towers fire at the nearest enemy, some towers fire at the enemy with the most defense, some towers fire at the enemy with the lowest HP, and so on.

But anyway, even if you're just keeping it to range, going through all the enemies and finding which enemies are in a tower's range is surprisingly fast. I wouldn't worry about it. I use an interpreted language and it's still extremely fast, it'd probably be even faster in C++.

Share this post


Link to post
Share on other sites

This topic is 3848 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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