aq1229 2023-12-02 14:51 采纳率: 37.5%
浏览 3

c++游戏编译运行问题

1.GameMain.cpp

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <ctime>
#include "sCImage.h"
#include "sprite.h"
#include "SpriteManager.h"
#include "resource1.h"

#include "AlienSprite.h"

#pragma comment(lib, "winmm.lib")
//////////////////////////////////////////////////5)
#pragma comment(lib,"Msimg32.lib")//当使用函数TransparentBlt时,需引用这个库
//////////////////////////////////////////////////5)


#define WINDOW_TITLE "飞机大战"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480





enum GameState  ///控制游戏的三种状态 菜单界面 运行界面 结束界面
{
    GS_Menu,
    GS_Playing,
    GS_Result
};

GameState gameState;//定义一个状态的变量



/////////////////////////////////////////////////////////////////////////////////////////////////1
//HDC hDC;
//HDC g_hdc;
//HDC g_mdc;
//HDC g_bufdc;
/////////////////////////////////////////////////////////////////////////////////////////////////1
HWND        g_hWnd = NULL;//保存全局窗口句柄
HINSTANCE    g_hInstance = NULL;//保存全局实例句柄

HDC hdc;
HDC         g_hOffscreenDC;
HBITMAP     g_hOffscreenBitmap;

void ChangeToState(HDC hDC, GameState gs, HWND hwnd);

struct GameMenu
{
    //////////////////////////////////////////////////1)
    sCImage* imgMenu;
    sCImage* imgButton1;
    sCImage* imgButton2;
    sCImage* imgButton5;
    sCImage* imgButton6;
    sCImage* imgButton3;
    sCImage* imgButton4;
    sCImage* imgButton7;
    sCImage* imgButton8;
    sCImage* imgTeam1;
    sCImage* imgTeam2;
    //////////////////////////////////////////////////1)

    void Init(HDC hDC) {//当程序启动时,做初始化的工作
    //////////////////////////////////////////////////2)
        g_hOffscreenDC = CreateCompatibleDC(hDC);
        g_hOffscreenBitmap = CreateCompatibleBitmap(hDC, 640, 430);
        SelectObject(g_hOffscreenDC, g_hOffscreenBitmap);


        imgMenu = new sCImage("Res/menu.png");
        imgButton1 = new sCImage("Res/button1.png");
        imgButton2 = new sCImage("Res/button2.png");
        imgButton5 = new sCImage("Res/button5.png");
        imgButton6 = new sCImage("Res/button6.png");
        imgButton3 = new sCImage("Res/button3.png");
        imgButton4 = new sCImage("Res/button4.png");
        imgButton7 = new sCImage("Res/button7.png");
        imgButton8 = new sCImage("Res/button8.png");
        imgTeam1 = new sCImage("Res/team1.png");
        imgTeam2 = new sCImage("Res/team2.png");

    ////////////////////////////////////////////////////2)
    }
    void Start(HDC hDC) {//当进入到当前状态需要做的初始化工作,当进入到当前状态时会执行这个函数
    //////////////////////////////////////////////////3)
        //绘制背景
        imgMenu->Draw(hDC, 0, 0);
        //imgButton1->Draw(hDC, 200, 200, true, RGB(255, 255, 255));
        imgButton2->Draw(hDC, 190, 220, true, RGB(255, 255, 255));
        //imgButton5->Draw(hDC, 200, 200, true, RGB(255, 255, 255));
        imgButton6->Draw(hDC, 190, 270, true, RGB(255, 255, 255));
        //imgButton3->Draw(hDC, 200, 200, true, RGB(255, 255, 255));
        imgButton4->Draw(hDC, 190, 320, true, RGB(255, 255, 255));
        //imgButton7->Draw(hDC, 200, 200, true, RGB(255, 255, 255));
        imgButton8->Draw(hDC, 190, 370, true, RGB(255, 255, 255));
        //imgTeam1->Draw(hDC, 200, 200, true, RGB(255, 255, 255));
        imgTeam2->Draw(hDC, 450, 100, true, RGB(0,0,0));

        
    }
    void Update(HDC hDC) {//在当前状态 游戏运行的时候,循环调用的函数,用来放置游戏的逻辑

    }
    void OnWindowMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
        case WM_LBUTTONUP:
            //进入下一个状态
            ChangeToState(hdc, GS_Playing, hwnd);
            /*gameState = GS_Playing;
            gamePlaying.Start(hdc);*/
            break;
        
        }
    }

    void Destroy(HDC hDC) {//当程序退出,终止的时候,调用,做一些清理的工作

    }
};

struct GamePlaying
{
    sCImage* g_pBackgroundBitmap;//背景图
    sCImage* g_pShipBitmap;//轮船位图
    sCImage* g_pBombBitmap;//导弹位图
    sCImage* g_pLeftSubmarineBitmap;//潜艇位图
    sCImage* g_pRightSubmarineBitmap;//潜艇位图
    sCImage* g_pTorpedoBitmap;//水雷位图

    sCImage* g_pGameOverBitmap;
    sCImage* g_pSmExplosionBitmap;//小爆炸位图
    sCImage* g_pLgExplosionBitmap;//大爆炸位图

    Sprite* g_pShipSprite;


    //按键延迟
    int         g_iFileInputDelay;  //输入延迟变量,有助于改变键盘和游戏杆的输入响应,以便改进游戏的可玩性
    int         g_iNumLives, g_iScore, g_iDifficulty; //
    BOOL        g_bGameOver;//游戏是否结束
    int flag = 0;

    int seq[8] = { 0,1,2,3,4,5,6,7 };

    void NewGame()
    {
        CleanupSprites();
        RECT rcBounds = { 0,0,640,480 };
        g_pShipSprite = new Sprite(g_pShipBitmap, 1, 1, rcBounds, BA_WRAP);
        g_pShipSprite->SetPosition(320, 70 - 27);
        AddSprite(g_pShipSprite);

        g_iFileInputDelay = 0;
        g_iNumLives = 3;
        g_iScore = 0;
        g_iDifficulty = 80;
        g_bGameOver = FALSE;

        //PlayMIDISong(TEXT("Music.mid"));

    }
    void Init(HDC hDC) {//当程序启动时,做初始化的工作
        // Seed the random number generator
        srand(GetTickCount());

        // Create the offscreen device context and bitmap
        g_hOffscreenDC = CreateCompatibleDC(hDC);
        g_hOffscreenBitmap = CreateCompatibleBitmap(hDC, 640, 430);
        SelectObject(g_hOffscreenDC, g_hOffscreenBitmap);

        g_pBackgroundBitmap = new sCImage("Res/Background.bmp");
        g_pShipBitmap = new sCImage("Res/SHIP.BMP");
        g_pLeftSubmarineBitmap = new sCImage("Res/submarine1.bmp");
        g_pRightSubmarineBitmap = new sCImage("Res/submarine2.bmp");
        g_pBombBitmap = new sCImage("Res/BOMB.BMP");
        g_pTorpedoBitmap = new sCImage("Res/TORPEDO.BMP");
        g_pSmExplosionBitmap = new sCImage("Res/SmExplosion.bmp");
        g_pLgExplosionBitmap = new sCImage("Res/LgExplosion.bmp");
        g_pGameOverBitmap = new sCImage("Res/GameOver.bmp");

        NewGame();

    }
    void Start(HDC hDC) {//当进入到当前状态需要做的初始化工作,当进入到当前状态时会执行这个函数

    }
    void Update(HDC hDC) {//在当前状态 游戏运行的时候,循环调用的函数,用来放置游戏的逻辑
        //绘制背景
        g_pBackgroundBitmap->Draw(hDC, 0, 0);
        //绘制精灵
        //DrawSprites(hDC);
    }
    void OnWindowMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {

    }
    void Destroy(HDC hDC) {//当程序退出,终止的时候,调用,做一些清理的工作

    }
};

struct GameResult
{
    void Init(HDC hDC) {//当程序启动时,做初始化的工作

    }
    void Start(HDC hDC) {//当进入到当前状态需要做的初始化工作,当进入到当前状态时会执行这个函数

    }
    void Update(HDC hDC) {//在当前状态 游戏运行的时候,循环调用的函数,用来放置游戏的逻辑

    }
    void OnWindowMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {

    }
    void Destroy(HDC hDC) {//当程序退出,终止的时候,调用,做一些清理的工作

    }
};
//声明结构体的变量
GameMenu gameMenu;//开始界面
GamePlaying gamePlaying;//游戏对战界面
GameResult gameResult;//游戏结束界面







LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void GameStart(HDC hDC);
void GameUpdate(HDC hDC);
void GameEnd(HDC hDC);



//hInstance 实例句柄,每一个应用程序都是有一个应用程序的实例句柄(唯一的);Windows自动分配
//lpCmdLine 命令行的参数
//nShowCmd 用来指定窗口如何显示,最大化,还是最小化
/*windows主函数(程序运行的入口,从这里进入执行,离开该函数表示程序的结束)*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow
)
{
    //定义一个窗口类的对象(参考MSDN)
    WNDCLASSEX winclass;
    winclass.cbSize = sizeof(WNDCLASSEX);
    winclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    winclass.lpfnWndProc = WinProc;
    winclass.cbClsExtra = 0;
    winclass.cbWndExtra = 0;
    winclass.hInstance = hInstance;
    winclass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    winclass.hCursor = LoadCursor(hInstance, IDC_ARROW);
    winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    winclass.lpszMenuName = NULL;
    winclass.lpszClassName = "GameWndClass";
    winclass.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);

    //注册窗口类
    if (!RegisterClassEx(&winclass))
        return 0;

    //将实例句柄用一个全局变量保存,方便在其他函数中使用
    g_hInstance = hInstance;

    //根据上面定义的窗口类创建一个特定的窗口对象
    HWND hwnd = CreateWindowEx(
        NULL,
        "GameWndClass",
        "Submarine Battle",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        //WS_POPUP | WS_VISIBLE,
        0,
        0,
        WINDOW_WIDTH,
        WINDOW_HEIGHT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    //将窗口句柄用一个全局变量保存,方便在其他函数中使用
    g_hWnd = hwnd;

    ShowWindow(hwnd, SW_SHOW);//显示窗口
    UpdateWindow(hwnd);//重新绘制一遍窗口

    HDC hdc = GetDC(hwnd);

    GameStart(hdc);/////////////////////////////////////////////////////终于,找到问题所在了,应该在已经创建了的画布hdc上操作,而该画布又在已经创建好的窗口hwnd上,完美!

    //启动消息循环,注意这里使用一种非阻塞的获取消息的方式
    MSG msg;
    while (1)
    {
        DWORD starttime = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if (KEYDOWN(VK_ESCAPE))
            PostQuitMessage(1);

        GameUpdate(hdc);

        if (GetTickCount() - starttime < 33)
            Sleep(33 - (GetTickCount() - starttime));

    }

    GameEnd(hdc);

    return 1;
}

void ChangeToState(HDC hDC,GameState gs, HWND hwnd)
{
    gameState = gs;
    switch (gs)
    {
    case GS_Menu:
        gameMenu.Start(hDC);
        break;
    case GS_Playing:
        gamePlaying.Start(hDC);
        break;
    case GS_Result:
        gamePlaying.Start(hDC);
        break;
    }
}





//回调函数,完成对消息的处理,由操作系统直接调用
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{    //根据不同的消息类型进行相应的处理
    switch (uMsg)
    {//分发事件给当前游戏的状态,来处理
    case WM_KEYDOWN:
    case WM_LBUTTONUP:
    case WM_LBUTTONDOWN:
        switch (gameState)
        {
        case GS_Menu:
            gameMenu.OnWindowMessage(hwnd, uMsg, wParam, lParam);
            break;
        case GS_Playing:
            gamePlaying.OnWindowMessage(hwnd, uMsg, wParam, lParam);
            break;
        case GS_Result:
            gameResult.OnWindowMessage(hwnd, uMsg, wParam, lParam);
            break;
        }
        break;
    case WM_DESTROY: //窗口销毁时触发的消息
        PostQuitMessage(0);
        break;
    default:
        break;
    }
    //未被上述代码处理的消息都交由这里处理。
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void GameStart(HDC hDC)
{
    



    gameMenu.Init(hDC);
    gamePlaying.Init(hDC);
    gameResult.Init(hDC);

    //设置默认状态
    gameState = GS_Menu;
    gameMenu.Start(hDC);
}
void GameUpdate(HDC hDC)
{
    switch (gameState)
    {
    case GS_Menu:
        gameMenu.Update(hDC);
        break;
    case GS_Playing:
        gamePlaying.Update(hDC);
        break;
    case GS_Result:
        gameResult.Update(hDC);
        break;
    }

}
void GameEnd(HDC hDC)
{
    /////////////////////////////////////////////////////////////////////////////////////////////////3
    /*DeleteDC(g_bufdc);
    DeleteDC(g_mdc);
    ReleaseDC(hwnd, g_hdc);*/
    DeleteObject(g_hOffscreenBitmap);
    DeleteDC(g_hOffscreenDC);
    /////////////////////////////////////////////////////////////////////////////////////////////////3

    gameMenu.Destroy(hDC);
    gamePlaying.Destroy(hDC);
    gameResult.Destroy(hDC);
}

