Programming 2D Games 读书笔记(第五章)

时间:2023-02-06 23:34:01

 

http://www.programming2dgames.com/chapter5.htm

示例一:Planet

真正示例的开始,首先是载入2张图片

1.Graphics添加了2个方法

loadTexture和drawSprite

loadTexture方法得到一个IRECT3DTEXTURE9接口

//=============================================================================
// Load the texture into default D3D memory (normal texture use)
// For internal engine use only. Use the TextureManager class to load game textures.
// Pre: filename is name of texture file.
//      transcolor is transparent color
// Post: width and height = size of texture
//       texture points to texture
// Returns HRESULT
//=============================================================================
HRESULT Graphics::loadTexture(const char *filename, COLOR_ARGB transcolor,
                                UINT &width, UINT &height, LP_TEXTURE &texture)
{
    // The struct for reading file info
    D3DXIMAGE_INFO info;
    result = E_FAIL;

    try{
        if(filename == NULL)
        {
            texture = NULL;
            return D3DERR_INVALIDCALL;
        }
    
        // Get width and height from file
        result = D3DXGetImageInfoFromFile(filename, &info);
        if (result != D3D_OK)
            return result;
        width = info.Width;
        height = info.Height;
    
        // Create the new texture by loading from file
        result = D3DXCreateTextureFromFileEx( 
            device3d,           //3D device
            filename,           //image filename
            info.Width,         //texture width
            info.Height,        //texture height
            1,                  //mip-map levels (1 for no chain)
            0,                  //usage
            D3DFMT_UNKNOWN,     //surface format (default)
            D3DPOOL_DEFAULT,    //memory class for the texture
            D3DX_DEFAULT,       //image filter
            D3DX_DEFAULT,       //mip filter
            transcolor,         //color key for transparency
            &info,              //bitmap file info (from loaded file)
            NULL,               //color palette
            &texture );         //destination texture

    } catch(...)
    {
        throw(GameError(gameErrorNS::FATAL_ERROR, "Error in Graphics::loadTexture"));
    }
    return result;
}

drawSprite方法则是画精灵元素

//=============================================================================
// Draw the sprite described in SpriteData structure
// Color is optional, it is applied like a filter, WHITE is default (no change)
// Pre : sprite->Begin() is called
// Post: sprite->End() is called
// spriteData.rect defines the portion of spriteData.texture to draw
//   spriteData.rect.right must be right edge + 1
//   spriteData.rect.bottom must be bottom edge + 1
//=============================================================================
void Graphics::drawSprite(const SpriteData &spriteData, COLOR_ARGB color)
{
    if(spriteData.texture == NULL)      // if no texture
        return;

    // Find center of sprite
    D3DXVECTOR2 spriteCenter=D3DXVECTOR2((float)(spriteData.width/2*spriteData.scale),
                                        (float)(spriteData.height/2*spriteData.scale));
    // Screen position of the sprite
    D3DXVECTOR2 translate=D3DXVECTOR2((float)spriteData.x,(float)spriteData.y);
    // Scaling X,Y
    D3DXVECTOR2 scaling(spriteData.scale,spriteData.scale);
    if (spriteData.flipHorizontal)  // if flip horizontal
    {
        scaling.x *= -1;            // negative X scale to flip
        // Get center of flipped image.
        spriteCenter.x -= (float)(spriteData.width*spriteData.scale);
        // Flip occurs around left edge, translate right to put
        // Flipped image in same location as original.
        translate.x += (float)(spriteData.width*spriteData.scale);
    }
    if (spriteData.flipVertical)    // if flip vertical
    {
        scaling.y *= -1;            // negative Y scale to flip
        // Get center of flipped image
        spriteCenter.y -= (float)(spriteData.height*spriteData.scale);
        // Flip occurs around top edge, translate down to put
        // Flipped image in same location as original.
        translate.y += (float)(spriteData.height*spriteData.scale);
    }
    // Create a matrix to rotate, scale and position our sprite
    D3DXMATRIX matrix;
    D3DXMatrixTransformation2D(
        &matrix,                // the matrix
        NULL,                   // keep origin at top left when scaling
        0.0f,                   // no scaling rotation
        &scaling,               // scale amount
        &spriteCenter,          // rotation center
        (float)(spriteData.angle),  // rotation angle
        &translate);            // X,Y location

    // Tell the sprite about the matrix "Hello Neo"
    sprite->SetTransform(&matrix);

    // Draw the sprite
    sprite->Draw(spriteData.texture,&spriteData.rect,NULL,NULL,color);
}

2.Texture的2个包装类

TextureManager(依赖loadTexture)用于载入一个IRECT3DTEXTURE9对象,Image类(依赖drawSprite)用于获取TextureManager的IRECT3DTEXTURE9对象并进行绘制

Programming 2D Games 读书笔记(第五章)

TextureManager

//=============================================================================
// Loads the texture file from disk.
// Post: returns true if successful, false if failed
//=============================================================================
bool TextureManager::initialize(Graphics *g, const char *f)
{
    try{
        graphics = g;                       // the graphics object
        file = f;                           // the texture file

        hr = graphics->loadTexture(file, TRANSCOLOR, width, height, texture);
        if (FAILED(hr))
        {
            SAFE_RELEASE(texture);
            return false;
        }
    }
    catch(...) {return false;}
    initialized = true;                    // set true when successfully initialized
    return true;
}

Image

