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