Page 2 of 6

Re: Attack on the Forest

Posted: July 29th, 2017, 8:23 pm
by albinopapa

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.

Re: Attack on the Forest

Posted: July 29th, 2017, 8:28 pm
by albinopapa
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;
		}
	}
}

Re: Attack on the Forest

Posted: July 29th, 2017, 8:46 pm
by Zedtho
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

Re: Attack on the Forest

Posted: July 29th, 2017, 10:32 pm
by albinopapa
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.

Re: Attack on the Forest

Posted: July 29th, 2017, 11:05 pm
by albinopapa
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.

Re: Attack on the Forest

Posted: July 30th, 2017, 5:35 pm
by Zedtho
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.

Re: Attack on the Forest

Posted: July 30th, 2017, 5:49 pm
by albinopapa
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>;

Re: Attack on the Forest

Posted: July 30th, 2017, 6:11 pm
by albinopapa
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.

Re: Attack on the Forest

Posted: July 31st, 2017, 7:52 am
by Zedtho
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.

Re: Attack on the Forest

Posted: July 31st, 2017, 10:12 am
by albinopapa
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.