用C++(TC3.0)做的贪吃蛇游戏-

Posted on 2005-08-29 10:21  Fly off sea  阅读(1379)  评论(1)    收藏  举报
用C++(TC3.0)做的贪吃蛇游戏---(1)     选择自 EmilMatthew 的 Blog  
关键字   用C++(TC3.0)做的贪吃蛇游戏---(1) 
出处    
 
 //游戏中使用链表的思路来自于金色甲虫站点,在此表示感谢。

/*贪吃蛇游戏引擎:MADE BY EMILMATTHEW 05/1/16
COMPILE ENVIROMENT TC3.0
注意事宜
1请设定好BGI文件路径
2上下左右键控制蛇移动,回车键暂停,ESC键退出.
*/
/*
难点及重点:
1) 采用链表的结构来表示蛇对象,要求我们学会用用一个链表类.
2) 画图时,使用一个抽象类AbstractDraw,它的派生类有三个:
 Drawhead,Drawbody,Drawtail,DrawBean;分别用以画头,身体,尾部及豆子.
 一开始都要画,游戏过程中,只要重画头及擦去尾巴即可。
3) 由于TC3不支持友元,所以对链表的插入工作需要放在Snake类本身中,稍显不适。
3) 碰撞检测:
 a)用一个数组加一个随机数过程生成不与蛇相交的一个位置,画出豆子.用以标识蛇头的坐标与豆子坐标重合时,蛇增长一节。
 b)与墙的碰撞检测,进行X,Y是否出界的CHECK 即可.

核心算法描述:

1声明游戏中相关的对象及标志变量: 
ScreenLayOut Test; Drawhead sDh; Drawbody sDb ;Cleantail   sCt…
 2初始化图形库,绘制场景图形及一开始的完整的蛇。
  sDh.Draw(h.getx(),h.gety());sDb.Draw(b1.getx(),b1.gety());….
  同时用一个检测函数在不与蛇相交处随机生成一粒豆子.
  3利用一个while(Gamestatur!=Exit)来控制整个游戏的进程{
      4.1利用while(bioskey(1)==0)进入一个不按键时的循环{
                a)Render,首先进入的动态绘制区,通过每次传回的GotBean来确定以何种方式画新的一帧:是否要增长一节?
       if(GotBean) 
        在头部与第二节处做链表插入动作,并改变相关的方向值及mx,my,mdir值。   
       蛇头变蛇身,在MDIR(方向标识变量)处画蛇头。并生成新的豆子.
       Else  
       遍历蛇的链表,把前一节的DIR(方向标识变量)传给一节(注意是从链表尾部往头部遍历),
       并相应的改变mx,my及dir的值.
       蛇头变蛇身,在MDIR(方向标识变量)处画蛇头,擦蛇尾.
       Delay2(0.1);//自编的DELAY函数,精度0.05秒,不随机器性能而有过大变动
        b)DataChecking{
        1碰边检测:是,则GameStatur=Failure;
        2是否咬到自己的检测.:是,则GameStatur=Failure
        3是否碰到障碍物:是,GameStatur=Failure
        4是否吃到豆子的检测:是,则GotBean=true;
        }
     4.2按下键后,用key=bioskey(0);得到键值,再用
         switch(key){
     case keyUp:…来做相应的数据处理。
       其中:上下左右键将改变头部的dir方向。
             ESC使GameStatur变成Exit;
             Enter使游戏处于暂停状态,用一个内嵌的键盘检测来做.      
         }//LOOPING BODY
这就是贪吃游戏的核心实现了,采用链表使得算法的设计上轻松的不少,当然,实现起来其中细节另有一番推敲及改进。
*/
#i nclude <dos.h>
#i nclude <bios.h>
#i nclude <time.h>
#i nclude <graphics.h>
#i nclude <conio.h>
#i nclude <stdio.h>
#i nclude <iostream.h>
#i nclude <stdio.h>
#i nclude <stdlib.h>
char address[]="C:\\TC\\bgi";
int Score=0;
typedef int bool;
const int true=1;
const int false=0;

const int fudu=10;

int const KeyUp=0x4800;
int const KeyDown=0x5000;
int const KeyLeft=0x4b00;
int const KeyRight=0x4d00;
int const KeyEnter=0x1c0d;
int const KeyEsc=0x11b;

void Delay2(double Times,float SecPerFrame);
enum GameStaturs{Gaming,Win,Failure,Exit};
enum Direction{Up,Down,Left,Right};
GameStaturs GameStatur=Gaming;
float SecPerFrame=0.05;

struct Obstacle{
 public:
  int x;
  int y;
  bool flag;
  Obstacle(){x=0;y=0;flag=false;}
};
struct Bean{
  public:
  int x;
  int y;
  bool flag;
  Bean(){x=0;y=0;flag=false;}
};


//一个集成化的画图类
class SuperCol{
       public:
       void rect2(int tx,int ty,int bx,int by,int col=10);
       void printword(int x, int y, const char far *textstring,int font=1,
    int direction=0,int charsize=0,int col=12);
       void bar2(int tx,int ty,int bx,int by,int patternCol=BLUE,int patterns=SOLID_FILL);
       void shapefill(int x,int y,int BorderCol,int patternCol=BLUE,int patterns=SOLID_FILL);
       void circle2(int x,int y,int r,int col=RED);
       void circle3(int x,int y,int r,int scol=RED,int fcol=RED,int patterns=SOLID_FILL);
       void line2(int tx,int ty,int bx,int by,int col=10);
       void ellipse2(int x, int y,int xradius, int yradius, int col=GREEN,int stangle=0,
        int endangle=360);
       void backOutWord(int tx,int ty,int bx,int by,const char far *textstring,
        int colB=YELLOW,int colF=GREEN,int font=1,int charsize=0,int direction=0);
       };
void  SuperCol::rect2(int tx,int ty,int bx,int by,int col){
  setcolor(col);
  rectangle(tx,ty,bx,by);
  }
void SuperCol::printword(int x, int y, const char far *textstring,int font,int direction,int charsize,int col){
  setcolor(col);
  settextstyle(font, direction, charsize);
  outtextxy(x,y,textstring);
  }
void SuperCol::bar2(int tx,int ty,int bx,int by,int patternCol,int patterns){
  setfillstyle(patterns,patternCol);
  bar(tx, ty, bx,by);
}
void SuperCol::shapefill(int x,int y,int BorderCol,int patternCol,int patterns){
  setfillstyle(patterns,patternCol);
  floodfill(x,y,BorderCol);
 }
void SuperCol::circle2(int x,int y,int r,int col){
  setcolor(col);
  circle(x,y,r);
 }
void SuperCol::line2(int tx,int ty,int bx,int by,int col){
  setcolor(col);
  line(tx,ty,bx,by);
 }
void SuperCol::circle3(int x,int y,int r,int scol,int fcol,int patterns){
 circle2(x,y,r,scol);
 shapefill(x,y,scol,fcol,patterns);
 }
void SuperCol::ellipse2(int x, int y,int xradius, int yradius, int col,int stangle, int endangle){
  setcolor(col);
  ellipse(x,y,stangle,endangle,xradius,yradius);
}
void SuperCol::backOutWord(int tx,int ty,int bx,int by,const char far *textstring,int colB,
 int colf,int font,int charsize,int direction){
  bar2(tx,ty,bx,by,colB);
  printword(tx+3, ty+3, textstring,font,direction,charsize,colf);
 }

 

用C++(TC3.0)做的贪吃蛇游戏---(2)     选择自 EmilMatthew 的 Blog  
关键字   用C++(TC3.0)做的贪吃蛇游戏---(2) 
  
 
 //**画蛇的相关类,实现时并没有用到多态性**//
class AbstractDraw{
public:
 virtual void Draw(int x,int y)=0;
};
class Drawhead:public AbstractDraw{
public:
 void Draw(int x,int y);
};
class Drawbody:public AbstractDraw{
public:
 void Draw(int x,int y);
 };
class Cleantail:public AbstractDraw{
public:
 void Draw(int x,int y);
};

class DrawBean:public AbstractDraw{
public:
 void Draw(int x,int y);
};
 

void Drawhead::Draw(int x,int y){
 SuperCol Brush;
 int sx,sy;
 sx=getmaxx()/2-19*fudu;sy=getmaxy()/2-14*fudu;
 Brush.circle3(sx+(x+0.5)*fudu,sy+(y+0.5)*fudu,fudu/2,BLUE,RED);
 }
void Drawbody::Draw(int x,int y){
 SuperCol Brush;
 int sx,sy;
 sx=getmaxx()/2-19*fudu;sy=getmaxy()/2-14*fudu;
 Brush.bar2(sx+x*fudu,sy+y*fudu,sx+(x+1)*fudu,sy+(y+1)*fudu,BLUE);
 }
