0

次のエラーが表示されます。

1>------ ビルド開始: プロジェクト: TankSample、構成: Win32 をデバッグ ------

1>Debug\BackBuffer.obj: 警告 LNK4042: オブジェクトが複数回指定されました。エキストラは無視

1>Source.obj: エラー LNK2019: 未解決の外部シンボル "public: thiscall BackBuffer::BackBuffer(struct HWND *,int,int)" (??0BackBuffer@@QAE@PAUHWND__@@HH@Z) 関数 "long で参照stdcall WndProc(struct HWND *,unsigned int,unsigned int,long)" (?WndProc@@YGJPAUHWND__@@IIJ@Z)

1>Source.obj : エラー LNK2019: 未解決の外部シンボル "public: __thiscall BackBuffer::~BackBuffer(void)" (??1BackBuffer@@QAE@XZ) 関数で参照されている "public: void * __thiscall BackBuffer::`scalar の削除デストラクタ'(unsigned int)" (??_GBackBuffer@@QAEPAXI@Z)

1>Source.obj: エラー LNK2019: 未解決の外部シンボル "public: struct HDC__ * thiscall BackBuffer::getDC(void)" (?getDC@BackBuffer@@QAEPAUHDC @@XZ) が関数 "int __cdecl Run(void)" で参照されています(?Run@@YAHXZ)

1>Source.obj: エラー LNK2019: 未解決の外部シンボル "public: void __thiscall BackBuffer::present(void)" (?present@BackBuffer@@QAEXXZ) が関数 "int __cdecl Run(void)" で参照されています (?Run@@ YAHXZ)

1>C:\Users\Josh\Documents\Game Institute\C++\Module Two\Exercises\Chapter 17\TankSample\Debug\TankSample.exe : 致命的なエラー LNK1120: 4 つの未解決の外部 ========== ビルド: 0 成功、1 失敗、0 最新、0 スキップ ==========

このコードをコンパイルしようとすると:

バックバッファ.h

#pragma once

#include <Windows.h>

class BackBuffer
{
public:
    BackBuffer( HWND hWnd, int width, int height );
    ~BackBuffer();

    HDC getDC();

    int width();
    int height();

    void present();

//private:
    // Make copy constructor and assignment operator private so
    // client cannot copy BackBuffers. We do this because this
    // class is not designed to be copied because it is not
    // efficient--copying bitmaps is slow. (Lots of memory).
    //BackBuffer( const BackBuffer& rhs );
    //BackBuffer& operator=( const BackBuffer& rhs );

private:
    HWND    mhWnd;       // A handle to the main window. We need this to obtain a device context associated to the main window.
    HDC     mhDC;        // A handle to the system memoy DC.
    HBITMAP mhSurface;   // A handle to the bitmap that serves as our backbuffer.
    HBITMAP mhOldObject; // A handle to the previous bitmap.
    int     mWidth;      // The width of the bitmap matrix.
    int     mHeight;     // The height of the bitmap matrix.

};

バックバッファ.cpp

#include "BackBuffer.h"

BackBuffer::BackBuffer( HWND hWnd, int width, int height ) : mhWnd( hWnd ), mWidth( width ), mHeight ( height )
{

    // Get a handle to the device context associated with the window.
    HDC hWndDC = GetDC( hWnd );

    // Create system memory DC that is compatible with the window one.
    mhDC = CreateCompatibleDC( hWndDC );

    // Create the backbuffer surface bitmap that is compatible with the window
    // DC bitmap format. That is the surface we will render onto.
    mhSurface = CreateCompatibleBitmap( mhDC, mWidth, mHeight );

    // Done with window DC.
    ReleaseDC( hWnd, hWndDC );

    // At this point, the backbuffer surface is uninitialized, so lets clear it
    // to some non-zero value. Note that it needs to be non-zero. If it is zero
    // then it will mess up our sprite blending logic.

    // Select the backbuffer bitmap into the DC.
    mhOldObject = (HBITMAP)SelectObject( hWndDC, mhSurface );

    // Select a white brush.
    HBRUSH white    = (HBRUSH)GetStockObject( WHITE_BRUSH );
    HBRUSH oldBrush = (HBRUSH)SelectObject( mhDC, white );

    // Clear the backbuffer rectangle.
    Rectangle( hWndDC, 0, 0, mWidth, mHeight );

    // Restore the original brush.
    SelectObject( mhDC, oldBrush );

}
/*
BackBuffer::BackBuffer( const BackBuffer& rhs )
{
    *this = rhs;
}

BackBuffer& BackBuffer::operator=( const BackBuffer& rhs )
{
    if( this == &rhs )
        return *this;

    mhWnd       = rhs.mhWnd;       
    mhDC        = rhs.mhDC;
    mhSurface   = rhs.mhSurface;
    mhOldObject = rhs.mhOldObject;
    mWidth      = rhs.mWidth;
    mHeight     = rhs.mHeight;

    return *this;
}*/


