diff options
Diffstat (limited to 'Runtime/Input')
-rw-r--r-- | Runtime/Input/GetInput.h | 288 | ||||
-rw-r--r-- | Runtime/Input/InputAxis.cpp | 226 | ||||
-rw-r--r-- | Runtime/Input/InputAxis.h | 151 | ||||
-rw-r--r-- | Runtime/Input/InputManager.cpp | 1329 | ||||
-rw-r--r-- | Runtime/Input/InputManager.h | 470 | ||||
-rw-r--r-- | Runtime/Input/LocationService.h | 51 | ||||
-rw-r--r-- | Runtime/Input/OnScreenKeyboard.h | 47 | ||||
-rw-r--r-- | Runtime/Input/SimulateInputEvents.cpp | 222 | ||||
-rw-r--r-- | Runtime/Input/SimulateInputEvents.h | 10 | ||||
-rw-r--r-- | Runtime/Input/TimeManager.cpp | 489 | ||||
-rw-r--r-- | Runtime/Input/TimeManager.h | 149 | ||||
-rw-r--r-- | Runtime/Input/TouchPhaseEmulation.cpp | 719 | ||||
-rw-r--r-- | Runtime/Input/TouchPhaseEmulation.h | 68 |
13 files changed, 4219 insertions, 0 deletions
diff --git a/Runtime/Input/GetInput.h b/Runtime/Input/GetInput.h new file mode 100644 index 0000000..bccbe2b --- /dev/null +++ b/Runtime/Input/GetInput.h @@ -0,0 +1,288 @@ +#ifndef GETINPUT_H +#define GETINPUT_H + +#include "Runtime/Math/Vector2.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Quaternion.h" +#include "Runtime/Math/Color.h" +#include "PlatformDependent/iPhonePlayer/APN.h" +#include <string> + +/* Mac os x input handling + +--- Joystick input: +* Uses HID + +--- Keyboard input: +* Uses HID for keyboard events +* Uses normal event passing for Input.inputString. The applications are responsible for passing the events on to GetInput.cpp + +--- Mouse input + +HID gives the nicest mouse delta, so we always try to use it. +BUT Some mice don't have mouse delta from HID and USB overdrive snatches it away. + +So we must provide fallbacks. At startup hid mouse delta is disabled, as soon any hid mouse delta +is reported, we switch to using only hid mouse delta. + +The fallback look different for all editor/player/webplayer + +Some facts: +- In a cocoa application, Carbon events work only in fullscreen mode. In window mode they get snatched. + (In Safari carbon events fail, in Firefox they work) +- In editor/player we can access all cocoa events directly. In the web player we can't. +- Cocoa/Carbon mouse delta is non-accelerated and supports screen boundary deltas +- getmouse based is accelerated and doesnt support screen boundaries + +Which leads to the following workarounds: + +Editor +-> NSApp sendEvents forwards all mouse moved NSEvents to InputProcessMouseMove +-> Mouse down is generated from GameView + +Standalone +-> NSApp sendEvents forwards all mouse moved NSEvents to InputProcessMouseMove +-> Mouse down forwarded from NSApp + +Web plugin windowed +-> getmouse based mouse delta (Fails at screen borders) +-> mouse down comes from the plugin events (No right or middle mouse buttons) + +Web plugin fullscreen +-> carbon event based mouse delta +-> carbon event based mouse down +*/ + +void GetJoystickNames (std::vector<std::string> &names); +std::string GetJoystickAxisName(int joyNum,int axis); +std::string GetNiceKeyname(int key); + +void InputShutdown (); + +void InputReadMousePosition (); +void InputReadMouseState(); +void InputReadKeyboardState(); +void InputReadJoysticks(); + +void ClearInputEvents (); + +// Clears all input axes, keydowns, mouse buttons and sets up how event handlers +void ResetInput (); +void ResetInputAfterPause (); + +namespace Unity +{ + class GameObject; +}; + +// touchscreen +struct Touch +{ +#if ENABLE_NEW_EVENT_SYSTEM + SInt32 id; // -1 = LMB, -2 = RMB, -3 = MMB, 0+ = actual touch IDs. + Vector2f pos; + Vector2f rawPos; + Vector2f oldPos; + Vector2f deltaPos; + Vector2f deltaScroll; + Vector3f worldPos; + float deltaTime; // time between two events that came to us during this + // touch/drag gesture. 1.0f = 1 second. + UInt32 tapCount; + UInt32 phase; + + Unity::GameObject* hover; // Object the mouse is hovering over + Unity::GameObject* press; // Object that was pressed on via mouse or touch + + //bool eligibleForClick; + + enum TouchPhase + { + kTouchBegan = 0, + kTouchMoved = 1, + kTouchStationary = 2, + kTouchEnded = 3, + kTouchCanceled = 4 + }; + + Touch () : hover(NULL), press(NULL) {} +#else + UInt32 id; + Vector2f pos; + Vector2f rawPos; + Vector2f deltaPos; + float deltaTime; // time between two events that came to us during this + // touch/drag gesture. 1.0f = 1 second. + UInt32 tapCount; + UInt32 phase; +#endif +}; + +struct Acceleration +{ + Vector3f acc; + float deltaTime; +}; + +Vector3f GetAcceleration (); +size_t GetAccelerationCount (); +void GetAcceleration (size_t index, Acceleration& acceleration); +size_t GetTouchCount (); +#if ENABLE_NEW_EVENT_SYSTEM +Touch* GetTouch (unsigned index); +#else +bool GetTouch (unsigned index, Touch& touch); +#endif +bool IsMultiTouchEnabled(); +void SetMultiTouchEnabled(bool flag = true); + +// Must match DeviceOrientation in UnityEngineInput.txt +enum DeviceOrientation +{ + // The orientation of the device cannot be determined. + DEVICE_ORIENTATION_UNKNOWN = 0, + // The device is in portrait mode, with the device held upright and the home button at the bottom. + DEVICE_ORIENTATION_PORTRAIT = 1, + // The device is in portrait mode but upside down, with the device held upright and the home button at the top. + DEVICE_ORIENTATION_PORTRAITUPSIDEDOWN = 2, + // The device is in landscape mode, with the device held upright and the home button on the right side. + DEVICE_ORIENTATION_LANDSCAPELEFT = 3, + // The device is in landscape mode, with the device held upright and the home button on the left side. + DEVICE_ORIENTATION_LANDSCAPERIGHT = 4, + // The device is held parallel to the ground with the screen facing upwards. + DEVICE_ORIENTATION_FACEUP = 5, + // The device is held parallel to the ground with the screen facing downwards. + DEVICE_ORIENTATION_FACEDOWN = 6 +}; +unsigned int GetOrientation (); // device orientation + +void Vibrate (); +bool IsApplicationGenuine (); +bool IsApplicationGenuineAvailable (); +void PlayMovie (std::string const& path, ColorRGBA32 const& backgroundColor, + UInt32 controlMode, UInt32 scalingMode, bool pathIsUrl); + +Vector3f GetAcceleration (int controllerID); +Quaternionf GetRotation(int controllerID); +Vector3f GetPosition(int controllerID); +bool IsCompensatingSensors(); +void SetCompensatingSensors(bool val); +Vector3f GetGyroRotationRate(int idx); +bool IsGyroAvailable(); +Vector3f GetGyroRotationRateUnbiased(int idx); +Vector3f GetGravity(int idx); +Vector3f GetUserAcceleration(int idx); +Quaternionf GetAttitude(int idx); +bool IsGyroEnabled(int idx); +void SetGyroEnabled(int idx, bool enabled); +float GetGyroUpdateInterval(int idx); +void SetGyroUpdateInterval(int idx, float interval); +int GetGyro(); +size_t GetLocalNotificationCount(); +iPhoneLocalNotification* CopyLocalNotification(unsigned index); +size_t GetRemoteNotificationCount(); +iPhoneRemoteNotification* CopyRemoteNotification(unsigned index); +void ClearLocalNotifications(); +void ClearRemoteNotifications(); + +#if UNITY_OSX + //scaling value for mouse deltas + #define kHIDMouseDeltaScale 0.5 + #define kCocoaMouseDeltaScale 0.25 + + struct InputEvent; + + void InputInit(); + void CleanupMouseInputHandlers(); + int MapCocoaScancodeToSDL (int inKey); + void InputProcessMacOSEvent(EventRecord *evt); + void InputProcessMouseMove (float x, float y); + Vector2f MacOSGlobalPointToLocal (Point p); + void ResetUnicodeInput(); + int ConvertCocoaModifierFlags (UInt32 modFlags); + void SanitizeKeyEvent( InputEvent& event); + void ProcessCocoaKeyInput(InputEvent &ie, CFStringRef characters, UInt32 keyCode); + void GetKeyStateFromFlagsChangedEvent (UInt32 mod); + void InputGetKeyboardIMEMode(); + + #if WEBPLUG + typedef struct _NPCocoaEvent NPCocoaEvent; + + void InputProcessMacOSEventHIDWorkaround(EventRecord *evt); + void InputProcessCocoaEvent(NPCocoaEvent *event, bool letBrowserHandleTextComposition); + #endif +#elif UNITY_IPHONE || UNITY_ANDROID + void InputInit(); + // Process any input - call once per main loop + void InputProcess(); +#elif UNITY_BB10 + void InputInit(); + // Process any input - call once per main loop + void InputProcess(); + void AddKeyEvent(int key, int flag); + void HandleEvents(); + int GetFrameCount(); +#elif UNITY_TIZEN + void InputInit(); + // Process any input - call once per main loop + void InputProcess(); +#elif UNITY_LINUX + #define kX11MouseDeltaScale 0.25 + void InputInit(); + // Process any input - call once per main loop + void InputProcess(); + void processKey( void* event, bool down ); + void processMouseButton( void* event, bool down ); + void processMouseMovedEvent( void* event ); + void processMouseWarp( void* event ); +#elif UNITY_WII + #include <revolution/kpad.h> + + void InputInit(); + // Process any input - call once per main loop + void InputProcess(); + +#elif UNITY_WIN + + // Initialize input with application's main window + void InputInit( HWND window ); + + void InputInitWindow (HWND window); + void InputShutdownWindow (); + + // Process any input - call once per main loop + void InputProcess (); + + // Activate input - call when application goes to foreground + void InputActivate(); + // Passivate input - call when application goes to background + void InputPassivate(); + // Window/fullscreen has changed + void InputSetWindow(HWND window, bool fullscreen); + + LRESULT ProcessInputMessage(HWND window, UINT message, WPARAM wParam, LPARAM lParam, BOOL &handled); + + int InputKeycodeFromVKEY( int vkey ); + bool GetMousePresent(); + void SetMousePresent(const bool mousePresent); + + #if UNITY_EDITOR + void SetEditorMouseOffset( int x, int y, HWND window ); + void GetEditorMouseOffset(int *x, int *y, HWND *window = NULL); + #endif +#elif UNITY_XENON + + void InputInit(); + void InputProcess (); + +#elif UNITY_PS3 + int InputInit(); + int InputProcess (); +#elif UNITY_PEPPER + void InputInit (); +#elif UNITY_FLASH || UNITY_WEBGL +#else +#error "Unknown platform" +#endif + +#endif diff --git a/Runtime/Input/InputAxis.cpp b/Runtime/Input/InputAxis.cpp new file mode 100644 index 0000000..62cf9d1 --- /dev/null +++ b/Runtime/Input/InputAxis.cpp @@ -0,0 +1,226 @@ +#include "UnityPrefix.h" +#include "InputAxis.h" +#include "TimeManager.h" +#include "InputManager.h" + +using namespace std; + +InputAxis::InputAxis () { + positiveButton = 0; + negativeButton = 0; + altPositiveButton = 0; + altNegativeButton = 0; + joyNum = 0; + type=kAxisButton; + dead = 0.001f; + gravity = 0.0; + sensitivity= .1f; + invert = false; + snap = false; + value = 0.0f; + axis = 0; + descriptiveName=""; + descriptiveNegativeName=""; +} + +InputAxis::InputAxis (const string &name) { + positiveButton = 0; + negativeButton = 0; + altPositiveButton = 0; + altNegativeButton = 0; + joyNum = 0; + type=kAxisButton; + dead = 0.001f; + gravity = 0.0; + sensitivity= .1f; + invert = false; + snap = false; + value = 0.0f; + axis = 0; + m_Name = name; + descriptiveName=""; + descriptiveNegativeName=""; +} + +void InputAxis::DoGravity (float time) { + // Handle gravity + if (gravity) { + if (value > 0) { + value -= gravity * time; + if (value < 0) + value = 0; + } else if (value < 0) { + value += gravity * time; + if (value > 0) + value = 0; + } + } +} + +void InputAxis::Update () { + float time = GetDeltaTime(); + Vector2f pos; +#if !ENABLE_NEW_EVENT_SYSTEM + Vector3f delta; +#endif + +// Vector2f pos; + int posFlag, negFlag; + + if (invert) + value = -value; + + switch (type) { + case kAxisButton: + posFlag = GetInputManager().GetKey (positiveButton)||GetInputManager().GetKey (altPositiveButton); + negFlag = GetInputManager().GetKey (negativeButton)||GetInputManager().GetKey (altNegativeButton); + rawValue = 0.0F; + + // Lock if both up and down are held + if (!(posFlag && negFlag)) { + if (posFlag) { + if (snap && value < 0.0) + value = 0.0; + else + value += sensitivity * time; + if (value < 0.0) + value += gravity * time; + + value = min (1.0F, value); + rawValue = 1.0F; + + } + else if (negFlag) { + if (snap && value > 0.0) + value = 0.0; + else + value -= sensitivity * time; + if (value > 0.0) + value -= gravity * time; + + value = max (-1.0F, value); + rawValue = -1.0F; + + } else + DoGravity (time); + } + + break; + + case kAxisMouse: +#if ENABLE_NEW_EVENT_SYSTEM + if (axis == 0) + value = GetInputManager().GetMouseDelta().x; + else if (axis == 1) + value = GetInputManager().GetMouseDelta().y; + else + value = GetInputManager().GetMouseScroll().x; +#else + delta = GetInputManager().GetMouseDelta (); + if (axis == 0) + value = delta.x; + else if (axis == 1) + value = delta.y; + else + value = delta.z; +#endif + + rawValue = value; + value *= sensitivity; + + break; + case kAxisJoystick: + value = GetInputManager().GetJoystickPosition (joyNum, axis); + rawValue = value; + value *= sensitivity; + + + if (value > 1.0F) + value = 1.0F; + else if (value < -1.0F) + value = -1.0F; + else if (value < dead && value > -dead) + value = 0.0F; + else if (value > 0.0F) + value = Lerp (0.0F, 1.0F, (value - dead) / (1.0F - dead)); + else + value = Lerp (0.0F, -1.0F, (-value - dead) / (1.0F - dead)); + break; + } + + if (invert) + { + value = -value; + rawValue = -rawValue; + } +} + + +void InputAxis::MakeAnalogKey (int pos, int neg, int altpos, int altnegpos) { + positiveButton = pos; + negativeButton = neg; + altPositiveButton = altpos; + altNegativeButton = altnegpos; + type = kAxisButton; + sensitivity = 3; + gravity = 3; + snap = true; +} + +void InputAxis::MakeButton (int button, int altbutton) { + positiveButton = button; + negativeButton = 0; + altPositiveButton = altbutton; + altNegativeButton = 0; + type = kAxisButton; + sensitivity = 1000; + gravity = 1000; + snap = false; +} + +void InputAxis::MakeMouse (int a) { + type = kAxisMouse; + axis = a; + dead = 0.0; + sensitivity = 0.1f; +} + +void InputAxis::MakeJoystick (int a) { + type = kAxisJoystick; + axis = a; + sensitivity = 1.0F; + dead = 0.19F; + gravity = 0.0F; + snap = false; + invert = false; +} + +UnityStr InputAxis::GetDescriptiveName(bool neg) { + if(neg) + if(descriptiveNegativeName.length()) + return descriptiveNegativeName; + else + if(descriptiveName.length()) + return descriptiveName+" (-)"; + else + return m_Name+" (-)"; + else + if(descriptiveName.length()) + if(negativeButton&&!descriptiveNegativeName.length()) + return descriptiveName+" (+)"; + else + return descriptiveName; + else + if(negativeButton) + return m_Name+" (+)"; + else + return m_Name; +} + +float InputAxis::GetValueRaw () const +{ + if (type == kAxisButton) + return rawValue; + else + return value; +} diff --git a/Runtime/Input/InputAxis.h b/Runtime/Input/InputAxis.h new file mode 100644 index 0000000..cde499b --- /dev/null +++ b/Runtime/Input/InputAxis.h @@ -0,0 +1,151 @@ +#ifndef INPUTAXIS_H +#define INPUTAXIS_H + +#include "Runtime/Serialize/SerializeUtility.h" +#include "Runtime/Serialize/SerializationMetaFlags.h" +#include <string> +using std::string; + +enum AxisType { + kAxisButton, + kAxisMouse, + kAxisJoystick, +}; + +/// The class for one input axis +class InputAxis { +public: + DECLARE_SERIALIZE (InputAxis) + + InputAxis (); + InputAxis (const string &name); + + const UnityStr &GetName () { return m_Name; } + void SetName (const string &name) { m_Name = name; } + + float GetValue () const { return value; } + float GetValueRaw () const; + + void Reset () { value = 0.0F; rawValue = 0.0F; } + /// Set the key codes for positiove and negative position. + void SetKeys (int pos, int neg) { positiveButton = pos; negativeButton = neg; } + void SetPosKey (int pos) { positiveButton = pos; } + int GetPosKey () const { return positiveButton; } + void SetNegKey (int neg) { negativeButton = neg; } + int GetNegKey () const { return negativeButton; } + + void SetAltPosKey (int pos) { altPositiveButton = pos; } + int GetAltPosKey () const { return altPositiveButton; } + void SetAltNegKey (int neg) { altNegativeButton = neg; } + int GetAltNegKey () const { return altNegativeButton; } + + /// Set the mouse or joystick axis for this input. + /// 0 = x, 1 = y + void SetAxis (int a) { axis = a; } + int GetAxis() { return axis; } + /// for joysticks: Set the joystick number to read. + void SetJoystickNumber (int input) { joyNum = input; } + int GetJoystickNumber () const { return joyNum; } + + /// Make this axis return to neutral if left alone + /// @param grav time it takes to return to neutral position + /// @param pos neutral position + void SetGravity (float grav) { gravity = grav; } + float GetGravity () const { return gravity; } + + /// How sensitive is this axis. + /// In mouse or joystick control, it maps the factor between mouse position and axis position. + /// If keyboard control, how much we should move for each update. HINT: Use a value < .5 + void SetSensitivity (float sens) { sensitivity = sens; } + float GetSensitivity () const { return sensitivity; } + + /// Keyboard control only: should the axis snap to neutral position if the opposite key is pressed + void SetKeySnap (bool s) { snap = s; } + bool GetKeySnap () const { return snap; } + /// Set the size of the center. + void SetDeadZone (float size) { dead = size; } + float GetDeadZone () const { return dead; } + + /// Should this axis be inverted ? + void SetInvert (bool invert) { this->invert = invert; } + bool GetInvert () const { return invert; } + + /// Helper: Make a sensible analog key controller. + void MakeAnalogKey (int keyPos, int keyNeg, int altKeyPos, int altKeyNeg); + /// Helper: Make a sensible fire button. + void MakeButton (int keyPos, int altKeyPos); + void MakeMouse (int axis); + void MakeJoystick (int axis); + + int GetType () { return type; } + + virtual void Update (); + + UnityStr GetDescriptiveName(bool neg); + + float& GetValueRawRef () { return rawValue; } + float& GetValueRef () { return value; } + + private: + UnityStr m_Name; + UnityStr descriptiveName; ///< Name presented to the user for setup if present + UnityStr descriptiveNegativeName; ///< Name for negative Button presented to the user for setup if present + int positiveButton; ///< Button to be pressed for movement in negative direction + int negativeButton; ///< Button to be pressed for movement in positive direction + int altPositiveButton; ///< alternative Button to be pressed for movement in negative direction + int altNegativeButton; ///< alternative Button to be pressed for movement in positive direction + int joyNum; ///< Joystick identifier index enum {Get Motion from all Joysticks = 0, Joystick 1,Joystick 2,Joystick 3,Joystick 4,Joystick 5,Joystick 6,Joystick 7,Joystick 8,Joystick 9,Joystick 10,Joystick 11 } + int type; ///< enum { Key or Mouse Button = 0, Mouse Movement, Joystick Axis} + float value; + float rawValue; + + int axis; ///< Axis to use enum { X axis = 0, Y axis = 1, 3rd axis (Joysticks and Scrollwheel) = 2, 4th axis (Joysticks) = 3, 5th axis (Joysticks) = 4, 6th axis (Joysticks) = 5, 7th axis (Joysticks) = 6, 8th axis (Joysticks) = 7, 9th axis (Joysticks) = 8, 10th axis (Joysticks) = 9 } + float gravity; ///< Speed (in units/sec) that the output value falls towards neutral when device at rest + float dead; ///< Size of the analog dead zone. All analog device values within this range map to neutral + float sensitivity; ///< Speed to move towards target value for digital devices (in units per sec) + bool snap; ///< If we have input in opposite direction of current, do we jump to neutral and continue from there? + bool invert; ///< flip positive and negative? + void DoGravity (float time); +}; + +std::string ConvertKeyToString (int key); +int ConvertStringToKey (const std::string& name); + +string KeyToString (int key); +int StringToKey (const string& name); + +template<class TransferFunc> +void InputAxis::Transfer (TransferFunc& transfer) +{ + transfer.SetVersion (3); + + TRANSFER(m_Name); + TRANSFER(descriptiveName); + TRANSFER(descriptiveNegativeName); + + TRANSFER_WITH_CUSTOM_GET_SET (UnityStr, "negativeButton", + value = KeyToString (negativeButton), + negativeButton = StringToKey (value), kSimpleEditorMask); + TRANSFER_WITH_CUSTOM_GET_SET (UnityStr, "positiveButton", + value = KeyToString (positiveButton), + positiveButton = StringToKey (value), kSimpleEditorMask); + TRANSFER_WITH_CUSTOM_GET_SET (UnityStr, "altNegativeButton", + value = KeyToString (altNegativeButton), + altNegativeButton = StringToKey (value), kSimpleEditorMask); + TRANSFER_WITH_CUSTOM_GET_SET (UnityStr, "altPositiveButton", + value = KeyToString (altPositiveButton), + altPositiveButton = StringToKey (value), kSimpleEditorMask); + + TRANSFER(gravity); + TRANSFER(dead); + TRANSFER_SIMPLE(sensitivity); + TRANSFER(snap); + TRANSFER(invert); + transfer.Align(); + + TRANSFER_SIMPLE(type); + TRANSFER(axis); + TRANSFER(joyNum); +} + +#endif diff --git a/Runtime/Input/InputManager.cpp b/Runtime/Input/InputManager.cpp new file mode 100644 index 0000000..e9d5e05 --- /dev/null +++ b/Runtime/Input/InputManager.cpp @@ -0,0 +1,1329 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#include "InputManager.h" +#include "LocationService.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Utilities/BitSetSerialization.h" +#include "Runtime/BaseClasses/ManagerContext.h" +#include "Runtime/Graphics/ScreenManager.h" +#include "Runtime/Math/Quaternion.h" +#include "PlatformDependent/iPhonePlayer/APN.h" +#include "Runtime/Misc/BuildSettings.h" + +#if ENABLE_NEW_EVENT_SYSTEM +#include "Runtime/Camera/RenderManager.h" +#include "Runtime/Dynamics/PhysicsManager.h" +#include "Runtime/Dynamics/Collider.h" +#include "Runtime/Dynamics/Rigidbody.h" +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Graphics/Transform.h" +#endif + +#include "Runtime/IMGUI/GUIState.h" +#include "Runtime/IMGUI/GUIWindows.h" + +#if SUPPORT_REPRODUCE_LOG +#include <fstream> +#include "Runtime/Misc/ReproductionLog.h" +#endif + +IMPLEMENT_CLASS_HAS_INIT (InputManager) +IMPLEMENT_OBJECT_SERIALIZE (InputManager) +IMPLEMENT_CLUSTER_SERIALIZE (InputManager) +GET_MANAGER (InputManager) +GET_MANAGER_PTR (InputManager) + +using namespace std; + + +InputManager::InputManager (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + MakeDefault (); + m_CurrentKeyState.resize (kKeyAndJoyButtonCount, false); + m_ThisFrameKeyDown.resize (kKeyAndJoyButtonCount, false); + m_ThisFrameKeyUp.resize (kKeyAndJoyButtonCount, false); + +#if ENABLE_NEW_EVENT_SYSTEM + m_CurrentTouch = NULL; + m_Selection = NULL; +#else + m_MousePos = Vector2f (0.0F,0.0F); + m_MouseDelta = Vector3f (0.0F,0.0F,0.0F); +#endif + m_MousePresent = true; + m_ShouldQuit = false; + m_TextFieldInput = false; + m_IMEIsSelected = false; + m_SimulateMouseWithTouches = true; + + for(int i=0;i<kMaxJoySticks;i++) + { + std::vector<float> axes; + for(int j=0;j<kMaxJoyStickAxis;j++) + axes.push_back(0.0); + m_JoystickPos.push_back(axes); + } + + m_EatKeyPressOnTextFieldFocus = true; + m_IMECompositionMode = kCompositionModeAuto; +} + +InputManager::~InputManager () { +} + +void InputManager::Reset () +{ + Super::Reset(); + + m_Axes.clear (); + m_CompositionString.clear (); + MakeDefault (); +} + +void InputManager::MakeDefault () { + m_Axes.push_back (InputAxis ("Horizontal")); + m_Axes.push_back (InputAxis ("Vertical")); + m_Axes[0].MakeAnalogKey (StringToKey ("right"), StringToKey ("left"), StringToKey ("d"), StringToKey ("a")); + m_Axes[1].MakeAnalogKey (StringToKey ("up"), StringToKey ("down"), StringToKey ("w"), StringToKey ("s")); + + m_Axes.push_back (InputAxis ("Fire1")); + m_Axes.push_back (InputAxis ("Fire2")); + m_Axes.push_back (InputAxis ("Fire3")); + m_Axes.push_back (InputAxis ("Jump")); + m_Axes[2].MakeButton (StringToKey ("left ctrl"), StringToKey ("mouse 0")); + m_Axes[3].MakeButton (StringToKey ("left alt"), StringToKey ("mouse 1")); + m_Axes[4].MakeButton (StringToKey ("left cmd"), StringToKey ("mouse 2")); + m_Axes[5].MakeButton (StringToKey ("space"), 0); + + m_Axes.push_back (InputAxis ("Mouse X")); + m_Axes.push_back (InputAxis ("Mouse Y")); + m_Axes.push_back (InputAxis ("Mouse ScrollWheel")); + m_Axes[6].MakeMouse (0); + m_Axes[7].MakeMouse (1); + m_Axes[8].MakeMouse (2); + + m_Axes.push_back (InputAxis ("Horizontal")); + m_Axes.push_back (InputAxis ("Vertical")); + m_Axes[9].MakeJoystick (0); + m_Axes[10].MakeJoystick (1); + m_Axes[10].SetInvert(true); + + m_Axes.push_back (InputAxis ("Fire1")); + m_Axes.push_back (InputAxis ("Fire2")); + m_Axes.push_back (InputAxis ("Fire3")); + m_Axes.push_back (InputAxis ("Jump")); + m_Axes[11].MakeButton (StringToKey ("joystick button 0"), 0); + m_Axes[12].MakeButton (StringToKey ("joystick button 1"), 0); + m_Axes[13].MakeButton (StringToKey ("joystick button 2"), 0); + m_Axes[14].MakeButton (StringToKey ("joystick button 3"), 0); +} + +bool InputManager::GetButton (const string &name) { + bool finalButton = false; + for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) { + if (i->GetName() == name) + { + finalButton |= GetKey(i->GetPosKey ()); + finalButton |= GetKey(i->GetNegKey ()); + finalButton |= GetKey(i->GetAltPosKey ()); + finalButton |= GetKey(i->GetAltNegKey ()); + } + } + return finalButton; +} + +bool InputManager::GetButtonDown (const string &name) +{ + bool finalButton = false; + for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) + { + if (i->GetName() == name && i->GetType () == kAxisButton) + { + finalButton |= m_ThisFrameKeyDown[i->GetPosKey ()]; + finalButton |= m_ThisFrameKeyDown[i->GetNegKey ()]; + finalButton |= m_ThisFrameKeyDown[i->GetAltPosKey ()]; + finalButton |= m_ThisFrameKeyDown[i->GetAltNegKey ()]; + } + } + return finalButton; +} + +bool InputManager::GetButtonUp (const string &name) +{ + bool finalButton = false; + for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) + { + if (i->GetName() == name && i->GetType () == kAxisButton) + { + finalButton |= m_ThisFrameKeyUp[i->GetPosKey ()]; + finalButton |= m_ThisFrameKeyUp[i->GetNegKey ()]; + finalButton |= m_ThisFrameKeyUp[i->GetAltPosKey ()]; + finalButton |= m_ThisFrameKeyUp[i->GetAltNegKey ()]; + } + } + return finalButton; +} + + +float InputManager::GetAxis (const string &name) +{ + float finalValue = 0.0F; + for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) { + if (i->GetName() == name && Abs (i->GetValue ()) > Abs (finalValue)) + finalValue = i->GetValue (); + } + return finalValue; +} + +float InputManager::GetAxisRaw (const string &name) { + float finalValue = 0.0F; + for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) { + if (i->GetName() == name && Abs (i->GetValueRaw ()) > Abs (finalValue)) + finalValue = i->GetValueRaw (); + } + return finalValue; +} + + +bool InputManager::HasAxisOrButton (const string& name) +{ + for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end();i++) { + if (i->GetName() == name) + return true; + } + return false; +} + +// Might consider doing this inside InputAxis somehow... +void InputManager::CheckConsistency () { + Super::CheckConsistency (); + ResetInputAxes (); +} + +void InputManager::ResetInputAxes () +{ + for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end(); i++) + i->Reset(); + + m_CurrentKeyState.reset (); + m_ThisFrameKeyDown.reset (); + m_ThisFrameKeyUp.reset(); + + for (int i=0;i<m_JoystickPos.size ();i++) + { + for (int j=0;j<m_JoystickPos[i].size ();j++) + m_JoystickPos[i][j] = 0.0F; + } + +#if ENABLE_NEW_EVENT_SYSTEM + for (int i = 0; i < 3; ++i) + { + m_Mouse[i].deltaPos = Vector2f::zero; + m_Mouse[i].deltaScroll = Vector2f::zero; + } +#else + m_MouseDelta = Vector3f (0,0,0); +#endif +} + +bool InputManager::GetAnyKey () +{ + #if SUPPORT_REPRODUCE_LOG + if(GetKeyDown(286) || GetKeyDown(287)) + return false; + #endif + return m_CurrentKeyState.any () || m_ThisFrameKeyDown.any (); +} + +bool InputManager::GetAnyKeyThisFrame () +{ + #if SUPPORT_REPRODUCE_LOG + if(GetKeyDown(286) || GetKeyDown(287)) + return false; + #endif + return m_ThisFrameKeyDown.any (); +} + +template<class TransferFunc> +void InputManager::Transfer (TransferFunc& transfer) { + Super::Transfer (transfer); + TRANSFER_SIMPLE (m_Axes); +} + +#if ENABLE_CLUSTER_SYNC +template<class TransferFunc> +void InputManager::ClusterTransfer (TransferFunc& transfer) { + + TRANSFER(m_Axes); + + TRANSFER(m_CurrentKeyState); + TRANSFER(m_ThisFrameKeyDown); + TRANSFER(m_ThisFrameKeyUp); + + TRANSFER(m_MouseDelta.x); + TRANSFER(m_MouseDelta.y); + TRANSFER(m_MouseDelta.z); + + TRANSFER(m_MousePos.x); + TRANSFER(m_MousePos.y); +} +#endif + + + +float InputManager::GetJoystickPosition (int joyNum, int axis) const +{ + if (joyNum < m_JoystickPos.size () && axis < m_JoystickPos[joyNum].size ()) + return m_JoystickPos[joyNum][axis]; + else + return 0; +} + +void InputManager::SetJoystickPosition (int joyNum, int axis, float pos) +{ + if (joyNum < m_JoystickPos.size () && axis < m_JoystickPos[joyNum].size ()) + { + m_JoystickPos[joyNum][axis]=pos; + } +} + + +typedef std::map<int, std::string> KeyToName; +typedef std::map<std::string, int> NameToKey; +static KeyToName* g_KeyToName = NULL; +static NameToKey* g_NameToKey = NULL; + +void SetupKeyNameMapping (); +void InputManager::InitializeClass() +{ + g_KeyToName = UNITY_NEW(KeyToName, kMemResource); + g_NameToKey = UNITY_NEW(NameToKey, kMemResource); + SetupKeyNameMapping(); +} + +void InputManager::CleanupClass() +{ + UNITY_DELETE(g_KeyToName, kMemResource); + UNITY_DELETE(g_NameToKey, kMemResource); +} + +void SetupKeyNameMapping () +{ + static bool isInitialized = false; + if (isInitialized) + return; + isInitialized = true; + + SET_ALLOC_OWNER(NULL); + + (*g_KeyToName)[(int)SDLK_BACKSPACE] = "backspace"; + (*g_KeyToName)[(int)SDLK_TAB] = "tab"; + (*g_KeyToName)[(int)SDLK_CLEAR] = "clear"; + (*g_KeyToName)[(int)SDLK_RETURN] = "return"; + (*g_KeyToName)[(int)SDLK_PAUSE] = "pause"; + (*g_KeyToName)[(int)SDLK_ESCAPE] = "escape"; + (*g_KeyToName)[(int)SDLK_SPACE] = "space"; + (*g_KeyToName)[(int)SDLK_EXCLAIM] = "!"; + (*g_KeyToName)[(int)SDLK_QUOTEDBL] = "\""; + (*g_KeyToName)[(int)SDLK_HASH] = "#"; + (*g_KeyToName)[(int)SDLK_DOLLAR] = "$"; + (*g_KeyToName)[(int)SDLK_AMPERSAND] = "&"; + (*g_KeyToName)[(int)SDLK_QUOTE] = "'"; + (*g_KeyToName)[(int)SDLK_LEFTPAREN] = "("; + (*g_KeyToName)[(int)SDLK_RIGHTPAREN] = ")"; + (*g_KeyToName)[(int)SDLK_ASTERISK] = "*"; + (*g_KeyToName)[(int)SDLK_PLUS] = "+"; + (*g_KeyToName)[(int)SDLK_COMMA] = ","; + (*g_KeyToName)[(int)SDLK_MINUS] = "-"; + (*g_KeyToName)[(int)SDLK_PERIOD] = "."; + (*g_KeyToName)[(int)SDLK_SLASH] = "/"; + (*g_KeyToName)[(int)SDLK_0] = "0"; + (*g_KeyToName)[(int)SDLK_1] = "1"; + (*g_KeyToName)[(int)SDLK_2] = "2"; + (*g_KeyToName)[(int)SDLK_3] = "3"; + (*g_KeyToName)[(int)SDLK_4] = "4"; + (*g_KeyToName)[(int)SDLK_5] = "5"; + (*g_KeyToName)[(int)SDLK_6] = "6"; + (*g_KeyToName)[(int)SDLK_7] = "7"; + (*g_KeyToName)[(int)SDLK_8] = "8"; + (*g_KeyToName)[(int)SDLK_9] = "9"; + (*g_KeyToName)[(int)SDLK_COLON] = ":"; + (*g_KeyToName)[(int)SDLK_SEMICOLON] = ";"; + (*g_KeyToName)[(int)SDLK_LESS] = "<"; + (*g_KeyToName)[(int)SDLK_EQUALS] = "="; + (*g_KeyToName)[(int)SDLK_GREATER] = ">"; + (*g_KeyToName)[(int)SDLK_QUESTION] = "?"; + (*g_KeyToName)[(int)SDLK_AT] = "@"; + (*g_KeyToName)[(int)SDLK_LEFTBRACKET] = "["; + (*g_KeyToName)[(int)SDLK_BACKSLASH] = "\\"; + (*g_KeyToName)[(int)SDLK_RIGHTBRACKET] = "]"; + (*g_KeyToName)[(int)SDLK_CARET] = "^"; + (*g_KeyToName)[(int)SDLK_UNDERSCORE] = "_"; + (*g_KeyToName)[(int)SDLK_BACKQUOTE] = "`"; + (*g_KeyToName)[(int)SDLK_a] = "a"; + (*g_KeyToName)[(int)SDLK_b] = "b"; + (*g_KeyToName)[(int)SDLK_c] = "c"; + (*g_KeyToName)[(int)SDLK_d] = "d"; + (*g_KeyToName)[(int)SDLK_e] = "e"; + (*g_KeyToName)[(int)SDLK_f] = "f"; + (*g_KeyToName)[(int)SDLK_g] = "g"; + (*g_KeyToName)[(int)SDLK_h] = "h"; + (*g_KeyToName)[(int)SDLK_i] = "i"; + (*g_KeyToName)[(int)SDLK_j] = "j"; + (*g_KeyToName)[(int)SDLK_k] = "k"; + (*g_KeyToName)[(int)SDLK_l] = "l"; + (*g_KeyToName)[(int)SDLK_m] = "m"; + (*g_KeyToName)[(int)SDLK_n] = "n"; + (*g_KeyToName)[(int)SDLK_o] = "o"; + (*g_KeyToName)[(int)SDLK_p] = "p"; + (*g_KeyToName)[(int)SDLK_q] = "q"; + (*g_KeyToName)[(int)SDLK_r] = "r"; + (*g_KeyToName)[(int)SDLK_s] = "s"; + (*g_KeyToName)[(int)SDLK_t] = "t"; + (*g_KeyToName)[(int)SDLK_u] = "u"; + (*g_KeyToName)[(int)SDLK_v] = "v"; + (*g_KeyToName)[(int)SDLK_w] = "w"; + (*g_KeyToName)[(int)SDLK_x] = "x"; + (*g_KeyToName)[(int)SDLK_y] = "y"; + (*g_KeyToName)[(int)SDLK_z] = "z"; + (*g_KeyToName)[(int)SDLK_DELETE] = "delete"; + + (*g_KeyToName)[(int)SDLK_WORLD_0] = "world 0"; + (*g_KeyToName)[(int)SDLK_WORLD_1] = "world 1"; + (*g_KeyToName)[(int)SDLK_WORLD_2] = "world 2"; + (*g_KeyToName)[(int)SDLK_WORLD_3] = "world 3"; + (*g_KeyToName)[(int)SDLK_WORLD_4] = "world 4"; + (*g_KeyToName)[(int)SDLK_WORLD_5] = "world 5"; + (*g_KeyToName)[(int)SDLK_WORLD_6] = "world 6"; + (*g_KeyToName)[(int)SDLK_WORLD_7] = "world 7"; + (*g_KeyToName)[(int)SDLK_WORLD_8] = "world 8"; + (*g_KeyToName)[(int)SDLK_WORLD_9] = "world 9"; + (*g_KeyToName)[(int)SDLK_WORLD_10] = "world 10"; + (*g_KeyToName)[(int)SDLK_WORLD_11] = "world 11"; + (*g_KeyToName)[(int)SDLK_WORLD_12] = "world 12"; + (*g_KeyToName)[(int)SDLK_WORLD_13] = "world 13"; + (*g_KeyToName)[(int)SDLK_WORLD_14] = "world 14"; + (*g_KeyToName)[(int)SDLK_WORLD_15] = "world 15"; + (*g_KeyToName)[(int)SDLK_WORLD_16] = "world 16"; + (*g_KeyToName)[(int)SDLK_WORLD_17] = "world 17"; + (*g_KeyToName)[(int)SDLK_WORLD_18] = "world 18"; + (*g_KeyToName)[(int)SDLK_WORLD_19] = "world 19"; + (*g_KeyToName)[(int)SDLK_WORLD_20] = "world 20"; + (*g_KeyToName)[(int)SDLK_WORLD_21] = "world 21"; + (*g_KeyToName)[(int)SDLK_WORLD_22] = "world 22"; + (*g_KeyToName)[(int)SDLK_WORLD_23] = "world 23"; + (*g_KeyToName)[(int)SDLK_WORLD_24] = "world 24"; + (*g_KeyToName)[(int)SDLK_WORLD_25] = "world 25"; + (*g_KeyToName)[(int)SDLK_WORLD_26] = "world 26"; + (*g_KeyToName)[(int)SDLK_WORLD_27] = "world 27"; + (*g_KeyToName)[(int)SDLK_WORLD_28] = "world 28"; + (*g_KeyToName)[(int)SDLK_WORLD_29] = "world 29"; + (*g_KeyToName)[(int)SDLK_WORLD_30] = "world 30"; + (*g_KeyToName)[(int)SDLK_WORLD_31] = "world 31"; + (*g_KeyToName)[(int)SDLK_WORLD_32] = "world 32"; + (*g_KeyToName)[(int)SDLK_WORLD_33] = "world 33"; + (*g_KeyToName)[(int)SDLK_WORLD_34] = "world 34"; + (*g_KeyToName)[(int)SDLK_WORLD_35] = "world 35"; + (*g_KeyToName)[(int)SDLK_WORLD_36] = "world 36"; + (*g_KeyToName)[(int)SDLK_WORLD_37] = "world 37"; + (*g_KeyToName)[(int)SDLK_WORLD_38] = "world 38"; + (*g_KeyToName)[(int)SDLK_WORLD_39] = "world 39"; + (*g_KeyToName)[(int)SDLK_WORLD_40] = "world 40"; + (*g_KeyToName)[(int)SDLK_WORLD_41] = "world 41"; + (*g_KeyToName)[(int)SDLK_WORLD_42] = "world 42"; + (*g_KeyToName)[(int)SDLK_WORLD_43] = "world 43"; + (*g_KeyToName)[(int)SDLK_WORLD_44] = "world 44"; + (*g_KeyToName)[(int)SDLK_WORLD_45] = "world 45"; + (*g_KeyToName)[(int)SDLK_WORLD_46] = "world 46"; + (*g_KeyToName)[(int)SDLK_WORLD_47] = "world 47"; + (*g_KeyToName)[(int)SDLK_WORLD_48] = "world 48"; + (*g_KeyToName)[(int)SDLK_WORLD_49] = "world 49"; + (*g_KeyToName)[(int)SDLK_WORLD_50] = "world 50"; + (*g_KeyToName)[(int)SDLK_WORLD_51] = "world 51"; + (*g_KeyToName)[(int)SDLK_WORLD_52] = "world 52"; + (*g_KeyToName)[(int)SDLK_WORLD_53] = "world 53"; + (*g_KeyToName)[(int)SDLK_WORLD_54] = "world 54"; + (*g_KeyToName)[(int)SDLK_WORLD_55] = "world 55"; + (*g_KeyToName)[(int)SDLK_WORLD_56] = "world 56"; + (*g_KeyToName)[(int)SDLK_WORLD_57] = "world 57"; + (*g_KeyToName)[(int)SDLK_WORLD_58] = "world 58"; + (*g_KeyToName)[(int)SDLK_WORLD_59] = "world 59"; + (*g_KeyToName)[(int)SDLK_WORLD_60] = "world 60"; + (*g_KeyToName)[(int)SDLK_WORLD_61] = "world 61"; + (*g_KeyToName)[(int)SDLK_WORLD_62] = "world 62"; + (*g_KeyToName)[(int)SDLK_WORLD_63] = "world 63"; + (*g_KeyToName)[(int)SDLK_WORLD_64] = "world 64"; + (*g_KeyToName)[(int)SDLK_WORLD_65] = "world 65"; + (*g_KeyToName)[(int)SDLK_WORLD_66] = "world 66"; + (*g_KeyToName)[(int)SDLK_WORLD_67] = "world 67"; + (*g_KeyToName)[(int)SDLK_WORLD_68] = "world 68"; + (*g_KeyToName)[(int)SDLK_WORLD_69] = "world 69"; + (*g_KeyToName)[(int)SDLK_WORLD_70] = "world 70"; + (*g_KeyToName)[(int)SDLK_WORLD_71] = "world 71"; + (*g_KeyToName)[(int)SDLK_WORLD_72] = "world 72"; + (*g_KeyToName)[(int)SDLK_WORLD_73] = "world 73"; + (*g_KeyToName)[(int)SDLK_WORLD_74] = "world 74"; + (*g_KeyToName)[(int)SDLK_WORLD_75] = "world 75"; + (*g_KeyToName)[(int)SDLK_WORLD_76] = "world 76"; + (*g_KeyToName)[(int)SDLK_WORLD_77] = "world 77"; + (*g_KeyToName)[(int)SDLK_WORLD_78] = "world 78"; + (*g_KeyToName)[(int)SDLK_WORLD_79] = "world 79"; + (*g_KeyToName)[(int)SDLK_WORLD_80] = "world 80"; + (*g_KeyToName)[(int)SDLK_WORLD_81] = "world 81"; + (*g_KeyToName)[(int)SDLK_WORLD_82] = "world 82"; + (*g_KeyToName)[(int)SDLK_WORLD_83] = "world 83"; + (*g_KeyToName)[(int)SDLK_WORLD_84] = "world 84"; + (*g_KeyToName)[(int)SDLK_WORLD_85] = "world 85"; + (*g_KeyToName)[(int)SDLK_WORLD_86] = "world 86"; + (*g_KeyToName)[(int)SDLK_WORLD_87] = "world 87"; + (*g_KeyToName)[(int)SDLK_WORLD_88] = "world 88"; + (*g_KeyToName)[(int)SDLK_WORLD_89] = "world 89"; + (*g_KeyToName)[(int)SDLK_WORLD_90] = "world 90"; + (*g_KeyToName)[(int)SDLK_WORLD_91] = "world 91"; + (*g_KeyToName)[(int)SDLK_WORLD_92] = "world 92"; + (*g_KeyToName)[(int)SDLK_WORLD_93] = "world 93"; + (*g_KeyToName)[(int)SDLK_WORLD_94] = "world 94"; + (*g_KeyToName)[(int)SDLK_WORLD_95] = "world 95"; + + (*g_KeyToName)[(int)SDLK_KP0] = "[0]"; + (*g_KeyToName)[(int)SDLK_KP1] = "[1]"; + (*g_KeyToName)[(int)SDLK_KP2] = "[2]"; + (*g_KeyToName)[(int)SDLK_KP3] = "[3]"; + (*g_KeyToName)[(int)SDLK_KP4] = "[4]"; + (*g_KeyToName)[(int)SDLK_KP5] = "[5]"; + (*g_KeyToName)[(int)SDLK_KP6] = "[6]"; + (*g_KeyToName)[(int)SDLK_KP7] = "[7]"; + (*g_KeyToName)[(int)SDLK_KP8] = "[8]"; + (*g_KeyToName)[(int)SDLK_KP9] = "[9]"; + (*g_KeyToName)[(int)SDLK_KP_PERIOD] = "[.]"; + (*g_KeyToName)[(int)SDLK_KP_DIVIDE] = "[/]"; + (*g_KeyToName)[(int)SDLK_KP_MULTIPLY] = "[*]"; + (*g_KeyToName)[(int)SDLK_KP_MINUS] = "[-]"; + (*g_KeyToName)[(int)SDLK_KP_PLUS] = "[+]"; + (*g_KeyToName)[(int)SDLK_KP_ENTER] = "enter"; + (*g_KeyToName)[(int)SDLK_KP_EQUALS] = "equals"; + + (*g_KeyToName)[(int)SDLK_UP] = "up"; + (*g_KeyToName)[(int)SDLK_DOWN] = "down"; + (*g_KeyToName)[(int)SDLK_RIGHT] = "right"; + (*g_KeyToName)[(int)SDLK_LEFT] = "left"; + (*g_KeyToName)[(int)SDLK_DOWN] = "down"; + (*g_KeyToName)[(int)SDLK_INSERT] = "insert"; + (*g_KeyToName)[(int)SDLK_HOME] = "home"; + (*g_KeyToName)[(int)SDLK_END] = "end"; + (*g_KeyToName)[(int)SDLK_PAGEUP] = "page up"; + (*g_KeyToName)[(int)SDLK_PAGEDOWN] = "page down"; + + (*g_KeyToName)[(int)SDLK_F1] = "f1"; + (*g_KeyToName)[(int)SDLK_F2] = "f2"; + (*g_KeyToName)[(int)SDLK_F3] = "f3"; + (*g_KeyToName)[(int)SDLK_F4] = "f4"; + (*g_KeyToName)[(int)SDLK_F5] = "f5"; + (*g_KeyToName)[(int)SDLK_F6] = "f6"; + (*g_KeyToName)[(int)SDLK_F7] = "f7"; + (*g_KeyToName)[(int)SDLK_F8] = "f8"; + (*g_KeyToName)[(int)SDLK_F9] = "f9"; + (*g_KeyToName)[(int)SDLK_F10] = "f10"; + (*g_KeyToName)[(int)SDLK_F11] = "f11"; + (*g_KeyToName)[(int)SDLK_F12] = "f12"; + (*g_KeyToName)[(int)SDLK_F13] = "f13"; + (*g_KeyToName)[(int)SDLK_F14] = "f14"; + (*g_KeyToName)[(int)SDLK_F15] = "f15"; + + (*g_KeyToName)[(int)SDLK_NUMLOCK] = "numlock"; + (*g_KeyToName)[(int)SDLK_CAPSLOCK] = "caps lock"; + (*g_KeyToName)[(int)SDLK_SCROLLOCK] = "scroll lock"; + (*g_KeyToName)[(int)SDLK_RSHIFT] = "right shift"; + (*g_KeyToName)[(int)SDLK_LSHIFT] = "left shift"; + (*g_KeyToName)[(int)SDLK_RCTRL] = "right ctrl"; + (*g_KeyToName)[(int)SDLK_LCTRL] = "left ctrl"; + (*g_KeyToName)[(int)SDLK_RALT] = "right alt"; + (*g_KeyToName)[(int)SDLK_LALT] = "left alt"; + (*g_KeyToName)[(int)SDLK_RMETA] = "right cmd"; + (*g_KeyToName)[(int)SDLK_LMETA] = "left cmd"; + (*g_KeyToName)[(int)SDLK_LSUPER] = "left super"; /* "Windows" keys */ + (*g_KeyToName)[(int)SDLK_RSUPER] = "right super"; + (*g_KeyToName)[(int)SDLK_MODE] = "alt gr"; + (*g_KeyToName)[(int)SDLK_COMPOSE] = "compose"; + + (*g_KeyToName)[(int)SDLK_HELP] = "help"; + (*g_KeyToName)[(int)SDLK_PRINT] = "print screen"; + (*g_KeyToName)[(int)SDLK_SYSREQ] = "sys req"; + (*g_KeyToName)[(int)SDLK_BREAK] = "break"; + (*g_KeyToName)[(int)SDLK_MENU] = "menu"; + (*g_KeyToName)[(int)SDLK_POWER] = "power"; + (*g_KeyToName)[(int)SDLK_EURO] = "euro"; + (*g_KeyToName)[(int)SDLK_UNDO] = "undo"; + + (*g_KeyToName)[(int)kKeyCount + 0] = "mouse 0"; + (*g_KeyToName)[(int)kKeyCount + 1] = "mouse 1"; + (*g_KeyToName)[(int)kKeyCount + 2] = "mouse 2"; + (*g_KeyToName)[(int)kKeyCount + 3] = "mouse 3"; + (*g_KeyToName)[(int)kKeyCount + 4] = "mouse 4"; + (*g_KeyToName)[(int)kKeyCount + 5] = "mouse 5"; + (*g_KeyToName)[(int)kKeyCount + 6] = "mouse 6"; + + for (int joystick=0;joystick<kMaxJoySticks;joystick++) { + for (int button=0;button<kMaxJoyStickButtons;button++) { + char buffy[100]; + if(joystick!=0) + sprintf (buffy, "joystick %d button %d", joystick, button); + else + sprintf (buffy, "joystick button %d", button); + (*g_KeyToName)[kKeyAndMouseButtonCount + joystick * kMaxJoyStickButtons + button] = buffy; + } + } + + AssertIf ((int)kKeyCount != (int)SDLK_LAST); + + g_NameToKey->clear (); + for (KeyToName::iterator i=g_KeyToName->begin ();i != g_KeyToName->end ();i++) + (*g_NameToKey)[i->second] = i->first; +} + +string KeyToString (int key) { + KeyToName::iterator found = g_KeyToName->find (key); + if (found == g_KeyToName->end ()) + return string (); + else + return found->second; +} + +int StringToKey (const string& name) { + NameToKey::iterator found = g_NameToKey->find (name); + if (found == g_NameToKey->end ()) + return 0; + else + return found->second; +} + +void InputManager::InputEndFrame () +{ + m_ThisFrameKeyDown.reset (); + m_ThisFrameKeyUp.reset (); +#if ENABLE_NEW_EVENT_SYSTEM + m_Mouse->deltaPos = Vector2f::zero; + m_Mouse->deltaScroll = Vector2f::zero; +#else + m_MouseDelta = Vector3f::zero; +#endif + m_InputString.clear (); +} + +void InputManager::ProcessInput() +{ + // Update Joystick 0 + + if (m_JoystickPos.size() > 0) + { + vector<float>& joy0 = m_JoystickPos[0]; + + for (vector<float>::iterator axis = joy0.begin(); axis != joy0.end(); ++axis) + { + *axis = 0.0f; + } + + for (vector<vector<float> >::const_iterator joy = (m_JoystickPos.begin() + 1); joy != m_JoystickPos.end(); ++joy) + { + size_t const size = min(joy0.size(), joy->size()); + + for (size_t i = 0; i < size; ++i) + { + if (abs((*joy)[i]) > abs(joy0[i])) + { + joy0[i] = (*joy)[i]; + } + } + } + } + + //Update Axes + for (vector<InputAxis>::iterator i = m_Axes.begin(); i != m_Axes.end(); i++) + i->Update(); +} + +#if ENABLE_NEW_EVENT_SYSTEM +Rigidbody* FindRigidbodyInParents (GameObject& root) +{ + Rigidbody* rb = root.QueryComponentT<Rigidbody>(ClassID(Rigidbody)); + + if (rb != NULL) + return rb; + + Transform* parent = root.GetComponentT<Transform>(ClassID(Transform)).GetParent(); + + if (parent == NULL) + return NULL; + + return FindRigidbodyInParents(parent->GetGameObject()); +} + +void InputManager::SendInputEvents() +{ + // Prior to Unity 4.1 events were sent out via C# in MouseEvents.cs, and only for mouse events + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1)) + { + ProcessMouse(); + ProcessTouches(); + m_CurrentTouch = NULL; + } +} + +void InputManager::ProcessMouse () +{ + const RenderManager::CameraContainer& cameras = GetRenderManager().GetOnscreenCameras(); + + // Mouse events are handled as separate touches for each button + for (int b = 0; b < 3; ++b) + { + // Touch IDs: -1 for LMB, -2 for RMB, -3 for MMB. + // Touch IDs for actual touch events are 0+. + m_CurrentTouch = &m_Mouse[b]; + m_CurrentTouch->id = -1 - b; + + bool isPressed = GetMouseButtonDown(b); + bool isReleased = GetMouseButtonUp(b); + bool isDragging = (m_CurrentTouch->press != NULL); + + if (isReleased) m_CurrentTouch->phase = Touch::kTouchEnded; + else if (isPressed) m_CurrentTouch->phase = Touch::kTouchBegan; + else m_CurrentTouch->phase = Touch::kTouchMoved; + + // TODO: What order are these in? We need to go from highest depth to lowest depth for events + for (RenderManager::CameraContainer::const_iterator i = cameras.begin(); i != cameras.end(); ++i) + { + RaycastHit hit; + const Camera& cam = (**i); + Ray ray = cam.ScreenPointToRay(m_Mouse[0].pos); + UInt32 mask = cam.GetEventMask() & cam.GetCullingMask(); + + GameObject* go = NULL; + + if (GetPhysicsManager().Raycast(ray, 1000.0f, hit, mask)) + { + m_CurrentTouch->worldPos = hit.point; + go = &hit.collider->GetGameObject(); + + // Raycast hitting a collider should target its rigidbody instead + Rigidbody* rb = FindRigidbodyInParents(*go); + if (rb != NULL) + go = &rb->GetGameObject(); + } + + if (isPressed) + { + // Newly pressed -- clear all values + m_CurrentTouch->press = NULL; + m_CurrentTouch->oldPos = m_CurrentTouch->pos; + //m_CurrentTouch->eligibleForClick = true; + m_CurrentTouch->deltaPos = Vector2f::zero; + m_CurrentTouch->deltaScroll = Vector2f::zero; + } + else if (b == 0) + { + // Left mouse button should calculate delta + m_CurrentTouch->deltaPos = m_CurrentTouch->pos - m_CurrentTouch->oldPos; + m_CurrentTouch->oldPos = m_CurrentTouch->pos; + } + else + { + // Other mouse buttons should simply copy the first button's data + m_CurrentTouch->pos = m_Mouse[0].pos; + m_CurrentTouch->deltaPos = m_Mouse[0].deltaPos; + m_CurrentTouch->deltaScroll = m_Mouse[0].deltaScroll; + m_CurrentTouch->deltaTime = m_Mouse[0].deltaTime; + } + + // Process this mouse event as a touch + ProcessTouch(go, isPressed, isReleased, isDragging, b == 0); + if (go != NULL) break; + } + } +} + +void InputManager::ProcessTouches() +{ + const RenderManager::CameraContainer& cameras = GetRenderManager().GetOnscreenCameras(); + + size_t count = GetTouchCount(); + + for (size_t i = 0; i < count; ++i) + { + m_CurrentTouch = GetTouch(count); + + bool isPressed = (m_CurrentTouch->phase == Touch::kTouchBegan); + bool isReleased = (m_CurrentTouch->phase == Touch::kTouchEnded) || (m_CurrentTouch->phase == Touch::kTouchCanceled); + + for (RenderManager::CameraContainer::const_iterator i = cameras.begin(); i != cameras.end(); ++i) + { + RaycastHit hit; + const Camera& cam = (**i); + Ray ray = cam.ScreenPointToRay(m_Mouse[0].pos); + UInt32 mask = cam.GetEventMask() & cam.GetCullingMask(); + bool isHit = GetPhysicsManager().Raycast(ray, 1000.0f, hit, mask); + + GameObject* go = NULL; + + if (GetPhysicsManager().Raycast(ray, 1000.0f, hit, mask)) + { + m_CurrentTouch->worldPos = hit.point; + go = &hit.collider->GetGameObject(); + + // Raycast hitting a collider should target its rigidbody instead + Rigidbody* rb = FindRigidbodyInParents(*go); + if (rb != NULL) + go = &rb->GetGameObject(); + } + + if (m_CurrentTouch->phase == Touch::kTouchBegan) + { + m_CurrentTouch->press = NULL; + m_CurrentTouch->oldPos = m_CurrentTouch->pos; + //m_CurrentTouch->eligibleForClick = true; + m_CurrentTouch->deltaPos = Vector2f::zero; + m_CurrentTouch->deltaScroll = Vector2f::zero; + } + + ProcessTouch(go, isPressed, isReleased, true, true); + if (go != NULL) break; + } + } +} + +void InputManager::ProcessTouch (GameObject* hover, bool isPressed, bool isReleased, bool isDragging, bool moveEvents) +{ + GUIState& state = GetGUIState(); + state.BeginUsingEvents(); + + // Save the previous game objects prior to updates + GameObject* previousHover = m_CurrentTouch->hover; + GameObject* previousPress = m_CurrentTouch->press; + + // Update the current hovered so that Input.current.hover is proper in the callbacks below + m_CurrentTouch->hover = hover; + + // Update the pressed object if it has changed + if (isPressed) m_CurrentTouch->press = hover; + + MessageData data; + InputEvent ev; + ev.touch = *m_CurrentTouch; + ev.button = -m_CurrentTouch->id; + + // Set the event so that C# can access it via Event.current.touch + GetGUIState().SetEvent(ev); + + // Move and Drag notifications + if (Magnitude(m_CurrentTouch->deltaPos) > 0.001f) + { + if (previousPress != NULL) + { + previousPress->SendMessageAny(kOnDragEvent, data); + } + else if (previousHover != NULL) + { + if (moveEvents) + previousHover->SendMessageAny(kOnMouseMoveEvent, data); + } + } + + // Hovering over a different object + if (previousHover != hover) + { + if (moveEvents && hover != NULL) + hover->SendMessageAny(isDragging ? kOnDragEnterEvent : kOnMouseEnterEvent, data); + + if (moveEvents && previousHover != NULL) + previousHover->SendMessageAny(isDragging ? kOnDragExitEvent : kOnMouseExitEvent, data); + } + + // Pressed the mouse button on something + if (isPressed) + { + // Change the selection + if (m_Selection != hover) + { + GetGUIState().SetEvent(ev); + + if (hover != NULL) + hover->SendMessageAny(kOnSelectEvent, data); + + // Only send the OnDeselect if the Event.current was marked as 'used' + if (GetGUIState().GetIsEventUsed()) + { + if (m_Selection != NULL) + m_Selection->SendMessageAny(kOnDeselectEvent, data); + + m_Selection = hover; + } + } + + // Pressed on the hovered item + if (hover != NULL) + hover->SendMessageAny(kOnPressEvent, data); + } + + // Released the mouse button + if (isReleased) + { + if (hover != NULL) + { + if (previousPress == hover) + hover->SendMessageAny(kOnClickEvent, data); + else if (hover != NULL) + hover->SendMessageAny(kOnDropEvent, data); + } + + if (previousPress != NULL) + previousPress->SendMessageAny(kOnReleaseEvent, data); + } + + // Now that we are done with events we can clear the pressed object. + // Clearing it earlier makes the property pointless for scripts: Null is not very informative. + if (isReleased) m_CurrentTouch->press = NULL; + state.EndUsingEvents(); +} +#endif + +void InputManager::SetKeyState (int key, bool state) +{ + // This ignores keyRepeats (multiple keydown without a keyup event between) + if (state && !m_CurrentKeyState[key]) + m_ThisFrameKeyDown[key] = true; + if (!state && m_CurrentKeyState[key]) + m_ThisFrameKeyUp[key] = true; + + m_CurrentKeyState[key] = state; +} + +bool InputManager::ConfigureButton(int *button) +{ + for(int key=0;key<kKeyAndMouseButtonCount;key++) + if(m_ThisFrameKeyDown[key]) + { + *button=key; + return true; + } + //skip virtual joystick 0 + for(int key=kKeyAndMouseButtonCount+kMaxJoyStickButtons;key<kKeyAndJoyButtonCount;key++) + if(m_ThisFrameKeyDown[key]) + { + *button=key; + return true; + } + return false; +} + +#if SUPPORT_REPRODUCE_LOG + +template<class T> +void WriteFloat (std::ofstream& out, T& value) +{ + int intValue = RoundfToInt(value * 1000.0); + out << intValue; + value = intValue / 1000.0; +} + +template<class T> +void ReadFloat (std::ifstream& in, T& value) +{ + int intValue = 0; + in >> intValue; + value = intValue / 1000.0; +} + +void WriteBitSet (std::ofstream& out, dynamic_bitset& value) +{ + out << value.count() << ' '; + for (int i=0;i<value.size();i++) + { + if (value[i]) + out << i << ' '; + } + out << std::endl; +} + +void ReadBitSet (std::ifstream& in, dynamic_bitset& value) +{ + int size = 0; + in >> size; + value.reset(); + for (int i=0;i<size;i++) + { + int index = 0; + in >> index; + value[index] = true; + } +} + +void InputManager::WriteLog (std::ofstream& out) +{ + out << "Input" << std::endl; + + for (int i=0;i<m_Axes.size();i++) + { + WriteFloat(out, m_Axes[i].GetValueRef()); out << ' '; + WriteFloat(out, m_Axes[i].GetValueRawRef()); out << ' '; + } + + out << std::endl; + + WriteBitSet(out, m_CurrentKeyState); + WriteBitSet(out, m_ThisFrameKeyDown); + WriteBitSet(out, m_ThisFrameKeyUp); + +// to_string(m_CurrentKeyState, keystate); out << keystate << std::endl; +// to_string(m_ThisFrameKeyDown, keystate); out << keystate << std::endl; +// to_string(m_ThisFrameKeyUp, keystate); out << keystate << std::endl; + + + + WriteFloat(out, m_MouseDelta.x); out << ' '; + WriteFloat(out, m_MouseDelta.y); out << ' '; + WriteFloat(out, m_MousePos.x); out << ' '; + WriteFloat(out, m_MousePos.y); out << ' '; + float obsoleteWindowDelta = 0; + WriteFloat(out, obsoleteWindowDelta); out << ' '; + WriteFloat(out, obsoleteWindowDelta); out << std::endl; + WriteReproductionString(out, m_InputString); + + out << GetScreenManager().GetAllowCursorLock() << ' '; +} + + +void InputManager::ReadLog (std::ifstream& in) +{ + CheckReproduceTagAndExit("Input", in); + + for (int i=0;i<m_Axes.size();i++) + { + ReadFloat(in, m_Axes[i].GetValueRef()); + ReadFloat(in, m_Axes[i].GetValueRawRef()); + } + + ReadBitSet(in, m_CurrentKeyState); + ReadBitSet(in, m_ThisFrameKeyDown); + ReadBitSet(in, m_ThisFrameKeyUp); + + ReadFloat(in, m_MouseDelta.x); + ReadFloat(in, m_MouseDelta.y); + + ReadFloat(in, m_MousePos.x); + ReadFloat(in, m_MousePos.y); + + float obsoleteWindowDelta = 0; + ReadFloat(in, obsoleteWindowDelta); + ReadFloat(in, obsoleteWindowDelta); + + if (GetReproduceVersion () == 1 || GetReproduceVersion () == 2) + { + in.ignore(1); + getline(in, m_InputString); + if (in.peek() != 'E') + in.ignore(1); + } + else + { + ReadReproductionString(in, m_InputString); + } + + if (GetReproduceVersion () >= 5) + { + bool cursorLock; + in >> cursorLock; + GetScreenManager().SetAllowCursorLock(cursorLock); + } +} +#endif + +UInt16 NormalizeInputCharacter (UInt16 input) +{ + // standardize input characters as different platforms report different + // ascii codes for the same input. + if (input == 3) + return '\n'; + if (input == '\r') + return '\n'; + if (input == 8 || input == 127) //backspace + return '\b'; + + return input; +} + +bool InputManager::GetTextFieldInput() +{ + return m_TextFieldInput; +} + +bool InputManager::GetEnableIMEComposition() +{ + if (m_IMECompositionMode != InputManager::kCompositionModeAuto) + return m_IMECompositionMode == InputManager::kCompositionModeOn; + else + return m_TextFieldInput; +} + +bool InputManager::GetEatKeyPressOnTextFieldFocus() +{ + // The "or" predicate ensures that we force input-eating when modal windows are open + if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_4_a1)) + return m_EatKeyPressOnTextFieldFocus || (GetGUIState().m_MultiFrameGUIState.m_Windows != NULL && GetGUIState().m_MultiFrameGUIState.m_Windows->m_ModalWindow != NULL); +#if UNITY_WIN + return false; +#else + return true; +#endif +} + + +// These stubs are for all the other platforms that do not (yet?) support this. +#if !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_EDITOR && !UNITY_WINRT && !UNITY_BB10 && !UNITY_TIZEN +unsigned GetOrientation () { return 0; } +size_t GetAccelerationCount () { return 0; } +void GetAcceleration (size_t, struct Acceleration&) {} +#endif + +#if !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_BB10 && !UNITY_EDITOR && !UNITY_TIZEN && !UNITY_METRO && !UNITY_WP8 +bool IsApplicationGenuine () { return true; } +bool IsApplicationGenuineAvailable () { return false; } +LocationInfo LocationService::GetLastLocation () { return LocationInfo (); } +LocationServiceStatus LocationService::GetLocationStatus () +{ return kLocationServiceStopped; } +bool LocationService::IsServiceEnabledByUser () { return true; } +void LocationService::StartUpdatingLocation () {} +void LocationService::StopUpdatingLocation () {} +void LocationService::SetDesiredAccuracy (float) {} +void LocationService::SetDistanceFilter (float) {} +#endif + +#if !UNITY_IPHONE && !UNITY_EDITOR && !UNITY_ANDROID && !UNITY_METRO && !UNITY_WP8 +void LocationService::SetHeadingUpdatesEnabled (bool enabled) { } +bool LocationService::IsHeadingUpdatesEnabled() { return false; } +LocationServiceStatus LocationService::GetHeadingStatus () +{ return kLocationServiceStopped; } +const HeadingInfo &LocationService::GetLastHeading () +{ + static HeadingInfo dummy = { 0, 0, Vector3f::zero, 0 }; + return dummy; +} +bool LocationService::IsHeadingAvailable () { return false; } +#endif + +#if !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_WINRT +bool IsCompensatingSensors() { return false; } +void SetCompensatingSensors(bool val) { } +#endif +#if !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_EDITOR && !UNITY_METRO && !UNITY_WP8 && !UNITY_BB10 && !UNITY_TIZEN +Vector3f GetGyroRotationRate(int idx) { return Vector3f(0.0f, 0.0f, 0.0f); } +bool IsGyroAvailable() { return false; } +Vector3f GetGyroRotationRateUnbiased(int idx) { return Vector3f(0.0f, 0.0f, 0.0f); } +Vector3f GetGravity(int idx) { return Vector3f(0.0f, 0.0f, 0.0f); } +Vector3f GetUserAcceleration(int idx) { return Vector3f(0.0f, 0.0f, 0.0f); } +Quaternionf GetAttitude(int idx) { return Quaternionf(0.0f, 0.0f, 0.0f, 1.0f); } +bool IsGyroEnabled(int idx) { return false; } +void SetGyroEnabled(int idx, bool enabled) {} +float GetGyroUpdateInterval(int idx) { return 0.0f; } +void SetGyroUpdateInterval(int idx, float interval) {} +int GetGyro() { return 0; } +#endif + +#if !UNITY_PS3 +Vector3f GetAcceleration (int controllerID) { return Vector3f(0,0,0); } +Quaternionf GetRotation(int controllerID) { return Quaternionf(0,0,0,0); } +Vector3f GetPosition(int controllerID) { return Vector3f(0,0,0); } +void SetActuator(int controllerID, UInt32 mode) {} +#endif + +#if !UNITY_IPHONE +// --- iPhoneLocalNotification --- +iPhoneLocalNotification::iPhoneLocalNotification() + : m_Notification(0) +{ } + +iPhoneLocalNotification::iPhoneLocalNotification(UILocalNotification *notification) + : m_Notification(0) +{ } + +iPhoneLocalNotification::iPhoneLocalNotification(const iPhoneLocalNotification &other) + : m_Notification(0) +{ } + +iPhoneLocalNotification::~iPhoneLocalNotification() +{ } + +iPhoneLocalNotification &iPhoneLocalNotification::operator=(const iPhoneLocalNotification &other) +{ return *this; } + +double iPhoneLocalNotification::GetFireDate() const +{ + return 0; +} + +void iPhoneLocalNotification::SetFireDate(const double fireDate) +{ } + +const char *iPhoneLocalNotification::GetTimeZone() const +{ return 0; } + +void iPhoneLocalNotification::SetTimeZone(const char *timeZone) +{ } + +unsigned int iPhoneLocalNotification::GetRepeatInterval() const +{ return 0; } + +void iPhoneLocalNotification::SetRepeatInterval(unsigned int repeatInterval) +{ } + +CalendarIdentifier iPhoneLocalNotification::GetRepeatCalendar() const +{ return kGregorianCalendar; } + +void iPhoneLocalNotification::SetRepeatCalendar(CalendarIdentifier calendar) +{ } + +const char *iPhoneLocalNotification::GetAlertBody() const +{ return 0; } + +void iPhoneLocalNotification::SetAlertBody(const char *message) +{ } + +const char *iPhoneLocalNotification::GetAlertAction() const +{ return 0; } + +void iPhoneLocalNotification::SetAlertAction(const char *action) +{ } + +bool iPhoneLocalNotification::HasAction() const +{ return false; } + +void iPhoneLocalNotification::HasAction(bool yes) +{ } + +const char *iPhoneLocalNotification::GetAlertLaunchImage() const +{ return 0; } + +void iPhoneLocalNotification::SetAlertLaunchImage(const char *imagePath) +{ } + +int iPhoneLocalNotification::GetApplicationIconBadgeNumber() const +{ return 0; } + +void iPhoneLocalNotification::SetApplicationIconBadgeNumber(int number) +{ } + +const char *iPhoneLocalNotification::GetSoundName() const +{ return 0; } + +void iPhoneLocalNotification::SetSoundName(const char *soundName) +{ } + +const char *iPhoneLocalNotification::GetDefaultSoundName() +{ return 0; } + +MonoObject *iPhoneLocalNotification::GetUserInfo() const +{ return 0; } + +void iPhoneLocalNotification::SetUserInfo(MonoObject *userInfo) +{ } + +void iPhoneLocalNotification::Schedule() +{ } + +void iPhoneLocalNotification::PresentNow() +{ } + +void iPhoneLocalNotification::Cancel() +{ } + +void iPhoneLocalNotification::CancelAll() +{ } + +std::vector<iPhoneLocalNotification*> iPhoneLocalNotification::GetScheduled() +{ return std::vector<iPhoneLocalNotification*>(); } + +// --- + +size_t GetLocalNotificationCount() +{ return 0; } + +iPhoneLocalNotification *CopyLocalNotification(unsigned index) +{ return 0; } + +void ClearLocalNotifications() +{ } + + +// --- iPhoneRemoteNotification --- + +iPhoneRemoteNotification::iPhoneRemoteNotification(NSDictionary *notification) + : m_HasAction(false), + m_Badge(0), + m_UserInfo(0) +{ } + +iPhoneRemoteNotification::iPhoneRemoteNotification(const iPhoneRemoteNotification &other) +: m_HasAction(false), + m_Badge(0), + m_UserInfo(0) +{ } + +iPhoneRemoteNotification::~iPhoneRemoteNotification() +{ } + +iPhoneRemoteNotification &iPhoneRemoteNotification::operator=(const iPhoneRemoteNotification &other) +{ return *this; } + +const char *iPhoneRemoteNotification::GetAlertBody() const +{ return 0; } + +bool iPhoneRemoteNotification::HasAction() const +{ return false; } + +int iPhoneRemoteNotification::GetApplicationIconBadgeNumber() const +{ return 0; } + +const char *iPhoneRemoteNotification::GetSoundName() const +{ return 0; } + +MonoObject *iPhoneRemoteNotification::GetUserInfo() const +{ return 0; } + +void iPhoneRemoteNotification::Register(int notifTypes) +{ } + +void iPhoneRemoteNotification::Unregister() +{ } + +int iPhoneRemoteNotification::GetEnabledTypes() +{ return 0; } + +int iPhoneRemoteNotification::GetDeviceTokenLength() +{ return 0; } + +const char *iPhoneRemoteNotification::GetDeviceToken() +{ return 0; } + +void iPhoneRemoteNotification::SetDeviceToken(NSData *deviceToken) +{ } + +//void iPhoneRemoteNotification::SendProviderDeviceToken(NSData *deviceToken) +//{ } + +void iPhoneRemoteNotification::SetError(NSError *error) +{ } + +const char *iPhoneRemoteNotification::GetError() +{ return 0; } + +// --- + +size_t GetRemoteNotificationCount() +{ return 0; } + +iPhoneRemoteNotification *CopyRemoteNotification(unsigned index) +{ return 0; } + +void ClearRemoteNotifications() +{ } + +#endif diff --git a/Runtime/Input/InputManager.h b/Runtime/Input/InputManager.h new file mode 100644 index 0000000..1fe4a70 --- /dev/null +++ b/Runtime/Input/InputManager.h @@ -0,0 +1,470 @@ +#ifndef INPUTMANAGER_H +#define INPUTMANAGER_H + +#include "Runtime/BaseClasses/GameManager.h" +#include <map> +#include "Runtime/Input/InputAxis.h" +#include "Runtime/Utilities/dynamic_bitset.h" +#include "Runtime/Math/Vector2.h" +#include "Runtime/Math/Vector3.h" +#include "Configuration/UnityConfigure.h" +#include <iosfwd> + +#if ENABLE_NEW_EVENT_SYSTEM +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Camera/Camera.h" +#include "Runtime/Input/GetInput.h" +#endif + +#include "Runtime/ClusterRenderer/ClusterRendererDefines.h" + +enum { + kMaxJoySticks = 12, + kMaxJoyStickButtons = 20, + kMaxJoyStickAxis = 20 +}; + +enum { + kKeyCount = 323, + kMouseButtonCount = 7, + kKeyAndMouseButtonCount = kKeyCount + kMouseButtonCount, + kKeyAndJoyButtonCount = kKeyAndMouseButtonCount + kMaxJoySticks * kMaxJoyStickButtons +}; + +class InputManager : public GlobalGameManager +{ + public: + + InputManager (MemLabelId label, ObjectCreationMode mode); + // virtual ~InputManager(); declared-by-macro + + REGISTER_DERIVED_CLASS (InputManager, GlobalGameManager) + DECLARE_OBJECT_SERIALIZE (InputManager) + // for cluster renderer + DECLARE_CLUSTER_SERIALIZE (InputManager) + + static void InitializeClass (); + static void CleanupClass (); + + /// Make up some sensible controls... + void MakeDefault (); + + enum { kCompositionModeAuto, kCompositionModeOn, kCompositionModeOff }; + + bool GetButton (const string &name); + bool GetButtonDown (const string &name); + bool GetButtonUp (const string &name); + float GetAxis (const string &name); + float GetAxisRaw (const string &name); + bool HasAxisOrButton (const string& name); + + void ProcessInput(); + void SendInputEvents(); +#if ENABLE_NEW_EVENT_SYSTEM + void ProcessInput (const GameObject& go); + +private: + void ProcessMouse (); + void ProcessTouches (); + void ProcessTouch (GameObject* hover, bool isPressed, bool isReleased, bool isDragging, bool moveEvents); +#endif + +public: + void InputEndFrame (); + + // Is the button down or was down during this frame? + bool GetMouseButton (int mouseBut) const { return GetKey (kKeyCount + mouseBut); } + bool GetMouseButtonState (int mouseBut) const { return m_CurrentKeyState[kKeyCount + mouseBut]; } + + // Is the button down or was down during this frame? + bool GetMouseButtonDown (int mouseBut) const { return GetKeyDown (kKeyCount + mouseBut); } + bool GetMouseButtonUp (int mouseBut) const { return GetKeyUp (kKeyCount + mouseBut); } + + // Is the key down or was down during this frame? + bool GetKey (int keyNum) const { return m_ThisFrameKeyDown[keyNum] | m_CurrentKeyState[keyNum]; } + // Is the key down this frame but was up last frame? + bool GetKeyDown (int keyNum) const { return m_ThisFrameKeyDown[keyNum]; } + bool GetKeyUp (int keyNum) const { return m_ThisFrameKeyUp[keyNum]; } + +#if ENABLE_NEW_EVENT_SYSTEM + const Touch& GetMouse() { return m_Mouse[0]; } + const Vector2f &GetMouseDelta () const { return m_Mouse[0].deltaPos; } + const Vector2f &GetMouseScroll () const { return m_Mouse[0].deltaScroll; } + const Vector2f &GetMousePosition () const { return m_Mouse[0].pos; } + + // Set prior to OnPress / OnRelease / OnDrag etc callbacks + const Touch& GetCurrentTouch() const { return m_CurrentTouch != NULL ? *m_CurrentTouch : m_Mouse[0]; } +#else + const Vector3f &GetMouseDelta () const { return m_MouseDelta; } + const Vector2f &GetMousePosition () const { return m_MousePos; } +#endif + + float GetJoystickPosition (int joyNum, int axis) const; + void SetJoystickPosition (int joyNum, int axis, float pos); + + void SetKeyState (int key, bool state); +#if ENABLE_NEW_EVENT_SYSTEM + void SetMouseDelta (const Vector2f &delta) { m_Mouse[0].deltaPos = delta; } + void SetMouseScroll (const Vector2f& delta) { m_Mouse[0].deltaScroll = delta; } + void SetMousePosition (const Vector2f &pos) { m_Mouse[0].pos = pos; } +#else + void SetMouseDelta (const Vector3f &pos) { m_MouseDelta = pos; } + void SetMousePosition (const Vector2f &pos) { m_MousePos = pos; } +#endif + void SetMouseButton (int button, bool enabled) { SetKeyState (kKeyCount + button, enabled); } + + void QuitApplication () { m_ShouldQuit = true; } + void CancelQuitApplication () { m_ShouldQuit = false; } + bool ShouldQuit () const { return m_ShouldQuit; } + + string& GetInputString () { return m_InputString; } + string& GetCompositionString () { return m_CompositionString;} + + bool GetSimulateMouseWithTouches () const { return m_SimulateMouseWithTouches; } + void SetSimulateMouseWithTouches (bool value) { m_SimulateMouseWithTouches = value; } + + void SetTextFieldInput(bool value) { m_TextFieldInput = value; } + bool GetTextFieldInput(); + + Vector2f &GetTextFieldCursorPos() { return m_TextFieldCursorPos; } + void SetTextFieldCursorPos(Vector2f &value) { m_TextFieldCursorPos = value; } + + bool GetEatKeyPressOnTextFieldFocus (); + void SetEatKeyPressOnTextFieldFocus (bool value) { m_EatKeyPressOnTextFieldFocus = value; } + + int GetIMECompositionMode () { return m_IMECompositionMode; } + void SetIMECompositionMode (int value) { m_IMECompositionMode = value; } + + bool GetIMEIsSelected() { return m_IMEIsSelected; } + void SetIMEIsSelected(bool value) { m_IMEIsSelected = value; } + + bool GetEnableIMEComposition (); + + /// Needed here because m_Axes need to be preprocessed before use. + virtual void CheckConsistency (); + void ResetInputAxes (); + + bool GetAnyKey (); + bool GetAnyKeyThisFrame (); + + virtual void Reset (); + + bool ConfigureButton(int *button); + + int NumAxes() {return m_Axes.size();} + InputAxis *GetIndexedAxis(int i) {if(i<m_Axes.size())return &(m_Axes[i]); else return NULL;} + + #if SUPPORT_REPRODUCE_LOG + void WriteLog (std::ofstream& out); + void ReadLog (std::ifstream& in); + #endif + + const std::vector< std::vector<float> >& GetJoystickPositions() const { return m_JoystickPos; } + + private: + std::vector<InputAxis> m_Axes; + + // There are shortclick keys because in one frame the player might hit a key and release it. + // We want the key to be down for one frame in this case. + dynamic_bitset m_CurrentKeyState; + dynamic_bitset m_ThisFrameKeyDown; + dynamic_bitset m_ThisFrameKeyUp; + +#if ENABLE_NEW_EVENT_SYSTEM + Touch m_Mouse[3]; + Touch* m_CurrentTouch; + GameObject* m_Selection; +#else + Vector3f m_MouseDelta; //x and y for delta, z for scrollwheel + Vector2f m_MousePos; +#endif + bool m_MousePresent; + + std::vector<std::vector<float> > m_JoystickPos; + + std::string m_InputString; + std::string m_CompositionString; + + Vector2f m_TextFieldCursorPos; + bool m_TextFieldInput; + + bool m_EatKeyPressOnTextFieldFocus; + int m_IMECompositionMode; + bool m_IMEIsSelected; + + int m_LastJoyNum,m_LastJoyAxis; + bool m_ShouldQuit; + bool m_SimulateMouseWithTouches; + + friend bool GetConfigureJoyAxis(std::string name,int *joyNum,int *axis); +}; + +InputManager* GetInputManagerPtr (); +InputManager& GetInputManager (); + +string KeyToString (int key); +int StringToKey (const string& name); + +UInt16 NormalizeInputCharacter (UInt16 input); + +enum { + /* The keyboard syms have been cleverly chosen to map to ASCII */ + SDLK_UNKNOWN = 0, + SDLK_FIRST = 0, + SDLK_BACKSPACE = 8, + SDLK_TAB = 9, + SDLK_CLEAR = 12, + SDLK_RETURN = 13, + SDLK_PAUSE = 19, + SDLK_ESCAPE = 27, + SDLK_SPACE = 32, + SDLK_EXCLAIM = 33, + SDLK_QUOTEDBL = 34, + SDLK_HASH = 35, + SDLK_DOLLAR = 36, + SDLK_AMPERSAND = 38, + SDLK_QUOTE = 39, + SDLK_LEFTPAREN = 40, + SDLK_RIGHTPAREN = 41, + SDLK_ASTERISK = 42, + SDLK_PLUS = 43, + SDLK_COMMA = 44, + SDLK_MINUS = 45, + SDLK_PERIOD = 46, + SDLK_SLASH = 47, + SDLK_0 = 48, + SDLK_1 = 49, + SDLK_2 = 50, + SDLK_3 = 51, + SDLK_4 = 52, + SDLK_5 = 53, + SDLK_6 = 54, + SDLK_7 = 55, + SDLK_8 = 56, + SDLK_9 = 57, + SDLK_COLON = 58, + SDLK_SEMICOLON = 59, + SDLK_LESS = 60, + SDLK_EQUALS = 61, + SDLK_GREATER = 62, + SDLK_QUESTION = 63, + SDLK_AT = 64, + /* + Skip uppercase letters + */ + SDLK_LEFTBRACKET = 91, + SDLK_BACKSLASH = 92, + SDLK_RIGHTBRACKET = 93, + SDLK_CARET = 94, + SDLK_UNDERSCORE = 95, + SDLK_BACKQUOTE = 96, + SDLK_a = 97, + SDLK_b = 98, + SDLK_c = 99, + SDLK_d = 100, + SDLK_e = 101, + SDLK_f = 102, + SDLK_g = 103, + SDLK_h = 104, + SDLK_i = 105, + SDLK_j = 106, + SDLK_k = 107, + SDLK_l = 108, + SDLK_m = 109, + SDLK_n = 110, + SDLK_o = 111, + SDLK_p = 112, + SDLK_q = 113, + SDLK_r = 114, + SDLK_s = 115, + SDLK_t = 116, + SDLK_u = 117, + SDLK_v = 118, + SDLK_w = 119, + SDLK_x = 120, + SDLK_y = 121, + SDLK_z = 122, + SDLK_DELETE = 127, + /* End of ASCII mapped keysyms */ + + /* International keyboard syms */ + SDLK_WORLD_0 = 160, /* 0xA0 */ + SDLK_WORLD_1 = 161, + SDLK_WORLD_2 = 162, + SDLK_WORLD_3 = 163, + SDLK_WORLD_4 = 164, + SDLK_WORLD_5 = 165, + SDLK_WORLD_6 = 166, + SDLK_WORLD_7 = 167, + SDLK_WORLD_8 = 168, + SDLK_WORLD_9 = 169, + SDLK_WORLD_10 = 170, + SDLK_WORLD_11 = 171, + SDLK_WORLD_12 = 172, + SDLK_WORLD_13 = 173, + SDLK_WORLD_14 = 174, + SDLK_WORLD_15 = 175, + SDLK_WORLD_16 = 176, + SDLK_WORLD_17 = 177, + SDLK_WORLD_18 = 178, + SDLK_WORLD_19 = 179, + SDLK_WORLD_20 = 180, + SDLK_WORLD_21 = 181, + SDLK_WORLD_22 = 182, + SDLK_WORLD_23 = 183, + SDLK_WORLD_24 = 184, + SDLK_WORLD_25 = 185, + SDLK_WORLD_26 = 186, + SDLK_WORLD_27 = 187, + SDLK_WORLD_28 = 188, + SDLK_WORLD_29 = 189, + SDLK_WORLD_30 = 190, + SDLK_WORLD_31 = 191, + SDLK_WORLD_32 = 192, + SDLK_WORLD_33 = 193, + SDLK_WORLD_34 = 194, + SDLK_WORLD_35 = 195, + SDLK_WORLD_36 = 196, + SDLK_WORLD_37 = 197, + SDLK_WORLD_38 = 198, + SDLK_WORLD_39 = 199, + SDLK_WORLD_40 = 200, + SDLK_WORLD_41 = 201, + SDLK_WORLD_42 = 202, + SDLK_WORLD_43 = 203, + SDLK_WORLD_44 = 204, + SDLK_WORLD_45 = 205, + SDLK_WORLD_46 = 206, + SDLK_WORLD_47 = 207, + SDLK_WORLD_48 = 208, + SDLK_WORLD_49 = 209, + SDLK_WORLD_50 = 210, + SDLK_WORLD_51 = 211, + SDLK_WORLD_52 = 212, + SDLK_WORLD_53 = 213, + SDLK_WORLD_54 = 214, + SDLK_WORLD_55 = 215, + SDLK_WORLD_56 = 216, + SDLK_WORLD_57 = 217, + SDLK_WORLD_58 = 218, + SDLK_WORLD_59 = 219, + SDLK_WORLD_60 = 220, + SDLK_WORLD_61 = 221, + SDLK_WORLD_62 = 222, + SDLK_WORLD_63 = 223, + SDLK_WORLD_64 = 224, + SDLK_WORLD_65 = 225, + SDLK_WORLD_66 = 226, + SDLK_WORLD_67 = 227, + SDLK_WORLD_68 = 228, + SDLK_WORLD_69 = 229, + SDLK_WORLD_70 = 230, + SDLK_WORLD_71 = 231, + SDLK_WORLD_72 = 232, + SDLK_WORLD_73 = 233, + SDLK_WORLD_74 = 234, + SDLK_WORLD_75 = 235, + SDLK_WORLD_76 = 236, + SDLK_WORLD_77 = 237, + SDLK_WORLD_78 = 238, + SDLK_WORLD_79 = 239, + SDLK_WORLD_80 = 240, + SDLK_WORLD_81 = 241, + SDLK_WORLD_82 = 242, + SDLK_WORLD_83 = 243, + SDLK_WORLD_84 = 244, + SDLK_WORLD_85 = 245, + SDLK_WORLD_86 = 246, + SDLK_WORLD_87 = 247, + SDLK_WORLD_88 = 248, + SDLK_WORLD_89 = 249, + SDLK_WORLD_90 = 250, + SDLK_WORLD_91 = 251, + SDLK_WORLD_92 = 252, + SDLK_WORLD_93 = 253, + SDLK_WORLD_94 = 254, + SDLK_WORLD_95 = 255, /* 0xFF */ + + /* Numeric keypad */ + SDLK_KP0 = 256, + SDLK_KP1 = 257, + SDLK_KP2 = 258, + SDLK_KP3 = 259, + SDLK_KP4 = 260, + SDLK_KP5 = 261, + SDLK_KP6 = 262, + SDLK_KP7 = 263, + SDLK_KP8 = 264, + SDLK_KP9 = 265, + SDLK_KP_PERIOD = 266, + SDLK_KP_DIVIDE = 267, + SDLK_KP_MULTIPLY = 268, + SDLK_KP_MINUS = 269, + SDLK_KP_PLUS = 270, + SDLK_KP_ENTER = 271, + SDLK_KP_EQUALS = 272, + + /* Arrows + Home/End pad */ + SDLK_UP = 273, + SDLK_DOWN = 274, + SDLK_RIGHT = 275, + SDLK_LEFT = 276, + SDLK_INSERT = 277, + SDLK_HOME = 278, + SDLK_END = 279, + SDLK_PAGEUP = 280, + SDLK_PAGEDOWN = 281, + + /* Function keys */ + SDLK_F1 = 282, + SDLK_F2 = 283, + SDLK_F3 = 284, + SDLK_F4 = 285, + SDLK_F5 = 286, + SDLK_F6 = 287, + SDLK_F7 = 288, + SDLK_F8 = 289, + SDLK_F9 = 290, + SDLK_F10 = 291, + SDLK_F11 = 292, + SDLK_F12 = 293, + SDLK_F13 = 294, + SDLK_F14 = 295, + SDLK_F15 = 296, + + /* Key state modifier keys */ + SDLK_NUMLOCK = 300, + SDLK_CAPSLOCK = 301, + SDLK_SCROLLOCK = 302, + SDLK_RSHIFT = 303, + SDLK_LSHIFT = 304, + SDLK_RCTRL = 305, + SDLK_LCTRL = 306, + SDLK_RALT = 307, + SDLK_LALT = 308, + SDLK_RMETA = 309, + SDLK_LMETA = 310, + SDLK_RGUI = 309, + SDLK_LGUI = 310, + SDLK_LSUPER = 311, /* Left "Windows" key */ + SDLK_RSUPER = 312, /* Right "Windows" key */ + SDLK_MODE = 313, /* "Alt Gr" key */ + SDLK_COMPOSE = 314, /* Multi-key compose key */ + + /* Miscellaneous function keys */ + SDLK_HELP = 315, + SDLK_PRINT = 316, + SDLK_SYSREQ = 317, + SDLK_BREAK = 318, + SDLK_MENU = 319, + SDLK_POWER = 320, /* Power Macintosh power key */ + SDLK_EURO = 321, /* Some european keyboards */ + SDLK_UNDO = 322, /* Atari keyboard has Undo */ + + /* Add any other keys here */ + + SDLK_LAST +}; + + +#endif diff --git a/Runtime/Input/LocationService.h b/Runtime/Input/LocationService.h new file mode 100644 index 0000000..eecd4d3 --- /dev/null +++ b/Runtime/Input/LocationService.h @@ -0,0 +1,51 @@ +#ifndef UNITY_LOCATION_SERVICE_H_ +#define UNITY_LOCATION_SERVICE_H_ + +#include "Runtime/Math/Vector3.h" + +struct LocationInfo +{ + double timestamp; + float latitude; + float longitude; + float altitude; + float horizontalAccuracy; + float verticalAccuracy; +}; + +struct HeadingInfo +{ + float magneticHeading; + float trueHeading; + Vector3f raw; + double timestamp; +}; + +enum LocationServiceStatus +{ + kLocationServiceStopped, + kLocationServiceInitializing, + kLocationServiceRunning, + kLocationServiceFailed +}; + +class LocationService +{ +public: + static void SetDesiredAccuracy (float val); + static float GetDesiredAccuracy (); + static void SetDistanceFilter (float val); + static float GetDistanceFilter (); + static bool IsServiceEnabledByUser (); + static void StartUpdatingLocation (); + static void StopUpdatingLocation (); + static void SetHeadingUpdatesEnabled (bool enabled); + static bool IsHeadingUpdatesEnabled(); + static LocationServiceStatus GetLocationStatus (); + static LocationServiceStatus GetHeadingStatus (); + static LocationInfo GetLastLocation (); + static const HeadingInfo &GetLastHeading (); + static bool IsHeadingAvailable (); +}; + +#endif // #ifndef UNITY_LOCATION_SERVICE_H_ diff --git a/Runtime/Input/OnScreenKeyboard.h b/Runtime/Input/OnScreenKeyboard.h new file mode 100644 index 0000000..a828173 --- /dev/null +++ b/Runtime/Input/OnScreenKeyboard.h @@ -0,0 +1,47 @@ +#ifndef UNITY_ONSCREEN_KEYBOARD_ +#define UNITY_ONSCREEN_KEYBOARD_ + +#include "Runtime/Math/Color.h" +#include "Runtime/Math/Rect.h" +#include "Runtime/Graphics/ScreenManager.h" + +class KeyboardOnScreen +{ +public: + + static Rectf GetRect(); + static bool IsVisible(); + + static void Hide(); + static void setInputHidden(bool flag); + static bool isInputHidden(); + +public: + KeyboardOnScreen(std::string const& text, UInt32 keyboardType = 0, + bool autocorrection = true, bool multiline = false, + bool secure = false, bool alert = false, + std::string const& textPlaceholder = ""); + ~KeyboardOnScreen(); + bool isActive() const; + bool isDone() const; + bool wasCanceled() const; + std::string getText() const; + void setText(std::string text); + void setActive(bool flag = true); + +private: + #if UNITY_WP8 + static BridgeInterface::IWP8Keyboard^ ms_Keyboard; + BridgeInterface::IWP8Keyboard^ m_Keyboard; + #endif + ColorRGBA32 textColor; + ColorRGBA32 backgroundColor; + UInt32 keyboardType; + std::string textPlaceholder; + bool autocorrection; + bool multiline; + bool secure; + bool alert; +}; + +#endif diff --git a/Runtime/Input/SimulateInputEvents.cpp b/Runtime/Input/SimulateInputEvents.cpp new file mode 100644 index 0000000..4802477 --- /dev/null +++ b/Runtime/Input/SimulateInputEvents.cpp @@ -0,0 +1,222 @@ +#include "UnityPrefix.h" +#include "SimulateInputEvents.h" +#include "GetInput.h" +#include "Runtime/Graphics/ScreenManager.h" +#include "InputManager.h" +#include "Runtime/IMGUI/GUIManager.h" +#include <math.h> + +#if UNITY_EDITOR && UNITY_OSX +#include "Editor/Src/RemoteInput/iPhoneRemoteImpl.h" +#endif + +#if UNITY_EDITOR +#include "Editor/Src/RemoteInput/AndroidRemote.h" +#endif + +#ifndef MAXFLOAT + #define MAXFLOAT ((float)3.40282346638528860e+38) +#endif + +void CaptureEventMousePosition (InputEvent& e) +{ + e.Init(); + + Vector2f p = GetInputManager().GetMousePosition(); + +#if ENABLE_NEW_EVENT_SYSTEM + e.touch.pos = p; + e.touch.pos.y = GetScreenManager().GetHeight() - p.y; + e.touch.deltaPos = GetInputManager().GetMouseDelta(); +#else + e.mousePosition = p; + e.mousePosition.y = GetScreenManager().GetHeight() - e.mousePosition.y; + Vector3f d = GetInputManager().GetMouseDelta(); + e.delta = Vector2f(d.x, d.y); +#endif + e.pressure = 1.0f; + + e.clickCount = 1; + + size_t touchCount = GetActiveTouchCount(); + + for (int i = 0; i < touchCount; ++i) + { +#if ENABLE_NEW_EVENT_SYSTEM + Touch* touch = GetTouch(i); + + if (touch != NULL) + { + if (touch->tapCount > e.clickCount) + { + e.clickCount = touch->tapCount; + } + } +#else + Touch touch; + + if (GetTouch(i, touch)) + { + if (touch.tapCount > e.clickCount) + { + e.clickCount = touch.tapCount; + } + } +#endif + } +} + +// send event on button down/up +void GenerateAndSendInputDownUpEvent( const InputEvent::MouseButton button, const bool isDown ) +{ + InputEvent ie; + CaptureEventMousePosition (ie); + ie.button = button; + ie.type = isDown ? InputEvent::kMouseDown : InputEvent::kMouseUp; +#if UNITY_METRO + ie.touchType = InputEvent::kFingerTouch; +#endif + GetGUIManager().QueueEvent (ie); + // If the touch has ended we need to "disable" the mouse hover state, by moving the mouse pointer "away" + if (!isDown) + { + ie.Init(); + ie.type = InputEvent::kMouseUp; +#if ENABLE_NEW_EVENT_SYSTEM + ie.touch.pos = Vector2f(MAXFLOAT,MAXFLOAT); +#else + ie.mousePosition = Vector2f(MAXFLOAT,MAXFLOAT); +#endif + GetGUIManager().QueueEvent (ie); + } +} + +void RestoreMouseState( const Vector2f& mousePosition ) +{ + enum { MaxSimulatedMouseButtons = 3 }; + GetInputManager().SetMousePosition(mousePosition); + if (GetActiveTouchCount() > 0) + { + for (int i = 0; i < MaxSimulatedMouseButtons; ++i) + GetInputManager().SetMouseButton(i, false); + GetInputManager().InputEndFrame(); + } +} + +void SimulateMouseInput() +{ + enum { MaxSimulatedMouseButtons = 3 }; + static size_t prevTouchPointCount = 0; + +#if UNITY_WINRT + Vector2f originalPos = GetInputManager().GetMousePosition(); +#endif + + for (int i = 0; i < MaxSimulatedMouseButtons; ++i) + { + if (i < GetActiveTouchCount()) + GetInputManager().SetMouseButton(i, true); + else if (i < prevTouchPointCount) + GetInputManager().SetMouseButton(i, false); + } + + prevTouchPointCount = GetActiveTouchCount(); + + size_t touchCount = GetTouchCount(); + Vector2f pos(0.0f, 0.0f); + static Vector2f prevPos(0.0f, 0.0f); + + for (int i = 0; i < GetTouchCount(); ++i) + { +#if ENABLE_NEW_EVENT_SYSTEM + Touch* touch = GetTouch(i); + if (touch != NULL) + pos += touch->pos; +#else + Touch touch; + if (GetTouch(i, touch)) + pos += touch.pos; +#endif + } + + if (touchCount > 0) + { + float invCount = 1.0f / (float)touchCount; + pos.x *= invCount; + pos.y *= invCount; + + GetInputManager().SetMousePosition(pos); +#if ENABLE_NEW_EVENT_SYSTEM + GetInputManager().SetMouseDelta(Vector2f(pos.x - prevPos.x, pos.y - prevPos.y)); +#else + GetInputManager().SetMouseDelta(Vector3f(pos.x - prevPos.x, + pos.y - prevPos.y, 0.0f)); +#endif + prevPos = pos; + } + +#if UNITY_IPHONE || UNITY_ANDROID || UNITY_WINRT || UNITY_BB10 || UNITY_TIZEN + SimulateInputEvents (); + +#elif UNITY_EDITOR + if ( +#if UNITY_OSX + iPhoneHasRemoteConnected () || +#endif + AndroidHasRemoteConnected()) + { + SimulateInputEvents(); + } +#endif + +#if UNITY_WINRT + if (!GetInputManager().GetSimulateMouseWithTouches()) + RestoreMouseState(originalPos); +#endif +} + +void SimulateInputEvents() +{ + InputEvent ie; + + static bool lastMouseB0 = false; + static bool lastMouseB1 = false; + + if (SqrMagnitude(GetInputManager().GetMouseDelta()) > 1e-6) + { + CaptureEventMousePosition (ie); + ie.type = InputEvent::kMouseMove; + ie.button = 0; +#if UNITY_METRO + ie.touchType = InputEvent::kFingerTouch; +#endif + + if (GetInputManager().GetMouseButton(0) && lastMouseB0) + { + ie.type = InputEvent::kMouseDrag; + ie.button |= InputEvent::kLeftButton; + } + if (GetInputManager().GetMouseButton(1) && lastMouseB1) + { + ie.type = InputEvent::kMouseDrag; + ie.button |= InputEvent::kRightButton; + } + + GetGUIManager().QueueEvent (ie); + } + + bool buttonDown = GetInputManager().GetMouseButton(0); + if (buttonDown != lastMouseB0) + { + GenerateAndSendInputDownUpEvent( InputEvent::kLeftButton, buttonDown ); + lastMouseB0 = buttonDown; + } + + buttonDown = GetInputManager().GetMouseButton(1); + if (buttonDown != lastMouseB1) + { + GenerateAndSendInputDownUpEvent( InputEvent::kRightButton, buttonDown ); + lastMouseB1 = buttonDown; + } +} + diff --git a/Runtime/Input/SimulateInputEvents.h b/Runtime/Input/SimulateInputEvents.h new file mode 100644 index 0000000..9624e4c --- /dev/null +++ b/Runtime/Input/SimulateInputEvents.h @@ -0,0 +1,10 @@ +#ifndef UNITY_SIMULATE_INPUT_EVENTS_ +#define UNITY_SIMULATE_INPUT_EVENTS_ + +void SimulateInputEvents(); +void SimulateMouseInput(); + +// Implemented by various platforms: +size_t GetActiveTouchCount (); + +#endif diff --git a/Runtime/Input/TimeManager.cpp b/Runtime/Input/TimeManager.cpp new file mode 100644 index 0000000..faadada --- /dev/null +++ b/Runtime/Input/TimeManager.cpp @@ -0,0 +1,489 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#include "TimeManager.h" +#include <limits> +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Math/FloatConversion.h" +#include "Runtime/BaseClasses/ManagerContext.h" +#include "Runtime/Utilities/Utility.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Misc/ReproductionLog.h" +#include "Runtime/Threads/Thread.h" +#if SUPPORT_REPRODUCE_LOG +#include <fstream> +#endif + +using namespace std; + + +#define DEBUG_TIME_MANAGER 0 + + +const float kMaximumDeltaTime = 1.0F / 3.0F; +const float kStartupDeltaTime = 0.02F; +const float kNewDeltaTimeWeight = 0.2F; // for smoothing + +float CalcInvDeltaTime (float dt) +{ + if (dt > kMinimumDeltaTime) + return 1.0F / dt; + else + return 1.0F; +} + +TimeManager::TimeHolder::TimeHolder() +: m_CurFrameTime(0) +, m_LastFrameTime(0) +, m_DeltaTime(0) +, m_SmoothDeltaTime(0) +, m_SmoothingWeight(0) +, m_InvDeltaTime(0) +{ +} + +TimeManager::TimeManager (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +, m_CullFrameCount(0) +{ + m_SetTimeManually = false; + m_UseFixedTimeStep = false; + + m_FixedTime.m_SmoothDeltaTime = m_FixedTime.m_DeltaTime; + + m_TimeScale = 1.0F; + m_FixedTime.m_DeltaTime = 0.02F; + m_MaximumTimestep = kMaximumDeltaTime; + m_LastSyncEnd = 0; + ResetTime (); +} + +TimeManager::~TimeManager () {} + +inline void CalcSmoothDeltaTime (TimeManager::TimeHolder& time) +{ + // If existing weight is zero, don't take existing value into account + time.m_SmoothingWeight *= (1.0F - kNewDeltaTimeWeight); + time.m_SmoothingWeight += kNewDeltaTimeWeight; + // As confidence in smoothed value increases the divisor goes towards 1 + float normalized = kNewDeltaTimeWeight / time.m_SmoothingWeight; + time.m_SmoothDeltaTime = Lerp (time.m_SmoothDeltaTime, time.m_DeltaTime, normalized); +} + +void TimeManager::SetPause(bool pause) +{ + m_FirstFrameAfterPause = true; +} + +void TimeManager::Sync (float framerate) +{ + double time = GetTimeSinceStartup (); + // wait for enough time to pass for the requested framerate + if (framerate > 0) + { + // Wait a bit less (0.1ms), to accomodate for small fluctuations (gives much better result in webplayers) + double frameTime = 1.0/(double)framerate - 0.0001; + if (!CompareApproximately(time, m_LastSyncEnd) && time - m_LastSyncEnd < frameTime) + { +#if SUPPORT_THREADS + Thread::Sleep(frameTime - (time-m_LastSyncEnd)); +#endif + int i = 0; + double start = GetTimeSinceStartup(); + // do the last adjustment with a busy wait + do + { + time = GetTimeSinceStartup(); + if (++i >= 1000) + { + // When using PerfHUD ES together with the NVIDIA time extension + // the time might be stopped, thus causing a diff of 0. + if ((time - start) == 0) + { + m_LastSyncEnd = GetTimeSinceStartup (); + return; + } + // Need to reset "start" here just in case we started to + // busy wait and then the time was stopped while busy waiting + start = time; + i = 0; + } + } + while(time - m_LastSyncEnd < frameTime); + m_LastSyncEnd += frameTime; + return; + } + } + m_LastSyncEnd = GetTimeSinceStartup (); +} + +void TimeManager::Update () +{ + AssertIf (m_UseFixedTimeStep); + m_FrameCount++; + m_RenderFrameCount++; + if (m_SetTimeManually) + return; + + // Capture framerate is always constant + if (m_CaptureFramerate > 0) + { + #if DEBUG_TIME_MANAGER + printf_console( "time: setting time using capture framerate of %i, timescale %f\n", m_CaptureFramerate, m_TimeScale ); + #endif + SetTime (m_DynamicTime.m_CurFrameTime + 1.0F / (float)m_CaptureFramerate * m_TimeScale); + return; + } + + // Don't do anything to delta time the first frame! + if (m_FirstFrameAfterReset) + { + m_FirstFrameAfterReset = false; + return; + } + + // When coming out of a pause / startup / level load we don't want to have a spike in delta time. + // So just default to kStartupDeltaTime. + if (m_FirstFrameAfterPause) + { + m_FirstFrameAfterPause = false; + #if DEBUG_TIME_MANAGER + printf_console( "time: setting time first frame after pause\n" ); + #endif + SetTime (m_DynamicTime.m_CurFrameTime + kStartupDeltaTime * m_TimeScale); + // This is not a real delta time so don't include in smoothed time + m_ActiveTime.m_SmoothingWeight = 0.0f; + m_DynamicTime.m_SmoothingWeight = 0.0f; + return; + } + + double time = GetTimeSinceStartup () - m_ZeroTime; + m_RealtimeStartOfFrame = time; + + // clamp the delta time in case a frame takes too long. + if (time - m_DynamicTime.m_CurFrameTime > m_MaximumTimestep) + { + #if DEBUG_TIME_MANAGER + printf_console( "time: maximum dt (was %f)\n", time - m_DynamicTime.m_CurFrameTime ); + #endif + SetTime (m_DynamicTime.m_CurFrameTime + m_MaximumTimestep * m_TimeScale); + return; + } + + // clamp the delta time in case a frame goes to fast! (prevent delta time being zero) + if (time - m_DynamicTime.m_CurFrameTime < kMinimumDeltaTime) + { + #if DEBUG_TIME_MANAGER + printf_console( "time: minimum dt (was %f)\n", time - m_DynamicTime.m_CurFrameTime ); + #endif + SetTime (m_DynamicTime.m_CurFrameTime + kMinimumDeltaTime * m_TimeScale); + return; + } + + // Handle time scale + if (!CompareApproximately (m_TimeScale, 1.0F)) + { + #if DEBUG_TIME_MANAGER + printf_console( "time: time scale path, delta %f\n", time - m_DynamicTime.m_CurFrameTime ); + #endif + float deltaTime = time - m_DynamicTime.m_CurFrameTime; + SetTime (m_DynamicTime.m_CurFrameTime + deltaTime * m_TimeScale); + return; + } + + #if DEBUG_TIME_MANAGER + printf_console( "time: set to %f\n", time ); + #endif + m_DynamicTime.m_LastFrameTime = m_DynamicTime.m_CurFrameTime; + m_DynamicTime.m_CurFrameTime = time; + m_DynamicTime.m_DeltaTime = m_DynamicTime.m_CurFrameTime - m_DynamicTime.m_LastFrameTime; + m_DynamicTime.m_InvDeltaTime = CalcInvDeltaTime(m_DynamicTime.m_DeltaTime); + CalcSmoothDeltaTime (m_DynamicTime); + + m_ActiveTime = m_DynamicTime; +} + +void TimeManager::SetDeltaTimeHack (float dt) +{ + m_ActiveTime.m_DeltaTime = max(dt, kMinimumDeltaTime); + m_ActiveTime.m_InvDeltaTime = CalcInvDeltaTime(m_ActiveTime.m_DeltaTime); +} + +void TimeManager::SetTime (double time) +{ + AssertIf (m_UseFixedTimeStep); +// AssertIf (time - m_DynamicTime.m_CurFrameTime < kMinimumDeltaTime * 0.1F); + + m_DynamicTime.m_LastFrameTime = m_DynamicTime.m_CurFrameTime; + m_DynamicTime.m_CurFrameTime = time; + m_DynamicTime.m_DeltaTime = m_DynamicTime.m_CurFrameTime - m_DynamicTime.m_LastFrameTime; + + m_DynamicTime.m_InvDeltaTime = CalcInvDeltaTime(m_DynamicTime.m_DeltaTime); + CalcSmoothDeltaTime (m_DynamicTime); + + m_ActiveTime = m_DynamicTime; + + // Sync m_ZeroTime with timemanager time + m_ZeroTime = GetTimeSinceStartup () - m_DynamicTime.m_CurFrameTime; + #if DEBUG_TIME_MANAGER + printf_console( "time: set to %f, sync zero to %f\n", time, m_ZeroTime ); + #endif +} + +#if UNITY_EDITOR +void TimeManager::NextFrameEditor () { + m_RenderFrameCount++; +} +#endif + +bool TimeManager::StepFixedTime () +{ + if (m_FixedTime.m_CurFrameTime + m_FixedTime.m_DeltaTime > m_DynamicTime.m_CurFrameTime && !m_FirstFixedFrameAfterReset) + { + m_ActiveTime = m_DynamicTime; + m_UseFixedTimeStep = false; + + return false; + } + + m_FixedTime.m_LastFrameTime = m_FixedTime.m_CurFrameTime; + if (!m_FirstFixedFrameAfterReset) + m_FixedTime.m_CurFrameTime += m_FixedTime.m_DeltaTime; + + m_ActiveTime = m_FixedTime; + m_UseFixedTimeStep = true; + m_FirstFixedFrameAfterReset = false; + + return true; +} + +void TimeManager::ResetTime () +{ + AssertIf (m_UseFixedTimeStep); + m_DynamicTime.m_CurFrameTime = 0.0F; + m_DynamicTime.m_LastFrameTime = 0.0F; + if (IsWorldPlaying()) + { + m_DynamicTime.m_DeltaTime = 0.02F; + m_DynamicTime.m_InvDeltaTime = 1.0F / m_DynamicTime.m_DeltaTime; + } + else + { + m_DynamicTime.m_DeltaTime = 0.0F; + m_DynamicTime.m_InvDeltaTime = 0.0F; + } + m_DynamicTime.m_SmoothDeltaTime = 0.0F; + m_DynamicTime.m_SmoothingWeight = 0.0F; + + m_FixedTime.m_CurFrameTime = 0.0F; + m_FixedTime.m_LastFrameTime = 0.0F; + // Dont erase the fixed delta time + m_FixedTime.m_InvDeltaTime = 1.0F / m_FixedTime.m_DeltaTime; + + m_ActiveTime = m_DynamicTime; + + m_FirstFrameAfterReset = true; + m_FirstFrameAfterPause = true; + m_FirstFixedFrameAfterReset = true; + + m_FrameCount = 0; + m_RenderFrameCount = 0; + m_ZeroTime = GetTimeSinceStartup (); + m_RealZeroTime = m_ZeroTime; + #if DEBUG_TIME_MANAGER + printf_console( "time: startup, zero time %f\n", m_RealZeroTime ); + #endif + m_LevelLoadOffset = 0.0F; + m_CaptureFramerate = 0; + m_RealtimeStartOfFrame = 0.0; +} + +template<class TransferFunction> +void TimeManager::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.Transfer (m_FixedTime.m_DeltaTime, "Fixed Timestep", kSimpleEditorMask); + transfer.Transfer (m_MaximumTimestep, "Maximum Allowed Timestep", kSimpleEditorMask); + transfer.Transfer (m_TimeScale, "m_TimeScale", kSimpleEditorMask); +} + +void TimeManager::SetFixedDeltaTime (float fixedStep) +{ + fixedStep = clamp<float>(fixedStep, 0.0001F, 10.0F); + m_FixedTime.m_DeltaTime = fixedStep; + m_FixedTime.m_InvDeltaTime = 1.0F / m_FixedTime.m_DeltaTime; + m_FixedTime.m_SmoothDeltaTime = m_FixedTime.m_DeltaTime; + + SetMaximumDeltaTime(m_MaximumTimestep); +} + +void TimeManager::SetMaximumDeltaTime (float maxStep) +{ + m_MaximumTimestep = max<float>(maxStep, m_FixedTime.m_DeltaTime); +} + +void TimeManager::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad(awakeMode); + + m_FixedTime.m_InvDeltaTime = 1.0F / m_FixedTime.m_DeltaTime; + m_FixedTime.m_SmoothDeltaTime = m_FixedTime.m_DeltaTime; +} + +void TimeManager::CheckConsistency () +{ + Super::CheckConsistency (); + + m_FixedTime.m_DeltaTime = clamp<float>(m_FixedTime.m_DeltaTime, 0.0001F, 10.0F); + m_MaximumTimestep = max<float>(m_MaximumTimestep, m_FixedTime.m_DeltaTime); +} + +void TimeManager::DidFinishLoadingLevel () +{ + m_LevelLoadOffset = -m_DynamicTime.m_CurFrameTime; + // Trying to reconstruct what was intended here, this seems plausible: + m_FirstFrameAfterPause = m_FirstFrameAfterReset = true; +} + +void TimeManager::SetTimeScale (float scale) { + bool outOfRange = scale <= 100.0f && scale >= 0.0f; + if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1)) + outOfRange = scale < 100; + + if (outOfRange) + { + m_TimeScale = scale; + SetDirty (); + } + else + { + ErrorString ("Time.timeScale is out of range. Needs to be between 0 and 100."); + } +} + +#if SUPPORT_REPRODUCE_LOG + +void TimeManager::ReadLog (std::ifstream& in) +{ + if (!CheckReproduceTag("Time", in)) + { + FailReproduction("Error reading reproduce log"); + return; + } + + ReadFloat(in, m_DynamicTime.m_CurFrameTime); + ReadFloat(in, m_DynamicTime.m_LastFrameTime); + ReadFloat(in, m_DynamicTime.m_DeltaTime); + ReadFloat(in, m_DynamicTime.m_SmoothDeltaTime); + ReadFloat(in, m_DynamicTime.m_InvDeltaTime); + ReadFloat(in, m_RealtimeStartOfFrame); + + m_ActiveTime = m_DynamicTime; +} + +void TimeManager::WriteLog (std::ofstream& out) +{ + // In the web player WebScripting.cpp injects the first new line for the frame + #if !WEBPLUG + out << std::endl; + #endif + + out << "Time" << std::endl; + WriteFloat(out, m_DynamicTime.m_CurFrameTime); out << " "; + WriteFloat(out, m_DynamicTime.m_LastFrameTime); out << " "; + WriteFloat(out, m_DynamicTime.m_DeltaTime); out << " "; + WriteFloat(out, m_DynamicTime.m_SmoothDeltaTime); out << " "; + WriteFloat(out, m_DynamicTime.m_InvDeltaTime); out << " "; + WriteFloat(out, m_RealtimeStartOfFrame); out << std::endl; + + m_ActiveTime = m_DynamicTime; +} + +double TimeManager::GetRealtime() +{ + #if SUPPORT_REPRODUCE_LOG + + if (RunningReproduction() && (GetReproduceVersion() == 1 || GetReproduceVersion() == 2)) + { + return m_RealtimeStartOfFrame; + } + + if (GetReproduceMode() == kPlaybackReproduceLog) + { + std::ifstream& in = *GetReproduceInStream(); + + if (!CheckReproduceTag("RealTime", in)) + { + ErrorString("Grabbing realtime but there are no realtime calls recorded"); + return m_RealtimeStartOfFrame; + } + + double realtime; + ReadBigFloat(in, realtime); + + return realtime; + } + else if (RunningReproduction()) + { + double realtime = GetTimeSinceStartup() - m_RealZeroTime; + + std::ofstream& out = *GetReproduceOutStream(); + out << "RealTime "; + WriteBigFloat(out, realtime); + out << std::endl; + + return realtime; + } + #endif + double realtime = GetTimeSinceStartup() - m_RealZeroTime; + return realtime; +} +#else +double TimeManager::GetRealtime() +{ + return GetTimeSinceStartup() - m_RealZeroTime; +} +#endif + +#if ENABLE_CLUSTER_SYNC +template<class TransferFunc> +void TimeManager::ClusterTransfer (TransferFunc& transfer) +{ + TRANSFER(m_DynamicTime.m_CurFrameTime); + TRANSFER(m_DynamicTime.m_LastFrameTime); + TRANSFER(m_DynamicTime.m_DeltaTime); + TRANSFER(m_DynamicTime.m_SmoothDeltaTime); + TRANSFER(m_DynamicTime.m_InvDeltaTime); + TRANSFER(m_RealtimeStartOfFrame); + TRANSFER(m_TimeScale); +} +#endif + + + +#if UNITY_ANDROID || UNITY_NACL +#include <sys/time.h> + +inline double clock_gettime_to_double () +{ + timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + return time.tv_sec + (double)time.tv_nsec * 0.000000001; +} + +double TimeSinceStartupImpl () +{ + static double sStartTime = 0; + + if (sStartTime == 0) + sStartTime = clock_gettime_to_double (); + + return clock_gettime_to_double () - sStartTime; +} +#endif + + +IMPLEMENT_CLASS (TimeManager) +IMPLEMENT_OBJECT_SERIALIZE (TimeManager) +IMPLEMENT_CLUSTER_SERIALIZE(TimeManager) +GET_MANAGER (TimeManager) diff --git a/Runtime/Input/TimeManager.h b/Runtime/Input/TimeManager.h new file mode 100644 index 0000000..17a5da9 --- /dev/null +++ b/Runtime/Input/TimeManager.h @@ -0,0 +1,149 @@ +#ifndef TIMEMANAGER_H +#define TIMEMANAGER_H + +#include "Runtime/BaseClasses/GameManager.h" +#include "Configuration/UnityConfigure.h" +#include "Runtime/ClusterRenderer/ClusterRendererDefines.h" + +class TimeManager; + +EXPORT_COREMODULE TimeManager& GetTimeManager (); +/// The time since startup +double GetTimeSinceStartup (); +double TimeSinceStartupImpl (); + + + +/* +Time requirements: +- Delta time shall never be less than kMinimumDeltaTime (So people dont get nans when dividing by delta time) +- The first frame when starting up is always at zero and kStartupDeltaTime delta time. +- delta time is clamped to a maximum of kMaximumDeltaTime +- adding up delta time always gives you the current time + +- after loading a level or pausing, the first frames delta time is kStartupDeltaTime +- fixed delta time is always smaller or equal to dynamic time + +- When starting up there is always one physics frame before the first display! +*/ + +class TimeManager : public GlobalGameManager +{ + public: + + struct TimeHolder + { + TimeHolder(); + double m_CurFrameTime; + double m_LastFrameTime; + float m_DeltaTime; + float m_SmoothDeltaTime; + float m_SmoothingWeight; + float m_InvDeltaTime; + }; + + TimeManager (MemLabelId label, ObjectCreationMode mode); + // ~TimeManager (); declared-by-macro + + REGISTER_DERIVED_CLASS (TimeManager, GlobalGameManager) + DECLARE_OBJECT_SERIALIZE (TimeManager) + // for cluster renderer + DECLARE_CLUSTER_SERIALIZE(TimeManager) + + void AwakeFromLoad (AwakeFromLoadMode mode); + virtual void CheckConsistency (); + + virtual void Update (); + + #if UNITY_EDITOR + /// Called from the editor to bump the frameCount. Later, this would also update GraphicsTime, + /// So we can get animations from the editor. + void NextFrameEditor (); + #endif + + void ResetTime (); + + void SetPause(bool pause); + + +// void SetMinimumDeltaTime (float c) { m_MinimumDeltaTime = c; } + + inline double GetCurTime () const { return m_ActiveTime.m_CurFrameTime; } + inline double GetTimeSinceLevelLoad () const { return m_ActiveTime.m_CurFrameTime + m_LevelLoadOffset; } + inline float GetDeltaTime () const { return m_ActiveTime.m_DeltaTime; } + inline float GetSmoothDeltaTime () const { return m_ActiveTime.m_SmoothDeltaTime; } + + inline float GetInvDeltaTime () const { return m_ActiveTime.m_InvDeltaTime; } + inline int GetFrameCount () const { return m_FrameCount; } + + inline int GetRenderFrameCount () const { return m_RenderFrameCount; } + + inline float GetFixedDeltaTime () {return m_FixedTime.m_DeltaTime; } + void SetFixedDeltaTime (float fixedStep); + inline double GetFixedTime () {return m_FixedTime.m_CurFrameTime; } + + inline float GetMaximumDeltaTime () {return m_MaximumTimestep; } + void SetMaximumDeltaTime (float maxStep); + + + /// Steps the fixed time step until the dynamic time is exceeded. + /// Returns true if it has to be called again to reach the dynamic time + bool StepFixedTime (); + + void SetTimeManually (bool manually) { m_SetTimeManually = manually; } + void SetTime (double time); + void SetDeltaTimeHack (float dt); + void SetTimeScale (float scale); + float GetTimeScale () { return m_TimeScale; } + + inline bool IsUsingFixedTimeStep () const { return m_UseFixedTimeStep; } + + void DidFinishLoadingLevel (); + + void SetCaptureFramerate (int rate) { m_CaptureFramerate = rate; } + int GetCaptureFramerate () { return m_CaptureFramerate; } + + double GetRealtime(); + + void Sync(float framerate); + + #if SUPPORT_REPRODUCE_LOG + void WriteLog (std::ofstream& out); + void ReadLog (std::ifstream& in); + #endif + + private: + + TimeHolder m_FixedTime; + TimeHolder m_DynamicTime; + TimeHolder m_ActiveTime; + + bool m_FirstFrameAfterReset; + bool m_FirstFrameAfterPause; + bool m_FirstFixedFrameAfterReset; + + int m_FrameCount; + int m_RenderFrameCount; + int m_CullFrameCount; + int m_CaptureFramerate; + double m_ZeroTime; + double m_RealZeroTime; + double m_LevelLoadOffset; + double m_RealtimeStartOfFrame; + + bool m_SetTimeManually; + bool m_UseFixedTimeStep; + float m_TimeScale;///< How fast compared to the real time does the game time progress (1.0 is realtime, .5 slow motion) range { -infinity, 100 } + float m_MaximumTimestep; + + double m_LastSyncEnd; +}; + +inline double GetCurTime () { return GetTimeManager ().GetCurTime (); } +inline float GetDeltaTime () { return GetTimeManager ().GetDeltaTime (); } +inline float GetInvDeltaTime () { return GetTimeManager ().GetInvDeltaTime (); } +float CalcInvDeltaTime (float dt); + +const float kMinimumDeltaTime = 0.00001F; + +#endif diff --git a/Runtime/Input/TouchPhaseEmulation.cpp b/Runtime/Input/TouchPhaseEmulation.cpp new file mode 100644 index 0000000..be1ab28 --- /dev/null +++ b/Runtime/Input/TouchPhaseEmulation.cpp @@ -0,0 +1,719 @@ +#include "UnityPrefix.h" + +/* + * TouchPhaseEmulation is layer between an 'event-based' touch OS (Android, Metro etc) and our phase-based script API (similar to iOS). + * + * The core logic of this layer is in DispatchTouchEvent, which handles mapping and collapsing of events to a frame-discrete phase. + */ + +#define DEBUG_TOUCH_EMU (DEBUGMODE && 0) + +#include "TouchPhaseEmulation.h" + +#if UNITY_BLACKBERRY + static const int touchTimeout = 400; +#else + static const int touchTimeout = 150; +#endif + +// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** + +class TouchImpl : public Touch +{ + enum { kEmptyTouchId = ~0UL }; + +public: + TouchImpl () + { + clear (); + } + + long long timestamp; // in milliseconds + UInt32 pointerId; // matches OS pointerId + size_t frameToReport; // frame # for this event to be reported. Should + // only be =gFrameCount or =gFrameCount+1 + size_t frameBegan; // frame # when BEGIN event was received + UInt32 endPhaseInQueue; // acts both as a bool to indicate that this event + // has already received an END/CANCEL from OS and + // will be reported to scripts next frame, and as + // a container to hold the actual value: was it + // END or CANCEL? + + void setDeltaTime (long long newTimestamp) + { + if (timestamp == 0) + return; + + deltaTime = (newTimestamp - timestamp) / 1000.0f; + } + + void setDeltaPos (Vector2f const& newPos) + { + if (CompareApproximately (pos, Vector2f::zero)) + return; + + deltaPos = newPos - pos; + } + + bool isMultitap (long long newTimestamp, Vector2f const& newPos, float screenDPI) + { + static const float tapZoneRadiusCM = 0.4f; // 4mm + static const float cmToInch = 0.393701f; + static const float multitapRadiusPixels = screenDPI * tapZoneRadiusCM * cmToInch; + static const float multitapRadiusSqr = multitapRadiusPixels * multitapRadiusPixels; + + return newTimestamp - timestamp < touchTimeout + && SqrMagnitude (pos - newPos) < multitapRadiusSqr; + } + + void setTapCount (long long newTimestamp, Vector2f const &newPos, float screenDPI) + { + if (isMultitap (newTimestamp, newPos, screenDPI)) + ++tapCount; + else + tapCount = 1; + } + + void init(size_t _pointerId, Vector2f _pos, TouchPhaseEmulation::TouchPhase _phase, + long long _timestamp, size_t currFrame) + { + pointerId = _pointerId; + pos = _pos; + rawPos = _pos; + phase = _phase; + frameBegan = currFrame; + timestamp = _timestamp; + frameToReport = currFrame; + } + + void clear () + { + id = kEmptyTouchId; + phase = TouchPhaseEmulation::kTouchCanceled; + endPhaseInQueue = 0; + deltaPos = Vector2f (0.0f, 0.0f); + deltaTime = 0.0f; + frameToReport = 0; + frameBegan = 0; + tapCount = 0; + rawPos = pos = Vector2f (0.0f, 0.0f); + timestamp = 0; + pointerId = kEmptyTouchId; + } + + bool isOld (size_t frame) const + { + return frameToReport < frame; + } + + bool isEmpty () const + { + return id == kEmptyTouchId; + } + + bool isFinished () const + { + return !isEmpty () && IsEnd (phase); + } + + bool willBeFinishedNextFrame () const + { + return !isEmpty () && IsEnd (endPhaseInQueue); + } + + bool isNow (size_t frame) const + { + return frameToReport == frame; + } + + static bool IsBegin (size_t phase) + { + return phase == TouchPhaseEmulation::kTouchBegan; + } + + static bool IsTransitional (size_t phase) + { + return phase == TouchPhaseEmulation::kTouchMoved || phase == TouchPhaseEmulation::kTouchStationary; + } + + static bool IsEnd (size_t phase) + { + return phase == TouchPhaseEmulation::kTouchEnded || phase == TouchPhaseEmulation::kTouchCanceled; + } + + +#if DEBUG_TOUCH_EMU + void dump (TouchImpl* gAllTouches) + { + size_t index = this - gAllTouches; + char const* phaseNames[] = { + "<Began>", + "<Moved>", + "<Stationary>", + "<Ended>", + "<Canceled>", + }; + + char const *fmt = + "T[%02d]={fid=%d, pid=%d, phase=%s, p=(%3.1f,%3.1f), dp=(%3.1f,%3.1f),\n" + " tm=%lld, dtm=%f, endPhaseQ=%d, fbeg=%d, frep=%d, tcnt=%d}\n"; + + printf_console (fmt, index, id, pointerId, phaseNames[phase], pos.x, pos.y, + deltaPos.x, deltaPos.y, timestamp, deltaTime, + endPhaseInQueue, frameBegan, frameToReport, tapCount); + } +#endif +}; + +// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** + +TouchPhaseEmulation::TouchPhaseEmulation(float screenDPI, bool singleTouchDevice) +: m_AllocatedFingerIDs(0) +, m_FrameCount(0) +, m_ScreenDPI(screenDPI) +, m_IsMultiTouchEnabled(!singleTouchDevice) +, m_IsSingleTouchDevice(singleTouchDevice) +{ + m_TouchSlots = new TouchImpl[kMaxTouchCount]; + InitTouches(); +} + +TouchPhaseEmulation::~TouchPhaseEmulation() +{ + delete [] m_TouchSlots; +} + +void TouchPhaseEmulation::InitTouches () +{ + for (size_t i = 0; i < kMaxTouchCount; ++i) + { + m_TouchSlots[i].clear(); + } + m_AllocatedFingerIDs = 0; + + m_FrameCount = 1; +} + +void TouchPhaseEmulation::PreprocessTouches () +{ + DiscardRedundantTouches(); +} + +void TouchPhaseEmulation::PostprocessTouches () +{ +#if DEBUG_TOUCH_EMU + DumpAll (); +#endif + ++m_FrameCount; + UpdateActiveTouches(); +} + + +bool TouchPhaseEmulation::IsExistingTouch( int pointerId ) +{ + TouchImpl* matchingSlots[kMaxTouchCount]; + const size_t slotsFound = FindByPointerId(matchingSlots, pointerId); + + for (size_t i = 0; i < slotsFound; ++i) + if (matchingSlots[i]) + return true; + + return false; +} + +void TouchPhaseEmulation::AddTouchEvent (int pointerId, float x, float y, TouchPhase newPhase, long long timestamp) +{ + Vector2f pos = Vector2f (x, y); + +#if UNITY_WINRT + // [Metro] With one finger touching; that touch pointerId is usually > 0 + if (!m_IsMultiTouchEnabled && GetTouchCount() > 0 && !IsExistingTouch(pointerId)) + return; +#else + if (!m_IsMultiTouchEnabled && pointerId > 0) + return; +#endif + + DispatchTouchEvent (pointerId, pos, static_cast<TouchPhase> (newPhase), timestamp, m_FrameCount); +} + +void TouchPhaseEmulation::DispatchTouchEvent (size_t pointerId, Vector2f pos, TouchPhase newPhase, long long timestamp, size_t currFrame) +{ + // Brief terminology legend: + // action The OS level indication of what happened in a particular touch event; DOWN, MOVE, UP etc. + // phase Logical state of a touch, emulated to the script side; Began/Moved/Ended. + // Phase is considered constant per frame, and all intra-frame actions are collapsed to a single phase. + // If two actions can't be collapsed (like UP/DOWN), then the DOWN action will be delayed one frame. + // pointerId The ID given to a touch event by the OS. These IDs can be reused if touches end/start within the same frame. + // fingerId The ID we give a touch when presented to the script side. Also known as 'id' in the Touch struct. + // touch event OS level touch information + // touch slot Script level touch information. + + // Step 1: Determine if already tracking this 'pointerId' + // We do this by looping through all touch slots while looking for an active touch with matching 'pointerId'. + // If a slot with phase == Ended has been inactive for 150ms => set it to Inactive + // Step 2: Determine if an old touch slot should be updated with the new event information, or if a new touch slot should be allocated. + // If no touch slot was found in Step 1 => allocate a new touch slot. Skip to step 4. + // If only a singular touch slot was found in Step 1 => use that slot. Skip to step 4. + // If multiple slots were found in Step 1 => determine which slot to use, or if another slot needs to be allocated. + // Step 3: Determine which active slot to use, or allocate a new. + // If phase == Began : for each slot with phase == Ended, consider event to be a multi-tap by comparing position and timestamp. + // If phase != Began : find slot with phase != Ended (should only be one!) + // If no slot matches : allocate a new slot with fingerId = highestFingerIdUsed + 1. + // Step 4: Initialize/update the touch slot based on current and old touch phase. + // If new phase == Began and old phase == Ended => increase tap count. + // If new phase == Began => try to compact finger id + // If new phase == Ended and old phase == Began on the same frame => delay new phase (Ended) until next frame. + // If new phase == Moved and old phase == Stationary => check distance against threshold for Moved/Stationary. + // + // After every frame, loop through all active touch slots: + // If a delayed phase (Ended) was enabled => update the phase. + // If a touch != Ended wasn't updated this frame => set phase = Stationary + // If a touch began and ended the this frame, and it resulted in another, currently active, touch having an increased tap count + // => clear those extra touches, and compact the finger id + + // NB: for now we do use passed position as both raw position and position + // on ios, where we actually implement raw position, different code is used + + FreeExpiredTouches(m_FrameCount, timestamp); + + TouchImpl* matchingSlots[kMaxTouchCount]; + const size_t slotsFound = FindByPointerId(matchingSlots, pointerId); + + TouchImpl* touch = NULL; + int inheritedTapCount = 0; + +#if UNITY_WINRT + // [Metro] Calculate tapCount manually as pointerIds aren't reused. + if (TouchImpl::IsBegin(newPhase)) + inheritedTapCount += CalculateTapCount(timestamp, pos); +#endif + + for (size_t i = 0; i < slotsFound; ++i) + { + TouchImpl* slot = matchingSlots[i]; + + bool touchFinished = slot->isFinished() || slot->willBeFinishedNextFrame(); + if (TouchImpl::IsBegin(newPhase)) + { + if (touchFinished) + { + if (slot->isOld(m_FrameCount)) + { + touch = slot; + } + + if (slot->isMultitap(timestamp, pos, m_ScreenDPI)) + { + inheritedTapCount = slot->tapCount; + } + } + } + else + { + if (!touchFinished) + { + if (touch) + { + if (DEBUGMODE) printf_console("Stale/stuck touch released."); + ExpireOld(*touch); + } + touch = slot; + } + } + } + + if (!touch) + { + if (!TouchImpl::IsBegin(newPhase)) + { + if (DEBUGMODE) printf_console("Dropping touch event part of canceled gesture."); + return; + } + if (!(touch = AllocateNew())) + return; + } + + if (TouchImpl::IsBegin (newPhase)) + { + touch->tapCount = inheritedTapCount; + #if DEBUG_TOUCH_EMU + printf_console("Slot before initialized:"); + touch->dump(m_TouchSlots); + #endif + + touch->init( pointerId, pos, newPhase, timestamp, currFrame ); + touch->setTapCount( timestamp, pos, m_ScreenDPI ); + touch->id = CompactFingerID(touch->id); + + #if DEBUG_TOUCH_EMU + printf_console("Slot initialized:"); + touch->dump(m_TouchSlots); + #endif + return; + } + else if (TouchImpl::IsEnd (newPhase)) + { + // if touch began this frame, we will delay end phase for one frame + if (touch->frameBegan == currFrame) + touch->endPhaseInQueue = newPhase; + else + touch->phase = newPhase; + + // Android only sends CANCELED on the first pointer in a gesture - kill all other active touches when this happens. + if (newPhase == kTouchCanceled) + { + for (size_t i = 0; i < kMaxTouchCount; ++i) + { + TouchImpl* slot = &m_TouchSlots[i]; + if (slot->isEmpty() || slot->isFinished() || slot->willBeFinishedNextFrame()) + continue; + slot->endPhaseInQueue = newPhase; + #if DEBUG_TOUCH_EMU + m_TouchSlots[i].dump(m_TouchSlots); + #endif + } + } + } + else if (newPhase == kTouchMoved + && touch->phase == kTouchStationary) + { + // old event is STATIONARY, the new one is MOVE. Android does not + // report STATIONARY Events, so if MOVE's deltaPos is not big enough, + // let's keep it STATIONARY. Promote to MOVE otherwise. + static const float deltaPosTolerance = 0.5f; + if (Magnitude (touch->pos - pos) >= deltaPosTolerance) + touch->phase = newPhase; + } + + touch->setDeltaPos (pos); + touch->pos = pos; + + touch->setDeltaTime (timestamp); + touch->timestamp = timestamp; + touch->frameToReport = currFrame; + +#if DEBUG_TOUCH_EMU + printf_console("Slot updated:"); + touch->dump(m_TouchSlots); +#endif + +} + +size_t TouchPhaseEmulation::FindByPointerId(TouchImpl* matchingSlots[kMaxTouchCount], size_t pointerId) +{ +#if DEBUG_TOUCH_EMU + printf_console("%s", __FUNCTION__); +#endif + size_t slotsFound = 0; + for (size_t i = 0; i < kMaxTouchCount; ++i) + { + if (m_TouchSlots[i].pointerId != pointerId) + continue; +#if DEBUG_TOUCH_EMU + m_TouchSlots[i].dump(m_TouchSlots); +#endif + matchingSlots[slotsFound++] = &m_TouchSlots[i]; + } + return slotsFound; +} + +TouchImpl* TouchPhaseEmulation::AllocateNew() +{ +#if DEBUG_TOUCH_EMU + printf_console("%s", __FUNCTION__); +#endif + // allocate virtual fingerId + int fingerId = 0; + const int maxFingerId = sizeof(m_AllocatedFingerIDs) * 8; + for (; fingerId < maxFingerId; ++fingerId) + { + UInt32 bitField = (1 << fingerId); + if (m_AllocatedFingerIDs & bitField) + continue; + m_AllocatedFingerIDs |= bitField; + break; + } + if (fingerId >= maxFingerId) + { + Assert (!"Out of virtual finger IDs!"); + return NULL; + } + + // find empty slot for touch + for (size_t i = 0; i < kMaxTouchCount; ++i) + { + TouchImpl& t = m_TouchSlots[i]; + + if (!t.isEmpty()) + continue; + + t.id = fingerId; + t.deltaPos = Vector2f(0, 0); + t.deltaTime = 0.0f; + t.endPhaseInQueue = 0; + + return &t; + } + + Assert (!"Out of free touches!"); + return NULL; +} + +void TouchPhaseEmulation::ExpireOld(TouchImpl& touch) +{ +#if DEBUG_TOUCH_EMU + printf_console("%s", __FUNCTION__); +#endif + + if (touch.isEmpty()) + { + ErrorString("Trying to expire empty touch slot!"); + return; + } + + // deallocate virtual fingerId + UInt32 bitField = (1 << touch.id); + Assert((m_AllocatedFingerIDs & bitField) && "Touch with stale finger ID killed!"); + m_AllocatedFingerIDs &= ~bitField; + + Assert(!touch.endPhaseInQueue && "Delayed touch killed prematurely!"); + touch.clear(); +} + +int TouchPhaseEmulation::CompactFingerID(int id) +{ + int fingerId = 0; + const int maxFingerId = sizeof(m_AllocatedFingerIDs) * 8; + for (; fingerId < maxFingerId; ++fingerId) + { + UInt32 bitField = (1 << fingerId); + if (m_AllocatedFingerIDs & bitField) + continue; + + if (id < fingerId) + return id; + + m_AllocatedFingerIDs |= bitField; + bitField = (1 << id); + Assert((m_AllocatedFingerIDs & bitField) && "Touch with stale finger ID killed!"); + m_AllocatedFingerIDs &= ~bitField; + id = fingerId; + break; + } + return id; +} + +void TouchPhaseEmulation::FreeExpiredTouches (size_t eventFrame, long long timestamp) +{ + for (size_t i = 0; i < kMaxTouchCount; ++i) + { + TouchImpl& touch = m_TouchSlots[i]; + + if (touch.isEmpty()) + continue; + + long long age = timestamp - touch.timestamp; + if (touch.isOld(eventFrame) && touch.isFinished() && age > touchTimeout) + { + ExpireOld(touch); + } + } +} + +void TouchPhaseEmulation::DiscardRedundantTouches() +{ + for (size_t i = 0; i < kMaxTouchCount; ++i) + { + TouchImpl* t0 = &m_TouchSlots[i]; + TouchImpl* t1 = 0; + + if (t0->isEmpty()) + continue; + + // find Down/Up touches recorded within a single frame + bool downUpTouch = + t0->frameBegan == m_FrameCount && + t0->frameToReport == m_FrameCount && + t0->willBeFinishedNextFrame() && + !t0->isFinished(); + + if (!downUpTouch) + continue; + + #if DEBUG_TOUCH_EMU + printf_console("Found new touch set to expire next frame"); + t0->dump(m_TouchSlots); + #endif + + bool redundant = false; + + // compare the multitap info + for (size_t j = 0; j < kMaxTouchCount; ++j) + { + t1 = &m_TouchSlots[j]; + + if (t1->isEmpty() || i == j) + continue; + + bool multitapTouch = + t1->frameBegan == m_FrameCount && + t1->frameToReport == m_FrameCount && + t1->pointerId == t0->pointerId && + t1->tapCount > t0->tapCount && + t0->isMultitap(t1->timestamp, t1->pos, m_ScreenDPI) && + !t1->isFinished(); + + if (!multitapTouch) + continue; + + #if DEBUG_TOUCH_EMU + printf_console("Found new touch, with tapCount that matches the one found earlier"); + t1->dump(m_TouchSlots); + #endif + // found a match + redundant = true; + break; + } + + if (redundant) + { + t0->endPhaseInQueue = 0; // this touch is officially gone anyway + ExpireOld(*t0); + t1->id = CompactFingerID(t1->id); + #if DEBUG_TOUCH_EMU + printf_console("New touch, with compacted finger id"); + t1->dump(m_TouchSlots); + #endif + } + else + { + t0->id = CompactFingerID(t0->id); + #if DEBUG_TOUCH_EMU + printf_console("Refresh touch, with compacted finger id"); + t0->dump(m_TouchSlots); + #endif + } + } +} + +void TouchPhaseEmulation::UpdateActiveTouches() +{ + for (size_t i = 0; i < kMaxTouchCount; ++i) + { + TouchImpl& touch = m_TouchSlots[i]; + + if (touch.isEmpty()) + continue; + + // Skip Expired Touches + if (touch.isFinished()) + { + continue; + } + + // End Delayed Touches + if (touch.willBeFinishedNextFrame ()) + { + touch.deltaPos = Vector2f (0, 0); + touch.phase = touch.endPhaseInQueue; + touch.endPhaseInQueue = 0; + touch.frameToReport = m_FrameCount; + continue; + } + + // Default Stationary Touches + touch.phase = kTouchStationary; + touch.deltaPos = Vector2f (0, 0); + touch.frameToReport = m_FrameCount; + } + +} + +size_t TouchPhaseEmulation::GetTouchCount () +{ + size_t count = 0; + + // TODO: on first call to GetTouchCount() per frame, call PackTouchIds() to + // compact virtual touch IDs to stand out less + + for (size_t i = 0; i < kMaxTouchCount; ++i) + if ( m_TouchSlots[i].isNow(m_FrameCount) && + !m_TouchSlots[i].isEmpty() ) + ++count; + + return count; +} + +size_t TouchPhaseEmulation::GetActiveTouchCount () +{ + size_t count = 0; + + for (size_t i = 0; i < kMaxTouchCount; ++i) + if (!m_TouchSlots[i].isEmpty() && !m_TouchSlots[i].isFinished()) + ++count; + + return count; +} + +// @param index Zero-based index of events that are to be reported this +// frame. Since not all of the events in the container need to +// be reported this frame, it's used to skip already reported +// ones. +bool TouchPhaseEmulation::GetTouch (size_t index, Touch& touch) +{ + for (size_t i = 0; i < kMaxTouchCount; ++i) + { + if ( m_TouchSlots[i].isNow(m_FrameCount) && + !m_TouchSlots[i].isEmpty() && + index-- == 0 ) + { + touch = m_TouchSlots[i]; + return true; + } + } + + return false; +} + +bool TouchPhaseEmulation::IsMultiTouchEnabled () +{ + if (m_IsSingleTouchDevice) + return false; + + return m_IsMultiTouchEnabled; +} + +void TouchPhaseEmulation::SetMultiTouchEnabled (bool enabled) +{ + if (m_IsSingleTouchDevice) + return; + + m_IsMultiTouchEnabled = enabled; +} + +int TouchPhaseEmulation::CalculateTapCount( long long timestamp, Vector2f const &pos ) const +{ + int result = 0; + for (size_t i = 0; i < kMaxTouchCount; ++i) + { + TouchImpl& touch = m_TouchSlots[i]; + + if (touch.isEmpty()) + continue; + + if (touch.isMultitap(timestamp, pos, m_ScreenDPI)) + result += touch.tapCount; + } + + return result; +} + +#if DEBUG_TOUCH_EMU +void TouchPhaseEmulation::DumpAll (bool verbose) +{ + for (int i = 0; i < kMaxTouchCount; ++i) + if (!m_TouchSlots[i].isEmpty() && !m_TouchSlots[i].isOld(m_FrameCount) || verbose) + m_TouchSlots[i].dump( m_TouchSlots ); +} +#endif diff --git a/Runtime/Input/TouchPhaseEmulation.h b/Runtime/Input/TouchPhaseEmulation.h new file mode 100644 index 0000000..e18c1ca --- /dev/null +++ b/Runtime/Input/TouchPhaseEmulation.h @@ -0,0 +1,68 @@ +#ifndef __UNITY_INPUT_TOUCHPHASEEMULATION_H +#define __UNITY_INPUT_TOUCHPHASEEMULATION_H + +#include "Runtime/Math/Vector2.h" +#include "GetInput.h" + +class TouchImpl; + +class TouchPhaseEmulation +{ +public: + TouchPhaseEmulation(float screenDPI, bool singleTouchDevice); + virtual ~TouchPhaseEmulation(); + +public: + + void InitTouches(); + void PreprocessTouches(); + void PostprocessTouches(); + + enum TouchPhase + { + kTouchBegan = 0, + kTouchMoved = 1, + kTouchStationary = 2, + kTouchEnded = 3, + kTouchCanceled = 4 + }; + + void AddTouchEvent (int pointerId, float x, float y, TouchPhase newPhase, long long timestamp); + size_t GetTouchCount(); + size_t GetActiveTouchCount(); + bool GetTouch(size_t index, Touch& touch); + + bool IsMultiTouchEnabled (); + void SetMultiTouchEnabled (bool enabled); + +private: + + void DispatchTouchEvent (size_t pointerId, Vector2f pos, TouchPhase newPhase, long long timestamp, size_t currFrame); + bool IsExistingTouch( int pointerId ); + + enum { kMaxTouchCount = 32 }; + + size_t FindByPointerId(TouchImpl* matchingSlots[kMaxTouchCount], size_t pointerId); + TouchImpl* AllocateNew(); + void ExpireOld(TouchImpl& touch); + int CompactFingerID(int id); + void FreeExpiredTouches (size_t eventFrame, long long timestamp); + + void DiscardRedundantTouches(); + void UpdateActiveTouches(); + int CalculateTapCount( long long timestamp, Vector2f const &pos ) const; + +#if DEBUG_TOUCH_EMU + void DumpAll (bool verbose=false); +#endif + + TouchImpl* m_TouchSlots; //[kMaxTouchCount]; + UInt32 m_AllocatedFingerIDs; // holds one bit per finger + size_t m_FrameCount; + const float m_ScreenDPI; + bool m_IsMultiTouchEnabled; + const bool m_IsSingleTouchDevice; + +}; + +#endif // __UNITY_INPUT_TOUCHPHASEEMULATION_H |