Attack on the Forest

The Partridge Family were neither partridges nor a family. Discuss.
albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Attack on the Forest

Post by albinopapa » July 29th, 2017, 8:23 pm

Code: Select all

bool User::Move( MainWindow& wnd, float dt )
{
	// More descriptive variable name
	bool isMoving = false;

	// Determine state
	if( wnd.kbd.KeyIsPressed( 'W' ) )
	{
		isMoving = true;
		if( wnd.kbd.KeyIsPressed( 'A' ) )
		{
			Direction = DIRSTATE::NORTHWEST;
		}
		else if( wnd.kbd.KeyIsPressed( 'D' ) )
		{
			Direction = DIRSTATE::NORTHEAST;
		}
		else
		{
			Direction = DIRSTATE::NORTH;
		}
	}
	else if( wnd.kbd.KeyIsPressed( 'S' ) )
	{
		isMoving = true;
		if( wnd.kbd.KeyIsPressed( 'A' ) )
		{
			Direction = DIRSTATE::SOUTHWEST;
		}
		else if( wnd.kbd.KeyIsPressed( 'D' ) )
		{
			Direction = DIRSTATE::SOUTHEAST;
		}
		else
		{
			Direction = DIRSTATE::SOUTH;
		}
	}
	else if( wnd.kbd.KeyIsPressed( 'A' ) )
	{
		isMoving = true;
		Direction = DIRSTATE::WEST;
	}
	else if( wnd.kbd.KeyIsPressed( 'D' ) )
	{
		isMoving = true;
		Direction = DIRSTATE::EAST;
	}

	// Early exit if not moving
	if( !isMoving ) return false;

	// Handle current state
	switch( Direction )
	{
		case User::DIRSTATE::WEST:
			usrvec.x -= ( Speed * dt );
			break;
		case User::DIRSTATE::EAST:
			usrvec.x += ( Speed * dt );
			break;
		case User::DIRSTATE::NORTH:
			usrvec.y -= ( Speed * dt );
			break;
		case User::DIRSTATE::SOUTH:
			usrvec.y += ( Speed * dt );
			break;
		case User::DIRSTATE::NORTHWEST:
		{
			const auto deltaMove = sqrtf( Speed * dt );
			usrvec.y -= deltaMove;
			usrvec.x -= deltaMove;
			break;
		}
		case User::DIRSTATE::NORTHEAST:
		{
			const auto deltaMove = sqrtf( Speed * dt );
			usrvec.y -= deltaMove;
			usrvec.x += deltaMove;
			break;
		}
		case User::DIRSTATE::SOUTHWEST:
		{
			const auto deltaMove = sqrtf( Speed * dt );
			usrvec.y += deltaMove;
			usrvec.x -= deltaMove;
			break;
		}
		case User::DIRSTATE::SOUTHEAST:
		{
			const auto deltaMove = sqrtf( Speed * dt );
			usrvec.y += deltaMove;
			usrvec.x += deltaMove;
			break;
		}
	}

	// If program makes it this far, isMoving will be true
	return isMoving;
}
I would do something like this really.

Separate the code that determines state and the handling of the current state.

With it separated, you could put all the "determine state" code in a private function that returns a bool and a "handle state" private function that handles the current state. I personally believe that separation of responsibilities is a good idea, especially for debugging.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Attack on the Forest

Post by albinopapa » July 29th, 2017, 8:28 pm

Ex:

Code: Select all

ool User::Move( MainWindow& wnd, float dt )
{
	const bool isMoving = DetermineState( wnd.kbd );

	// Early exit if not moving
	if( !isMoving ) return false;

	HandleState( dt );

	// If program makes it this far, isMoving will be true
	return isMoving;
}

