Allegro学习笔记二十三

 

现在开始进入计算机图形学的领域~在这里,我们将重新认识Allegro--一个图像引擎.从这个例子开始,将是一段艰难的旅程!所以,别掉队了~

http://wiki.allegro.cc/AllegroExamples 以上是英文例子站点。

Shawn Hargreaves,allegro的作者

目录: 1 Allegro 例子

1.1 exhello
1.2 exmem
1.3 expat
1.4 expal
1.5 exflame
1.6 exbuf
1.7 exflip
1.8 exfixed
1.9 exfont
1.10exmouse
1.11extimer
1.12exkeys
1.16exgui
1.17excustoms
1.21exconfig
1.23exsprite

/*
 *    Example program for the Allegro library, by Grzegorz Ludorowski.
 *
 *    这个例子展示了如何使用datafile,以及一些绘制精灵的方法。
 *    并且通过消除闪烁来完美展现画面.
 *
 *    为什么animate()函数会那样编写?
 *    可能你已经知道了,读取显存要比读取内存慢的多,
 *    所以明智的做法是尽可能少的使用VRAM blits.
 *    在(存储在VRAM中的)“屏幕”上绘制精灵并用一个背景来清理它
 *    是很慢的.这个例子使用了一种不同的方法,但是要快的多,
 *    只不过它需要申请一些额外的内存.
 *
 *    首先位图指针被清理(这是内存位图),然后将精灵画在上面,
 *    当绘制完成, 这个指针将立即被复制到屏幕上.
 *    最后的结果就是,我们仅使用了VRAM blit一步,省略了清理、blitting背景、
 *    在上面绘制精灵(3步).当你不得不重新装载背景的时候,这也是一种好方法.
 *    当然,这种方法完全消除了闪烁效果.
 *
 *    当使用很大的位图空间 (ie. 800x600的背景) 并且在上面绘制时,
 *    明智的做法是在内存中创建一个背景的副本,然后重新装载这个
 *    虚拟背景.在SVGA模式下,使用VRAM来做这个事,它很可能做例行测试,
 *    以便确定哪里有位置来放置这个位图空间,我想我不需要提醒那会
 *    慢成什么鬼样子.
 *
 *    注意,在现代系统中,上面的情况也不一定正确,
 *    通常你会把VRAM中的特效放到超高速缓存中,
 *    这样只需要做VRAM->VRAM的blits,从而获得极佳的性能
 *    这样的话,根本就不需要RAM->VRAM的转换了.
 *    通常这样的转换(VRAM->VRAM)会很好的以并行的方式在GPU中运行,
 *    因此实际上完全不占用CPU的时间
 *    例子exaccel就是这样的情况.
 */

#include <math.h>
#include <allegro.h>
#include "running.h"

 

#define FRAMES_PER_SECOND 30

/* 为动画设置一个计时器 */
volatile int ticks = 0;
void ticker(void)
{
    ticks++;
}
END_OF_FUNCTION(ticker)

/* pointer to data file */
DATAFILE *running_data;

/* 桢和当前桢 */
int frame, frame_number = 0;

/* 一个指向精灵的指针 */
BITMAP *sprite_buffer;

/* 这是关于控制演示例程的 */
int next;

 

void animate(void)
{
   /* 等待动画计时器. */
   while (frame > ticks) {
       /* 空的等待. */
       rest(1);
   }

   /* 为了更完美的显示,我们应该为一个多桢的动画速度
  * 设置刷新率监视器,从而代替使用计时器.
    * 并且通过垂直扫描来同步 - 来获得完全平滑的动画
    * 但是这种方法不适合于所有的设置模式(e.g. in X11 windowed modes), 
    * 因此首先应该做一些测试,或者让用户自己来设置.
    * 这样的例子太多了 (实际上这个例子并没有使用垂直扫描同步,
    * 这里的文字只是告诉我们存在着更好的方法)
    */

   frame++;

   /* 向屏幕绘制精灵 */
   blit(sprite_buffer, screen, 0, 0, (SCREEN_W - sprite_buffer->w) / 2,
 (SCREEN_H - sprite_buffer->h) / 2, sprite_buffer->w, sprite_buffer->h);

   /* 清除精灵指针 */
   clear_bitmap(sprite_buffer);

   /* 如果有按键按下则进入下一步演示 */
   if (keypressed())
      next = TRUE;
   else
      next = FALSE;

   if (frame_number == 0)
      play_sample(running_data[SOUND_01].dat, 128, 128, 1000, FALSE);

   /* 递增桢数, 如果等于9,则将它归0 */
   if (frame_number == 9)
      frame_number = 0;
   else
      frame_number++;
}

 

int main(int argc, char *argv[])
{
   char datafile_name[256];
   int angle = 0;
   int x, y;
   int text_y;
   int color;

   if (allegro_init() != 0)
      return 1;
   install_keyboard();
   install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL);
   install_timer();
   LOCK_FUNCTION(ticker);
   LOCK_VARIABLE(ticks);
   install_int_ex(ticker, BPS_TO_TIMER(30));

   if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0) != 0) {
      if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0) {
  set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
  allegro_message("Unable to set any graphic mode/n%s/n",
    allegro_error);
  return 1;
      }
   }

   /* 读取datafile并用用户存储在其中的调色板初始化 */
   replace_filename(datafile_name, argv[0], "running.dat",
      sizeof(datafile_name));
   running_data = load_datafile(datafile_name);
   if (!running_data) {
      set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
      allegro_message("Error loading %s!/n", datafile_name);
      return 1;
   }

   /* select the palette which was loaded from the datafile */
   set_palette(running_data[PALETTE_001].dat);

   /* 创建和清理一个精灵位图, 
    * 要足够大,应该是对角线的长度,以便在旋转时不会丢失画面*/
   sprite_buffer = create_bitmap((int)(82 * sqrt(2) + 2),
      (int)(82 * sqrt(2) + 2));
   clear_bitmap(sprite_buffer);

   x = (sprite_buffer->w - 82) / 2;
   y = (sprite_buffer->h - 82) / 2;
   color = makecol(0, 80, 0);
   text_y = SCREEN_H - 10 - text_height(font);

   frame = ticks;

   /* 在屏幕上写明当前的精灵绘制方法 */
   textout_centre_ex(screen, font, "Press a key for next part...",
       SCREEN_W / 2, 10, palette_color[1], -1);
   textout_centre_ex(screen, font, "Using draw_sprite",
       SCREEN_W / 2, text_y, palette_color[15], -1);

   do {
      hline(sprite_buffer, 0, y + 82, sprite_buffer->w - 1, color);
      draw_sprite(sprite_buffer, running_data[frame_number].dat, x, y);
      animate();
   } while (!next);

   clear_keybuf();
   rectfill(screen, 0, text_y, SCREEN_W, SCREEN_H, 0);
   textout_centre_ex(screen, font, "Using draw_sprite_h_flip",
       SCREEN_W / 2, text_y, palette_color[15], -1);

   do {
      hline(sprite_buffer, 0, y + 82, sprite_buffer->w - 1, color);
      draw_sprite_h_flip(sprite_buffer, running_data[frame_number].dat, x, y);
      animate();
   } while (!next);

   clear_keybuf();
   rectfill(screen, 0, text_y, SCREEN_W, SCREEN_H, 0);
   textout_centre_ex(screen, font, "Using draw_sprite_v_flip",
       SCREEN_W / 2, text_y, palette_color[15], -1);

   do {
      hline(sprite_buffer, 0, y - 1, sprite_buffer->w - 1, color);
      draw_sprite_v_flip(sprite_buffer, running_data[frame_number].dat, x, y);
      animate();
   } while (!next);

   clear_keybuf();
   rectfill(screen, 0, text_y, SCREEN_W, SCREEN_H, 0);
   textout_centre_ex(screen, font, "Using draw_sprite_vh_flip",
       SCREEN_W / 2, text_y, palette_color[15], -1);

   do {
      hline(sprite_buffer, 0, y - 1, sprite_buffer->w - 1, color);
      draw_sprite_vh_flip(sprite_buffer, running_data[frame_number].dat, x, y);
      animate();
   } while (!next);

   clear_keybuf();
   rectfill(screen, 0, text_y, SCREEN_W, SCREEN_H, 0);
   textout_centre_ex(screen, font, "Now with rotating - pivot_sprite",
       SCREEN_W / 2, text_y, palette_color[15], -1);

   do {
      /* pivot_sprite()的最后一个参数是fixed类型,
       * 因此我必须使用itofix()来转换(int 转 fixe).
       */
      circle(sprite_buffer, x + 41, y + 41, 47, color);
      pivot_sprite(sprite_buffer, running_data[frame_number].dat, sprite_buffer->w / 2,
  sprite_buffer->h / 2, 41, 41, itofix(angle));
      animate();
      angle -= 4;
   } while (!next);

   clear_keybuf();
   rectfill(screen, 0, text_y, SCREEN_W, SCREEN_H, 0);
   textout_centre_ex(screen, font, "Now using pivot_sprite_v_flip",
       SCREEN_W / 2, text_y, palette_color[15], -1);

   do {
      /* pivot_sprite_v_flip() 的最后一个参数是fixed类型,
       * 因此我必须使用itofix()来转换(int 转 fixed).
       */
      circle(sprite_buffer, x + 41, y + 41, 47, color);
      pivot_sprite_v_flip(sprite_buffer, running_data[frame_number].dat,
  sprite_buffer->w / 2, sprite_buffer->h / 2, 41, 41, itofix(angle));
      animate();
      angle += 4;
   } while (!next);

   unload_datafile(running_data);
   destroy_bitmap(sprite_buffer);
   return 0;
}

