Traversing a large game world

Started by
2 comments, last by Hawkblood 10 years, 8 months ago

Hi everyone,

I am planning a game where the player builds their own city and is inhabited by citizens which they can see moving around. The players' city will be one of many in a large game world.

I want the player to be able to visit other cities but I am not sure how to implement it. I want the player to scroll smoothly across the game world instead of having something like "Click here to go to Bobs city" and I don't want the player to have to wait for things to load.

Any ideas?

Advertisement

use double precision coordinates or just manage multiple chunks of terrain.

Peace and love, now I understand really what it means! Guardian Angels exist! Thanks!

Not having to wait for things to load is hard unless the world is small enough so everything fits into RAM.

However, you can cheat. You can start loading before data is needed. If you have partitioned your world for example with a regular grid, you can start loading a city that is in a nearby not-yet-visible cell on a tentative base. In the worst case, you load it in vain, in the best case, you never notice that it's loading. This presumes either a dedicated loader thread or using an asynchronous IO API (using a thread is most likely a lot less painful).

Insofar, moving smoothly is much easier to get right than implementating a "teleport to Bob's town" function. While you are moving, your loader thread still has time to get stuff into memory whereas when teleporting, everything would need to be there instantly (which is kind of impossible).

Using doubles for coordinates is not something I'd do, this is unwise. Rather you will want to either use fixed point precision or use normal "small" floats which are relative to a local point of reference, which can be implicit (i.e. grid cell position multiplied with some number) or explicit.

The "just use doubles and be good" approach kind of works most of the time, but when it doesn't work, it's a nuisance to debug. You have different precision dependent where on the map you are, which is usually not something you want. Fixed point gives you exactly the same precision everywhere, and "local" float values give you slightly but not very noticeably varying precision, since they are overall smaller numbers.

as mentioned, use streaming to avoid load screens, and use a world coordinate system that gets converted to camera relative just before frustum cull and draw.

i recently came up with a "generic" system for big game worlds:

Int64 lightyears

Int64 centimeters

this system is big enough to hold an entire galaxy, with a resolution of 0.01 meters.

in your case, if your scale is something like one 3d unit = 1 meter, you might use an int64 decimeters as your coordinate system. this would give you a sufficiently large world, with an accuracy of 0.1 meters, plenty enough for a god game citybuilder / RTS thing.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

in your case, if your scale is something like one 3d unit = 1 meter, you might use an int64 decimeters as your coordinate system. this would give you a sufficiently large world, with an accuracy of 0.1 meters, plenty enough for a god game citybuilder / RTS thing.

No need to stay that coarse if you use a 64-bit integer, 263 nanometers are 9.2 million kilometers. For anything not "space adventure", 9 million kilometers is plenty -- that's about 220 times the circumference of the earth. So, if you want, you can easily use nanometer resolution with 64-bit integers, and never need to waste a thought.

If millimeter resolution is enough, and your game world is not larger than "once across the Atlantic Ocean", you can use a 32-bit integer. Which, frankly, for pretty much most people, is just fine.

You can make a data structure that easily stores your coordinate system. Most of the posts have eluded to this.



