aq1229 2023-12-02 15:12 采纳率: 37.5%
浏览 8
已结题

函数调用问题,编译运行错误

问题描述:
在SpriteManager.h文件中声明了函数 SpriteCollision 和 SpriteDying ,
但是在SpriteManager.cpp文件中没有 SpriteCollision 和 SpriteDying 相关函数的定义,
因为涉及到相关参数的引用所以想在GameMain.cpp 中去定义 ,
(为什么不直接在GameMain中去直接声明和定义,是因为在 SpriteManager.cpp 有相关的函数需要用到 SpriteCollision 和 SpriteDying函数)

总而言之: 在GameMain.cpp 中去定义函数 SpriteCollision 和 SpriteDying的时候遇到了以下问题:

img

怎么解决?

相关代码:
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

/*精灵管理器*/

#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.函数 SpriteCollision 和 SpriteDying

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);
    }

}

  • 写回答

6条回答 默认 最新

  • 急速光粒 2023-12-02 15:50
    关注

    私我看一下

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(5条)

报告相同问题?

问题事件

  • 系统已结题 12月10日
  • 已采纳回答 12月2日
  • 创建了问题 12月2日