这是在师傅那里拿到点阵板后为了练习16*16点阵板写的一个弹球小游戏,算法是自己写的一、使用的工具
1.点阵板:七星虫德飞莱的LY-LED16*16BV2.1
2.单片机:STC89C52 LY51-L2c的单片机,晶振是12.000
3.编程软件:Keil
4.调试工具:stc-isp-15xx-v6.86D
5.STC的89c52 USB转串口线驱动
二,主要功能有下1.显示开始界面
2.消除,并数码管显示分数
3.继续消除
4.结束游戏
5.游戏中可以加速和减速小球
6.消除完全部小球后会显示笑脸
三、规则
开始、结束、胜利中使用s4作为确认键
开始游戏后按键功能如下
1.s1 左移
2.s2 右移
3.s3 减速
4.s4 加速
四、缺点
1.要消除完一整行之后才能消除下一行
2.挡板的和球的反弹有点问题
3.别的还有问题希望大家多多指教
五、单片机源程序如下:
- /*-----------------------------------------------
- 名称:弹弹球游戏
- 编写:田*
- 日期:2018年
- 内容:屏+5V连接电源5V
- GND接GND
- P3.0-P3.3分别连接A、B、C、D,用于控制16行
- SCK时钟连接P1.5
- LATCH锁存连接P1.6
- R1红色数据信号连接至P1.4
- G1绿色数据信号连接至P3.4,如果单红色屏此信号无需连接
- OE使能端接P1.1
- ------------------------------------------------*/
- #include<reg52.h>
- #include "scanKey.h"
- #include "scanNum.h"
- sbit T_STR = P1^6; // 锁存引脚,屏上标识位STR
- sbit R1 = P1^4; // 数据引脚,屏上标识R1
- sbit G1 = P3^4; // 数据引脚,屏上标识G1 ·没有该灯光,可不用
- sbit T_CLK = P1^5; // 时钟引脚,屏上标识位CLK
- sbit OE = P1^1; // 使能引脚,屏上标识EN/OE
- unsigned char DIS[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; //行数据
- unsigned char ledShow[][32]={0x00}; // 显示挡板
- code dotDis[8] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; // 扭转顺序
- void input2Byte(unsigned char dataR1); // 写入数据
- void init_Timer1(void); // 初始化定时器
- void position(int pos); // 移动挡板位置
- void gameOver(); // 游戏结束
- void gameWin(); // 游戏胜利
- bit isGameWin(); // 判断游戏是否胜利
- void gameMenu(); // 游戏菜单
- void playGame(); // 开始游戏
- /*
- 以下是挡板移动位置的十六进制
- 1111 1000 0000 0000 ledShow[28] = 0xF8 ledShow[29] = 0x00
- 0111 1100 0000 0000 ledShow[28] = 0x7C ledShow[29] = 0x00
- 0011 1110 0000 0000 ledShow[28] = 0x3E ledShow[29] = 0x00
- 0001 1111 0000 0000 ledShow[28] = 0x1F ledShow[29] = 0x00
- 0000 1111 1000 0000 ledShow[28] = 0x0F ledShow[29] = 0x80
- 0000 0111 1100 0000 ledShow[28] = 0x07 ledShow[29] = 0xC0
- 0000 0011 1110 0000 ledShow[28] = 0x03 ledShow[29] = 0xE0
- 0000 0001 1111 0000 ledShow[28] = 0x01 ledShow[29] = 0xF0
- 0000 0000 1111 1000 ledShow[28] = 0x00 ledShow[29] = 0xF8
- 0000 0000 0111 1100 ledShow[28] = 0x00 ledShow[29] = 0x7C
- 0000 0000 0011 1110 ledShow[28] = 0x00 ledShow[29] = 0x3E
- 0000 0000 0001 1111 ledShow[28] = 0x00 ledShow[29] = 0x1F
- */
- bit move = 0; // 是否移动
- bit isUp = 1; // 小球往上
- bit isDown = 0; // 小球往下
- char rolIndex = 26; // 初始行位置
- char colIndex = 3; // 初始列位置
- char pos = 5; // 挡板位置
- char sysFun = 0; // 系统功能
- bit gameState = 0; // 游戏状态
- char speed = 50; // 游戏速度
- unsigned short gameScore = 0; // 游戏分数
- bit f_num = 0; // 数码管显示
- // 主程序
- int main()
- {
- static unsigned char count = 0; // 数码管显示计时
- init_Timer1(); // 初始化定时器
- fillDisBuf(gameScore,1); // 填充数码管
- while(1)
- {
- scanKey(); // 实时扫描按键
- if (sysFun == 1 && f_num) {
-
- count++;
- if (count == 20) { // 100ms比较好
- display();
- count = 0;
- }
- f_num = 0;
- }
-
- /* 多任务控制核心 */
- switch(sysFun) {
- case 0: // 显示游戏菜单
- gameMenu();
- break;
- case 1: // 开始游戏
- playGame();
- break;
- case 2: // 游戏胜利
- gameWin();
- break;
- case 3: // 游戏结束
- gameOver();
- break;
- }
- }
- }
- /* 显示游戏菜单 */
- void gameMenu() {
- char i;
- if (!gameState) {
-
- // 全部先初始化为不亮
- for (i = 0; i < 32; i++){
- ledShow[0][i] = 0x00;
- }
-
- // 初始化游戏LED灯,显示初始界面
- ledShow[0][0] = 0xEA; ledShow[0][1] = 0x05;
- ledShow[0][2] = 0x2E; ledShow[0][3] = 0xEF;
- ledShow[0][4] = 0xEA; ledShow[0][5] = 0x44;
- ledShow[0][6] = 0x8E; ledShow[0][7] = 0x4E;
- ledShow[0][8] = 0xEA; ledShow[0][9] = 0xF5;
- ledShow[0][10] = 0x2E; ledShow[0][11] = 0x44;
- ledShow[0][12] = 0x24; ledShow[0][13] = 0x4C;
- ledShow[0][14] = 0xEE; ledShow[0][15] = 0xE4;
- ledShow[0][16] = 0x04;
- gameState = 1; // 开启游戏状态为
- /* 挡板归位 */
- pos = 5; // 挡板初始位置
- rolIndex = 26; // 小球索引
- ledShow[0][rolIndex] = 0x01; // 球初始位置
- position(pos); // 显示挡板
- gameScore = 0; // 游戏归0
- }
- /* s4作为游戏开始 */
- if (s4d) {
- s4d = 0;
- sysFun = 1;
- gameState = 1;
- }
- }
- /* 玩游戏 */
- void playGame() {
- char i;
- if (gameState) { // 是否经过了初始化
- /* 初始化游戏区域 */
- gameState = 0;
- for (i = 0; i < 32; i++){
- if (i <= 15) {
- ledShow[0][i] = 0xFF;
- }
- else{
- ledShow[0][i] = 0x00;
- }
- }
- /* 挡板归位 */
- pos = 5; // 挡板位置
- rolIndex = 26; // 小球索引
- ledShow[0][rolIndex] = 0x01; // 球初始位置
- position(pos); // 显示挡板
- gameScore = 0; // 游戏归0
- }
- /* 根据按键移动挡板 */
- if (s1d){
- s1d = 0;
- pos--;
- if (pos < 0) { // 判断是否在边界,是则不能移动
- pos = 0;
- }
- else{
- position(pos);
- }
- /* 判断是否在另外一个区域的边界 */
- if (ledShow[0][rolIndex] == 0x80 && rolIndex == 27) {
- ledShow[0][rolIndex] = 0x00; // 关闭当前行
- rolIndex--; // 跳转下一行
- ledShow[0][rolIndex] = 0x01; // 新位置
- }
- else if (rolIndex >= 25 && rolIndex <= rolIndex + 2) {
- /* 判断是否在可移动边界 */
- if (ledShow[0][rolIndex] != 0x80) { // 是否还能移动
- ledShow[0][rolIndex] <<= 1; // 小球左移
- }
- }
- }
- if (s2d){
- s2d = 0;
- pos++;
- if (pos >= 12) { // 12是到右边界移动的距离
- pos = 11;
- }
- else{
- position(pos); // 往右移动挡板
- }
- /* 判断是否在另外一个区域的边界 */
- if (ledShow[0][rolIndex] == 0x01 && rolIndex == 26) {
- ledShow[0][rolIndex] = 0x00;
- rolIndex++;
- ledShow[0][rolIndex] = 0x80;
- }
- else if (rolIndex >= 25 && rolIndex <= rolIndex + 2) {
- /* 判断是否在可移动边界 */
- if (ledShow[0][rolIndex] != 0x01) { // 判断是否已到达边界
- ledShow[0][rolIndex] >>= 1;
- }
- }
- }
- // 游戏减速
- if(s3d) {
- s3d = 0;
- // 加大延迟
- speed += 10;
- speed = (speed <= 50) ? speed : 50;
- }
-
- // 游戏加速
- if(s4d) {
- s4d = 0;
- // 减小延迟
- speed -= 10;
- speed = (speed >= 20) ? speed : 20;
- }
-
- // 点移动
- if (move) {
- move = 0;
-
- /* 当球往上移动的时候 */
- if (isUp) {
- rolIndex -= 2; // 点阵版是顺序加,-2才上球的正上方
- /* 如果是在空白区域则直接往上移动 */
- if (ledShow[0][rolIndex] == 0x00 && rolIndex > 0) {
- ledShow[0][rolIndex] = ledShow[0][rolIndex+2];
- ledShow[0][rolIndex+2] = 0x00; // 移动之后需要将前一行灯关闭
- }
- else if (rolIndex < 0) { // 判断是否到达边界
- if(isGameWin()){ // 判断是否全部打完
- sysFun = 2; // 切换系统状态
- }
- else{
- isUp = 0;
- isDown = 1;
- rolIndex += 2; // +2回到顶部
- }
- }
- else if (ledShow[0][rolIndex] != 0x00 &&
- (ledShow[0][rolIndex] & ledShow[0][rolIndex+2]) == 0x00){
- // 上一行没有消除完整但是正上一个为空,直接返回
-
- isUp = 0;
- isDown = 1;
- rolIndex += 2;
- }
- else if (ledShow[0][rolIndex] == 0xFF ||
- (ledShow[0][rolIndex] & ledShow[0][rolIndex+2]) != 0x00) {
-
- // 判断上面一行是不是整行未消除或者正上方有
- ledShow[0][rolIndex] &= (~ledShow[0][rolIndex+2]);
- isUp = 0;
- isDown = 1;
- rolIndex += 2;
- // 根据小球速度进行加分
- switch(speed) {
- case 50: gameScore += 1; break; // 四等级+1分
- case 40: gameScore += 2; break; // 三等级+2分
- case 30: gameScore += 3; break; // 二等级+3分
- case 20: gameScore += 4; break; // 一等级+4分
- }
- fillDisBuf(gameScore,1); // 分数+完填充数码管
- }
- }
- if (isDown) {
- rolIndex += 2;
- if (rolIndex > 31) { // 超出边界
- rolIndex = 30;
- sysFun = 3; // 切换系统状态到结束
-
- }
- else if (ledShow[0][rolIndex] == 0x00) { // 空白区域直接下移
- ledShow[0][rolIndex] = ledShow[0][rolIndex-2];
- ledShow[0][rolIndex-2] = 0x00;
- }
- else if ((ledShow[0][rolIndex]) != 0x00) { // 碰到板
- isUp = 1;
- isDown = 0;
- rolIndex -= 2;
- }
- }
- }
- }
- /* 判断游戏胜利 */
- bit isGameWin() {
- bit flag = 1;
- char i;
- for(i = 0; i < 16; i++) { // 上方砖块区域
- /*判断是否除了小球外游戏区域的砖块全部消除*/
- if (ledShow[0][i] != 0x00 && ledShow[0][i] != ledShow[0][rolIndex+2]) { // 判断点阵是否全灭
- flag = 0;
- break;
- }
- }
- return flag;
- }
- /* 游戏胜利 */
- void gameWin() {
- // 游戏胜利标志
- ledShow[0][0] = 0x00; ledShow[0][1] = 0x00;
- ledShow[0][2] = 0x00; ledShow[0][3] = 0x00;
- ledShow[0][4] = 0x07; ledShow[0][5] = 0xE0;
- ledShow[0][6] = 0x08; ledShow[0][7] = 0x10;
- ledShow[0][8] = 0x10; ledShow[0][9] = 0x08;
- ledShow[0][10] = 0x26; ledShow[0][11] = 0x64;
- ledShow[0][12] = 0x20; ledShow[0][13] = 0x04;
- ledShow[0][14] = 0x20; ledShow[0][15] = 0x04;
-
- ledShow[0][16] = 0x20; ledShow[0][17] = 0x04;
- ledShow[0][18] = 0x22; ledShow[0][19] = 0x44;
- ledShow[0][20] = 0x21; ledShow[0][21] = 0x84;
- ledShow[0][22] = 0x10; ledShow[0][23] = 0x08;
- ledShow[0][24] = 0x08; ledShow[0][25] = 0x10;
- ledShow[0][26] = 0x07; ledShow[0][27] = 0xE0;
- ledShow[0][28] = 0x00; ledShow[0][29] = 0x00;
- ledShow[0][30] = 0x00; ledShow[0][31] = 0x00;
- if (s4d) {
- s4d = 0;
- sysFun = 0;
- }
- }
- /* 游戏结束 */
- void gameOver() {
- // 游戏结束标志
- ledShow[0][0] = 0x00; ledShow[0][1] = 0x00;
- ledShow[0][2] = 0x00; ledShow[0][3] = 0x00;
- ledShow[0][4] = 0x07; ledShow[0][5] = 0xE0;
- ledShow[0][6] = 0x08; ledShow[0][7] = 0x10;
- ledShow[0][8] = 0x10; ledShow[0][9] = 0x08;
- ledShow[0][10] = 0x26; ledShow[0][11] = 0x64;
- ledShow[0][12] = 0x20; ledShow[0][13] = 0x04;
- ledShow[0][14] = 0x20; ledShow[0][15] = 0x04;
-
- ledShow[0][16] = 0x20; ledShow[0][17] = 0x04;
- ledShow[0][18] = 0x21; ledShow[0][19] = 0x84;
- ledShow[0][20] = 0x22; ledShow[0][21] = 0x44;
- ledShow[0][22] = 0x10; ledShow[0][23] = 0x08;
- ledShow[0][24] = 0x08; ledShow[0][25] = 0x10;
- ledShow[0][26] = 0x07; ledShow[0][27] = 0xE0;
- ledShow[0][28] = 0x00; ledShow[0][29] = 0x00;
- ledShow[0][30] = 0x00; ledShow[0][31] = 0x00;
- if (s4d) {
- s4d = 0;
- sysFun = 0;
- }
- }
- /* 初始化定时器,做为动态扫描 */
- void init_Timer1(void)
- {
- TMOD |= 0x11;
- EA=1; //总中断使能
- ET1=1; //定时中断使能
- TR1=1; //打开定时器开关
- TR0=1; // start
- TH0=0xEC;
- TL0=0x78;
- ET0=1;
- }
- // 一次写入两个字节
- void input2Byte(unsigned char dataR1) //写两个字节
- {
- unsigned char i;
- bit temp = 0;
- for(i=0; i<8; i++)
- {
- R1 = !(dataR1 & dotDis[i]); // 调整成较为方便的看的顺序
-
- T_CLK = 0;
- T_CLK = 1;
- }
- }
- // 定时器执行动态扫描整个点阵
- void timer1_isr(void) interrupt 3
- {
- static unsigned char count;
- unsigned char i;
-
- TH1=0xFF; //重装初值
- TL1=0;
- T_STR=0; // 锁存释放
- input2Byte(ledShow[0][i]);
- input2Byte(ledShow[0][i+1]);
- OE=1; // 暂时关闭屏幕,防止出现鬼影,如果没有使用该引脚控制则会出现拖影现象。
- T_STR=1; // 锁存有效,此时一行的数据显示到屏上
- P3=DIS[15-count]|0xF0; // 用P0口的前4位控制16行,屏内部通过4-16译码器工作,循环扫描其他15行
- OE=0; // 使能
- count++;
- i+=2; // 累加调用下一行数据
- if(count==16) count=0;
- if(i==32) i=0; // 又从第一个位置扫描
- }
- // 中断0 ,产生移动信号
- void init_timer0()interrupt 1 using 1{
- static unsigned char count = 0;
- TR0=0;
- TH0=0xEC;
- TL0=0x78;
- TR0=1;
- f_num = 1; // 5ms显示一次数码管
- count++;
- if (count >= speed) {
- count = 0;
- move = 1;
- }
- }
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有代码51hei提供下载:
弹球小游戏-源代码.zip
(53.77 KB, 下载次数: 60)
|