struct INTVECTOR3{
	INTVECTOR3(){}
	INTVECTOR3(__int64 x,__int64 y,__int64 z){ix=x;iy=y;iz=z;}
	__int64 ix,iy,iz;
	INTVECTOR3 operator + ( CONST INTVECTOR3& other) const;
	INTVECTOR3 operator - ( CONST INTVECTOR3& other) const;
	INTVECTOR3 operator * ( CONST __int64& other) const;
	INTVECTOR3 operator * ( CONST double& other) const;
	INTVECTOR3 operator / ( CONST double& other) const;
	INTVECTOR3 operator / ( CONST __int64& other) const;
	INTVECTOR3& operator -= ( CONST INTVECTOR3& other);
	INTVECTOR3& operator+=( CONST INTVECTOR3& other);
	INTVECTOR3& operator/=( CONST __int64 other);
	INTVECTOR3& operator*=( CONST __int64 other);
	D3DXVECTOR3 Div(double m);
	D3DXVECTOR3 Mul(double m);
};
INTVECTOR3 INTVECTOR3::operator*( CONST double& other) const{
	return INTVECTOR3(__int64(ix*other),__int64(iy*other),__int64(iz*other));
}
INTVECTOR3 INTVECTOR3::operator*( CONST __int64& other) const{
	return INTVECTOR3(ix*other,iy*other,iz*other);
}
INTVECTOR3 INTVECTOR3::operator/( CONST __int64& other) const{
	return INTVECTOR3(ix/other,iy/other,iz/other);
}
INTVECTOR3 INTVECTOR3::operator/( CONST double& other) const{
	return INTVECTOR3(int(ix/other),int(iy/other),int(iz/other));
}
INTVECTOR3 INTVECTOR3::operator+( CONST INTVECTOR3& other) const{
	return INTVECTOR3(ix+other.ix,iy+other.iy,iz+other.iz);
}
INTVECTOR3 INTVECTOR3::operator-( CONST INTVECTOR3& other) const{
	return INTVECTOR3(ix-other.ix,iy-other.iy,iz-other.iz);
}
INTVECTOR3& INTVECTOR3::operator-=( CONST INTVECTOR3& other){
	this->ix-=other.ix;
	this->iy-=other.iy;
	this->iz-=other.iz;
	return *this;
}
INTVECTOR3& INTVECTOR3::operator+=( CONST INTVECTOR3& other){
	this->ix+=other.ix;
	this->iy+=other.iy;
	this->iz+=other.iz;
	return *this;
}
INTVECTOR3& INTVECTOR3::operator/=( CONST __int64 other){
	this->ix/=other;
	this->iy/=other;
	this->iz/=other;
	return *this;
}
INTVECTOR3& INTVECTOR3::operator*=( CONST __int64 other){
	this->ix*=other;
	this->iy*=other;
	this->iz*=other;
	return *this;
}
D3DXVECTOR3 INTVECTOR3::Div(double m){
	float x=float(ix);
	float y=float(iy);
	float z=float(iz);
	x/=float(m);
	y/=float(m);
	z/=float(m);
	ix=__int64(x);
	iy=__int64(y);
	iz=__int64(z);
	D3DXVECTOR3 v;
	v=D3DXVECTOR3(x-float(ix),y-float(iy),z-float(iz));
	return v;
}
D3DXVECTOR3 INTVECTOR3::Mul(double m){
	double x=double(ix);
	double y=double(iy);
	double z=double(iz);
	x*=double(m);
	y*=double(m);
	z*=double(m);
	ix=__int64(x);
	iy=__int64(y);
	iz=__int64(z);
	D3DXVECTOR3 v;
	v=D3DXVECTOR3(float(x)-float(ix),float(y)-float(iy),float(z)-float(iz));
	return v;
}
struct HUGEVECTOR3{
	HUGEVECTOR3(){Sector=INTVECTOR3(0,0,0);Loc=D3DXVECTOR3(0,0,0);};
	HUGEVECTOR3(INTVECTOR3 s,D3DXVECTOR3 l){Sector=s;Loc=l;}
	HUGEVECTOR3(D3DXVECTOR3 l){Sector=INTVECTOR3(0,0,0);Loc=l;}
	HUGEVECTOR3(INTVECTOR3 s){Sector=s;Loc=D3DXVECTOR3(0,0,0);}
	HUGEVECTOR3 operator * ( CONST double& other) const;
	HUGEVECTOR3 operator / ( CONST double& other) const;
	HUGEVECTOR3 operator + ( CONST HUGEVECTOR3& other) const;
	HUGEVECTOR3 operator - ( CONST HUGEVECTOR3& other) const;
	HUGEVECTOR3& operator-=( CONST HUGEVECTOR3& other);
	HUGEVECTOR3& operator+=( CONST HUGEVECTOR3& other);
	HUGEVECTOR3& operator-=( CONST D3DXVECTOR3& other);
	HUGEVECTOR3& operator+=( CONST D3DXVECTOR3& other);
	HUGEVECTOR3& operator/=( CONST double other);
	HUGEVECTOR3& operator*=( CONST double other);

