结对作业 周杨威、吴原润

一、代码仓库项目地址

https://github.com/zhouyangwei123/PairingProject

 

二、设计历程

        拿到任务后,我们首先准备做一款类似于“愤怒的小鸟”的小游戏。但是经过一段时间的尝试,功能实现情况不尽如人意。 这时和一位从事IT工作的朋友天点醒了我“有个词专门形容你们正做的事——Reinventing The Wheel,你们居然不用游戏引擎写游戏?”

        我们一直困扰的问题终于迎刃而解——我们为什么觉得每个最简单的操作都举步维艰?为什么觉得自己的作品毫无竞争力(即便说出优点也生硬没有说服力)?因为考虑我们的知识储备,目标本身就不应该是完成一件画面或者操作手感多么优秀的作品,最重要是应该熟悉软件设计中的每个环节,熟练运用版本控制、代码测试等各种工具,之后再考虑努力在游戏性上做文章在,退一步说,通过“再造车轮”锻炼编程水平也算是这门课重要的收获。这样我们终于放下包袱,全心投入其中。

这时已经是提交作业前两天的晚上了,我们决定转变思路,做一款横版过关休闲小游戏。结合游戏性考虑,我们最终决定添随机障碍元素,进一步实现随即迷宫。可以用鼠标或者键盘控制人物移动,在不能碰壁的迷宫中寻找出路,人物移动是有惯性作用的,为游戏增加了难度。

三、需求分析

       我们发觉,研究生阶段校园生活比较繁忙,并没有太多时间玩大型游戏,课业之余也常常没有精力玩烧脑的游戏。于是我们决定设计一款可以在碎片化的闲暇时间玩、不需要太多思考的横版小游戏。

对产品的功能性需求:

1、作为动作过关游戏,有良好的操作感,但没有复杂的键位、技能和需要背板的攻略。

2、地图、道具(待添加)等全随机,不容易厌烦。

3、游戏体积小、运行速度快、玩完就扔到一边。

4、有背景音乐(歌曲库)可以转换学习期间紧绷的心情。

 

四、设计实现过程

本程序建立三个额外CPP文件及对应H文件。 MAIN.cpp存放主函数。Model为输入控制、速度及坐标计算等。Painting为面板绘制、图片资源调用等函数,Others存放各种不好分类的函数。设计草稿及流程图如下:

 

 

 

 

 

功能的一步步实现:

1、首先实现了鼠标和键盘两种不同控制的方法。

  

 

2、加入了背景贴图

 

图五

3、实现了随机生成障碍和计算碰撞体积,碰壁则游戏结束。

  

 

图六

 

 

 

五、代码说明

选其中几个主要函数说明如下:

rand_p_block(); 

初始化随机障碍物坐标函数,调用rand()函数,赋值到坐标数组。

panel_init();    

面板初始化,包括参数赋初值;调用easyx库创建画布、加载资源图片、调用背景绘制函数。

choose_input(); 

绘制模式选择界面和游戏说明。接受键盘输入,返回一个int型值。

contral_input(mod_flag);

调用非阻断函数接受键盘和鼠标输入,更新vx、vy值。

change_pos();

根据vx、vy乘以时间得出实时坐标并刷新显示。

 draw_block_goal();

根据坐标值放置随机障碍物和终点,并检测碰撞。

int main()
{


    //********** 初始化图形窗口**********
    panel_init();                 //面板初始化
    mod_flag = choose_input();    //控制模式选择

    //***********************************
    while(esc == 0) 
    {
        contral_input(mod_flag);  //读取鼠标或键盘输入,改变速度
        limit_speed();            //检查限速
        change_pos();             //位置改变坐标
        draw_pos();               //位置显示刷新
        Sleep(20);
        erase_pos();              //擦除旧图片
    
    }
    // 关闭图形窗口,退出
    closegraph();
    return 0;
}

 

//*********************初始化背景板**********************
void panel_init(void)
{
    int i,j;
    mod_flag = 0;
    esc = 0;
    px = 30;
    py = 30;

    initgraph(graph_X, graph_Y);     //初始化画布
    setcolor(WHITE);
    line(160, 240, 480, 240);
    loadimage(&img[0], _T("jpg"), MAKEINTRESOURCE(IDR_JPG1));   //飞行物 img 0
    loadimage(&img[1], _T("jpg"), MAKEINTRESOURCE(IDR_JPG2));   //背景图 img 1
    loadimage(&img[2], _T("jpg"), MAKEINTRESOURCE(IDR_JPG3));   //障碍物 img 2
    loadimage(&img[3], _T("jpg"), MAKEINTRESOURCE(IDR_JPG4));   //按钮   img 3
    loadimage(&img[4], _T("jpg"), MAKEINTRESOURCE(IDR_JPG5));   //终点   img 4
    draw_background();
}
//*******************************************************
//***************显示飞行物位置,判断触边界,碰到则停止*****************
void draw_pos(void)
{
    draw_background();
    if ((py < 5) || (py > 475)) //碰到上下边界
    {
        vy = 0;
        gameover();
    }
    if ((px < 5) || (px > 635)) //碰到左右边界
    {
        vx = 0;
        gameover();
    }

    setcolor(GREEN);
    putimage(px - 15, py - 15, &img[0]);

}
//********************************************************************

