Gun Mayhem
Gun Mayhem

Gun Mayhem

Gun Mayhem—混乱大枪战游戏设计

一、项目说明

设计目的

1.学习编程基础:编写一款游戏需要熟悉C语言的基础语法和编程逻辑,因此,编写游戏是体现C语言学习成果的一种很好的方式,可以帮初学者,更好的把握各知识点的关系,增强自己的实践编程能力,使自己能够根据实际需求,在将来的项目开发中做出更好的编程策略与逻辑判断。

2.开发游戏编程能力:本游戏需要我设计和实现游戏逻辑和算法,包括实现玩家的移动、游戏画面的渲染、游戏间物体的碰撞检测、角色发射功能的实现、不同类的功能、各类逻辑关系等内容,这些能力对于我开发其他类型的游戏,以及实现其他功能都是有帮助的。

3.提高代码规范和模块化思维:通过代码模块化的方式来提高编写代码的效率,和代码可维护性。同时培养良好的代码规范、注释、命名等习惯,来提高代码的可读性和可复用性。

设计意义

     通过这种形式可以深化对本学期学习的C语言相关知识的理解,例如结构体、数组、枚举等等,同时也可以增加C语言相关知识应用时的熟练度,提高对C语言深层知识的掌握程度,培养一个良好的编程习惯,加强自己的编程能力和相关思维能力,并积累用C语言解决生活中问题的经验。

另外本次学习中用到conio.h、time.h、graphics.h等函数库,编程过程中我对时间计算,键盘响应,图像绘制在C语言中的实现有了更加深入的理解。

模块组成

项目模块:

项目使用函数:不包括类的成员函数,类成员函数在类的介绍中有详细说明

void Get img(double kx,double ky)//加载用到的图片

void delete img()//释放图片避免空指针导致内存泄露

void initgame()//初始化游戏

int startgame()//开始界面

void showgame()/更新游戏画面

void update noinput()//更新游戏数据

void update input()//玩家输入系统

int CheckCollision(void *obj1,int datetype1,void *obj2,int datetype2)//碰撞检测利用函数

void CollisionActor(actor *player)//角色与物体碰撞后的逻辑

void Collisionobject()//物体间碰撞后的逻辑

void Collisionsystem()//碰撞检测系统

4、各模功能

(1)、游戏开始选择

开始界面:

游戏教程页面:

游戏界面:

实现代码:

int startgame(double kx, double ky)

{

    int choice = 0;

    int a[3][4] = {

        {2619, 977},

        {2610, 1297},

        {2619, 1618}};

    while (1)

    {

        putimage(0, 0, getwidth(), getheight(), img_start, 0, 0, 4250, 2000);

        setfillcolor(EGERGB(241, 120, 111));

        bar(a[choice][0] * kx, a[choice][1] * ky, a[choice][0] * kx + 50, a[choice][1] * ky + 50);

        if ((GetAsyncKeyState(0x57) & 0x8000))

        {

            choice--;

            if (choice < 0)

                choice = 2;

        }

        else if ((GetAsyncKeyState(0x53) & 0x8000))

        {

            choice++;

            if (choice > 2)

                choice = 0;

        }

        else if ((GetAsyncKeyState(VK_RETURN) & 0x8000))

        {

            break;

        }

        Sleep(100);

    }

    printf("over%d\n", choice);

    return choice;

}

以及主函数中控制函数:

while (1)

    {

        // 选择选项

        int choice;

        choice = startgame(Game_kx, Game_ky);

        printf("%d", choice);

        if (choice == 0)

        {

            initgame();

            GAME();

        }

        else if (choice == 1)

        {

            cleardevice(NULL);

            putimage(0, 0, getwidth(), getheight(), img_help, 0, 0, 4250, 2000);

            Sleep(1000);

            getch();

        }

        else

      exit(0);

    }

(2)、项目中创建的类:各类创建在class.h中

两个基础建筑类:用于辅助实现传送和承载人物和物体的功能,功能将在各类中展示:

block—障碍物类: door—传送门类:

功能实现代码:

//-----------------------------障碍物类------------------------------------------//

struct block

{

    int x;

    int y;

    int width;

    int hight;

    int datetype; // 数据类型

    void init(int a[4], double kx, double ky)