	INTVECTOR3 Sector;
	D3DXVECTOR3 Loc;
	double Length;
	void CheckScale(void);
	double GetLength(void);
	double GetLargeLengthKM(void);//returns distance in kilometers
	__int64 GetLargeLength(void);
	D3DXVECTOR3 Normalize(void);
};
double HUGEVECTOR3::GetLargeLengthKM(void){//returns distance in kilometers
	HUGEVECTOR3 tmp;
	tmp.Sector=Sector;
	tmp.Sector.ix/=1000;
	tmp.Sector.iy/=1000;
	tmp.Sector.iz/=1000;
	tmp.Loc=Loc/1000.0f;

	tmp.Loc.x+=float(Sector.ix-tmp.Sector.ix*1000);
	tmp.Loc.y+=float(Sector.iy-tmp.Sector.iy*1000);
	tmp.Loc.z+=float(Sector.iz-tmp.Sector.iz*1000);

	return tmp.GetLength();
}
__int64 HUGEVECTOR3::GetLargeLength(void){
	__int64 L;
	L=__int64(sqrt((long double(Sector.ix*Sector.ix)+long double(Sector.iy*Sector.iy)+long double(Sector.iz*Sector.iz))));
	return L*1000;

}
double HUGEVECTOR3::GetLength(){
	long double Dsq=(long double(Sector.ix*1000)+Loc.x)*(long double(Sector.ix*1000)+Loc.x)
		+(long double(Sector.iy*1000)+Loc.y)*(long double(Sector.iy*1000)+Loc.y)
		+(long double(Sector.iz*1000)+Loc.z)*(long double(Sector.iz*1000)+Loc.z);
	double sr=sqrtl(Dsq);
	Length=sr;
	return sr;

}
D3DXVECTOR3 HUGEVECTOR3::Normalize(){
	Length=GetLength();
	if (Length>0.0f){
		float x=float((float(Sector.ix*1000.0f)+Loc.x)/Length);
		float y=float((float(Sector.iy*1000.0f)+Loc.y)/Length);
		float z=float((float(Sector.iz*1000.0f)+Loc.z)/Length);
		return D3DXVECTOR3(x,y,z);
	}
	return D3DXVECTOR3(0,0,0);
}
void HUGEVECTOR3::CheckScale(void){
	if ((this->Loc.x>=1000.0f)||(this->Loc.x<0.0f)){
		int x=int(this->Loc.x)-int(this->Loc.x)%1000;
		this->Loc.x-=float(x);
		this->Sector.ix+=__int64(x/1000);
	}
	if ((this->Loc.y>=1000.0f)||(this->Loc.y<0.0f)){
		int y=int(this->Loc.y)-int(this->Loc.y)%1000;
		this->Loc.y-=float(y);
		this->Sector.iy+=__int64(y/1000);
	}
	if ((this->Loc.z>=1000.0f)||(this->Loc.z<0.0f)){
		int z=int(this->Loc.z)-int(this->Loc.z)%1000;
		this->Loc.z-=float(z);
		this->Sector.iz+=__int64(z/1000);
	}
}
HUGEVECTOR3& HUGEVECTOR3::operator+=( CONST D3DXVECTOR3& other){
	this->Loc+=other;
	CheckScale();
	return *this;
}
HUGEVECTOR3& HUGEVECTOR3::operator-=( CONST D3DXVECTOR3& other){
	this->Loc-=other;
	CheckScale();
	return *this;
}
HUGEVECTOR3& HUGEVECTOR3::operator+=( CONST HUGEVECTOR3& other){
	this->Loc+=other.Loc;
	this->Sector+=other.Sector;
	CheckScale();
	return *this;
}
HUGEVECTOR3& HUGEVECTOR3::operator-=( CONST HUGEVECTOR3& other){
	this->Loc-=other.Loc;
	this->Sector-=other.Sector;
	CheckScale();
	return *this;
}

HUGEVECTOR3 HUGEVECTOR3::operator*( CONST double& other) const{
	HUGEVECTOR3 tmp;
	tmp.Loc=Loc*float(other);
	tmp.Sector=Sector*other;
	tmp.CheckScale();
	return tmp;
}
HUGEVECTOR3 HUGEVECTOR3::operator/( CONST double& other) const{
	HUGEVECTOR3 tmp;
	tmp.Loc=Loc/float(other);
	tmp.Sector=Sector/other;
	tmp.CheckScale();
	return tmp;
}
HUGEVECTOR3 HUGEVECTOR3::operator+( CONST HUGEVECTOR3& other) const{
	HUGEVECTOR3 tmp;
	tmp.Loc=Loc+other.Loc;
	tmp.Sector=Sector+other.Sector;
	tmp.CheckScale();
	return tmp;
}
HUGEVECTOR3 HUGEVECTOR3::operator-( CONST HUGEVECTOR3& other) const{
	HUGEVECTOR3 tmp;
	tmp.Loc=Loc-other.Loc;
	tmp.Sector=Sector-other.Sector;
	tmp.CheckScale();
	return tmp;
}
HUGEVECTOR3& HUGEVECTOR3::operator *= ( CONST double other) {
	this->Loc*=float(other);
	this->Loc+=(this->Sector.Mul(other))*1000.0f;
	this->CheckScale();
	return *this;
}
HUGEVECTOR3& HUGEVECTOR3::operator /= ( CONST double other) {
	this->Loc/=float(other);
	this->Loc+=(this->Sector.Div(other))*1000.0f;
	this->CheckScale();
	return *this;
}

This is the data structure I made for my space game. It is for 3D, but it can be modified to be 2D.

This topic is closed to new replies.

Advertisement