C++小項目-吃豆子游戲


GMap.h

#pragma once //保證頭文件只被編譯一次

#include "stdafx.h"

#define MAP_LEN    19    //邏輯地圖大小 (邏輯地圖由行、列各為19的方塊組成)

#define P_ROW 10 //大嘴出生地的橫邏輯坐標
#define P_COLUMN 9 //大嘴出生地的列邏輯坐標
#define E_ROW 8 //敵人出生地的橫邏輯坐標
#define E_COLUMN 9 //敵人出生地的列邏輯坐標

/*
    地圖中應該存在障礙物和豆子,所以要有豆子和障礙物的尺寸和邏輯地圖點陣,以及繪制地圖(包括障礙物)和豆子的繪制函數
    以及顏色變量和設置敵我出生位置不能有豆子存在,以及地圖類要能被物體類和大嘴類訪問,故設置物體類和大嘴類為地圖類的友元類
    下面為地圖類,為關卡1、關卡2、關卡3的父類
*/    
class GMap
{
protected:
    static int LD;    //障礙物的尺寸大小,設置為靜態變量,因為要所有GMap對象共用這個變量
    static int PD;    //豆子的半徑,設置為靜態變量,因為要所有GMap對象共用這個變量
    bool mapData[MAP_LEN][MAP_LEN]; //障礙物邏輯地圖點陣
    bool peaMapData[MAP_LEN][MAP_LEN]; //豆子邏輯地圖點陣
    COLORREF color; //COLORREF是DWORD的宏,而DWORD是unsigned long
    //----
    void InitOP();    //使敵我雙方的出生位置沒有豆子存在

public:
    void DrawMap(HDC &hdc); //繪制地圖
    void DrawPeas(HDC &hdc); //繪制豆子

    friend class GObject; //設置物體類為地圖類的友元類,使物體類對象能訪問地圖類對象
    friend class PacMan; //設置大嘴類為地圖類的友元類,使大嘴類對象能訪問地圖類對象
};

//關卡1類,為地圖類的子類
class Stage_1:public GMap  //c++默認的是private繼承,無法進行轉換,所以繼承后面都要有一個public
{
private:
    bool static initData[MAP_LEN][MAP_LEN]; //關卡邏輯地圖點陣

public:
    Stage_1(); //構造函數
};

class Stage_2:public GMap
{
private: 
    bool static initData[MAP_LEN][MAP_LEN];
public:
    Stage_2();
};
class Stage_3:public GMap
{
private: 
    bool static initData[MAP_LEN][MAP_LEN];
public:
    Stage_3();
};

 

GMap.cpp 

#include "stdafx.h"
#include "GMap.h"

int GMap::LD =36; //初始化靜態變量  障礙物的尺寸大小
int GMap::PD =3; //豆子的半徑

//使敵我雙方的出生位置沒有豆子存在
void GMap::InitOP()
{
    peaMapData[P_ROW][P_COLUMN] = false; //讓大嘴出生地的豆子消失 true,有豆子 false,無豆子
    peaMapData[E_ROW][E_COLUMN] = false; //讓敵人出生地的豆子消失 true,有豆子 false,無豆子
}

//繪制地圖
void GMap::DrawMap(HDC &hdc)
{
    for(int i=0;i<MAP_LEN;i++)
    {
        for(int j=0;j<MAP_LEN;j++)
        {
            if(!mapData[i][j]) //元素為B,即false,為牆壁/障礙物,則進入if內繪制   是A的位置不繪制,即為空白處
            {
                RECT rect; //矩形結構體對象
                rect.left = j*LD; //設置矩形4個方向啟動的坐標
                rect.top = i*LD;
                rect.right = (j+1)*LD;
                rect.bottom = (i+1)*LD;

                FillRect(hdc,&rect,CreateSolidBrush(color)); //繪制矩形(牆壁/障礙物)
            }
        }
    }
}

//繪制豆子
void GMap::DrawPeas(HDC &hdc)
{
    for(int i=0;i<MAP_LEN;i++)
    {
        for(int j=0;j<MAP_LEN;j++)
        {
            if(peaMapData[i][j]) //元素為A,即true,為豆子,則進入if內繪制   是B的位置則不繪制豆子
            {
                Ellipse(hdc,(LD/2-PD)+j*LD,(LD/2-PD)+i*LD,(LD/2+PD)+j*LD,(LD/2+PD)+i*LD); //繪制圓弧(豆子)
            }
        }
    }        
}

#define A true
#define B false

//初始化static數組InitData[MAP_LEN][MAP_LEN,即關卡1的原始數據    B表示障礙物/牆壁,A表示空白處(除出生地外的空白處有豆子)
bool Stage_1::initData[MAP_LEN][MAP_LEN] =
                      {
                        B,B,B,B,B,B,B,B,B,A,B,B,B,B,B,B,B,B,B,//0
                        B,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,//1
                        B,A,A,B,A,A,B,B,B,A,B,B,B,A,A,B,A,A,B,//2
                        B,A,B,B,A,A,A,A,A,A,A,A,A,A,A,B,B,A,B,//3
                        B,A,B,A,A,A,B,B,B,A,B,B,B,A,A,A,B,A,B,//4
                        B,A,B,A,A,A,A,A,A,A,A,A,A,A,A,A,B,A,B,//5
                        B,A,A,A,A,A,B,B,A,A,A,B,B,A,A,A,A,A,B,//6
                        B,A,B,A,A,A,A,A,A,A,A,A,A,A,A,A,B,A,B,//7
                        B,A,B,A,A,A,A,A,B,A,B,A,A,A,A,A,B,A,B,//8
                        A,A,A,A,A,A,A,A,B,B,B,A,A,A,A,A,A,A,A,//9
                        B,A,B,A,A,A,A,A,A,A,A,A,A,A,A,A,B,A,B,//10
                        B,A,B,A,A,B,A,A,A,A,A,A,A,B,A,A,B,A,B,//11
                        B,A,B,A,B,B,B,A,A,A,A,A,B,B,B,A,B,A,B,//12
                        B,A,A,A,A,B,A,A,A,A,A,A,A,B,A,A,A,A,B,//13
                        B,A,B,B,A,A,A,A,A,A,A,A,A,A,A,B,B,A,B,//14
                        B,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,//15
                        B,A,A,A,A,B,B,B,A,B,A,B,B,B,A,A,A,A,B,//16
                        B,A,A,A,A,B,A,A,A,A,A,A,A,B,A,A,A,A,B,//17                                 
                        B,B,B,B,B,B,B,B,B,A,B,B,B,B,B,B,B,B,B,//18
                      };
