Sign in to follow this  
darren_mfuk

A Pause without Pause

Recommended Posts

darren_mfuk    148
Hey there, OK the subject might sound confusing, but I could not really describe the problem in so few works. I have a card came (simple memory), you click the first card, and turn it over. Click the second card, and turn it over, then check for a match. If matched, leave them turned over (or remove them), and if no match turn them both back over ... simple... OK heres the code for selection/matching
void cardSelected(int ID)
{
	int index = 0;
	int verifyID = 1000;  // 1000 card object range

	static int noOfCardsSelected = 0;

	// valid card within ID range ??
	if ( (ID >= verifyID) && (ID <= verifyID + sizeOfCardDeck) ) 
	{
		for (int i = 0; i < sizeOfCardDeck; i++) // get array index for card
			{
				if (ID == cards[i].ID) // array index position for selected card
				{
					index = i;	// index equals cards ID position in array
				}
			}
		
		if (cards[index].TurnedOver == FALSE) // check cards not already turned over
		{

			// valid card now process that array index
			noOfCardsSelected++;


			// Turn over first card
			if (noOfCardsSelected == 1)
			{
				firstCardSelected = index;
				cards[firstCardSelected].Selected = TRUE;	// Set first card to Selected
				//MessageBox(NULL, "first found", "MATCH", MB_OK);
			}

			// Turn over second card
			else if (noOfCardsSelected == 2)
			{
				
				secondCardSelected = index;

				if (secondCardSelected != firstCardSelected)
				{
					cards[secondCardSelected].Selected = TRUE;	// Set second card to Selected
					//MessageBox(NULL, "second found", "MATCH", MB_OK);

					// check for match
					if (cards[firstCardSelected].pairedNumber == cards[secondCardSelected].pairedNumber )
					{
						//MessageBox(NULL, "Match found", "MATCH", MB_OK);
							// remove or make matched cards staic
							cards[firstCardSelected].TurnedOver = TRUE;		// Set first card to turned over
							cards[secondCardSelected].TurnedOver = TRUE;	// Set second card to turned over
							cards[firstCardSelected].Selected = FALSE;	// cancel first card selection
							cards[secondCardSelected].Selected = FALSE;	// cancel second card selection

						// Check for bonus
						if ( cards[firstCardSelected].Type == 1 && cards[secondCardSelected].Type == 1 )
						{
							MessageBox(NULL, "BONUS JOKERS FOUND", "MATCH", MB_OK);
							bonusCardsFound = TRUE;
						}

					}

					else
					{
						//MessageBox(NULL, "NO Match found", "MATCH", MB_OK);
							// turn both cards back over
							cards[firstCardSelected].Selected = FALSE;	// cancel first card selection
							cards[secondCardSelected].Selected = FALSE;	// cancel second card selection

					} // end check for match

					noOfCardsSelected = 0; // reset selected card count
				}
				else 
				{
					//MessageBox(NULL, "same found", "MATCH", MB_OK);
					noOfCardsSelected = 1;	// same first card selected, drop card count back to 1

				} // end check second card selected 

			} // end check first card selected

		} // end check if turned over

	} // end valid card
}







and here's where it's rendered:
void RenderCardDeck()
{
	for (int i = 0; i < sizeOfCardDeck; i++) 
	{

		glLoadName(cards[i].ID);	// Push on our label for card 1001, 1002, etc etc
		
		glPushMatrix();

			glTranslatef(cards[i].posX, cards[i].posY, cards[i].posZ);

			if (cards[i].TurnedOver) // card turned over and static
			{
				glRotatef(180, 0, 1, 0);
			}

			if (cards[i].Selected) // highlight or spin
			{
				glRotatef(timeElapsedTime * flipSpeed, 0, 1, 0);
			}

			Draw_3DCard(cards[i].Texture, 1.0f, 1.5f, 0.05f);

		glPopMatrix();
	}
}






