Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103554798
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: C/C++

2008-05-18 22:59:01

  来源:


 四、 一个完整的Allegro实例

  本教程将使用Kee-Yip Chan的SnookerClone演示程序,它是基于James Lohr的另一个具有相同名字的演示程序。图1显示了演示程序的基本屏幕快照。


图1.Kee-Yip Chan的"SnookerClone"演示程序

  这个工程向你展示了许多不同的Allegro技术,包括动画,键盘输入和鼠标输入,碰撞和游戏物理知识(例如重力)。它利用了三个主要的元素:一个有8个扶手的旋转的车轮,一个用箭头键来控制的大红球,还有一些从顶部往下坠落的蓝球。车轮以接触方式推动红球,而当红球碰上蓝球时,它们之间相互影响。

  下列是完整的Allegro演示程序的代码:

1 #include
2 vector g_points; //aka球上点的列表
3 vector g_joints; //物理对象列表,如车轮和缓冲器
4 kVec g_accControl;
6 int main(void)
7 {
8  allegro_init(); // 初始化allegro.
9  install_keyboard(); // 启动键盘.
10 install_mouse(); // 启动鼠标.
11 install_timer(); //过程show_mouse()所需要;
13 // 创建一个800x600的非全屏窗口.
14 set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0);
16 set_window_title("Kee-Yip Chan’s Snooker Clone");
17 text_mode(-1); // 文本将被画在透明的背景之上
19 BITMAP* buffer = create_bitmap(SCREEN_W, SCREEN_H);
  //创建一张位图用于双缓冲.
21  // 初始化数据.
22 create_joints(g_joints); //注册车轮、地板和缓冲器的硬编码的屏幕位置
25 // 创建顶点以组成aka球: 玩家所用球和三个蓝球
26 // 的位置, , 大小和质量.
27 g_points.push_back(Point(kVec(100, 300),kVec(0, 0),16, 10));
// 玩家.
28 g_points.push_back(Point(kVec(50, 40), kVec(0, 0),12, 5));
// 中等的球.
29 g_points.push_back(Point(kVec(80, 40), kVec(0, 0) 12, 5));
//中等的球.
30 g_points.push_back(Point(kVec(110, 40),kVec(0, 0),6, 1));
// 小球.
32 //主循环,在按ESC键后退出
33 while(!key[KEY_ESC]) { //检查输入.
34  if(key[KEY_UP])
35   g_accControl.y = -0.07; //Jet pack.向上加速
36  if(key[KEY_LEFT])
37   g_accControl.x = -0.07; //左走.向左加速
38  if(key[KEY_RIGHT])
39   g_accControl.x = 0.07; //右走.向右加速
41   static bool leftMousePressed = false,
    rightMousePressed = false;
42  if(mouse_b & 1) { //鼠标左键按下
43   if(!leftMousePressed){
44    leftMousePressed = true; // 创建一个新球.
45    g_points.push_back(Point(kVec(mouse_x, mouse_y),kVec(0, 0), 12, 5));
46   }
47  }
48  if(!(mouse_b & 1))
49  //保证不重复鼠标按键
50  //否则,就会出现许多的新球
51  leftMousePressed = false;
52  if(mouse_b & 2) { //鼠标右键按下
53   if(!rightMousePressed){
54    rightMousePressed = true; // 创建一个新球
55    g_points.push_back(Point(kVec(mouse_x, mouse_y),kVec(0, 0), 6, 1));
56   }
57  }
58  if(!(mouse_b & 2))
59   //保证不重复鼠标按键
60   //否则,就会出现许多的新球.
61   rightMousePressed = false;
63   doPhysics();
65   // 着色:如果我们能再次使用缓冲区,则清除它;
    //否则,旧图像将滞留显示
66   //用白色进行清除.
67   clear_to_color(buffer, makecol(255, 255, 255));
68   for(unsigned i = 0; i < g_points.size(); i++) {
     //画点.
69    //画一个实心球
70    circlefill(buffer, //画向缓冲区
71    g_points[i].position.x,g_points[i].position.y,// aka 球的点的位置
72    g_points[i].size, // 半径.
73    (i == 0) ? makecol(255, 0, 0) : makecol(0, 0, 255)); //红色如果是玩家;否则为
75    // 画一个轮廓球.
76    circle(buffer, //画向缓冲区
77     g_points[i].position.x,g_points[i].position.y, // aka 球的中心点的位置.
78     g_points[i].size, // 半径.
79     makecol(0, 0, 0)); //红色如果是玩家;否则为蓝色.
81   }
83   // 画接合点
84   for (unsigned i = 0; i < g_joints.size(); i++)
85    line(buffer, //画向缓冲区
86     g_joints[i].p1.x, g_joints[i].p1.y, // 点 1.
87     g_joints[i].p2.x, g_joints[i].p2.y, // 点 2.
88     makecol(0, 0, 0)); // 黑颜色.
89    );
91   // 打印指令.
92   textout(buffer, font, "Left Mouse Button - new big ball Right Mouse Button - new small ball",
93     125, 1, makecol(0, 0, 0));
95   textout(buffer, font, "Arrow Keys - move red ball",
96     300, 592, makecol(0, 0, 0));
98   show_mouse(buffer); // 画鼠标光标.
100   draw_sprite(screen, buffer, 0, 0);// 把缓冲区中的数据画向屏幕.
101  } // while循环结束
103  return 0;
105 }END_OF_MAIN();

  33-101行包括了典型的游戏编程循环模式。游戏继续进行直到玩家按下ESC键退出为止。34-39行支持同时进行的键盘输入,因为你可以按下向上和向左箭头键来获取粗略的斜向运动。

  在41-61行,鼠标动作被捕获到全局变量mouse_b(用于按钮),mouse_x和mouse_y。如果你一直在使用一滚轮鼠标,你还可以使用变量mouse_z。我们对反向弹跳逻辑进行了一点硬编码以确保每次鼠标按下事件只有一个球下落。

  第63行调用doPhysics(),其目的是旋转车轮的线段,更新球位置,检测球碰撞和适当地改变它们的方向矢量。这个模块(350行的数学代码)有点深入了些,但它确实是一个一流的实现,值得你深入研究。

  余下的代码,65-101行,开始着色,在典型的示例程序中这属于常规实现部分。这里的着色用典型的双缓冲区技术,下一次屏幕变化被出来并进行脱屏绘制并在最的一毫秒进行缓冲交换(第100行)。这确保了视觉的连续性又减少了烦人的闪烁-对象看上去是随机地绘制的。在着色代码部分,对line()和circlefill()的调用是相当直接的:circlefill()以x,y,半径和填充颜色作为参数。

  textout_ex()函数的功能稍强于textout()(示于92-96行),允许你指定前景和背景颜色。Allegro提供例程以直接从GRX格式.fnt文件,8x8或8x16 BIOS格式.fnt文件,位图图象以及数据文件格式中装入字体。作为选择,你能导入一种大的Unicode字体,这可以通过写一个.txt脚本-它为每一范围的字符指定相应的不同的源文件-来实现。如果你想要支持TrueType,那么你需要AllegroTTF或相同功能的插件。