#undef A
#undef B

Stage_1::Stage_1()
{
    color =RGB(140,240,240); //設置關卡1的地圖顏色

    for(int i=0;i<MAP_LEN;i++)
    {
        for(int j=0;j<MAP_LEN;j++)
        {
            this->mapData[i][j] = this->initData[i][j]; //初始化關卡1的地圖數據
            this->peaMapData[i][j] = this->initData[i][j]; //初始化關卡1的豆子數據
        }
    }

    //敵我雙方出現位置沒有豆子出現
    this->InitOP();
}

//Stage_2成員定義
#define A true
#define B false
bool Stage_2::initData[MAP_LEN][MAP_LEN]=
{
    B,B,B,B,B,B,B,B,B,A,B,B,B,A,B,B,B,B,B,//0
    A,A,A,A,A,A,A,B,A,A,B,A,A,A,B,A,B,A,A,//1
    B,A,A,A,B,A,A,B,A,A,B,A,B,A,B,A,B,A,B,//2
    B,B,B,A,B,A,A,B,B,A,B,A,B,A,B,A,B,B,B,//3
    B,A,A,A,A,A,A,A,A,A,A,A,B,B,B,A,A,A,B,//4
    B,A,A,B,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,//5
    B,A,A,B,A,A,A,B,B,B,B,B,B,A,A,B,A,A,B,//6
    B,A,A,B,A,B,A,A,A,A,A,A,A,A,A,B,A,A,B,//7
    B,A,A,B,A,B,A,A,B,A,B,A,A,B,A,B,A,A,B,//8
    A,A,A,B,A,B,A,A,B,B,B,A,A,B,A,B,A,A,A,//9
    B,A,A,B,A,B,A,A,A,A,A,A,A,B,A,A,A,A,B,//10
    B,A,A,B,A,A,A,B,B,B,B,B,A,B,A,A,A,A,B,//11
    B,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,//12
    B,A,A,A,B,B,B,B,B,B,B,A,A,A,A,A,A,A,B,//13
    B,A,A,A,A,A,A,A,A,A,A,A,A,B,A,A,A,A,B,//14
    B,B,B,B,B,A,A,A,A,B,B,B,A,B,A,A,A,A,B,//15
    B,A,A,A,B,B,B,A,A,A,A,B,A,B,B,B,A,A,B,//16
    A,A,A,A,B,A,A,A,A,A,A,B,A,A,A,B,A,A,A,//17                                 
    B,B,B,B,B,B,B,B,B,A,B,B,B,A,B,B,B,B,B,//18
};
#undef A
#undef B

Stage_2::Stage_2()
{
    color = RGB(240,140,140);
    for(int i= 0;i<MAP_LEN;i++)
    {
        for(int j =0;j<MAP_LEN;j++)
        {
            this->mapData[i][j] = this->initData[i][j]; 
            this->peaMapData[i][j] = this->initData[i][j];
        }
    }
    //敵我雙方出現位置沒有豆子出現
    this->InitOP();
}

//Stage_3成員定義
#define A true
#define B false
bool Stage_3::initData[MAP_LEN][MAP_LEN]=
{
    B,B,B,B,B,B,B,B,B,A,B,B,B,B,B,B,B,B,B,//0
    A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,//1
    B,A,A,B,A,A,B,B,B,B,B,B,B,A,A,A,B,A,B,//2
    B,A,B,B,A,A,A,A,A,A,A,A,B,A,A,A,B,A,B,//3
    B,A,B,A,A,A,B,B,B,B,B,B,B,A,A,A,B,A,B,//4
    B,A,B,A,B,B,B,A,A,A,A,A,B,B,B,A,B,A,B,//5
    B,A,A,A,B,A,B,A,A,A,A,A,A,A,A,A,B,A,B,//6
    B,A,B,A,B,A,A,A,A,A,A,A,A,B,A,A,B,A,B,//7
    B,A,B,A,B,B,A,A,B,A,B,A,A,B,A,A,B,A,B,//8
    B,A,A,A,A,B,A,A,B,B,B,A,A,B,A,A,B,A,B,//9
    B,A,B,A,A,B,A,A,A,A,A,A,A,B,A,A,A,A,B,//10
    B,A,B,A,A,B,A,A,A,A,A,A,B,B,B,A,B,A,B,//11
    B,A,B,A,A,B,A,B,B,B,B,B,B,A,B,A,B,A,B,//12
    B,A,B,A,A,B,A,A,A,A,A,A,A,A,B,A,B,A,B,//13
    B,A,B,B,A,B,B,B,B,B,B,A,B,A,B,A,B,A,B,//14
    B,A,A,A,A,B,A,A,A,A,A,A,B,A,B,A,B,A,B,//15
    B,B,B,B,B,B,A,A,B,B,B,A,B,A,B,A,B,A,B,//16
    A,A,A,A,A,A,A,A,B,A,A,A,A,A,B,A,A,A,A,//17                                 
    B,B,B,B,B,B,B,B,B,A,B,B,B,B,B,B,B,B,B,//18
};
#undef A
#undef B