bool User::DetermineState( Keyboard & kbd )
{
	// More descriptive variable name
	bool isMoving = false;

	// Determine state based on input
	if( kbd.KeyIsPressed( 'W' ) )
	{
		isMoving = true;
		if( kbd.KeyIsPressed( 'A' ) )
		{
			Direction = DIRSTATE::NORTHWEST;
		}
		else if( kbd.KeyIsPressed( 'D' ) )
		{
			Direction = DIRSTATE::NORTHEAST;
		}
		else
		{
			Direction = DIRSTATE::NORTH;
		}
	}
	else if( kbd.KeyIsPressed( 'S' ) )
	{
		isMoving = true;
		if( kbd.KeyIsPressed( 'A' ) )
		{
			Direction = DIRSTATE::SOUTHWEST;
		}
		else if( kbd.KeyIsPressed( 'D' ) )
		{
			Direction = DIRSTATE::SOUTHEAST;
		}
		else
		{
			Direction = DIRSTATE::SOUTH;
		}
	}
	else if( kbd.KeyIsPressed( 'A' ) )
	{
		isMoving = true;
		Direction = DIRSTATE::WEST;
	}
	else if( kbd.KeyIsPressed( 'D' ) )
	{
		isMoving = true;
		Direction = DIRSTATE::EAST;
	}

	return isMoving;
}

void User::HandleState( float dt )
{
	switch( Direction )
	{
		case User::DIRSTATE::WEST:
			usrvec.x -= ( Speed * dt );
			break;
		case User::DIRSTATE::EAST:
			usrvec.x += ( Speed * dt );
			break;
		case User::DIRSTATE::NORTH:
			usrvec.y -= ( Speed * dt );
			break;
		case User::DIRSTATE::SOUTH:
			usrvec.y += ( Speed * dt );
			break;
		case User::DIRSTATE::NORTHWEST:
		{
			const auto deltaMove = sqrtf( Speed * dt );
			usrvec.y -= deltaMove;
			usrvec.x -= deltaMove;
			break;
		}
		case User::DIRSTATE::NORTHEAST:
		{
			const auto deltaMove = sqrtf( Speed * dt );
			usrvec.y -= deltaMove;
			usrvec.x += deltaMove;
			break;
		}
		case User::DIRSTATE::SOUTHWEST:
		{
			const auto deltaMove = sqrtf( Speed * dt );
			usrvec.y += deltaMove;
			usrvec.x -= deltaMove;
			break;
		}
		case User::DIRSTATE::SOUTHEAST:
		{
			const auto deltaMove = sqrtf( Speed * dt );
			usrvec.y += deltaMove;
			usrvec.x += deltaMove;
			break;
		}
	}
}
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
Zedtho
Posts: 189
Joined: February 14th, 2017, 7:32 pm

Re: Attack on the Forest

Post by Zedtho » July 29th, 2017, 8:46 pm

Will definitely do that!. Separating what causes states and what states cause is probably a good idea.

I've got a question on that, should I have the functions that cause states and those where states cause stuff both be called in Update, or should I separate that into two subfunctions. If anything, the changes to states should happen first though. Some functions might need to work in pairs though, because some states might need the output from other states to be triggered, which means they have to be called first.

Thanks for the feedback and for writing the example code, I really appreciate it! :D

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Attack on the Forest

Post by albinopapa » July 29th, 2017, 10:32 pm

Yes, usually determining state will come first. There are probably going to instances where during handling state, you'll want to change state, or better yet make those conditions part of determining the current state.

My advice would be, write the code in a way that makes sense, then refactor the code to fit with using states.

Edited because the original post was done on phone.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Attack on the Forest

Post by albinopapa » July 29th, 2017, 11:05 pm

Here's an example of needing to rely on external state.

Say you want to write an AI that obeys traffic lights. In the US, Red is Stop, Yellow is proceed with caution ( not speed up like some believe lol ) and Green means Go. The AI would need to know the color of the light to determine it's own state. There could be other factors that the AI would need to stop for, not just the Red light, like pedestrian crossing the road or player cutting the AI off or just maybe a long line of cars even after the light is green.

You could write a function that takes in a trafficlight, another function that takes in a vector of pedestrians and another function that takes in a vector of cars and other functions that take in whatever else you want the AI to consider. You could make things more abstract by having the AI aware of all objects it needs to interact with and hold a Car object. The AI would call a Car::ChangeState function passing in a Car::State enum that says whether the car should be in the drive state or the stopped state. Then you just call car.Update( dt ) and let the car do whatever it needs for the current state.

In this case, you have a controller (the AI or the player) and the model ( the car ). The contoller handles external state, the model handles internal state. In this configuration, you can have a Human controller that handles the mouse/keyboard/gamepad inputs and changes the state of the car like, ChangeState( Car::State::Stop ) which will internally, the Car::Update for that Stop state apply the breaks ( acceleration in reverse direction ) and when the car speed is 0, changes the state to Car::Stopped.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
Zedtho
Posts: 189
Joined: February 14th, 2017, 7:32 pm

Re: Attack on the Forest

Post by Zedtho » July 30th, 2017, 5:35 pm

Thank you for the info!
I tried adding your modified framework to my stuff Albino, but it only removed my User.cpp file for some reason and conflicted with my Vec2 class. I can store my Vec2 class in a different file, so that shouldn't be a problem anymore. Also, would _Vec2 and Vec2 as class names conflict with eachother? I've heard some technical stuff happens with _ before variable names as such. Anyways will try again and then report back.

Edit: It just destroyed my "MyVector.cpp" file. Dragging the files in engine from Albino's thing to mine didn't seem to quite do it. I should probably retarget Albino's version before sliding it over to mine.
Last edited by Zedtho on July 30th, 2017, 8:01 pm, edited 4 times in total.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Attack on the Forest

Post by albinopapa » July 30th, 2017, 5:49 pm

Nah, it's just a prefix. nothing special about the under score. Having a leading underscore for the template version allows you to alias the class with the more familiar Vec2.

You cam alias a type like so:
Old way, typedef _Vec2<float> Vec2;
New way, using Vec2 = _Vec2<float>;
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Attack on the Forest

Post by albinopapa » July 30th, 2017, 6:11 pm

Just wanted to add that if you look at the bottom of Vec2.h, you'll see the aliases for _Vec2.

Code: Select all

typedef _Vec2<float> Vec2;
typedef _Vec2<double> Ved2;
typedef _Vec2<int> Vei2;

using Vec2f = Vec2;
using Vec2i = Vei2;
using Vec2d = Ved2;
The typedefs are part of chili's implementation, and the bottom three are ones I added. The bottom three should have been

Code: Select all

using Vec2f = _Vec2<float>;
using Vec2d = _Vec2<double>;
using Vec2i = _Vec2<int>;
Instead of replacing the originals, I kept them, so I could be lazy and wouldn't have to find and replace places where they were used.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
Zedtho
Posts: 189
Joined: February 14th, 2017, 7:32 pm

Re: Attack on the Forest

Post by Zedtho » July 31st, 2017, 7:52 am

Oh ok, thanks for clearing that up.

By the way, I added your stuff! Next up is exploring how to use it :D.

Just something I noticed, my character square moves very slowly all of the sudden in Debug mode, even though it's not using any of the new files I added (yet). I should rephrase that, it seems like it's skipping frames, and my square teleports to where it should be (thank goodness for chrono support :D). I'm not sure why this is happening, because I have only added your files to my solution, not embedded it in to my program (yet), so it shouldn't change anything to make it slower. I should add that this wasn't happening before I added the files in Debug mode.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Attack on the Forest

Post by albinopapa » July 31st, 2017, 10:12 am

Yes, the issue is the BeginFrame that I changed in Graphics.
In the original chili framework, he uses a function called memset. Everything is set to 0, really fast. The downside is you aren't able to clear the background to a specific color, only shades of grey ( from 0 to 255 ). If you want to go back to more performance:

Code: Select all

void Graphics::BeginFrame( Color C )
{
	// clear the sysbuffer
	//pSysBuffer.Fill( C );
	pSysBuffer.Clear();
}
Change the code to what you see here. This uses the original memset, though it uses my aligned_ptr class to do so. This will clear the background to black ( all 0's ). If you wanted to clear to a specific color, you'd use the Fill function.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply