在Linux系统下,不要以为C语言就只能写那种只有字符的控制台程序,别忘了,Linux系统有FrameBuffer(帧缓冲),只要显示器是彩色的,并且是linux系统的,就可以用C语言代码通过读写FrameBuffer里的数据在屏幕上绘制图形;
图形嘛,有png库,用它的函数解码图片文件,得到图片数组,共有red、green、blue、alpha四种数组,alpha用于图片之间的组合,最终将RGB数组输出到FrameBuffer就能显示了;
想要动态图形效果,自己用算法实现。
目前,我正在为自己的嵌入式设备开发一个游戏,图形素材来源于互联网,游戏截图如下图所示:
如上图所示,主菜单中的红色光标有闪烁效果,游戏画面切换有淡入淡出效果。
下面有源码,现在使用的按键控制方法的效果不理想。
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; }