夏季小学期STC-B,基于485通信实现双模式(单机联机)拼图游戏


摘要

本课程设计旨在数码管上编写一个有完整环节的可联机游戏,在课程设计中采用了设置中断,编写回调函数的方法,将学习板的运行程序多线程化并使用485通信实现双机联机游戏的功能。并在本次课程设计中成功完成了一款有完整环节的可联机游戏。

关键字:数码管,联机,游戏,485,回调函数,中断,多线程

绪论

课程设计的主题是实现一个在数码管显示通过按键操作的联机游戏;由于STC-B学习板的使用的是数码管显示同时按键个数较少,可以实现的游戏种类单一且大部分为单机游戏,本次课程设计旨在设计一款在STC-B上运行的具有完整环节和一定趣味性的可联机游戏,以填补在数码管上显示的游戏部分空白,同时锻炼学习相关的中断和回调函数之间的灵活运用。

正文

功能介绍:

在学习板上按键key3为切换单机和联机模式,初始为单机模式,在单机情况下游戏初始界面如下图:
初始界面
在这里插入图片描述

此界面为游戏准备阶段右边的7个数码管为上次游戏所得分数这里初始为0,最左边的数码管显示的是“拼图碎片”,在游戏开始时如上图右图所示通过导航按键的上下可以操作最左边的“拼图碎片”同时左边会随机出现一个缺一角的“图片”当左边的拼图碎片与右边的图形合在一起恰好形成一个带小数电的数字8时(即合起来恰好数码管全点亮)则得一分,并继续游戏,在游戏中按下key1会暂停游戏,这时数码管上回固定你的拼图碎片同时在右侧显示出目前的得分,再次按下key1后可以继续游戏(此时会触发游戏暂停惩罚图形会往前移一格)。若失败则学习板发出提醒声音游戏结束,在右侧显示本次游戏的得分(如下图)。(与暂停时情况类似但此时key1无法开始游戏),在游戏任何时候按下key2可以重新开始游戏(但分数会归0)。
在这里插入图片描述

在游戏进行时随着游戏得分的增加,图形移动速度也会增加,得分每多5分游戏速度会升一级最高5级。
游戏任何时候按下key3切换模式,切换到联机模式时,在联机模式中双方初始状态如下
在这里插入图片描述

左侧数码管显示为双方目前的得分,右边的两个数码管靠左的数码管显示的是设置的难度等级,最右边的数码管显示的是对方下一次游戏时会出现的图形。此时通过key1,key2可以对难度和图形进行设置,设置完成后往下按下导航按键,此时对方进入游戏,出现的难度和图形与自己设置一致,设置难度越高对方成功拼图后得分也越高,对方若失败则学习板发出提醒声音对方游戏结束
对方此时不能再进入游戏得分但可以继续设置难度和图形发送,当双方玩家均失败后得分高的玩家会播放胜利音乐,若分数相同则双方都播放音乐。播放音乐过程中或播放结束都可以重新开始游戏,而一旦任何一方重新开始游戏双方分数都会清零然后重新开始游戏。

设计思路

1,碎片和图形的设计:
在单机模式中为了在数码管上显示出自己想要的图形,在main.H中修改了decode_table数组将自己设计的图形数据添加进去。

2,游戏速度的实现:
为了做出游戏速度加快的效果在回调函数进行计数,根据计数的情况来运行程序,然后修改计数的量就可以达到每次修改运行程序的时间不同以达到修改游戏时间的目的

3,单机联机的模式切换:
采用定义全局变量为标志位,通过标志位的不同从而运行不同的程序,这样在按键回调函数改变标志位的值就能够实现模式的转换

4,联机对方和己方的识别:
在485通信中除了2个字节的信息还有对方板子的信息包括对方的得分以及对方游戏的状态以及发送时的状态

5,数码管的实时更新:
在1ms的中断回调函数中更新数码管的信息这样程序在任意位置改变数码管的信息时都能及时的显示在数码管上

总体设计方案

1,数码管的实时更新函数:

void updatadisp() //显示更新函数
{
    
    
    Seg7Print(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]);
}

2,游戏的回调函数
在标志位format取不同值时,进行不同的模式在单机和联机模式中的游戏进行时都会进行计数操作来达到改变游戏速度的功能

