Welcome 微信登录

首页 / 操作系统 / Linux / Linux系统下用C语言编写2D图形游戏

在Linux系统下,不要以为C语言就只能写那种只有字符的控制台程序,别忘了,Linux系统有FrameBuffer(帧缓冲),只要显示器是彩色的,并且是linux系统的,就可以用C语言代码通过读写FrameBuffer里的数据在屏幕上绘制图形;图形嘛,有png库,用它的函数解码图片文件,得到图片数组,共有red、green、blue、alpha四种数组,alpha用于图片之间的组合,最终将RGB数组输出到FrameBuffer就能显示了;想要动态图形效果,自己用算法实现。目前,我正在为自己的嵌入式设备开发一个游戏,图形素材来源于互联网,游戏截图如下图所示:
如上图所示,主菜单中的红色光标有闪烁效果,游戏画面切换有淡入淡出效果。下面有源码,现在使用的按键控制方法的效果不理想。linux系统环境,按键判断,使用了getch()和kbhit()函数,函数是模拟实现的。为了判断按键是否为按住状态,我使用一个变量count来计数,每循环一次自增,也就是按键检测延迟的最大时间,超过了这段时间,如果getch()函数没有再次返回接受到同样的按键的键值,那么就判断为这个按键已经释放,否则,该按键处于按住状态,并继续显示之前的动作。还有图形显示,目前没掌握局部刷新的技术,游戏显示的每一帧图形都是全屏刷新的,效率低,每秒大概刷新4帧。源码只提供部分:[cpp]
  1. void next_frames(void)  
  2. {//更新到下一帧   
  3.     frames[0] += speed[0];//玩家1的   
  4.     frames[1] += speed[1];//AI的   
  5. }  
  6.   
  7. void init_player_data(void)  
  8. //初始化各个玩家的数据   
  9. {  
  10.     direction[0] = IS_RIGHT;//玩家朝向右边   
  11.     direction[1] = IS_LEFT;//AI角色朝向左边   
  12.   
  13.     //各个角色的初始位置   
  14.     map_site_x[0] = 100;  
  15.     map_site_x[1] = 300;  
  16.     map_site_y[1] = map_hight - 20;  
  17.     map_site_y[0] = map_hight - 20;//在地图上的坐标   
  18.       
  19.     speed[0] = 1;//每次循环帧frames自增的的值,之前是想做能设置游戏刷新速度的功能,现在已抛弃,这个变量貌似没用了。   
  20.     speed[1] = 1;  
  21.     need_move[0] = 0;//玩家是否需要移动   
  22.     need_move[1] = 0;//AI是否需要移动   
  23.       
  24.     can_use_next[0] = 0;//可以使用下一个动作   
  25.     can_use_next[1] = 0;//AI可以使用下一个动作   
  26.     status[0] = STANCE;//玩家为站立状态   
  27.     status[1] = STANCE;//AI为站立状态   
  28.       
  29.     shock_wave_num = 0;//冲击波的总数为0   
  30.     need_view_shock_wave = 0;//不需要显示冲击波   
  31. }  
  32. int play_game(void)  
  33. {  
  34.     unsigned char **game_map;//游戏地图   
  35.     int map_id,temp_key = 0,count = 0,key = 0,man_id;//人物代号   
  36.     //分配内存   
  37.     man_id = ICHIGO;//人物动画选择黑崎一护   
  38.     init_player_data();//初始化玩家数据   
  39.     map_id = 1;  
  40.     if(map_id == 1) {  
  41.         game_map = load_map_1();//载入地图数据   
  42.     }  
  43.     while(1){  
  44.         update_graph();//规定镜头,显示玩家所在的区域的图形   
  45.         view_map_area(game_map[0],game_map[1],game_map[2]);//根据主角的位置,显示地图中的某个区域   
  46.         if(kbhit()){//调用kbhit()函数检测是否有按键输入   
  47.             key = getch();//有按键输入就用getch()函数获取键值并赋给key,下面开始判断key的值   
  48.             if(key == KEY_BACK) break;  
  49.             if(key == "w" || key == "W" || key == KEY_UP) {  
  50.                 temp_key = key;  
  51.             }  
  52.             else if((key == "d" || key == "D" || key == KEY_RIGHT) && status[0] != JUMP_ATTACK){//如果按的是a键或者左键   
  53.                 if((temp_key == "d" || temp_key == "D" || temp_key == KEY_RIGHT)&& count < 5) {  
  54.   
  55.                     next_action[0] = WALK;//下一个动作还是行走   
  56.                 }  
  57.                 else if(can_use_next[0] == 0){//如果之前没有按过a键或者左键,并且,可以使用下一个动作   
  58.                     if(temp_status[0] != JUMP) {//如果之前不是跳跃状态   
  59.                         status[0] = WALK;  
  60.                         if(temp_status[0] != WALK) frames[0] = 0;//如果之前不是行走状态   
  61.                         else if(temp_status[0] == WALK){//如果之前是行走状态   
  62.                             next_action[0] = WALK;//下一个动作还是行走   
  63.                         }  
  64.                     }  
  65.                     else need_move[0] = 1;//否则,需要移动   
  66.                 }  
  67.                 direction[0] = IS_RIGHT;  
  68.                 need_move[0] = 1;  
  69.                 temp_key = key;  
  70.                 count = 0;//计数清零   
  71.             }  
  72.             else if((key == "a" || key == "A" || key == KEY_LEFT) && status[0] != JUMP_ATTACK){//如果按的是a键或者左键   
  73.                 if((temp_key == "a" || temp_key == "A" || temp_key == KEY_LEFT)&& count < 5) {  
  74.                     next_action[0] = WALK;//下一个动作还是行走   
  75.                 }  
  76.                 else if(can_use_next[0] == 0){//如果之前没有按过a键或者左键,并且,可以使用下一个动作   
  77.                     if(temp_status[0] != JUMP) {//如果之前不是跳跃状态   
  78.                         status[0] = WALK;  
  79.                         if(temp_status[0] != WALK) frames[0] = 0;//如果之前不是行走状态   
  80.                         else if(temp_status[0] == WALK){//如果之前是行走状态   
  81.                             next_action[0] = WALK;//下一个动作还是行走   
  82.                         }  
  83.                     }  
  84.                     else need_move[0] = 1;//否则,需要移动   
  85.                 }  
  86.                 direction[0] = IS_LEFT;  
  87.                 need_move[0] = 1;  
  88.                 temp_key = key;  
  89.                 count = 0;//计数清零   
  90.             }  
  91.             else if(key == "s" || key == "S" || key == KEY_DOWN){//如果按的是s键或者是下键   
  92.                 if(can_use_next[0] == 0){  
  93.                     status[0] = BLOCK;  
  94.                     if((temp_key == "s" || temp_key == "S" || temp_key == KEY_DOWN) && count <10){  
  95.                         //如果之前按过s键或者是下键,并且在最大延迟时间内   
  96.                         if(temp_status[0] == BLOCK && status[0] == BLOCK)//如果之前的动作是防御   
  97.                         {  
  98.                             frames[0] = 2;speed[0] = 0;  
  99.                         }  
  100.                     }  
  101.                     else {  
  102.                         frames[0] = 0;//帧数归零www.linuxidc.com   
  103.                         speed[0] = 1;  
  104.                     }  
  105.                 }  
  106.                 temp_key = key;//保存按键的键值   
  107.                 count = 0;//计数归零   
  108.             }  
  109.             else if(key == "j" || key == "J"){//如果按的是j键   
  110.                 if(temp_status[0] == JUMP || temp_status[0] == JUMP_ATTACK)//如果之前还处于跳跃状态   
  111.                 {  
  112.                     if(jump_attack_num == 0) {  
  113.                         status[0] = JUMP_ATTACK;//跳跃攻击   
  114.                         jump_attack_num = 1;  
  115.                     }  
  116.                 }  
  117.                 else if(status[0] == DOWN_ATTACK || status[0] == BLOCK || temp_key == KEY_DOWN || temp_key == "s" || temp_key == "S"){  
  118.                     status[0] = DOWN_ATTACK;  
  119.                     if(temp_status[0] != DOWN_ATTACK) frames[0] = 0;//帧数归零   
  120.                 }  
  121.                 else if(temp_key == KEY_UP || temp_key == "w" || temp_key == "W"){  
  122.                     if(can_use_next[0] == 0){//如果可以使用下一个动作   
  123.                         status[0] = UP_ATTACK;  
  124.                         frames[0] = 0;//帧数归零   
  125.                     }  
  126.                 }  
  127.                 else if((temp_key == "j" || temp_key == "J") && count <5){//如果之前按过j键,并且在最大延迟时间内   
  128.                     if(temp_status[0] == FIRST_ATTACK && status[0] == FIRST_ATTACK)//如果之前处于第一段攻击状态下   
  129.                     {  
  130.                         status[0] = FIRST_ATTACK;  
  131.                         if(can_use_next[0] == 0 && first_attack_complete[0] == 0){//如果可以使用下一个动作   
  132.                             status[0] = SECOND_ATTACK;//开始进行第二段攻击   
  133.                             frames[0] = 0;//帧数归零www.linuxidc.com   
  134.                         }  
  135.                         else{  
  136.                             next_action[0] = SECOND_ATTACK;//保存下一个动作,等待之前的动作完成   
  137.                         }  
  138.                     }  
  139.                     else if(temp_status[0] == SECOND_ATTACK || status[0] == SECOND_ATTACK)//如果之前处于第二段攻击状态下   
  140.                     {  
  141.                         status[0] = SECOND_ATTACK;  
  142.                         if(can_use_next[0] == 0 && second_attack_complete[0] == 0){//如果可以使用下一个动作   
  143.                             status[0] = THIRD_ATTACK;//开始进行第二段攻击   
  144.                             frames[0] = 0;//帧数归零   
  145.                         }  
  146.                         else{  
  147.                             next_action[0] = THIRD_ATTACK;//保存下一个动作,等待之前的动作完成   
  148.                         }  
  149.                     }  
  150.                     else if(temp_status[0] == THIRD_ATTACK || status[0] == THIRD_ATTACK)//如果之前处于第二段攻击状态下   
  151.                     {  
  152.                         status[0] = THIRD_ATTACK;  
  153.                         if(can_use_next[0] == 0 && third_attack_complete[0] == 0){//如果可以使用下一个动作   
  154.                             status[0] = FIRST_ATTACK;//开始进行第一段攻击   
  155.                             frames[0] = 0;//帧数归零   
  156.                         }  
  157.                         else{  
  158.                             //next_action = FIRST_ATTACK;//保存下一个动作,等待之前的动作完成   
  159.                         }  
  160.                     }  
  161.                     else{  
  162.                         status[0] = FIRST_ATTACK;  
  163.                         frames[0] = 0;  
  164.                     }  
  165.                 }  
  166.   
  167.                 else {  
  168.                     status[0] = FIRST_ATTACK;  
  169.                     if(temp_status[0] != FIRST_ATTACK) frames[0] = 0;  
  170.                 }  
  171.                 speed[0] = 1;  
  172.                 count = 0;  
  173.                 temp_key = key;//保存按键的键值   
  174.             }  
  175.             else if((key == "k" || key == "K") && can_use_next[0] == 0){//如果按的是k键,并且能使用下一个动作   
  176.                 status[0] = JUMP;  
  177.                 count = 0;  
  178.                 jump_attack_num = 0;  
  179.                 frames[0] = 0;  
  180.             }  
  181.             else if(key == "U" || key == "u"){  
  182.                 if(status[0] == DOWN_Y_ATTACK || status[0] == BLOCK || temp_key == KEY_DOWN || temp_key == "s" || temp_key == "S"){  
  183.                     speed[0] = 1;  
  184.                     status[0] = DOWN_Y_ATTACK;  
  185.                     if(temp_status[0] != DOWN_Y_ATTACK) frames[0] = 0;//帧数归零   
  186.                 }  
  187.                 else if(can_use_next[0] == 0){//如果可以使用下一个动作   
  188.                     status[0] = Y_ATTACK;//开始进行第二段攻击   
  189.                     frames[0] = 0;//帧数归零   
  190.                 }  
  191.                 else{  
  192.                     next_action[0] = Y_ATTACK;//保存下一个动作,等待之前的动作完成   
  193.                 }  
  194.                 count = 0;  
  195.                 temp_key = key;  
  196.             }  
  197.             else if(key == "L" || key == "l"){   
  198.                 if(can_use_next[0] == 0){//如果可以使用下一个动作   
  199.                     status[0] = DASH;  
  200.                     frames[0] = 0;//帧数归零   
  201.                 }  
  202.                 else{  
  203.                     next_action[0] = DASH;//保存下一个动作,等待之前的动作完成   
  204.                 }  
  205.                 need_move[0] = 1;//需要移动   
  206.             }  
  207.         }  
  208.         else if(count > 5 && can_use_next[0] == 0) {//如果在计数大于5时还没有接收到按键输入,并且可以使用下一个动作   
  209.             status[0] = STANCE;//状态改为站立   
  210.             speed[0] = 1;  
  211.             temp_key = 0;  
  212.             count = 0;//计数清零www.linuxidc.com   
  213.         }  
  214.         if_need_move(0,count);//判断是否需要移动   
  215.         if(man_id == ICHIGO) ichigo(0);//使用黑崎一护的动作图形   
  216.         ichigo(1);  
  217.         next_frames();//更新到下一个帧   
  218.         write_to_fb(screen[0],screen[1],screen[2]);//显示图形   
  219.         //usleep(10000);//之前用过usleep(30000),牺牲帧的刷新速度,获得按键响应效果的提升,我认为这不值   
  220.         count++;//计数自增   
  221.     }  
  222.     //释放背景图占用的内存   
  223.     free(game_map[0]);  
  224.     free(game_map[1]);  
  225.     free(game_map[2]);  
  226.     free(game_map);  
  227.     return 0;  
  228. }  
  229. int main()  
  230. {  
  231.     init_game();//初始化游戏,获取屏幕尺寸,分配内存   
  232.     game_boot();//显示游戏启动画面   
  233.     main_menu();//显示游戏主菜单   
  234.     system("stty echo");  
  235.     return 0;  
  236. }