Stage_3::Stage_3()
{
    color = RGB(100,300,100);
    for(int i= 0;i<MAP_LEN;i++)
    {
        for(int j =0;j<MAP_LEN;j++)
        {
            this->mapData[i][j] = this->initData[i][j]; 
            this->peaMapData[i][j] = this->initData[i][j];
        }
    }
    //敵我雙方出現位置沒有豆子出現
    this->InitOP();
}

 

GObject.h

#include "stdafx.h"
#include <time.h>
#include "GMap.h"

#define DISTANCE 10//圖型范圍
#define D_OFFSET   2//繪圖誤差
#define RD (DISTANCE + D_OFFSET)//繪圖范圍 12 
//----
#define PLAYER_SPEED 6//玩家速度
#define ENERMY_SPEED 4//敵人速度
//----
#define LEGCOUNTS 5//敵人腿的數量
#define BLUE_ALERT 8//藍色警戒范圍

//方向枚舉
enum TWARDS{UP,DOWN,LEFT,RIGHT,OVER};

//物體類 (含有純虛函數,為抽象類,不能被實例化)
class GObject
{
protected:
    int dRow; //邏輯橫坐標
    int dColumn; //邏輯縱坐標
    POINT point; //中心坐標
    int mX; //物體的中心橫坐標
    int mY; //物體的中心縱坐標
    //----
    TWARDS twCommand;//朝向指令緩存
    TWARDS tw;//朝向
    //----
    int speed;//速度
    int frame;//幀數

    //子程序
    bool Achive(); //判斷物體是否到達邏輯坐標位置
    int PtTransform(int k);//將實際坐標轉換為邏輯坐標
    virtual void AchiveCtrl();//到達邏輯點后更新數據
    bool Collision() ;//邏輯碰撞檢測,將物體擺放到合理的位置

public:
    static GMap* pStage; //指向地圖類的指針,設置為靜態,使所有自類對象都能夠使用相同的地圖

    GObject(int Row,int Column); //有參構造函數

    int GetRow(); //提供共有接口,獲取保護成員變量dRow
    int GetArray(); //提供共有接口,獲取保護成員變量dColumn
    virtual void action() = 0;//數據變更的表現
    //----
    void SetPosition(int Row,int Column); //設置自身中心位置
    void DrawBlank(HDC &hdc);
    virtual void Draw(HDC &hdc) = 0;//繪制對象
        
};

//大嘴,玩家控制的對象
class PacMan:public GObject //PacMan是GObject的子類
{
protected:
    virtual void AchiveCtrl();//重寫虛函數 (到達邏輯點后更新數據)

public:
    PacMan(int Row,int Column);
    
    POINT GetPos(); //提供共有接口,獲取保護成員變量point
    TWARDS GetTw(); //提供共有接口,獲取保護成員變量twCommand
    //----
    bool Win(); //輪詢peaMapData[][],判斷是否存在任意一個豆子,來判斷是否獲勝
    void SetTwCommand(TWARDS command); //設置大嘴的移動方向指令
    void Over(); //設置大嘴的方向為OVER
    //----
    void Draw(HDC &hdc); //重寫虛函數: 根據移動方向指令,繪制不同朝向的大嘴4幀動畫
    void action();//數據變更的表現 (直接調用父類GObject的邏輯碰撞檢測函數)
};

//追捕大嘴的敵人
class Enermy:public GObject //Enermy是GObject的子類
{
protected:
    void Catch(); //檢測敵人是否抓捕到大嘴
    void virtual MakeDecision(bool b) = 0;//AI實現
    COLORREF color;

public:
    static PacMan* player; //在敵人類中,定義一個玩家(大嘴) 設置為靜態變量,全部敵人追捕一個公共的玩家

    void virtual  Draw(HDC& hdc); //繪制敵人的圖像
    Enermy(int x,int y);

    void virtual action();
};

//松散型的敵人,隨機移動
class RedOne:public Enermy  
{
protected:
    void virtual MakeDecision(bool b);
public:
    void Draw(HDC& hdc);
    RedOne(int x,int y):Enermy(x,y)
    {
        color = RGB(255,0,0); //松散型的敵人,顏色為紅色
    }
};

//守衛者
class BlueOne:public RedOne
{    
protected:
    void virtual MakeDecision(bool b);
public:
    void Draw(HDC& hdc);
    BlueOne(int x,int y):RedOne(x,y) //守衛者,顏色為藍色
    {

        color = RGB(0,0,255);
    }

};

//擾亂者
class YellowOne:public RedOne 
{
protected:
    void virtual MakeDecision(bool b);
public:
    void Draw(HDC& hdc);
    YellowOne(int x,int y):RedOne(x,y) 
    {
        color = RGB(200,200,100); //擾亂者,顏色為黃色
    }
};

 

GObject.cpp

#include "stdafx.h"
#include "GObject.h"

//-----------------------------------------------------以下為GOject成員定義:------------------------------------------------
GMap* GObject::pStage =NULL; //靜態變量使用前,必須先初始化

//有參構造函數  參數為物體出生地的邏輯坐標
GObject::GObject(int Row,int Column)
{
    this->dRow = Row; //邏輯橫坐標
    this->dColumn = Column; //邏輯縱坐標
    this->point.y = dRow*pStage->LD + pStage->LD/2; //根據邏輯坐標換算獲得中心坐標  【注意y方向對應的是行dRow,x方向對應的是列dColumn】
    this->point.x = dColumn*pStage->LD + pStage->LD/2;
    this->mX = point.x; //物體的中心橫坐標
    this->mY = point.y ; //物體的中心縱坐標

    frame = 1; //初始幀數為1
    pStage = NULL; //初始化靜態GMap指針
}

