教程:向 DirectX 游戏添加触摸控件

Applies to Windows and Windows Phone

学习如何使用 DirectX 向应用商店 C++ 游戏添加基本触摸控件。我们将介绍如何添加基于触摸的控件以便在 Direct3D 环境中移动固定平台相机,其中包括使用手指拖动或触笔改变相机视景。

你可以将这些控件合并到游戏中,让玩家能够在 3D 环境中通过拖动滚动或平移地图或游戏区。例如,在策略游戏或拼图游戏中,你可以使用这些控件让玩家通过左右平移来观看大于屏幕的游戏环境。

注意  我们的代码还使用基于鼠标的平移控件。与指针相关的事件通过 Windows 运行时 API 进行抽象,因此它们可以处理基于触摸或基于鼠标的指针事件。

目标

  • 创建简单触摸拖动控件,用于在 DirectX 游戏中平移固定平面相机。

设置基本触摸事件基础结构

首先,我们在本例中定义基本控制器类型 CameraPanController。这里我们将控制器定义为抽象概念。即用户可以执行的行为集。

CameraPanController 类是一个定期刷新的有关相机控制器状态的信息集合,并为我们的应用提供一种从其更新循环中获取该信息的方法。


using namespace Windows::UI::Core;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Devices::Input;

// Methods to get input from the UI pointers
ref class CameraPanController
{
}

现在,让我们创建一个定义相机控制器状态的标头,以及实现相机控制器交互的基本方法和事件处理程序。


ref class CameraPanController
{
private:
    // properties of the controller object
    float3 m_position;			    // the position of the camera

    // properties of the camera pan control
    bool m_panInUse;			    
    uint32 m_panPointerID;		    
    float2 m_panFirstDown;		    
    float2 m_panPointerPosition;   
    float3 m_panCommand;		    

public:

    // Methods to get input from the UI pointers
    void OnPointerPressed(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerMoved(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerReleased(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    // set up the Controls supported by this controller
    void Initialize( _In_ Windows::UI::Core::CoreWindow^ window );

    // accessor to set the position of the ontroller
    void SetPosition( _In_ float3 pos );

	   // accessor to set fixed "look point" of the controller
	   float3 get_FixedLookPoint();

    // returns the position of the controller object
    float3 get_Position();


    void Update( Windows::UI::Core::CoreWindow ^window );

};  // class CameraPanController

私有字段包含相机控制器的当前状态。下面我们了解这些内容。

  • m_position 是相机在场景空间中的位置。在本例中,z 坐标值固定在 0。我们可以使用 float2 表示此值,但是对于本例和为了未来可扩展性,我们使用 float3。我们通过 get_Position 属性将此值传递到应用本身,以便它可以相应地更新视区。
  • m_panInUse 是一个布尔值,用于指示是否激活平移操作;更具体地说,就是玩家是否在触摸屏幕和移动相机。
  • m_panPointerID 是指针的唯一 ID。我们在示例中不使用此项,但使用它可以很好地将控制器状态类与特定指针关联。
  • m_panFirstDown 是屏幕上的点,指示在相机平移操作期间玩家首次触摸屏幕或单击鼠标的位置。稍后我们将使用此值设置死区,以防止触摸屏幕时发生抖动,或者鼠标轻度颤动。
  • m_panPointerPosition 是玩家当前在屏幕上将指针移动到的点。我们通过检查它相对于 m_panFirstDown 的移动,来确定玩家想要移动的方向。
  • m_panCommand 是相机控制器最后计算的命令:向上、向下、向左或向右。因为我们使用固定在 x-y 平面上的相机,它可以是一个 float2 值。

我们使用这 3 个事件处理程序更新相机控制器状的态信息。

  • OnPointerPressed 是一个事件处理程序,当玩家在触摸表面按下手指并且指针移动到所按位置的坐标时我们的应用将调用此程序。
  • OnPointerMoved 是一个事件处理程序,玩家在触摸表面上轻扫手指时我们的应用将调用此程序。它将使用拖动路径的新坐标进行更新。
  • OnPointerReleased 是一个事件处理程序,玩家从触摸表面移开按着的手指时我们的应用将调用此程序。

最后,我们使用这些方法和属性来初始化、访问和更新相机控制器的状态信息。

  • Initialize 是一个事件处理程序,我们的应用调用此处理程序来初始化控件,并将它们附加到描述显示窗口的 CoreWindow 对象。
  • SetPosition 是一个方法,我们的应用调用此方法在场景空间中设置控件的(x、y 和 z)坐标。注意,z 坐标在整个教程中都是 0。
  • get_Position 是一个属性,我们的应用访问此属性来获取相机在场景空间的当前位置。使用此属性作为当前相机位置与应用进行通信的方法。
  • get_FixedLookPoint 是一个属性,我们的应用访问此属性来获取控制器相机面对的当前点。在本例中,它通常锁定到 x-y 平面。
  • Update 是一个方法,用于读取控制器状态和更新相机位置。从应用的主循环中不断调用此 <something> 可刷新相机控制器数据和相机在场景空间的位置。

现在你已了解实现触摸控件的全部组件。你可以检测触摸或鼠标指针事件发生的时间和位置,以及操作是什么。你可以相对于场景空间设置相机的位置和方向,并跟踪更改。最后,你可以将新相机位置传达给执行调用的应用。

现在,我们将各个片段连接在一起。

创建基本触摸事件

Windows 运行时事件调度程序提供我们希望自己的应用处理的 3 个事件:

这些事件在 CoreWindow 类型上实现。我们假设你有一个 CoreWindow 对象要处理。有关详细信息,请参阅如何设置 Windows 应用商店 C++ 应用以显示 DirectX 视图

由于在我们的应用运行时将触发这些事件,因此处理程序将更新相机控制器在我们的私有字段中定义的状态信息。

首先,让我们填充触摸指针事件处理程序。在第一个事件处理程序 OnPointerPressed 中,我们从 CoreWindow 获取指针的 x-y 坐标,该类型管理用户触摸屏幕或单击鼠标时的显示。

OnPointerPressed


void CameraPanController::OnPointerPressed(
                                           _In_ CoreWindow^ sender,
                                           _In_ PointerEventArgs^ args)
{
    // get the current pointer position
    uint32 pointerID = args->CurrentPoint->PointerId;
    float2 position = float2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );

    auto device = args->CurrentPoint->PointerDevice;
    auto deviceType = device->PointerDeviceType;
    

	   if ( !m_panInUse )	// if no pointer is in this control yet
    {
       m_panFirstDown = position;					// save location of initial contact
       m_panPointerPosition = position;
       m_panPointerID = pointerID;				// store the id of pointer using this control
       m_panInUse = TRUE;
    }
    
}


我们使用此处理程序让当前 CameraPanController 实例知道应将相机控制器视为处于活动状态(通过将 m_panInUse 设置为 TRUE)。这样,当应用调用 Update 时,它将使用当前位置数据更新视区。

现在,我们已经建立了当用户在显示窗口中触摸屏幕或点按时相机移动的基本值,我们必须确定当用户拖动鼠标点按或按住按钮移动鼠标时的操作。

每当指针移动、在玩家每次将其拖动到屏幕时将触发 OnPointerMoved 事件处理程序。我们需要让应用知道指针的当前位置,通过上述方式可以做到这一点。

OnPointerMoved


void CameraPanController::OnPointerMoved(
                                        _In_ CoreWindow ^sender,
                                        _In_ PointerEventArgs ^args)
{
    uint32 pointerID = args->CurrentPoint->PointerId;
    float2 position = float2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );

    m_panPointerPosition = position;
}

最后,当玩家停止触摸屏幕时,我们需要停用相机平移行为。我们使用在触发 PointerReleased 时调用的 OnPointerReleasedm_panInUse 设置为 FALSE,并关闭相机平移,将指针 ID 设置为零。

OnPointerReleased


void CameraPanController::OnPointerReleased(
                                             _In_ CoreWindow ^sender,
                                             _In_ PointerEventArgs ^args)
{
    uint32 pointerID = args->CurrentPoint->PointerId;
    float2 position = float2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );

    m_panInUse = FALSE;
    m_panPointerID = 0;
}

初始化触摸控件和控制器状态

让我们挂起该事件并初始化相机控制器的所有基本状态字段。

Initialize


void CameraPanController::Initialize( _In_ CoreWindow^ window )
{

    // Start recieving touch/mouse events
    window->PointerPressed += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerPressed);

    window->PointerMoved += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerMoved);

    window->PointerReleased += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerReleased);


    // Initialize state of the controller
    m_panInUse = FALSE;				
    m_panPointerID = 0;

    //	Initialize this as it is reset every frame
    m_panCommand = float3( 0.0f, 0.0f, 0.0f );

}

Initialize 将指向该应用的 CoreWindow 实例的引用作为参数提供,并在该 CoreWindow 上注册我们已开发的相应的事件处理程序。

获取并设置相机控制器的位置

让我们定义一些方法,以便在场景空间获取和设置相机控制器的位置。


void CameraPanController::SetPosition( _In_ float3 pos )
{
    m_position = pos;
}

// Returns the position of the controller object
float3 CameraPanController::get_Position()
{
    return m_position;
}

float3 CameraPanController::get_FixedLookPoint()
{
    // For this sample, we don't need to use the trig functions because our
    // look point is fixed. 
    return m_position + float3( 0, 0, 1 );
}

SetPosition 是一个公共方法,如果需要将相机控制器位置设置到特定的点,我们可以从应用中调用此方法。

get_Position 是我们最重要的公共属性:它是我们的应用在场景空间获取相机控制器当前位置的方法,从而可以相应地更新视区。

get_FixedLookPoint 是一个公共属性,在本例中,获取一个通常位于 x-y 平面的观看点。在计算 x、y 和 z 轴坐标值时,如果希望为固定相机创建多个斜角,你可以更改此方法以使用三角函数的正弦和余弦。

更新相机控制器的状态信息

现在,我们将执行计算,将 m_panPointerPosition 中跟踪的指针坐标信息转换为相应的 3D 场景空间的新坐标信息。每当我们刷新主屏循环时,我们的应用一般会调用此方法,其中我们计算希望传递到应用的新位置信息,以便在投影到视区之前更新视图矩阵。




void CameraPanController::Update( CoreWindow ^window )
{
    if ( m_panInUse )
    {
        float2 pointerDelta = m_panPointerPosition - m_panFirstDown;

        if ( pointerDelta.x > 16.0f )		// leave 32 pixel-wide dead spot for being still
            m_panCommand.x += 1.0f;
        else
            if ( pointerDelta.x < -16.0f )
                m_panCommand.x += -1.0f;

        if ( pointerDelta.y > 16.0f )		
            m_panCommand.y += 1.0f;
        else
            if (pointerDelta.y < -16.0f )
                m_panCommand.y += -1.0f;
    }

	   float3 command = m_panCommand;
   
    // our velocity is based on the command
    float3 Velocity;
    Velocity.x =  command.x;
    Velocity.y =  command.y;
    Velocity.z =  0.0f;

    // integrate
    m_position = m_position + Velocity;

    // Clear the movement input accumulator for use during next frame
    m_panCommand = float3( 0.0f, 0.0f, 0.0f );

}

由于我们不希望触摸或鼠标的抖动造成相机平移不稳定,因此围绕指针设置一个直径为 32 像素的死区。我们还有一个速度值,在本例中为 1:1,该值是指针经过死区时跨过的像素与移动速度的比值。你可以调整此行为来减慢或加快移动速度。

使用相机的新位置更新视图矩阵

现在,我们可以获取相机聚焦的场景空间坐标,每当指示你的应用执行更新坐标时,将执行此操作(例如在主屏循环中每 60 秒)。 此伪代码建议你可以实现的调用行为:


 myCameraPanController->Update( m_window );	

 // update the view matrix based on the camera position
 myCamera->MyMethodToComputeViewMatrix(
        myController->get_Position(),		// the position in the 3D scene space
        myController->get_FixedLookPoint(),		// the point in the space we are looking at
        float3( 0, 1, 0 )					// the axis that is "up" in our space
        );	

恭喜你!你已在采用 DirectX 和 C++ 的 Windows 应用商店应用中实现了一组简单的相机平移触摸控件

 

 

显示:
© 2015 Microsoft