Home > Articles

This chapter is from the book

Building the Game

Hopefully by now you're getting antsy to see how the Henway game is put together. The next couple of sections explore the code development for the Henway game, which is relatively simple when you consider that this is the first fully functioning game you've created that supports double-buffer sprite animation. The revamped game engine with sprite management really makes the Henway game a smooth game to develop.

Writing the Game Code

The code for the Henway game begins with the Henway.h header file, which is shown in Listing 12.1.

Listing 12.1 The Henway.h Header File Declares Global Variables that Are Used to Manage the Game, as well as a Helper Function

 1: #pragma once
 3: //-----------------------------------------------------------------
 4: // Include Files
 5: //-----------------------------------------------------------------
 6: #include <windows.h>
 7: #include "Resource.h"
 8: #include "GameEngine.h"
 9: #include "Bitmap.h"
10: #include "Sprite.h"
12: //-----------------------------------------------------------------
13: // Global Variables
14: //-----------------------------------------------------------------
15: HINSTANCE  _hInstance;
16: GameEngine* _pGame;
17: HDC     _hOffscreenDC;
18: HBITMAP   _hOffscreenBitmap;
19: Bitmap*   _pHighwayBitmap;
20: Bitmap*   _pChickenBitmap;
21: Bitmap*   _pCarBitmaps[4];
22: Bitmap*   _pChickenHeadBitmap;
23: Sprite*   _pChickenSprite;
24: int     _iInputDelay;
25: int     _iNumLives;
26: int     _iScore;
27: BOOL    _bGameOver;
29: //-----------------------------------------------------------------
30: // Function Declarations
31: //-----------------------------------------------------------------
32: void MoveChicken(int iXDistance, int iYDistance); 

The global variables for the Henway game consist largely of the different bitmaps used throughout the game. The offscreen device context and bitmap are declared (lines 17 and 18), as well as the different bitmaps that comprise the game's graphics (lines 19–22). Even the sprite management features of the game engine are being used in this game; it's necessary to keep a pointer to the chicken sprite so that you can change its position in response to user input events. The _pChickenSprite member variable is used to store the chicken sprite pointer (line 23). The input delay for the keyboard and joystick is declared next (line 24), along with the number of chicken lives remaining (line 25) and the score (line 26). The last variable is the Boolean game over variable, which simply keeps track of whether the game is over (line 27).

You'll notice that a helper function named MoveChicken() is declared in the Henway.h file. This function is called by other game functions within the game to move the chicken sprite in response to user input events. The two arguments to the MoveChicken() function are the X and Y amounts to move the chicken.

The actual game functions appear in the Henway.cpp source code file, which is located on the accompanying CD-ROM. The first game function worth mentioning is the GameInitialize() function, which creates the game engine, establishes the frame rate, and initializes the joystick.

The GameStart() function is a little more interesting than GameInitialize(). This function is responsible for initializing the game data, as shown in Listing 12.2.

Listing 12.2 The GameStart() Function Creates the Offscreen Buffer, Loads the Game Bitmaps, Creates the Game Sprites, and Initializes Game State Member Variables

 1: void GameStart(HWND hWindow)
 2: {
 3:  // Seed the random number generator
 4:  srand(GetTickCount());
 6:  // Create the offscreen device context and bitmap
 7:  _hOffscreenDC = CreateCompatibleDC(GetDC(hWindow));
 8:  _hOffscreenBitmap = CreateCompatibleBitmap(GetDC(hWindow),
 9:   _pGame->GetWidth(), _pGame->GetHeight());
10:  SelectObject(_hOffscreenDC, _hOffscreenBitmap);
12:  // Create and load the bitmaps
13:  HDC hDC = GetDC(hWindow);
14:  _pHighwayBitmap = new Bitmap(hDC, IDB_HIGHWAY, _hInstance);
15:  _pChickenBitmap = new Bitmap(hDC, IDB_CHICKEN, _hInstance);
16:  _pCarBitmaps[0] = new Bitmap(hDC, IDB_CAR1, _hInstance);
17:  _pCarBitmaps[1] = new Bitmap(hDC, IDB_CAR2, _hInstance);
18:  _pCarBitmaps[2] = new Bitmap(hDC, IDB_CAR3, _hInstance);
19:  _pCarBitmaps[3] = new Bitmap(hDC, IDB_CAR4, _hInstance);
20:  _pChickenHeadBitmap = new Bitmap(hDC, IDB_CHICKENHEAD, _hInstance);
22:  // Create the chicken and car sprites
23:  Sprite* pSprite;
24:  RECT  rcBounds = { 0, 0, 465, 400 };
25:  _pChickenSprite = new Sprite(_pChickenBitmap, rcBounds, BA_STOP);
26:  _pChickenSprite->SetPosition(4, 175);
27:  _pChickenSprite->SetVelocity(0, 0);
28:  _pChickenSprite->SetZOrder(1);
29:  _pGame->AddSprite(_pChickenSprite);
30:  pSprite = new Sprite(_pCarBitmaps[0], rcBounds, BA_WRAP);
31:  pSprite->SetPosition(70, 0);
32:  pSprite->SetVelocity(0, 7);
33:  pSprite->SetZOrder(2);
34:  _pGame->AddSprite(pSprite);
35:  pSprite = new Sprite(_pCarBitmaps[1], rcBounds, BA_WRAP);
36:  pSprite->SetPosition(160, 0); 
37:  pSprite->SetVelocity(0, 3);
38:  pSprite->SetZOrder(2);
39:  _pGame->AddSprite(pSprite);
40:  pSprite = new Sprite(_pCarBitmaps[2], rcBounds, BA_WRAP);
41:  pSprite->SetPosition(239, 400);
42:  pSprite->SetVelocity(0, -5);
43:  pSprite->SetZOrder(2);
44:  _pGame->AddSprite(pSprite);
45:  pSprite = new Sprite(_pCarBitmaps[3], rcBounds, BA_WRAP);
46:  pSprite->SetPosition(329, 400);
47:  pSprite->SetVelocity(0, -10);
48:  pSprite->SetZOrder(2);
49:  _pGame->AddSprite(pSprite);
51:  // Initialize the remaining global variables
52:  _iInputDelay = 0;
53:  _iNumLives = 3;
54:  _iScore = 0;
55:  _bGameOver = FALSE;
56: }