void Cleantail::Draw(int x,int y){
 SuperCol Brush;
 int sx,sy;
 sx=getmaxx()/2-19*fudu;sy=getmaxy()/2-14*fudu;
 Brush.bar2(sx+x*fudu,sy+y*fudu,sx+(x+1)*fudu,sy+(y+1)*fudu,BLACK);
 }
void DrawBean::Draw(int x,int y){
 SuperCol Brush;
 int sx,sy;
 sx=getmaxx()/2-19*fudu;sy=getmaxy()/2-14*fudu;
 Brush.circle3(sx+(x+0.5)*fudu,sy+(y+0.5)*fudu,fudu/2,YELLOW,WHITE);
}

//* /*负责图形函数的起始与静态场景的绘制*/  *//
class ScreenLayOut{
 public:
  void GraphicStart();
  void GraphicEnd();
  void ErrorDetect();
  void test();
 };

void ScreenLayOut::GraphicStart(){
 int gdriver,gmode;
 gdriver=DETECT;
 gmode=VGAHI;
 //Kinitgraph(&gdriver,&gmode,"D:\\TC\\BGI");//Bgi's address shoud be compitabled with Source File
 initgraph(&gdriver,&gmode,address);
 cleardevice();//Haha Got it!!!
 }
void ScreenLayOut::GraphicEnd(){
       closegraph();
       }
void ScreenLayOut::ErrorDetect(){
 int errorcode=0;
 errorcode = graphresult();
        if (errorcode != grOk)  /* an error occurred */
        {
    GameStatur=Exit;
    printf("Graphics error: %s\n", grapherrormsg(errorcode));
    printf("Press any key to halt:");
    getch();
        }
 }
void ScreenLayOut::test(){
;
 }

class Snake{//核心的蛇类
 public:
  void Insert(Snake *now);
  void DirChan();
  void CheckEatSelf();
  void CheckEatBean();
  Snake(){
   mx=0;my=0;mdir=Right;next=NULL;
  }
  Snake(int x,int y,Direction dir,Snake *s=NULL){
   mx=x;
   my=y;
   mdir=dir;
   next=s;
  }
  void show();
  //void Bianli();
  int getx(){return mx;}
  int gety(){return my;}
  int getdir(){return mdir;}
  void setdir(Direction p){mdir=p;
      DirChan(); }
  void Bianlidir();//精->遍链表并改变相关变量值的函数
     bool EatSelf();
  //void SetTail(){this->next=NULL;}
  bool HitWall();//碰墙检测
     void MakeNewBean(Obstacle Ob[],int num,Bean &tmpD);//产生新豆
  bool HitBrack(Obstacle Ob[],int num);//碰障碍物检测
     bool GotBean(Bean dou);//吃到豆子的检测

 private:
  int mx;
  int my;
  Direction mdir;
  Snake *next;
//here you didn't use  new/delete ,which is surely a hidden dangerous.
//As a beginner ,I forgive you.
};
void Snake::show(){
cout<<" x: "<<mx<<" y: "<<my<<" dir "<<mdir<<" -> ";
if(next)next->show();
else cout<<"End\n";
}
void Snake::DirChan(){
 switch(this->mdir){
 case Up:this->my--;break;
 case Down:this->my++;break;
 case Left:this->mx--;break;
 case Right:this->mx++;break;
 default:break;
}
}
void Snake::Insert(Snake *now){
 now->next=this->next;//注意了,now 的内部是可以访问的,这给没有友元的TC带来很大的方便.
 this->next=now;
 now->mx=this->mx;now->my=this->my;now->mdir=this->mdir;
    // cout<<(now->next==NULL);
 DirChan();//yes
}