//************************* 擦除飞行物 *******************************
void erase_pos(void)
{
    setcolor(BLACK); 
    rectangle(px, py, px + 10, py + 10);
}
//********************************************************************

//****************** 生成随机障碍物坐标存入数组  *********************
void rand_p_block(void)
{
    int i,j;
    srand((unsigned)time(NULL));
        for (i = 0; i < block_num; i++)
        {
            p_block[0][i] = { rand() % 11 + 2 };
            p_block[1][i] = { rand() % 8 + 2 };
        } 

        for (j = 0; j < goal_num; j++)
        {
            p_goal[0][j] = {13};
            p_goal[1][j] = { rand() % 8 + 2 };
        }
    
}
//*********************** 绘制地形方块 ************************
void draw_block_goal(void)
{
    int k;
    for (k = 0; k < block_num; k++)
    {
        put_a_block (p_block[0][k] * 50, p_block[1][k] * 50);
    }
    for (k = 0; k < goal_num; k++)
    {
        put_a_goal(600, p_goal[1][k] * 50);
    }
}


//************************* 输入模式选择 *************************
int choose_input(void)
{
    int ch1 = 0;
    int ch2 = 0;
    char s1[] = "1 : press M -- mouse contral (click button)  ";
    char s2[] = "2 : press K -- keyboard contral (W A S D)";
    char s3[] = "         tip : press SPACE -- STOP ";
    outtextxy(190, 170, s1);
    outtextxy(190, 210, s2);
    outtextxy(190, 300, s3);
    while (1)
    {
        ch1 = _getch();
        if (ch1 == 77 || ch1 == 109)    //if input M or m
        {
            cleardevice();//清屏
            return 1;
        }
        else if (ch1 == 75 || ch1 == 107) //if input K or k
        {
            cleardevice();//清屏
            return 2;
        }
        else if (ch1 == 27)
        {
            esc = 1;
            return 3;
        }
    }
}
//***************************************************************

//**************** 控制模块,读取鼠标或者键盘输入 ***************
//                    鼠标左键点击按钮区域
//                     键盘使用 W A S D 
void contral_input(int flag)
{
    //*********************鼠标模式************************
    if (flag == 1)
    {
        //******* 支持esc退出,空格急停  *******
        if (_kbhit())                     
        {
            if (_getch() == 27)  esc = 1;
            else if (_getch() == 32)
            {
                vx = 0;
                vy = 0;
            }
            else ;
        }
        //**************************************

        if (MouseHit())//左键按下触发
        {
            MOUSEMSG m = GetMouseMsg();
        
            switch (m.uMsg)
            {
            case WM_LBUTTONDOWN:

                if ((m.x > 100) && (m.x < 150) && (m.y < 330) && (m.y > 280))    //
                {
                    vy -= speed_val;
                }
                if ((m.x > 100) && (m.x < 150) && (m.y > 440) && (m.y < 470))    //
                {
                    vy += speed_val;
                }
                if ((m.x <= 220) && (m.x >= 170) && (m.y <= 400) && (m.y >= 350))//
                {
                    vx += speed_val;
                }
                if ((m.x <= 80) && (m.x >= 30) && (m.y <= 420) && (m.y >= 350))  //
                {
                    vx -= speed_val;
                }
                break;
            }
            FlushMouseMsgBuffer();
        }
    }
    //************************************************************* 

    //**************************** 键盘 ***************************
    else if (flag == 2)
    {

        if (_kbhit())                       //检测键盘有输入才进入判断,不影响运动流畅性
        {
            switch (_getch())
            {
            case 27 : esc = 1 ;                     //esc退出
            case 119: vy -= speed_val; break;       //
            case 115: vy += speed_val; break;       //
            case 97 : vx -= speed_val; break;       //
            case 100: vx += speed_val; break;       //
            case 32 : 
                vx = 0;
                vy = 0;                             //急停
            }
        }
    }
}
   //*****************************************************************





 