//提供共有接口,獲取保護成員變量dRow
int GObject::GetRow()
{
    return dRow;
}

//提供共有接口,獲取保護成員變量dColumn
int GObject::GetArray()
{
    return dColumn;
}

//判斷物體是否到達邏輯坐標位置
bool GObject::Achive()
{
    //若物體在邏輯坐標位置,則point.x - pStage->LD/2 為pStage->LD的整數倍,取余為0
    if( (point.x - pStage->LD/2)%pStage->LD==0 && (point.y - pStage->LD/2)%pStage->LD==0 ) //到達邏輯坐標位置,返回true
    {
        return true; //到達邏輯坐標位置,返回true
    }

    return false; //未到達,返回false
}

//將實際坐標轉換為邏輯坐標 k為point.x或者point.y
int GObject::PtTransform(int k)
{
    return (k - pStage->LD/2)/pStage->LD;
}

//到達邏輯坐標位置后更新數據
void GObject::AchiveCtrl() //聲明時,為虛函數,定義加virtual會報錯
{
    if(Achive()) //先判斷是否到達邏輯坐標位置
    {
        dRow = PtTransform(point.y); //更新活動邏輯橫坐標,由實際橫坐標轉換而來 【注意y方向對應的是行dRow,x方向對應的是列dColumn】
        dColumn = PtTransform(point.x); //更新活動邏輯縱坐標,由實際縱坐標轉換而來
    }
}

//邏輯碰撞檢測,將物體擺放到合理的位置    返回指令有效與否的標志位
bool GObject::Collision()
{
    bool cmdValid_flag = true; //指令有效與否的標志位  ==true,指令有效  ==false,指令無效

    //檢測邏輯碰撞前,先更新數據,直到到達邏輯坐標位置,進行邏輯碰撞檢測
    AchiveCtrl(); //更新邏輯坐標數據,若物體是大嘴,則會執行PacMan重寫的AchiveCtrl()消除豆子

    //判斷指令的有效性
    if(dRow<0 || dColumn<0 || dRow>MAP_LEN-1 || dColumn>MAP_LEN-1) //邏輯橫、縱坐標超出允許范圍,即在屏幕外,則不可以改變指令(新指令無效)
        cmdValid_flag = false; //(新)指令無效
    else if (Achive()) //到達邏輯坐標位置,才能進行邏輯碰撞檢測
    {
        switch(twCommand) //判斷前進的方向
        {
        case LEFT:
            {
                if(dColumn>0 && !pStage->mapData[dRow][dColumn-1]) //判斷下一個格子是否是牆/障礙物  是牆,則!B=!false=true
                    cmdValid_flag = false; //下一個格子是牆/障礙物,(新)指令無效
            }
            break;
        case RIGHT:
            {
                if(dColumn<MAP_LEN-1 && !pStage->mapData[dRow][dColumn+1])
                    cmdValid_flag = false;
            }
            break;
        case UP:
            {
                if(dRow>0 && !pStage->mapData[dRow-1][dColumn])
                    cmdValid_flag = false;
            }
            break;
        case DOWN:
            {
                if(dRow<MAP_LEN-1 && !pStage->mapData[dRow+1][dColumn])
                    cmdValid_flag = false;
            }
            break;
        }

        if(cmdValid_flag)
            tw = twCommand; //前進方向上的下一方格不是牆壁/障礙物,則指令成功,改變方向有效
    }

    //依照真實的方向移動
    mX = point.x;
    mY = point.y;
    int MAX = pStage->LD*MAP_LEN + pStage->LD/2; //超出地圖右、下邊界一個方格的位置(注意是"中心"坐標)
    int MIN = pStage->LD/2; //超出地圖左、上邊界一個方格的位置

    switch(tw)//判斷行進的方向
    {
    case LEFT:
        if(dColumn>0 && !pStage->mapData[dRow][dColumn-1])//判斷下一個格子是否能夠通行
        {
            cmdValid_flag= false;
            break;//"撞牆了"
        }    
        point.x -= speed; //改變物體在地圖上的中心坐標,相當於物體在地圖中移動(這里向左移動)
        if(point.x<MIN) //向左移動到了地圖左邊界外半個方格外,則從另一邊出現
        {
            point.x = MAX;
        }

        break;
        //以下方向的判斷原理相同
    case RIGHT:         
        if(dColumn<MAP_LEN-1&&!pStage->mapData[dRow][dColumn+1])
        {
            cmdValid_flag= false;
            break;//"撞牆了"
        }
        point.x += speed;
        if(point.x>MAX)
        {
            point.x = MIN;
        }

        break;
    case UP:
        if(dRow>0&&!pStage->mapData[dRow-1][dColumn])
        {
            cmdValid_flag= false;
            break;//"撞牆了"
        }
        point.y -=speed;
        if(point.y<MIN)
        {
            point.y = MAX;
        }
        break;
    case DOWN:
        if(dRow<MAP_LEN-1&&!pStage->mapData[dRow+1][dColumn])
        {
            cmdValid_flag= false;
            break;//"撞牆了"
        }
        point.y +=speed;
        if(point.y>MAX)
        {
            point.y = MIN;
        }
        break;
    }

    return !cmdValid_flag; //返回指令有效與否的標志位
}

//設置自身中心位置
void GObject::SetPosition(int Row,int Column)
{
    dRow = Row;
    dColumn = Column;

    point.y = dRow*pStage->LD + pStage->LD/2; //根據邏輯坐標換算獲得中心坐標 【注意y方向對應的是行dRow,x方向對應的是列dColumn】
    point.x = dColumn*pStage->LD + pStage->LD/2;
}

//將這一幀時物體所在的方格處繪制的圖像擦除,移動后再繪制下一方格的圖像,使物體看起來像在移動一樣
void GObject::DrawBlank(HDC &hdc)
{
    RECT rect;

    rect.left = mX - RD; //這一幀時物體所在的方格(矩形),4個方向邊緣坐標
    rect.top = mY - RD;
    rect.right = mX + RD;
    rect.bottom = mY + RD;

    FillRect(hdc,&rect,CreateSolidBrush(RGB(255,255,255)));
}

//-----------------------------------------------------以下為PacMan(大嘴)成員定義:------------------------------------------------

PacMan::PacMan(int Row,int Column):GObject(Row,Column) //使用初始化列表,同時初始化了PacMan::dROW、dColumn、point.x、point.y等等
{
    this->speed = PLAYER_SPEED; //設置大嘴速度
    twCommand = tw = LEFT; //設置開局時大嘴的移動方向
}

//提供共有接口,獲取保護成員變量point
POINT PacMan::GetPos()
{
    return point;
}

//設置大嘴的方向為OVER
void PacMan::Over()
{
    tw = OVER;
}

//提供共有接口,獲取保護成員變量tw
TWARDS PacMan::GetTw()
{
    return tw;
}

//設置大嘴的移動方向指令
void PacMan::SetTwCommand(TWARDS command)
{
    twCommand = command;
}

//輪詢peaMapData[][],判斷是否存在任意一個豆子,來判斷是否獲勝
bool PacMan::Win()
{
    for(int i=0;i<=MAP_LEN;i++)
    {
        for(int j=0;j<=MAP_LEN;j++)
        {    
            if(pStage->peaMapData[i][j]==true)
            {
                return false; //存在任意一個豆子,沒取得勝利
            }
        }
    }
    return true;//沒有豆子,勝利
}

//重寫虛函數 (到達邏輯點后更新數據)
void PacMan::AchiveCtrl()
{
    GObject::AchiveCtrl(); //直接調用GObject類的功能函數: 到達邏輯坐標位置后更新數據

    //重寫虛函數,為實現大嘴的吃豆子功能
    if(Achive()) //必須到達邏輯坐標位置,才能進行下一步
    {
        if(dRow>=0 && dRow<MAP_LEN && dColumn>=0 && dColumn<MAP_LEN)//防止數組越界
        {
            //peaMapData[dRow][dColumn]:   ==true,表示有豆子   ==false,表示沒有豆子
            if(pStage->peaMapData[dRow][dColumn]) //豆子到達的邏輯坐標位置,如果有豆子
            {
                pStage->peaMapData[dRow][dColumn] = false; //則將豆子"吃掉"
            }
        }
    }    
}

//行為函數,直接調用父類GObject的邏輯碰撞檢測函數
void PacMan::action()
{
    Collision(); //直接調用父類GObject的邏輯碰撞檢測函數
}

//重寫虛函數: 根據移動方向指令,繪制不同朝向的大嘴4幀動畫
void PacMan::Draw( HDC& hdc)
{
    if(tw == OVER) //移動指令為OVER,無效,不執行如何程序
    {

    }
    else if(frame%2 == 0) //繪制第2幀動畫和第4幀動畫 (都是V形開口的圓弧)
    {
        int x1=0,x2=0,y1=0,y2=0;
        int offsetX =  DISTANCE/2+D_OFFSET;//弧弦交點 10/2+2 = 7
        int offsetY =  DISTANCE/2+D_OFFSET;//弧弦交點
        switch(tw) //移動方向不同,(大嘴)圓弧的V形開口不同
        {
            case UP: 
                x1 = point.x - offsetX;
                x2 = point.x + offsetX;
                y2 = y1 = point.y-offsetY;
            break;
            case DOWN:
                x1 = point.x + offsetX;
                x2 = point.x - offsetX;
                y2 = y1 = point.y+offsetY;
            break;
            case LEFT:
                x2 = x1 = point.x-offsetX;
                y1 = point.y + offsetY;
                y2 = point.y - offsetY;
            break;
            case RIGHT: //V形開口向右
                x2 = x1 =point.x + offsetX;
                y1 = point.y - offsetY;
                y2 = point.y + offsetY;
            break;
        }

        //繪制圓弧   RIGHT朝向情況下,x1,y1和x2,y2各為右上角、右下角的弧弦交點
        Arc(hdc,point.x-DISTANCE,point.y-DISTANCE,
            point.x+DISTANCE,point.y+DISTANCE,
            x1,y1,
            x2,y2);
        MoveToEx(hdc,x1,y1,NULL); //RIGHT朝向情況下,移動坐標原點到右上角的弧弦交點
        LineTo(hdc,point.x,point.y); //繪制右上角的弧弦交點到大嘴中心坐標點的直線
        LineTo(hdc,x2,y2); //繪制大嘴中心坐標點到右下角的弧弦交點的直線
    }
    else if(frame%3 ==0) //繪制第3幀動畫 (橢圓)
    {
        Ellipse(hdc,point.x-DISTANCE,point.y-DISTANCE,
            point.x+DISTANCE,point.y+DISTANCE);
    }
    else  //繪制第1幀動畫 半圓
    {
        int x1=0,x2=0,y1=0,y2=0;
        switch(tw)
        {
            case UP: 
                x1 = point.x - DISTANCE;
                x2 = point.x + DISTANCE;
                y2 = y1 = point.y;
            break;
            case DOWN:
                x1 = point.x + DISTANCE;
                x2 = point.x - DISTANCE;
                y2 = y1 = point.y;
            break;
            case LEFT:
                x2 = x1 = point.x;
                y1 = point.y + DISTANCE;
                y2 = point.y - DISTANCE;
            break;
            case RIGHT:
                x2 = x1 =point.x ;
                y1 = point.y - DISTANCE;
                y2 = point.y + DISTANCE;
            break;
        }

        Arc(hdc,point.x-DISTANCE,point.y-DISTANCE,
            point.x+DISTANCE,point.y+DISTANCE,
            x1,y1,
            x2,y2);
        MoveToEx(hdc,x1,y1,NULL);
        LineTo(hdc,point.x,point.y);
        LineTo(hdc,x2,y2);
    }

    frame++;//繪制下一禎
}