BackBuffer::~BackBuffer()
{
    // Restore the original selected bitmap before deleting anything.
    SelectObject( mhDC, mhOldObject );

    // Delete the backbuffer bitmap.
    DeleteObject( mhSurface );

    // Delete the DC.
    DeleteDC( mhDC );
}

HDC BackBuffer::getDC()
{
    return mhDC;
}

int BackBuffer::width()
{
    return mWidth;
}

int BackBuffer::height()
{
    return mHeight;
}

void BackBuffer::present()
{
    // Get a handle to the DC associated with the window.
    HDC hWndDC = GetDC( mhWnd );

    // Copy the backbuffer contents over to the windows client area.
    BitBlt( hWndDC, 0, 0, mWidth, mHeight, mhDC, 0, 0, SRCCOPY );

    // Always free a window DC when done.
    ReleaseDC( mhWnd, hWndDC );
}

Vec2.h

#pragma once

#include <cmath>

class Vec2
{
public:
    // Constructors
    Vec2() : x( 0.0f ), y( 0.0f ){};
    Vec2( float coords[2] ) : x( coords[0] ), y( coords[1] ){};
    Vec2( const POINT& p ) : x( p.x ), y( p.y ){};
    Vec2( float v1, float v2 ) : x( v1 ), y( v2 ){};

    Vec2( const Vec2& rhs )
    {
        *this = rhs;            
    }

    Vec2& operator=( const Vec2& rhs )
    {
        if( this == &rhs )
            return *this;

        x = rhs.x;
        y = rhs.y;

        return *this;
    }

    ~Vec2(){};

    Vec2 operator+( const Vec2& rhs ) const
    {
        Vec2 sum;

        sum.x = x + rhs.x;
        sum.y = y + rhs.y;

        return sum;
    }

    Vec2 operator-( const Vec2& rhs ) const
    {
        Vec2 result;

        result.x = x - rhs.x;
        result.y = y - rhs.y;

        return result;
    }

    Vec2 operator-()
    {
        Vec2 result;
        x = -x;
        y = -y;

        return result;
    }

    void operator*=( float scalar )
    {
        x *= scalar;
        y *= scalar;
    }

    void operator+=( const Vec2& rhs )
    {
        x += rhs.x;
        y += rhs.y;
    }

    void operator-=( const Vec2& rhs )
    {
        x -= rhs.x;
        y -= rhs.y;
    }

    void operator/=( const Vec2& rhs )
    {
        // Assumes rhs != 0
        x /= rhs.x;
        y /= rhs.y;
    }

    // Convert to point.
    operator POINT()
    {
        POINT p = {(int)x, (int)y};
        return p;
    };

    // Return length.
    float length()
    {
        return sqrtf( x * y + y * y );
    }

    float dot( const Vec2& rhs )
    {
        return x * rhs.x + y * rhs.y;
    }

    Vec2& rotate( float t )
    {
        float newX;
        newX = x * cosf(t) - y * sinf(t);
        y    = y * cosf(t) + x * sinf(t);

        this->x = newX;
        return *this;
    }

    // Data members.
    float x;
    float y;

};

Vec2 operator*( const Vec2& rhs, float s )
{
    Vec2 result;

    result.x = rhs.x * s;
    result.y = rhs.y * s;

    return result;
}

Vec2 operator/( const Vec2& rhs, float s )
{
    Vec2 result;

    result.x = rhs.x / s;
    result.y = rhs.y / s;

    return result;
}

ソース.cpp

#include <Windows.h>
#include <string>
#include <list>
#include "BackBuffer.h"
#include "Vec2.h"
#include "resource.h"

using namespace std;

// WINDOWS HANDLES
HWND      ghMainWnd  = 0;
HINSTANCE ghAppInst  = 0;
HMENU     ghMainMenu = 0;

// The backbuffer we will render onto.
BackBuffer* gBackBuffer = 0;

// The text that will appear in the main window's caption bar.
string gWndCaption = "Tank Sample";

// Client rectangle dimensions we will use.
const int gClientWidth  = 800;
const int gClientHeight = 600;

const POINT gClientCenter =
{
    gClientWidth / 2,
    gClientHeight / 2
};

// Pad window dimensions so that there is room for window
// borders, caption bar, and menu.
const int gWindowWidth  = gClientWidth + 6;
const int gWindowHeight = gClientHeight + 52;

// Client area rectangle.
RECT gMapRect = {0, 0, 800, 600};

// Vector to store the center position of the tank
// relative to the client area rectangle.
Vec2 gTankPos( 400.0f, 300.0f );

// Handle to the pen we will use to draw the tanks' gun.
HPEN gGunPen;