void Snake::Bianlidir(){//from tail to head ,not head to tail
    if(next){
 next->Bianlidir();
 next->mdir=this->mdir;
 next->DirChan();
 }
}
bool Snake::EatSelf(){
 bool flag=false;
 Snake *pTmp=NULL;
 if(next){
 for(pTmp=next;pTmp!=NULL;pTmp=pTmp->next)
 {if((this->getx()==pTmp->getx())&&(this->gety()==pTmp->gety()))
  {
  flag=true;
  break;
  }
 }
 }
 return flag;
}
bool Snake::HitWall(){
 bool flag=false;
 //Bad smell,for the bare number!.
 if((this->mx<0)||(this->mx>37)||(this->my<0)||(this->my>27))
 {
 flag=true;
 
 }
 return flag;
}
void Snake::MakeNewBean(Obstacle Ob[],int num,Bean &tmpD){
 bool flag=true;
 int i=0,j=0,dx=0,dy=0;
    Snake *pTmp=NULL;
 int tmpArray[38][28];
 for(i=0;i<38;i++){
  for(j=0;j<28;j++){
   tmpArray[i][j]=0;
   }
  }
    randomize();
    for(i=0;i<num;i++){
  if(Ob[i].flag){
      tmpArray[Ob[i].x][Ob[i].y]=1;
     
      }
 }
    if(next){
      for(pTmp=next;pTmp!=NULL;pTmp=pTmp->next)
   {
   tmpArray[pTmp->getx()][pTmp->gety()]=1;
   }
   }
    flag=false;
    while(!flag)
    {dx=random(35)+1;
     dy=random(25)+1;
    // cout<<"dx"<<dx;
    if(tmpArray[dx][dy]==0){
    tmpD.x=dx;
    tmpD.y=dy;
    flag=true;
      }

  }
gotoxy(2,2);
printf("Score:%d",Score);
Score++;
}

bool Snake::HitBrack(Obstacle Ob[],int num)
{       bool flag=false;
     int i=0 ;
  for(i=0;i<num;i++){
  if(Ob[i].flag){
   if((Ob[i].x==this->getx())&&(Ob[i].y==this->gety()))flag=true;
   }
  }
  return flag;
}

bool Snake::GotBean(Bean dou){
//Consider a multiple beans mode?Too complicated ,maybe.
   bool flag=false;
   if((dou.x==this->getx())&&(dou.y==this->gety()))flag=true;
   return flag;
}

 

class Game{//最为关键的游戏类
 public:
  void MainBody();
  void GameInitialize();
  void MapObstacle(Obstacle Ob[],int x);
     void GraphicEnd();
  void Keywaiting();
  bool Continue();
  void PressAnyKey();
};
void Game::GameInitialize(){
  ScreenLayOut P;
  SuperCol Brush;
  int midx,midy,i;
  P.GraphicStart();
  P.ErrorDetect();
  midx=getmaxx()/2;midy=getmaxy()/2;
  Brush.bar2(midx-20*fudu,midy-15*fudu,midx+20*fudu,midy+15*fudu,LIGHTBLUE);
  Brush.bar2(midx-19*fudu,midy-14*fudu,midx+19*fudu,midy+14*fudu,BLACK);
 Brush.printword(midx-16*fudu, midy-19*fudu+5, "Snake",1,0,0,LIGHTGREEN);
 Brush.printword(midx-2*fudu,midy-17*fudu+10,"Made by:EmilMatthew 05/1/16 ",0,0,1,BROWN);
 }