//***************  倒计时器  ********************
void countdown_timer(void)
{
    char s[10] = { 0 };
    sprintf_s(s, _T("%d"), limit_time);
    settextcolor(RED);
    outtextxy(20, 400, s);
    refresh_counter++;
    if ((refresh_counter * ref_time) >= 1000) //刷新次数 * 单次刷新时间 >= 1s 
    {
        refresh_counter = 0;
        if (--limit_time == 0)
            win_flag = 0;
        else
            ;
    }
    else
        ;
}

 

 六、单元测试 

        1、建立单元测试工程文件,添加了资源附加依赖项, 编写单元测试程序

       

 

 

 

         但由于本程序主要函数大量使用键鼠输入,并使用函数 _kbhit() 进行按键检测,模拟键盘输入的keybd_event()输入检测不到的问题没有解决,且easyx中的图像显示函数在单元测试中经常报错,单元检测未达到效果。

 

 七、性能分析

      本程序现阶段未加入复杂算法,电脑资源占用情况极低。

   

 

GPU使用情况 图八

CPU使用情况 图九

 

 

 

七、        PSP表格(估计值与真实值的区别)

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 

 

· Estimate

· 估计这个任务需要多少时间

 1290

 2370

Development

开发

 

 

· Analysis

· 需求分析 (包括学习新技术)

 60

 60

· Design Spec

· 生成设计文档

 30

 60

· Design Review

· 设计复审

 30

 20

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 30

 20

· Design

· 具体设计

 60

60 

· Coding

· 具体编码

 800

 1500

· Code Review

· 代码复审

 120

 200

· Test

· 测试(自我测试,修改代码,提交修改)

 200

 240

Reporting

报告

 

 

· Test Repor

· 测试报告

 60

 60

· Size Measurement

· 计算工作量

 30

 30

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 60

 60

 

合计

1680

 2310 


 

八、        软件工程学习日志

 

时间:2019-10-25晚

地点:寝室

人员:周杨威、吴原润

 

内容:

通过讨论,我们初步给出了我们的项目,就是做一款具有物理引擎的游戏。通过了解,我觉的周杨威的编程能力比我强,所以我们共同决定由周杨威主要负责代码的编写,我主要负责博客的编写。期间我们共同讨论内容和方案以及查找各种资料。

收获:

在一个团队项目里,合理的分工是非常重要的。并且还要勇于说出自己的想法,好的想法是在不同的idea之间碰撞产生的。

自我评价:

第一天我们对于自己的项目都很有激情,我相信我们会干好的。

 

 

时间:2019-10-29晚

地点:Z806

人员:周杨威、吴原润

 

内容:

我在网上查找资料,在CSDN上翻看各种有关博客。但收获甚微,对于其他的语言,我们没有时间临时学习。可能是我们查资料的能力有待提高,我们很难准确在这大量资料里找到我们想要的内容。所以在查到资料的基础上,我们只实现原定的部分功能。其余计划搁置。

收获:

如何利用网络去高效查找自己需要的资料是一件非常重要事情;要学会去利用网上的丰富资源;提出目标时,要考虑到自己的能力问题。

自我评价:

遇到困难有点泄气,效率有点不高。

 

 

时间:2019-10-31晚

地点:Z806

人员:周杨威、吴原润

 

内容:

考虑到原定目标无法实现,我们决定更改计划目标。为此我们展开热烈的讨论。我们尝试在现有实现的功能上找到新的方向。我们决定做一种避碰类的小游戏,控制人物在障碍物之间穿越。我们考虑设计不动障碍和运动障碍两种障碍类型。为此我们开始在网上查找,如何可以自动生成障碍物的代码。但考虑到时间紧任务重,我们决定首先通过手绘不动障碍实现游戏的功能实现。

收获:

有时遇到困难,可以换个思路绕过难关也未尝不可。

自我评价:

可能因为这是首次独立设计一款小游戏,总是不断的遇到困难,所以效率老是不高,总不能完成既定目标,有待加强。

 

 

 

时间:2019-11-2下午

地点:西区阅览室

人员:周杨威、吴原润

 

内容:

为了增加游戏的可玩性,我们想为什么不能动的障碍物变成迷宫的墙呢?走迷宫可比单纯的避碰有意思多了,首先我们把不动障碍物按照迷宫图纸摆成一个迷宫,通过控制任务在迷宫中行走。和避碰游戏最大的不同是,游戏人物在游戏中,触碰到迷宫壁不会使游戏停止。

收获:

灵感不是一下子迸发出来的,是在不断地实践中产生的。

自我评价:

革命尚未成功,同志仍需努力。

 

时间:2019-11-3

地点:西区阅览室

人员:周杨威、吴原润

 

内容:

今天我们的主要内容是找出一个可以自动生成迷宫的办法,我们在网上找到多种不同的迷宫生成代码。有些代码生成的迷宫根本没有出口,有些代码生成的迷宫难度无法控制,有些干脆无法运行。为此我对不同的代码进行反复的比较、修改、测试。

收获:

一串好的代码是在不断地比较、修改、测试中诞生的。

自我评价:

进过几次的磨合,我们的效率明显提高了。

 

 

 

posted @ 2019-11-05 14:22  WUYUANRUN  阅读(237)  评论(1编辑  收藏  举报