//-----------------------------------------------------以下為Enermy(敵人)成員定義:------------------------------------------------

PacMan* Enermy::player = NULL; //在敵人類中,定義一個玩家(大嘴) 設置為靜態變量,全部敵人追捕一個公共的玩家

//Enermy(敵人)的構造函數
Enermy::Enermy(int x,int y):GObject(x,y)
{
    this->speed = ENERMY_SPEED; //設置敵人的速度
    twCommand = tw = LEFT; //設置開局時,敵人的移動方向
    //twCommand = UP;
}

//檢測敵人是否抓捕到大嘴
void Enermy::Catch()
{
    int DX =point.x - player->GetPos().x; //敵人與玩家的中心坐標,在x方向上的距離
    int DY =point.y - player->GetPos().y; //敵人與玩家的中心坐標,在y方向上的距離

    //敵人與玩家的中心坐標,在x、y方向上的距離小於繪圖距離RD,則說明敵人和玩家接觸了一半身體  敵人和玩家的直徑接近2*RD,實際是2*DISTANCE
    if((-RD<DX && DX<RD) && (-RD<DY && DY<RD)) 
    {
        player->Over(); //敵人抓到了玩家,玩家方向設置為OVER,不能再移動了
    }
}

//繪制敵人的圖像
void Enermy::Draw(HDC& hdc)
{
    HPEN pen =::CreatePen(0,0,color); //創建畫筆
    HPEN oldPen = (HPEN)SelectObject(hdc,pen); //應用創建的畫筆

    Arc(hdc,point.x-DISTANCE,point.y-DISTANCE,
        point.x+DISTANCE,point.y+DISTANCE,
        point.x+DISTANCE,point.y,
        point.x-DISTANCE,point.y);//先繪制半圓型的頭

    int const LEGLENTH = (DISTANCE)/(LEGCOUNTS); //LEGLENTH為“腿部圓弧”的半徑

    //根據禎數來繪制身體和“腿部”
    if(frame%2 == 0) //繪制第2幀和第4幀圖像
    {    
        //繪制2根直線,作為敵人的身子
        MoveToEx(hdc,point.x-DISTANCE,point.y,NULL);//矩形的身子
        LineTo(hdc,point.x-DISTANCE,point.y +DISTANCE - LEGLENTH);
        MoveToEx(hdc,point.x+DISTANCE,point.y,NULL); 
        LineTo(hdc,point.x+DISTANCE,point.y +DISTANCE - LEGLENTH);

        for(int i = 0;i<LEGCOUNTS;i++)//從左往右繪制“腿部”
        {
            //繪制腿部,為多個圓弧
            Arc(hdc,point.x-DISTANCE+i*2*LEGLENTH,point.y+DISTANCE-2*LEGLENTH,
                point.x-DISTANCE+(i+1)*2*LEGLENTH,point.y+DISTANCE,
                point.x-DISTANCE+i*2*LEGLENTH,point.y+DISTANCE-LEGLENTH,
                point.x-DISTANCE+(i+1)*2*LEGLENTH,point.y+DISTANCE-LEGLENTH
                );
        }        
    }
    else //繪制第1幀和第3幀圖像
    {
        MoveToEx(hdc,point.x-DISTANCE,point.y,NULL);//繪制身體
        LineTo(hdc,point.x-DISTANCE,point.y +DISTANCE);
        MoveToEx(hdc,point.x+DISTANCE,point.y,NULL); 
        LineTo(hdc,point.x+DISTANCE,point.y +DISTANCE);
        //從左往右繪制“腿部”

        MoveToEx(hdc,point.x-DISTANCE,point.y+DISTANCE,NULL);
        LineTo(hdc,point.x-DISTANCE+LEGLENTH,point.y+DISTANCE-LEGLENTH);

        for(int i = 0;i<LEGCOUNTS-1;i++)
        {
            Arc(hdc,point.x-DISTANCE+(1+i*2)*LEGLENTH,point.y+DISTANCE-2*LEGLENTH,
                point.x-DISTANCE+(3+i*2)*LEGLENTH,point.y+DISTANCE,
                point.x-DISTANCE+(1+i*2)*LEGLENTH,point.y+DISTANCE-LEGLENTH,
                point.x-DISTANCE+(3+i*2)*LEGLENTH,point.y+DISTANCE-LEGLENTH
                );
        }

        MoveToEx(hdc,point.x+DISTANCE,point.y+DISTANCE,NULL);
        LineTo(hdc,point.x+DISTANCE-LEGLENTH,point.y+DISTANCE-LEGLENTH);
    }

    //根據方向繪制眼睛
    int R = DISTANCE/5; //眼睛的半徑
    switch(tw)
    {
        case UP:
            Ellipse(hdc,point.x-2*R,point.y-2*R,point.x,point.y);
            Ellipse(hdc,point.x,point.y-2*R,point.x+2*R,point.y);
        break;
        case DOWN:
            Ellipse(hdc,point.x-2*R,point.y,point.x,point.y+2*R);
            Ellipse(hdc,point.x,point.y,point.x+2*R,point.y+2*R);
        break;
        case LEFT:
            Ellipse(hdc,point.x-3*R,point.y-R,point.x-R,point.y +R);
            Ellipse(hdc,point.x-R,point.y-R,point.x+R,point.y +R);
        break;
        case RIGHT:
            Ellipse(hdc,point.x-R,point.y-R,point.x+R,point.y +R);
            Ellipse(hdc,point.x+R,point.y-R,point.x+3*R,point.y+R);
        break;
    }

    frame++; //准備繪制下一禎

    SelectObject(hdc,oldPen);
    DeleteObject(pen); //銷毀畫筆

    return;
}