void Game::MapObstacle(Obstacle Ob[],int x){
 SuperCol Brush;
 int i=0;
 int midx=getmaxx()/2,midy=getmaxy()/2;
 for(i=0;i<x;i++){
   if(Ob[i].flag){
   Brush.bar2(midx-19*fudu+Ob[i].x*fudu,midy-14*fudu+Ob[i].y*fudu,
   midx-18*fudu+Ob[i].x*fudu,midy-13*fudu+Ob[i].y*fudu,LIGHTBLUE);
   }
  }

}
void Game::Keywaiting(){
 gotoxy(5,10);
    printf ("Pause");
    while(bioskey(1)==0){;}
    gotoxy(5,10);
    printf("       ");
}
void Game::PressAnyKey(){
 gotoxy(5,10);
 printf("Press any key to continue...\n");
}
bool Game::Continue(){
  //  clrscr();
    char tmp;
 gotoxy(5,10);
 printf("                             ");
 gotoxy(8,11);
 printf("Oh ,you failure\n");
 gotoxy(8,12);
 printf("Try Again?(y/n)\n");
 gotoxy(8,13);
 scanf("%c",&tmp);
 if(tmp=='y')return true;
 else     return false;
}
void Game::MainBody(){
 //Declare Object or Variables
 ScreenLayOut Test;
 Drawhead sDh;
 Drawbody sDb;
 Cleantail   sCt;
 DrawBean dou;
 Obstacle stock[5];
 Bean     douzi;
 int key=0,index=0;
 bool Gotbean=false,check;
 bool KeyPressed=false;//如果按键过快,将出现不改变
 Direction tmdir=Right;
 //Set Start Data;
 Snake w(1,0,Right,NULL);
 Snake b2(2,0,Right,&w);
 Snake b1(3,0,Right,&b2);
 Snake h(4,0,Right,&b1);
 Snake tb1[300];//(0,0,Right,NULL);
 Snake tb2(0,0,Right,NULL);
 randomize();
 stock[0].flag=true;
 stock[0].x=random(22)+5;
 stock[0].y=random(26)+1;
 douzi.flag=true;
 Score=0;
   
    MapObstacle(stock,1);
     
 //Draw First Snake.
 sDh.Draw(h.getx(),h.gety());
 sDb.Draw(b1.getx(),b1.gety());
 sDb.Draw(b2.getx(),b2.gety());
    //Generate Bean.
 h.MakeNewBean(stock,5,douzi);//this interface is not a good one,as I thought
 dou.Draw(douzi.x,douzi.y);
 while(GameStatur==Gaming){
      while(bioskey(1)==0&&GameStatur==Gaming){
        if(Gotbean){
      //1Insert
     h.Insert(&tb1[index]);
    
     sDb.Draw(tb1[index].getx(),tb1[index].gety());

     index++;
     sDh.Draw(h.getx(),h.gety());
     Gotbean=false;
    //Not so perfect.
   //Add random make bean  code etc.
         // w.SetTail();
    h.MakeNewBean(stock,1,douzi);
    dou.Draw(douzi.x,douzi.y);
    }
         else{
    //2bian li
   //At first ,you use this:*tb=&h,of course it just stands for h,
   //can't work as your thought.
    tb2=h;//If you use tb1=h,there will be an error ,because of the shallow copy
    h.Bianlidir();
    h.setdir(tmdir);
    sDh.Draw(h.getx(),h.gety());
    sDb.Draw(tb2.getx(),tb2.gety());
    sCt.Draw(w.getx(),w.gety());
         }
        Delay2(0.15,SecPerFrame);
  //Collision Check
  //Eat self
     if(h.EatSelf())
  {   cout<<"Eat self"<<endl;
   PressAnyKey();
   GameStatur=Failure;
   break;
  }
 //Collision with wall
  if(h.HitWall())
  {   cout<<"Hit Wall"<<endl;
   PressAnyKey();
   GameStatur=Failure;
   break; //Attention here
  }
  //Collision with obstacle
  if(h.HitBrack(stock,1))
  {       cout<<"HitBrack"<<endl;
   PressAnyKey();
   GameStatur=Failure;
   break;
  }
     if(h.GotBean(douzi)){

   Gotbean=true;
  }
  KeyPressed=false;
  }
 //Check key
   key=bioskey(0);
         if(!KeyPressed)
          {switch(key){
    case KeyUp: if(tmdir!=Down){
         tmdir=Up;
           }
         break;
    case KeyDown:if(tmdir!=Up){
         tmdir=Down; }
         break;
    case KeyLeft:if(tmdir!=Right){
          tmdir=Left;}
          break;
    case KeyRight:if(tmdir!=Left){
           tmdir=Right;}
          break;
    case KeyEnter:
     //small KeyWaiting
     Keywaiting();
        break;
    case KeyEsc:
        GameStatur=Exit;
    break;
    default:
    break;
    }
    KeyPressed=true;
    }

 }

 if(GameStatur==Failure)
 {
  check=Continue();
  if(check)GameStatur=Gaming;
  else GameStatur=Exit;
  }
}

void Game::GraphicEnd(){
 ScreenLayOut P;
 P.GraphicEnd();
 }

void main(void){
 //ScreenLayOut Gg;
 Game Director;
 while(GameStatur==Gaming){
  Director.GameInitialize();
  if(GameStatur!=Exit)Director.MainBody();
     Director.GraphicEnd();
 }
 getch();
 }

void Delay2(double Times,float SecPerFrame){
bool Running=true;
double Tmptime=(double)clock()/(double)CLOCKS_PER_SEC;
double caculate=0,delaying=SecPerFrame;
while(Running&&caculate<Times){
   if((Tmptime+delaying)<((double)clock()/(double)CLOCKS_PER_SEC))
   {Tmptime=(double)clock()/(double)CLOCKS_PER_SEC;
   caculate+=delaying;
  }
}
}

博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3