The GameStart() function contains a fair amount of code, which primarily has to do with the fact that creating each sprite requires a few lines of code. The function starts out by creating the offscreen device context and bitmap (lines 7–10). All the bitmaps for the game are then loaded (lines 13–20). Finally, the really interesting part of the function involves the creation of the sprites, which you should be able to follow without too much difficulty. The chicken sprite is first created at a position in the Start Area of the game screen, and with 0 velocity (lines 25–29). The car sprites are then created at different positions and with varying velocities (lines 30–49). Notice that the bounds actions for the car sprites are set so that the cars wrap around the game screen, whereas the chicken sprite stops when it encounters a boundary. Also, the Z-order of the cars is set higher than the chicken so that the chicken will appear under the cars when it gets run over.

The remaining member variables in the Henway game are initialized in the GameStart() function after the sprites are created. The input delay is set to 0 (line 52), whereas the number of chicken lives is set to 3 (line 53). The score is also set to 0 (line 54), and the game over variable is set to FALSE to indicate that the game isn't over (line 55).

The Henway game relies on the keyboard and joystick for user input. In order to support joystick input, it's important to capture and release the joystick whenever the game window is activated and deactivated. Listing 12.3 shows the code for the GameActivate() and GameDeactivate() functions, which are responsible in this case for capturing and releasing the joystick.

Listing 12.3 The GameActivate() and GameDeactivate() Functions Capture and Release the Joystick, Respectively

 1: void GameActivate(HWND hWindow)
 2: {
 3:  // Capture the joystick
 4:  _pGame->CaptureJoystick();
 5: }
 7: void GameDeactivate(HWND hWindow)
 8: {
 9:  // Release the joystick
10:  _pGame->ReleaseJoystick();
11: }

The GameActivate() function calls the CaptureJoystick() method on the game engine to capture the joystick (line 4), whereas the GameDeactivate() function calls ReleaseJoystick() to release the joystick (line 10).

As you know, the GamePaint() function is responsible for painting games. Listing 12.4 contains the code for the Henway game's GamePaint() function.

Listing 12.4 The GamePaint()Function Draws the Highway Background Image, the Game Sprites, and the Number of Remaining Chicken Lives

 1: void GamePaint(HDC hDC)
 2: {
 3:  // Draw the background highway
 4:  _pHighwayBitmap->Draw(hDC, 0, 0);
 6:  // Draw the sprites
 7:  _pGame->DrawSprites(hDC);
 9:  // Draw the number of remaining chicken lives
10:  for (int i = 0; i < _iNumLives; i++)
11:   _pChickenHeadBitmap->Draw(hDC,
12:    406 + (_pChickenHeadBitmap->GetWidth() * i), 382, TRUE);
13: }

This GamePaint() function must draw all the game graphics for the Henway game. The function begins by drawing the background highway image (line 4), and then it draws the game sprites (line 7). The remainder of the function draws the number of remaining chicken lives in the lower right corner of the game screen using small chicken head bitmaps (lines 10–12). A small chicken head is drawn for each chicken life remaining, which helps you to know how many times you can get run over before the game ends.