END_OF_MAIN()

----------------------------------------------------------------------------------------------------------------------------
小节二十三:

1、精灵 sprite
其实就是子画面,是动画技术中最常使用的一种方法。再以后的例子中,我会采用“子画面”来称呼它。

2、巾贞 frame
这是一个字-_-#。还记得胶卷式电影带吧,一个桢就对应于一格画面。按一定顺序快速的显示这些桢就会产生动画的效果。一般来说超过24桢/秒(fps)人的眼睛就以为是连续的了。电视机一般为30fps。对应于这个概念,需要设置至少2个参数。当前桢、桢总数。

3、/* 创建和清理一个精灵位图, 
    * 要足够大,应该是对角线的长度,以便在旋转时不会丢失画面*/
   sprite_buffer = create_bitmap((int)(82 * sqrt(2) + 2),(int)(82 * sqrt(2) + 2));
想像一下,以一个矩形的中心为圆点,把矩形旋转360度,矩形的4个顶点的运动轨迹将形成一个圆,这个圆的半径就是对角线的长度。对角线长=sqrt(长^2 + 宽^2),  在这里,将单位换算成了sqrt(2)了,因为位图是正方形,并且位图边长为82,所以对角线就是82*sqrt(2)。这是很简单的数学知识。

4、同步 这个知识点有点难于理解。我将例子中关于同步的代码摘出来放在一起,便于分析。

volatile int ticks = 0;

void ticker(void)
{
    ticks++;
}
END_OF_FUNCTION(ticker)

main()
{
  install_int_ex(ticker, BPS_TO_TIMER(30));

  frame = ticks;

  do{
         while (frame > ticks) {
            rest(1);
         }
         frame++;
  }while(!next)
}

我将animation()函数中关于同步的部分直接替换到了do-while(!next)循环中。这样可以更好的理解这个过程。
1、参与同步的2个变量是frame 和 ticks。
2、 ticks第1次改变是在install_int_ex(ticker, BPS_TO_TIMER(30));执行完毕的时候。这个时候ticks将以每秒30次的速度递增(通过ticker()),并且这种递增速率是不受系统时钟干扰的。
3、第1次同步的时候发生在 frame= ticks。从这里开始,就会神奇将整个图像显示的速度设置为30fps。
4、为了达到30fps的效果,加入了
         while (frame > ticks) {
            rest(1);
         }
         frame++;
这段神奇的代码。
执行main()函数是依照系统时钟,
而执行ticks递增则是按照install_int_ex(ticker, BPS_TO_TIMER(30))定义的30fps时钟。
因此这个时候frame++(在main()中)可能会比ticks++(ticker()中)先执行,
为了不让程序执行的太快导致图像显示速度不正常,
因此需要让main() 等一等 ticker()的时钟,也就是在main() 中执行rest(1)。
上面那段就是公式化代码。
当然,在main()的最大fps<定义的fps时,frame>ticks 恒为false,因此如果发生那样的情况的话,没必要添加此同步机制。
或者你可以在自定义时钟里运行rest(1),从而改变参照物--以前是30fps,现在是自定义时钟 = maxfps of main()。但是千万不要这么做,至少在DOS下这样会引起一系列异常。

5、基本绘图函数
hline
circle
绘制子画面函数
draw_sprite
draw_sprite_vh_flip
pivot_sprite
pivot_sprite_v_flip
这些可以参看帮助手册来获得更详细的使用方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值