Linux系统下用C语言编写2D图形游戏

在Linux系统下,不要以为C语言就只能写那种只有字符的控制台程序,别忘了,Linux系统有FrameBuffer(帧缓冲),只要显示器是彩色的,并且是linux系统的,就可以用C语言代码通过读写FrameBuffer里的数据在屏幕上绘制图形;

图形嘛,有png库,用它的函数解码图片文件,得到图片数组,共有red、green、blue、alpha四种数组,alpha用于图片之间的组合,最终将RGB数组输出到FrameBuffer就能显示了;

想要动态图形效果,自己用算法实现。

目前,我正在为自己的嵌入式设备开发一个游戏,图形素材来源于互联网,游戏截图如下图所示:

Linux系统下用C语言编写2D图形游戏

Linux系统下用C语言编写2D图形游戏

Linux系统下用C语言编写2D图形游戏

Linux系统下用C语言编写2D图形游戏


如上图所示,主菜单中的红色光标有闪烁效果,游戏画面切换有淡入淡出效果。

下面有源码,现在使用的按键控制方法的效果不理想。

linux系统环境,按键判断,使用了getch()和kbhit()函数,函数是模拟实现的。

为了判断按键是否为按住状态,我使用一个变量count来计数,每循环一次自增,也就是按键检测延迟的最大时间,超过了这段时间,如果getch()函数没有再次返回接受到同样的按键的键值,那么就判断为这个按键已经释放,否则,该按键处于按住状态,并继续显示之前的动作。

还有图形显示,目前没掌握局部刷新的技术,游戏显示的每一帧图形都是全屏刷新的,效率低,每秒大概刷新4帧。

源码只提供部分:

[cpp]

void next_frames(void)   {//更新到下一帧        frames[0] += speed[0];//玩家1的        frames[1] += speed[1];//AI的    }      void init_player_data(void)   //初始化各个玩家的数据    {       direction[0] = IS_RIGHT;//玩家朝向右边        direction[1] = IS_LEFT;//AI角色朝向左边           //各个角色的初始位置        map_site_x[0] = 100;       map_site_x[1] = 300;       map_site_y[1] = map_hight - 20;       map_site_y[0] = map_hight - 20;//在地图上的坐标               speed[0] = 1;//每次循环帧frames自增的的值,之前是想做能设置游戏刷新速度的功能,现在已抛弃,这个变量貌似没用了。        speed[1] = 1;       need_move[0] = 0;//玩家是否需要移动        need_move[1] = 0;//AI是否需要移动               can_use_next[0] = 0;//可以使用下一个动作        can_use_next[1] = 0;//AI可以使用下一个动作        status[0] = STANCE;//玩家为站立状态        status[1] = STANCE;//AI为站立状态               shock_wave_num = 0;//冲击波的总数为0        need_view_shock_wave = 0;//不需要显示冲击波    }   int play_game(void)   {       unsigned char **game_map;//游戏地图        int map_id,temp_key = 0,count = 0,key = 0,man_id;//人物代号        //分配内存        man_id = ICHIGO;//人物动画选择黑崎一护        init_player_data();//初始化玩家数据        map_id = 1;       if(map_id == 1) {           game_map = load_map_1();//载入地图数据        }       while(1){           update_graph();//规定镜头,显示玩家所在的区域的图形            view_map_area(game_map[0],game_map[1],game_map[2]);//根据主角的位置,显示地图中的某个区域            if(kbhit()){//调用kbhit()函数检测是否有按键输入                key = getch();//有按键输入就用getch()函数获取键值并赋给key,下面开始判断key的值                if(key == KEY_BACK) break;               if(key == 'w' || key == 'W' || key == KEY_UP) {                   temp_key = key;               }               else if((key == 'd' || key == 'D' || key == KEY_RIGHT) && status[0] != JUMP_ATTACK){//如果按的是a键或者左键                    if((temp_key == 'd' || temp_key == 'D' || temp_key == KEY_RIGHT)&& count < 5) {                          next_action[0] = WALK;//下一个动作还是行走                    }                   else if(can_use_next[0] == 0){//如果之前没有按过a键或者左键,并且,可以使用下一个动作                        if(temp_status[0] != JUMP) {//如果之前不是跳跃状态                            status[0] = WALK;                           if(temp_status[0] != WALK) frames[0] = 0;//如果之前不是行走状态                            else if(temp_status[0] == WALK){//如果之前是行走状态                                next_action[0] = WALK;//下一个动作还是行走                            }                       }                       else need_move[0] = 1;//否则,需要移动                    }                   direction[0] = IS_RIGHT;                   need_move[0] = 1;                   temp_key = key;                   count = 0;//计数清零                }               else if((key == 'a' || key == 'A' || key == KEY_LEFT) && status[0] != JUMP_ATTACK){//如果按的是a键或者左键                    if((temp_key == 'a' || temp_key == 'A' || temp_key == KEY_LEFT)&& count < 5) {                       next_action[0] = WALK;//下一个动作还是行走                    }                   else if(can_use_next[0] == 0){//如果之前没有按过a键或者左键,并且,可以使用下一个动作                        if(temp_status[0] != JUMP) {//如果之前不是跳跃状态                            status[0] = WALK;                           if(temp_status[0] != WALK) frames[0] = 0;//如果之前不是行走状态                            else if(temp_status[0] == WALK){//如果之前是行走状态                                next_action[0] = WALK;//下一个动作还是行走                            }                       }                       else need_move[0] = 1;//否则,需要移动                    }                   direction[0] = IS_LEFT;                   need_move[0] = 1;                   temp_key = key;                   count = 0;//计数清零                }               else if(key == 's' || key == 'S' || key == KEY_DOWN){//如果按的是s键或者是下键                    if(can_use_next[0] == 0){                       status[0] = BLOCK;                       if((temp_key == 's' || temp_key == 'S' || temp_key == KEY_DOWN) && count <10){                           //如果之前按过s键或者是下键,并且在最大延迟时间内                            if(temp_status[0] == BLOCK && status[0] == BLOCK)//如果之前的动作是防御                            {                               frames[0] = 2;speed[0] = 0;                           }                       }                       else {                           frames[0] = 0;//帧数归零                            speed[0] = 1;                       }                   }                   temp_key = key;//保存按键的键值                    count = 0;//计数归零                }               else if(key == 'j' || key == 'J'){//如果按的是j键                    if(temp_status[0] == JUMP || temp_status[0] == JUMP_ATTACK)//如果之前还处于跳跃状态                    {                       if(jump_attack_num == 0) {                           status[0] = JUMP_ATTACK;//跳跃攻击                            jump_attack_num = 1;                       }                   }                   else if(status[0] == DOWN_ATTACK || status[0] == BLOCK || temp_key == KEY_DOWN || temp_key == 's' || temp_key == 'S'){                       status[0] = DOWN_ATTACK;                       if(temp_status[0] != DOWN_ATTACK) frames[0] = 0;//帧数归零                    }                   else if(temp_key == KEY_UP || temp_key == 'w' || temp_key == 'W'){                       if(can_use_next[0] == 0){//如果可以使用下一个动作                            status[0] = UP_ATTACK;                           frames[0] = 0;//帧数归零                        }                   }                   else if((temp_key == 'j' || temp_key == 'J') && count <5){//如果之前按过j键,并且在最大延迟时间内                        if(temp_status[0] == FIRST_ATTACK && status[0] == FIRST_ATTACK)//如果之前处于第一段攻击状态下                        {                           status[0] = FIRST_ATTACK;                           if(can_use_next[0] == 0 && first_attack_complete[0] == 0){//如果可以使用下一个动作                                status[0] = SECOND_ATTACK;//开始进行第二段攻击                                frames[0] = 0;//帧数归零                            }                           else{                               next_action[0] = SECOND_ATTACK;//保存下一个动作,等待之前的动作完成                            }                       }                       else if(temp_status[0] == SECOND_ATTACK || status[0] == SECOND_ATTACK)//如果之前处于第二段攻击状态下                        {                           status[0] = SECOND_ATTACK;                           if(can_use_next[0] == 0 && second_attack_complete[0] == 0){//如果可以使用下一个动作                                status[0] = THIRD_ATTACK;//开始进行第二段攻击                                frames[0] = 0;//帧数归零                            }                           else{                               next_action[0] = THIRD_ATTACK;//保存下一个动作,等待之前的动作完成                            }                       }                       else if(temp_status[0] == THIRD_ATTACK || status[0] == THIRD_ATTACK)//如果之前处于第二段攻击状态下                        {                           status[0] = THIRD_ATTACK;                           if(can_use_next[0] == 0 && third_attack_complete[0] == 0){//如果可以使用下一个动作                                status[0] = FIRST_ATTACK;//开始进行第一段攻击                                frames[0] = 0;//帧数归零                            }                           else{                               //next_action = FIRST_ATTACK;//保存下一个动作,等待之前的动作完成                            }                       }                       else{                           status[0] = FIRST_ATTACK;                           frames[0] = 0;                       }                   }                      else {                       status[0] = FIRST_ATTACK;                       if(temp_status[0] != FIRST_ATTACK) frames[0] = 0;                   }                   speed[0] = 1;                   count = 0;                   temp_key = key;//保存按键的键值                }               else if((key == 'k' || key == 'K') && can_use_next[0] == 0){//如果按的是k键,并且能使用下一个动作                    status[0] = JUMP;                   count = 0;                   jump_attack_num = 0;                   frames[0] = 0;               }               else if(key == 'U' || key == 'u'){                   if(status[0] == DOWN_Y_ATTACK || status[0] == BLOCK || temp_key == KEY_DOWN || temp_key == 's' || temp_key == 'S'){                       speed[0] = 1;                       status[0] = DOWN_Y_ATTACK;                       if(temp_status[0] != DOWN_Y_ATTACK) frames[0] = 0;//帧数归零                    }                   else if(can_use_next[0] == 0){//如果可以使用下一个动作                        status[0] = Y_ATTACK;//开始进行第二段攻击                        frames[0] = 0;//帧数归零                    }                   else{                       next_action[0] = Y_ATTACK;//保存下一个动作,等待之前的动作完成                    }                   count = 0;                   temp_key = key;               }               else if(key == 'L' || key == 'l'){                    if(can_use_next[0] == 0){//如果可以使用下一个动作                        status[0] = DASH;                       frames[0] = 0;//帧数归零                    }                   else{                       next_action[0] = DASH;//保存下一个动作,等待之前的动作完成                    }                   need_move[0] = 1;//需要移动                }           }           else if(count > 5 && can_use_next[0] == 0) {//如果在计数大于5时还没有接收到按键输入,并且可以使用下一个动作                status[0] = STANCE;//状态改为站立                speed[0] = 1;               temp_key = 0;               count = 0;//计数清零            }           if_need_move(0,count);//判断是否需要移动            if(man_id == ICHIGO) ichigo(0);//使用黑崎一护的动作图形            ichigo(1);           next_frames();//更新到下一个帧            write_to_fb(screen[0],screen[1],screen[2]);//显示图形            //usleep(10000);//之前用过usleep(30000),牺牲帧的刷新速度,获得按键响应效果的提升,我认为这不值            count++;//计数自增        }       //释放背景图占用的内存        free(game_map[0]);       free(game_map[1]);       free(game_map[2]);       free(game_map);       return 0;   }   int main()   {       init_game();//初始化游戏,获取屏幕尺寸,分配内存        game_boot();//显示游戏启动画面        main_menu();//显示游戏主菜单        system("stty echo");       return 0;   }  

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wydjdy.html