Odd problem with my custom event handler class. (fixed)

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
Mastiff
Posts: 17
Joined: Thu Apr 19, 2007 9:55 pm

Odd problem with my custom event handler class. (fixed)

Post by Mastiff »

I've been working on a class that is designed to make my life simpler.(aren't they all?)
Basically the class is designed to handle keyboard events and give me easy to use functions for 4 types of key events:

Key Press (happens only once, the moment the key is pressed)
Key Down (whether the key is down/pressed)
Key Release (happens only once, the moment the key is released)
Key Up (whether the key is up/released)

Now I needed to make sure that key events where not being handled during the execution of my Main game loop(otherwise bad things happen...), thus I've got a stop and start function designed to basically freeze the state of the keys during the loop.

Now it all works great, except for one small problem.

The first time I press A, G, H, Escape, or a few other keys when they're being handled as "key Press" events, they wont execute the first time, but the second time you press them they will.

The application I wrote uses A/D for key Press movement, and W/S for key Down movement.

As you can see Pressing "A" for the first time does not move the block, but pressing "D" always does (even after a restart of the app and D being the first key ever pressed)

I'd greatly appreciate it if someone could help me fix this problem.

Random speculation is welcome.

Here is my application:

Code: Select all

#include <iostream>
#include <wchar.h>
// Change this to where you store irrlicht.
#include "./irrlicht/irrlicht.h"
// Change this to where you store MastEventReceiver.cpp (or just inline it)
#include "../projects/MastEventReceiver.cpp"

using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#pragma comment(lib, "Irrlicht.lib")



ISceneNode* node = 0;
IrrlichtDevice* device = 0;
ICameraSceneNode *camera = 0;


///////////////
//Start Main
///////////////
int main()
{
	MastEventReceiver receiver;

	device = createDevice(EDT_OPENGL, dimension2d<s32>(640,480), 16, false, false, false, &receiver);

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();

	//Stationary Box Model
	node = smgr->addCubeSceneNode();
	node->setPosition(vector3df(0,0,30));
	node->setMaterialTexture(0, driver->getTexture("C:/Irrlicht/media/wall.bmp"));

	//Create Camera
	ICameraSceneNode *camera = smgr->addCameraSceneNodeMaya();

	int lastFPS = -1;


	while (device->run())
	{
		receiver.endEventProcess();

		// On Key Press
		if (receiver.keyPressed(KEY_KEY_A))
		{
			core::vector3df v = node->getPosition();
			v.X += -15.0f;
			node->setPosition(v);
		}
		if (receiver.keyPressed(KEY_KEY_D))
		{

			core::vector3df v = node->getPosition();
			v.X += 15.0f;
			node->setPosition(v);
		}
		if (receiver.keyPressed(KEY_ESCAPE))
		{
			device->closeDevice(); // stop Irrlicht!!!
			cout << "Now Closing the Irrlicht Device\n";
		}

		// On Key Down
		if (receiver.keyDown(KEY_KEY_W))
		{
			core::vector3df v = node->getPosition();
			v.Z += 0.1f;
			node->setPosition(v);
		}
		if (receiver.keyDown(KEY_KEY_S))
		{
			core::vector3df v = node->getPosition();
			v.Z += -0.1f;
			node->setPosition(v);
		}


		// Draw everything.
		driver->beginScene(true, true, SColor(255,90,90,156));
		smgr->drawAll();
		driver->endScene();

		int fps = driver->getFPS();

		if (lastFPS != fps)
		{
			wchar_t tmp[1024];
			swprintf(tmp, 1024, L"Movement Example - Irrlicht Engine (%s)(fps:%d)", driver->getName(), fps);

			device->setWindowCaption(tmp);
			lastFPS = fps;
		}

		receiver.startEventProcess();
	}
	//END OF  while (device->run())


	device->drop();

 	cout << "Program has Ended, Press Enter to Continue.\n";
	cin.get();

	return 0;
}

And here is the MastEventReceiver Class file:

Code: Select all


// Change this to the path where your Irrlicht Header Files are.
#include "./irrlicht/irrlicht.h"

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

////////////////////////////////////////////////////////////////////////////
//
// To use this Class create an instance of it like so: MastEventReceiver eventReceiver;
// Then inside your Main Game Loop place "eventReceiver.endEventProcess();" in the beginning of your game loop, before anything -
// that would require input, then put "eventReceiver.startEventProcess();" at the very end of your Main Game Loop.
// yeah I know it's confusing, but it makes alot more sense in the internals of the class.
//
////////////////////////////////////////////////////////////////////////////

/// ==============================
/// MastEventReceiver  EVENT RECEIVER FOR KEY PRESS/RELEASE HANDLING.
/// ==============================
class MastEventReceiver : public IEventReceiver
{
	public:

	// Definitions for UP, DOWN, PRESSED and RELEASED key states.
	#define UP        0
	#define DOWN      1
	#define PRESSED   2
	#define RELEASED  3

	MastEventReceiver();