// A vector describing the direction of the tanks' gun
// is aimed in. The vectors' magnitude denotes the 
// length of the gun.
Vec2 gGunDir( 0.0f, -120.0f );

// A list, where we will add bullets to as they are fired.
// The list stores the bullets positions, so that we can draw
// an ellipse at the positino of each bullet.
list<Vec2> gBulletList;

//==============================================
// Function Prototypes
//==============================================

bool InitMainWindow();
int Run();
void DrawFramesPerSecond( float deltaTime );

LRESULT CALLBACK
WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );

//==============================================
// Name: WinMain
// Desc: Program execution starts here.
//==============================================
int WINAPI
WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR cmdLine, int showCmd )
{

    ghAppInst = hInstance;

    if( !InitMainWindow() )
    {
        MessageBox( 0, "Window Creation Failed", "Error", MB_OK );
        return 0;
    }


    return Run();

}

//==============================================
// Name: InitMainWindow
// Desc: Creates the main window upon which
//       we will draw onto.
//==============================================

bool InitMainWindow()
{
    WNDCLASS wc; 
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = ghAppInst;
    wc.hIcon         = ::LoadIcon(0, IDI_APPLICATION);
    wc.hCursor       = ::LoadCursor(0, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)::GetStockObject(NULL_BRUSH);
    wc.lpszMenuName  = 0;
    wc.lpszClassName = "MyWndClassName";

    RegisterClass( &wc );

    // WS_OVERLAPPED | WS_SYSMENU: Window cannot be resized
    // and does not have a min/max button.  
    ghMainMenu = LoadMenu(ghAppInst, MAKEINTRESOURCE(IDR_MENU1));

    ghMainWnd = CreateWindow("MyWndClassName", gWndCaption.c_str(), WS_OVERLAPPED | WS_SYSMENU, 200, 200, gWindowWidth, gWindowHeight, 0, ghMainMenu, ghAppInst, 0);

    if(ghMainWnd == 0)
    {
        ::MessageBox(0, "CreateWindow - Failed", 0, 0);
        return 0;
    }

    ShowWindow(ghMainWnd, SW_NORMAL);
    UpdateWindow(ghMainWnd);

    return true;
}

//=========================================================
// Name: Run
// Desc: Encapsulates the message loop.
//=========================================================
int Run()
{
    MSG msg;
    ZeroMemory( &msg, sizeof( MSG ) );

    // Get the performance timer frequency.
    __int64 cntsPerSec = 0;
    bool prefExists = QueryPerformanceFrequency( ( LARGE_INTEGER* )&cntsPerSec) != 0;
    if( !prefExists )
    {
        MessageBox( 0, "Perforamce timer doesn't exist.", 0, 0 );
        return 0;
    }

    double timeScale = 1.0 / (double)cntsPerSec;

    // Get the current time.
    __int64 lastTime = 0;
    QueryPerformanceCounter( ( LARGE_INTEGER* )&lastTime );

    double timeElasped = 0;

    while( msg.message != WM_QUIT )
    {

        // IF there is a windows message then process it.
        if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else // DO GAME STUFF
        {
            // Get the time now.
            __int64 currTime = 0; 
            QueryPerformanceCounter( ( LARGE_INTEGER* )&currTime );

            // Compute the differences in time from the last time
            // we checked. Since the last time we checked was given
            // the previous loop iteration, this difference gives
            // us the time between loop iterations...
            // or, I.E., the time between frames.
            double deltaTime = double( currTime - lastTime ) * timeScale;

            timeElasped += deltaTime;

            // Get the backbuffer DC.
            HDC bbDC = gBackBuffer->getDC();

            // Clear the entire backbuffer to black. This gives up a black background.
            HBRUSH oldBrush = (HBRUSH)SelectObject( bbDC, GetStockObject( BLACK_BRUSH ) );
            Rectangle( bbDC, 0, 0, 800, 600 );

            // Draw the base of the tank--a rectangle surrounding the tanks'
            // center position point.
            SelectObject( bbDC, GetStockObject( DKGRAY_BRUSH ) );
            Rectangle( bbDC, (int)gTankPos.x - 50, (int)gTankPos.y - 75, (int)gTankPos.x + 50, (int)gTankPos.y + 75 );

            // Draw the gun base of the tank--an ellipse surrounding the tanks'
            // center position point.
            SelectObject( bbDC, GetStockObject( GRAY_BRUSH ) );
            Ellipse( bbDC, (int)gTankPos.x - 40, (int)gTankPos.y - 40, (int)gTankPos.x + 40, (int)gTankPos.y + 40 );

            // Draw the gun itself--a line from the tanks' center
            // position point to the tip of the gun.
            HPEN oldPen = (HPEN)SelectObject( bbDC, gGunPen );
            MoveToEx( bbDC, (int)gTankPos.x, (int)gTankPos.y, 0 );
            LineTo( bbDC, (int)(gTankPos.x + gGunDir.x), (int)(gTankPos.y + gGunDir.y) );

            // Draw any bullets that get fired.
            SelectObject( bbDC, GetStockObject( WHITE_BRUSH ) );
            SelectObject( bbDC, oldPen );

            // Bullet velocity is 5X the guns' directions' magnitude.
            Vec2 bulletVel = gGunDir * 5.0f;
            list<Vec2>::iterator i;
            i = gBulletList.begin();
            while( i != gBulletList.end() )
            {
                // Update the bullet position.
                *i += bulletVel * deltaTime;

                // Get POINT form.
                POINT p = *i;

                // Only draw bullet if it is inside the map boundaries, otherwise delete it.
                if( !PtInRect( &gMapRect, p ) )
                    i = gBulletList.erase( i ); // Deletes element as position i and increments i by 1.
                else
                {
                    // Draw bullet as a circle.
                    Ellipse( bbDC, p.x - 4, p.y - 4, p.x + 4, p.y + 4 );

                    // Next in the list.
                    ++i;
                }
            }

            // Select old brush.
            SelectObject( bbDC, oldBrush );

            DrawFramesPerSecond( ( float )deltaTime );

            // Present the backbuffer contents.
            gBackBuffer->present();

            // We are at the end of the loop iteration, so prepare for the
            // next loop iteration by making the "current time" the "last time".
            lastTime = currTime;

            // Free 20 miliseconds to Windows so we don't hog the system resources.
            Sleep( 20 );
        }

    }

    // Return exit code back to the operating system.
    return (int)msg.wParam;

}

