Sol's Graphics for Beginners

33 - Prettier Main Menu

At this point the tasks are more and more just connecting the dots, so I won't be explaining many things in detail.

In chapter 18. Bumping Along we made a 2d bumpmapping effect. Let's integrate it to the main menu.

First, add these global variables:

// Main menu background
SDL_Surface *gMenuBg;
// Light surface
SDL_Surface *gLight;

// Bumpmap lookup table
short *gBumpLut;

Next, in loadresources(), load the images and process the heightmap..

temp = SDL_LoadBMP("menubg.bmp");
  gMenuBg = SDL_ConvertSurface(temp, gScreen->format, SDL_SWSURFACE);
  SDL_FreeSurface(temp);

  temp = SDL_LoadBMP("menubg_ht.bmp");
  SDL_Surface *heightmap = SDL_ConvertSurface(temp, gScreen->format, SDL_SWSURFACE);
  SDL_FreeSurface(temp);

  temp = SDL_LoadBMP("menubg_lt.bmp");
  gLight = SDL_ConvertSurface(temp, gScreen->format, SDL_SWSURFACE);
  SDL_FreeSurface(temp);

  int i, j, k;

  gBumpLut = new short[WIDTH * HEIGHT];
  memset(gBumpLut, 0, sizeof(short) * WIDTH * HEIGHT);

  if (SDL_MUSTLOCK(heightmap))
    if (SDL_LockSurface(heightmap) < 0) 
      return;

  for (i = 1; i < HEIGHT - 1; i++)
  {
    for (j = 1; j < WIDTH - 1; j++)
    {
      int ydiff = 
        ((((unsigned int*)heightmap->pixels)[j + (i + 1) * (heightmap->pitch / 4)] & 0xff) -
         (((unsigned int*)heightmap->pixels)[j + (i - 1) * (heightmap->pitch / 4)] & 0xff));
      int xdiff = 
        ((((unsigned int*)heightmap->pixels)[j + 1 + i * (heightmap->pitch / 4)] & 0xff) -
         (((unsigned int*)heightmap->pixels)[j - 1 + i * (heightmap->pitch / 4)] & 0xff));

      gBumpLut[i * WIDTH + j] = ((ydiff & 0xff) << 8) | (xdiff & 0xff);
    }
  }

  if (SDL_MUSTLOCK(heightmap)) 
    SDL_UnlockSurface(heightmap);
  SDL_FreeSurface(heightmap);

  gLut = new unsigned short[WIDTH * HEIGHT * 4];
  gMask = new unsigned int[WIDTH * HEIGHT * 4];

If you compile, you may get an error about the redefinition of int i,j and k - remove the second declaration.

Next, create a new function called menubgeffect():

if (SDL_MUSTLOCK(gMenuBg))
    if (SDL_LockSurface(gMenuBg) < 0) 
      return;

  if (SDL_MUSTLOCK(gLight))
    if (SDL_LockSurface(gLight) < 0) 
      return;

  int posx = gUIState.mMouseX - 128;
  int posy = gUIState.mMouseY - 128;
  int i, j;
  for (i = 0; i < HEIGHT; i++)
  {
    for (j = 0; j < WIDTH; j++)
    {
      int u = j + ((signed char)gBumpLut[i * WIDTH + j]) - posx;
      int v = i + (gBumpLut[i * WIDTH + j] / 256) - posy;
      if (v < 0 || v >= gLight->w ||
        u < 0 || u >= gLight->h)
      {
        ((unsigned int*)gScreen->pixels)[(j) + (i) * PITCH] = 
          ((unsigned int*)gMenuBg->pixels)[(j) + (i) * (gMenuBg->pitch / 4)];
      }
      else
      {
        ((unsigned int*)gScreen->pixels)[(j) + (i) * PITCH] = 
          blend_add(
          ((unsigned int*)gMenuBg->pixels)[(j) + (i) * (gMenuBg->pitch / 4)],
          ((unsigned int*)gLight->pixels)[(u) + (v) * (gLight->pitch / 4)]);
      }
    }
  }

  if (SDL_MUSTLOCK(gLight)) 
    SDL_UnlockSurface(gLight);

  if (SDL_MUSTLOCK(gMenuBg)) 
    SDL_UnlockSurface(gMenuBg);

Finally, replace the screen-clearing drawrect with:

// Render the menu background effect
  menubgeffect();

After this the main menu will look somewhat different.

Let's add the credits screen as well, with the same background effect. First, go to changestate() and change the STATE_CREDITS blocks to open and close the imgui system, the same way as was done with the main menu itself.

Next, go to render() and add a case for the credits rendering, like so:

case STATE_CREDITS:
    rendercredits();
    break;

And the rendering function is pretty much the same as for the main menu..

void rendercredits()
{
  // Lock surface if needed
  if (SDL_MUSTLOCK(gScreen))
    if (SDL_LockSurface(gScreen) < 0) 
      return;

  // Render the menu background effect
  menubgeffect();

  // Set up IMGUI block
  imgui_begin();


  drawstring((WIDTH - 90) / 2, (HEIGHT / 2) - 40, &quot;Copyright 2006&quot;);
  drawstring((WIDTH - 80) / 2, (HEIGHT / 2) - 20, &quot;Jari Komppa&quot;);
  drawstring((WIDTH - 90) / 2, (HEIGHT / 2), &quot;http://iki.fi/sol/gp/&quot;);

  if (imgui_button(GEN_ID, (WIDTH - 100) / 2,(HEIGHT / 2) + 60, 100, 20, &quot;Return to main&quot;))
  {
    gNextState = STATE_MAINMENU;
  }

  imgui_end();

  // Unlock if needed
  if (SDL_MUSTLOCK(gScreen)) 
    SDL_UnlockSurface(gScreen);

  // Tell SDL to update the whole gScreen
  SDL_UpdateRect(gScreen, 0, 0, WIDTH, HEIGHT);    

  // Don't hog all the CPU power
  SDL_Delay(5);
}

After this, there's only three more new states that we haven't covered. In order to do so, we'll have to introduce a new concept.

Next we'll be changing the game structure by adding 34. Stages..

Having problems? Improvement ideas? Just want to discuss this tutorial? Try the forums!

Any comments etc. can be emailed to me.