The GameCycle() function works hand in hand with GamePaint() to update the game's sprites and then reflect the changes onscreen. Listing 12.5 shows the code for the GameCycle() function.

Listing 12.5 The GameCycle() Function Updates the Game Sprites and Repaints the Game Screen Using an Offscreen Buffer to Eliminate Flicker

 1: void GameCycle()
 2: {
 3:  if (!_bGameOver)
 4:  {
 5:   // Update the sprites
 6:   _pGame->UpdateSprites();
 8:   // Obtain a device context for repainting the game
 9:   HWND hWindow = _pGame->GetWindow();
10:   HDC  hDC = GetDC(hWindow);
12:   // Paint the game to the offscreen device context
13:   GamePaint(_hOffscreenDC);
15:   // Blit the offscreen bitmap to the game screen
16:   BitBlt(hDC, 0, 0, _pGame->GetWidth(), _pGame->GetHeight(),
17:    _hOffscreenDC, 0, 0, SRCCOPY);
19:   // Cleanup
20:   ReleaseDC(hWindow, hDC);
21:  }
22: }

The GameCycle() function first checks to make sure that the game isn't over (line 3); in which case, there would be no need to update anything. The function then updates the sprites (line 6) and goes about redrawing the game graphics using double-buffer animation. This double-buffer code should be fairly familiar to you by now, so I won't go into the details. The main thing to notice is that the GamePaint() function is ultimately being used to draw the game graphics (line 13).

I mentioned earlier that the Henway game supports both keyboard and joystick input. Listing 12.6 contains the code for the HandleKeys() function, which takes care of processing and responding to keyboard input in the game.

Listing 12.6 The HandleKeys() Function Responds to the Arrow Keys on the Keyboard by Moving the Chicken

 1: void HandleKeys()
 2: {
 3:  if (!_bGameOver && (++_iInputDelay > 2))
 4:  {
 5:   // Move the chicken based upon key presses
 6:   if (GetAsyncKeyState(VK_LEFT) < 0)
 7:    MoveChicken(-20, 0);
 8:   else if (GetAsyncKeyState(VK_RIGHT) < 0)
 9:    MoveChicken(20, 0);
10:   if (GetAsyncKeyState(VK_UP) < 0)
11:    MoveChicken(0, -20);
12:   else if (GetAsyncKeyState(VK_DOWN) < 0)
13:    MoveChicken(0, 20);
15:   // Reset the input delay
16:   _iInputDelay = 0;
17:  }
18: }

The HandleKeys() function begins by making sure that the game isn't over, as well as incrementing and testing the input delay (line 3). By testing the input delay before processing any keyboard input, the HandleKeys() function effectively slows down the input so that the chicken is easier to control. The chicken is actually controlled via the arrow keys, which are checked using the Win32 GetAsyncKeyState() function. Each arrow key is handled by calling the MoveChicken() function, which moves the chicken by a specified amount (lines 6–13). After processing the keys, the input delay is reset so that the input process can be repeated (line 16).

The joystick is handled in the Henway game in a similar manner as the mouse, as Listing 12.7 reveals.

Listing 12.7 The HandleJoystick() Function Responds to Joystick Movements by Moving the Chicken, and also Supports Using the Primary Joystick Button to Start a New Game

 1: void HandleJoystick(JOYSTATE jsJoystickState)
 2: {
 3:  if (!_bGameOver && (++_iInputDelay > 2))
 4:  {
 5:   // Check horizontal movement
 6:   if (jsJoystickState & JOY_LEFT)
 7:     MoveChicken(-20, 0);
 8:   else if (jsJoystickState & JOY_RIGHT)
 9:     MoveChicken(20, 0);
11:   // Check vertical movement
12:   if (jsJoystickState & JOY_UP)
13:     MoveChicken(0, -20);
14:   else if (jsJoystickState & JOY_DOWN)
15:     MoveChicken(0, 20);
17:   // Reset the input delay
18:   _iInputDelay = 0;
19:  }
21:  // Check the joystick button and start a new game, if necessary
22:  if (_bGameOver && (jsJoystickState & JOY_FIRE1))
23:  {
24:   _iNumLives = 3;
25:   _iScore = 0;
26:   _bGameOver = FALSE;
27:  }
28: }

The HandleJoystick() function performs the same check on the _bGameOver and _iInputDelay variables to make sure that it is time to check the joystick for input (line 3). If so, the joystick is first checked for horizontal movement by examining the jsJoystickState argument passed in to the function. The chicken is then moved left or right, if necessary, by calling the MoveChicken() function (lines 6–9). A similar process is then repeated for vertical joystick movement (lines 12–15). After handling joystick movement, the HandleJoystick() function resets the _iInputDelay variable (line 18). The function then concludes by checking to see if the primary joystick button was pressed (line 22); in which case, a new game is started (lines 24–26).

