SDL入门教程(十三):1、多线程,从动画说起

作者:龙飞

1.1:简单动画

        游戏离不开动画。我们考虑最简单的情况:将一个角色从一个位置移动到另外一个位置。这个行为表述给电脑就是,将一个surface不断的blit(),从起始位置的坐标,移动到结束位置的坐标。移动速度取决于每次blit()的坐标差和blit()的时间间隔(v = ds/dt )。
        我们来设计一个函数实现这个简单的动画。我们需要的数据有:起始坐标(int beginX, int beginY),结束坐标(int endX, int endY),以及作为SDL基础的ScreenSurface窗口(const ScreenSurface& screen)。一般的考虑是,将这5个数据以参数的方式传入函数;但是一种更加通用一点的方式是,将这5种数据合并成一个结构,这样函数的参数形式会更加的统一,这正是触发多线程的函数所需要的。在SDL中,我们通过函数:

SDL_Thread  * SDL_CreateThread( int  ( * fn)( void   * ),  void   * data);

触发多线程,其中所需要的函数指针形式为:

typedef  int  ( * fn)( void * );

而void*类型的data就是函数(*fn)()需要的的数据。我们可以将任意的结构体指针,转化为void*,作为这个函数的第二个参数需要。
因此,我们可以为我们需要的动画函数定义一个结构作为传递所有数据的载体:

struct  AmnArg
{
    
int  beginX;
    
int  beginY;
    
int  endX;
    
int  endY;
    
const  ScreenSurface &  screen;
    AmnArg(
int  begin_x,  int  begin_y,  int  end_x,  int  end_y,  const  ScreenSurface &  _screen): beginX(begin_x), beginY(begin_y), endX(end_x), endY(end_y), screen(_screen){}
};

这样,我们可以将AmnArg对象的指针传递给动画函数——考虑到多线程函数的需要,我们再曲折一点:先将AmnArg*转换成void*传递给函数,在函数内部再将其转换回来以供调用。

int  amn( void *  data)
{
    AmnArg
*  pData  =  (AmnArg * )data;
    PictureSurface stand(
" ./images/am01.png " , pData -> screen);
    stand.colorKey();
    PictureSurface bg(
" ./images/background.png " , pData -> screen);

    
const   int  SPEED_CTRL  =   300 ;
    
int  speedX  =  (pData -> endX  -  pData -> beginX)  /  SPEED_CTRL;
    
int  speedY  =  (pData -> endY  -  pData -> beginY)  /  SPEED_CTRL;

    
for  (  int  i  =   0 ; i  <  SPEED_CTRL; i ++  ){
        pData
-> beginX  +=  speedX;
        pData
-> beginY  +=  speedY;
        bg.blit(pData
-> beginX, pData -> beginY, pData -> beginX, pData -> beginY, stand.point() -> w, stand.point() -> h,  2 2 );
        stand.blit(pData
-> beginX, pData -> beginY);
        pData
-> screen.flip();
    }

    
return   0 ;
}

注意:我们这里仅仅设定了每次blit()的位移差(ds)而没有设定时间差(dt)。这并不意味着dt == 0,事实上,电脑处理数据是需要时间的,包括运算和显示。我们这里事实上将dt的设定交给了电脑,也就是说,让电脑以其最快的速度来完成。为什么要这么做呢?这是为了演示多线程的一个现象,卖个关子,后面解释。:)

1.2:动画函数在主程序中的调用

#include  " SurfaceClass.hpp "
#include 
" amn.hpp "

int  main( int  argc , char *  argv[])
{
    
// Create a SDL screen.
     const   int  SCREEN_WIDTH  =   640 ;
    
const   int  SCREEN_HEIGHT  =   480 ;
    
const  Uint32 SCREEN_FLAGS  =   0 // SDL_FULLSCREEN | SDL_DOUBLEBUF | SDL_HWSURFACE
     const  std:: string  WINDOW_NAME  =   " Amn Test " ;
    ScreenSurface screen(SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_NAME, 
0 , SCREEN_FLAGS);

    PictureSurface bg(
" ./images/background.png " , screen);
    bg.blit(
0 );
    screen.flip();

    AmnArg test1(
0 250 600 250 , screen);
    amn((
void * ) & test1);

    SDL_Event gameEvent;
    
bool  gameOver  =   false ;
    
while  ( gameOver  ==   false  ){
        
while  ( SDL_PollEvent( & gameEvent)  !=   0  ){
            
if  ( gameEvent.type  ==  SDL_QUIT ){
                gameOver 
=   true ;
            }
            
if  ( gameEvent.type  ==  SDL_KEYDOWN ){
                
if  ( gameEvent.key.keysym.sym  ==  SDLK_ESCAPE ){
                    gameOver 
=   true ;
                }
            }
            screen.flip();
        }
    }

    
return   0 ;
}

当这个程序运行的时候,我们会发现一些很明显的问题:
1、图片移动的时候,界面不接受任何信息。这是因为必须把amn()执行完毕才会运行到有事件响应的事件轮询循环。
2、如果我们需要另外一张图片移动起来,我们唯一能做的事情,是修改amn()函数,而不是把amn()以不同的参数调用两次——如果以不同的参数调用两次,那么移动总是有先后的——是不可能完成“同时”移动的。

1.3:创建线程

        如果要将这个程序从主线程(主进程)调用函数修改为通过新创建的线程调用函数,只需要做很小的修改,即将amn((void*)&test1);修改为:

SDL_Thread *  thread1  =  SDL_CreateThread(amn, ( void * ) & test1);

然后在return 0;之前加入清理线程的语句:

SDL_KillThread(thread1);

这样,程序在执行动画的同时,事件轮询就已经开始,我们可以随时结束程序,SDL界面也不会出现不响应的情况。

 

此文章来自于【http://blog.csdn.net/lf426/article/details/2657234

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值