    {

        datetype = BLOCK;

        x = a[0] * kx;

        y = a[1] * ky;

        width = a[2] * kx;

        hight = a[3] * ky;

        printf("%d,%d,%d,%d\n", x, y, width, hight);

    }

    void show()

    {

        setfillcolor(WHITE);

        bar(x, y, x + width, y + hight);

    }

};

//--------------------------------------传送门对类--------------------------------//

struct door

{

    int x;

    int y;

    int dir;

    int x2;

    int y2;

    int hight;

    int width;

    int datetype; // 数据类型

    void init(int a[7], double kx, double ky)

    {

        datetype = DOOR;

        x = a[0] * kx;

        y = a[1] * ky;

        dir = a[2];

        x2 = a[3] * kx;

        y2 = a[4] * ky;

        width = a[5] * kx;

        hight = a[6] * ky;

    }

    void show()

    {

        setfillcolor(BROWN);

        bar(x, y, x + width, y + hight);

    }

};

actor—角色类:

功能展示:

不同的枪的渲染:

发射子弹: 跳跃:

捡箱子前: 捡箱子后:实现了枪的切换

通过传送门传送:

实现代码:

struct actor

{

    int x, y;                                                     // 角色位置

    int width, hight;                                             // 角色宽高

    int actor_isonblock;                                          // 用于判断是否在物体上(0在空中,1在物体上)

    int actor_isjp, actor_jplimit, actor_jphight, actor_haslimit; // jp跳跃次数,jplimit最大跳跃高度,jphight跳跃高度,haslimit判断是否达到最大高度;

    int dir, lastdir;                                             // 角色朝向,0正面 1右 2下 -1左

    int speed_move, speed_jp;                                     // 角色移动跳跃速度

    PIMAGE img_actor_left, img_actor_right;                       // 角色贴图

    int life;

    int guntime; // 生命值

    int guntype; // 记录捡到什么枪

    int isfire;  // 记录是否开火如果按下攻击键isfire置为1,开枪后刷新为0

    int isdancing;

    int datetype;

    int dance_tietu, run_tietu, withgun_tietu;

    clock_t dance_start, dance_end, die_start, die_end;

    void init(int Game_width, double kx, double ky)

    {

        // 加载人物站立图像及其掩图

        x = Game_width / 2; // 初始x位置    // 角色构造函数人物初始化从空中下落

        y = -100;           // 初始y

        img_actor_left = newimage();

        getimage_pngfile(img_actor_left, "E:\\VSCODE\\game\\painting\\man_left.png");

        datetype = ACTOR;    // 数据类型为ACTOR

        actor_isonblock = 0; // 初始角色在空中

        dir = FRONT;

        speed_move = ACTOR_SPEED_MOVE;         // 移动速度

        speed_jp = ACTOR_SPEED_JUMP;           // 跳跃速度

        width = Actor_width * kx;              // 角色宽度

        hight = Actor_hight * ky;              // 角色高度

        actor_jplimit = ACTOR_JUMP_LIMIT * ky; // 跳跃极限

        actor_jphight = 0;                     // 当前跳跃高度

        actor_isjp = 0;                        // 是否处于弹跳状态1为跳跃,0为不跳跃

        life = ACTOR_LIFE;                     // 开局生命值为10

        guntype = lanqiu;                    // 开局默认是手枪

        isfire = 0;                            // 开局不开枪

        isdancing = 0;                         // 开局不跳舞

        guntime = gun[guntype][2];

        dance_start = 0;

        dance_end = 0;

        die_start = 0;

        die_end = 0;

        dance_tietu = 0;

        run_tietu = 0;

        withgun_tietu = 0;

    }

    //jump跳跃函数

    void jump()

    {

        if (actor_isjp == 0 && actor_isonblock == 0 && y < 720)

        {

            y = y + G;

        }

        if (actor_isjp == 1)

        {

            y -= speed_jp;

            actor_jphight += speed_jp;

            if (actor_jphight > actor_jplimit)

            {

                actor_isjp = 0;

                actor_jphight = 0;

            }

        }

    }

    // 由子弹类调用hurt()

    void hurt(int type, int damage)

    {

        switch (type)

        {

        case jinqiang:

            /*之后加上回血特效*/

            life = life - damage;

            break;

        case lanqiu:

            isdancing = 1; // 让角色跳舞

        default:

            life = life - damage;

            break;

        }

        printf("life:%d", life);

    }

    // 更新函数用于更新位置及贴图信息

    void update()

    {

        if (life > 0)

        {

            if (isdancing == 0)

            {

                dance_start = time(NULL);

                // 刷新左右移动

                if (dir == -1 || dir == 1)

                {

                    x = x + (speed_move * dir); // 更新移动

                }

            }

            dance_end = time(NULL);

            int diff = difftime(dance_end, dance_start);

            if (diff > 3)

            {

                isdancing = 0;

                dance_start = 0;

                dance_end = 0;

                dance_tietu = 0;

            }

            jump();

            // 记录上一个方向

            if (dir == RIGHT || dir == LEFT)

            {

                lastdir = dir;

            }

        }

        // 死亡重新复活

        if (die_start != 0)

        {

            die_end = time(NULL);

        }

        if (life <= 0 && die_start == 0)

        {

            die_start = time(NULL);

        }

        int diff_die = difftime(die_end, die_start);

        if (diff_die > 5)

        {

            life = ACTOR_LIFE;   // 生命值为10

            guntype = shouqiang; // 默认是手枪s

            actor_isonblock = 0; // 初始角色在空中

            dir = FRONT;

            x = rand() % 1200; // 初始x位置

            y = -100;          // 初始y

            die_start = 0;

            die_end = 0;

            diff_die = 0;

        }

    }

    void show(double kx, double ky)

    {

        if (life > 0)

        {

            if (isdancing == 1)

            {

                int i = (dance_tietu / 4) % 4;

                suofang_img(img_chongfeng, x, y, img_man[dance][i][0], img_man[dance][i][1], width, hight, kx, ky);

                dance_tietu++;

            }

            else

            {

                // 朝左运动

                if (dir == LEFT)

                {

                    int i = (run_tietu / 10) % 4;

                    switch (guntype)

                    {

                    case chongfeng:

                    {

                        suofang_img(img_chongfeng, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    case jujiqiang:

                    {

                        suofang_img(img_juji, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    case jinqiang:

                    {

                        suofang_img(img_jinqiang, x, y, img_man[run][i][0]+190, img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    case shouqiang:

                    {

                        suofang_img(img_shouqiang, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    case lanqiu:

                    {

                        suofang_img(img_jinqiang, x, y, img_man[run][i][0]+190, img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    default:

                        break;

                    }

                }

                // 朝右运动

                if (dir == RIGHT)

                {

                    int i = (run_tietu / 10) % 4;

                    switch (guntype)

                    {

                    case chongfeng:

                    {

                        suofang_img(img_chongfeng, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    case jujiqiang:

                    {

                        suofang_img(img_juji, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    case jinqiang:

                    {

                        suofang_img(img_jinqiang, x, y, img_man[run][i][0]+190, img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    case shouqiang:

                    {

                        suofang_img(img_shouqiang, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    case lanqiu:

                    {

                        suofang_img(img_jinqiang, x, y, img_man[run][i][0]+190, img_man[run][i][1], width, hight, kx, ky);

                        run_tietu++;

                        if (i > 4)

                        {

                            run_tietu = 0;

                        }

                    }

                    break;

                    default:

                        break;

                    }

                }

                // 不动展示正面

                if (dir == 0)

                {

                    suofang_img(img_chongfeng, x, y, 0, 0, width, hight, kx, ky);

                }

            }

        }

        dir = FRONT;

    }

};

box—箱子类:

功能展示:

箱子渲染及其与地面的碰撞检测:

实现代码:

//-------------------箱子类----------------------------//

struct box

{

    int box_type;

    int box_speed_x, box_speed_y;

    int y, x;

    int randvalue;

    int width;      // 盒子的边长

    int is_onblock; // 在物体上

    int valid;      // 判断箱子是否被捡

    int dir;        // 箱子下落过程中水平运动方向

    int datetype;   // 数据类型

    void init(int Game_width)

    {

        datetype = BOX;

        randvalue = rand();

        width = BOX_WIDTH;                    // 盒子边长

        y = 0;                                // 盒子从窗口顶端释放

        x = randvalue % Game_width;           // 随机生成盒子x位置

        dir = (randvalue % 2) * (-1);         // 随机方向

        box_type = randvalue % 5;             // 随机生成盒子中的枪类型(0-4之间)

        box_speed_x = dir * (randvalue % 10); // 随机盒子下抛x,y速度

        box_speed_y = randvalue % 10;

        valid = 1;

        is_onblock = 0; // 盒子在空中

    }

    // 让盒子沿着抛物线运动

    void box_run(int Game_width, double ky)

    {

        if (is_onblock == 0)

        {

            // 当盒子靠近窗口边缘就反弹

            if (x > 0 && x + width < Game_width)

                x += box_speed_x;

            else

            {

                box_speed_x = -box_speed_x;

                x += box_speed_x;

            }

            if (y < 1780 * ky)

                y += box_speed_y;

            box_speed_y += box_speed_y / 5;

        }

    }

    // 画出盒子

    void box_show(double kx, double ky)

    {

        suofang_img(img_xiangzi, x, y, 0, 0, width, width, kx, ky);

    }

    void getbox(actor *temp)

    {

        temp->guntype = box_type;

        valid = 0;

    }

};

bullet—子弹类:

功能展示:

不同枪对应不同子弹:

及其隐藏枪械篮球:击中敌人会让敌人跳舞并且无法移动

子弹传送:

实现代码:

//----------————————-子弹类---————————————------------//

struct Bullet

{

    int x;            // 子弹横坐标

    int y;            // 子弹纵坐标

    int dir;          // 子弹朝向,1右 -1左

    int hight, width; // 子弹宽高

    bool valid;       // 子弹是否还在有效范围内有效范围外为0

    int type;         // 子弹类型

    int damage;       // 子弹伤害

    double speed;     // 子弹速度

    int fire_speed;   // 连发速度

    int num;          // 子弹数量

    int houzuoli;     // 后坐力

    int fjxg;         // 附加效果-1为让敌人跳舞 -2为扣自己的血给敌人加血

    int datetype;     // 数据类型

    // 当角色开火时射出子弹

    // 根据角色捡到什么类型的箱子,匹配子弹伤害子弹速度

    void init(actor *player)

    {

        // 初始化子弹属性

        datetype = BULLET;

        dir = player->lastdir;

        type = player->guntype;

        speed = gun[type][0] * 0.5;

        damage = gun[type][1];

        fire_speed = gun[type][2];

        houzuoli = gun[type][3];

        fjxg = gun[type][4];

        width = 10;

        hight = 5;

        if (dir == LEFT)

        {

            x = player->x - 15;

            y = player->y + player->hight * 0.3;

        }

        else

        {

            x = player->x + player->width + 15;

            y = player->y + player->hight * 0.3; // 修正子弹在枪口

        }

        valid = 1; // 子弹射出

    }

    void show(double kx, double ky)

    {

        if (valid == 1)

        {

            switch (type)

            {

            case shouqiang:

                setfillcolor(WHITE);

                bar(x, y, x + 10, y + 5);

                break;

            case jujiqiang:

                setfillcolor(RED);

                bar(x, y, x + 15, y + 5);

                break;

            case jinqiang:

                setfillcolor(GREEN);

                bar(x, y, x + 10, y + 5);

                break;

            case chongfeng:

                setfillcolor(WHITE);

                line(x, y, x + 300, y);

                line(x, y, x + 300, y + 3);

                line(x, y, x + 300, y - 3);

                line(x, y, x + 300, y + 2);

                line(x, y, x + 300, y - 2);

                break;

            case lanqiu:

                suofang_img(img_lanqiu, x, y, 0, 0, 110, 110, kx, ky);

                break;

            default:

                break;

            }

        }

    }

    void run()

    {

        if (valid == 1)

            x = x + dir * speed;

    }

    void isvalid(int Game_width)

    {

        if (x < 0 || x > Game_width)

            valid = 0;

    }

    void hit(actor *mubiao)

    {

        // 调用目标的hurt()函数实现

        mubiao->hurt(type, damage);

        valid = 0;

    }

};

(3)游戏初始化:

游戏数据更新依赖数据初始化:

Class.h中预处理:

Game.c中:

调用initgame()函数初始化游戏数据更新依赖数据:

initgame()中调用了Get_img(double kx, double ky)函数载入图片:

角色初始化由角色成员函数actor::init(int Game_width, double kx, double ky)完成:

实现代码:

// 初始化游戏

void initgame()

{

    // 初始化时间刷新变量

    startTime = time(NULL);

    fire1_start = 0;

    fire1_end = 0;

    fire2_start = 0;

    fire2_end = 0;

    num_box = 0; // 开始时没有盒子

    num_block = 16;

    num_door = 3;

    escape = 0; // 退出键设置为0

    printf("%d,%d\n", Game_hight, Game_width);

    Game_kx = Game_width * 1.0 / BK_WIDTH;

    Game_ky = Game_hight * 1.0 / BK_HEIGHT;

    printf("%lf,%lf\n", Game_kx, Game_ky);

    Get_img(Game_kx, Game_ky);

    // door初始化

    int door_date[3][7] = {

        {1959, 608, RIGHT, 647, 110, 422, 422},

        {0, 998, LEFT, 1847, 805, 265, 655},

        {4125, 855, RIGHT, 320, 1348, 120, 425}};

    for (int i = 0; i < num_door; i++)

    {

        doors[i].init(door_date[i], Game_kx, Game_ky);

    }

    int block_date[16][4] = {

        {0, 1660, 1117, 20},

        {0, 566, 130, 20},

        {0, 860, 550, 20},

        {567, 282, 425, 20},

        {1000, 930, 170, 20},

        {1130, 1790, 1980, 20},

        {1300, 930, 150, 20},

        {1543, 1411, 1239, 20},

        {1600, 360, 400, 20},

        {1977, 1100, 800, 20},

        {2400, 380, 400, 20},

        {3060, 430, 250, 20},

        {3110, 1510, 1130, 20},

        {3500, 820, 740, 20},

        {3730, 260, 530, 20},

        {3730, 1300, 530, 20},

    };

    // block初始化

    for (int i = 0; i < num_block; i++)

    {

        blocks[i].init(block_date[i], Game_kx, Game_ky);

    }

    img_bk = newimage();

    getimage(img_bk, "E:\\VSCODE\\game\\painting\\gunman_bk.png");

    player1.init(Game_width, Game_kx, Game_ky); // 角色1初始化

    player2.init(Game_width, Game_kx, Game_ky); // 角色2初始化

}

(4)玩家输入update_input()函数

实现代码:

// 玩家输入系统

void update_input()

{

    ///////////////////////////////////////////////player1///////////////////////////////////////////

    if ((GetAsyncKeyState(0x41) & 0x8000)) // player1输入a时左移      //GetAsyncKeyState()实现同步输入

    {

        if (player1.x <= 1124 * Game_kx &&

                player1.y + player1.hight >= 1683 * Game_ky + 5 ||

            player1.x <= 123 * Game_kx &&

                player1.y + player1.hight >= 582 * Game_ky)

            ;

        else

        {

            if (player1.x > 0)

                player1.dir = LEFT;

        }

    }

    if ((GetAsyncKeyState(0x44) & 0x8000)) // player1输入d时左移

    {

        if (player1.x + player1.width >= 3105 * Game_kx &&

                player1.y + player1.width >= 1504 * Game_ky ||

            player1.x + player1.width >= 3730 * Game_kx &&

                player1.y + player1.width >= 1300 * Game_ky)

            ;

        else

        {

            if (player1.x + player1.width < Game_width)

                player1.dir = RIGHT;

        }

    }

    if ((GetAsyncKeyState(0x53) & 0x8000)) // player1输入s时跳下block

    {

        if (player1.y + player1.hight >= 1510 * Game_ky ||

            player1.x >= 3722 * Game_kx &&

                player1.y + player1.hight >= 1292 * Game_ky ||

            player1.x <= 266)

            ;

        else

        {

            if (player1.actor_isonblock == 1)

                player1.actor_isonblock = 0;

        }

    }

    if ((GetAsyncKeyState(0x4B) & 0x8000)) // player1输入k时跳跃,

    {

        if (player1.actor_isonblock == 1) // 当达到最大高度时不可以输入k,未达到时可以输入k

        {

            player1.actor_isjp = 1; // 设置跳跃标志位

        }

    }

    if ((GetAsyncKeyState(0x4A) & 0x8000)) // 当按下j时发出子弹

    {

        if (fire1_start == 0)

        {

            player1.isfire = 1;

            fire1_start = time(NULL);

        }

        fire1_end = time(NULL);

        double diff = difftime(fire1_end, fire1_start);

        if (diff >= player1.guntime)

        {

            player1.isfire = 1;

            fire1_start = fire1_end;

        }

    }

    if ((GetAsyncKeyState(VK_ESCAPE) & 0x8000)) // 设置退出按键

    {

        escape = 1; // 设置退出按键

    }

///////////////////////////////////player2///////////////////////////////////////////////////////

    if ((GetAsyncKeyState(VK_LEFT) & 0x8000)) // player2输入左键时左移

    {

        if (player2.x <= 1124 * Game_kx &&

                player2.y + player2.hight >= 1683 * Game_ky + 5 ||

            player2.x <= 123 * Game_kx &&

                player2.y + player2.hight >= 582 * Game_ky)

            ;

        else

        {

            if (player2.x > 0)

                player2.dir = LEFT;

        }

    }

    if ((GetAsyncKeyState(VK_RIGHT) & 0x8000)) // player2输入右键时左移

    {

        if (player2.x + player2.width >= 3105 * Game_kx &&

                player2.y + player2.width >= 1504 * Game_ky ||

            player2.x + player2.width >= 3730 * Game_kx &&

                player2.y + player2.width >= 1300 * Game_ky)

            ;

        else

        {

            if (player2.x + player2.width < Game_width)

                player2.dir = RIGHT;

        }

    }

    if ((GetAsyncKeyState(VK_DOWN) & 0x8000)) // player2输入下键时跳下block

    {

        if (player2.y + player2.hight >= 1510 * Game_ky ||

            player2.x >= 3722 * Game_kx &&

                player2.y + player2.hight >= 1292 * Game_ky ||

            player2.x <= 266)

            ;

        else

        {

            if (player2.actor_isonblock == 1)

                player2.actor_isonblock = 0;

        }

    }

    if ((GetAsyncKeyState(VK_NUMPAD1) & 0x8000)) // 当按下1时发出子弹

    {

        if (fire2_start == 0)

        {

            player2.isfire = 1;

            fire2_start = time(NULL);

        }

        fire2_end = time(NULL);

        double diff = difftime(fire2_end, fire2_start);

        if (diff >= player2.guntime)

        {

            player2.isfire = 1;

            fire2_start = fire2_end;

        }

    }

    if ((GetAsyncKeyState(VK_NUMPAD2) & 0x8000)) // player1输入k时跳跃,

    {

        if (player2.actor_isonblock == 1) // 当达到最大高度时不可以输入k,未达到时可以输入k

        {

            player2.actor_isjp = 1; // 设置跳跃标志位

        }

    }

}

(5)游戏数据更新update_noinput()函数

实现代码:

// 更新游戏变化

void update_noinput()

{

    player1.update();

    player2.update();

    // 遍历子弹,对子弹数据进行更新,并检测子弹有效性无效则释放内存

    if (num_bullet > 0)

    {

        for (int i = 0; i < num_bullet; i++)

        {

            bullets[i]->run();

            bullets[i]->isvalid(Game_width);

            if (bullets[i]->valid == 0)

            {

                free(bullets[i]);

                for (int j = i; j < num_bullet; j++)

                { // 移动数组中的元素

                    bullets[j] = bullets[j + 1];

                }

                num_bullet--;

            }

        }

    }

    // 检测开火

    // 角色1

    if (player1.isfire == 1 && num_bullet < MAX_BULLETS && player1.life > 0)

    {

        bullets[num_bullet] = Creat_Bullet(&player1);

        player1.x = player1.x - player1.lastdir * bullets[num_bullet]->houzuoli;

        if (player1.guntype == jinqiang)

            player1.life -= 2;

        num_bullet++;

        player1.isfire = 0;

    }

    // 角色2

    if (player2.isfire == 1 && num_bullet < MAX_BULLETS && player2.life > 0)

    {

        bullets[num_bullet] = Creat_Bullet(&player2);

        player2.x = player2.x - player2.dir * bullets[num_bullet]->houzuoli;

        if (player2.guntype == jinqiang)

            player2.life -= 2;

        num_bullet++;

        player2.isfire = 0;

    }

    // 遍历箱子,对箱子数据进行更新,并检测箱子有效性无效则释放内存

    if (num_box > 0)

    {

        for (int i = 0; i < num_box; i++)

        {

            box1[i]->box_run(Game_width, Game_ky);

            if (box1[i]->valid == 0)

            {

                free(box1[i]);

                printf("boxfree:%d\n", i);

                for (int j = i; j < num_box; j++)

                {

                    // 移动数组中的元素

                    box1[i] = box1[i + 1];

                    box1[num_box] = {0};

                }

                num_box--;

            }

        }

    }

    // 每三十秒生成一个箱子定时生成

    endTime = time(NULL);

    double diff = difftime(endTime, startTime);

    if (diff >= 30 && num_box <= MAX_BOXES) //&& num_box < MAX_BOXES

    {

        box1[num_box] = (box *)malloc(sizeof(box));

        box1[num_box]->init(Game_width);

        num_box++;

        printf("num_box:%d\n", num_box);

        startTime = endTime;

    }

}

(6)碰撞检测系统CollisionSystem():

CollisionSystem()由物体碰撞检测CollisionObject()函数和人物碰撞检测函数CollisionActor(actor *player)构成他们都是基于碰撞检测函数CheckCollision(void *obj1, int datetype1, void *obj2, int datetype2)实现:

1.CheckCollision()函数用于判断单独两个对象直接的碰撞;

2.CollisionActor(actor *player)函数和CollisionObject()函数用于实现,检测碰撞前的逻辑和碰撞   后的数据更改;

实现代码:

// 碰撞检测利用函数

int CheckCollision(void *obj1, int datetype1, void *obj2, int datetype2)

{

    int x1, y1, hight1, width1, x2, y2, hight2, width2;

    // 将void类型的指针转换成对应的数据类型的指针

    // 使用空指针

    switch (datetype1)

    {

    case ACTOR:

    {

        actor *obj1_temp = (actor *)obj1;

        x1 = obj1_temp->x;

        y1 = obj1_temp->y;

        hight1 = obj1_temp->hight;

        width1 = obj1_temp->width;

        break;

    }

    case BOX:

    {

        box *obj1_temp = (box *)obj1;

        x1 = obj1_temp->x;

        y1 = obj1_temp->y;

        hight1 = obj1_temp->width;

        width1 = obj1_temp->width;

        break;

    }

    case BULLET:

    {

        Bullet *obj1_temp = (Bullet *)obj1;

        x1 = obj1_temp->x;

        y1 = obj1_temp->y;

        hight1 = obj1_temp->hight;

        width1 = obj1_temp->width;

        break;

}

case DOOR:

    {

        door *obj1_temp = (door *)obj1;

        x1 = obj1_temp->x;

        y1 = obj1_temp->y;

        hight1 = obj1_temp->hight;

        width1 = obj1_temp->width;

        break;

    }

    case BLOCK:

    {

        block *obj1_temp = (block *)obj1;

        x1 = obj1_temp->x;

        y1 = obj1_temp->y;

        hight1 = obj1_temp->hight;

        width1 = obj1_temp->width;

        break;

    }

    default:

        break;

    }

    // 使用空指针

    switch (datetype2)

    {

    case ACTOR:

    {

        actor *obj2_temp = (actor *)obj2;

        x2 = obj2_temp->x;

        y2 = obj2_temp->y;

        hight2 = obj2_temp->hight;

        width2 = obj2_temp->width;

        break;

    }

    case BOX:

    {

        box *obj2_temp = (box *)obj2;

        x2 = obj2_temp->x;

        y2 = obj2_temp->y;

        hight2 = obj2_temp->width;

        width2 = obj2_temp->width;

        break;

    }

    case BULLET:

    {

        Bullet *obj2_temp = (Bullet *)obj2;

        x2 = obj2_temp->x;

        y2 = obj2_temp->y;

        hight2 = obj2_temp->hight;

        width2 = obj2_temp->width;

        break;

    }

    case DOOR:

    {

        door *obj2_temp = (door *)obj2;

        x2 = obj2_temp->x;

        y2 = obj2_temp->y;

        hight2 = obj2_temp->hight;

        width2 = obj2_temp->width;

        break;

    }

    case BLOCK:

    {

        block *obj2_temp = (block *)obj2;

        x2 = obj2_temp->x;

        y2 = obj2_temp->y;

        hight2 = obj2_temp->hight;

        width2 = obj2_temp->width;

        break;

    }

    default:

        break;

    }

    // 如果两个矩形不重叠,返回0,否则返回1

    if (datetype1 == ACTOR && datetype2 == BLOCK)

    {

        y1 = y1 + hight1 - 5;

        hight1 = 5;

    }

    if (x1 + width1 < x2 || x1 > x2 + width2 ||

        y1 + hight1 < y2 || y1 > y2 + hight2)

    {

        return 0;

    }

    else

    {

        return 1;

    }

}

// 角色与物体碰撞后的逻辑

void CollisionActor(actor *player)

{

    // 检测箱子与角色的碰撞

    for (int i = 0; i < num_box; i++)

    {

        if (CheckCollision(player, player->datetype, box1[i], box1[i]->datetype))

        {

            box1[i]->getbox(player);

            printf("boxget:%d", i);

            box1[i]->valid = 0;

        };

    }

    // 检测子弹与角色的碰撞

    for (int i = 0; i < num_bullet; i++)

    {

        if (CheckCollision(player, player->datetype, bullets[i], bullets[i]->datetype))

        {

            player->x = player->x + bullets[i]->houzuoli * bullets[i]->dir;

            bullets[i]->hit(player);

        };

    }

    // 检测block和人物碰撞

    for (int i = 0; i < num_block; i++)

    {

        if (CheckCollision(player, player->datetype, &blocks[i], blocks[i].datetype) == 1)

        {

            player->actor_isonblock = 1;

            // printf("%d.checkcollision:%d,%d;player:%d,%d\n", i, blocks[i].x, blocks[i].y, player->x, player->y);

            break;

        }

        else

        {

            player->actor_isonblock = 0;

        }

    }

    // 检测传送门和人

    for (int i = 0; i < num_door; i++)

    {

        if (CheckCollision(player, player->datetype, &doors[i], doors[i].datetype) == 1)

        {

            player->x = doors[i].x2;

            player->y = doors[i].y2;

            player->dir = doors[i].dir;

        }

    }

}

// 物体间碰撞后的逻辑

void CollisionObject()

{

    // 检测箱子和block和传送门碰撞

    for (int i = 0; i < num_box; i++)

    {

        for (int k = 0; k < num_block; k++)

        {

            if (CheckCollision(box1[i], box1[i]->datetype, &blocks[k], blocks[k].datetype))

            {

                box1[i]->is_onblock = 1;

            }

        }

        for (int l = 0; l < num_door; l++)

        {

            if (CheckCollision(box1[i], box1[i]->datetype, &doors[l], doors[l].datetype))

            {

                box1[i]->x = doors[l].x2;

                box1[i]->y = doors[l].y2;

                box1[i]->dir = doors[l].dir;

            }

        }

    }

    // 检测子弹传送门

    for (int i = 0; i < num_bullet; i++)

    {

        for (int l = 0; l < num_door; l++)

        {

            if (CheckCollision(bullets[i], bullets[i]->datetype, &doors[l], doors[l].datetype))

            {

                bullets[i]->x = doors[l].x2;

                bullets[i]->y = doors[l].y2;

                bullets[i]->dir = doors[l].dir;

            }}}}

// 碰撞检测系统

void CollisionSystem()

{

    CollisionActor(&player1); // player1碰撞检测

    CollisionActor(&player2); // player2碰撞检测

    CollisionObject();        // 物体碰撞检测

}

(7)游戏画面渲染

功能展示:

实现代码:

// 更新游戏画面

void showgame()

{

    putimage(0, 0, getwidth(), getheight(), img_bk, 0, 0, 4250, 2000);

    // 渲染block

    for (int i = 0; i < num_block; i++)

    {

        blocks[i].show();

    }

    // 渲染传送门

    for (int i = 0; i <= num_door; i++)

    {

        doors[i].show();

    }

    player1.show(Game_kx, Game_ky);

    player2.show(Game_kx, Game_ky);

    // 渲染子弹

    for (int i = 0; i < num_bullet; i++)

    {

        bullets[i]->show(Game_kx, Game_ky);

    }

    // 渲染箱子

    for (int i = 0; i < num_box; i++)

    {

        box1[i]->box_show(Game_kx, Game_ky);

    }

    // 渲染生命条

    if (player1.life > 0)

    {

        setfillcolor(EGERGB(241, 83, 83));

        bar(player1.x - 5, player1.y - 15, player1.x + player1.life * 5, player1.y - 10);

    }

    if (player2.life > 0)

    {

        setfillcolor(EGERGB(241, 83, 83));

        bar(player2.x - 5, player2.y - 15, player2.x + player2.life * 5, player2.y - 10);

    }

}

欢迎下载体验又部分懒得改的bug

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注