標(biāo)題:
基于89S52單片機(jī)與12864液晶的俄羅斯方塊游戲
[打印本頁]
作者:
朱光光
時(shí)間:
2016-3-29 19:46
標(biāo)題:
基于89S52單片機(jī)與12864液晶的俄羅斯方塊游戲
#ifndef _MAIN_H
#define _MAIN_H
#include <reg52.h>
#define LcdDataPort P2 //數(shù)據(jù)端口
#ifndef UCHAR_DEF
#define UCHAR_DEF
typedef unsigned char uchar;
#endif
sbit Reset = P3^0; //復(fù)位
sbit RS = P3^1; //指令數(shù)據(jù)選擇
sbit E = P3^2; //指令數(shù)據(jù)控制
sbit CS1 = P3^4; //左屏幕選擇,低電平有效
sbit CS2 = P3^5; //右屏幕選擇
sbit RW = P3^3; //讀寫控制
sbit busy = P2^7; //忙標(biāo)志
void SetOnOff(uchar onoff); //開關(guān)顯示
void SelectScreen(uchar screen);//選擇屏幕
void ClearScreen(uchar screen); //清屏
void Show1616(uchar lin,uchar colum,uchar *address);//顯示一個(gè)漢字
void CheckState(); //判斷狀態(tài)
void LcdDelay(unsigned int time); //延時(shí)
void WriteData(uchar dat); //寫數(shù)據(jù)
void SendCommand(uchar command); //寫指令
void SetLine(uchar line); //置行地址
void SetColum(uchar colum);//置列地址
void SetStartLine(uchar startline);//置顯示起始行
void InitLcd(); //初始化
void ResetLcd(); //復(fù)位
#endif
/*********************************************************************/
/***********************************lcd.c****************************************/
void CheckState()
{
E = 1;
RS = 0;
RW = 1;
LcdDataPort = 0xff;
while(!busy);
}
void LcdDelay(unsigned int time)
{
while(time --);
}
void WriteData(uchar dat)
{
CheckState();
E = 1;
RS = 1;
RW = 0;
LcdDataPort = dat;
E = 0;
}
void SendCommand(uchar command)
{
CheckState();
E = 1;
RW = 0;
RS = 0;
LcdDataPort = command;
E = 0;
}
void SelectScreen(uchar screen) //0-全屏,1-左屏,2-右屏
{
switch(screen)
{
case 0 :
CS1 = 0;
LcdDelay(2);
CS2 = 1;
LcdDelay(2);
break;
case 1 :
CS1 = 1;
LcdDelay(2);
CS2 = 0;
LcdDelay(2);
break;
case 2 :
CS1 = 0;
LcdDelay(2);
CS2 = 0;
LcdDelay(2);
break;
}
}
void ClearScreen(uchar screen) // screen 0-全屏,1-左屏,2-右屏
{
uchar i,j;
SelectScreen(screen);
for(i = 0;i < 8;i ++)
{
SetLine(i);
SetColum(0);
for(j = 0;j < 64; j ++)
WriteData(0);
}
}
void SetLine(uchar line) //line -> 0 : 7
{
line = line & 0x07;
line = line | 0xb8; //1011 1xxx
SendCommand(line);
}
void SetColum(uchar colum) //colum -> 0 :63
{
colum = colum & 0x3f;
colum = colum | 0x40; //01xx xxxx
SendCommand(colum);
}
void SetStartLine(uchar startline) //startline -> 0 : 63
{
startline = startline & 0x3f;
startline = startline | 0xc0; //11xxxxxx
SendCommand(startline);
}
void SetOnOff(uchar onoff) //1-開顯示 0-關(guān)
{
if(onoff == 1)
SendCommand(0x3f); //0011 111x
else
SendCommand(0x3e);
}
void ResetLcd()
{
Reset = 0;
LcdDelay(2);
Reset = 1;
LcdDelay(2);
RS0 = 0;
LcdDelay(2);
RS1 = 0;
LcdDelay(2);
SetOnOff(1);
}
void InitLcd()
{
ResetLcd();
SetOnOff(0);
ClearScreen(2);
SetLine(0);
SetColum(0);
SetStartLine(0);
SetOnOff(1);
}
void Show1616(uchar lin,uchar colum,uchar *address)
{
uchar i;
SetLine(lin);
SetColum(colum);
for(i = 0;i < 16;i ++)
WriteData(*(address ++));
SetLine(lin + 1);
SetColum(colum);
for(i = 0;i < 16;i ++)
WriteData(*(address ++));
}
/*******************************************************************************/
/********************************main.c***********************************************/
#include <reg52.h>
const uchar code HZ_tab[] = {44
};
void main()
{
uchar i,line,colum ;
uchar *address ;
InitLcd();
while(1)
{
colum = 16;
line = 1;
address = HZ_tab;
SetOnOff(0); //關(guān)顯示
for(i = 1;i < 7;i ++)
{
if(i < 4)
SelectScreen(0);
else
SelectScreen(1);
Show1616(line,colum ,address);
colum += 16;
if(colum >63)
colum = 0;
address += 32; //向DDRAM中寫入數(shù)據(jù)
}
line = 5;
colum = 0;
for(i = 0;i <8; i ++)
{
if(i < 4)
SelectScreen(0);
else
SelectScreen(1);
Show1616(line,colum ,address);
colum += 16;
if(colum >63)
colum = 0;
address += 32;
}
SelectScreen(2);
SetOnOff(1); // 開顯示
for(i = 0;i < 50;i ++) //延時(shí)
LcdDelay(30000);
}
}
/************說明******************************
此程序包含一個(gè)俄羅斯方塊.c 文件和一個(gè)12864.h 文件
********************俄羅斯方塊.c文件**************************/
#include "reg51.h"
#include "12864.h"
#define uchar unsigned char
#define uint unsigned int
static unsigned long Seed = 1;
#define A 48271L
#define M 2147483647L
#define Q (M / A)
#define R (M % A)
sbit K1=P3^4;
sbit K2=P3^5;
sbit K3=P3^6;
sbit K4=P3^7;
unsigned int idata num[19+2]={
0xfff,//第1行,最下面
0x801,0x801,0x801,0x801,0x801,0x801,0x801,0x801,0x801,0x801,
0x801,0x801,0x801,0x801,0x801,0x801,0x801,0x801,0x801,//第2行到第20行共19行
0xfff//第21行,最上面
};//定義共21行,其中num[0]為下墻壁行,num[20]為上墻壁行,每行12格,最左一格為左墻壁列,最右一格為右墻壁列
unsigned char code Block[28][2]={
/*
* 口 口口口 口口
* 口 口 口 口
* 口口 口 口口口
*/
{0x88,0xc0},{0xe8,0x00},{0x62,0x20},{0x02,0xe0},
/*
* 口 口口 口口口
* 口 口 口 口
* 口口 口口口 口
*/
{0x22,0x60},{0x08,0xe0},{0xc8,0x80},{0xe2,0x00},
/*
* 口
* 口口 口口
* 口 口口
*/
{0x8c,0x40},{0x6c,0x00},{0x8c,0x40},{0x6c,0x00},
/*
* 口 口口
* 口口 口口
* 口
*/
{0x4c,0x80},{0xc6,0x00},{0x4c,0x80},{0xc6,0x00},
/*
* 口 口
* 口 口口 口口口 口口
* 口口口 口 口 口
*/
{0x04,0xe0},{0x8c,0x80},{0xe4,0x00},{0x26,0x20},
/*口
* 口
* 口 口口口口
* 口
*/
{0x44,0x44},{0x0f,0x00},{0x44,0x44},{0x0f,0x00},
/*
* 口口
* 口口
*/
{0x06,0x60},{0x06,0x60},{0x06,0x60},{0x06,0x60}
};
#define PASSSCORE 20
struct Jimu
{
unsigned int dat;
char x;
unsigned char y;
unsigned char type;
unsigned char change;
}Sign[3];//積木結(jié)構(gòu)體
unsigned char SysFlag=0;
#define NEWSIGNFLAG 0
#define DEADFLAG 1
#define PAUSEFLAG 2
unsigned char Score=0;
unsigned char Level=1;
unsigned char DelayCnt=5;
/*********************************************************/
#define N 25
/************************************
偽隨機(jī)數(shù)發(fā)生器
*************************************/
double Random(void)
{
long TmpSeed;
TmpSeed=A*(Seed%Q)-R*(Seed/Q);
if(TmpSeed>=0)
Seed=TmpSeed;
else
Seed=TmpSeed+M;
return (double)Seed/M;
}
/**************************************
為偽隨機(jī)數(shù)發(fā)生器播種
***************************************/
void InitRandom(unsigned long InitVal)
{
Seed=InitVal;
}
//延時(shí)子程序
void Delay(unsigned int t)
{
unsigned int i,j;
for(i=0;i<t;i++)
for(j=0;j<10;j++);
}
/*********************************
初始化MPU
**********************************/
void InitCpu(void)
{
TMOD=0x0;
TH0=0;
TL0=0;
TR0=1;
ET0=1;
EX1=1;
EA=1;
TCON|=0x04;
}
/****************************
welcome 游戲選擇界面
/**********************/
void welcome()
{
Lcd_WriteStr(0,0,"歡迎來玩");
Lcd_WriteStr(0,1,"俄羅斯方塊");
Lcd_WriteStr(0,2,"設(shè)置按K1");
Lcd_WriteStr(0,2,"開玩按K2");
}
/*************俄羅斯方塊部分
/******************************
畫墻壁,初始化界面
*******************************/
void DrawBoard(void)
{
unsigned char n;
for(n=0;n<12;n++) ////畫上下兩邊的墻壁
{
Lcd_Rectangle(3*n,0,3*n+2,2,1);////在最上面一行畫個(gè)2*2的方塊
Lcd_Rectangle(3*n,60,3*n+2,62,1);////在最下面一行畫個(gè)2*2的方塊
}
for(n=0;n<20;n++) ////畫左右兩邊的墻壁
{
Lcd_Rectangle(0,3*n,2,3*n+2,1); ////在左面畫2*2的方塊
Lcd_Rectangle(33,3*n,35,3*n+2,1);////在右面畫2*2的方塊
}
Lcd_WriteStr(4,0,"經(jīng)典游戲");
Lcd_WriteStr(3,2,"Score:");
Lcd_WriteStr(3,3,"Level:");
}
/***********************************
游戲結(jié)束處理
************************************/
void GameOver(void)
{
if((SysFlag&(1<<DEADFLAG))!=0)
Lcd_WriteStr(3,1,"You Fail");////SysFlag=2
else
Lcd_WriteStr(3,1,"You Pass");////SysFlag=1或=0
}
unsigned int code MaskTab[16]={
0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,
0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000
};
/**********************************
根據(jù)積木圖標(biāo)左下坐標(biāo)X,Y來畫出積木圖標(biāo)
***********************************/
void DrawSign(struct Jimu Temp,unsigned char DrawMode)
{
unsigned char m,n;
for(m=0;m<4;m++)
for(n=0;n<4;n++)
{
if((Temp.dat&MaskTab[4*m+n])!=0)
Lcd_Rectangle(Temp.x+n*3,Temp.y-2-3*m,Temp.x+n*3+2,Temp.y-3*m,DrawMode);
}
}
/********************************
將積木圖標(biāo)值融入num數(shù)據(jù)中
也即把積木圖標(biāo)固定,無法再下降
*********************************/
void FixSign(void)
{
unsigned char m,n;
for(m=0;m<4;m++)//行循環(huán)
for(n=0;n<4;n++)//列循環(huán)
{
if((Sign[0].dat&MaskTab[4*m+n])!=0)
{
num[20-(Sign[0].y-2)/3+m]|=MaskTab[11-Sign[0].x/3-n];
}
}
}
/********************************
判斷積木圖標(biāo)中方塊是否與障礙方塊重合
*********************************/
unsigned char CheckIf(void)
{
unsigned char m,n;
for(m=0;m<4;m++)//行循環(huán)
for(n=0;n<4;n++)//列循環(huán)
{
if((Sign[1].dat&MaskTab[4*m+n])!=0)
{
if((num[20-(Sign[1].y-2)/3+m]&MaskTab[11-Sign[1].x/3-n])!=0)
return 0;
}
}
return 1;
}
/********************************
判斷積木圖標(biāo)是否可以繼續(xù)下降一格
********************************/
unsigned char CheckIfDown(void)
{
Sign[1]=Sign[0];//
Sign[1].y+=3;//假設(shè)下降一格
return CheckIf();
}
/********************************
判斷積木圖標(biāo)是否可以向左移動(dòng)
*********************************/
unsigned char CheckIfLeft(void)
{
Sign[1]=Sign[0];
Sign[1].x-=3;
return CheckIf();
}
/********************************
判斷積木圖標(biāo)是否可以向右移動(dòng)
*********************************/
unsigned char CheckIfRight(void)
{
Sign[1]=Sign[0];
Sign[1].x+=3;
return CheckIf();
}
/********************************
判斷是否可以旋轉(zhuǎn)
*********************************/
unsigned char CheckIfRoll(void)
{
unsigned char i;
unsigned int Temp;
Sign[1]=Sign[0];
if(++Sign[1].change>3)
Sign[1].change=0;
i=Sign[1].type*4+Sign[1].change;
Temp=(unsigned int)Block[i][0]<<8;
Temp=Temp|Block[i][1];
Sign[1].dat=Temp;
return CheckIf();
}
/********************************
尋找滿格的行并做消除處理
最多尋找4個(gè)滿行并做消除
*********************************/
void DelFull(void)
{
unsigned char m,n;
unsigned char Temp;
unsigned char Flag=0;
Temp=(Sign[0].y-2)/3;
if(Temp>=20)//防止越過了下邊界
Temp=1;
else
Temp=20-Temp;
////Temp為積木的最下邊界,即最下一行
for(n=Temp+3;n>=Temp;n--)//積木圖標(biāo)的最頂行開始尋找滿行比較有利于運(yùn)算
{
if(num[n]==0xfff)
{
Flag=1;
for(m=n+1;m<=19;m++)
{
num[m-1]=num[m];
}
num[m]=0x801;
Score++;//每找到一個(gè)滿行,則分?jǐn)?shù)加1
}
}
if(Flag)//為加速而設(shè)置并判斷的標(biāo)志,有已固定的積木有滿格消行變化則重畫積木界面
{
/*行*/for(m=Temp;m<=19;m++)//為加速,不必要重第一行重畫起,只需要從積木圖標(biāo)最下行開始往上的重畫
/*列*/for(n=1;n<=10;n++)
{
if((num[m]&MaskTab[n])==0)
{
if(Lcd_ReadPixel(30-(n-1)*3,57-(m-1)*3)!=0)
//為加速而做的讀象素操作
////如果該像素為1,即為黑點(diǎn)
{
Lcd_Rectangle(30-(n-1)*3,57-(m-1)*3,30-(n-1)*3+2,57-(m-1)*3+2,0);
}////將給像素設(shè)置為0,即為空白
}
else
{
if(Lcd_ReadPixel(30-(n-1)*3,57-(m-1)*3)==0)//為加速而做的讀象素操作
{ ////如果該像素為0
Lcd_Rectangle(30-(n-1)*3,57-(m-1)*3,30-(n-1)*3+2,57-(m-1)*3+2,1);
} ////將給像素設(shè)置為1
}
}
}
}
/*******************************
隨機(jī)產(chǎn)生一個(gè)積木圖標(biāo)放到預(yù)產(chǎn)生區(qū)域并顯示出來
********************************/
void CreatSign(void)
{
unsigned char n;
unsigned int Temp;
DrawSign(Sign[2],0);//先清除
n=Random()*28;
Temp=(unsigned int)Block[n][0]<<8; ////產(chǎn)生積木圖形的高八位
Temp=Temp|Block[n][1]; ////產(chǎn)生積木圖形的第八位
Sign[2].dat=Temp;
Sign[2].x=45;
Sign[2].y=4*3+2;
Sign[2].type=n/4;////什么意思
Sign[2].change=n%4;
DrawSign(Sign[2],1);//后畫出
}
void PrintScore(void)
{
unsigned char Str[3];
Str[0]=(Score/10)|0x30;
Str[1]=(Score%10)|0x30;
Str[2]=0;
Lcd_WriteStr(6,2,Str);
}
void PrintLevel(void)
{
unsigned char Str[3];
Str[0]=(Level/10)|0x30;
Str[1]=(Level%10)|0x30;
Str[2]=0;
Lcd_WriteStr(6,3,Str);
}
/********************************
游戲的具體過程,也是俄羅斯方塊算法的關(guān)鍵部分
*********************************/
void GamePlay(void)
{
unsigned char m,n;
unsigned int Temp;
SysFlag|=1<<NEWSIGNFLAG;//剛開始初始化為需要產(chǎn)生新的積木圖標(biāo) ////SysFlag=1
InitRandom(TL0); ////用以產(chǎn)生隨即數(shù)
Lcd_WriteStr(3,1,"Playing");
PrintScore();
PrintLevel();
CreatSign();////先清空積木顯示區(qū)域,在隨即產(chǎn)生一個(gè)類型的積木
while(1)
{
if((SysFlag&(1<<NEWSIGNFLAG))==1)//判是否需要產(chǎn)生新的積木圖標(biāo)
{////SysFlag=1時(shí)產(chǎn)生新積木
SysFlag&=~(1<<NEWSIGNFLAG); ////SysFlag=0
Sign[0]=Sign[2];
CreatSign();
Sign[0].x=12;
Sign[0].y=14;
for(m=0;m<4;m++)//行循環(huán)
{
for(n=0;n<4;n++)//列循環(huán)
{
if((Sign[0].dat&MaskTab[15-m*4-n])==0) ////從最高位開始倒序測試
break;////測試Sign[0].dat的每一位,如果為該位為1,則繼續(xù)測試下一位
////若為0,則直接測試下半字節(jié)
}
if(n==4) ////如果在半個(gè)字節(jié)里面每一位數(shù)據(jù)都是1,則將縱坐標(biāo)-3,即向上移動(dòng)一個(gè)小方格
Sign[0].y-=3;
}//將積木圖標(biāo)出現(xiàn)置頂
for(m=0;m<4;m++)//行循環(huán)
for(n=0;n<4;n++)//列循環(huán)
{
if((Sign[0].dat&MaskTab[4*m+n])!=0)////從最低位開始順序測試
{
if((num[20-(Sign[0].y-2)/3+m]&MaskTab[11-Sign[0].x/3-n])!=0)
////2為上墻壁,(Sign[0].y-2)/3為積木的總高度
////num[20-(Sign[0].y-2)/3+m] 從下往上變化
////MaskTab[11-Sign[0].x/3-n]為從4*4積木的最右邊往積木的最左邊變化
SysFlag|=1<<DEADFLAG; ////SysFlag=2;
}
}
if((SysFlag&(1<<DEADFLAG))!=0)
break;//如果產(chǎn)生新的積木圖標(biāo)中的方塊與已固定好的方塊重合,則死亡。游戲結(jié)束
DrawSign(Sign[0],1);
}
if((CheckIfLeft())&&(K3==0)) //左
{
DrawSign(Sign[0],0);
Sign[0].x-=3;
DrawSign(Sign[0],1);
}
if((CheckIfRight())&&(K4==0)) //右
{
DrawSign(Sign[0],0);
Sign[0].x+=3;
DrawSign(Sign[0],1);
}
if((CheckIfDown())&&(K2==0))//下
{
DrawSign(Sign[0],0);
Sign[0].y+=3;
DrawSign(Sign[0],1);
}
if((CheckIfRoll())&&(K1==0)) //翻轉(zhuǎn)
{
DrawSign(Sign[0],0);
if(++Sign[0].change>3)
Sign[0].change=0;
m=Sign[0].type*4+Sign[0].change;
////將Block數(shù)組中的元素分為7類,type為該類型;該類型分為4種,change為種類
////所以,change加1之后就表示翻轉(zhuǎn)之后的積木形態(tài)
Temp=(unsigned int)Block[m][0]<<8;
Temp=Temp|Block[m][1]; ////讀取翻轉(zhuǎn)之后積木形態(tài)的dat數(shù)據(jù)
Sign[0].dat=Temp; ////賦dat值給積木結(jié)構(gòu)體的dat
DrawSign(Sign[0],1); ////畫出翻轉(zhuǎn)后的積木
}
////2
if((SysFlag&(1<<PAUSEFLAG))!=0)
continue;
Delay(500);
if(++DelayCnt>=2*(11-Level))
{
DelayCnt=0;
if(CheckIfDown())//判斷是否能繼續(xù)下降一格
{
DrawSign(Sign[0],0);
Sign[0].y+=3;
DrawSign(Sign[0],1);
}
else
{
FixSign(); ////將積木移入num數(shù)組中
DelFull(); ////消行處理
PrintScore();
if(Score>=PASSSCORE)
{
SysFlag&=~(1<<DEADFLAG); ////SysFlag=1或=0
break;//跳出玩游戲過程
}
SysFlag|=1<<NEWSIGNFLAG;//新的積木圖標(biāo)產(chǎn)生標(biāo)志置1
}
}
}
}
void Main()
{
InitCpu();//初始化CPU
Lcd_Reset(); //初始化LCD屏
Lcd_Clear(0);//清屏
Lcd_Reset(); //初始化LCD屏
Lcd_Clear(0);//清屏
DrawBoard();//畫界面
GamePlay();//玩游戲
GameOver();//游戲結(jié)束
Lcd_Reset(); //初始化LCD屏
Lcd_Clear(0);//清屏
}
復(fù)制代碼
歡迎光臨 (http://www.torrancerestoration.com/bbs/)
Powered by Discuz! X3.1