//敵人:數據變更的表現
void Enermy::action()
{
    bool b = Collision();
    MakeDecision(b);
    Catch();
}

//-----------------------------------------------------以下為RedOne(敵人)成員定義:------------------------------------------------

//繪制RedOne(敵人)的圖像
void RedOne::Draw(HDC& hdc)
{
    Enermy::Draw(hdc);
}

//AI-人工智能函數: 松散型
void RedOne::MakeDecision(bool b)
{
    //srand(time(0));
    int i = rand();
    if(b)//撞到牆壁,改變方向
    {
        //逆時針轉向
        if(i%4==0)
        {
            tw == UP?twCommand = LEFT:twCommand=UP;        
        }
        else if(i%3==0)
        {
            tw == DOWN?twCommand =RIGHT:twCommand=DOWN;
        }
        else if(i%2==0)
        {
            tw == RIGHT?twCommand = UP:twCommand=RIGHT;
        }
        else 
        {
            tw == LEFT?twCommand = DOWN:twCommand=LEFT;
        }
        return;
    }

    if(i%4==0)
    {
        twCommand!=UP?tw==DOWN:twCommand ==UP;        
    }
    else if(i%3==0)
    {
        tw != DOWN?twCommand = UP:twCommand=DOWN;
    }
    else if(i%2==0)
    {
        tw != RIGHT?twCommand = LEFT:twCommand=RIGHT;
    }
    else 
    {
        tw != LEFT?twCommand = RIGHT:twCommand=LEFT;
    }

}

//-----------------------------------------------------以下為BlueOne(敵人)成員定義:------------------------------------------------

//繪制BlueOne(敵人)的圖像
void BlueOne::Draw( HDC& hdc)
{
    Enermy::Draw(hdc);
}

//AI-人工智能函數: 守衛者
void BlueOne::MakeDecision(bool b)
{

    const int DR = this->dRow-player->GetRow();
    const int DA = this->dColumn-player->GetArray();
    if(!b&&DR==0)
    {
        if(DA<=BLUE_ALERT&&DA>0)//玩家在左側邊警戒范圍s
        {
            twCommand = LEFT;    //向左移動
            return;
        }
        if(DA<0&&DA>=-BLUE_ALERT)//右側警戒范圍
        {
            twCommand = RIGHT;//向右移動
            return;
        }
    }
    if(!b&&DA==0)
    {
        if(DR<=BLUE_ALERT&&DR>0)//下方警戒范圍
        {
            twCommand = UP;    
            return;
        }
        if(DR<0&&DR>=-BLUE_ALERT)//上方警戒范圍
        {    
            twCommand = DOWN;
            return;
        }
    }

    RedOne::MakeDecision(b);//不在追蹤模式時RED行為相同
}

//-----------------------------------------------------以下為YellowOne(敵人)成員定義:------------------------------------------------

//繪制YellowOne(敵人)的圖像
void YellowOne::Draw(HDC& hdc)
{
    Enermy::Draw(hdc);
}

//AI-人工智能函數:擾亂者
void YellowOne::MakeDecision(bool b)
{
    const int DR = this->dRow-player->GetRow();
    const int DA = this->dColumn-player->GetArray();
    if(!b)
    {
        if(DR*DR>DA*DA)
        {
            if(DA>0)//玩家在左側邊警戒范圍
            {
                twCommand = LEFT;    //向左移動
                return;
            }
            else if(DA<0)//右側警戒范圍
            {
                twCommand = RIGHT;//向右移動
                return;
            }
        }
        else
        {
            if(DR>0)//下方警戒范圍
            {
                twCommand = UP;    
                return;
            }
            if(DR<0)//上方警戒范圍
            {    
                twCommand = DOWN;
                return;
            }
        }
    }
    RedOne::MakeDecision(b);
}

 

PacMan.cpp(包含主函數)

// pacman.cpp : 定義應用程序的入口點。
//

#include "stdafx.h"
#include "pacman.h"
#include "GObject.h"

//-----start-----
#define WLENTH 700
#define WHIGHT 740
#define STAGE_COUNT 3 //關卡數

//游戲物體
PacMan* p ;
GObject* e1;
GObject* e2 ;
GObject* e3 ;
GObject* e4 ;

//釋放動態內存函數模板
template<class T>
void Realese(T t)
{
    if(t!=NULL)
        delete t; 
}

//進入下一關卡的時候,還原所有物體的位置
void ResetGObjects()
{
    p->SetPosition(P_ROW,P_COLUMN);
    e1->SetPosition(E_ROW,E_COLUMN);
    e2->SetPosition(E_ROW,E_COLUMN);
    e3->SetPosition(E_ROW,E_COLUMN);
    e4->SetPosition(E_ROW,E_COLUMN);
}

//------end------


#define MAX_LOADSTRING 100

