在Linux系统下,不要以为C语言就只能写那种只有字符的控制台程序,别忘了,Linux系统有FrameBuffer(帧缓冲),只要显示器是彩色的,并且是linux系统的,就可以用C语言代码通过读写FrameBuffer里的数据在屏幕上绘制图形;
图形嘛,有png库,用它的函数解码图片文件,得到图片数组,共有red、green、blue、alpha四种数组,alpha用于图片之间的组合,最终将RGB数组输出到FrameBuffer就能显示了;
想要动态图形效果,自己用算法实现。
目前,我正在为自己的嵌入式设备开发一个游戏,图形素材来源于互联网,游戏截图如下图所示:
如上图所示,主菜单中的红色光标有闪烁效果,游戏画面切换有淡入淡出效果。
下面有源码,现在使用的按键控制方法的效果不理想。
linux系统环境,按键判断,使用了getch()和kbhit()函数,函数是模拟实现的。
为了判断按键是否为按住状态,我使用一个变量count来计数,每循环一次自增,也就是按键检测延迟的最大时间,超过了这段时间,如果getch()函数没有再次返回接受到同样的按键的键值,那么就判断为这个按键已经释放,否则,该按键处于按住状态,并继续显示之前的动作。
还有图形显示,目前没掌握局部刷新的技术,游戏显示的每一帧图形都是全屏刷新的,效率低,每秒大概刷新4帧。
源码只提供部分:
[cpp]
voidnext_frames(void)
{//更新到下一帧
frames[0] += speed[0];//玩家1的
frames[1] += speed[1];//AI的
}
voidinit_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;//不需要显示冲击波
}
intplay_game(void)
{
unsignedchar**game_map;//游戏地图
intmap_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;
}
elseif((key =='d'|| key =='D'|| key == KEY_RIGHT) && status[0] != JUMP_ATTACK){//如果按的是a键或者左键
if((temp_key =='d'|| temp_key =='D'|| temp_key == KEY_RIGHT)&& count
next_action[0] = WALK;//下一个动作还是行走
}
elseif(can_use_next[0] == 0){//如果之前没有按过a键或者左键,并且,可以使用下一个动作
if(temp_status[0] != JUMP) {//如果之前不是跳跃状态
status[0] = WALK;
if(temp_status[0] != WALK) frames[0] = 0;//如果之前不是行走状态
elseif(temp_status[0] == WALK){//如果之前是行走状态
next_action[0] = WALK;//下一个动作还是行走
}
}
elseneed_move[0] = 1;//否则,需要移动
}
direction[0] = IS_RIGHT;
need_move[0] = 1;
temp_key = key;
count = 0;//计数清零
}
elseif((key =='a'|| key =='A'|| key == KEY_LEFT) && status[0] != JUMP_ATTACK){//如果按的是a键或者左键
if((temp_key =='a'|| temp_key =='A'|| temp_key == KEY_LEFT)&& count
next_action[0] = WALK;//下一个动作还是行走
}
elseif(can_use_next[0] == 0){//如果之前没有按过a键或者左键,并且,可以使用下一个动作
if(temp_status[0] != JUMP) {//如果之前不是跳跃状态
status[0] = WALK;
if(temp_status[0] != WALK) frames[0] = 0;//如果之前不是行走状态
elseif(temp_status[0] == WALK){//如果之前是行走状态
next_action[0] = WALK;//下一个动作还是行走
}
}
elseneed_move[0] = 1;//否则,需要移动
}
direction[0] = IS_LEFT;
need_move[0] = 1;
temp_key = key;
count = 0;//计数清零
}
elseif(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;//帧数归零www.linuxidc.com
speed[0] = 1;
}
}
temp_key = key;//保存按键的键值
count = 0;//计数归零
}
elseif(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;
}
}
elseif(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;//帧数归零
}
elseif(temp_key == KEY_UP || temp_key =='w'|| temp_key =='W'){
if(can_use_next[0] == 0){//如果可以使用下一个动作
status[0] = UP_ATTACK;
frames[0] = 0;//帧数归零
}
}
elseif((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;//帧数归零www.linuxidc.com
}
else{
next_action[0] = SECOND_ATTACK;//保存下一个动作,等待之前的动作完成
}
}
elseif(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;//保存下一个动作,等待之前的动作完成
}
}
elseif(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;//保存按键的键值
}
elseif((key =='k'|| key =='K') && can_use_next[0] == 0){//如果按的是k键,并且能使用下一个动作
status[0] = JUMP;
count = 0;
jump_attack_num = 0;
frames[0] = 0;
}
elseif(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;//帧数归零
}
elseif(can_use_next[0] == 0){//如果可以使用下一个动作
status[0] = Y_ATTACK;//开始进行第二段攻击
frames[0] = 0;//帧数归零
}
else{
next_action[0] = Y_ATTACK;//保存下一个动作,等待之前的动作完成
}
count = 0;
temp_key = key;
}
elseif(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;//需要移动
}
}
elseif(count > 5 && can_use_next[0] == 0) {//如果在计数大于5时还没有接收到按键输入,并且可以使用下一个动作
status[0] = STANCE;//状态改为站立
speed[0] = 1;
temp_key = 0;
count = 0;//计数清零www.linuxidc.com
}
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);
return0;
}
intmain()
{
init_game();//初始化游戏,获取屏幕尺寸,分配内存
game_boot();//显示游戏启动画面
main_menu();//显示游戏主菜单
system("stty echo");
return0;
}