So basically, I check I have picked a vaild card object, and not somthng else in the scene. Then if first card selected, I set card.selected TRUE. and in render that just rotates/spins the card until the second is selected. Same thing for the second card, set selected TRUE, check for match render.. Some logic in between to make sure I didn't pick the same card twice, and also there are a pair of jokers which are bonus cards (set by card.type) So the problem .... The first card is selected, and starts spinning.. great The second card is selected, checks for match, but never spins... If it's a match, both cards are turned over face up (static) if no match, both cards turn face down. Second card never moves, as it's selected (TRUE) does a check, but never gets as far as rendering, I need some pause/delay there to render the card for say 3 seconds, or until I click on my next card to start again. cardSelected() is called from mouse click as follows. ... rendering ... so first click, calls cardSelected(), and spins first card. ... rendering ... second click, calls cardSelected(), (match or turn both cards) (PROBLEM HERE, ALL LOGIC ON SECOND CARD GETS DONE BEFORE RENDERING) ... rendering ... not sure the best place to stick some kind of pause/delay (without pausing the application)
if (secondCardSelected != firstCardSelected)
{
cards[secondCardSelected].Selected = TRUE;	// Set second card to Selected
// SPIN SECOND CARD HERE

// ***** SOME PAUSE/DELAY *****

// check for match
if (cards[firstCardSelected].pairedNumber == cards[secondCardSelected].pairedNumber )
{
// MAKE STATIC OR TURN BOTH BACKOVER HERE


Hope I've kind of made that clear, didn't mean to make this a big post.

Share this post


Link to post
Share on other sites
Cowboy Coder    365
The simplest way is to have a counter in your card structure. When you want your "pause" to start, then set it to some value (like 300), then every time you render the card, check if this counter is >0, and if so, decrement it. Then when the counter reaches zero, turn the card over.

That's hacky, but demonstrates the concept. Then you probably want to sperate out this kind of logic into an "update" function for each card, which you call every frame. Then sicne you don't know in advance how long a frame is, you might want to add some actual timing code, and make you counter a value in seconds, and subtract the frame length from it each frame.

Now you really want to change your card struct into a class, and have CCard::Render() and CCard::Update() functions.

Share this post


Link to post
Share on other sites
Zahlman    1682
(EDIT: fixed link.)

You really want separate update and render processes. Pay attention, because the technique(s) shown here will serve you well for years to come.

The basic idea is that update()'ing a Card changes its state to reflect events going on in the game (such as the user clicking on them), and render()'ing a Card takes that state information into account in order to draw an appropriate image. The render() must *not* itself change the state, or you will be in a world of pain. In C++, you can take advantage of const correctness to let the compiler help you make sure you did this properly.

In our case, the obvious "state" that's relevant to the problem is the orientation of the card - face up or face down. Of course, the card animates as it turns, so we'll probably also want "turning to face up" (I'll call it "showing") and "turning to face down" ("hiding") states. And it takes some time to make that transition, so the render() code needs to know how long the card has been in one of those states. (Another option - more appropriate when you have pre-rendered sprite graphics instead of doing stuff with a 3D engine - is to expand these states into "showing, frame 1", "showing, frame 2" etc. In your case, though, the "frames" aren't really definite.)

So, we decide that this state will be represented by a couple of variables - a timer (we can use an int and have it count milliseconds, for now) and an enumeration of possible states. We'll make this into a separate struct, to show that these members of the Card are related. (It doesn't cost us anything to do so, after all, unless we get polymorphism involved, which shouldn't be necessary.)

As for our enumeration... do we have all the states yet? Well, we have all the ones we need for *drawing*, but the *game logic* could use more. In particular, when a card is face up, we need to know whether it is up for good, waiting for the user to turn the second card, or just waiting a short period of time to turn back down. (I assume you want to leave a non-matching pair up for a fixed amount of time so that the user can see they don't match, and study what the cards were.)

So, we make our structure. For now, we'll make it a nested class within Card:


class Card {
struct AnimationState {
int milliseconds;
enum state_e {
DOWN, SHOWING, UP_FIRST, UP_MISMATCH, MATCHED, HIDING
} state;
// I hope those names are clear; feel free to pick better ones if you
// think you can :)
} animation;




This state replaces our boolean 'TurnedOver', BTW. Rather, it contains redundant information. There is probably some game logic code that needs to know if the card is turned over, though; so we can make a helper function for that...


bool TurnedOver() {
AnimationState::state_e s = animation.state;
return s == AnimationState::UP_FIRST ||
s == AnimationState::UP_MISMATCH ||
s == AnimationState::MATCHED;
}


Next we need our update and render functions, and probably a few helpers:


private:
void changeState(AnimationState::state_e next) {
animation.state = next;
animation.milliseconds = 0;
}

// Indicate whether the current state has "expired" by being in it for too
// long (i.e. longer than the threshold).
bool expired(int elapsed, int threshold) {
animation.milliseconds += elapsed;
return animation.milliseconds >= threshold;
}

public:
// Handling clicks on the card should be done separately from handling the
// passage of time.
void click() {
if (animation.state == AnimationState::DOWN) {
changeState(AnimationState::SHOWING);
}
// You might need it to be more complicated than this.
}

// This only handles changes to state that result from time passing. Handling
// clicks on a card happens separately.
void update(int elapsed) {
// 'elapsed' milliseconds have passed since the previous call (we'll have to
// arrange for the main loop to pass in the right value), so the
// animation.milliseconds should be updated - if we care. In the
// non-animating states, we might find it easier to keep the timer at 0.
switch(animation.state) {
case AnimationState::SHOWING:
if (expired(elapsed, 1000)) {
changeState(/* either UP_FIRST, UP_MISMATCH or UP_MATCHED, depending
on game logic. If it's UP_MISMATCH, you'll have to find
the other card and change its state too, because it will
be in UP_FIRST. */
);
}
break;

case AnimationState::UP_MISMATCH:
if (expired(elapsed, 2000)) {
changeState(AnimationState::HIDING);
}
break;

case AnimationState::HIDING:
if (expired(elapsed, 1000)) {
changeState(AnimationState::DOWN);
}
} // In other states we do nothing, and just let the timer sit.
}

// Rendering should be fairly obvious by now:
void render() const {
switch(animation.state) {
case AnimationState::SHOWING:
// Look at the animation.milliseconds, and draw the card in the process
// of turning face up.
break;

case AnimationState::HIDING:
// Look at the animation.milliseconds, and draw the card in the process
// of turning face down.
break;

case AnimationState::DOWN:
// Draw the card face down.
break;

default: // other states
// Draw the card face up.
break;
}
}
}




Finally, the main loop handles the game by calling update(), render() and click() functions on the cards. Basically, you'll make helpers to "call update on all cards", "call render on all cards", and "figure out which card was clicked, see if we accept clicks at the moment and possibly call click on the card".

Share this post


Link to post
Share on other sites
darren_mfuk    148
Zahlman & Cowboy Coder to the rescue again,

You guys are pretty switched on, I'm impressed.

Zahlman, thanks for that, you put a lot of effort into that response.

I have just got in from a long day, so my brains not upto to thinking to hard about the process right now, but hopefully I'll crack on with some coding tomorrow.

Thanks guys [cool]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this