Archived

This topic is now archived and is closed to further replies.

Walk in one straight line, damn you!

This topic is 5128 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

I'm trying to make a LucasArts-type point-and-click adventure game, but I'm having trouble getting the character to walk right. I click diagonally from the character's current position, and he'll start walking at an angle, but never enough of one to get straight to his destination, so he eventually ends up walking straight up/down or right/left to cover the rest of the distance. I checked Sam and Max just now, and Sam doesn't have any such silly problems, he goes walks straight to where you click, so this should be possible. So, here's my messy code involved, abridged. I'm using OpenGL in a 640x480 window with an orthographic projection that sets the upper-left corner as 0,0 and lower-right as 640,-480. Everything is done to this coordinate system.
// walking info struct
struct baffWalkingInfo
{
	bool bIsWalking; // whether we're walking
	int tickstarted; // when walking started in milliseconds
	baffPosition destination; // struct containing integers x and y
	int lastxmove; // last time character moved in the x direction, in milliseconds
	int lastymove; // last time character moved in the y direction, in milliseconds
	baffTPP tixperpix; /* struct containing doubles x and y which store the number of
			      milliseconds to wait between movements of 1 pixel in the
			      corresponding direction */
};

// stores the number of milliseconds since the start of the game, updated every cycle
int baffTicks;

/* general number of milliseconds between movements of one pixel, will change from area
   to area (currently should make character walk 100 pixels per second, but actual speed
   in game is much much slower) */
double areaspeed = 10.0

// struct containing x and y position of main character, pretend it's initialized to something
baffPosition frenchyPosition;

/* this is called when the player clicks somewhere, and the place the player clicked
   is sent through x and y */
void baffSetWalkInfo(short int x, short int y)
{
	baffWalkingInfo.bIsWalking = true;
	baffWalkingInfo.tickstarted = baffTicks;
	baffWalkingInfo.destination.x = x;
	baffWalkingInfo.destination.y = y;
	baffWalkingInfo.lastxmove = baffTicks;
	baffWalkingInfo.lastymove = baffTicks;

	// x and y distance of destination from current position, in pixels
	double deltaX = double(baffWalkingInfo.destination.x) - double(frenchyPosition.x);
	double deltaY = double(baffWalkingInfo.destination.y) - double(frenchyPosition.y);

	// absolute value of distances
	double absdeltaX = sqrt(deltaX*deltaX);
	double absdeltaY = sqrt(deltaY*deltaY);

	if (absdeltaX < 20.0 && deltaY > -1.0 && deltaY < 20.0)
	{
		// too small a change to waste any more time on
		baffWalkingInfo.bIsWalking = false;
		return;
	}

	// length of straight line between location and destination, hypothetically in pixels
	double deltaUber = sqrt(deltaX*deltaX + deltaY*deltaY);
	
	// total milliseconds the traveling should take
	double ticks = areaspeed * deltaUber;

	// speed of character in **pixels per millisecond** along direct diagonal line
	double speed = deltaUber / ticks;

	// angle of movement
	double angle;
	if (deltaX != 0.0) // Dividing by zero is bad, mmkay?
		angle = atan(deltaY/deltaX);
	else
	{
		baffWalkingInfo.tixperpix.x = 0.0;
		baffWalkingInfo.tixperpix.y = areaspeed;
		return;
	}

	double cosine = cos(angle);
	double sine = sin(angle);

	// get absolute values sine and cosine
	cosine = sqrt(cosine*cosine);
	sine = sqrt(sine*sine);

	// find x and y components of speed and invert them to milliseconds per pixel
	baffWalkingInfo.tixperpix.x = 1 / (speed * cosine));	
	baffWalkingInfo.tixperpix.y = 1 / (speed * sine));
}

/* updates walking info and Frenchy's position, called every cycle while
   baffWalkingInfo.bIsWalking is true */
void baffUpdateWalkInfo()
{	
	if (frenchyPosition.x == baffWalkingInfo.destination.x &&
		frenchyPosition.y == baffWalkingInfo.destination.y)
	{
		// Frenchy's reached his destination, no more walking
		baffWalkingInfo.bIsWalking = false;
		return;
	}

	// if enough time has passed since we last moved in the x direction, move again
	if (baffTicks - baffWalkingInfo.lastxmove > baffWalkingInfo.tixperpix.x
		&& baffWalkingInfo.tixperpix.x > 0.0)
	{
		// if destination is to the right, move right
		if (frenchyPosition.x < baffWalkingInfo.destination.x)
			frenchyPosition.x++;
		// if destination is to the left, move left
		else if (frenchyPosition.x > baffWalkingInfo.destination.x)
			frenchyPosition.x--;
		// update the last time we moved in x to now
		baffWalkingInfo.lastxmove = baffTicks;
	}

	// if enough time has passed since we last moved in the y direction, move again
	if (baffTicks - baffWalkingInfo.lastymove > baffWalkingInfo.tixperpix.y
		&& baffWalkingInfo.tixperpix.y > 0.0)
	{
		// if destination is above, move up
		if (frenchyPosition.y < baffWalkingInfo.destination.y)
			frenchyPosition.y++;
		// if destination is below, move down
		else if (frenchyPosition.y > baffWalkingInfo.destination.y)
			frenchyPosition.y--;
		// update the last time we moved in y to now
		baffWalkingInfo.lastymove = baffTicks;
	}
}
  
Then there's the function that draws the main character at the position that this code has set. So what's the problem? Why won't my character move in a single straight line in a timely manner, and how can I fix it? It seems like this should be easy. [edited by - PsiRadish on November 30, 2003 4:40:02 PM]

Share this post


Link to post
Share on other sites
I just briefly scanned your code so perhaps i''m overlooking something, but i think i know where your problem lies:

first off, everything you are doing is in screen coord. Screen coord. should NEVER be negative, only positive. So have a 0x0 by 640 x -480 is a big NONO :-D. change your coor so it is at least positive.

secondly, you can''t simply say playerpos.x++ or something similar. You have to use vectors. a vector just represents the x,y distance from your cursor position to your player. so:

double vectorx = cursorx - playerx;

so now you have a vector. If you want to animate him walking for 30 frames to reach that point, you simply do:

vectorx /= 30;

and then you add that to your player position every frame:

playerx += vectorx;

what you seem to be doing is simply adding or subtracting 1 every frame during your walk sequence, which means he will walk in a 45 degree angle, not perhaps a 25 degree, etc etc.

hope that helps

Share this post


Link to post
Share on other sites
I had the screen coordinates set up all positive before, and I didn''t like it. I find the current setup much more intuitive. Also, x and y movement are calculated completely seperately, so it''s not stuck in a 1/1 slope (45 degree angle)

Your algorithm would probably work, but so would mine, except for the problem that was in it, which I have now found and fixed. The problem was it would inc/decrement 1 and only 1 if the elapsed time was greater than the corresponding tixperpix, no matter how much greater it was, and then the last move time would be set to the current time, whiping out any time unaccounted for. So if the elapsed time between cycles was big enough that the character should move, say, 5 pixels at once, they would still move just 1, and this was WRONG. It has now been fixed with while loops and stuff, and now I am GIDDY, like a SCHOOL GIRL. XD

Share this post


Link to post
Share on other sites
More like...


/* Psuedo-code */

// how it was, broken
if (now - time last moved > desired time between movements)
{
move 1;
time last moved = now;
}

// how it is now, fixed
while (now - time last moved > desired time between movements)
{
move 1;
time last moved += desired time between movements;
}

Share this post


Link to post
Share on other sites