Skip to content

Latest commit

 

History

History
154 lines (128 loc) · 5.63 KB

File metadata and controls

154 lines (128 loc) · 5.63 KB

Handling blitted object undraw

So far, you've managed to load and display the background, but paddles are trashing it with black color. Now it's time to handle proper background restoring.

In order to fix this, you need to create bitmaps which you'll use as buffers to store background beneath the paddles. To do so, add following code:

// ...old defines go here...
//-------------------------------------------------------------- NEW STUFF START
#define PADDLE_BG_BUFFER_WIDTH CEIL_TO_FACTOR(PADDLE_WIDTH, 16)
#define BALL_BG_BUFFER_WIDTH CEIL_TO_FACTOR(BALL_WIDTH, 16)
//---------------------------------------------------------------- NEW STUFF END

// ...old static variables go here...
//-------------------------------------------------------------- NEW STUFF START
static tBitMap *s_pBmPaddleLeftBg;
static tBitMap *s_pBmPaddleRightBg;
static tBitMap *s_pBmPaddleBallBg;
static UBYTE s_hasBackgroundToRestore;
//---------------------------------------------------------------- NEW STUFF END

void gameGsCreate(void) {
  //... old code here
  // Draw line separating score VPort and main VPort, leave one line blank after it
  blitLine(
    s_pScoreBuffer->pBack,
    0, s_pVpScore->uwHeight - 2,
    s_pVpScore->uwWidth - 1, s_pVpScore->uwHeight - 2,
    SCORE_COLOR, 0xFFFF, 0 // Try patterns 0xAAAA, 0xEEEE, etc.
  );

//-------------------------------------------------------------- NEW STUFF START
  s_pBmPaddleLeftBg = bitmapCreate(PADDLE_BG_BUFFER_WIDTH, PADDLE_HEIGHT, 4, 0);
  s_pBmPaddleRightBg = bitmapCreate(PADDLE_BG_BUFFER_WIDTH, PADDLE_HEIGHT, 4, 0);
  s_pBmPaddleBallBg = bitmapCreate(BALL_BG_BUFFER_WIDTH, BALL_WIDTH, 4, 0);
  s_hasBackgroundToRestore = 0;
//---------------------------------------------------------------- NEW STUFF END

  systemUnuse();

  // Load the view
  viewLoad(s_pView);
}

void gameGsDestroy(void) {
  systemUse();

//-------------------------------------------------------------- NEW STUFF START
  bitmapDestroy(s_pBmPaddleLeftBg);
  bitmapDestroy(s_pBmPaddleRightBg);
  bitmapDestroy(s_pBmPaddleBallBg);
//---------------------------------------------------------------- NEW STUFF END

  // This will also destroy all associated viewports and viewport managers
  viewDestroy(s_pView);
}

This will create background buffers for both paddles and the ball the moment the game state gets created, and they will be destroyed along with it. Note that bitmap widths are dependent on PADDLE_BG_BUFFER_WIDTH and its ball equivalent - remember that bitmaps need to have width equal to multiple of 16 - CEIL_TO_FACTOR() allows to easily ensure this.

Now it all boils down to saving fragments of display bitmap on paddles' positions before drawing them, and drawing them back on the start of the frame. You need to also do the check, which will be used for the first loop pass, if background was actually saved before restoring it - without it random garbage contents from paddle background buffers would appear.

void gameGsLoop(void) {
  // This will loop every frame
  if(keyCheck(KEY_ESCAPE)) {
    gameExit();
    return; // early return - don't process anything else, no need for big `else` anymore
  }

  UWORD uwPaddleRightPosX = s_pVpMain->uwWidth - PADDLE_WIDTH;

  if(s_hasBackgroundToRestore) {
    // Restore background under left paddle
    blitCopy(
      s_pBmPaddleLeftBg, 0, 0,
      s_pMainBuffer->pBack, 0, uwPaddleLeftPosY,
      PADDLE_WIDTH, PADDLE_HEIGHT, MINTERM_COOKIE
    );

    // Restore background under right paddle
    blitCopy(
      s_pBmPaddleRightBg, 0, 0,
      s_pMainBuffer->pBack, uwPaddleRightPosX, uwPaddleRightPosY,
      PADDLE_WIDTH, PADDLE_HEIGHT, MINTERM_COOKIE
    );
  }

  // Update left paddle position
  if(keyCheck(KEY_S)) {
    uwPaddleLeftPosY = MIN(uwPaddleLeftPosY + PADDLE_SPEED, PADDLE_MAX_POS_Y);
  }
  else if(keyCheck(KEY_W)) {
    uwPaddleLeftPosY = MAX(uwPaddleLeftPosY - PADDLE_SPEED, 0);
  }

  // Update right paddle position
  if(keyCheck(KEY_DOWN)) {
    uwPaddleRightPosY = MIN(uwPaddleRightPosY + PADDLE_SPEED, PADDLE_MAX_POS_Y);
  }
  else if(keyCheck(KEY_UP)) {
    uwPaddleRightPosY = MAX(uwPaddleRightPosY - PADDLE_SPEED, 0);
  }

  // Save background under left paddle
  blitCopy(
    s_pMainBuffer->pBack, 0, uwPaddleLeftPosY,
    s_pBmPaddleLeftBg, 0, 0,
    PADDLE_WIDTH, PADDLE_HEIGHT, MINTERM_COOKIE
  );

  // Save background under right paddle
  blitCopy(
    s_pMainBuffer->pBack, uwPaddleRightPosX, uwPaddleRightPosY,
    s_pBmPaddleRightBg, 0, 0,
    PADDLE_WIDTH, PADDLE_HEIGHT, MINTERM_COOKIE
  );
  s_hasBackgroundToRestore = 1;

  // Draw left paddle
  blitRect(
    s_pMainBuffer->pBack, 0, uwPaddleLeftPosY,
    PADDLE_WIDTH, PADDLE_HEIGHT, PADDLE_LEFT_COLOR
  );

  // Draw right paddle
  blitRect(
    s_pMainBuffer->pBack, uwPaddleRightPosX, uwPaddleRightPosY,
    PADDLE_WIDTH, PADDLE_HEIGHT, PADDLE_RIGHT_COLOR
  );

  // Draw ball
  blitRect(
    s_pMainBuffer->pBack,
    // x center: half of screen width minus half of ball
    (s_pVpMain->uwWidth - BALL_WIDTH) / 2,
    // y center: half of screen height minus half of ball
    (s_pVpMain->uwHeight - BALL_WIDTH) / 2,
    BALL_WIDTH, BALL_WIDTH, BALL_COLOR
  );
  vPortWaitForEnd(s_pVpMain);
}

As you can now observe, background is restored correctly. There's a problem however - in debug builds, the right paddle flickers on its topmost position. This is due to using single-buffered mode - the game simply works too slow to draw everything before it gets displayed on-screen. Switching to release builds in your CMake options should help for now, but there are also some optimizations to be done which could help speed up blitter operations.

The next step is using proper paddle and ball graphics as well as animating the ball itself.