// 全局變量:
HINSTANCE hInst;                                // 當前實例
TCHAR szTitle[MAX_LOADSTRING];                    // 標題欄文本
TCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口類名s

// 此代碼模塊中包含的函數的前向聲明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE,int,HWND&);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR    lpCmdLine,
    int       nCmdShow)
{

    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此放置代碼。
    int s_n = 0;//進行到的關卡數
    p = new PacMan(P_ROW,P_COLUMN);
    e1 =new RedOne(E_ROW,E_COLUMN);
    e2 =new RedOne(E_ROW,E_COLUMN);
    e3 = new BlueOne(E_ROW,E_COLUMN);
    e4 = new YellowOne(E_ROW,E_COLUMN);
    GMap* MapArray[STAGE_COUNT] = {new Stage_1(),new Stage_2(),new Stage_3()};
    GObject::pStage =MapArray[s_n];//初始化為第一關地圖
    Enermy::player = p;
    //-----end-----

    MSG msg;
    HACCEL hAccelTable;

    // 初始化全局字符串
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_PACMAN, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 執行應用程序初始化: 【改寫了】
    HWND hWnd;
    if (!InitInstance (hInstance, nCmdShow,hWnd))
    {
        return FALSE;
    }
    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PACMAN));

    DWORD t =0;
    // 主消息循環:
    while(p->GetTw()!=OVER&&s_n<3)
    {
        if(p->Win()) //贏了,p->Win()返回true
        {
            HDC hdc = GetDC(hWnd);    
            s_n++;
            ResetGObjects(); //進入下一關卡的時候,還原所有物體的位置
            if(s_n <3)
            {
                MessageBoxA(hWnd,"恭喜您過關","吃豆子提示",MB_OK);
                GObject::pStage = MapArray[s_n];
                RECT screenRect;
                screenRect.top = 0;
                screenRect.left = 0;
                screenRect.right = WLENTH;
                screenRect.bottom = WHIGHT;
                ::FillRect(hdc,&screenRect,CreateSolidBrush(RGB(255,255,255)));
                GObject::pStage->DrawMap(hdc);
            }
            continue;
        }

        if(PeekMessage(&msg, NULL, 0, 0,PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if(GetAsyncKeyState(VK_DOWN)&0x8000)
        {
            p->SetTwCommand(DOWN);
        }

        if(GetAsyncKeyState(VK_LEFT)&0x8000)
        {
            p->SetTwCommand(LEFT);
        }

        if(GetAsyncKeyState(VK_RIGHT)&0x8000)
        {
            p->SetTwCommand(RIGHT);
        }

        if(GetAsyncKeyState(VK_UP)&0x8000)
        {
            p->SetTwCommand(UP);
        }

        else
        {
            if(GetTickCount()-t>58) //每58ms,游戲的數據和畫面會更新一次
            {
                HDC hdc = GetDC(hWnd);    
                e1->action();
                e2->action();
                e3->action();
                e4->action();
                p->action();

                GObject::pStage->DrawPeas(hdc);
                e1->DrawBlank(hdc);
                e2->DrawBlank(hdc);
                e3->DrawBlank(hdc);
                e4->DrawBlank(hdc);    

                p->DrawBlank(hdc);        
                e1->Draw(hdc);
                e2->Draw(hdc);
                e3->Draw(hdc);
                e4->Draw(hdc);
                p->Draw(hdc);

                DeleteDC(hdc); //銷毀上下文設備

                t = GetTickCount(); //GetTickCount()函數獲得的是從開機到當前時刻機器運行的毫秒數
            }
        }
    }

    Realese(e1);
    Realese(e2);
    Realese(e3);
    Realese(e4);

    for(int i = 0;i<STAGE_COUNT;i++)
    {
        Realese(MapArray[i]);
    }

    if(p->GetTw()==OVER)
    {
        MessageBoxA(hWnd,"出師未捷","吃豆子提示",MB_OK);
    }
    else
    {
        MessageBoxA(hWnd,"恭喜您贏得了勝利","吃豆子提示",MB_OK);
    }

    Realese(p);

    return (int) msg.wParam;
}

//
//  函數: MyRegisterClass()
//
//  目的: 注冊窗口類。
//
//  注釋:
//
//    僅當希望
//    此代碼與添加到 Windows 95 中的“RegisterClassEx”
//    函數之前的 Win32 系統兼容時,才需要此函數及其用法。調用此函數十分重要,
//    這樣應用程序就可以獲得關聯的
//    “格式正確的”小圖標。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style            = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra        = 0;
    wcex.cbWndExtra        = 0;
    wcex.hInstance        = hInstance;
    wcex.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PACMAN));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName    = MAKEINTRESOURCE(IDC_PACMAN);
    wcex.lpszClassName    = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

//
//   函數: InitInstance(HINSTANCE, int)
//
//   目的: 保存實例句柄並創建主窗口
//
//   注釋:
//
//        在此函數中,我們在全局變量中保存實例句柄並
//        創建和顯示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow,HWND& hWnd)  //【改寫了,增加了一個參數HWND& hWnd】
{


    hInst = hInstance; // 將實例句柄存儲在全局變量中

    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        0, 0, WLENTH, WHIGHT, NULL, NULL, hInstance, NULL);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

//
//  函數: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的: 處理主窗口的消息。
//
//  WM_COMMAND    - 處理應用程序菜單
//  WM_PAINT    - 繪制主窗口
//  WM_DESTROY    - 發送退出消息並返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // 分析菜單選擇:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_START:

        case IDM_EXIT:
            DestroyWindow(hWnd);
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO: 在此添加任意繪圖代碼...
        GObject::pStage->DrawMap(hdc);
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        ::exit(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “關於”框的消息處理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM