问题
I can not find an example how to handle multi touch in windows phone 8 app ( in game on c++ and directx ). May be anyone can help me? Thanks!
回答1:
I figured out how to do it. I am a C++ newbie so there may be much better ways to do this, but my way works.
(FYI - I wrote an article about this on Nokia's Developer site: http://developer.nokia.com/Community/Wiki/Multi-touch_handling_using_DirectX_C%2B%2B#PointerPoint )
You need to keep track of the PointerId inside of the PointerPoint. I store the PointerId in the OnPointerPressed event in a std::unordered_map. The key (unsigned int) is the PointerId. You then erase them from the map when you get the OnPointerReleased event. The PointerId will match up. It turns out that for every new finger, you get a new PointerId... even if you release your first finger and put it back down again.
Then, when you get to your OnPointerMoved event handler, you just have to call the size() function on your unordered_map to find out how many touches you currently have on screen.
The reason I'm using the unordered_map is because I needed to keep track of previous point positions. I realize that you can probably get old positions using the GetIntermediatePoints method, but I didn't want to have to troubleshoot something new...
Anyways, hope this helps. Code below:
EDIT: Code updated to handle the x move events you get from each touch for each frame. Like I said, this isn't the best, but it works.
#include <unordered_map>
std::unordered_map<unsigned int, Windows::UI::Input::PointerPoint^> m_pointerIds;
std::unordered_map<unsigned int, Windows::UI::Input::PointerPoint^> m_oldPoints;
void Direct3DBackground::OnPointerPressed(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
m_pointerIds.emplace(args->CurrentPoint->PointerId, args->CurrentPoint);
m_oldPoints.emplace(args->CurrentPoint->PointerId, args->CurrentPoint);
}
void Direct3DBackground::OnPointerMoved(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
if (m_pointerIds.size() == 1)
{
DoSomethingForOneTouch(args);
}
else if (m_pointerIds.size() == 2)
{
UINT changedPointId = args->CurrentPoint->PointerId;
UINT frameId = args->CurrentPoint->FrameId;
UINT otherPointId;
for (auto it = m_pointerIds.begin(); it != m_pointerIds.end(); ++it)
{
if (it->first != changedPointId)
{
otherPointId = it->first;
break;
}
}
m_oldPoints[changedPointId] = m_pointerIds[changedPointId];
m_pointerIds[changedPointId] = args->CurrentPoint;
if (m_pointerIds[otherPointId]->FrameId == frameId)
{
//DO YOUR CALCULATION using new points and old points
//as both touches have been updated for the current frame.
}
}
}
void Direct3DBackground::OnPointerReleased(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
m_pointerIds.erase(args->CurrentPoint->PointerId);
m_oldPoints.erase(args->CurrentPoint->PointerId);
}
回答2:
The code above greatly shows the idea, but there are some crucial details behind which can be unseen even in practice. OnPointerPressed
event comes right from the message queue which is handled by the UI thread. Whereas OnPointerMoved
event is called from the other thread NPCtrl.dll!CDirectManipulationServer::ServerThreadStatic
. It can easily be seen from the native debugger in VS. This is an open field for race conditions between the two threads which indeed happened in practice in this particular case as my test showed. So obviously this code needs to be improved with synchronization patterns like
void Direct3DBackground::OnPointerPressed(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
std::unique_lock<std::mutex> l(eventLock);
m_pointerIds.emplace(args->CurrentPoint->PointerId, args->CurrentPoint);
m_oldPoints.emplace(args->CurrentPoint->PointerId, args->CurrentPoint);
}
where eventLock
is std::mutex
. Same lock should be added to all three events. The other approach is more complex and would involve a concurrent queue which is added to whenever a new event fires.
来源:https://stackoverflow.com/questions/19641199/how-to-handle-multi-touch-in-windows-phone-8-app