4.2. Example: A Glass Bead Game with Moving BeadsIt is rather difficult to show examples of motion with static images, so I suggest that you download glassgame.zip, unzip it, run the executable, and follow the instructions in the text below. The opening screen shows a set of bouncing beads (essentially an implementation of the code of the previous section). When you strike a key you switch to another game. 't' or 'T' takes you to the tile version of the game discussed in Section 3.3, 'b' or 'B' to the disc version of that game, 'r' or 'R' takes you to a "ring" game, and "i" ir "I" returns you to the initial screen of bouncing beads. What interests us here is the ring game. The image below shows the starting screen:
There are two types of bead motion. When the user drags a drops a bead in another spot, the beads re-arrange themselves to keep the uniform spacing bewteen them. Also at random times two adjacent beads exchange positions (beads with "free will") thus frustrating the user. The second is the simplest case and we describe the code first.
// Listing A
BOOL CRingGame::OnGameIdle()
{
/* other idle processing */
// Code for switching bead position at random
int span = m_Nbeads*20;
if(rand()%span gt; 0) return FALSE;
int k = rand()%m_Nbeads;
int j = (k+1)%m_Nbeads;
// If beads are of the same color we will try again
if(m_bead[k].m_color_name == m_bead[j].m_color_name) return TRUE;
CRect r = m_bead[k].m_pos;
m_bead[k].m_pos = m_bead[j].m_pos;
m_bead[j].m_pos = r;
DrawArea(&(m_bead[k].m_pos));
DrawArea(&(m_bead[j].m_pos));
return FALSE;
}
We call a (pseudo) random number generator twice, first to decide on whether we will try to switch the position of two beads, and then to select a bead for switching. The value of span has been chosen heuristically. The gradual re-arrangement of positions is a bit trickier. When the user moves a bead, we calculate the new arrangement but we do not move the beads there immediately. Instead we save the new positions in a CPoint array m_target[] and set the flag m_transition to TRUE. The code is shown below.
// Listing B
BOOL CRingGame::OnGameIdle()
{
if(m_transition == TRUE) {
CPoint v, d;
double df;
BOOL action = FALSE;
for(int i=0; i < m_Nbeads; i++) {
d = m_bead[i].m_pos.CenterPoint() - m_target[i];
df = sqrt((double)(d.x*d.x + d.y*d.y));
if(df < 2) m_bead[i].MoveTo(m_target[i]re
else {
action = TRUE;
v = m_bead[i].m_pos.CenterPoint() + m_target[i];
v.x /= 2; v.y /= 2;
m_bead[i].MoveTo(v);
}
}
::Sleep(50);
DrawArea(NULL); // redraw everything
if(action==FALSE) m_transition = FALSE;
return TRUE;
}
/* other idle processing code */
}
For each bead we compute its distance df from its target. If df is less than two pixels we move the bead to its final position, otherwise we move it halfway there and set the flag action to TRUE. If all beads have moved to their final position action will be FALSE and we finish the transition process. You can use the code of Listing B to write your version of the zuma game.
|