//=============================================================================
// Draw this image using the specified SpriteData.
//   The current SpriteData.rect is used to select the texture.
// Pre : spriteBegin() is called
// Post: spriteEnd() is called
//=============================================================================
void Image::draw(SpriteData sd, COLOR_ARGB color)
{
    if (!visible || graphics == NULL)
        return;
    sd.rect = spriteData.rect;                  // use this Images rect to select texture
    sd.texture = textureManager->getTexture();  // get fresh texture incase onReset() was called

    if(color == graphicsNS::FILTER)             // if draw with filter
        graphics->drawSprite(sd, colorFilter);  // use colorFilter
    else
        graphics->drawSprite(sd, color);        // use color as filter
}

Spacewar载入图片

1.initialize

//=============================================================================
// Initializes the game
// Throws GameError on error
//=============================================================================
void Spacewar::initialize(HWND hwnd)
{
    Game::initialize(hwnd); // throws GameError

    // nebula texture
    if (!nebulaTexture.initialize(graphics,NEBULA_IMAGE))
        throw(GameError(gameErrorNS::FATAL_ERROR, "Error initializing nebula texture"));

    // planet texture
    if (!planetTexture.initialize(graphics,PLANET_IMAGE))
        throw(GameError(gameErrorNS::FATAL_ERROR, "Error initializing planet texture"));

    // nebula
    if (!nebula.initialize(graphics,0,0,0,&nebulaTexture))
        throw(GameError(gameErrorNS::FATAL_ERROR, "Error initializing nebula"));

    // planet
    if (!planet.initialize(graphics,0,0,0,&planetTexture))
        throw(GameError(gameErrorNS::FATAL_ERROR, "Error initializing planet"));
    // place planet in center of screen
    planet.setX(GAME_WIDTH*0.5f  - planet.getWidth()*0.5f);
    planet.setY(GAME_HEIGHT*0.5f - planet.getHeight()*0.5f);

    return;
}

2.render

//=============================================================================
// Render game items
//=============================================================================
void Spacewar::render()
{
    graphics->spriteBegin();                // begin drawing sprites

    nebula.draw();                          // add the orion nebula to the scene
    planet.draw();                          // add the planet to the scene

    graphics->spriteEnd();                  // end drawing sprites
}

呈现了2个图片

Programming 2D Games 读书笔记(第五章)

示例二:Spaceship

动画

Programming 2D Games 读书笔记(第五章)

重复更新1张图的4个位置

//============================================================================= // update // typically called once per frame // frameTime is used to regulate the speed of movement and animation //============================================================================= void Image::update(float frameTime)
{
    if (endFrame - startFrame > 0)          // if animated sprite {
        animTimer += frameTime;             // total elapsed time if (animTimer > frameDelay)
        {
            animTimer -= frameDelay;
            currentFrame++;
            if (currentFrame < startFrame || currentFrame > endFrame)
            {
                if(loop == true)            // if looping animation currentFrame = startFrame;
                else // not looping animation {
                    currentFrame = endFrame;
                    animComplete = true;    // animation complete }
            }
            setRect();                      // set spriteData.rect }
    }
}
//============================================================================= // Set spriteData.rect to draw currentFrame //============================================================================= inline void Image::setRect() 
{
    // configure spriteData.rect to draw currentFrame spriteData.rect.left = (currentFrame % cols) * spriteData.width;
    // right edge + 1 spriteData.rect.right = spriteData.rect.left + spriteData.width;
    spriteData.rect.top = (currentFrame / cols) * spriteData.height;
    // bottom edge + 1 spriteData.rect.bottom = spriteData.rect.top + spriteData.height;       
}

示例三:Spaceship Movement

各种效果叠加组成的动画效果(图片更换,角度,大小,位置)来模拟

//============================================================================= // Update all game items //============================================================================= void Spacewar::update()
{
    ship.update(frameTime);
    ship.setDegrees(ship.getDegrees() + frameTime * ROTATION_RATE); // rotate ship ship.setScale(ship.getScale() - frameTime * SCALE_RATE);    // make ship smaller ship.setX(ship.getX() + frameTime * SHIP_SPEED);    // move ship right if (ship.getX() > GAME_WIDTH)                       // if off screen right {
        ship.setX((float)-ship.getWidth());             // position off screen left ship.setScale(SHIP_SCALE);                      // set to starting size }
}

示例四:Spaceship Control

根据键盘来移动元素

//============================================================================= // Update all game items //============================================================================= void Spacewar::update()
{
    if(input->isKeyDown(SHIP_RIGHT_KEY))            // if move right {
        ship.setX(ship.getX() + frameTime * SHIP_SPEED);
        if (ship.getX() > GAME_WIDTH)               // if off screen right ship.setX((float)-ship.getWidth());     // position off screen left }
    if(input->isKeyDown(SHIP_LEFT_KEY))             // if move left {
        ship.setX(ship.getX() - frameTime * SHIP_SPEED);
        if (ship.getX() < -ship.getWidth())         // if off screen left ship.setX((float)GAME_WIDTH);           // position off screen right }
    if(input->isKeyDown(SHIP_UP_KEY))               // if move up {
        ship.setY(ship.getY() - frameTime * SHIP_SPEED);
        if (ship.getY() < -ship.getHeight())        // if off screen top ship.setY((float)GAME_HEIGHT);          // position off screen bottom }
    if(input->isKeyDown(SHIP_DOWN_KEY))             // if move down {
        ship.setY(ship.getY() + frameTime * SHIP_SPEED);
        if (ship.getY() > GAME_HEIGHT)              // if off screen bottom ship.setY((float)-ship.getHeight());    // position off screen top }

    ship.update(frameTime);
}