`
javababy1
  • 浏览: 1166683 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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
这些可以参看帮助手册来获得更详细的使用方法。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics