summaryrefslogtreecommitdiff
path: root/Runtime/Input
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Input')
-rw-r--r--Runtime/Input/GetInput.h288
-rw-r--r--Runtime/Input/InputAxis.cpp226
-rw-r--r--Runtime/Input/InputAxis.h151
-rw-r--r--Runtime/Input/InputManager.cpp1329
-rw-r--r--Runtime/Input/InputManager.h470
-rw-r--r--Runtime/Input/LocationService.h51
-rw-r--r--Runtime/Input/OnScreenKeyboard.h47
-rw-r--r--Runtime/Input/SimulateInputEvents.cpp222
-rw-r--r--Runtime/Input/SimulateInputEvents.h10
-rw-r--r--Runtime/Input/TimeManager.cpp489
-rw-r--r--Runtime/Input/TimeManager.h149
-rw-r--r--Runtime/Input/TouchPhaseEmulation.cpp719
-rw-r--r--Runtime/Input/TouchPhaseEmulation.h68
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