void Play_callback() //游戏回调函数
{
    
    
    if (format == 0)
    {
    
    
        if (flag == 0)
        {
    
    
            time++;
            if (time == delay)
            {
    
    
                PlayGame();
                time = 0;
                if (score1 < 5)
                    delay = 100;
                else if (score1 < 10)
                    delay = 80;
                else if (score1 < 15)
                    delay = 50;
                else if (score1 < 20)
                    delay = 40;
                else if (score1 < 25)
                    delay = 35;
                else
                    delay = 30;
            }
        }
        else
            PlayGame();
    }
    if (format == 1)
    {
    
    

        if (flag2 == 0)
            PlayGameTogater();
        else
        {
    
    
            time++;
            if (time == delay)
            {
    
    
                PlayGameTogater();
                time = 0;
            }
        }
    }
}

单机游戏的运行函数:

void PlayGameTogater()
{
    
    
    int i, mid;
    if (flag2 == 0) //双方发题阶段
    {
    
    
        mid = score1;
        for (i = 0; i < 4; i++)
        {
    
    
            d[3 - i] = (char)(mid % 10);
            mid = mid / 10;
        }
        d[4] = 10;
        d[5] = 10;
        d[6] = val6;
        d[7] = val7;
        if (OK == 0 && OK2 == 0)
        {
    
    
            if (score1 >= score2)
            {
    
    
                SetMusic(enumModeInvalid, enumModeInvalid, &song, sizeof(song), enumMscDrvLed);
                SetPlayerMode(enumModePlay);
            }
            OK = 1;
            First=1;
        }
    }
    else //游戏阶段跑动中需要添加延时
    {
    
    
        SetPlayerMode(enumModeStop);
        LedPrint(0);
        for (i = 1; i < 8; i++)
            d[i] = 10;
        d[loc] = disp;
        if (loc == 1)
        {
    
    
            loc = 7;
            if (d[0] + disp == 65)
            {
    
    
                OK = 1;
                if (diff <= 5)
                    score1 += diff;
                else if (diff <= 8)
                    score1 += diff * 2;
                else
                    score1 += diff * 3;
            }
            else
            {
    
    
                OK = 0;
                SetBeep(2000, 50);
                datao[0] = 0xaa; //校验码0xaa,0x55
                datao[1] = 0x55;
                datao[2] = d[6];
                datao[3] = d[7];
                datao[4] = score1;
                datao[5] = OK;
                datao[6] = 1; //异常发送
                Uart2Print(&datao, sizeof(datao));
            }
            flag2 = 0;
        }
        else
            loc--;
    }
}

联机游戏的运行函数:

void PlayGame() //玩游戏
{
    
    
    int mid = score1;
    int i;
    if (flag == 1) //游戏结束阶段
    {
    
    
        for (i = 0; i < 7; i++)
        {
    
    
            d[7 - i] = (char)(mid % 10);
            mid = mid / 10;
        }
    }
    if (flag == 0)
    {
    
    
        for (i = 1; i < 8; i++)
            d[i] = 10;
        d[loc] = disp;
        if (loc == 1)
        {
    
    
            if (d[0] + disp == 65)
            {
    
    
                score1++;
                loc = 7;
                disp = rand() % 7 + 26;
            }
            else
            {
    
    
                SetBeep(2000, 50);
                flag = 1;
                fin = 1;
            }
        }
        else
        {
    
    
            loc--;
        }
    }
}

在此基础上设置相应的按键回调函数来改变相应的标志位,这样就可以做到通过按键来进行游戏
按键回调函数:

void mykey_callback() //按键回调
{
    
    
    if (GetKeyAct(enumKey1) == enumKeyPress)
    {
    
    
        SetBeep(1000, 10);
        if (format == 0 && fin == 0)
        {
    
    
            flag = !flag;
        }
        if (format == 1 && flag2 == 0) //联机且在准备阶段
        {
    
    
            if (val7 == 32)
                val7 = 26;
            else
                val7++;
        }
    }
    if (GetKeyAct(enumKey2) == enumKeyPress)
    {
    
    
        SetBeep(1000, 10);
        if (format == 0)
        {
    
    
            fin = 0;
            loc = 7;
            score1 = 0;
            disp = rand() % 7 + 26;
            delay = 100;
            flag = 0;
            time = 0;
        }
        if (format == 1 && flag2 == 0)
        {
    
    
            if (val6 == 9)
                val6 = 1;
            else
                val6++;
        }
    }
}