最后,在第100行的draw_sprite()实现一个覆盖性复制新生成的位图到第14行创建的屏幕对象上。覆盖性复制意指只有非透明的颜色像素被复制。在本例中,我确信它已被退化成一个"blit"(块复制)转储。

  五、 Allegro的音频

  这个snooker演示程序只涉及到了一些最基本的图形和I/O函数,但是并没有用到Allegro的音频开发包。该包中的MIDI混频,音响效果和录音API,其效果达到或超过几乎每一个我所见过的的声音库。Allegro音频应用软件大量存在,包括WinCab-一个MP3和OGG Vorbis音乐唱片机,还有LouTronic Rhythm Box-一个鼓声生成合成器,它具有可全面融合到一起的snare鼓,低音鼓和hi hat的效果。下面我们简单地回顾Allegro音频API的一小部分。

  每一个使用音频的程序都应该调用reserve_voices()来指定数字和MIDI声音驱动程序分别使用的声音的数目。接下去,你能控制这些音频轨道的混合.

  你可以非常容易地象下面这样插入一个音轨:

MIDI *midFile = load_midi("myfile.mid’);
play_midi(midFile, TRUE);//连续循环

  对于更复杂的需要,你可以安装三个钩子函数之一,它们可以使你拦截MIDI玩家事件。如果被设置为非NULL,这些例程将在每次MIDI消息,元事件和系统独占的数据块中被分别调用。

  Allegro的数字音频系统被设计为从最基本的配置到可高度扩展的。你能容易安装读取器和写入器来处理新的或者不同的音频文件类型, 例如:

register_sample_file_type("mp3",load_mp3,NULL);//安装MP3读取器

  当正播放数字音频时,你可以随时编辑它。下列代码改变将在播放一个样本参数时改变该样本(用于循环播放的声音):

void adjust_sample(const SAMPLE *spl, int vol, int pan, int freq, int loop);

  你能改变音量,平移音频数据并清除循环,在下次执行到循环末尾时,这将停止该样本。如果在播放相同样本的好几个副本,这会调整它遇到的第一个副本。如果该样本没有播放,对它没有任何影响
阅读(858) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~