	char keyState[KEY_KEY_CODES_COUNT];

	bool processState; // 1 = started, 0 = ended.

	virtual bool keyPressed(char keycode)
	{
		if (keyState[keycode] == 2)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	virtual bool keyDown(char keycode)
	{
		if (keyState[keycode] == 1 || keyState[keycode] == 2)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	virtual bool keyUp(char keycode)
	{
		if (keyState[keycode] == 0 || keyState[keycode] == 3)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	virtual bool keyReleased(char keycode)
	{
		if (keyState[keycode] == 3)
		{
			return true;
		}
		else
		{
			return false;
		}
	}


	// This is used so that the Key States will not be changed during execution of your Main game loop.
	// Place this at the very START of your Main Loop
	void endEventProcess()
	{
		processState = 1;
	}

	// This is used so that the Key States will not be changed during execution of your Main game loop.
	// Place this function at the END of your Main Loop.
	void startEventProcess()
	{

		processState = 0;
		for (int i = 0; i <= KEY_KEY_CODES_COUNT; i++)
		{
			if (keyState[i] == RELEASED)
			{
				keyState[i] = UP;
			}

			if (keyState[i] == PRESSED)
			{
				keyState[i] = DOWN;
			}
		}

	}

	//
	//
	//
	virtual bool OnEvent(SEvent event)
	{
		bool eventprocessed = false;

			if (event.EventType == EET_KEY_INPUT_EVENT)
			{
			//////////////////////////////
			// Keyboard Input Event
			//////////////////////////////

			// if key is Pressed Down
			if (event.KeyInput.PressedDown == true && processState == 0)
			{
				// If key was not down before
				if (keyState[event.KeyInput.Key] == UP || keyState[event.KeyInput.Key] == RELEASED)
				{
					// if key was not down before
					keyState[event.KeyInput.Key] = PRESSED; // Set to Pressed
				}
				else
				{
					// if key was down before
					keyState[event.KeyInput.Key] = DOWN; // Set to Down
				}
			}
			else
			{
				if (processState == 0)
				{
					// if the key is not down
					if (keyState[event.KeyInput.Key] == UP)
					{
						keyState[event.KeyInput.Key] = UP; // Set to Up
					}
					else
					{
						// if key was down before
						keyState[event.KeyInput.Key] = RELEASED; // Set to Released
					}
				}

			}
			eventprocessed = 1;

		}

		return eventprocessed;

	}

};

MastEventReceiver::MastEventReceiver()
{
	char keyState[KEY_KEY_CODES_COUNT];

	for (int i = 0; i <= KEY_KEY_CODES_COUNT; i++)
	{
		 keyState[i] = UP;
	}

	bool processState = 0; // 0 = started, 1 = ended.

}
/// ==========================================
/// END OF MastEventReceiver Class Definitions
/// ==========================================
Last edited by Mastiff on Fri May 25, 2007 5:02 pm, edited 1 time in total.
#include <"signature.h">

OS = Windows XP
IDE = Code::Blocks
Compiler = GCC MingW
Ico
Posts: 289
Joined: Tue Aug 22, 2006 11:35 pm

Post by Ico »

Just did a quick look but ...

Code: Select all

               // if the key is not down 
               if (keyState[event.KeyInput.Key] == UP) 
               { 
                  keyState[event.KeyInput.Key] = UP; // Set to Up 
               } 
Looks a little bit weird or at least useless to me?
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Your event receiver constructor doesn't initialize the keyState array that is a member of the class, it initializes one that is declared in the constructor and is immediately destroyed. Same thing with your processState flag.

Travis
Dances
Posts: 454
Joined: Sat Jul 02, 2005 1:45 am
Location: Canada
Contact:

Post by Dances »

Code: Select all

if (keyState[event.KeyInput.Key] == UP) 
               { 
                  keyState[event.KeyInput.Key] = UP; // Set to Up 
               } 
               else 
               { 
                  // if key was down before 
                  keyState[event.KeyInput.Key] = RELEASED; // Set to Released 
               }
Would be better condensed to:

Code: Select all

if (keyState[event.KeyInput.Key] != UP) 
                  keyState[event.KeyInput.Key] = RELEASED;
However I doubt that solves the issue.
Mastiff
Posts: 17
Joined: Thu Apr 19, 2007 9:55 pm

Post by Mastiff »

To Ico & Dances:
Yeah it is kind of pointless I basically reconstructed this class from another design I had and I kind of left that the way it was.
The code for that is now:

Code: Select all

if (processState == 0)
{
	// if the key is down
	if (keyState[event.KeyInput.Key] != UP)
	{
		keyState[event.KeyInput.Key] = RELEASED; // Set to Released
	}

}

Vitek had it right though.

I now have instead, an init() function called once that fills in the Key State array, it fixed the problem.

Thanks for the help, it now works perfectly :D
#include <"signature.h">

OS = Windows XP
IDE = Code::Blocks
Compiler = GCC MingW
Post Reply