Speaking of starting a new game, the mouse is used in the Henway game solely to start a new game if the current game has ended. Listing 12.8 shows the code for the MouseButtonDown() function, which starts a new game in response to a mouse button click.

Listing 12.8 The MouseButtonDown() Function Starts a New Game if the Current Game Is Over

 1: void MouseButtonDown(int x, int y, BOOL bLeft)
 2: {
 3:  // Start a new game, if necessary
 4:  if (_bGameOver)
 5:  {
 6:   _iNumLives = 3;
 7:   _iScore = 0;
 8:   _bGameOver = FALSE;
 9:  }
10: }

If a mouse button is clicked and the current game is over, the MouseButtonDown() function starts a new game by clearing the game state variables (lines 6–8).

The game play of the Henway game is largely dictated by the sprites in the game. These sprites are capable of colliding; in which case, the SpriteCollision() function gets called. Of course, the car sprites are designed to only move vertically up and down the screen, so they'll never hit each other. This means that the SpriteCollision() function only gets called when a car hits the chicken, or vice versa. Listing 12.9 shows how this collision is handled in the SpriteCollision() function.

Listing 12.9 The SpriteCollision() Function Checks to See if the Chicken Was Hit by a Car, and Then Responds Accordingly

 1: BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee)
 2: {
 3:  // See if the chicken was hit
 4:  if (pSpriteHittee == _pChickenSprite)
 5:  {
 6:   // Move the chicken back to the start
 7:   _pChickenSprite->SetPosition(4, 175);
 9:   // See if the game is over
10:   if (--_iNumLives > 0)
11:    MessageBox(_pGame->GetWindow(), TEXT("Ouch!"), TEXT("Henway"), MB_OK);
12:   else
13:   {
14:    // Display game over message
15:    TCHAR szText[64];
16:    wsprintf(szText, "Game Over! You scored %d points.", _iScore);
17:    MessageBox(_pGame->GetWindow(), szText, TEXT("Henway"), MB_OK);
18:    _bGameOver = TRUE;
19:   }
21:   return FALSE;
22:  }
24:  return TRUE;
25: }

Most of the game logic in the Henway game is located in the SpriteCollision() function. First, a check is made to ensure that the chicken was indeed involved in the collision (line 4). If so, you can safely assume that the chicken was hit by a car, so the chicken's position is restored to its starting position in the Start Area (line 7). The number of chicken lives is then decremented and checked to see if the game is over (line 10). If the game isn't over, a message is displayed indicating that you lost a chicken (line 11), but the game ultimately continues on. If the game is over, however, a special "Game Over" message is displayed and the _bGameOver variable is set to TRUE (lines 15–18).

If you recall from the design of the sprite manager, the return value of the SpriteCollision() function determines whether the sprite's old position is restored; a value of TRUE restores the old position, whereas FALSE allows the sprite to keep its newly updated position. In this case, the sprite keeps its new position when it collides with a car (line 21). Because the position was just set to the Start Area in line 7, the effect is that the chicken is allowed to move to the start area, which gives the appearance of a new chicken appearing.

The final function in the Henway game is the MoveChicken() function, which you've used several times throughout the game code. Listing 12.10 shows the code for the MoveChicken() function.

Listing 12.10 The MoveChicken() Function Moves the Chicken Sprite by a Specified Distance While Checking to See if the Chicken Made It Across the Highway

 1: void MoveChicken(int iXDistance, int iYDistance)
 2: {
 3:  // Move the chicken to its new position
 4:  _pChickenSprite->OffsetPosition(iXDistance, iYDistance);
 6:  // See if the chicken made it across
 7:  if (_pChickenSprite->GetPosition().left > 400)
 8:  {
 9:   // Move the chicken back to the start and add to the score
10:   _pChickenSprite->SetPosition(4, 175);
11:   _iScore += 150;
12:   MessageBox(_pGame->GetWindow(), TEXT("You made it!"), TEXT("Henway"),
13:    MB_OK);
14:  }
15: }

The MoveChicken() function is a helper function that simplifies the task of moving the chicken around on the game screen. The iXDistance and iYDistance arguments specify how many pixels to move the chicken in the X and Y directions (line 1). These arguments are used to move the chicken by calling the OffsetPosition() method on the Sprite class (line 4). If you didn't care what happened to the chicken, this is all the code you would need in the MoveChicken() function. However, you need to know when the chicken makes it across the highway, and this is a perfect place to perform the check (line 7). If the chicken made it safely across, its position is set back to the Start Area (line 10) and the score is increased (11). A message is also displayed that notifies the player of a successful highway crossing (lines 12–13) .

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information

To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.


Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.


If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information

Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.


This site is not directed to children under the age of 13.


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information

If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information

Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents

California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure

Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact

Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice

We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020