-
Notifications
You must be signed in to change notification settings - Fork 3
Touch vs Mouse
A Touch Event and a Mouse Event are not same event.
This is one of the hardest things to grasp when moving from a desktop development to mobile, is the understanding that there is no such thing as a "mouse" in the mobile development world. There are only touch events and unlike mouse events, touch events do not maintain state.
And that's it in a nutshell...
- There is no concept of a Left button events (i.e. Click, Down, Up etc)
- There is no concept of a Right button events (i.e. Click, Down, Up etc)
- There is no concept of Mouse Location Events (Hover etc)
The good news is, a single touch event, olc::GetTouch()
, can be likened to a olc::GetMouse(0)
which means your simple fire and forget code will work the very same.
-
olc::GetTouch().bPressed
can be executed the same asolc::GetMouse(0).bPressed
-
olc::GetTouch().bHeld
can be executed the same asolc::GetMouse(0).bHeld
-
olc::GetTouch().bReleased
can be executed the same asolc::GetMouse(0).bReleased
However that is where the likeness ends.
To use Touch Events in the OLOC PGE 2.0 Mobile simply execute olc::GetTouch()
or olc::GetTouchPos()
commands. You can also select which touch by passing the index of that touch, i.e. olc::GetTouch(1)
or olc::GetTouchPos(1)
.
Always keep in mind that touch events do not maintain state.
- If a user places 1 finger on the screen:
- The OS (Android/iOS) will intrepid this as Touch Event 0 or
olc::GetTouch()
/olc::GetTouch(0)
- The OS (Android/iOS) will intrepid this as Touch Event 0 or
- If the user then places a second finger on the screen
- The OS (Android/iOS) will intrepid this as Touch Event 1 or
olc::GetTouch(1)
- The OS (Android/iOS) will intrepid this as Touch Event 1 or
- If the user then places a third finger on the screen
- The OS (Android/iOS) will intrepid this as Touch Event 2 or
olc::GetTouch(2)
- The OS (Android/iOS) will intrepid this as Touch Event 2 or
- And all is happy with the world until the user raises any finger
- The OS will then re-intrepid the touch events, and work out what is the most likely the new Touch Event 0 and Touch Event 1
- This means you can never rely on the state of your touch events and must code them without state
- i.e. Touch Event 0, could now become Touch Event 1 or Touch Event 2 becomes 1, there is no state
Note: For OLC PGE 2.0 Mobile we leave the OS (Android/iOS) decide the order (index) of touch points. The OS developers have a team of people of who have the data and research to what the use is probably trying to do, and provide the best probably of which touch point is which.
The best place to start is to download and play with the Pan and Zoom Demo
This demo will give you a good oversight of how touch differs from mouse events and the power touch can bring to your game.
- When we want to control panning, we need two touch points to be set, as stated earlier the OS will work out which touch points are 0, 1, 2.
- Here we are detecting that the user has added a new Touch Point
if (GetTouch(1).bPressed && GetTouch().bHeld){...}
- We do not know yet if the user will be panning, zooming, swiping etc.
- As touch points do not maintain state we need to temp store the current location
fStartPanX = fMouseX;
fStartPanY = fMouseY;
- We now need to manage the Rate of Change
m_StartTime = std::chrono::system_clock::now();
m_CurrentTime = m_StartTime;
- Net we use some Geometry to work out the distance between to two points
fStartDistance = GetDistanceBetweenPoints(GetTouchPos(), GetTouchPos(1));
float GetDistanceBetweenPoints(float x1, float y1, float x2, float y2)
{
return std::sqrtf(std::powf((x2 - x1), 2) + std::powf((y2 - y1), 2));
}
- Finally we setup some variables needed for later
fCurrentDistance = fStartDistance;
fChangeDistance = 0.0f;
fLastDistance = 0.0f;
// For panning, we need to capture the screen location when the user starts to pan...
if (GetTouch(1).bPressed && GetTouch().bHeld)
{
fStartPanX = fMouseX;
fStartPanY = fMouseY;
// Let get out zoom params ready
m_StartTime = std::chrono::system_clock::now();
m_CurrentTime = m_StartTime;
fStartDistance = GetDistanceBetweenPoints(GetTouchPos(), GetTouchPos(1));
fCurrentDistance = fStartDistance;
fChangeDistance = 0.0f;
fLastDistance = 0.0f;
}
- Now that we have everything setup, lets handle the panning and zooming
- The panning is simple, we just enable Panning movement when both
(GetTouch().bHeld && GetTouch(1).bHeld)
are set
// ...as the mouse moves, the screen location changes. Convert this screen
// coordinate change into world coordinates to implement the pan. Simples.
if (GetTouch().bHeld && GetTouch(1).bHeld)
{
fOffsetX -= (fMouseX - fStartPanX) / fScaleX;
fOffsetY -= (fMouseY - fStartPanY) / fScaleY;
// Start "new" pan for next epoch
fStartPanX = fMouseX;
fStartPanY = fMouseY;
- We could also use some Geometry to work out the centre (center USA) point to ensure a very accurate movement
olc::vi2d GetCenterPoint(olc::vi2d vPos1, olc::vi2d vPos2)
{
return GetCenterPoint(vPos1.x, vPos1.y, vPos2.x, vPos2.y);
}
olc::vi2d GetCenterPoint(int x1, int y1, int x2, int y2)
{
olc::vi2d cPoint;
cPoint.x = (x1 + x2) / 2;
cPoint.y = (y1 + y2) / 2;
return cPoint;
}
- Now lets work out the zooming factor
- In short all we are doing is checking if the Rate of Change is increasing or decrease beyond the minimum Change Rate.
- If the rate of change is more than the allow minimum change rate, do something. Its stops the screen from zooming when the user just wants to pan it
// Lets calcutate the zoom factor
// Get the time changed
m_CurrentTime = std::chrono::system_clock::now();
std::chrono::duration<float> fduration = m_CurrentTime - m_StartTime;
float fdurationTime = fduration.count();
// Get the current distance
fCurrentDistance = GetDistanceBetweenPoints(GetTouchPos(), GetTouchPos(1));
// Get the change in distance
fChangeDistance = fCurrentDistance - fStartDistance;
// Check if we have met the min distance, and some time has passed
if (std::abs(fChangeDistance) >= fMinDistance && fdurationTime > 0)
{
// Now we need to check if we are zooming in or out
if (fChangeDistance > fLastDistance)
{
// Zooming out
fScaleX *= (0.999f - fElapsedTime);
fScaleY *= (0.999f - fElapsedTime);
}
if (fChangeDistance < fLastDistance)
{
fScaleX *= (1.001f + fElapsedTime);
fScaleY *= (1.001f + fElapsedTime);
}
}
fLastDistance = fChangeDistance;
- Finally we also use Geometry to work out other cool stuff like: Angles in both 2D and 3D
- Please see Javidx9 Video about Geometry 2D for more information
float GetAngleDegress(int x1, int y1, int x2, int y2)
{
float angleRadians = GetAngleRadians(x1, y1, x2, y2);
return angleRadians * 180 / 3.14159f;
}
float GetAngleRadians(int x1, int y1, int x2, int y2)
{
float deltaY = float(y2 - y1);
float deltaX = float(x2 - x1);
return atan2f(deltaY, deltaX);
}
float GetAngleDegressXYZ(olc::vi2d vPosX, olc::vi2d vPosY, olc::vi2d vPosZ)
{
float alpha = GetAngleRadiansXYZ(vPosX, vPosY, vPosZ);
return (alpha * 180. / 3.14159f + 0.5f);
}
float GetAngleRadiansXYZ(olc::vi2d vPosX, olc::vi2d vPosY, olc::vi2d vPosZ)
{
olc::vi2d xy = { vPosY.x - vPosX.x, vPosY.y - vPosX.y };
olc::vi2d zy = { vPosY.x - vPosZ.x, vPosY.y - vPosZ.y };
float dot = (xy.x * zy.x + xy.y * zy.y); // dot product
float cross = (xy.x * zy.y - xy.y * zy.x); // cross product
return atan2(cross, dot);
}
To make life easier for you, we have incorporated the touch Panning & Zooming into the olcPGEX_TransformedView.h extension for you. However if you like to learn more on how this works, again please download and play with the Pan and Zoom Demo
The below code is from the olcPGEX_TransformedView.h
void TransformedView::HandlePanAndZoomTouch(const float fZoomRate, const bool bPan, const bool bZoom)
{
const auto& vTouch = pge->GetTouchPos();
if (bPan)
{
if (pge->GetTouch().bReleased && pge->GetTouch(1).bPressed) return; // Edge case, helps stops the screen from jumping
if (pge->GetTouch().bHeld && pge->GetTouch(1).bPressed)
{
fStartDistance = GetDistanceTouchPoints(pge->GetTouchPos(), pge->GetTouchPos(1));
fCurrentDistance = fStartDistance;
fChangeDistance = 0.0f;
fLastDistance = 0.0f;
StartPan(vTouch);
}
if (pge->GetTouch().bHeld && pge->GetTouch(1).bHeld) UpdatePan(vTouch);
if (pge->GetTouch().bReleased || pge->GetTouch(1).bReleased) EndPan();
}
if (bZoom)
{
if (pge->GetTouch().bReleased && pge->GetTouch(1).bPressed) return; // Edge case, helps stops the screen from jumping
if (pge->GetTouch().bHeld && pge->GetTouch(1).bPressed)
{
fStartDistance = GetDistanceTouchPoints(pge->GetTouchPos(), pge->GetTouchPos(1));
fCurrentDistance = fStartDistance;
fChangeDistance = 0.0f;
fLastDistance = 0.0f;
}
if (pge->GetTouch().bHeld && pge->GetTouch(1).bHeld)
{
// Get the current distance
fCurrentDistance = GetDistanceTouchPoints(pge->GetTouchPos(), pge->GetTouchPos(1));
// A little edge case fix, stops the screen from jumping
if (fStartDistance == 0.0f) fStartDistance = fCurrentDistance;
// Get the change in distance
fChangeDistance = fCurrentDistance - fStartDistance;
// Check if we have met the min distance, and some time has passed
if (std::abs(fChangeDistance) >= fMinTouchDistance)
{
// Get the center point
vCenterPoint = GetCenterPoint(pge->GetTouchPos(), pge->GetTouchPos(1));
// Now we need to check if we are zooming in or out
if (fChangeDistance > fLastDistance)
{
// Zooming out
ZoomAtScreenPos(1.0f + fZoomRate, vCenterPoint);
}
if (fChangeDistance < fLastDistance)
{
// Zooming in
ZoomAtScreenPos(1.0f - fZoomRate, vCenterPoint);
}
}
fLastDistance = fChangeDistance;
}
}
}
- For Visual Studio All In One Android and iOS (Windows) Project Template: OLC Pixel Game Engine Mobile 2.2.8 Visual Studio for Android and iOS
- For Visual Studio Android Only (Windows) Use this project: OLC Pixel Game Engine Mobile 2.2.8 for Android Visual Studio
- For Android Studio (Windows/Linux/MAC) Use this project: OLC Pixel Game Engine Mobile 2.2.8 for Android Studio
- For Xcode (MAC) Use this project: OLC Pixel Game Engine Mobile 2.2.8 for Xcode