Eh, it's hard to explain. I don't know how Shades and RTR, etc do this. Swamp uses tiles, though.
I think I have examples, but not on this computer.
You have three issues;
The first one is where you have the most choice.
I think someone needs to write an article on this subject. It's the most asked question, and it does not have a straight-forward, general-purpose answer. Even with such an answer, if you don't understand why a certain technique is used, I'd expect you to run into more confusion later.
Let's just focus on how Swamp works: 2D arrays of tiles.
There are several ways to implement tile arrays!
Swamp uses a 2D array of ints (there are rarely more than 32 types of tile in one map, so single bytes, or int8[][] is fairly efficient.).
The ints are the index in the list of tile definitions. One could make a 2D array of tile objects, but that would be much less efficient, since most maps reuse the same tiles many times.
The tile definitions aren't strictly necessary; it depends on your game, and how reusable you want code to be (reusable code is extremely popular, and with good reason).
So you might want something like this:
class tile {
string name; // EX: grass, pavement, steel, wall, pole, etc.
string snd; // The sound for interacting with this tile. In swamp, this is either a directory name (for floor tiles), or a filename (for impassable tiles).
int8 type=0; // This could represent height, floor/wall/impassable, or something more generic like floor/wall/fire.
}
Then the map would look like this:
class map {
tile@[] list; // tile definitions.
int8[][] tiles;
// Swamp includes zones, which you might do like this:
string[] zonenames;
int8[][] zone1;
int8[][] zone2;
}
You would need to define what tile types mean what. Let's keep using Swamp as an example, and go with 1=floor, 2=wall, 4=impassable (like cars, pews, etc).
First, we need a function to rotate vectors:
vector rotate(vector p, vector o, double theta) {
vector r;
r.x = (cosine(theta) * (p.x-o.x)) - (sine(theta) * (p.y-o.y)) + o.x;
r.y = (sine(theta) * (p.x-o.x)) + (cosine(theta) * (p.y-o.y)) + o.y;
return r;
}// Rotate.
Where p=the vector to rotate, and o=the origin.
Enemies and players alike probably have several things in common, so let's give them a superclass:
class actor {
vector position;
vector velocity;
double angle=0.0; // east.
double health;
// do all actors have weapons, ammo, etc?
}
Now, your move_actor function might look like this:
const vector zero(0, 0, 0);
vector move_actor(actor@ me, map@ place, double dt) {
// Rotate the velocity vector. It isn't great to do this all the time, but in bgt it might help to do it this way.
vector v=rotate(me.v, zero, me.angle);
// Get the destination:
vector destination=pose+(v*dt);
// Now, determine if the destination is a walkable tile.
// Warning: this function doesn't check for extremely high speeds!
int x=round(destination.x, 0); // The map only has integer precision, but actors can move with double precision.
int y=round(destination.y, 0);
// Out of bounds:
if(x<0 or y<0 or x>=place.tiles.length() or y>=place.tiles[x].length()) return me.position;
// Save the destination tile, just to save typing:
tile@ t=place.list[place.tiles[x][y]];
// Notice: if the tile at [x, y] is not set correctly, this line will result in an index out of bounds error.
if(t.type==1) {
return destination;
}
else {
return actor.position;
}
}
You might call this function like so:
double dt=game_timer.elapsed/1000.0;
player.position=move_actor(player, current_map, dt);
看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
George... Don't do that.