
使用cairo库进行游戏画面绘制
界面分为游戏人物、路、障碍物,分别定义成结构体,记录坐标,状态,颜色等信息
游戏人物结构体
对于画面的绘制,没有使用多线程,而是使用单次和循环计时器进行绘制
使用单次定时器绘制开始界面
gboolean start_window(GtkWidget *widget){
cairo_t *cr;
cr = gdk_cairo_create (widget->window);
//background
cairo_set_source_rgb(cr,0.6,0,0.7);
cairo_move_to(cr,0, 0);
cairo_line_to(cr,X_WIDTH,0);
cairo_line_to(cr,X_WIDTH,X_HEIGHT);
cairo_line_to(cr,0,X_HEIGHT);
cairo_stroke_preserve(cr);//save line path,cairo_fill can fill
cairo_fill(cr);
//game reference
//char info[1000] = {'\0'};
//sprintf(info,"Ctrl to start\nspace to jump\n p to pause/continue\n");
cairo_set_source_rgb(cr,0.0,0.7,0.3);
cairo_set_font_size(cr,24);
cairo_move_to(cr,30, 40);
cairo_show_text(cr,"Ctrl to start");
cairo_move_to(cr,30, 90);
cairo_show_text(cr,"Space to jump");
cairo_move_to(cr,20, 130);
cairo_show_text(cr,"p to pause/continue");
cairo_stroke(cr);
cairo_destroy(cr);
//run only once
return false;
}
游戏分数、时间、速度等信息的绘制
gint score;//游戏分数
gint timeElapse;//游戏时间
gint gameSpeed;//游戏速度
//time
char strTime[10] = {'\0'};
cairo_set_source_rgb(cr,0.4,0.4,0.7);
cairo_set_font_size(cr,14);
cairo_move_to(cr,20, 30);
sprintf(strTime,"Time:%d",timeElapse);
cairo_show_text(cr,strTime);
cairo_stroke(cr);
使用定时器实时移动障碍物坐标
gboolean move_obstacle(){
for(int i = 0;i<5;i++){
obstacles[i].x -= gameSpeed;
//if obstacle go end,move it as farest obstacle
if(obstacles[i].x<0){
int xMax = 0;
for(int j =0;j<5;j++){
if(obstacles[j].x>xMax){
xMax = obstacles[j].x;
}
}
obstacles[i].x = xMax + rand()%50 +100;
obstacles[i].r = (float)(rand()%100)/100;
obstacles[i].g = (float)(rand()%100)/100;
obstacles[i].b = (float)(rand()%100)/100;
score++;
}
}
if(hit_check()){
game_over();
}
return true;
}
通过按键Ctrl开始游戏,space跳跃,P暂停/继续游戏
void my_key_press(GtkWidget* widget,GdkEventKey *event,gpointer data){
switch(event->keyval){
case GDK_KEY_Control_L:
case GDK_KEY_Control_R:
game_start();
break;
case GDK_KEY_space:
g_thread_create(man_jump,(void*)&man_0,FALSE,NULL);
break;
case GDK_KEY_p:
case GDK_KEY_P:
game_pause_continue();
break;
}
}
对于按键键值,在gdkkeysyms.h有详细定义
#define GDK_Delete 0xffff
#define GDK_Multi_key 0xff20
#define GDK_Codeinput 0xff37
#define GDK_SingleCandidate 0xff3c
#define GDK_MultipleCandidate 0xff3d
#define GDK_PreviousCandidate 0xff3e
#define GDK_Kanji 0xff21
#define GDK_Muhenkan 0xff22
#define GDK_Henkan_Mode 0xff23
#define GDK_Henkan 0xff23
#define GDK_Romaji 0xff24
#define GDK_Hiragana 0xff25
#define GDK_Katakana 0xff26
#define GDK_Hiragana_Katakana 0xff27
...
编译参数的设置:
使用以下命令获得g++编译gtk程序的参数
另外,使用到g_thread_init函数,因此额外的链接库参数
游戏有闪烁,使用双缓冲技术可以消除闪烁
源码
common.h
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <stdio.h>
//time
#include <time.h>
//rand
#include <stdlib.h>
#include <math.h>
typedef enum _MAN_STATUS{RUN,JUMP}ManStatus;//人物状态
typedef enum _GAME_STATUS{GOING,PAUSE,STOP}GameStatus;//游戏状态
//人物结构体
typedef struct _MAN{
gint x;
gint y;
ManStatus status;
}Man;
//路结构体
typedef struct _ROAD{
gint startPointX;
gint startPointY;
gint endPointX;
gint endPointY;
gboolean valiable;
}Road;
//障碍物结构体
typedef struct _OBSTACLE{
gint x;
float r;
float g;
float b;
}Obstacle;
void* man_jump(void *man);
void my_key_press(GtkWidget* widget,GdkEventKey *event,gpointer data);
gboolean show_graph(GtkWidget *widget);
void init_road();
gboolean move_road();
void init_obstacle();
gboolean move_obstacle();
gboolean set_time();
gboolean set_game_speed();
void game_start();
void game_pause_continue();
void game_over();
gboolean start_window(GtkWidget *widget);
gboolean hit_check();
//定义屏幕大小
#define X_WIDTH 300
#define X_HEIGHT 200
//最大游戏速度
#define MAX_GAME_SPEED 5
extern Man man_0;
extern Road roads[10];
extern Obstacle obstacles[5];
extern gint score;
extern gint gameSpeed;
extern gint timeElapse;
extern GameStatus gameStatus;
extern guint timer_0,timer_1,timer_2,timer_score,timer_game_speed;
extern GtkWidget *window;
q.cpp
#include "common.h"
//win32下的延时函数支持
#ifdef WIN32
#include <windows.h>
#define SLEEP_TIME_1 Sleep(1000*0.003);
#define SLEEP_TIME_2 Sleep(1000*0.001);
#else
#define SLEEP_TIME_1 usleep(1000*1000*0.003);
#define SLEEP_TIME_2 usleep(1000*1000*0.001);
#endif
//键值定义
#ifndef GDK_KEY_space
#define GDK_KEY_space GDK_space
#endif
#ifndef GDK_KEY_P
#define GDK_KEY_P GDK_P
#endif
#ifndef GDK_KEY_p
#define GDK_KEY_p GDK_p
#endif
#ifndef GDK_KEY_Control_L
#define GDK_KEY_Control_L GDK_Control_L
#endif
#ifndef GDK_KEY_Control_R
#define GDK_KEY_Control_R GDK_Control_R
#endif
//人物跳跃函数
void* man_jump(void *man){
Man *pMan = (Man*)man;
if(pMan->status == JUMP){
return NULL;
}
pMan->status = JUMP;
for(int i = 0;i<50;i++){
pMan->y -= 1;
SLEEP_TIME_1
}
for(int i = 0;i<50;i++){
pMan->y += 1;
SLEEP_TIME_2
}
pMan->status = RUN;
}
//按键检测
void my_key_press(GtkWidget* widget,GdkEventKey *event,gpointer data){
switch(event->keyval){
case GDK_KEY_Control_L:
case GDK_KEY_Control_R:
game_start();
break;
case GDK_KEY_space:
g_thread_create(man_jump,(void*)&man_0,FALSE,NULL);
break;
case GDK_KEY_p:
case GDK_KEY_P:
game_pause_continue();
break;
}
}
//游戏画面绘制
gboolean show_graph(GtkWidget *widget){
cairo_t *cr;
cr = gdk_cairo_create (widget->window);
//background
cairo_set_source_rgb(cr,0,0,0);
cairo_move_to(cr,0, 0);
cairo_line_to(cr,X_WIDTH,0);
cairo_line_to(cr,X_WIDTH,X_HEIGHT);
cairo_line_to(cr,0,X_HEIGHT);
cairo_stroke_preserve(cr);
cairo_fill(cr);
//road
cairo_set_source_rgb(cr,0.6,0.4,0);
cairo_move_to(cr,0, 110);
cairo_line_to(cr,X_WIDTH,110);
cairo_stroke(cr);
for(int i = 0;i<10;i++){
cairo_set_source_rgb(cr,0.2,0.4,0.6);
cairo_move_to(cr,roads[i].startPointX, roads[i].startPointY);
cairo_line_to(cr,roads[i].endPointX,roads[i].endPointY);
cairo_stroke(cr);
}
//obstacle
for(int i = 0;i<5;i++){
cairo_set_source_rgb(cr,obstacles[i].r,obstacles[i].g,obstacles[i].b);
cairo_move_to(cr,obstacles[i].x-5, 110);
cairo_line_to(cr,obstacles[i].x-5,90);
cairo_line_to(cr,obstacles[i].x+10,90);
cairo_line_to(cr,obstacles[i].x+10,110);
cairo_line_to(cr,obstacles[i].x-5,110);
cairo_stroke_preserve(cr);
cairo_fill(cr);
//printf("%d \n",obstacles[i].x);
}
//score
char strScore[10] = {'\0'};
cairo_set_source_rgb(cr,0.2,0.9,0.4);
cairo_set_font_size(cr,14);
cairo_move_to(cr,X_WIDTH-70, 30);
sprintf(strScore,"score:%d",score);
cairo_show_text(cr,strScore);
cairo_stroke(cr);
//time
char strTime[10] = {'\0'};
cairo_set_source_rgb(cr,0.4,0.4,0.7);
cairo_set_font_size(cr,14);
cairo_move_to(cr,20, 30);
sprintf(strTime,"Time:%d",timeElapse);
cairo_show_text(cr,strTime);
cairo_stroke(cr);
//game speed
char strSpeed[10] = {'\0'};
cairo_set_source_rgb(cr,0.7,0.4,0.6);
cairo_set_font_size(cr,14);
cairo_move_to(cr,110, 30);
sprintf(strSpeed,"speed:%d",gameSpeed-1);
cairo_show_text(cr,strSpeed);
cairo_stroke(cr);
//man
cairo_set_line_width(cr,0.1);
cairo_set_source_rgb(cr,1,1,0);
cairo_arc(cr,man_0.x, man_0.y,6,0,360);
cairo_stroke_preserve(cr);
cairo_fill(cr);
cairo_destroy(cr);
return true;
}
void init_road(){
for(int i=0;i<10;i++){
roads[i].startPointY = rand()%2?120:130;
roads[i].startPointX = rand()%X_WIDTH;
roads[i].endPointX = roads[i].startPointX + rand()%2+10;
roads[i].endPointY = roads[i].startPointY;
}
}
//路移动
gboolean move_road(){
for(int i = 0;i<10;i++){
roads[i].startPointX -= gameSpeed;
roads[i].endPointX -= gameSpeed;
if(roads[i].endPointX<0){
roads[i].startPointY = rand()%2?120:130;
roads[i].startPointX = X_WIDTH;
roads[i].endPointX = roads[i].startPointX + rand()%2+10;
roads[i].endPointY = roads[i].startPointY;
}
}
return true;
}
void init_obstacle(){
obstacles[0].x = X_WIDTH+20;
obstacles[0].r = (float)(rand()%100)/100;
obstacles[0].g = (float)(rand()%100)/100;
obstacles[0].b = (float)(rand()%100)/100;
for(int i = 1;i<5;i++){
obstacles[i].x = obstacles[i-1].x + rand()%50 +100;
obstacles[i].r = (float)(rand()%100)/100;
obstacles[i].g = (float)(rand()%100)/100;
obstacles[i].b = (float)(rand()%100)/100;
}
}
//障碍物移动
gboolean move_obstacle(){
for(int i = 0;i<5;i++){
obstacles[i].x -= gameSpeed;
if(obstacles[i].x<0){
int xMax = 0;
for(int j =0;j<5;j++){
if(obstacles[j].x>xMax){
xMax = obstacles[j].x;
}
}
obstacles[i].x = xMax + rand()%50 +100;
obstacles[i].r = (float)(rand()%100)/100;
obstacles[i].g = (float)(rand()%100)/100;
obstacles[i].b = (float)(rand()%100)/100;
score++;
}
}
if(hit_check()){
game_over();
}
return true;
}
gboolean set_time(){
timeElapse += 1;
return true;
}
gboolean set_game_speed(){
if(gameSpeed < MAX_GAME_SPEED){
gameSpeed++;
}
return true;
}
//游戏开始
void game_start(){
if(gameStatus != STOP){
return;
}
gameStatus = GOING;
score = 0;
timeElapse = 0;
gameSpeed = 2;
man_0.x = 30;
man_0.y = 100;
init_road();
init_obstacle();
timer_0 = g_timeout_add(30,(GSourceFunc)show_graph,window);
timer_1 = g_timeout_add(30,(GSourceFunc)move_road,NULL);
timer_2 = g_timeout_add(30,(GSourceFunc)move_obstacle,NULL);
timer_score = g_timeout_add(1000,(GSourceFunc)set_time,NULL);
timer_game_speed = g_timeout_add(20000,(GSourceFunc)set_game_speed,NULL);
}
//暂停/继续
void game_pause_continue(){
if(gameStatus == PAUSE){
gameStatus = GOING;
timer_0 = g_timeout_add(30,(GSourceFunc)show_graph,window);
timer_1 = g_timeout_add(30,(GSourceFunc)move_road,NULL);
timer_2 = g_timeout_add(30,(GSourceFunc)move_obstacle,NULL);
timer_score = g_timeout_add(1000,(GSourceFunc)set_time,NULL);
timer_game_speed = g_timeout_add(20000,(GSourceFunc)set_game_speed,NULL);
}else if(gameStatus == GOING){
gameStatus = PAUSE;
g_source_remove(timer_0);
g_source_remove(timer_1);
g_source_remove(timer_2);
g_source_remove(timer_score);
g_source_remove(timer_game_speed);
}
}
//游戏结束
void game_over(){
gameStatus = STOP;
g_source_remove(timer_0);
g_source_remove(timer_1);
g_source_remove(timer_2);
g_source_remove(timer_score);
g_source_remove(timer_game_speed);
//
cairo_t *crr;
crr = gdk_cairo_create (window->window);
cairo_set_source_rgb(crr,0.5,0.7,0.9);
cairo_set_font_size(crr,24);
cairo_move_to(crr,50, 60);
cairo_show_text(crr,"game over");
cairo_stroke(crr);
cairo_destroy(crr);
}
//开始界面
gboolean start_window(GtkWidget *widget){
cairo_t *cr;
cr = gdk_cairo_create (widget->window);
//background
cairo_set_source_rgb(cr,0.6,0,0.7);
cairo_move_to(cr,0, 0);
cairo_line_to(cr,X_WIDTH,0);
cairo_line_to(cr,X_WIDTH,X_HEIGHT);
cairo_line_to(cr,0,X_HEIGHT);
cairo_stroke_preserve(cr);
cairo_fill(cr);
//game reference
//char info[1000] = {'\0'};
//sprintf(info,"Ctrl to start\nspace to jump\n p to pause/continue\n");
cairo_set_source_rgb(cr,0.0,0.7,0.3);
cairo_set_font_size(cr,24);
cairo_move_to(cr,30, 40);
cairo_show_text(cr,"Ctrl to start");
cairo_move_to(cr,30, 90);
cairo_show_text(cr,"Space to jump");
cairo_move_to(cr,20, 130);
cairo_show_text(cr,"p to pause/continue");
cairo_stroke(cr);
cairo_destroy(cr);
//run only once
return false;
}
//碰撞检测
gboolean hit_check(){
gint nearestObstacleID;
gint nearestDistance = 65535;
for(int i=0;i<5;i++){
if(abs(man_0.x-obstacles[i].x)<nearestDistance){
nearestObstacleID = i;
nearestDistance = abs(man_0.x-obstacles[i].x);
}
}
//printf("%d\n",nearestObstacleID);
//检测碰撞最近的障碍物
if((man_0.x-obstacles[nearestObstacleID].x)*(man_0.x-obstacles[nearestObstacleID].x)+(man_0.y-100)*(man_0.y-100)<100){
return true;
}else{
//printf("%d %d %d %d\n",man_0.x,obstacles[nearestObstacleID].x,man_0.y,100);
return false;
}
}
main.cpp
#include "common.h"
Man man_0;//游戏人物
Road roads[10];//路
Obstacle obstacles[5];//障碍物
GameStatus gameStatus;
gint score;//游戏分数
gint timeElapse;//游戏时间
gint gameSpeed;//游戏速度
guint timer_0,timer_1,timer_2,timer_score,timer_game_speed;//定时器
GtkWidget *window;
int main(int argc,char* argv[]){
srand((unsigned int)time(0));
gtk_init(&argc,&argv);
if(!g_thread_supported())
g_thread_init(NULL);//初始线程
gdk_threads_init();
window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window),"Jumpman");
gtk_window_set_default_size(GTK_WINDOW(window),X_WIDTH,X_HEIGHT);
g_signal_connect(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit),NULL);
//关联按键回调函数
g_signal_connect(G_OBJECT(window),"key-press-event",G_CALLBACK(my_key_press),NULL);
gtk_widget_show_all(window);
gtk_widget_set_app_paintable(window,true);
//启动游戏
g_timeout_add(30,(GSourceFunc)start_window,window);
game_over();
gdk_threads_enter();
gtk_main();
gdk_threads_leave();
return 0;
}