导航按键回调函数:

void myKN_callback() //导航按键回调
{
    
    
    if (GetAdcNavAct(enumAdcNavKey3) == enumKeyPress)
    {
    
    
        SetBeep(1000, 10);
        format = !format;
        fin = 0;
        loc = 7;
        score1 = 0;
        delay = 100;
        flag = 1;
        time = 0;
        if (format == 0)
            d[0] = 33;
    }
    if (GetAdcNavAct(enumAdcNavKeyUp) == enumKeyPress && ((!format && flag == 0) || (format && flag2 == 1)))
    {
    
    
        SetBeep(1000, 10);
        if (d[0] == 39)
            d[0] = 33;
        else
            d[0] = d[0] + 1;
    }
    if (GetAdcNavAct(enumAdcNavKeyDown) == enumKeyPress && ((!format && flag == 0) || (format && flag2 == 1)))
    {
    
    
        SetBeep(1000, 10);
        if (d[0] == 33)
            d[0] = 39;
        else
            d[0] = d[0] - 1;
    }
    if (GetAdcNavAct(enumAdcNavKeyCenter) == enumKeyPress && format == 1 && flag2 == 0) //联机模式且处于发题阶段
    {
    
    
        SetBeep(1000, 10);
        if(First)//第一次发生时
        {
    
    
            score1=0;
            score2=0;
            First=0;
        }
        time = 0;
        datao[0] = 0xaa; //校验码0xaa,0x55
        datao[1] = 0x55;
        datao[2] = d[6];
        datao[3] = d[7];
        datao[4] = score1;
        datao[5] = OK;
        datao[6] = 0; //正常发送
        Uart2Print(&datao, sizeof(datao));
    }
}

在以上的基础下在主函数中运行相关的驱动程序即可完成游戏的编写

void main()
{
    
    
    AdcInit(ADCexpEXT);
    MySTC_Init();
    DisplayerInit();
    Key_Init();
    BeepInit();
    MusicPlayerInit();

    flag = 1;   //游戏结束
    format = 0; //单机
    datao[5] = 1;
    SetDisplayerArea(0, 7);
    Seg7Print(34, 0, 0, 0, 0, 0, 0, 0);
    PlayTone(enumModeInvalid, enumModeInvalid, &song, sizeof(song));
    SetMusic(enumModeInvalid, enumModeInvalid, &song, sizeof(song), enumMscDrvLed);
    Uart2Init(2400, Uart2Usedfor485);
    SetUart2Rxd(&datao, sizeof(datao), datahead, sizeof(datahead));
    SetEventCallBack(enumEventKey, mykey_callback);
    SetEventCallBack(enumEventNav, myKN_callback);
    SetEventCallBack(enumEventSys1mS, updatadisp);
    SetEventCallBack(enumEventSys10mS, Play_callback);
    SetEventCallBack(enumEventUart2Rxd, myUart2Rxd_callback);
    while (1)
    {
    
    
        MySTC_OS();
    }

设计总结与心得

在两周的实训中终于完成了这个游戏的设计,这个设计最难的部分其实是在于如何在学习板上利用显示屏和几个按键来玩一个比较有趣味点的游戏,STC-B本身其实可以写入很多游戏但很多游戏在学习板的数码管上显示时总是显得格格不入,就像强行在一台10年前的还没有键盘的电脑上玩英雄联盟,本身游戏不错但显示出来的效果却总是不如人意,所以这次的课程设计我主要在于设计一款能很好贴合这个学习板硬件的游戏,在玩游戏的时候能够直观,简洁的操作游戏。
而在程序编写的过程中,也是经常出现BUG有些是对keil自身的属性不了解比如keil的全局变脸是不区分大小写的,keil的int类型变量只有2个字节,还有的问题就是因为标志位使用的很多,在很多的回调函数中也受到了修改,容易产生很多冲突,从而出现一些不让人理解的错误。在多次的改BUG中我也找到了一些技巧,在出现问题的环节代码处设置蜂鸣器响声这样我们可以通过蜂鸣器是否发声来判断程序是否运行到这里,从而缩小BUG的范围。

项目工程文件

链接:https://pan.baidu.com/s/10ZtYA-oaMoyUvafgfY6Z1w
提取码:ab12

おすすめ

転載: blog.csdn.net/qq_49327751/article/details/120324316