2.下面是:SpriteManager.h 声明了SpriteCollision和SpriteDying,但是在SpriteManager.cpp中没有实现函数的定义,想在GameMain.cpp中定义,但是报错:

img

怎么解决这个问题?


/*精灵管理器*/

#include "Sprite.h"


#include <vector>
using namespace std;


/////////////////////////////////6)
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1:0)
#define KEYUP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0:1)

extern HWND g_hWnd;
////////////////////////////////////声音播放引擎//////////////////////////////////////7)
extern UINT m_uiMIDIPlayerID;//记录MIDI设备ID
void PlayMIDISong(const char* szMIDIFileName = TEXT(""), BOOL bRestart = TRUE);
void ReplayMIDISong();
void PauseMIDISong();
void CloseMIDIPlayer();
/////////////////////////////精灵管理器//////////////////////////////////////////////////////////////////////////////////////
extern sCImage* g_pTorpedoBitmap;
extern vector<Sprite*>     m_vSprites;

//精灵碰撞响应函数接口,由特定的游戏实现
BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee);//8)

//精灵死亡事件响应函数
void SpriteDying(Sprite* pSprite);

BOOL CheckSpriteCollision(Sprite* pTestSprite);

void AddSprite(Sprite* pSprite);

void DrawSprites(HDC hDC);

void UpdateSprites();

void CleanupSprites();

Sprite* IsPointInSprite(int x, int y);

3.相关函数:

BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee)
{
    sCImage* pHitter = pSpriteHitter->GetBitmap();
    sCImage* pHittee = pSpriteHittee->GetBitmap();
    //1.玩家导弹与NPC潜艇的碰撞
    if ((pHitter == g_pBombBitmap && (pHitter == g_pLeftSubmarineBitmap || pHittee == g_pRightSubmarineBitmap)) ||
        (pHittee == g_pBombBitmap && (pHitter == g_pLeftSubmarineBitmap || pHitter == g_pRightSubmarineBitmap)))
    {
        //碰撞先销毁
        pSpriteHitter->kill();
        pSpriteHittee->kill();

        //销毁的同时发生爆炸
        RECT rcpos = pSpriteHitter->GetPosition();//先获取爆炸位置:发生碰撞的精灵的位置 ;  定义一个位置矩形

        RECT rcBounds = { 0,0,640,480 };
        Sprite* explore_Sprite = new Sprite(g_pLgExplosionBitmap, 8, 1, rcBounds , BA_STOP, true);
        explore_Sprite->setSequence(seq, 8, 1);//帧数:8,帧延迟:3  ; 起始帧、结束帧和循环次数
        explore_Sprite->SetPosition(rcpos.left,rcpos.top);
        AddSprite(explore_Sprite);
        // 每次播放动画序列时,将 Sprite 对象的位置设置为上一帧的位置
        explore_Sprite->SetPosition(explore_Sprite->GetPosition());
    }
        
    //2.潜艇水雷与玩家轮船的碰撞
    if ((pHitter == g_pTorpedoBitmap && pHittee == g_pShipBitmap) ||
        (pHittee == g_pTorpedoBitmap && pHitter == g_pShipBitmap))
    {
        if (pHitter == g_pTorpedoBitmap)
            pSpriteHitter->kill();
        else
            pSpriteHittee->kill();

        //销毁的同时发生爆炸
        RECT rcpos = pSpriteHitter->GetPosition();//先获取爆炸位置:发生碰撞的精灵的位置 ;  定义一个位置矩形

        RECT rcBounds = { 0,0,640,480 };
        Sprite* explore_Sprite = new Sprite(g_pLgExplosionBitmap, 8, 1, rcBounds , BA_STOP, true);
        explore_Sprite->setSequence(seq, 8, 1);
        explore_Sprite->SetPosition(rcpos.left, rcpos.top);
        AddSprite(explore_Sprite);
        // 每次播放动画序列时,将 Sprite 对象的位置设置为上一帧的位置
        explore_Sprite->SetPosition(explore_Sprite->GetPosition());
        //重置轮船的位置,不用销毁
        g_pShipSprite->SetPosition(320, 45);
        
    }


    return FALSE;
}
//精灵死亡事件响应函数
void SpriteDying(Sprite* pSprite)
{
    if (pSprite->GetBitmap() == g_pTorpedoBitmap)
    {
        //在水雷死亡的位置创建一个小爆炸精灵
        RECT rcpos = pSprite->GetPosition();//先获取爆炸位置:发生碰撞的精灵的位置 ;  定义一个位置矩形

        RECT rcBounds = { 0,0,640,480 };
        Sprite* smexplore_Sprite = new Sprite(g_pSmExplosionBitmap, 8, 1, rcBounds, BA_STOP, true);
        smexplore_Sprite->setSequence(seq, 8, 1);
        smexplore_Sprite->SetPosition(rcpos.left, rcpos.top);
        AddSprite(smexplore_Sprite);
    }

}

  • 写回答

2条回答 默认 最新

  • CSDN-Ada助手 CSDN-AI 官方账号 2023-12-02 18:04
    关注

    【以下回答由 GPT 生成】

    要在Windows上编译和运行以上的C++游戏代码(GameMain.cpp),你需要进行以下步骤:

    1. 安装C++编译器:首先,你需要安装一个C++编译器,例如Microsoft Visual Studio或MinGW。你可以从官方网站下载安装包并按照提示进行安装。

    2. 创建C++项目:打开所选的C++编译器,创建一个新的C++项目。选择空项目或控制台应用程序项目。

    3. 添加源代码文件:将GameMain.cpp文件添加到项目中。你可以直接在项目资源管理器中将文件拖放到项目目录中。

    4. 添加图像和资源文件:根据代码中的#include指令,你需要添加相应的图像和资源文件,包括sCImage.h、sprite.h、SpriteManager.h和resource1.h。确保这些文件与GameMain.cpp位于同一个文件夹中。

    5. 配置编译器:如果你使用的是Visual Studio,打开项目属性页,确保编译器设置和包含目录正确配置。

    6. 解决依赖关系:你的代码中使用了winmm.lib库,所以你需要在项目设置中添加该库的引用。在Visual Studio中,可以通过在“链接器”选项下的“输入”中添加对应库的名称。

    7. 构建项目:根据所选的编译器,选择“构建”或“生成”选项来构建项目。编译器将检查代码中的任何错误,并生成可执行文件。

    8. 运行游戏:如果构建步骤成功,你将在项目文件夹中找到生成的可执行文件。双击运行该文件,即可开始游戏。

    如果你遇到编译错误或其他问题,你可以通过查看编译器的错误输出来获取更多信息,以便解决问题。请注意,以上步骤可能因不同的编译器和开发环境而有所不同,所以根据你使用的编译器和开发环境进行相应的调整。


    如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^
    评论

报告相同问题?

问题事件

  • 创建了问题 12月2日