//=========================================================
// Name: DrawFramesPerSecond
// Desc: This function is called every frame and updates
//       the frame per second display in the main window
//       caption.
//=========================================================
void DrawFramesPerSecond( float deltaTime )
{
    // Make static variables persist even after the function returns.
    static int   frameCnt = 0;
    static float timeElasped = 0;
    static char  buffer[256];

    // Function called implies a new frame, so
    // increment frame count.
    ++frameCnt;

    // Also increment how much time has passed since
    // the last frame.
    timeElasped += deltaTime;

    // Has one second passed?
    if( timeElasped >= 1.0f )
    {
        // Yes, so compute the frames per second.
        // FPS = frameCnt / timeElasped, but since we
        // compute only when timeElasped = 1.0f, we can
        // reduce to:
        // FPS = frameCnt / 1.0 = frameCnt.

        sprintf_s( buffer, "--Frames Per Second = %d", frameCnt );

        // Add the frames per second string to the main
        // window caption.
        string newCaption = gWndCaption + buffer;

        SetWindowText( ghMainWnd, newCaption.c_str() );

        // Reset the counters to prepare for the next time
        // we compute the frames per second.
        frameCnt = 0;
        timeElasped = 0.0f;
    }
}

//=========================================================
// Name: Window Procedure
// Desc: The main window procedure.
//=========================================================
LRESULT CALLBACK
WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    LOGPEN lp;

    switch( msg )
    {
    case WM_CREATE:

        // Create the tanks' gun pen.
        lp.lopnColor   = RGB( 150, 150, 150 );
        lp.lopnStyle   = PS_SOLID;
        lp.lopnWidth.x = 10;
        lp.lopnWidth.y = 10;

        gGunPen = CreatePenIndirect( &lp );

        // Create the backbuffer.
        gBackBuffer = new BackBuffer( hWnd, gClientWidth, gClientHeight );

    return 0;

    case WM_DESTROY:

        DeleteObject( gGunPen );
        delete gBackBuffer;
        PostQuitMessage( 0 );

    return 0;

    case WM_COMMAND:

        switch( LOWORD( wParam ) )
        {
        case ID_FILE_EXIT:
            DestroyWindow( ghMainWnd );
            break;
        }

    return 0;

    case WM_KEYDOWN:

        switch( wParam )
        {
        case 'A':
            gTankPos.x -= 5.0f;
            break;
        case 'D':
            gTankPos.x += 5.0f;
            break;
        case 'W':
            gTankPos.y -= 5.0f;
            break;
        case 'S':
            gTankPos.y += 5.0f;
            break;

        case 'Q':
            gGunDir.rotate( -0.1f );
            break;
        case 'E':
            gGunDir.rotate( 0.1f );
            break;

        case VK_SPACE:
            gBulletList.push_back( gTankPos + gGunDir );
            break;
        }

    return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

明らかに BackBuffer と関係がありますが、よくわかりません。新しいプロジェクト全体を作成してコードをコピーしようとしましたが、うまくいきません。BackBuffer.obj ファイルを削除して再構築すると、同じエラーがスローされます。

なぜこれが起こっているのか、誰かが光を当てることができますか?

ありがとう。

4

0 に答える 0