SDL编程入门(19)游戏手柄和操纵杆

游戏手柄和操纵杆

就像鼠标输入键盘输入一样,SDL也有能力读取来自操纵杆/游戏手柄/游戏控制器的输入。在本教程中,我们将根据操纵杆的输入使箭头旋转。

//模拟手柄死区
const int JOYSTICK_DEAD_ZONE = 8000;

SDL处理游戏控制器上的模拟杆的方式是,它将其位置转换为-32768和32767之间的数字。这意味着一个轻敲可以报告一个1000+的位置。我们想忽略轻敲,所以我们想创建一个死区,在这个死区中,来自操纵杆的输入被忽略。这就是我们定义这个常数的原因,我们稍后会看到它是如何工作的。

//游戏控制器1处理机
SDL_Joystick* gGameController = NULL;

游戏控制器的数据类型是SDL_Joystick。在这里,我们声明全局操纵杆手柄,我们将使用它来与操纵杆进行交互。

bool init()
{
    //Initialization flag
    bool success = true;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0 )
    {
        printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
        success = false;
    }

这一点很重要。

到目前为止,我们一直只初始化视频,以便我们可以渲染到屏幕上。现在我们需要初始化操纵杆子系统,否则从操纵杆读取数据将无法工作。

//将纹理过滤设置为线性
if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
{
    printf( "Warning: Linear texture filtering not enabled!" );
}

//检查操纵杆
if( SDL_NumJoysticks() < 1 )
{
    printf( "Warning: No joysticks connected!\n" );
}
else
{
    //加载操纵杆
    gGameController = SDL_JoystickOpen( 0 );
    if( gGameController == NULL )
    {
        printf( "Warning: Unable to open game controller! SDL Error: %s\n", SDL_GetError() );
    }
}

在初始化操纵杆子系统后,我们要打开我们的操纵杆。首先我们调用SDL_NumJoysticks来检查是否至少有一个操纵杆连接。如果有,我们调用SDL_JoystickOpen来打开索引0的操纵杆。操纵杆打开后,现在它将向SDL事件队列报告事件。

void close()
{
    //Free loaded images
    gArrowTexture.free();

    //Close game controller
    SDL_JoystickClose( gGameController );
    gGameController = NULL;

    //Destroy window    
    SDL_DestroyRenderer( gRenderer );
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;
    gRenderer = NULL;

    //Quit SDL subsystems
    IMG_Quit();
    SDL_Quit();
}

完成操纵杆的操作后,请使用SDL_JoystickClose将其关闭。

//Main loop flag
bool quit = false;

//Event handler
SDL_Event e;

//正常化方向
int xDir = 0;
int yDir = 0;

在这个演示中,我们要跟踪x和y的方向。如果x等于-1,则操纵杆的x位置指向左边。如果是+1,则x位置指向右。操纵杆的y位置有正的为上,负的为下,所以y=+1为上,y=-1为下。如果x或y为0,说明它在死区,处于中心位置。

//Handle events on queue
while( SDL_PollEvent( &e ) != 0 )
{
    //User requests quit
    if( e.type == SDL_QUIT )
    {
        quit = true;
    }
    else if( e.type == SDL_JOYAXISMOTION )
    {
        //Motion on controller 0
        if( e.jaxis.which == 0 )
        {                        
            //X axis motion
            if( e.jaxis.axis == 0 )
            {
                //死区左侧
                if( e.jaxis.value < -JOYSTICK_DEAD_ZONE )
                {
                    xDir = -1;
                }
                //死区右侧
                else if( e.jaxis.value > JOYSTICK_DEAD_ZONE )
                {
                    xDir =  1;
                }
                else
                {
                    xDir = 0;
                }
            }

在我们的事件循环中,通过检查SDL_JoyAxisEvent来检查操纵杆是否已经移动。"which"变量表示轴的运动来自哪个控制器,这里我们检查事件来自操纵杆 0。

接下来我们要检查它是x方向的运动还是y方向的运动,"axis"变量表示。通常情况下,0轴是x轴。

"value"变量表示模拟杆在轴上的什么位置。如果x位置小于死区,则方向设置为负。如果位置大于死区,则方向设置为正。如果在死区,则方向设置为0。

//Y轴运动
else if( e.jaxis.axis == 1 )
{
    //死区下方
    if( e.jaxis.value < -JOYSTICK_DEAD_ZONE )
    {
        yDir = -1;
    }
    //死区上方
    else if( e.jaxis.value > JOYSTICK_DEAD_ZONE )
    {
        yDir =  1;
    }
    else
    {
        yDir = 0;
    }
}
}
}
}

在这里,我们再次对y轴(用轴ID 1标识)执行相同的操作。

//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );

//Calculate angle
double joystickAngle = atan2( (double)yDir, (double)xDir ) * ( 180.0 / M_PI );

//Correct angle
if( xDir == 0 && yDir == 0 )
{
    joystickAngle = 0;
}

在我们渲染箭头之前,我们需要计算出箭头的角度。 我们使用cmath函数atan2进行此操作,该函数代表反正切2,即AKA反正切2。

对于熟悉三角函数的人来说,这基本上是反正切函数,其中包含一些附加代码,这些附加代码考虑了值来自哪个象限。

对于只熟悉几何的人来说,只要知道您给它的y位置和x位置,它就会为您提供以弧度为单位的角度。 SDL想要以度为单位的旋转角度,所以我们必须将弧度转换成度,将它乘以Pi的180。

当x和y位置均为0时,我们可以得到一个无意义的角度,因此我们将该值校正为等于0。

//Render joystick 8 way angle
gArrowTexture.render( ( SCREEN_WIDTH - gArrowTexture.getWidth() ) / 2, ( SCREEN_HEIGHT - gArrowTexture.getHeight() ) / 2, NULL, joystickAngle );

//Update screen
SDL_RenderPresent( gRenderer );

最后我们将箭头呈现在屏幕上旋转。

还有其他的操纵杆事件,比如按钮按下pov hats插入或移除控制器。它们都相当简单,你应该可以通过查看文档和实验来了解它们。

这里下载本教程的媒体和源代码。

原文链接

关注我的公众号:编程之路从0到1
编程之路从0到1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值