first commit

This commit is contained in:
Stefano Rossi 2025-07-12 18:59:18 +02:00
commit aa44d3ad70
Signed by: chadmin
GPG key ID: 9EFA2130646BC893
1585 changed files with 277994 additions and 0 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9359fedb808df2941a6930749a3f9dcc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,64 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Bhaptics.SDK2
{
using System;
using UnityEngine;
public class BhapticsEvent
{
public const string WAIST_LEFT = "waist_left";
public const string DRINKPOTION = "drinkpotion";
public const string SHOULDER_RIGHT = "shoulder_right";
public const string FLAMEBURNING = "flameburning";
public const string EXPLOSION = "explosion";
public const string FOREARM_RIGHT = "forearm_right";
public const string HEALING = "healing";
public const string CHEST_RIGHT = "chest_right";
public const string STEP_LINK = "step_link";
public const string FOREARM_LEFT = "forearm_left";
public const string HEARTBEAT_FAST = "heartbeat_fast";
public const string HEAD_RIGHT = "head_right";
public const string GAME_START = "game_start";
public const string SHOULDER_LEFT = "shoulder_left";
public const string HEARTBEAT = "heartbeat";
public const string HEAD_ALL = "head_all";
public const string HAPTIC_STEP_LINK_PERFECT = "haptic_step_link_perfect";
public const string HEAD_LEFT = "head_left";
public const string ELECTRICSHOCK = "electricshock";
public const string EATFOOD = "eatfood";
public const string WAIST_RIGHT = "waist_right";
public const string CHEST_LEFT = "chest_left";
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b056a440d2364c140bc4ab0a8bd2cc6c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a57afe0846bbb0a48ba2f8aae6134ed3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
{
"name": "Bhaptics.SDK2",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 464dd5f1479658c42bf2844d11534e1e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,314 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Bhaptics.SDK2
{
[Serializable]
internal class Device
{
public bool paired;
public string deviceName;
public int position;
public bool connected;
public string address;
public int battery;
public bool audioJackIn;
}
public enum PositionType
{
Vest, ForearmL, ForearmR, Head, HandL, HandR, FootL, FootR, GloveL, GloveR
}
public enum GloveShapeValue
{
Constant = 0,
Decreasing = 1,
Increasing = 2
}
public enum GlovePlayTime
{
None = 0,
FiveMS = 1,
TenMS = 2,
TwentyMS = 4,
ThirtyMS = 6,
FortyMS = 8
}
[Serializable]
public class MappingMetaData
{
public int durationMillis;
public string key;
public string description;
public bool isAudio;
public long updateTime;
public string[] positions;
}
[Serializable]
internal class MappingMessage
{
public bool status;
public List<MappingMetaData> message;
public static MappingMessage CreateFromJSON(string jsonString)
{
return JsonUtility.FromJson<MappingMessage>(jsonString);
}
}
[Serializable]
public class DeployMessage
{
public string name;
public int version;
}
[Serializable]
public class DeployHttpMessage
{
public bool status;
public DeployMessage message;
public static DeployHttpMessage CreateFromJSON(string jsonString)
{
return JsonUtility.FromJson<DeployHttpMessage>(jsonString);
}
}
[Serializable]
public class HapticDevice
{
public bool IsPaired;
public bool IsConnected;
public string DeviceName;
public PositionType Position;
public string Address;
public PositionType[] Candidates;
public bool IsAudioJack;
public int Battery;
}
[Serializable]
class DeviceListMessage
{
public Device[] devices;
}
public class BhapticsHelpers
{
[Serializable]
private class AndroidDeviceMessage
{
public Device[] items;
}
private static readonly HapticDevice[] HapticDevices =
{
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
new HapticDevice(),
};
public static float Angle(Vector3 fwd, Vector3 targetDir)
{
var fwd2d = new Vector3(fwd.x, 0, fwd.z);
var targetDir2d = new Vector3(targetDir.x, 0, targetDir.z);
float angle = Vector3.Angle(fwd2d, targetDir2d);
if (AngleDir(fwd, targetDir, Vector3.up) == -1)
{
angle = 360.0f - angle;
if (angle > 359.9999f)
angle -= 360.0f;
return angle;
}
return angle;
}
private static int AngleDir(Vector3 fwd, Vector3 targetDir, Vector3 up)
{
Vector3 perp = Vector3.Cross(fwd, targetDir);
float dir = Vector3.Dot(perp, up);
if (dir > 0.0)
{
return 1;
}
if (dir < 0.0)
{
return -1;
}
return 0;
}
public static string ErrorCodeToMessage(int code)
{
switch (code)
{
case 0:
return "BHAPTICS_SETTINGS_SUCCESS";
case 1:
return "NETWORK_ERROR";
case 2:
return "API_KEY_INVALID";
case 3:
return "APP_ID_INVALID";
case 4:
return "APPLICATION_NOT_DEPLOY";
case 100:
return "NOT_CHANGED";
case 999:
return "UNKNOWN_ISSUES";
}
return "UNKNOWN CODE";
}
public static List<HapticDevice> ConvertToBhapticsDevices(string deviceJson)
{
var res = new List<HapticDevice>();
var devices = JsonUtility.FromJson<AndroidDeviceMessage>(deviceJson);
for (var i = 0; i < devices.items.Length; i++)
{
var hDevice = HapticDevices[i];
var d = devices.items[i];
if (i >= 15)
{
break;
}
hDevice.IsPaired = d.paired;
hDevice.IsConnected = d.connected;
hDevice.Address = d.address;
hDevice.Position = ToDeviceType(d.position);
hDevice.DeviceName = d.deviceName;
hDevice.Candidates = ToCandidates(d.position);
hDevice.Battery = d.battery;
hDevice.IsAudioJack = d.audioJackIn;
res.Add(hDevice);
}
return res;
}
internal static List<HapticDevice> Convert(Device[] deviceJson)
{
var res = new List<HapticDevice>();
for (var i = 0; i < deviceJson.Length; i++)
{
res.Add(Convert(deviceJson[i]));
}
return res;
}
private static HapticDevice Convert(Device d)
{
var isConnected = d.connected;
return new HapticDevice()
{
IsPaired = d.paired,
IsConnected = isConnected,
Address = d.address,
Position = ToDeviceType(d.position),
DeviceName = d.deviceName,
Candidates = ToCandidates(d.position),
Battery = d.battery,
IsAudioJack = d.audioJackIn,
};
}
private static readonly PositionType[] HeadCandidates = { PositionType.Head };
private static readonly PositionType[] VestCandidates = { PositionType.Vest };
private static readonly PositionType[] ArmCandidates = { PositionType.ForearmL, PositionType.ForearmR };
private static readonly PositionType[] HandCandidates = { PositionType.HandL, PositionType.HandR };
private static readonly PositionType[] FootCandidates = { PositionType.FootR, PositionType.FootL };
private static readonly PositionType[] GloveLCandidates = { PositionType.GloveL };
private static readonly PositionType[] GloveRCandidates = { PositionType.GloveR };
private static readonly PositionType[] EmptyCandidates = { };
private static PositionType[] ToCandidates(int type)
{
switch (type)
{
case 3:
return HeadCandidates;
case 0:
return VestCandidates;
case 1:
return ArmCandidates;
case 2:
return ArmCandidates;
case 4:
return HandCandidates;
case 5:
return HandCandidates;
case 6:
return FootCandidates;
case 7:
return FootCandidates;
case 8:
return GloveLCandidates;
case 9:
return GloveRCandidates;
}
return EmptyCandidates;
}
private static PositionType ToDeviceType(int type)
{
switch (type)
{
case 0:
return PositionType.Vest;
case 1:
return PositionType.ForearmL;
case 2:
return PositionType.ForearmR;
case 3:
return PositionType.Head;
case 4:
return PositionType.HandL;
case 5:
return PositionType.HandR;
case 6:
return PositionType.FootL;
case 7:
return PositionType.FootR;
case 8:
return PositionType.GloveL;
case 9:
return PositionType.GloveR;
}
return PositionType.Vest;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5770e2ca1fe6669488682dfa2cbc8936
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,125 @@
using System.IO;
using UnityEngine;
using UnityEngine.Events;
namespace Bhaptics.SDK2
{
public class BhapticsSDK2 : MonoBehaviour
{
private static BhapticsSDK2 instance;
[Header("Only for PC")]
[Tooltip("If bHaptics Player(PC) is not turned on when this program starts, it automatically runs bHaptics Player.")]
[SerializeField] private bool autoRunBhapticsPlayer = false;
private bool autoRequestBluetoothPermission = true;
private BhapticsSettings bhapticsSettings;
private void Awake()
{
if (instance != null)
{
DestroyImmediate(this);
return;
}
instance = this;
DontDestroyOnLoad(this);
bhapticsSettings = BhapticsSettings.Instance;
var hapticDevices = BhapticsLibrary.GetDevices();
BhapticsLogManager.LogFormat("[bHaptics] devices {0}", hapticDevices.Count);
if (string.IsNullOrEmpty(bhapticsSettings.AppId))
{
Debug.LogError("[bHaptics] Please set API_ID.");
return;
}
BhapticsLogManager.LogFormat("[bHaptics] {0} {1}", bhapticsSettings.AppId, bhapticsSettings.ApiKey);
BhapticsLibrary.Initialize(bhapticsSettings.AppId, bhapticsSettings.ApiKey, bhapticsSettings.DefaultDeploy, autoRequestBluetoothPermission);
var playerSetup = BhapticsLibrary.IsBhapticsAvailable(autoRunBhapticsPlayer);
BhapticsLogManager.LogFormat("[bHaptics] player IsBhapticsAvailable {0}", playerSetup);
BhapticsLogManager.LogFormat("[bHaptics] Initialized. ");
}
private void OnApplicationFocus(bool pauseStatus)
{
if (pauseStatus)
{
BhapticsLibrary.OnApplicationFocus();
}
else
{
BhapticsLibrary.OnApplicationPause();
}
}
private void OnDestroy()
{
if (instance.GetInstanceID() == this.GetInstanceID())
{
BhapticsLibrary.Destroy();
}
}
private void OnApplicationQuit()
{
BhapticsLibrary.OnApplicationQuit();
}
#if UNITY_EDITOR
public static string GetBhapticsFolderParentPath(bool localToAssetsFolder = false)
{
BhapticsSettings asset = ScriptableObject.CreateInstance<BhapticsSettings>();
UnityEditor.MonoScript scriptAsset = UnityEditor.MonoScript.FromScriptableObject(asset);
string scriptPath = UnityEditor.AssetDatabase.GetAssetPath(scriptAsset);
FileInfo settingsScriptFileInfo = new FileInfo(scriptPath);
string fullPath = settingsScriptFileInfo.Directory.Parent.Parent.FullName;
if (localToAssetsFolder == false)
{
return fullPath;
}
DirectoryInfo assetsDirectoryInfo = new DirectoryInfo(Application.dataPath);
string localPath = fullPath.Substring(assetsDirectoryInfo.Parent.FullName.Length + 1);
return localPath;
}
public static string GetResourcePath(bool localToAssetsFolder = false)
{
string path = Path.Combine(GetBhapticsFolderParentPath(localToAssetsFolder), "Resources");
if (Directory.Exists(path) == false)
{
Directory.CreateDirectory(path);
}
return path;
}
public static string GetScriptPath(bool localToAssetsFolder = false)
{
string path = Path.Combine(GetBhapticsFolderParentPath(localToAssetsFolder), "Scripts");
if (Directory.Exists(path) == false)
{
Directory.CreateDirectory(path);
}
return path;
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23aca228b96921048bd497757f5bca2a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,163 @@
using System;
using UnityEngine;
namespace Bhaptics.SDK2
{
[Serializable]
public class BhapticsConfig
{
public string appId = "";
public string apiKey = "";
}
public class BhapticsSettings : ScriptableObject
{
private static BhapticsSettings instance;
[SerializeField] private string appName = "";
[SerializeField] private string appId = "";
[SerializeField] private string apiKey = "";
[SerializeField] private int lastDeployVersion = -1;
[SerializeField] private MappingMetaData[] eventData;
[HideInInspector, SerializeField] private string defaultDeploy = "";
#region Properties
public static BhapticsSettings Instance
{
get
{
LoadInstance();
return instance;
}
}
public string AppName
{
get
{
return appName;
}
set
{
appName = value;
}
}
public string AppId
{
get
{
return appId;
}
set
{
appId = value;
}
}
public string ApiKey
{
get
{
return apiKey;
}
set
{
apiKey = value;
}
}
public MappingMetaData[] EventData
{
get
{
return eventData;
}
set
{
eventData = value;
}
}
public int LastDeployVersion
{
get
{
return lastDeployVersion;
}
set
{
lastDeployVersion = value;
}
}
public string DefaultDeploy
{
get
{
return defaultDeploy;
}
set
{
defaultDeploy = value;
}
}
#endregion
public static void VerifyScriptableObject()
{
LoadInstance();
}
public static void ResetInstance()
{
if (instance == null)
{
return;
}
BhapticsLogManager.LogFormat("[bHaptics] Reset App Setup: {0}", instance.AppName);
instance.appName = "";
instance.appId = "";
instance.apiKey = "";
instance.eventData = null;
instance.lastDeployVersion = -1;
instance.defaultDeploy = "";
#if UNITY_EDITOR
var localFolderPath = BhapticsSDK2.GetResourcePath(true);
var assetPath = System.IO.Path.Combine(localFolderPath, "BhapticsSettings.asset");
UnityEditor.EditorUtility.SetDirty(instance);
UnityEditor.AssetDatabase.SaveAssets();
#endif
}
private static void LoadInstance()
{
if (instance == null)
{
BhapticsLogManager.LogFormat("Load BhapticsSettings.asset");
instance = Resources.Load<BhapticsSettings>("BhapticsSettings");
if (instance == null)
{
instance = CreateInstance<BhapticsSettings>();
#if UNITY_EDITOR
var localFolderPath = BhapticsSDK2.GetResourcePath(true);
var assetPath = System.IO.Path.Combine(localFolderPath, "BhapticsSettings.asset");
UnityEditor.AssetDatabase.CreateAsset(instance, assetPath);
UnityEditor.AssetDatabase.SaveAssets();
#endif
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b3b9d9689fae93418e33472f9eccaa7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4ff8d7d6352ce564880dd5f34265ba94
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 09b951e214c53814ba1ab357433fa414
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
android:installLocation="auto"
android:versionCode="1"
android:versionName="1.0" >
<!--If your Android Target API Level is API Level 30 or above,
you need to add the following <queries> syntax to your AndroidManifest.-->
<queries>
<package android:name="com.bhaptics.player"/>
</queries>
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true" >
<activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name" android:screenOrientation="landscape" android:launchMode="singleTask" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density" android:hardwareAccelerated="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="com.oculus.intent.category.VR" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
<meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />
</application>
<!--
Important Notes
1. If your project contains a custom AndroidManifest.xml file, add permissions
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" android:minSdkVersion="31" />
2. Otherwise, copy this file to this location in your project:
Assets/Plugins/Android/AndroidManifest.xml.
-->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" android:minSdkVersion="31" />
</manifest>

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a83632b59de13ea4baf3d06d31b2bf16
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: ef161821b3ddad24abba5b8882c3e557
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: abe726f1745d2314e9b7dea2b3dbd909
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: f5e3094bd6a10bd45867b4cf61e15907
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: 66466aff6b8a126419a08f2737000387
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: 8caffa37d6e0be84486b14da52771369
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,516 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Bhaptics.SDK2
{
public class AndroidHaptic
{
protected static AndroidJavaObject androidJavaObject;
private readonly Dictionary<string, int> eventDictionary = new Dictionary<string, int>();
private static readonly object[] GetEventIdParams = new object[1];
private static readonly object[] PlayGloveParams = new object[5];
private static readonly jvalue[] PlayEventParams = new jvalue[6];
private static readonly jvalue[] PlayLoopParams = new jvalue[8];
private static readonly jvalue[] EmptyParams = new jvalue[0];
private static readonly object[] PlayMotorsParams = new object[3];
private static readonly jvalue[] IsPlayingParams = new jvalue[1];
private static readonly jvalue[] IsPlayingByEventIdParams = new jvalue[1];
private static readonly jvalue[] StopByRequestIdParams = new jvalue[1];
private static readonly jvalue[] StopByEventIdParams = new jvalue[1];
private static readonly object[] PingParams = new object[1];
private List<HapticDevice> deviceList;
private readonly IntPtr bhapticsWrapperObjectPtr;
private readonly IntPtr bhapticsWrapperClassPtr;
private readonly IntPtr initializeRequestPermissionPtr;
private readonly IntPtr playEventPtr;
private readonly IntPtr playMotorsPtr;
private readonly IntPtr playLoopPtr;
private readonly IntPtr playGlovePtr;
private readonly IntPtr getEventIdPtr;
private readonly IntPtr stopIntPtr;
private readonly IntPtr stopByEventIdPtr;
private readonly IntPtr stopAllPtr;
private readonly IntPtr pingPtr;
private readonly IntPtr pingAllPtr;
// bool methods
private readonly IntPtr isPlayingAnythingPtr;
private readonly IntPtr isBhapticsUserPtr;
private readonly IntPtr isPlayingByEventIdPtr;
private readonly IntPtr isPlayingByRequestIdPtr;
private readonly IntPtr refreshPairingInfoPtr;
private readonly IntPtr getDeviceListPtr;
public AndroidHaptic()
{
try
{
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
androidJavaObject =
new AndroidJavaObject("com.bhaptics.bhapticsunity.BhapticsManagerWrapper", currentActivity);
bhapticsWrapperObjectPtr = androidJavaObject.GetRawObject();
bhapticsWrapperClassPtr = androidJavaObject.GetRawClass();
initializeRequestPermissionPtr = AndroidJNIHelper.GetMethodID(
androidJavaObject.GetRawClass(),
"initializeWithPermissionOption",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
playEventPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "playEvent", "(IIFFFF)I");
playGlovePtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "playGlove", "(I[I[I[II)I");
getEventIdPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "getEventId", "(Ljava/lang/String;)I");
playLoopPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "playLoop", "(IIFFFFII)I");
playMotorsPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "playMotors", "(II[I)I");
stopIntPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "stopInt");
stopByEventIdPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "stopByEventId", "(I)Z");
stopAllPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "stopAll");
pingPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "ping", "(Ljava/lang/String;)V");
pingAllPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "pingAll");
isPlayingAnythingPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "isAnythingPlaying", "()Z");
isPlayingByEventIdPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "isPlayingByEventId", "(I)Z");
isPlayingByRequestIdPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "isPlayingByRequestId", "(I)Z");
isBhapticsUserPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "isBhapticsUser");
refreshPairingInfoPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "refreshPairing");
getDeviceListPtr = AndroidJNIHelper.GetMethodID(bhapticsWrapperClassPtr, "getDeviceListString", "()Ljava/lang/String;");
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("AndroidHaptic {0} {1} ", e.Message, e);
}
deviceList = GetDevices();
}
public bool CheckBhapticsAvailable()
{
if (androidJavaObject == null)
{
return false;
}
return AndroidUtils.CallNativeBoolMethod(bhapticsWrapperObjectPtr, isBhapticsUserPtr, EmptyParams);
}
public void RefreshPairing()
{
if (androidJavaObject == null)
{
return;
}
AndroidUtils.CallNativeVoidMethod(bhapticsWrapperObjectPtr, refreshPairingInfoPtr, EmptyParams);
}
private int GetEventId(string eventId)
{
if (androidJavaObject == null)
{
return -1;
}
GetEventIdParams[0] = eventId;
return AndroidUtils.CallNativeIntMethod(bhapticsWrapperObjectPtr, getEventIdPtr, GetEventIdParams);
}
public List<HapticDevice> GetDevices()
{
try
{
string result = AndroidUtils.CallNativeStringMethod(bhapticsWrapperObjectPtr, getDeviceListPtr, EmptyParams);
deviceList = BhapticsHelpers.ConvertToBhapticsDevices(result);
return deviceList;
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] GetDevices() {0}", e.Message);
}
return new List<HapticDevice>();
}
public void InitializeWithPermission(string workspaceId, string sdkKey, string json, bool requestPermission)
{
BhapticsLogManager.LogFormat("[bHaptics] InitializeWithPermission() {0} {1}", workspaceId, json);
AndroidUtils.CallNativeVoidMethod(
bhapticsWrapperObjectPtr, initializeRequestPermissionPtr,
new object[] { workspaceId, sdkKey, json, requestPermission ? 1 : 0 });
}
public bool IsConnect()
{
return false;
}
public bool IsPlaying()
{
if (androidJavaObject == null)
{
return false;
}
return AndroidUtils.CallNativeBoolMethod(bhapticsWrapperObjectPtr, isPlayingAnythingPtr, EmptyParams);
}
public bool IsPlayingByEventId(string eventId)
{
if (androidJavaObject == null)
{
return false;
}
int eventIntValue = TryGetEventIntValue(eventId);
IsPlayingByEventIdParams[0].i = eventIntValue;
return AndroidUtils.CallNativeBoolMethod(bhapticsWrapperObjectPtr, isPlayingByEventIdPtr, IsPlayingParams);
}
public bool IsPlayingByRequestId(int requestId)
{
if (androidJavaObject == null)
{
return false;
}
IsPlayingParams[0].i = requestId;
return AndroidUtils.CallNativeBoolMethod(bhapticsWrapperObjectPtr, isPlayingByRequestIdPtr, IsPlayingParams);
}
public void RefreshPairingInfo()
{
if(androidJavaObject == null)
{
return;
}
AndroidUtils.CallNativeVoidMethod(bhapticsWrapperObjectPtr, refreshPairingInfoPtr, EmptyParams);
}
public int Play(string eventId)
{
return PlayParam(eventId, 1f, 1f, 0f, 0f);
}
public int PlayParam(string eventId, float intensity, float duration, float angleX, float offsetY)
{
if (androidJavaObject == null)
{
return -1;
}
int eventIntValue = TryGetEventIntValue(eventId);
int requestId = UnityEngine.Random.Range(0, int.MaxValue);
PlayEventParams[0].i = eventIntValue;
PlayEventParams[1].i = requestId;
PlayEventParams[2].f = intensity;
PlayEventParams[3].f = duration;
PlayEventParams[4].f = angleX;
PlayEventParams[5].f = offsetY;
return AndroidUtils.CallNativeIntMethod(bhapticsWrapperObjectPtr, playEventPtr, PlayEventParams);
}
public int PlayMotors(int position, int[] motors, int durationMillis)
{
if (androidJavaObject == null)
{
return -1;
}
PlayMotorsParams[0] = position;
PlayMotorsParams[1] = durationMillis;
PlayMotorsParams[2] = motors;
return AndroidUtils.CallNativeIntMethod(bhapticsWrapperObjectPtr, playMotorsPtr, PlayMotorsParams);
}
public int PlayGlove(int position, int[] motors, int[] playTimeValues, int[] shapeValues)
{
if (androidJavaObject == null)
{
return -1;
}
PlayGloveParams[0] = position;
PlayGloveParams[1] = motors;
PlayGloveParams[2] = playTimeValues;
PlayGloveParams[3] = shapeValues;
PlayGloveParams[4] = 6;
return AndroidUtils.CallNativeIntMethod(bhapticsWrapperObjectPtr, playGlovePtr, PlayGloveParams);
}
public int PlayPath(int position, float[] xValues, float[] yValues, int[] intensityValues, int duration)
{
if (androidJavaObject == null)
{
return -1;
}
androidJavaObject.Call("submitPath", "key?", position, xValues, yValues, intensityValues, duration);
return -1;
}
public int PlayLoop(string eventId, float intensity, float duration, float angleX, float offsetY, int interval, int maxCount)
{
if(androidJavaObject == null)
{
return -1;
}
int eventIntValue = TryGetEventIntValue(eventId);
int requestId = UnityEngine.Random.Range(0, int.MaxValue);
PlayLoopParams[0].i = eventIntValue;
PlayLoopParams[1].i = requestId;
PlayLoopParams[2].f = intensity;
PlayLoopParams[3].f = duration;
PlayLoopParams[4].f = angleX;
PlayLoopParams[5].f = offsetY;
PlayLoopParams[6].i = interval;
PlayLoopParams[7].i = maxCount;
return AndroidUtils.CallNativeIntMethod(bhapticsWrapperObjectPtr, playLoopPtr, PlayLoopParams);
}
public bool StopByRequestId(int key)
{
if (androidJavaObject == null)
{
return false;
}
StopByRequestIdParams[0].i = key;
return AndroidUtils.CallNativeBoolMethod(bhapticsWrapperObjectPtr, stopIntPtr, StopByRequestIdParams);
}
public bool StopByEventId(string eventId)
{
if (androidJavaObject == null)
{
return false;
}
try
{
int eventIntValue = TryGetEventIntValue(eventId);
StopByEventIdParams[0].i = eventIntValue;
return AndroidUtils.CallNativeBoolMethod(bhapticsWrapperObjectPtr, stopByEventIdPtr, StopByEventIdParams);
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] StopByEventId() : {0}", e.Message);
}
return false;
}
private int TryGetEventIntValue(string eventId)
{
int eventIntValue = -1;
if (eventDictionary.TryGetValue(eventId, out var value))
{
eventIntValue = value;
}
else
{
eventIntValue = GetEventId(eventId);
eventDictionary[eventId] = eventIntValue;
}
return eventIntValue;
}
public bool Stop()
{
if (androidJavaObject != null)
{
try
{
return AndroidUtils.CallNativeBoolMethod(bhapticsWrapperObjectPtr, stopAllPtr, EmptyParams);
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] Stop() : {0}", e.Message);
}
}
return false;
}
public void Dispose()
{
if (androidJavaObject != null)
{
androidJavaObject.Call("quit");
androidJavaObject = null;
}
}
public void TogglePosition(string address)
{
if (androidJavaObject == null)
{
return;
}
if (androidJavaObject != null)
{
androidJavaObject.Call("togglePosition", address);
}
}
public void PingAll()
{
if (androidJavaObject == null)
{
return;
}
AndroidUtils.CallNativeVoidMethod(bhapticsWrapperObjectPtr, pingAllPtr, EmptyParams);
}
public void Ping(string address)
{
if (androidJavaObject == null)
{
return;
}
PingParams[0] = address;
AndroidUtils.CallNativeVoidMethod(bhapticsWrapperObjectPtr, pingPtr, PingParams);
}
}
internal static class AndroidUtils
{
internal static void CallNativeVoidMethod(IntPtr androidObjPtr, IntPtr methodPtr, object[] param)
{
jvalue[] args = AndroidJNIHelper.CreateJNIArgArray(param);
try
{
CallNativeVoidMethod(androidObjPtr, methodPtr, args);
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] CallNativeVoidMethod() : {0}", e.Message);
}
finally
{
AndroidJNIHelper.DeleteJNIArgArray(param, args);
}
}
internal static void CallNativeVoidMethod(IntPtr androidObjPtr, IntPtr methodPtr, jvalue[] param)
{
try
{
AndroidJNI.CallVoidMethod(androidObjPtr, methodPtr, param);
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] CallNativeVoidMethod() : {0}", e.Message);
}
}
internal static string CallNativeStringMethod(IntPtr androidObjPtr, IntPtr methodPtr, jvalue[] param)
{
try
{
return AndroidJNI.CallStringMethod(androidObjPtr, methodPtr, param);
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] CallNativeStringMethod() : {0}", e.Message);
}
return "";
}
internal static bool CallNativeBoolMethod(IntPtr androidObjPtr, IntPtr methodPtr, object[] param)
{
jvalue[] args = AndroidJNIHelper.CreateJNIArgArray(param);
bool res = false;
try
{
res = CallNativeBoolMethod(androidObjPtr, methodPtr, args);
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] CallNativeBoolMethod() : {0}", e.Message);
}
finally
{
AndroidJNIHelper.DeleteJNIArgArray(param, args);
}
return res;
}
internal static bool CallNativeBoolMethod(IntPtr androidObjPtr, IntPtr methodPtr, jvalue[] param)
{
bool res = false;
try
{
res = AndroidJNI.CallBooleanMethod(androidObjPtr, methodPtr, param);
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] CallNativeBoolMethod() : {0}", e.Message);
}
return res;
}
internal static int CallNativeIntMethod(IntPtr androidObjPtr, IntPtr methodPtr, object[] param)
{
jvalue[] args = AndroidJNIHelper.CreateJNIArgArray(param);
int res = -1;
try
{
res = CallNativeIntMethod(androidObjPtr, methodPtr, args);
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] CallNativeIntMethod() : {0}", e.Message);
}
finally
{
AndroidJNIHelper.DeleteJNIArgArray(param, args);
}
return res;
}
internal static int CallNativeIntMethod(IntPtr androidObjPtr, IntPtr methodPtr, jvalue[] param)
{
int res = -1;
try
{
res = AndroidJNI.CallIntMethod(androidObjPtr, methodPtr, param);
}
catch (Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] CallNativeIntMethod() : {0}", e.Message);
}
return res;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88a498f451437f4438d6e3b26b6baf85
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,717 @@
using System.Collections.Generic;
using UnityEngine;
namespace Bhaptics.SDK2
{
public class BhapticsLibrary
{
private static readonly object Lock = new object();
private static readonly List<HapticDevice> EmptyDevices = new List<HapticDevice>();
private static AndroidHaptic android = null;
private static bool _initialized = false;
private static bool isAvailable = false;
private static bool isAvailableChecked = false;
private static bool enableUniversal = false;
private static Universal.BhapticsTcpClient _client = new Universal.BhapticsTcpClient();
public static bool IsBhapticsAvailable(bool isAutoRunPlayer)
{
if (isAvailableChecked)
{
return isAvailable;
}
return IsBhapticsAvailableForce(isAutoRunPlayer);
}
public static bool IsBhapticsAvailableForce(bool isAutoRunPlayer)
{
if (Application.platform == RuntimePlatform.Android)
{
if (android == null)
{
BhapticsLogManager.LogErrorFormat("IsBhapticsAvailable() android object not initialized.");
isAvailable = false;
return isAvailable;
}
android.RefreshPairing();
isAvailable = android.CheckBhapticsAvailable();
isAvailableChecked = true;
return isAvailable;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
if (!bhaptics_library.isPlayerInstalled())
{
isAvailable = false;
isAvailableChecked = true;
return isAvailable;
}
if (!bhaptics_library.isPlayerRunning() && isAutoRunPlayer)
{
BhapticsLogManager.LogFormat("bHaptics Player(PC) is not running, so try launch it.");
bhaptics_library.launchPlayer(true);
}
#endif
isAvailable = true;
isAvailableChecked = true;
return isAvailable;
}
public static bool Initialize(string appId, string apiKey, string json, bool autoRequestBluetoothPermission = true)
{
lock (Lock)
{
if (_initialized)
{
return false;
}
_initialized = true;
}
if (enableUniversal)
{
_client.Initialize(appId, apiKey, json);
}
if (Application.platform == RuntimePlatform.Android)
{
if (android == null)
{
BhapticsLogManager.Log("BhapticsLibrary - Initialize ");
android = new AndroidHaptic();
android.InitializeWithPermission(appId, apiKey, json, autoRequestBluetoothPermission);
_initialized = true;
return true;
}
return false;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
if (bhaptics_library.wsIsConnected())
{
BhapticsLogManager.Log("BhapticsLibrary - connection already opened");
//return false; // NOTE-230117 Temporary comment out for IL2CPP
}
BhapticsLogManager.LogFormat("BhapticsLibrary - Initialize() {0} {1}", apiKey, appId);
return bhaptics_library.registryAndInit(apiKey, appId, json);
#endif
return false;
}
public static void Destroy()
{
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
android.Dispose();
android = null;
}
return;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
BhapticsLogManager.LogFormat("Destroy()");
bhaptics_library.wsClose();
#endif
if (enableUniversal)
{
_client.Destroy();
}
_initialized = false;
}
public static bool IsConnect(PositionType type)
{
if (!isAvailable)
{
return false;
}
return GetConnectedDevices(type).Count > 0;
}
public static int Play(string eventId)
{
if (!isAvailable)
{
return -1;
}
if (eventId == null || eventId.Equals(string.Empty))
{
return -1;
}
if (enableUniversal)
{
_client.Play(eventId);
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.Play(eventId);
}
return -1;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.play(eventId);
#endif
return -1;
}
public static int PlayParam(string eventId, float intensity, float duration, float angleX, float offsetY)
{
if (!isAvailable)
{
return -1;
}
if (eventId == null || eventId.Equals(string.Empty))
{
return -1;
}
if (enableUniversal)
{
_client.Play(eventId, intensity, duration, angleX, offsetY);
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.PlayParam(eventId, intensity, duration, angleX, offsetY);
}
return -1;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.playPosParam(eventId, 0, intensity, duration, angleX, offsetY);
#endif
return -1;
}
public static int PlayMotors(int position, int[] motors, int durationMillis)
{
if (!isAvailable)
{
return -1;
}
if (enableUniversal)
{
// TODO
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.PlayMotors(position, motors, durationMillis);
}
return -1;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.playDot(position, durationMillis, motors, motors.Length);
#endif
return -1;
}
public static int PlayWaveform(PositionType positionType, int[] motorValues, GlovePlayTime[] playTimeValues, GloveShapeValue[] shapeValues)
{
if (!isAvailable)
{
return -1;
}
if (motorValues.Length != 6 || playTimeValues.Length != 6 || shapeValues.Length != 6)
{
BhapticsLogManager.LogError("[bHaptics] BhapticsLibrary - PlayWaveform() 'motorValues, playTimeValues, shapeValues' necessarily require 6 values each.");
return -1;
}
var playTimes = new int[playTimeValues.Length];
var shapeVals = new int[shapeValues.Length];
for (int i = 0; i < playTimes.Length; i++)
{
playTimes[i] = (int)playTimeValues[i];
}
for (int i = 0; i < shapeVals.Length; i++)
{
shapeVals[i] = (int)shapeValues[i];
}
if (enableUniversal)
{
// TODO
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.PlayGlove((int)positionType, motorValues, playTimes, shapeVals);
}
return -1;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.playWaveform((int)positionType, motorValues, playTimes, shapeVals, 6);
#endif
return -1;
}
public static int PlayPath(int position, float[] xValues, float[] yValues, int[] intensityValues, int duration)
{
if (!isAvailable)
{
return -1;
}
if (enableUniversal)
{
// TODO
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.PlayPath(position, xValues, yValues, intensityValues, duration);
}
return -1;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.playPath(position, xValues, yValues, intensityValues, duration);
#endif
return -1;
}
public static int PlayLoop(string eventId, float intensity, float duration, float angleX, float offsetY, int interval, int maxCount)
{
if (!isAvailable)
{
return -1;
}
if (eventId == null || eventId.Equals(string.Empty))
{
return -1;
}
if (enableUniversal)
{
// TODO
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.PlayLoop(eventId, intensity, duration, angleX, offsetY, interval, maxCount);
}
return -1;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.playLoop(eventId, intensity, duration, angleX, offsetY, interval, maxCount);
#endif
return -1;
}
public static bool StopByEventId(string eventId)
{
if (!isAvailable)
{
return false;
}
if (enableUniversal)
{
_client.StopByEventId(eventId);
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.StopByEventId(eventId);
}
return false;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.stopByEventId(eventId);
#endif
return false;
}
public static bool StopInt(int requestId)
{
if (!isAvailable)
{
return false;
}
if (enableUniversal)
{
_client.StopByRequestId(requestId);
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.StopByRequestId(requestId);
}
return false;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.stop(requestId);
#endif
return false;
}
public static bool StopAll()
{
if (!isAvailable)
{
return false;
}
if (enableUniversal)
{
_client.StopAll();
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.Stop();
}
return false;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.stopAll();
#endif
return false;
}
public static bool IsPlaying()
{
if (!isAvailable)
{
return false;
}
if (enableUniversal)
{
// TODO ;
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.IsPlaying();
}
return false;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.isPlaying();
#endif
return false;
}
public static bool IsPlayingByEventId(string eventId)
{
if (!isAvailable)
{
return false;
}
if (enableUniversal)
{
// TODO ;
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.IsPlayingByEventId(eventId);
}
return false;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.isPlayingByEventId(eventId);
#endif
return false;
}
public static bool IsPlayingByRequestId(int requestId)
{
if (!isAvailable)
{
return false;
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.IsPlayingByRequestId(requestId);
}
return false;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.isPlayingByRequestId(requestId);
#endif
return false;
}
public static List<HapticDevice> GetDevices()
{
if (!isAvailable)
{
return EmptyDevices;
}
if (enableUniversal)
{
// TODO ;
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
return android.GetDevices();
}
return EmptyDevices;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
return bhaptics_library.GetDevices();
#endif
return EmptyDevices;
}
public static List<HapticDevice> GetConnectedDevices(PositionType pos)
{
if (!isAvailable)
{
return EmptyDevices;
}
var pairedDeviceList = new List<HapticDevice>();
var devices = GetDevices();
foreach (var device in devices)
{
if (device.IsPaired && device.Position == pos && device.IsConnected)
{
pairedDeviceList.Add(device);
}
}
return pairedDeviceList;
}
public static List<HapticDevice> GetPairedDevices(PositionType pos)
{
if (!isAvailable)
{
return EmptyDevices;
}
var res = new List<HapticDevice>();
var devices = GetDevices();
foreach (var device in devices)
{
if (device.IsPaired && device.Position == pos)
{
res.Add(device);
}
}
return res;
}
public static void Ping(PositionType pos)
{
if (!isAvailable)
{
return;
}
var currentDevices = GetConnectedDevices(pos);
foreach (var device in currentDevices)
{
Ping(device);
}
}
public static void Ping(HapticDevice targetDevice)
{
if (!isAvailable)
{
return;
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
android.Ping(targetDevice.Address);
}
return;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
bhaptics_library.ping(targetDevice.Address);
#endif
}
public static void PingAll()
{
if (!isAvailable)
{
return;
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
android.PingAll();
}
return;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
bhaptics_library.pingAll();
#endif
}
public static void TogglePosition(HapticDevice targetDevice)
{
if (!isAvailable)
{
return;
}
if (Application.platform == RuntimePlatform.Android)
{
if (android != null)
{
android.TogglePosition(targetDevice.Address);
}
return;
}
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
bhaptics_library.swapPosition(targetDevice.Address);
#endif
}
public static void OnApplicationFocus()
{
IsBhapticsAvailableForce(false);
}
public static void OnApplicationPause()
{
StopAll();
}
public static void OnApplicationQuit()
{
Destroy();
}
#if UNITY_EDITOR
public static List<MappingMetaData> EditorGetEventList(string appId, string apiKey, int lastVersion, out int status)
{
if (Application.platform == RuntimePlatform.Android)
{
status = 0;
return new List<MappingMetaData>();
}
var res = bhaptics_library.EditorGetEventList(appId, apiKey, lastVersion, out int code);
status = code;
return res;
}
public static string EditorGetSettings(string appId, string apiKey, int lastVersion, out int status)
{
if (Application.platform == RuntimePlatform.Android)
{
status = 0;
return "";
}
var bytes = bhaptics_library.EditorGetSettings(appId, apiKey, lastVersion, out int code);
BhapticsLogManager.LogFormat("EditorGetSettings {0} {1}", code, bytes);
status = code;
return bytes;
}
public static bool EditorReInitialize(string appId, string apiKey, string json)
{
lock (Lock)
{
_initialized = true;
}
if (Application.platform == RuntimePlatform.Android)
{
return false;
}
BhapticsLogManager.LogFormat("[bHaptics] BhapticsLibrary - ReInitialize() {0} {1}", apiKey, appId);
return bhaptics_library.reInitMessage(apiKey, appId, json);
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1fbce4ef5d424c3429eced6b184b893d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,29 @@

namespace Bhaptics.SDK2
{
public static class BhapticsLogManager
{
public static void Log(string format)
{
// UnityEngine.Debug.Log("[bHaptics]" + format);
return;
}
public static void LogFormat(string format, params object[] args)
{
// UnityEngine.Debug.LogFormat("[bHaptics]" + format, args);
return;
}
public static void LogErrorFormat(string format, params object[] args)
{
// UnityEngine.Debug.LogErrorFormat("[bHaptics]" + format, args);
return;
}
public static void LogError(string format)
{
// UnityEngine.Debug.LogErrorFormat("[bHaptics]" + format);
return;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b342bc04df079344adaceb30a43b27d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c6fbc97baa602c84dbb23c82903f5bfc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
namespace Bhaptics.SDK2
{
public class bhaptics_library
{
private const string ModuleName = "bhaptics_library";
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool registryAndInit(string sdkAPIKey, string workspaceId, string initData);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool registryAndInitHost(string sdkAPIKey, string workspaceId, string initData, string url);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool wsIsConnected();
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static void wsClose();
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool reInitMessage(string sdkAPIKey, string workspaceId, string initData);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static int play(string key);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static int playPosParam(string key, int position, float intensity, float duration, float angleX, float offsetY);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool stop(int key);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool stopByEventId(string eventId);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool stopAll();
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool isPlaying();
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool isPlayingByRequestId(int key);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool isPlayingByEventId(string eventId);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool isbHapticsConnected(int position);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool ping(string address);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool pingAll();
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool swapPosition(string address);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static IntPtr getDeviceInfoJson();
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool isPlayerInstalled();
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool isPlayerRunning();
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static bool launchPlayer(bool b);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static IntPtr bHapticsGetHapticMessage(string apiKey, string appId, int lastVersion,
out int status);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static IntPtr bHapticsGetHapticMappings(string apiKey, string appId, int lastVersion,
out int status);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static int playDot(int position, int durationMillis, int[] motors, int size);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static int playWaveform(int position, int[] motorValues, int[] playTimeValues, int[] shapeValues, int motorLen);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
public static extern int playPath(int position, float[] xValues, float[] yValues, int[] intensityValues, int Len);
[DllImport(ModuleName, CallingConvention = CallingConvention.Cdecl)]
extern public static int playLoop(string key, float intensity, float duration, float angleX, float offsetY, int interval, int maxCount);
// https://stackoverflow.com/questions/36239705/serialize-and-deserialize-json-and-json-array-in-unity
public static List<HapticDevice> GetDevices()
{
IntPtr ptr = getDeviceInfoJson();
var devicesStr = PtrToStringUtf8(ptr);
if (devicesStr.Length == 0)
{
BhapticsLogManager.LogFormat("GetDevices() empty. {0}", devicesStr);
return new List<HapticDevice>();
}
var hapticDevices = JsonUtility.FromJson<DeviceListMessage>("{\"devices\":" + devicesStr + "}");
return BhapticsHelpers.Convert(hapticDevices.devices);
}
public static List<MappingMetaData> EditorGetEventList(string appId, string apiKey, int lastVersion, out int status)
{
var bytes = bHapticsGetHapticMappings(apiKey, appId, lastVersion, out int code);
status = code;
if (code == 0)
{
string str = PtrToStringUtf8(bytes);
var mappingMessage = MappingMessage.CreateFromJSON(str);
return mappingMessage.message;
}
BhapticsLogManager.LogFormat("EditorGetEventList {0}", status);
return new List<MappingMetaData>();
}
public static string EditorGetSettings(string appId, string apiKey, int lastVersion, out int status2)
{
var bytes = bHapticsGetHapticMessage(apiKey, appId, lastVersion, out int status);
status2 = status;
if (status == 0)
{
string str = PtrToStringUtf8(bytes);
return str;
}
return "";
}
private static string PtrToStringUtf8(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
{
return "";
}
int len = 0;
while (Marshal.ReadByte(ptr, len) != 0)
len++;
if (len == 0)
{
return "";
}
byte[] array = new byte[len];
Marshal.Copy(ptr, array, 0, len);
return System.Text.Encoding.UTF8.GetString(array);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 029629b776f364f4ba0813bff3c21752
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,76 @@
fileFormatVersion: 2
guid: 82a3cd61707f8444e9c7f2590ebdaf88
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: x86_64
DefaultValueInitialized: true
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: None
- first:
Facebook: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Linux64
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: LinuxUniversal
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: x86_64
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a302fec275492bc43b93371aa7c6dd31
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,306 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Bhaptics.SDK2
{
public class PlayParamSample : MonoBehaviour
{
private readonly string SampleAppID = "p4fZNOU6ShHYBxos8Zmr";
private readonly string SampleApiKey = "BJ4Sacbew0qVfqONoMSM";
[Header("Canvas")]
[SerializeField] private Canvas initCanvas;
[SerializeField] private Canvas mainCanvas;
[Header("Dropdown")]
[SerializeField] private Dropdown eventsDropdown;
[Header("Slider")]
[SerializeField] private Slider sliderIntensity;
[SerializeField] private Slider sliderDuration;
[SerializeField] private Slider sliderAngleX;
[SerializeField] private Slider sliderOffsetY;
[Header("Text")]
[SerializeField] private Text intensityValueText;
[SerializeField] private Text durationValueText;
[SerializeField] private Text angleXValueText;
[SerializeField] private Text offsetYValueText;
[SerializeField] private Text playButtonText;
[Header("Button")]
[SerializeField] private Button intensityResetButton;
[SerializeField] private Button durationResetButton;
[SerializeField] private Button angleXResetButton;
[SerializeField] private Button offsetYResetButton;
[Header("Sample")]
[SerializeField] private BhapticsSettings sampleSettings;
private int requestId = -1;
private string eventName = "";
private MappingMetaData[] events;
private Coroutine onClickPlayCoroutine;
private BhapticsSettings currentSettings;
#region Properties
private float intensity = 1f;
private float duration = 1f;
private float angleX = 0f;
private float offsetY = 0.5f;
private int selectedIndex = -1;
public float Intensity
{
get
{
return intensity;
}
set
{
intensity = value;
if (intensityValueText != null)
{
intensityValueText.text = intensity.ToString("0.0");
}
if (intensityResetButton != null)
{
intensityResetButton.gameObject.SetActive(!intensity.Equals(1f));
}
}
}
public float Duration
{
get
{
return duration;
}
set
{
duration = value;
if (durationValueText != null)
{
durationValueText.text = duration.ToString("0.0");
}
if (durationResetButton != null)
{
durationResetButton.gameObject.SetActive(!duration.Equals(1f));
}
}
}
public float AngleX
{
get
{
return angleX;
}
set
{
angleX = value;
if (angleXValueText != null)
{
angleXValueText.text = angleX.ToString("0");
}
if (angleXResetButton != null)
{
angleXResetButton.gameObject.SetActive(!angleX.Equals(0f));
}
}
}
public float OffsetY
{
get
{
return offsetY;
}
set
{
offsetY = value;
if (offsetYValueText != null)
{
offsetYValueText.text = offsetY.ToString("0.00");
}
if (offsetYResetButton != null)
{
offsetYResetButton.gameObject.SetActive(!offsetY.Equals(0f));
}
}
}
public int SelectedIndex
{
get
{
return selectedIndex;
}
set
{
selectedIndex = value;
}
}
#endregion
private void Start()
{
currentSettings = BhapticsSettings.Instance;
CheckApplicationSetting();
}
private void OnDisable()
{
StopAllCoroutines();
onClickPlayCoroutine = null;
}
public void OnClickPlay()
{
if (onClickPlayCoroutine == null)
{
onClickPlayCoroutine = StartCoroutine(OnClickPlayCor());
}
else
{
StopHaptic();
playButtonText.text = "Play";
}
}
public void SetOffsetY(string offsetYStr)
{
if (float.TryParse(offsetYStr, out float res))
{
OffsetY = res;
}
}
public void OpenDeveloperPortal()
{
Application.OpenURL("https://developer.bhaptics.com");
}
public void OpenGuideLink()
{
Application.OpenURL("https://bhaptics.notion.site/How-to-Start-bHaptics-Unity-SDK2-Beta-33cc33dcfa44426899a3f21c62adf66d");
}
public void UseSampleSettings()
{
currentSettings = sampleSettings;
BhapticsLibrary.Initialize(currentSettings.AppId, currentSettings.ApiKey, currentSettings.DefaultDeploy);
CheckApplicationSetting();
}
private void PlayHaptic(string eventName, float intensity, float duration, float angleX, float offsetY)
{
this.eventName = eventName;
requestId = BhapticsLibrary.PlayParam(eventName, intensity, duration, angleX, offsetY);
}
private void StopHaptic()
{
BhapticsLogManager.LogFormat("Stop {0}", requestId);
BhapticsLibrary.StopInt(requestId);
if (onClickPlayCoroutine != null)
{
StopCoroutine(onClickPlayCoroutine);
onClickPlayCoroutine = null;
}
}
private void ResetValues()
{
Duration = 1f;
Intensity = 1f;
AngleX = 0f;
OffsetY = 0f;
sliderDuration.value = Duration;
sliderIntensity.value = Intensity;
sliderAngleX.value = AngleX;
sliderOffsetY.value = OffsetY;
}
private IEnumerator OnClickPlayCor()
{
playButtonText.text = "Stop";
// You can also use the const value "BhapticsEvent.OOO" instead of "eventsDropdown.options[SelectedIndex].text".
PlayHaptic(eventsDropdown.options[SelectedIndex].text, Duration, Intensity, AngleX, OffsetY);
yield return null;
while (true)
{
if (!BhapticsLibrary.IsPlayingByRequestId(requestId))
{
break;
}
yield return null;
}
playButtonText.text = "Play";
onClickPlayCoroutine = null;
}
private void SetupApplicationData()
{
events = currentSettings.EventData;
BhapticsLogManager.LogFormat("[bHaptics] eventSize {0}", events.Length);
eventsDropdown.options = new List<Dropdown.OptionData>();
foreach (var ev in events)
{
var option = new Dropdown.OptionData(ev.key);
eventsDropdown.options.Add(option);
}
if (events.Length > 0)
{
SelectedIndex = 0;
eventsDropdown.value = SelectedIndex;
}
ResetValues();
}
private void CheckApplicationSetting()
{
if (currentSettings.LastDeployVersion <= 0)
{
initCanvas.gameObject.SetActive(true);
mainCanvas.gameObject.SetActive(false);
return;
}
SetupApplicationData();
initCanvas.gameObject.SetActive(false);
mainCanvas.gameObject.SetActive(true);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d17bcd23407e38428a737982cb82b3c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7d83e8320bb8f244b8a1ccb4840ddaf3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,300 @@
using System;
using UnityEngine;
using UnityEngine.UI;
namespace Bhaptics.SDK2
{
[Serializable]
public class PositonIconSetting
{
public Sprite connect;
public Sprite disconnect;
}
[Serializable]
public class IconSetting
{
[Header("Setting Icons")]
public PositonIconSetting Vest;
public PositonIconSetting Head;
public PositonIconSetting Arm;
public PositonIconSetting Foot;
public PositonIconSetting Hand;
public PositonIconSetting GloveL;
public PositonIconSetting GloveR;
}
public class BhapticsDeviceUI : MonoBehaviour
{
private static string SelectHexColor = "#5267F9FF";
private static string SelectHoverHexColor = "#697CFFFF";
private static string DisableHexColor = "#525466FF";
private static string DisableHoverHexColor = "#63646FFF";
[Header("UI")]
[SerializeField] private Image icon;
[SerializeField] private IconSetting widgetSetting;
[SerializeField] private Sprite TactsuitWiredIcon;
[SerializeField] private Image batteryLowImage;
[Header("Connect Menu")]
[SerializeField] private GameObject ConnectMenu;
[SerializeField] private Button pingButton;
[SerializeField] private Button lButton;
[SerializeField] private Button rButton;
[SerializeField] private GameObject wiredNotification;
[Header("Disconnect Menu")]
[SerializeField] private GameObject DisconnectMenu;
private HapticDevice device;
private void Awake()
{
if (pingButton != null)
{
pingButton.onClick.AddListener(Ping);
}
if (lButton != null)
{
lButton.onClick.AddListener(ToLeft);
}
if (rButton != null)
{
rButton.onClick.AddListener(ToRight);
}
}
public void RefreshDevice(HapticDevice d)
{
device = d;
if (device == null)
{
gameObject.SetActive(false);
return;
}
if (!gameObject.activeSelf)
{
gameObject.SetActive(true);
}
UpdateIcon(d);
if (d.IsConnected)
{
RenderConnectMenu();
}
else
{
RenderDisconnectMenu();
}
}
private void RenderConnectMenu()
{
ConnectMenu.gameObject.SetActive(true);
DisconnectMenu.gameObject.SetActive(false);
batteryLowImage.gameObject.SetActive(device.Battery < 20 && device.Battery >= 0);
UpdateButtons();
}
private void RenderDisconnectMenu()
{
ConnectMenu.gameObject.SetActive(false);
DisconnectMenu.gameObject.SetActive(true);
batteryLowImage.gameObject.SetActive(false);
}
private void UpdateButtons()
{
if (device.IsAudioJack)
{
wiredNotification.SetActive(true);
pingButton.gameObject.SetActive(false);
rButton.gameObject.SetActive(false);
lButton.gameObject.SetActive(false);
return;
}
wiredNotification.SetActive(false);
if (IsLeft(device.Position) || IsRight(device.Position))
{
pingButton.gameObject.SetActive(false);
lButton.gameObject.SetActive(true);
rButton.gameObject.SetActive(true);
var isLeft = IsLeft(device.Position);
ChangeButtonColor(lButton, isLeft);
ChangeButtonColor(rButton, !isLeft);
}
else
{
pingButton.gameObject.SetActive(true);
lButton.gameObject.SetActive(false);
rButton.gameObject.SetActive(false);
ChangeButtonColor(pingButton, true);
}
}
private void UpdateIcon(HapticDevice d)
{
switch (d.Position)
{
case PositionType.Vest:
if (d.IsAudioJack)
{
icon.sprite = TactsuitWiredIcon;
return;
}
icon.sprite = GetSprite(widgetSetting.Vest, d.IsConnected);
break;
case PositionType.FootL:
case PositionType.FootR:
icon.sprite = GetSprite(widgetSetting.Foot, d.IsConnected);
break;
case PositionType.HandL:
case PositionType.HandR:
icon.sprite = GetSprite(widgetSetting.Hand, d.IsConnected);
break;
case PositionType.ForearmL:
case PositionType.ForearmR:
icon.sprite = GetSprite(widgetSetting.Arm, d.IsConnected);
break;
case PositionType.GloveL:
icon.sprite = GetSprite(widgetSetting.GloveL, d.IsConnected);
break;
case PositionType.GloveR:
icon.sprite = GetSprite(widgetSetting.GloveR, d.IsConnected);
break;
case PositionType.Head:
icon.sprite = GetSprite(widgetSetting.Head, d.IsConnected);
break;
default:
icon.sprite = null;
break;
}
}
private Sprite GetSprite(PositonIconSetting icon, bool connected)
{
if (icon == null)
{
return null;
}
return connected ? icon.connect : icon.disconnect;
}
private void Ping()
{
if (device == null)
{
return;
}
BhapticsLibrary.Ping(device);
}
private void ToLeft()
{
if (device == null)
{
return;
}
if (IsRight(device.Position))
{
BhapticsLibrary.TogglePosition(device);
}
else
{
Ping();
}
}
private void ToRight()
{
if (device == null)
{
return;
}
if (IsLeft(device.Position))
{
BhapticsLibrary.TogglePosition(device);
}
else
{
Ping();
}
}
private Color ToColor(string hex)
{
Color res = Color.white;
if (ColorUtility.TryParseHtmlString(hex, out res))
{
return res;
}
return res;
}
private void ChangeButtonColor(Button targetButton, bool isSelect)
{
var defaultColor = ToColor(isSelect ? SelectHexColor : DisableHexColor);
var hoverColor = ToColor(isSelect ? SelectHoverHexColor : DisableHoverHexColor);
var buttonColors = targetButton.colors;
buttonColors.normalColor = defaultColor;
buttonColors.highlightedColor = hoverColor;
buttonColors.pressedColor = defaultColor;
targetButton.colors = buttonColors;
}
private static bool IsLeft(PositionType pos)
{
switch (pos)
{
case PositionType.ForearmL:
case PositionType.HandL:
case PositionType.FootL:
case PositionType.GloveL:
return true;
}
return false;
}
private static bool IsRight(PositionType pos)
{
switch (pos)
{
case PositionType.ForearmR:
case PositionType.HandR:
case PositionType.FootR:
case PositionType.GloveR:
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 90919fd69ea2be746bb71c8eb3228019
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,234 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Bhaptics.SDK2
{
public class BhapticsUI : MonoBehaviour
{
[SerializeField] private float intervalRefreshTime = 1f;
[SerializeField] private RectTransform mainPanel;
[Header("Devices UI")]
[SerializeField] private Transform devicesContainer;
[SerializeField] private Transform deviceListPageUi;
[SerializeField] private Button deviceListNextPageButton;
[SerializeField] private Button deviceListBackPageButton;
[SerializeField] private Text deviceListPageText;
[SerializeField] private BhapticsDeviceUI devicePrefab;
[Header("No Paired Device UI")]
[SerializeField] private GameObject noPairedDeviceUi;
[SerializeField] private Button helpButton;
[SerializeField] private Button bHapticsLinkButton;
[SerializeField] private GameObject helpUi;
[SerializeField] private GameObject helpDescriptionPC;
[SerializeField] private GameObject helpDescriptionQuest;
[SerializeField] private Button helpCloseButton;
private List<BhapticsDeviceUI> controllers = new List<BhapticsDeviceUI>();
private BoxCollider mainPanelCollider;
private Vector2 defaultMainPanelSize;
private Vector2 defaultDeviceContainerSize;
private int deviceListSize = 5;
private int deviceListPageIndex = 0;
private int expandHeight = 78;
private int expandDeviceCount = 4;
private int pageActivateDeviceCount = 6;
private int pageExpandHeight = 28;
private int maxPageIndex;
private void Start()
{
for (int i = 0; i < deviceListSize; i++)
{
var go = Instantiate(devicePrefab, devicesContainer.transform);
go.gameObject.SetActive(false);
controllers.Add(go);
}
InvokeRepeating("Refresh", 1f, intervalRefreshTime);
#region AddListener
if (helpButton != null)
{
helpButton.onClick.AddListener(OnHelp);
}
if (helpCloseButton != null)
{
helpCloseButton.onClick.AddListener(CloseHelpNotification);
}
if (bHapticsLinkButton != null)
{
bHapticsLinkButton.onClick.AddListener(OpenLink);
}
if (deviceListNextPageButton != null)
{
deviceListNextPageButton.onClick.AddListener(NextPage);
}
if (deviceListBackPageButton != null)
{
deviceListBackPageButton.onClick.AddListener(BackPage);
}
if (mainPanel != null)
{
defaultMainPanelSize = mainPanel.sizeDelta;
mainPanelCollider = mainPanel.GetComponent<BoxCollider>();
}
if (devicesContainer != null)
{
defaultDeviceContainerSize = defaultMainPanelSize;
}
#endregion
}
void OnDestroy()
{
CancelInvoke("Refresh");
}
private void Refresh()
{
var devices = BhapticsLibrary.GetDevices();
maxPageIndex = Mathf.FloorToInt(devices.Count / (float)deviceListSize);
if (devices.Count == 0)
{
noPairedDeviceUi.SetActive(true);
var deviceContainerRect = devicesContainer as RectTransform;
deviceContainerRect.sizeDelta = defaultDeviceContainerSize;
var sizeDelta = mainPanel.sizeDelta;
mainPanel.sizeDelta = defaultMainPanelSize;
mainPanelCollider.center = new Vector3(0f, - sizeDelta.y * 0.5f, 0f);
mainPanelCollider.size = new Vector3(sizeDelta.x, sizeDelta.y, 1f);
deviceListPageIndex = 0;
deviceListPageUi.gameObject.SetActive(false);
}
else
{
Vector2 currentExpandSize = Vector2.zero;
if (devices.Count >= pageActivateDeviceCount)
{
currentExpandSize = new Vector2(0f, expandHeight * (pageActivateDeviceCount - expandDeviceCount) + pageExpandHeight);
deviceListPageUi.gameObject.SetActive(true);
deviceListPageText.text = (deviceListPageIndex + 1) + " / " + (maxPageIndex + 1);
deviceListBackPageButton.interactable = deviceListPageIndex != 0;
deviceListNextPageButton.interactable = deviceListPageIndex != maxPageIndex;
if (deviceListPageIndex > maxPageIndex)
{
deviceListPageIndex = 0;
}
}
else
{
if (devices.Count >= expandDeviceCount)
{
currentExpandSize = new Vector2(0f, expandHeight * (1 + devices.Count - expandDeviceCount));
}
deviceListPageIndex = 0;
deviceListPageUi.gameObject.SetActive(false);
}
var deviceContainerRect = devicesContainer as RectTransform;
deviceContainerRect.sizeDelta = defaultDeviceContainerSize + currentExpandSize;
mainPanel.sizeDelta = defaultMainPanelSize + currentExpandSize;
mainPanelCollider.center = new Vector3(0f, -mainPanel.sizeDelta.y * 0.5f, 0f);
mainPanelCollider.size = new Vector3(mainPanel.sizeDelta.x, mainPanel.sizeDelta.y, 1f);
noPairedDeviceUi.SetActive(false);
SetActiveHelpGameObject(false);
}
for (int i = 0; i < deviceListSize; i++)
{
try
{
if (i <= devices.Count - 1)
{
controllers[i].RefreshDevice(devices[i + deviceListPageIndex * deviceListSize]);
}
else
{
controllers[i].gameObject.SetActive(false);
}
}
catch (System.Exception e)
{
controllers[i].gameObject.SetActive(false);
}
}
}
private void OnHelp()
{
SetActiveHelpGameObject(true);
}
private void CloseHelpNotification()
{
SetActiveHelpGameObject(false);
}
private void OpenLink()
{
Application.OpenURL("https://www.bhaptics.com/support/download");
}
private void NextPage()
{
deviceListPageIndex = Mathf.Clamp(deviceListPageIndex + 1, 0, maxPageIndex);
}
private void BackPage()
{
deviceListPageIndex = Mathf.Clamp(deviceListPageIndex - 1, 0, maxPageIndex);
}
private void SetActiveHelpGameObject(bool value)
{
helpUi.SetActive(value);
if (Application.platform == RuntimePlatform.Android)
{
helpDescriptionQuest.SetActive(value);
helpDescriptionPC.SetActive(false);
return;
}
helpDescriptionQuest.SetActive(false);
helpDescriptionPC.SetActive(value);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e73d40d7fdd56646a3cb99deaea917f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e6765208bb5c0b34c84554f2d5213ab9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,358 @@
using System.Collections;
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using System.Net;
using UnityEngine.Networking;
namespace Bhaptics.SDK2.Universal
{
[Serializable]
public class UdpMessage
{
public string userId = "";
public int port = 8000;
}
public class BhapticsTcpClient
{
public bool connected = false;
public bool connecting = false;
private volatile bool _isListening = false;
private volatile bool _isTcpLoop = false;
#region private members
private TcpClient socketConnection;
private Thread sdkThread;
private Thread udpThread;
private Thread pingThread;
private string _apiKey;
private string _appId;
private string _serverIp = "";
private int _serverPort;
private DateTime last = new DateTime();
#endregion
public void Initialize(string appId, string apiKey, string defaultConfig)
{
_appId = appId;
_apiKey = apiKey;
StartUdpListener();
}
public int Play(string eventName)
{
return Play(eventName, 1f, 1f, 0f, 0f);
}
public int Play(string eventName, float intensity, float duration, float angleX, float offsetY)
{
BhapticsLogManager.Log("[bhaptics-universal] Play: " + eventName + ", " + DateTime.UtcNow.Millisecond);
int requesetId = UnityEngine.Random.Range(1, int.MaxValue);
var playMessage = new PlayMessage
{
eventName = eventName,
requestId = requesetId,
intensity = intensity,
duration = duration,
offsetAngleX = angleX,
offsetY = offsetY,
};
var message = TactHubMessage.SdkPlay(playMessage);
SendMessageToTactHub(JsonUtility.ToJson(message));
return requesetId;
}
public void StopAll()
{
BhapticsLogManager.Log("[bhaptics-universal] StopAll: ");
var message = TactHubMessage.StopAll;
SendMessageToTactHub(JsonUtility.ToJson(message));
}
public void StopByEventId(string eventName)
{
BhapticsLogManager.Log("[bhaptics-universal] StopByEventId: " + eventName);
var message = TactHubMessage.SdkStopByEventId(eventName);
SendMessageToTactHub(JsonUtility.ToJson(message));
}
public void StopByRequestId(int requestId)
{
BhapticsLogManager.Log("[bhaptics-universal] StopByEventId: " + requestId);
var message = TactHubMessage.SdkStopByRequestId(requestId);
SendMessageToTactHub(JsonUtility.ToJson(message));
}
public void Ping()
{
var message = TactHubMessage.SdkPingServer();
SendMessageToTactHub(JsonUtility.ToJson(message));
}
public void Pause()
{
StopUdpListener();
}
public void Destroy()
{
_isTcpLoop = false;
_isListening = false;
}
private void StartUdpListener()
{
if (_isListening)
{
BhapticsLogManager.LogFormat("[bhaptics-universal] StartUdpListener listening...");
return;
}
try
{
_isListening = true;
BhapticsLogManager.Log("[bhaptics-universal] StartUdpListener ");
udpThread = new Thread(UdpBroadcastingListenLoop);
udpThread.IsBackground = true;
udpThread.Start();
}
catch (Exception e)
{
BhapticsLogManager.LogFormat("[bhaptics-universal] ReceiveUDP exception {0}", e.Message);
_isListening = false;
}
}
private void StopUdpListener()
{
_isListening = false;
}
private void ConnectToTactHub(string ip, int port)
{
if (connected) {
BhapticsLogManager.LogFormat("[bhaptics-universal] ConnectToTactHub: connected {0}:{1}", ip, port);
return;
}
if (connecting) {
BhapticsLogManager.LogFormat("[bhaptics-universal] ConnectToTactHub: connecting {0}:{1}", ip, port);
return;
}
try
{
connecting = true;
_serverIp = ip;
_serverPort = port;
BhapticsLogManager.LogFormat("[bhaptics-universal] ConnectToTactHub {0}:{1}", ip, port);
_isTcpLoop = true;
sdkThread = new Thread(TactHubLoop)
{
IsBackground = true
};
sdkThread.Start();
pingThread = new Thread(PingLoop)
{
IsBackground = true
};
pingThread.Start();
connected = true;
connecting = false;
}
catch (Exception e)
{
BhapticsLogManager.Log("ConnectToTactHub exception " + e.Message);
}
}
private void PingLoop()
{
while (connected) {
var current = DateTime.Now;
long diffTicks = current.Ticks - last.Ticks;
var diffSpan = new TimeSpan(diffTicks);
// 1s = 10,000,000 ticks
if (diffSpan.TotalSeconds > 1)
{
Ping();
last = current;
}
}
}
private void UdpBroadcastingListenLoop()
{
BhapticsLogManager.Log("[bhaptics-universal] UdpBroadcastingListenLoop");
var udpEndPoint = new IPEndPoint(IPAddress.Any, 15884);
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(udpEndPoint);
while (_isListening)
{
if (connected)
{
BhapticsLogManager.Log("[bhaptics-universal] UdpBroadcastingListenLoop: connected");
StopUdpListener();
break;
}
var sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint remote = sender;
var data = new byte[1024];
int receivedSize = socket.ReceiveFrom(data, ref remote);
var endPoint = remote as IPEndPoint;
var str = (Encoding.UTF8.GetString(data, 0, receivedSize));
UdpMessage message = JsonUtility.FromJson<UdpMessage>(str);
if (connected || connecting) {
continue;
}
BhapticsLogManager.LogFormat("[bhaptics-universal] UdpBroadcastingListenLoop Message received from {0}:", remote.ToString());
ConnectToTactHub(endPoint.Address.ToString(), message.port);
BhapticsLogManager.LogFormat("[bhaptics-universal] UdpBroadcastingListenLoop ip: {0}:{1}, userId: {2}", endPoint.Address.ToString(), endPoint.Port , message.userId);
}
udpThread = null;
}
private void TactHubLoop()
{
try
{
socketConnection = new TcpClient(_serverIp, _serverPort);
var bytes = new byte[1024];
while (_isTcpLoop)
{
using (var stream = socketConnection.GetStream()) {
InitializeSdk();
if (!stream.CanRead)
{
continue;
}
int length;
while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
{
var incomingData = new byte[length];
Array.Copy(bytes, 0, incomingData, 0, length);
string serverMessage = Encoding.UTF8.GetString(incomingData);
BhapticsLogManager.Log("[bhaptics-universal] TactHubLoop received as: " + DateTime.UtcNow.Second + ":" + DateTime.UtcNow.Millisecond + " " + serverMessage);
var message = JsonUtility.FromJson<TactHubMessage>(serverMessage);
switch (message.type)
{
case MessageType.ServerTokenMessage:
var token = JsonUtility.FromJson<TokenMessage>(message.message);
UnityMainThreadDispatcher.Instance().Enqueue(CheckAPI(token.token, token.tokenKey));
break;
default:
BhapticsLogManager.Log("[bhaptics-universal] Unknown message " + message.message);
break;
}
}
}
}
}
catch (SocketException socketException)
{
BhapticsLogManager.Log("Socket exception: " + socketException);
}
catch (Exception e) {
BhapticsLogManager.Log("Exception: " + e);
}
_isTcpLoop = false;
connected = false;
connecting = false;
socketConnection = null;
StartUdpListener();
}
private void InitializeSdk()
{
var message = TactHubMessage.InitializeMessage(_appId, _apiKey);
SendMessageToTactHub(JsonUtility.ToJson(message));
}
private void SendMessageToTactHub(string message = "message")
{
if (socketConnection == null)
{
return;
}
try
{
//Debug.Log("SendMessageToTactHub: " + DateTime.UtcNow.Second + ":" + DateTime.UtcNow.Millisecond);
var stream = socketConnection.GetStream();
if (stream.CanWrite)
{
byte[] clientMessageAsByteArray = Encoding.UTF8.GetBytes($"{message}\n");
stream.Write(clientMessageAsByteArray, 0, clientMessageAsByteArray.Length);
}
}
catch (SocketException ex)
{ }
}
IEnumerator CheckAPI(string token, string tokenKey)
{
string url = "https://dev-sdk-apis.bhaptics.com/api/v1/tacthub-api/verify?token=" + token + "&token-key=" + tokenKey;
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
if (www.error == null)
{
BhapticsLogManager.Log(www.downloadHandler.text);
}
else
{
BhapticsLogManager.Log("[bhaptics-universal] CheckAPI() error " + www.error);
BhapticsLogManager.Log(www.downloadHandler.text);
// TactHub App is not valid
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d896ac2a7d138243a70bc481e2cc324
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Bhaptics.SDK2.Universal
{
public static class MessageType
{
public const string ServerTokenMessage = "ServerTokenMessage";
public const string SdkPlay = "SdkPlay";
public const string SdkPing = "SdkPing";
public const string SdkPingToServer = "SdkPingToServer";
public const string SdkPingAll = "SdkPingAll";
public const string SdkPlayDotMode = "SdkPlayDotMode";
public const string SdkStopByRequestId = "SdkStopByRequestId";
public const string SdkStopByEventId = "SdkStopByEventId";
public const string SdkStopAll = "SdkStopAll";
public const string SdkRequestAuthInit = "SdkRequestAuthInit";
public const string SdkPlayLoop = "SdkPlayLoop";
public const string SdkPlayGloveMode = "SdkPlayGloveMode";
}
[Serializable]
public class PlayMessage
{
public string eventName;
public int requestId;
public float intensity = 1f;
public float duration = 1f;
public float offsetAngleX = 0f;
public float offsetY = 0f;
}
[Serializable]
public class AuthenticationMessage
{
public string sdkApiKey;
public string applicationId;
}
[Serializable]
public class TokenMessage
{
public string token;
public string tokenKey;
}
[Serializable]
public class TactHubMessage
{
public string type;
public string message;
public static TactHubMessage SdkPlay(PlayMessage message)
{
return new TactHubMessage()
{
message = JsonUtility.ToJson(message),
type = MessageType.SdkPlay
};
}
public static TactHubMessage InitializeMessage(string appId, string apiKey)
{
return new TactHubMessage()
{
message = JsonUtility.ToJson(new AuthenticationMessage()
{
applicationId = appId,
sdkApiKey = apiKey,
}),
type = MessageType.SdkRequestAuthInit
};
}
public static TactHubMessage SdkStopByRequestId(int requestId)
{
return new TactHubMessage()
{
message = requestId.ToString(),
type = MessageType.SdkStopByRequestId,
};
}
public static TactHubMessage SdkStopByEventId(string eventId)
{
return new TactHubMessage()
{
message = eventId,
type = MessageType.SdkStopByEventId,
};
}
public static TactHubMessage SdkPingServer()
{
return new TactHubMessage()
{
message = "",
type = MessageType.SdkPingToServer,
};
}
public static TactHubMessage SdkPing(string address)
{
return new TactHubMessage()
{
message = address,
type = MessageType.SdkPing,
};
}
public static TactHubMessage SdkPingAll()
{
return new TactHubMessage()
{
type = MessageType.SdkPingAll,
};
}
public static readonly TactHubMessage StopAll = new TactHubMessage()
{
type = MessageType.SdkStopAll,
};
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7833598fe27a4a44086be4f5612dfe76
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,138 @@
/*
Copyright 2015 Pim de Witte All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading.Tasks;
namespace Bhaptics.SDK2.Universal
{
/// Author: Pim de Witte (pimdewitte.com) and contributors, https://github.com/PimDeWitte/UnityMainThreadDispatcher
/// <summary>
/// A thread-safe class which holds a queue with actions to execute on the next Update() method. It can be used to make calls to the main thread for
/// things such as UI Manipulation in Unity. It was developed for use in combination with the Firebase Unity plugin, which uses separate threads for event handling
/// </summary>
public class UnityMainThreadDispatcher : MonoBehaviour
{
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
public void Update()
{
lock (_executionQueue)
{
while (_executionQueue.Count > 0)
{
_executionQueue.Dequeue().Invoke();
}
}
}
/// <summary>
/// Locks the queue and adds the IEnumerator to the queue
/// </summary>
/// <param name="action">IEnumerator function that will be executed from the main thread.</param>
public void Enqueue(IEnumerator action)
{
lock (_executionQueue)
{
_executionQueue.Enqueue(() =>
{
StartCoroutine(action);
});
}
}
/// <summary>
/// Locks the queue and adds the Action to the queue
/// </summary>
/// <param name="action">function that will be executed from the main thread.</param>
public void Enqueue(Action action)
{
Enqueue(ActionWrapper(action));
}
/// <summary>
/// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes
/// </summary>
/// <param name="action">function that will be executed from the main thread.</param>
/// <returns>A Task that can be awaited until the action completes</returns>
public Task EnqueueAsync(Action action)
{
var tcs = new TaskCompletionSource<bool>();
void WrappedAction()
{
try
{
action();
tcs.TrySetResult(true);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
Enqueue(ActionWrapper(WrappedAction));
return tcs.Task;
}
IEnumerator ActionWrapper(Action a)
{
a();
yield return null;
}
private static UnityMainThreadDispatcher _instance = null;
public static bool Exists()
{
return _instance != null;
}
public static UnityMainThreadDispatcher Instance()
{
if (!Exists())
{
throw new Exception("UnityMainThreadDispatcher could not find the UnityMainThreadDispatcher object. Please ensure you have added the MainThreadExecutor Prefab to your scene.");
}
return _instance;
}
void Awake()
{
if (_instance == null)
{
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
void OnDestroy()
{
_instance = null;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d3309bbcfda3ce549b09ebc27e2d6f02
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9bdee2cb3d256c349b2fe66406f032a1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,17 @@
{
"name": "Bhaptics.SDK2.Editor",
"references": [
"GUID:464dd5f1479658c42bf2844d11534e1e"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1c8442bf8580b9246a99702a6e88c576
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,134 @@
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.CSharp;
using UnityEditor;
using UnityEngine;
namespace Bhaptics.SDK2
{
// https://github.com/ValveSoftware/steamvr_unity_plugin/blob/master/Assets/SteamVR/Input/Editor/SteamVR_Input_Generator.cs
public class BhapticsEventGenerator
{
private const string secretKey = "bhaptics";
private static bool fileChanged = false;
public static void CreateEventCsFile(string fileName, string[] eventList)
{
CodeCompileUnit compileUnit = new CodeCompileUnit();
CodeNamespace codeNamespace = new CodeNamespace(typeof(BhapticsLibrary).Namespace);
codeNamespace.Imports.Add(new CodeNamespaceImport("System"));
codeNamespace.Imports.Add(new CodeNamespaceImport("UnityEngine"));
compileUnit.Namespaces.Add(codeNamespace);
CodeTypeDeclaration setClass = new CodeTypeDeclaration(fileName);
setClass.Attributes = MemberAttributes.Public;
codeNamespace.Types.Add(setClass);
foreach (var s in eventList)
{
CodeMemberField field = new CodeMemberField(typeof(string), s.Replace("-", "_").Replace(".", "_").ToUpper());
field.Attributes = MemberAttributes.Public | MemberAttributes.Const;
field.InitExpression = new CodePrimitiveExpression(s);
setClass.Members.Add(field);
}
string folderPath = Path.Combine(BhapticsSDK2.GetScriptPath(), "AutoGenerated");
BhapticsLogManager.LogFormat("{0}", folderPath);
string fullSourceFilePath = Path.Combine(folderPath, setClass.Name + ".cs");
if (Directory.Exists(folderPath) == false)
{
Directory.CreateDirectory(folderPath);
}
CreateFile(fullSourceFilePath, compileUnit);
}
private static void CreateFile(string fullPath, CodeCompileUnit compileUnit)
{
// Generate the code with the C# code provider.
CSharpCodeProvider provider = new CSharpCodeProvider();
// Build the output file name.
string fullSourceFilePath = fullPath;
BhapticsLogManager.Log("[bHaptics] Success App Setup\nWriting class to: " + fullSourceFilePath);
string path = BhapticsSDK2.GetScriptPath();
string priorMD5 = null;
FileInfo file = new FileInfo(fullSourceFilePath);
if (file.Exists)
{
file.IsReadOnly = false;
priorMD5 = GetBadMD5HashFromFile(fullSourceFilePath);
}
// Create a TextWriter to a StreamWriter to the output file.
using (StreamWriter sw = new StreamWriter(fullSourceFilePath, false))
{
IndentedTextWriter tw = new IndentedTextWriter(sw, " ");
// Generate source code using the code provider.
provider.GenerateCodeFromCompileUnit(compileUnit, tw,
new CodeGeneratorOptions() { BracingStyle = "C" });
// Close the output file.
tw.Close();
string newMD5 = GetBadMD5HashFromFile(fullSourceFilePath);
if (priorMD5 != newMD5)
fileChanged = true;
}
if (fileChanged)
{
AssetDatabase.Refresh();
BhapticsLogManager.Log("class created at: " + fullSourceFilePath);
}
}
public static string GetBadMD5HashFromFile(string filePath)
{
if (File.Exists(filePath) == false)
{
return null;
}
string data = File.ReadAllText(filePath);
return GetBadMD5Hash(data + secretKey);
}
public static string GetBadMD5Hash(string usedString)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(usedString + secretKey);
return GetBadMD5Hash(bytes);
}
public static string GetBadMD5Hash(byte[] bytes)
{
System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] hash = md5.ComputeHash(bytes);
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("x2"));
}
return sb.ToString();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76372ec39f31fdb45b8a231d91e5dee0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,957 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Bhaptics.SDK2.Editor
{
public class BhapticsSettingWindow : EditorWindow
{
private enum NavigationButtonType
{
Home = 0, Events = 1, Documentation = 2
}
public enum DocumentationButtonType
{
Unity, bHaptics, MetaQuest2
}
private const string DeveloperPortalGuideUrl = "https://bhaptics.notion.site/Create-haptic-events-using-bHaptics-Developer-Portal-b056c5a56e514afeb0ed436873dd87c6";
private const string UnityHowToStartUrl = "https://bhaptics.notion.site/Plug-in-deployed-events-to-Unity-33cc33dcfa44426899a3f21c62adf66d";
private const string UnityMigrateUrl = "https://bhaptics.notion.site/How-to-migrate-from-SDK1-old-to-SDK2-new-007c00b65129404287d9175b71fa029c";
private const string WindowTitle = "bHaptics Developer Window";
private const float WindowWidth = 1000f;
private const float WindowHeight = 650f;
private const float WindowPositionX = 300f;
private const float WindowPositionY = 150f;
private const float EventsButtonWidth = 335f;
private const float EventsButtonHeight = 86f;
private const float EventsButtonDetailWidth = 680f;
private const float EventsButtonDetailHeight = 72f;
private const float EventsButtonSpacing = 10f;
private const float DocumentationButtonWidth = 580f;
private const float DocumentationButtonHeight = 70f;
private const float DocumentationButtonSpacing = 16f;
private static NavigationButtonType SelectedNavigationButtionType = NavigationButtonType.Home;
private static Vector2 CurrentEventsScrollPos = Vector2.zero;
private static bool IsSortLastUpdated = false;
private static bool IsViewGrid = true;
private SerializedProperty appIdProperty;
private SerializedProperty apiKeyProperty;
private BhapticsSettings settings;
private SerializedObject so;
#region Images
private Texture2D whiteImage;
private Texture2D latestDeployedVersionBox;
private Texture2D homeIcon;
private Texture2D hapticEventsIcon;
private Texture2D documentationIcon;
private Texture2D homeSelectedIcon;
private Texture2D hapticEventsSelectedIcon;
private Texture2D documentationSelectedIcon;
private Texture2D sampleGuidesImage;
private Texture2D appIdApiKeyOutlineImage;
private Texture2D eventsButtonAudioIcon;
private Texture2D unityDocumentationImage;
private Texture2D bHapticsDocumentationImage;
private Texture2D metaQuest2DocumentationImage;
#endregion
#region GUIStyle
private GUIStyle fontBoldStyle;
private GUIStyle fontMediumStyle;
private GUIStyle fontRegularStyle;
private GUIStyle fontLightStyle;
private GUIStyle divideStyle;
private GUIStyle navigationButtonStyle;
private GUIStyle selectedNavigationButtonStyle;
private GUIStyle tempAreaStyle;
private GUIStyle setupInputFieldStyle;
private GUIStyle setupButtonStyle;
private GUIStyle visitTextButtonStyle;
private GUIStyle visitButtonStyle;
private GUIStyle resetButtonStyle;
private GUIStyle hapticEventsButtonStyle;
private GUIStyle setupErrorTextStyle;
private GUIStyle appTitleTextStyle;
private GUIStyle blackBackgroundStyle;
private GUIStyle latestDeployedVersionTextStyle;
private GUIStyle refreshDeployedVersionButtonStyle;
private GUIStyle mainTapTitleStyle;
private GUIStyle sampleGuideButtonStyle;
private GUIStyle copyClipboardButtonStyle;
private GUIStyle viewAllTextStyle;
private GUIStyle eventsButtonDetailStyle;
private GUIStyle eventsButtonDetailSelectedStyle;
private GUIStyle eventsButtonStyle;
private GUIStyle eventsButtonSelectedStyle;
private GUIStyle eventsButtonTitleStyle;
private GUIStyle eventsButtonDeviceStyle;
private GUIStyle eventsButtonDurationStyle;
private GUIStyle documentationButtonStyle;
private GUIStyle lastUpdatedToggleStyle;
private GUIStyle lastUpdatedToggleSelectedStyle;
private GUIStyle aZToggleStyle;
private GUIStyle aZToggleSelectedStyle;
private GUIStyle viewGridStyle;
private GUIStyle viewGridSelectedStyle;
private GUIStyle viewListStyle;
private GUIStyle viewListSelectedStyle;
#endregion
private GUISkin bHapticsSkin;
private Action OnChangePage;
private MappingMetaData selectedEvent = null;
private MappingMetaData[] sortedEventData;
private string setupErrorText = string.Empty;
private bool isValidState = false;
private double nextForceRepaint;
[MenuItem("bHaptics/Developer Window &b")]
public static void ShowWindow()
{
// we need internet access
if (!PlayerSettings.Android.forceInternetPermission)
{
PlayerSettings.Android.forceInternetPermission = true;
BhapticsLogManager.Log("[bHaptics] Internet Access is set to Require");
}
var windowSize = new Vector2(WindowWidth, WindowHeight);
var bHapticsWindow = EditorWindow.GetWindowWithRect(typeof(BhapticsSettingWindow), new Rect(new Vector2(WindowPositionX, WindowPositionY), windowSize), true, WindowTitle);
bHapticsWindow.position = new Rect(new Vector2(WindowPositionX, WindowPositionY), windowSize);
bHapticsWindow.minSize = windowSize;
bHapticsWindow.Show();
}
private void OnEnable()
{
settings = BhapticsSettings.Instance;
so = new SerializedObject(settings);
wantsMouseMove = true;
so.Update();
appIdProperty = so.FindProperty("appId");
apiKeyProperty = so.FindProperty("apiKey");
InitializeImages();
bHapticsSkin = Resources.Load<GUISkin>(BhapticsSettingWindowAssets.GUISkin);
InitializeGUIStyles(bHapticsSkin);
isValidState = settings.LastDeployVersion > 0;
OnChangePage += ResetOnChangePage;
BhapticsLibrary.Initialize(settings.AppId, settings.ApiKey, settings.DefaultDeploy);
BhapticsLibrary.IsBhapticsAvailable(true);
}
private void OnDestroy()
{
BhapticsLibrary.Destroy();
}
private void OnGUI()
{
GUI.DrawTexture(new Rect(0f, 0f, WindowWidth, WindowHeight), whiteImage);
if (!isValidState)
{
DrawSetupPage();
ResetMainPage();
}
else
{
DrawAppTitle();
DrawNavigationTap();
DrawMainPage();
}
so.ApplyModifiedProperties();
DelayRepaint();
}
#region Initialize Method
private void InitializeImages()
{
whiteImage = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.WhiteImage, typeof(Texture2D));
latestDeployedVersionBox = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.LatestDeployedVersionBox, typeof(Texture2D));
homeIcon = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.HomeIcon, typeof(Texture2D));
hapticEventsIcon = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.EventsIcon, typeof(Texture2D));
documentationIcon = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.DocumentationIcon, typeof(Texture2D));
homeSelectedIcon = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.HomeSelectedIcon, typeof(Texture2D));
hapticEventsSelectedIcon = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.EventsSelectedIcon, typeof(Texture2D));
documentationSelectedIcon = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.DocumentationSelectedIcon, typeof(Texture2D));
sampleGuidesImage = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.SampleGuidesImage, typeof(Texture2D));
appIdApiKeyOutlineImage = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.AppIdApiKeyOutlineImage, typeof(Texture2D));
eventsButtonAudioIcon = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.EventsButtonAudioIcon, typeof(Texture2D));
unityDocumentationImage = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.UnityDocumentationImage, typeof(Texture2D));
bHapticsDocumentationImage = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.BhapticsDocumentationImage, typeof(Texture2D));
metaQuest2DocumentationImage = (Texture2D)Resources.Load(BhapticsSettingWindowAssets.MetaQuest2DocumentationImage, typeof(Texture2D));
}
private void InitializeGUIStyles(GUISkin targetSkin)
{
tempAreaStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.TempAreaStyle));
fontBoldStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.FontBold));
fontMediumStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.FontMedium));
fontRegularStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.FontRegular));
fontLightStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.FontLight));
divideStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.DivideStyle));
navigationButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.NavigationButtonStyle));
selectedNavigationButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.SelectedNavigationButtonStyle));
setupInputFieldStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.SetupInputFieldStyle));
setupButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.SetupButtonStyle));
visitTextButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.VisitTextButtonStyle));
visitButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.VisitButtonStyle));
resetButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.ResetButtonStyle));
hapticEventsButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.HapticEventsButtonStyle));
setupErrorTextStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.SetupErrorTextStyle));
appTitleTextStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.AppTitleTextStyle));
blackBackgroundStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.BlackBackgroundStyle));
latestDeployedVersionTextStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.LatestDeployedVersionTextStyle));
refreshDeployedVersionButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.RefreshDeployedVersionButtonStyle));
mainTapTitleStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.MainTapTitleStyle));
sampleGuideButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.SampleGuideButtonStyle));
copyClipboardButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.CopyClipboardButtonStyle));
viewAllTextStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.ViewAllTextStyle));
eventsButtonDetailStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.EventsButtonDetailStyle));
eventsButtonDetailSelectedStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.EventsButtonDetailSelectedStyle));
eventsButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.EventsButtonStyle));
eventsButtonSelectedStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.EventsButtonSelectedStyle));
eventsButtonTitleStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.EventsButtonTitleStyle));
eventsButtonDeviceStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.EventsButtonDeviceStyle));
eventsButtonDurationStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.EventsButtonDurationStyle));
documentationButtonStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.DocumentationButtonStyle));
lastUpdatedToggleStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.LastUpdatedToggleStyle));
lastUpdatedToggleSelectedStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.LastUpdatedToggleSelectedStyle));
aZToggleStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.AZToggleStyle));
aZToggleSelectedStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.AZToggleSelectedStyle));
viewGridStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.ViewGridStyle));
viewGridSelectedStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.ViewGridSelectedStyle));
viewListStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.ViewListStyle));
viewListSelectedStyle = new GUIStyle(GetCustomGUIStyle(targetSkin, BhapticsSettingWindowAssets.ViewListSelectedStyle));
}
#endregion
private void DrawSetupPage()
{
GUILayout.BeginArea(new Rect(295f, 122f, 410f, 402f));
var originCursorFlashSpeed = GUI.skin.settings.cursorFlashSpeed;
GUI.skin.settings.cursorFlashSpeed = bHapticsSkin.settings.cursorFlashSpeed;
GUILayout.Label("Setup", new GUIStyle(fontBoldStyle) { fontSize = 24 }, GUILayout.Height(36f));
GUILayout.Space(40f);
GUILayout.Label("App ID", new GUIStyle(fontBoldStyle) { fontSize = 14 });
GUILayout.Space(14f);
appIdProperty.stringValue = EditorGUILayout.TextField(appIdProperty.stringValue, setupInputFieldStyle, GUILayout.Height(setupInputFieldStyle.fixedHeight));
if (appIdProperty.stringValue.Equals(string.Empty))
{
GUI.Label(new Rect(14f, 124f, 111f, 18f), "<color=#cccccc>Enter your App ID</color>", new GUIStyle(fontRegularStyle) { fontSize = 14 });
}
GUILayout.Space(14f);
GUILayout.Label("API Key", new GUIStyle(fontBoldStyle) { fontSize = 14 });
GUILayout.Space(14f);
apiKeyProperty.stringValue = EditorGUILayout.TextField(apiKeyProperty.stringValue, setupInputFieldStyle, GUILayout.Height(setupInputFieldStyle.fixedHeight));
if (apiKeyProperty.stringValue.Equals(string.Empty))
{
GUI.Label(new Rect(14f, 220f, 111f, 18f), "<color=#cccccc>Enter your API Key</color>", new GUIStyle(fontRegularStyle) { fontSize = 14 });
}
if (!setupErrorText.Equals(string.Empty))
{
GUILayout.Space(10f);
GUILayout.Label(setupErrorText, setupErrorTextStyle);
GUILayout.Space(22f);
}
else
{
GUILayout.Space(50f);
}
if (GUILayout.Button("LINK", setupButtonStyle))
{
GetAppSettings(out setupErrorText);
}
GUILayout.Space(20f);
GUILayout.BeginHorizontal();
GUILayout.Space(138f - 12f);
if (GUILayout.Button("Forgot your App ID & API Key?", visitTextButtonStyle, GUILayout.Width(138f + 12f), GUILayout.Height(18f)))
{
Application.OpenURL("https://developer.bhaptics.com");
}
GUILayout.EndHorizontal();
GUI.skin.settings.cursorFlashSpeed = originCursorFlashSpeed;
GUILayout.EndArea();
}
private void DrawAppTitle()
{
GUILayout.BeginArea(new Rect(0f, 0f, WindowWidth, 54f), blackBackgroundStyle);
GUILayout.Space(15f);
GUILayout.BeginHorizontal();
GUILayout.Space(20f);
GUILayout.Label(settings.AppName, appTitleTextStyle);
GUILayout.FlexibleSpace();
if (GUILayout.Button(" ", visitButtonStyle))
{
Application.OpenURL("https://developer.bhaptics.com");
}
GUILayout.Space(20f);
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
private void DrawNavigationTap()
{
GUILayout.BeginArea(new Rect(10f, 74f, 230f, 455f));
GUILayout.BeginVertical();
if (DrawNavigationButton("Home", NavigationButtonType.Home))
{
SelectNavigationButton(NavigationButtonType.Home);
}
if (DrawNavigationButton("Events", NavigationButtonType.Events))
{
sortedEventData = GetSortedEventData(0);
SelectNavigationButton(NavigationButtonType.Events);
}
if (DrawNavigationButton("Documentation", NavigationButtonType.Documentation))
{
SelectNavigationButton(NavigationButtonType.Documentation);
}
GUILayout.EndVertical();
GUILayout.EndArea();
GUILayout.BeginArea(new Rect(20f, 529f, 220f, 120f));
GUILayout.BeginVertical();
GUILayout.Label("", divideStyle);
GUILayout.Space(10f);
GUI.DrawTexture(new Rect(0f, 10f, 220f, 42f), latestDeployedVersionBox);
GUILayout.BeginHorizontal();
GUILayout.Space(159f);
GUILayout.Label(settings.LastDeployVersion.ToString(), latestDeployedVersionTextStyle);
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent("", "Refresh"), refreshDeployedVersionButtonStyle))
{
if (GetAppSettings(out setupErrorText) != 0)
{
ResetAppSettings();
}
else
{
BhapticsLibrary.EditorReInitialize(settings.AppId, settings.ApiKey, settings.DefaultDeploy);
}
}
GUILayout.Space(4f);
GUILayout.EndHorizontal();
GUILayout.Space(22f);
if (GUILayout.Button("", resetButtonStyle))
{
ResetAppSettings();
}
GUILayout.EndVertical();
GUILayout.EndArea();
}
private GUIStyle GetCustomGUIStyle(GUISkin targetSkin, string styleName)
{
for (int i = 0; i < targetSkin.customStyles.Length; ++i)
{
if (targetSkin.customStyles[i].name.Equals(styleName))
{
return targetSkin.customStyles[i];
}
}
return null;
}
private void DelayRepaint()
{
var timeSinceStartup = Time.realtimeSinceStartup;
if (Event.current.type == EventType.MouseMove && timeSinceStartup > nextForceRepaint)
{
nextForceRepaint = timeSinceStartup + .05f;
Repaint();
}
}
private void ResetMainPage()
{
SelectedNavigationButtionType = NavigationButtonType.Home;
CurrentEventsScrollPos = Vector2.zero;
IsViewGrid = true;
IsSortLastUpdated = false;
sortedEventData = null;
}
private bool DrawNavigationButton(string title, NavigationButtonType buttonType)
{
bool isSelected = SelectedNavigationButtionType == buttonType;
Texture2D targetIcon = homeIcon;
GUIStyle targetStyle = isSelected ? selectedNavigationButtonStyle : navigationButtonStyle;
switch (buttonType)
{
case NavigationButtonType.Home:
targetIcon = isSelected ? homeSelectedIcon : homeIcon;
break;
case NavigationButtonType.Events:
targetIcon = isSelected ? hapticEventsSelectedIcon : hapticEventsIcon;
break;
case NavigationButtonType.Documentation:
targetIcon = isSelected ? documentationSelectedIcon : documentationIcon;
break;
}
return GUILayout.Button(new GUIContent(" " + title, targetIcon), targetStyle);
}
private void SelectNavigationButton(NavigationButtonType targetType)
{
if (!SelectedNavigationButtionType.Equals(targetType))
{
OnChangePage?.Invoke();
}
SelectedNavigationButtionType = targetType;
}
private void DrawMainPage()
{
GUILayout.BeginArea(new Rect(280f, 90f, 720f, 560f));
switch (SelectedNavigationButtionType)
{
case NavigationButtonType.Home:
ShowHomeTap();
break;
case NavigationButtonType.Events:
ShowHapticEventsTap();
break;
case NavigationButtonType.Documentation:
ShowDocumentationTap();
break;
}
GUILayout.EndArea();
}
private void ShowHomeTap()
{
GUILayout.Space(4f);
GUILayout.Label("Welcome to bHaptics SDK2 !", mainTapTitleStyle);
GUILayout.BeginArea(new Rect(0f, 40f, 350f, 176f));
GUI.DrawTexture(new Rect(0f, 0f, 350f, 176f), sampleGuidesImage);
GUILayout.Label("How to start",
new GUIStyle(fontBoldStyle)
{
fontSize = 16,
padding = new RectOffset(20, 0, 20, 0)
});
GUILayout.Label("<color=#646464>It is recommended that you view the\nGuide documentation before you begin.</color>",
new GUIStyle(fontRegularStyle)
{
fontSize = 14,
wordWrap = true,
padding = new RectOffset(20, 0, 10, 0)
});
GUILayout.Space(24f);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Developer Portal Guide", sampleGuideButtonStyle, GUILayout.Width(160f)))
{
Application.OpenURL(DeveloperPortalGuideUrl);
}
GUILayout.Space(10f);
if (GUILayout.Button("Unity SDK2 Guide", sampleGuideButtonStyle, GUILayout.Width(140f)))
{
Application.OpenURL(UnityHowToStartUrl);
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.EndArea();
DrawAppIdApiKeyButton(new Rect(360f, 40f, 320f, 176f), "App ID", settings.AppId);
GUILayout.Space(10f);
DrawAppIdApiKeyButton(new Rect(360f, 133f, 320f, 176f), "API Key", settings.ApiKey);
GUILayout.BeginArea(new Rect(0f, 250f, 680f, 24f));
GUILayout.BeginHorizontal();
GUILayout.Label("Events", mainTapTitleStyle);
GUILayout.FlexibleSpace();
if (GUILayout.Button("View All", viewAllTextStyle))
{
SelectNavigationButton(NavigationButtonType.Events);
}
GUILayout.EndHorizontal();
GUILayout.EndArea();
GUILayout.BeginArea(new Rect(0f, 284f, 680f, 236f));
if (settings.EventData != null)
{
int startIndex = 0;
int endIndex = settings.EventData.Length > 3 ? startIndex + 3 : settings.EventData.Length;
for (int i = startIndex; i < endIndex; ++i)
{
DrawEventsButtonDetail(i, settings.EventData[i]);
if (i != endIndex - 1)
{
GUILayout.Space(10f);
}
}
}
GUILayout.EndArea();
}
private void ShowHapticEventsTap()
{
if (settings.EventData == null)
{
return;
}
GUILayout.BeginHorizontal();
GUILayout.BeginVertical();
GUILayout.Space(4f);
GUILayout.BeginHorizontal();
GUILayout.Label("Events", new GUIStyle(mainTapTitleStyle) { fontSize = 22, });
GUILayout.Space(6f);
GUILayout.Label("<color=#959595>" + settings.EventData.Length.ToString() + "</color>",
new GUIStyle(fontMediumStyle)
{
fontSize = 16,
margin = new RectOffset(0, 0, 3, 0)
});
GUILayout.FlexibleSpace();
GUILayout.Label("<color=#646464>Sortby:</color>",
new GUIStyle(fontRegularStyle)
{
fontSize = 13,
margin = new RectOffset(0, 0, 5, 0)
});
GUILayout.Space(10f);
if (GUILayout.Button("", IsSortLastUpdated ? aZToggleStyle : aZToggleSelectedStyle))
{
IsSortLastUpdated = false;
sortedEventData = GetSortedEventData(1);
}
GUI.Label(new Rect(452f, 9f, 79f, 16f), "A-Z", new GUIStyle(fontRegularStyle) { fontSize = 13 });
GUILayout.Space(10f);
if (GUILayout.Button("", IsSortLastUpdated ? lastUpdatedToggleSelectedStyle : lastUpdatedToggleStyle))
{
IsSortLastUpdated = true;
sortedEventData = GetSortedEventData(0);
}
GUI.Label(new Rect(509f, 9f, 79f, 16f), "Last Updated", new GUIStyle(fontRegularStyle) { fontSize = 13 });
GUILayout.EndHorizontal();
GUILayout.EndVertical();
GUILayout.Space(20f);
if (GUILayout.Button("", IsViewGrid ? viewGridSelectedStyle : viewGridStyle))
{
IsViewGrid = true;
}
GUILayout.Space(4f);
if (GUILayout.Button("", IsViewGrid ? viewListStyle : viewListSelectedStyle))
{
IsViewGrid = false;
}
GUILayout.Space(40f);
GUILayout.EndHorizontal();
GUILayout.Space(IsViewGrid ? 16f : 26f);
var originVerticalScrollbar = GUI.skin.verticalScrollbar;
var originVerticalScrollbarUpButton = GUI.skin.verticalScrollbarUpButton;
var originVerticalScrollbarDownButton = GUI.skin.verticalScrollbarDownButton;
var originVerticalScrollbarThumb = GUI.skin.verticalScrollbarThumb;
GUI.skin.verticalScrollbar = bHapticsSkin.verticalScrollbar;
GUI.skin.verticalScrollbarUpButton = bHapticsSkin.verticalScrollbarUpButton;
GUI.skin.verticalScrollbarDownButton = bHapticsSkin.verticalScrollbarDownButton;
GUI.skin.verticalScrollbarThumb = bHapticsSkin.verticalScrollbarThumb;
try
{
CurrentEventsScrollPos = GUILayout.BeginScrollView(CurrentEventsScrollPos, GUILayout.Width(695f));
var targetEventNames = sortedEventData;
for (int i = 0; i < targetEventNames.Length; ++i)
{
if (IsViewGrid)
{
DrawEventsButton(i, targetEventNames[i]);
}
else
{
DrawEventsButtonDetail(i, targetEventNames[i]);
}
if (i != targetEventNames.Length - 1)
{
GUILayout.Space(10f);
}
}
GUILayout.Space(10f);
GUILayout.EndScrollView();
}
catch
{
sortedEventData = GetSortedEventData(0);
GUILayout.EndScrollView();
}
GUI.skin.verticalScrollbar = originVerticalScrollbar;
GUI.skin.verticalScrollbarUpButton = originVerticalScrollbarUpButton;
GUI.skin.verticalScrollbarDownButton = originVerticalScrollbarDownButton;
GUI.skin.verticalScrollbarThumb = originVerticalScrollbarThumb;
}
private void ShowDocumentationTap()
{
GUILayout.Space(4f);
GUILayout.Label("Documentation", new GUIStyle(mainTapTitleStyle) { fontSize = 22 });
GUILayout.BeginArea(new Rect(0f, 60f, 720f, 570f));
DrawDocumentationButton(GetDocumentationButtonPos(0), DocumentationButtonType.bHaptics, "Create haptic events", "(bHaptics Developer Portal)", DeveloperPortalGuideUrl);
DrawDocumentationButton(GetDocumentationButtonPos(1), DocumentationButtonType.Unity, "Plug in deployed events to Unity", "(Unity)", UnityHowToStartUrl);
DrawDocumentationButton(GetDocumentationButtonPos(2), DocumentationButtonType.Unity, "How to migrate from SDK1(old) to SDK2(new)", "(Unity)", UnityMigrateUrl);
//DrawDocumentationButton(GetDocumentationButtonPos(3), DocumentationButtonType.MetaQuest2, "Getting Started", "(Unity Meta Quest2)");
GUILayout.EndArea();
}
private void ResetAppSettings()
{
BhapticsSettings.ResetInstance();
isValidState = false;
}
private int GetAppSettings(out string errorMessage)
{
var json = BhapticsLibrary.EditorGetSettings(appIdProperty.stringValue, apiKeyProperty.stringValue, -1, out int code);
if (code == 0)
{
var events = BhapticsLibrary.EditorGetEventList(appIdProperty.stringValue, apiKeyProperty.stringValue, -1, out code);
if (code == 0)
{
try
{
var rawMessage = DeployHttpMessage.CreateFromJSON(json);
var message = rawMessage.message;
if (message.version > 0)
{
settings.AppName = message.name;
settings.AppId = appIdProperty.stringValue;
settings.ApiKey = apiKeyProperty.stringValue;
settings.LastDeployVersion = message.version;
settings.DefaultDeploy = json;
var eventNames = new string[events.Count];
var eventDataArr = new MappingMetaData[events.Count];
for (var i = 0; i < events.Count; i++)
{
eventNames[i] = events[i].key;
eventDataArr[i] = events[i];
}
settings.EventData = eventDataArr;
errorMessage = string.Empty;
isValidState = settings.LastDeployVersion > 0;
BhapticsEventGenerator.CreateEventCsFile("BhapticsEvent", eventNames);
EditorUtility.SetDirty(settings);
AssetDatabase.SaveAssets();
}
else
{
BhapticsLogManager.LogErrorFormat("[bHaptics] Not Valid format.");
errorMessage = "Not Valid format";
}
}
catch (System.Exception e)
{
BhapticsLogManager.LogErrorFormat("[bHaptics] Exception: {0}", e.Message);
errorMessage = "Exception: " + e.Message;
}
return code;
}
}
BhapticsLogManager.LogErrorFormat("[bHaptics] Error: {0}", BhapticsHelpers.ErrorCodeToMessage(code));
errorMessage = BhapticsHelpers.ErrorCodeToMessage(code);
return code;
}
private void DrawAppIdApiKeyButton(Rect areaRect, string title, string value)
{
GUILayout.BeginArea(areaRect);
GUI.DrawTexture(new Rect(0f, 0f, 320f, 83f), appIdApiKeyOutlineImage);
GUILayout.BeginHorizontal();
GUILayout.Space(20f);
GUILayout.BeginVertical();
GUILayout.Space(22f);
GUILayout.Label(title, new GUIStyle(fontBoldStyle) { fontSize = 14 });
GUILayout.Space(6f);
var valueStyle = new GUIStyle(fontLightStyle);
valueStyle.normal.textColor = new Color(0f, 0f, 0f, 0.8f);
valueStyle.fontSize = 13;
GUILayout.Label(value, valueStyle);
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
if (GUILayout.Button("", copyClipboardButtonStyle))
{
GUIUtility.systemCopyBuffer = value;
BhapticsLogManager.LogFormat("[bHaptics] Copy to Clipboard: {0}", value);
}
GUILayout.EndVertical();
GUILayout.Space(20f);
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
private void DrawEventsButtonDetail(int index, MappingMetaData eventData)
{
if (GUILayout.Button("", eventData.Equals(selectedEvent) ? eventsButtonDetailSelectedStyle : eventsButtonDetailStyle))
{
selectedEvent = eventData;
BhapticsLibrary.StopAll();
BhapticsLibrary.Play(eventData.key);
}
float additionalHeight = index * EventsButtonDetailHeight + index * EventsButtonSpacing;
GUI.Label(new Rect(64f, additionalHeight + 16f, 480f, 18f), eventData.key, eventsButtonTitleStyle);
string deviceString = ConvertOrderToDeviceType(eventData.positions);
GUI.Label(new Rect(64f, additionalHeight + 44f, 500f, 12f), deviceString.TrimEnd(), eventsButtonDeviceStyle);
if (eventData.isAudio)
{
GUI.Label(new Rect(569f, additionalHeight + 28f, 40f * 1.2f, 16f * 1.2f), eventsButtonAudioIcon);
}
GUI.Label(new Rect(629f, additionalHeight + 29f, 32f, 14f), (eventData.durationMillis * 0.001f).ToString("0.00") + " s", eventsButtonDurationStyle);
}
private void DrawEventsButton(int index, MappingMetaData eventData)
{
int mtp1 = index % 2;
int mtp2 = index / 2;
if (mtp1 == 0)
{
GUILayout.BeginHorizontal();
}
if (GUILayout.Button("", eventData.Equals(selectedEvent) ? eventsButtonSelectedStyle : eventsButtonStyle))
{
selectedEvent = eventData;
BhapticsLibrary.StopAll();
BhapticsLibrary.Play(eventData.key);
}
float additionalWidth = mtp1 * EventsButtonWidth + mtp1 * EventsButtonSpacing;
float additionalHeight = mtp2 * EventsButtonHeight + mtp2 * EventsButtonSpacing;
GUI.Label(new Rect(additionalWidth + 16f, additionalHeight + 12f, 256f, 36f), eventData.key, eventsButtonTitleStyle);
string deviceString = ConvertOrderToDeviceType(eventData.positions);
GUI.Label(new Rect(additionalWidth + 16f, additionalHeight + 54f, 310f, 26f), deviceString.TrimEnd(), eventsButtonDeviceStyle);
if (mtp1 == 1 || index == settings.EventData.Length - 1)
{
GUILayout.EndHorizontal();
}
}
private Vector2 GetDocumentationButtonPos(int index)
{
return new Vector2(0f, index * DocumentationButtonHeight + index * DocumentationButtonSpacing);
}
private Texture2D GetDocumentationImage(DocumentationButtonType buttonType)
{
switch (buttonType)
{
case DocumentationButtonType.Unity:
return unityDocumentationImage;
case DocumentationButtonType.bHaptics:
return bHapticsDocumentationImage;
case DocumentationButtonType.MetaQuest2:
return metaQuest2DocumentationImage;
}
return unityDocumentationImage;
}
private void DrawDocumentationButton(Vector2 pos, DocumentationButtonType buttonType, string mainText, string subText, string url)
{
GUILayout.BeginArea(new Rect(pos.x, pos.y, DocumentationButtonWidth, DocumentationButtonHeight));
var imageStyle = new GUIStyle();
imageStyle.normal.background = GetDocumentationImage(buttonType);
GUI.Label(new Rect(0f, 0f, 130f, 70f), "", imageStyle);
GUI.Label(new Rect(144f, 26f, 436f, 18f), mainText + " " + "<color=#959595>" + subText + "</color>", new GUIStyle(fontRegularStyle) { fontSize = 14 });
if (GUILayout.Button("", documentationButtonStyle))
{
if (url == string.Empty)
{
BhapticsLogManager.Log("[bHaptics] To be continue...");
}
else
{
Application.OpenURL(url);
}
}
GUILayout.EndArea();
}
private MappingMetaData[] GetSortedEventData(int sortType)
{
if (settings.EventData == null)
{
return new MappingMetaData[0];
}
MappingMetaData[] res = null;
var tempEventList = new List<MappingMetaData>();
tempEventList.AddRange(settings.EventData);
if (sortType == 0)
{
tempEventList.Sort(SortByUpdateTime);
tempEventList.Reverse();
}
else
{
tempEventList.Sort(SortByName);
}
res = tempEventList.ToArray();
return res;
}
private void ResetOnChangePage()
{
selectedEvent = null;
}
private int SortByUpdateTime(MappingMetaData x, MappingMetaData y)
{
return x.updateTime.CompareTo(y.updateTime);
}
private int SortByName(MappingMetaData x, MappingMetaData y)
{
return x.key.CompareTo(y.key);
}
private string ConvertOrderToDeviceType(string[] deviceArr)
{
string res = "";
string interval = " ";
string[] deviceListStr = new string[] { "Face", "Vest", "Arms", "Hands", "Feet", "Glove" };
bool[] useDeviceArr = new bool[deviceListStr.Length];
for (int i = 0; i < deviceArr.Length; ++i)
{
for (int di = 0; di < deviceListStr.Length; ++di)
{
if (deviceArr[i].Contains(deviceListStr[di]))
{
useDeviceArr[di] = true;
}
}
}
for (int i = 0; i < useDeviceArr.Length; ++i)
{
if (useDeviceArr[i])
{
res += deviceListStr[i] + interval;
}
}
return res.Trim();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 739f6e78fe788644bb5e74725fe9e6d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,70 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Bhaptics.SDK2.Editor
{
public class BhapticsSettingWindowAssets
{
public static string GUISkin = "bHapticsGUISkin";
public static string FontBold = "FontBold";
public static string FontMedium = "FontMedium";
public static string FontRegular = "FontRegular";
public static string FontLight = "FontLight";
public static string LabelStyle = "label";
public static string DivideStyle = "Divide";
public static string NavigationButtonStyle = "NavigationButton";
public static string SelectedNavigationButtonStyle = "SelectedNavigationButton";
public static string TempAreaStyle = "TEMP";
public static string SetupButtonStyle = "SetupButton";
public static string SetupInputFieldStyle = "SetupInputField";
public static string VisitButtonStyle = "VisitButton";
public static string VisitTextButtonStyle = "VisitTextButton";
public static string ResetButtonStyle = "ResetButton";
public static string HapticEventsButtonStyle = "HapticEventsButton";
public static string SetupErrorTextStyle = "SetupErrorText";
public static string AppTitleTextStyle = "AppTitleText";
public static string BlackBackgroundStyle = "BlackBackground";
public static string LatestDeployedVersionTextStyle = "LatestDeployedVersionText";
public static string RefreshDeployedVersionButtonStyle = "RefreshDeployedVersionButton";
public static string MainTapTitleStyle = "MainTapTitle";
public static string SampleGuideButtonStyle = "SampleGuideButton";
public static string CopyClipboardButtonStyle = "CopyClipboardButton";
public static string ViewAllTextStyle = "ViewAllText";
public static string EventsButtonDetailStyle = "EventsButtonDetail";
public static string EventsButtonDetailSelectedStyle = "EventsButtonDetailSelected";
public static string EventsButtonStyle = "EventsButton";
public static string EventsButtonSelectedStyle = "EventsButtonSelected";
public static string EventsButtonTitleStyle = "EventsButtonTitle";
public static string EventsButtonDeviceStyle = "EventsButtonDevice";
public static string EventsButtonDurationStyle = "EventsButtonDuration";
public static string DocumentationButtonStyle = "DocumentationButton";
public static string LastUpdatedToggleStyle = "LastUpdatedToggle";
public static string LastUpdatedToggleSelectedStyle = "LastUpdatedToggleSelected";
public static string AZToggleStyle = "AZToggle";
public static string AZToggleSelectedStyle = "AZToggleSelected";
public static string ViewGridStyle = "ViewGrid";
public static string ViewGridSelectedStyle = "ViewGridSelected";
public static string ViewListStyle = "ViewList";
public static string ViewListSelectedStyle = "ViewListSelected";
public static string HomeIcon = "icons/icon-home-def";
public static string HomeSelectedIcon = "icons/icon-home-fill";
public static string EventsIcon = "icons/icon-event-def";
public static string EventsSelectedIcon = "icons/icon-event-fill";
public static string DocumentationIcon = "icons/icon-documentation-def";
public static string DocumentationSelectedIcon = "icons/icon-documentation-fill";
public static string WhiteImage = "WhiteBackground";
public static string BlackBackground = "BlackBackground";
public static string LatestDeployedVersionBox = "LatestDeployedVersionBox";
public static string SampleGuidesImage = "sample-guides-background";
public static string AppIdApiKeyOutlineImage = "outline-appId-apiKey";
public static string EventsButtonAudioIcon = "icons/icon-events-audio";
public static string UnityDocumentationImage = "img-documentation-unity-def";
public static string BhapticsDocumentationImage = "img-documentation-bhaptics-def";
public static string MetaQuest2DocumentationImage = "img-documentation-meta-def";
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f388c59f103d5c744b9a44df01b3fa17
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace Bhaptics.SDK2
{
[CustomEditor(typeof(BhapticsSettings))]
public class BhapticsSettingsEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
GUI.enabled = false;
base.OnInspectorGUI();
GUI.enabled = true;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dedd661db77da6d42bccea01b15b1e87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: