first push!
This commit is contained in:
29299
Assets/HIIT30_Session.asset
Normal file
29299
Assets/HIIT30_Session.asset
Normal file
File diff suppressed because it is too large
Load Diff
8
Assets/HIIT30_Session.asset.meta
Normal file
8
Assets/HIIT30_Session.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5f909449d01d84b4f8118d1f972fe7cc
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
5
Assets/Plugins.meta
Normal file
5
Assets/Plugins.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a74419d4ed92a43589e8b33362cf2f95
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
5
Assets/Plugins/Android.meta
Normal file
5
Assets/Plugins/Android.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6bcb86970c7ff4e08a5499891901f8e9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
21
Assets/Plugins/Android/AndroidManifest.xml
Normal file
21
Assets/Plugins/Android/AndroidManifest.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.unity3d.player" xmlns:tools="http://schemas.android.com/tools" android:installLocation="preferExternal">
|
||||||
|
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
|
||||||
|
<application android:theme="@style/UnityThemeSelector" android:icon="@drawable/app_icon" android:label="@string/app_name" android:isGame="true">
|
||||||
|
<activity android:label="@string/app_name" android:screenOrientation="fullSensor" android:launchMode="singleTask" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density" android:hardwareAccelerated="false" android:name="com.unity3d.player.UnityPlayerActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/>
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:minSdkVersion="31" android:usesPermissionFlags="neverForLocation"/>
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" android:minSdkVersion="31"/>
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" android:minSdkVersion="31"/>
|
||||||
|
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
|
||||||
|
</manifest>
|
||||||
7
Assets/Plugins/Android/AndroidManifest.xml.meta
Normal file
7
Assets/Plugins/Android/AndroidManifest.xml.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4abd69b18c4ab45ec976851430122248
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Plugins/Android/unityandroidbluetoothlelib.jar
Normal file
BIN
Assets/Plugins/Android/unityandroidbluetoothlelib.jar
Normal file
Binary file not shown.
33
Assets/Plugins/Android/unityandroidbluetoothlelib.jar.meta
Normal file
33
Assets/Plugins/Android/unityandroidbluetoothlelib.jar.meta
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f158ceee465c745bc89002ae57bc033e
|
||||||
|
timeCreated: 1539484944
|
||||||
|
licenseType: Pro
|
||||||
|
PluginImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
platformData:
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Android: Android
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
395
Assets/Plugins/BluetoothDeviceScript.cs
Normal file
395
Assets/Plugins/BluetoothDeviceScript.cs
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class BluetoothDeviceScript : MonoBehaviour
|
||||||
|
{
|
||||||
|
#if UNITY_IOS
|
||||||
|
public Dictionary<string, string> BLEStandardUUIDs = new Dictionary<string, string>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public List<string> DiscoveredDeviceList;
|
||||||
|
|
||||||
|
public Action InitializedAction;
|
||||||
|
public Action DeinitializedAction;
|
||||||
|
public Action<string> ErrorAction;
|
||||||
|
public Action<string> ServiceAddedAction;
|
||||||
|
public Action StartedAdvertisingAction;
|
||||||
|
public Action StoppedAdvertisingAction;
|
||||||
|
public Action<string, string> DiscoveredPeripheralAction;
|
||||||
|
public Action<string, string, int, byte[]> DiscoveredPeripheralWithAdvertisingInfoAction;
|
||||||
|
public Action<BluetoothLEHardwareInterface.iBeaconData> DiscoveredBeaconAction;
|
||||||
|
public Action<string, string> RetrievedConnectedPeripheralAction;
|
||||||
|
public Action<string, byte[]> PeripheralReceivedWriteDataAction;
|
||||||
|
public Action<string> ConnectedPeripheralAction;
|
||||||
|
public Action<string> ConnectedDisconnectPeripheralAction;
|
||||||
|
public Action<string> DisconnectedPeripheralAction;
|
||||||
|
public Action<string, string> DiscoveredServiceAction;
|
||||||
|
public Action<string, string, string> DiscoveredCharacteristicAction;
|
||||||
|
public Action<string> DidWriteCharacteristicAction;
|
||||||
|
public Dictionary<string, Dictionary<string, Action<string>>> DidUpdateNotificationStateForCharacteristicAction;
|
||||||
|
public Dictionary<string, Dictionary<string, Action<string, string>>> DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction;
|
||||||
|
public Dictionary<string, Dictionary<string, Action<string, byte[]>>> DidUpdateCharacteristicValueAction;
|
||||||
|
public Dictionary<string, Dictionary<string, Action<string, string, byte[]>>> DidUpdateCharacteristicValueWithDeviceAddressAction;
|
||||||
|
public Action<string, int> RequestMtuAction;
|
||||||
|
public Action<string, int> ReadRSSIAction;
|
||||||
|
|
||||||
|
// Use this for initialization
|
||||||
|
void Start ()
|
||||||
|
{
|
||||||
|
DiscoveredDeviceList = new List<string> ();
|
||||||
|
DidUpdateNotificationStateForCharacteristicAction = new Dictionary<string, Dictionary<string, Action<string>>> ();
|
||||||
|
DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction = new Dictionary<string, Dictionary<string, Action<string, string>>> ();
|
||||||
|
DidUpdateCharacteristicValueAction = new Dictionary<string, Dictionary<string, Action<string, byte[]>>> ();
|
||||||
|
DidUpdateCharacteristicValueWithDeviceAddressAction = new Dictionary<string, Dictionary<string, Action<string, string, byte[]>>> ();
|
||||||
|
|
||||||
|
#if UNITY_IOS
|
||||||
|
BLEStandardUUIDs["Heart Rate Measurement"] = "00002A37-0000-1000-8000-00805F9B34FB";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is called once per frame
|
||||||
|
void Update ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const string deviceInitializedString = "Initialized";
|
||||||
|
const string deviceDeInitializedString = "DeInitialized";
|
||||||
|
const string deviceErrorString = "Error";
|
||||||
|
const string deviceServiceAdded = "ServiceAdded";
|
||||||
|
const string deviceStartedAdvertising = "StartedAdvertising";
|
||||||
|
const string deviceStoppedAdvertising = "StoppedAdvertising";
|
||||||
|
const string deviceDiscoveredPeripheral = "DiscoveredPeripheral";
|
||||||
|
const string deviceDiscoveredBeacon = "DiscoveredBeacon";
|
||||||
|
const string deviceRetrievedConnectedPeripheral = "RetrievedConnectedPeripheral";
|
||||||
|
const string devicePeripheralReceivedWriteData = "PeripheralReceivedWriteData";
|
||||||
|
const string deviceConnectedPeripheral = "ConnectedPeripheral";
|
||||||
|
const string deviceDisconnectedPeripheral = "DisconnectedPeripheral";
|
||||||
|
const string deviceDiscoveredService = "DiscoveredService";
|
||||||
|
const string deviceDiscoveredCharacteristic = "DiscoveredCharacteristic";
|
||||||
|
const string deviceDidWriteCharacteristic = "DidWriteCharacteristic";
|
||||||
|
const string deviceDidUpdateNotificationStateForCharacteristic = "DidUpdateNotificationStateForCharacteristic";
|
||||||
|
const string deviceDidUpdateValueForCharacteristic = "DidUpdateValueForCharacteristic";
|
||||||
|
const string deviceLog = "Log";
|
||||||
|
const string deviceRequestMtu = "MtuChanged";
|
||||||
|
const string deviceReadRSSI = "DidReadRSSI";
|
||||||
|
|
||||||
|
public void OnBluetoothMessage (string message)
|
||||||
|
{
|
||||||
|
if (message != null)
|
||||||
|
{
|
||||||
|
char[] delim = new char[] { '~' };
|
||||||
|
string[] parts = message.Split (delim);
|
||||||
|
|
||||||
|
string log = "";
|
||||||
|
for (int i = 0; i < parts.Length; ++i)
|
||||||
|
log += string.Format("| {0}", parts[i]);
|
||||||
|
BluetoothLEHardwareInterface.Log(log);
|
||||||
|
|
||||||
|
if (message.Length >= deviceInitializedString.Length && message.Substring (0, deviceInitializedString.Length) == deviceInitializedString)
|
||||||
|
{
|
||||||
|
if (InitializedAction != null)
|
||||||
|
InitializedAction ();
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceLog.Length && message.Substring (0, deviceLog.Length) == deviceLog)
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.Log (parts[1]);
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceDeInitializedString.Length && message.Substring (0, deviceDeInitializedString.Length) == deviceDeInitializedString)
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.FinishDeInitialize ();
|
||||||
|
|
||||||
|
if (DeinitializedAction != null)
|
||||||
|
DeinitializedAction ();
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceErrorString.Length && message.Substring (0, deviceErrorString.Length) == deviceErrorString)
|
||||||
|
{
|
||||||
|
string error = "";
|
||||||
|
|
||||||
|
if (parts.Length >= 2)
|
||||||
|
error = parts[1];
|
||||||
|
|
||||||
|
if (ErrorAction != null)
|
||||||
|
ErrorAction (error);
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceServiceAdded.Length && message.Substring (0, deviceServiceAdded.Length) == deviceServiceAdded)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 2)
|
||||||
|
{
|
||||||
|
if (ServiceAddedAction != null)
|
||||||
|
ServiceAddedAction (parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceStartedAdvertising.Length && message.Substring (0, deviceStartedAdvertising.Length) == deviceStartedAdvertising)
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.Log ("Started Advertising");
|
||||||
|
|
||||||
|
if (StartedAdvertisingAction != null)
|
||||||
|
StartedAdvertisingAction ();
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceStoppedAdvertising.Length && message.Substring (0, deviceStoppedAdvertising.Length) == deviceStoppedAdvertising)
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.Log ("Stopped Advertising");
|
||||||
|
|
||||||
|
if (StoppedAdvertisingAction != null)
|
||||||
|
StoppedAdvertisingAction ();
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceDiscoveredPeripheral.Length && message.Substring (0, deviceDiscoveredPeripheral.Length) == deviceDiscoveredPeripheral)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 3)
|
||||||
|
{
|
||||||
|
// the first callback will only get called the first time this device is seen
|
||||||
|
// this is because it gets added to the a list in the DiscoveredDeviceList
|
||||||
|
// after that only the second callback will get called and only if there is
|
||||||
|
// advertising data available
|
||||||
|
if (!DiscoveredDeviceList.Contains (parts[1] + "|" + parts[2]))
|
||||||
|
{
|
||||||
|
DiscoveredDeviceList.Add (parts[1] + "|" + parts[2]);
|
||||||
|
|
||||||
|
if (DiscoveredPeripheralAction != null)
|
||||||
|
DiscoveredPeripheralAction (parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.Length >= 5 && DiscoveredPeripheralWithAdvertisingInfoAction != null)
|
||||||
|
{
|
||||||
|
// get the rssi from the 4th value
|
||||||
|
int rssi = 0;
|
||||||
|
if (!int.TryParse (parts[3], out rssi))
|
||||||
|
rssi = 0;
|
||||||
|
|
||||||
|
// parse the base 64 encoded data that is the 5th value
|
||||||
|
byte[] bytes = System.Convert.FromBase64String (parts[4]);
|
||||||
|
|
||||||
|
DiscoveredPeripheralWithAdvertisingInfoAction (parts[1], parts[2], rssi, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceDiscoveredBeacon.Length && message.Substring (0, deviceDiscoveredBeacon.Length) == deviceDiscoveredBeacon)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 7)
|
||||||
|
{
|
||||||
|
var iBeaconData = new BluetoothLEHardwareInterface.iBeaconData ();
|
||||||
|
|
||||||
|
iBeaconData.UUID = parts[1];
|
||||||
|
if (!int.TryParse (parts[2], out iBeaconData.Major))
|
||||||
|
iBeaconData.Major = 0;
|
||||||
|
if (!int.TryParse (parts[3], out iBeaconData.Minor))
|
||||||
|
iBeaconData.Minor = 0;
|
||||||
|
if (!int.TryParse (parts[4], out iBeaconData.RSSI))
|
||||||
|
iBeaconData.RSSI = 0;
|
||||||
|
if (!int.TryParse (parts[5], out iBeaconData.AndroidSignalPower))
|
||||||
|
iBeaconData.AndroidSignalPower = 0;
|
||||||
|
int iOSProximity = 0;
|
||||||
|
if (!int.TryParse (parts[6], out iOSProximity))
|
||||||
|
iOSProximity = 0;
|
||||||
|
iBeaconData.iOSProximity = (BluetoothLEHardwareInterface.iOSProximity)iOSProximity;
|
||||||
|
|
||||||
|
if (DiscoveredBeaconAction != null)
|
||||||
|
DiscoveredBeaconAction (iBeaconData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceRetrievedConnectedPeripheral.Length && message.Substring (0, deviceRetrievedConnectedPeripheral.Length) == deviceRetrievedConnectedPeripheral)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 3)
|
||||||
|
{
|
||||||
|
DiscoveredDeviceList.Add (parts[1]);
|
||||||
|
|
||||||
|
if (RetrievedConnectedPeripheralAction != null)
|
||||||
|
RetrievedConnectedPeripheralAction (parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message.Length >= devicePeripheralReceivedWriteData.Length && message.Substring (0, devicePeripheralReceivedWriteData.Length) == devicePeripheralReceivedWriteData)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 3)
|
||||||
|
OnPeripheralData (parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceConnectedPeripheral.Length && message.Substring (0, deviceConnectedPeripheral.Length) == deviceConnectedPeripheral)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 2 && ConnectedPeripheralAction != null)
|
||||||
|
ConnectedPeripheralAction (parts[1]);
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceDisconnectedPeripheral.Length && message.Substring (0, deviceDisconnectedPeripheral.Length) == deviceDisconnectedPeripheral)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 2)
|
||||||
|
{
|
||||||
|
if (ConnectedDisconnectPeripheralAction != null)
|
||||||
|
ConnectedDisconnectPeripheralAction (parts[1]);
|
||||||
|
|
||||||
|
if (DisconnectedPeripheralAction != null)
|
||||||
|
DisconnectedPeripheralAction (parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceDiscoveredService.Length && message.Substring (0, deviceDiscoveredService.Length) == deviceDiscoveredService)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 3 && DiscoveredServiceAction != null)
|
||||||
|
DiscoveredServiceAction (parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceDiscoveredCharacteristic.Length && message.Substring (0, deviceDiscoveredCharacteristic.Length) == deviceDiscoveredCharacteristic)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 4 && DiscoveredCharacteristicAction != null)
|
||||||
|
DiscoveredCharacteristicAction (parts[1], parts[2], parts[3]);
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceDidWriteCharacteristic.Length && message.Substring (0, deviceDidWriteCharacteristic.Length) == deviceDidWriteCharacteristic)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 2 && DidWriteCharacteristicAction != null)
|
||||||
|
DidWriteCharacteristicAction (parts[1]);
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceDidUpdateNotificationStateForCharacteristic.Length && message.Substring (0, deviceDidUpdateNotificationStateForCharacteristic.Length) == deviceDidUpdateNotificationStateForCharacteristic)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 3)
|
||||||
|
{
|
||||||
|
if (DidUpdateNotificationStateForCharacteristicAction != null && DidUpdateNotificationStateForCharacteristicAction.ContainsKey (parts[1]))
|
||||||
|
{
|
||||||
|
var characteristicAction = DidUpdateNotificationStateForCharacteristicAction[parts[1]];
|
||||||
|
if (characteristicAction != null && characteristicAction.ContainsKey (parts[2]))
|
||||||
|
{
|
||||||
|
var action = characteristicAction[parts[2]];
|
||||||
|
if (action != null)
|
||||||
|
action (parts[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction != null && DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction.ContainsKey (parts[1]))
|
||||||
|
{
|
||||||
|
var characteristicAction = DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[parts[1]];
|
||||||
|
if (characteristicAction != null && characteristicAction.ContainsKey (parts[2]))
|
||||||
|
{
|
||||||
|
var action = characteristicAction[parts[2]];
|
||||||
|
if (action != null)
|
||||||
|
action (parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceDidUpdateValueForCharacteristic.Length && message.Substring (0, deviceDidUpdateValueForCharacteristic.Length) == deviceDidUpdateValueForCharacteristic)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 4)
|
||||||
|
OnBluetoothData (parts[1], parts[2], parts[3]);
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceRequestMtu.Length && message.Substring(0, deviceRequestMtu.Length) == deviceRequestMtu)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 3)
|
||||||
|
{
|
||||||
|
if (RequestMtuAction != null)
|
||||||
|
{
|
||||||
|
int mtu = 0;
|
||||||
|
if (int.TryParse(parts[2], out mtu))
|
||||||
|
RequestMtuAction(parts[1], mtu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message.Length >= deviceReadRSSI.Length && message.Substring(0, deviceReadRSSI.Length) == deviceReadRSSI)
|
||||||
|
{
|
||||||
|
if (parts.Length >= 3)
|
||||||
|
{
|
||||||
|
if (ReadRSSIAction != null)
|
||||||
|
{
|
||||||
|
int rssi = 0;
|
||||||
|
if (int.TryParse(parts[2], out rssi))
|
||||||
|
ReadRSSIAction(parts[1], rssi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBluetoothData (string base64Data)
|
||||||
|
{
|
||||||
|
OnBluetoothData ("", "", base64Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBluetoothData (string deviceAddress, string characteristic, string base64Data)
|
||||||
|
{
|
||||||
|
if (base64Data != null)
|
||||||
|
{
|
||||||
|
byte[] bytes = System.Convert.FromBase64String (base64Data);
|
||||||
|
if (bytes.Length > 0)
|
||||||
|
{
|
||||||
|
deviceAddress = deviceAddress.ToUpper ();
|
||||||
|
characteristic = characteristic.ToUpper ();
|
||||||
|
|
||||||
|
#if UNITY_IOS
|
||||||
|
if (BLEStandardUUIDs.ContainsKey(characteristic))
|
||||||
|
characteristic = BLEStandardUUIDs[characteristic];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.Log ("Device: " + deviceAddress + " Characteristic Received: " + characteristic);
|
||||||
|
|
||||||
|
string byteString = "";
|
||||||
|
foreach (byte b in bytes)
|
||||||
|
byteString += string.Format ("{0:X2}", b);
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.Log (byteString);
|
||||||
|
|
||||||
|
if (DidUpdateCharacteristicValueAction != null && DidUpdateCharacteristicValueAction.ContainsKey (deviceAddress))
|
||||||
|
{
|
||||||
|
var characteristicAction = DidUpdateCharacteristicValueAction[deviceAddress];
|
||||||
|
#if UNITY_ANDROID
|
||||||
|
characteristic = characteristic.ToLower ();
|
||||||
|
#endif
|
||||||
|
if (characteristicAction != null && characteristicAction.ContainsKey (characteristic))
|
||||||
|
{
|
||||||
|
var action = characteristicAction[characteristic];
|
||||||
|
if (action != null)
|
||||||
|
action (characteristic, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DidUpdateCharacteristicValueWithDeviceAddressAction != null && DidUpdateCharacteristicValueWithDeviceAddressAction.ContainsKey (deviceAddress))
|
||||||
|
{
|
||||||
|
var characteristicAction = DidUpdateCharacteristicValueWithDeviceAddressAction[deviceAddress];
|
||||||
|
#if UNITY_ANDROID
|
||||||
|
characteristic = characteristic.ToLower ();
|
||||||
|
#endif
|
||||||
|
if (characteristicAction != null && characteristicAction.ContainsKey (characteristic))
|
||||||
|
{
|
||||||
|
var action = characteristicAction[characteristic];
|
||||||
|
if (action != null)
|
||||||
|
action (deviceAddress, characteristic, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPeripheralData (string characteristic, string base64Data)
|
||||||
|
{
|
||||||
|
if (base64Data != null)
|
||||||
|
{
|
||||||
|
byte[] bytes = System.Convert.FromBase64String (base64Data);
|
||||||
|
if (bytes.Length > 0)
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.Log ("Peripheral Received: " + characteristic);
|
||||||
|
|
||||||
|
string byteString = "";
|
||||||
|
foreach (byte b in bytes)
|
||||||
|
byteString += string.Format ("{0:X2}", b);
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.Log (byteString);
|
||||||
|
|
||||||
|
if (PeripheralReceivedWriteDataAction != null)
|
||||||
|
PeripheralReceivedWriteDataAction (characteristic, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_IOS
|
||||||
|
private void IncludeCoreLocationFramework()
|
||||||
|
{
|
||||||
|
// this method is here because Unity now only includes CoreLocation
|
||||||
|
// if there are methods in the .cs code that access it
|
||||||
|
Input.location.Stop ();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public void OnApplicationQuit()
|
||||||
|
{
|
||||||
|
if (Application.isEditor)
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.DeInitialize(() =>
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.Log("Deinitialize complete");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Assets/Plugins/BluetoothDeviceScript.cs.meta
Normal file
8
Assets/Plugins/BluetoothDeviceScript.cs.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b188ba3ac565e48f58fc50dd5db4818d
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
1147
Assets/Plugins/BluetoothHardwareInterface.cs
Normal file
1147
Assets/Plugins/BluetoothHardwareInterface.cs
Normal file
File diff suppressed because it is too large
Load Diff
8
Assets/Plugins/BluetoothHardwareInterface.cs.meta
Normal file
8
Assets/Plugins/BluetoothHardwareInterface.cs.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b8496a9b1a1df40af9ada2311d1d6d09
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
33
Assets/Plugins/BluetoothLEOSX.bundle.meta
Normal file
33
Assets/Plugins/BluetoothLEOSX.bundle.meta
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5383f7d08256547f6b36ee834b840062
|
||||||
|
folderAsset: yes
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Plugins/BluetoothLEOSX.bundle/Contents.meta
Normal file
8
Assets/Plugins/BluetoothLEOSX.bundle/Contents.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 34c7c97d38c834839be439bcf96a0267
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
52
Assets/Plugins/BluetoothLEOSX.bundle/Contents/Info.plist
Normal file
52
Assets/Plugins/BluetoothLEOSX.bundle/Contents/Info.plist
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>BuildMachineOSBuild</key>
|
||||||
|
<string>21E258</string>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>BluetoothLEOSX</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.shatalmic.BluetoothLEOSX</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>BluetoothLEOSX</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>BNDL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
|
<array>
|
||||||
|
<string>MacOSX</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>DTCompiler</key>
|
||||||
|
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||||
|
<key>DTPlatformBuild</key>
|
||||||
|
<string>13E113</string>
|
||||||
|
<key>DTPlatformName</key>
|
||||||
|
<string>macosx</string>
|
||||||
|
<key>DTPlatformVersion</key>
|
||||||
|
<string>12.3</string>
|
||||||
|
<key>DTSDKBuild</key>
|
||||||
|
<string>21E226</string>
|
||||||
|
<key>DTSDKName</key>
|
||||||
|
<string>macosx12.3</string>
|
||||||
|
<key>DTXcode</key>
|
||||||
|
<string>1330</string>
|
||||||
|
<key>DTXcodeBuild</key>
|
||||||
|
<string>13E113</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>10.11</string>
|
||||||
|
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||||
|
<string>Bluetooth is required to work with devices.</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright © 2016 Shatalmic. All rights reserved.</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8e7811284d1734fc2ba4b29fccd3ccf7
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS.meta
Normal file
8
Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4e9d68945c9234df3b7855f4e1006f0f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS/BluetoothLEOSX
Executable file
BIN
Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS/BluetoothLEOSX
Executable file
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1a83baeb98e0141f587d978b89ecd37d
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 673293518de2c4fbf913749ebdb36dd1
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>files</key>
|
||||||
|
<dict/>
|
||||||
|
<key>files2</key>
|
||||||
|
<dict/>
|
||||||
|
<key>rules</key>
|
||||||
|
<dict>
|
||||||
|
<key>^Resources/</key>
|
||||||
|
<true/>
|
||||||
|
<key>^Resources/.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Resources/Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version.plist$</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>rules2</key>
|
||||||
|
<dict>
|
||||||
|
<key>.*\.dSYM($|/)</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>11</real>
|
||||||
|
</dict>
|
||||||
|
<key>^(.*/)?\.DS_Store$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>2000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
||||||
|
<dict>
|
||||||
|
<key>nested</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>10</real>
|
||||||
|
</dict>
|
||||||
|
<key>^.*</key>
|
||||||
|
<true/>
|
||||||
|
<key>^Info\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^PkgInfo$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Resources/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Resources/.*\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>optional</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1000</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>omit</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1100</real>
|
||||||
|
</dict>
|
||||||
|
<key>^Resources/Base\.lproj/</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>1010</real>
|
||||||
|
</dict>
|
||||||
|
<key>^[^/]+$</key>
|
||||||
|
<dict>
|
||||||
|
<key>nested</key>
|
||||||
|
<true/>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>10</real>
|
||||||
|
</dict>
|
||||||
|
<key>^embedded\.provisionprofile$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
<key>^version\.plist$</key>
|
||||||
|
<dict>
|
||||||
|
<key>weight</key>
|
||||||
|
<real>20</real>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2b362acd4e35f4bb380341cd3df2b9c0
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
5
Assets/Plugins/iOS.meta
Normal file
5
Assets/Plugins/iOS.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8d3303514acc04853a1fbd8393630e00
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
109
Assets/Plugins/iOS/UnityBluetoothLE.h
Normal file
109
Assets/Plugins/iOS/UnityBluetoothLE.h
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//
|
||||||
|
// UnityBluetoothLE.h
|
||||||
|
// Unity-iPhone
|
||||||
|
//
|
||||||
|
// Created by Tony Pitman on 03/05/2014.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <CoreBluetooth/CoreBluetooth.h>
|
||||||
|
|
||||||
|
#if !TARGET_OS_TV
|
||||||
|
#import <CoreLocation/CoreLocation.h>
|
||||||
|
|
||||||
|
@interface UnityBluetoothLE : NSObject <CBCentralManagerDelegate, CBPeripheralManagerDelegate, CBPeripheralDelegate, CLLocationManagerDelegate>
|
||||||
|
#else
|
||||||
|
@interface UnityBluetoothLE : NSObject <CBCentralManagerDelegate, CBPeripheralManagerDelegate, CBPeripheralDelegate>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{
|
||||||
|
CBCentralManager *_centralManager;
|
||||||
|
#if !TARGET_OS_TV
|
||||||
|
CLLocationManager *_locationManager;
|
||||||
|
#endif
|
||||||
|
NSMutableDictionary *_peripherals;
|
||||||
|
|
||||||
|
#if !TARGET_OS_TV
|
||||||
|
CBPeripheralManager *_peripheralManager;
|
||||||
|
|
||||||
|
NSString *_peripheralName;
|
||||||
|
|
||||||
|
NSMutableDictionary *_services;
|
||||||
|
NSMutableDictionary *_characteristics;
|
||||||
|
NSMutableDictionary *_allCharacteristics;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NSMutableArray *_backgroundMessages;
|
||||||
|
BOOL _isPaused;
|
||||||
|
BOOL _alreadyNotified;
|
||||||
|
BOOL _isInitializing;
|
||||||
|
BOOL _rssiOnly;
|
||||||
|
int _recordType;
|
||||||
|
|
||||||
|
long _mtu;
|
||||||
|
|
||||||
|
unsigned char *_writeCharacteristicBytes;
|
||||||
|
long _writeCharacteristicLength;
|
||||||
|
long _writeCharacteristicPosition;
|
||||||
|
long _writeCharacteristicBytesToWrite;
|
||||||
|
CBCharacteristicWriteType _writeCharacteristicWithResponse;
|
||||||
|
int _writeCharacteristicRetries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (atomic, strong) NSMutableDictionary *_peripherals;
|
||||||
|
@property (atomic) BOOL _rssiOnly;
|
||||||
|
|
||||||
|
- (void)initialize:(BOOL)asCentral asPeripheral:(BOOL)asPeripheral;
|
||||||
|
- (void)deInitialize;
|
||||||
|
- (void)scanForPeripheralsWithServices:(NSArray *)serviceUUIDs options:(NSDictionary *)options clearPeripheralList:(BOOL)clearPeripheralList recordType:(int)recordType;
|
||||||
|
- (void)stopScan;
|
||||||
|
- (void)retrieveListOfPeripheralsWithServices:(NSArray *)serviceUUIDs;
|
||||||
|
- (void)connectToPeripheral:(NSString *)name;
|
||||||
|
- (void)disconnectPeripheral:(NSString *)name;
|
||||||
|
- (CBCharacteristic *)getCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString;
|
||||||
|
- (void)readCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString;
|
||||||
|
- (void)writeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString data:(NSData *)data withResponse:(BOOL)withResponse;
|
||||||
|
- (void)subscribeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString;
|
||||||
|
- (void)unsubscribeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString;
|
||||||
|
- (void)writeCharactersticBytesReset;
|
||||||
|
- (void)writeCharactersticBytes:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic data:(NSData *)data withResponse:(CBCharacteristicWriteType)withResponse;
|
||||||
|
- (void)writeNextPacket:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic;
|
||||||
|
- (void)requestMtu:(NSString *)name mtu:(int)mtu;
|
||||||
|
- (void)readRSSI:(NSString *)name;
|
||||||
|
|
||||||
|
#if !TARGET_OS_TV
|
||||||
|
- (void)scanForBeacons:(NSArray<CLBeaconRegion *> *)beaconRegions;
|
||||||
|
- (void)stopBeaconScan;
|
||||||
|
|
||||||
|
- (void)peripheralName:(NSString *)newName;
|
||||||
|
- (void)createService:(NSString *)uuid primary:(BOOL)primary;
|
||||||
|
- (void)removeService:(NSString *)uuid;
|
||||||
|
- (void)removeServices;
|
||||||
|
- (void)createCharacteristic:(NSString *)uuid properties:(CBCharacteristicProperties)properties permissions:(CBAttributePermissions)permissions value:(NSData *)value;
|
||||||
|
- (void)removeCharacteristic:(NSString *)uuid;
|
||||||
|
- (void)removeCharacteristics;
|
||||||
|
- (void)startAdvertising;
|
||||||
|
- (void)stopAdvertising;
|
||||||
|
- (void)updateCharacteristicValue:(NSString *)uuid value:(NSData *)value;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
- (void)pauseMessages:(BOOL)isPaused;
|
||||||
|
- (void)sendUnityMessage:(BOOL)isString message:(NSString *)message;
|
||||||
|
|
||||||
|
+ (NSString *) base64StringFromData:(NSData *)data length:(int)length;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface UnityMessage : NSObject
|
||||||
|
|
||||||
|
{
|
||||||
|
BOOL _isString;
|
||||||
|
NSString *_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)initialize:(BOOL)isString message:(NSString *)message;
|
||||||
|
- (void)deInitialize;
|
||||||
|
- (void)sendUnityMessage;
|
||||||
|
|
||||||
|
@end
|
||||||
95
Assets/Plugins/iOS/UnityBluetoothLE.h.meta
Normal file
95
Assets/Plugins/iOS/UnityBluetoothLE.h.meta
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 89fad22a839074ac08f9f7ffc9dbce4e
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
'': Linux
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: x86
|
||||||
|
- first:
|
||||||
|
'': OSXIntel
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
'': OSXIntel64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Android: Android
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: AnyOS
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
iPhone: iOS
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CompileFlags:
|
||||||
|
FrameworkDependencies:
|
||||||
|
- first:
|
||||||
|
tvOS: tvOS
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
1368
Assets/Plugins/iOS/UnityBluetoothLE.mm
Normal file
1368
Assets/Plugins/iOS/UnityBluetoothLE.mm
Normal file
File diff suppressed because it is too large
Load Diff
124
Assets/Plugins/iOS/UnityBluetoothLE.mm.meta
Normal file
124
Assets/Plugins/iOS/UnityBluetoothLE.mm.meta
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6a40ca1fe2b7a48e5b257178dfdca41e
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
'': Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Android: 1
|
||||||
|
Exclude Editor: 1
|
||||||
|
Exclude Linux: 1
|
||||||
|
Exclude Linux64: 1
|
||||||
|
Exclude LinuxUniversal: 1
|
||||||
|
Exclude OSXUniversal: 1
|
||||||
|
Exclude Win: 1
|
||||||
|
Exclude Win64: 1
|
||||||
|
Exclude iOS: 0
|
||||||
|
- first:
|
||||||
|
'': Linux
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: x86
|
||||||
|
- first:
|
||||||
|
'': OSXIntel
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
'': OSXIntel64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Android: Android
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: ARMv7
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: AnyOS
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Linux
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: x86
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
iPhone: iOS
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
AddToEmbeddedBinaries: false
|
||||||
|
CompileFlags: -fno-objc-arc
|
||||||
|
FrameworkDependencies: CoreBluetooth;
|
||||||
|
- first:
|
||||||
|
tvOS: tvOS
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CompileFlags: -fno-objc-arc
|
||||||
|
FrameworkDependencies: CoreBluetooth;
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
34
Assets/Readme.asset
Normal file
34
Assets/Readme.asset
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: fcf7219bab7fe46a1ad266029b2fee19, type: 3}
|
||||||
|
m_Name: Readme
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
icon: {fileID: 2800000, guid: 727a75301c3d24613a3ebcec4a24c2c8, type: 3}
|
||||||
|
title: URP Empty Template
|
||||||
|
sections:
|
||||||
|
- heading: Welcome to the Universal Render Pipeline
|
||||||
|
text: This template includes the settings and assets you need to start creating with the Universal Render Pipeline.
|
||||||
|
linkText:
|
||||||
|
url:
|
||||||
|
- heading: URP Documentation
|
||||||
|
text:
|
||||||
|
linkText: Read more about URP
|
||||||
|
url: https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest
|
||||||
|
- heading: Forums
|
||||||
|
text:
|
||||||
|
linkText: Get answers and support
|
||||||
|
url: https://forum.unity.com/forums/universal-render-pipeline.383/
|
||||||
|
- heading: Report bugs
|
||||||
|
text:
|
||||||
|
linkText: Submit a report
|
||||||
|
url: https://unity3d.com/unity/qa/bug-reporting
|
||||||
|
loadedLayout: 1
|
||||||
8
Assets/Readme.asset.meta
Normal file
8
Assets/Readme.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8105016687592461f977c054a80ce2f2
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Scenes.meta
Normal file
8
Assets/Scenes.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9c53962885c2c4f449125a979d6ad240
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
2898
Assets/Scenes/SampleScene.unity
Normal file
2898
Assets/Scenes/SampleScene.unity
Normal file
File diff suppressed because it is too large
Load Diff
7
Assets/Scenes/SampleScene.unity.meta
Normal file
7
Assets/Scenes/SampleScene.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 99c9720ab356a0642a771bea13969a05
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Scripts.meta
Normal file
8
Assets/Scripts.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 49ef74d258a874397b18bf01ee85c53b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Scripts/Debug.meta
Normal file
8
Assets/Scripts/Debug.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b7792fdd9cc2c4791b509bfed728822b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
42
Assets/Scripts/Debug/ForceCurveDataLogger.cs
Normal file
42
Assets/Scripts/Debug/ForceCurveDataLogger.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
public class ForceCurveDataLogger : MonoBehaviour
|
||||||
|
{
|
||||||
|
private List<float> _strokeBuffer = new List<float>();
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
if (PerformanceMonitorManager.Instance != null)
|
||||||
|
PerformanceMonitorManager.Instance.OnForceCurveUpdated += HandleData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleData(List<float> points)
|
||||||
|
{
|
||||||
|
// 1. SIGNAL: Manager sends empty list when a NEW stroke starts
|
||||||
|
if (points.Count == 0)
|
||||||
|
{
|
||||||
|
if (_strokeBuffer.Count > 5)
|
||||||
|
{
|
||||||
|
LogFinishedStroke();
|
||||||
|
}
|
||||||
|
_strokeBuffer.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 2. Accumulate the cumulative data
|
||||||
|
_strokeBuffer = new List<float>(points);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogFinishedStroke()
|
||||||
|
{
|
||||||
|
float maxForce = _strokeBuffer.Max();
|
||||||
|
float totalImpulse = _strokeBuffer.Sum(); // Area under the curve
|
||||||
|
string csv = string.Join(",", _strokeBuffer.Select(p => p.ToString("0")));
|
||||||
|
|
||||||
|
Debug.Log($"<color=#FF7F00><b>[STROKE RECORDED]</b></color> Impulse: {totalImpulse:N0} | Pts: {_strokeBuffer.Count} | Max: {maxForce}");
|
||||||
|
Debug.Log("CSV:" + csv);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Debug/ForceCurveDataLogger.cs.meta
Normal file
11
Assets/Scripts/Debug/ForceCurveDataLogger.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a905dc9499e83430980b57cbc956dd70
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
86
Assets/Scripts/Debug/ForceCurveHistoryDebugger.cs
Normal file
86
Assets/Scripts/Debug/ForceCurveHistoryDebugger.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(LineRenderer))]
|
||||||
|
public class ForceCurveHistoryDebugger : MonoBehaviour
|
||||||
|
{
|
||||||
|
private LineRenderer _lr;
|
||||||
|
private List<float> _historyPoints = new List<float>();
|
||||||
|
|
||||||
|
[Header("Settings")]
|
||||||
|
public float xScale = 0.05f; // Distance between points
|
||||||
|
public float yScale = 0.05f; // Height multiplier
|
||||||
|
public int maxPoints = 1000; // Keep last 1000 points (approx 20-30 seconds)
|
||||||
|
|
||||||
|
private int _lastPointsCount = 0;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
_lr = GetComponent<LineRenderer>();
|
||||||
|
_lr.positionCount = 0;
|
||||||
|
_lr.startWidth = 0.05f;
|
||||||
|
_lr.endWidth = 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
if (PerformanceMonitorManager.Instance != null)
|
||||||
|
{
|
||||||
|
PerformanceMonitorManager.Instance.OnForceCurveUpdated += OnCurveData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCurveData(List<float> fullCurve)
|
||||||
|
{
|
||||||
|
// Calculate how many NEW points were added since last update
|
||||||
|
// (Since the Manager sends the whole cumulative curve of the current stroke)
|
||||||
|
int newPointsCount = fullCurve.Count - _lastPointsCount;
|
||||||
|
|
||||||
|
// If count dropped (new stroke started), we take the whole new list
|
||||||
|
if (newPointsCount < 0)
|
||||||
|
{
|
||||||
|
newPointsCount = fullCurve.Count;
|
||||||
|
AddPoints(fullCurve); // Add the whole new stroke start
|
||||||
|
}
|
||||||
|
else if (newPointsCount > 0)
|
||||||
|
{
|
||||||
|
// Just add the tail (the newest packet)
|
||||||
|
List<float> newSegment = fullCurve.GetRange(_lastPointsCount, newPointsCount);
|
||||||
|
AddPoints(newSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastPointsCount = fullCurve.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddPoints(List<float> newPoints)
|
||||||
|
{
|
||||||
|
UnityMainThreadDispatcher.Instance().Enqueue(() => {
|
||||||
|
|
||||||
|
_historyPoints.AddRange(newPoints);
|
||||||
|
|
||||||
|
// Trim history if too long
|
||||||
|
if (_historyPoints.Count > maxPoints)
|
||||||
|
{
|
||||||
|
_historyPoints.RemoveRange(0, _historyPoints.Count - maxPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawGraph();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawGraph()
|
||||||
|
{
|
||||||
|
_lr.positionCount = _historyPoints.Count;
|
||||||
|
|
||||||
|
for (int i = 0; i < _historyPoints.Count; i++)
|
||||||
|
{
|
||||||
|
float x = i * xScale;
|
||||||
|
float y = _historyPoints[i] * yScale;
|
||||||
|
|
||||||
|
// Shift x so the graph scrolls to the left
|
||||||
|
// (Optional: Keep it static and let it grow to the right)
|
||||||
|
|
||||||
|
_lr.SetPosition(i, new Vector3(x, y, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Debug/ForceCurveHistoryDebugger.cs.meta
Normal file
11
Assets/Scripts/Debug/ForceCurveHistoryDebugger.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 68892aba1c1c94fd7959380b4718584a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
71
Assets/Scripts/Debug/ForceCurveVisualizer.cs
Normal file
71
Assets/Scripts/Debug/ForceCurveVisualizer.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(LineRenderer))]
|
||||||
|
public class ForceCurveVisualizer : MonoBehaviour
|
||||||
|
{
|
||||||
|
private LineRenderer _lr;
|
||||||
|
|
||||||
|
[Header("Visual Settings")]
|
||||||
|
public float xScale = 0.1f;
|
||||||
|
public float yScale = 0.05f; // Raw force is usually 0-200. 0.05 scales it to 0-10 height.
|
||||||
|
public float lineWidth = 0.1f;
|
||||||
|
public Color lineColor = Color.cyan;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
_lr = GetComponent<LineRenderer>();
|
||||||
|
|
||||||
|
// Setup LineRenderer programmatically if not set in Inspector
|
||||||
|
_lr.positionCount = 0;
|
||||||
|
_lr.startWidth = lineWidth;
|
||||||
|
_lr.endWidth = lineWidth;
|
||||||
|
_lr.useWorldSpace = false; // Important for UI/HUD
|
||||||
|
_lr.material = new Material(Shader.Find("Sprites/Default")); // Basic white material
|
||||||
|
_lr.startColor = lineColor;
|
||||||
|
_lr.endColor = lineColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
if (PerformanceMonitorManager.Instance != null)
|
||||||
|
{
|
||||||
|
PerformanceMonitorManager.Instance.OnForceCurveUpdated += UpdateGraph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateGraph(List<float> points)
|
||||||
|
{
|
||||||
|
UnityMainThreadDispatcher.Instance().Enqueue(() => {
|
||||||
|
if (points.Count < 2)
|
||||||
|
{
|
||||||
|
_lr.positionCount = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lr.positionCount = points.Count;
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
for (int i = 0; i < points.Count; i++)
|
||||||
|
{
|
||||||
|
float x = i * xScale;
|
||||||
|
float y = points[i] * yScale;
|
||||||
|
|
||||||
|
// Simple 3-point smoothing
|
||||||
|
if (i > 0 && i < points.Count - 1)
|
||||||
|
{
|
||||||
|
float prev = points[i-1] * yScale;
|
||||||
|
float next = points[i+1] * yScale;
|
||||||
|
y = (prev + y + next) / 3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lr.SetPosition(i, new Vector3(x, y, 0));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// JUICE TIP: Use this to check for "Good Form"
|
||||||
|
// A good stroke is a smooth bell curve.
|
||||||
|
// A bad stroke has a "dip" in the middle (two peaks).
|
||||||
|
// You can analyze 'points' here to trigger the "sputtering engine" sound.
|
||||||
|
}
|
||||||
11
Assets/Scripts/Debug/ForceCurveVisualizer.cs.meta
Normal file
11
Assets/Scripts/Debug/ForceCurveVisualizer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a6d1a8e151ddc4bae990fe2e86149e70
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
84
Assets/Scripts/Debug/PM5TestUI.cs
Normal file
84
Assets/Scripts/Debug/PM5TestUI.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
public class PM5TestUI : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("UI References")]
|
||||||
|
[SerializeField] private TMP_Text wattsText;
|
||||||
|
[SerializeField] private TMP_Text spmText;
|
||||||
|
[SerializeField] private TMP_Text distanceText;
|
||||||
|
[SerializeField] private TMP_Text hrText; // NEW
|
||||||
|
[SerializeField] private TMP_Text caloriesText; // NEW
|
||||||
|
[SerializeField] private TMP_Text statusText;
|
||||||
|
[SerializeField] private Image statusIndicator;
|
||||||
|
[SerializeField] private Button connectButton;
|
||||||
|
|
||||||
|
[Header("Logging")]
|
||||||
|
[SerializeField] private Transform logContent;
|
||||||
|
[SerializeField] private GameObject logItemPrefab;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
if (PerformanceMonitorManager.Instance != null)
|
||||||
|
{
|
||||||
|
PerformanceMonitorManager.Instance.OnLog += AddLog;
|
||||||
|
PerformanceMonitorManager.Instance.OnConnectionStateChanged += UpdateStatusUI;
|
||||||
|
PerformanceMonitorManager.Instance.OnStatsUpdated += UpdateDashboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectButton.onClick.AddListener(() => {
|
||||||
|
PerformanceMonitorManager.Instance.StartScan();
|
||||||
|
});
|
||||||
|
|
||||||
|
UpdateStatusUI(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDashboard(PerformanceMonitorManager.RowingStats stats)
|
||||||
|
{
|
||||||
|
UnityMainThreadDispatcher.Instance().Enqueue(() => {
|
||||||
|
if (wattsText) wattsText.text = $"{stats.Watts} W";
|
||||||
|
if (spmText) spmText.text = $"{stats.SPM} s/m";
|
||||||
|
if (distanceText) distanceText.text = $"{stats.Distance:F0} m";
|
||||||
|
|
||||||
|
if (hrText) hrText.text = stats.HeartRate > 0 ? $"{stats.HeartRate} bpm" : "--";
|
||||||
|
if (caloriesText) caloriesText.text = $"{stats.Calories} cal";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatusUI(bool isConnected)
|
||||||
|
{
|
||||||
|
UnityMainThreadDispatcher.Instance().Enqueue(() => {
|
||||||
|
if (statusText)
|
||||||
|
{
|
||||||
|
statusText.text = isConnected ? "CONNECTED" : "DISCONNECTED";
|
||||||
|
statusText.color = isConnected ? Color.green : Color.red;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusIndicator)
|
||||||
|
statusIndicator.color = isConnected ? Color.green : Color.red;
|
||||||
|
|
||||||
|
if (connectButton)
|
||||||
|
connectButton.interactable = !isConnected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddLog(string message)
|
||||||
|
{
|
||||||
|
UnityMainThreadDispatcher.Instance().Enqueue(() => {
|
||||||
|
if (logContent == null || logItemPrefab == null) return;
|
||||||
|
|
||||||
|
// Optional: Limit log size to prevent lag
|
||||||
|
if (logContent.childCount > 20)
|
||||||
|
Destroy(logContent.GetChild(0).gameObject);
|
||||||
|
|
||||||
|
GameObject newItem = Instantiate(logItemPrefab, logContent);
|
||||||
|
TMP_Text txt = newItem.GetComponent<TMP_Text>();
|
||||||
|
if (txt) txt.text = $"[{System.DateTime.Now:HH:mm:ss}] {message}";
|
||||||
|
|
||||||
|
Canvas.ForceUpdateCanvases();
|
||||||
|
ScrollRect sr = logContent.GetComponentInParent<ScrollRect>();
|
||||||
|
if (sr) sr.verticalNormalizedPosition = 0f;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Debug/PM5TestUI.cs.meta
Normal file
11
Assets/Scripts/Debug/PM5TestUI.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 039ab1d0f5dab403db97489b34b3aa0e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Scripts/Input.meta
Normal file
8
Assets/Scripts/Input.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b1180c7df0fe547399159fad47c82773
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
92
Assets/Scripts/Input/RowingDataRecorder.cs
Normal file
92
Assets/Scripts/Input/RowingDataRecorder.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public class RowingDataRecorder : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Recording Setup")]
|
||||||
|
public bool isRecording = false;
|
||||||
|
public string saveFileName = "Zone2_Session";
|
||||||
|
|
||||||
|
private RowingSessionData _currentSession;
|
||||||
|
private float _recordingStartTime;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
if (PerformanceMonitorManager.Instance != null)
|
||||||
|
{
|
||||||
|
// We listen to the same events the InputManager uses
|
||||||
|
PerformanceMonitorManager.Instance.OnStatsUpdated += RecordStats;
|
||||||
|
PerformanceMonitorManager.Instance.OnForceCurveUpdated += RecordForceCurve;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.R)) ToggleRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleRecording()
|
||||||
|
{
|
||||||
|
isRecording = !isRecording;
|
||||||
|
if (isRecording)
|
||||||
|
{
|
||||||
|
_currentSession = ScriptableObject.CreateInstance<RowingSessionData>();
|
||||||
|
_currentSession.sessionName = saveFileName;
|
||||||
|
_recordingStartTime = Time.time;
|
||||||
|
Debug.Log($"[Recorder] Started recording: {saveFileName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SaveSessionAsset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecordStats(PerformanceMonitorManager.RowingStats stats)
|
||||||
|
{
|
||||||
|
if (!isRecording) return;
|
||||||
|
|
||||||
|
RowingFrame frame = new RowingFrame
|
||||||
|
{
|
||||||
|
timestamp = Time.time - _recordingStartTime,
|
||||||
|
watts = stats.Watts,
|
||||||
|
spm = stats.SPM,
|
||||||
|
heartRate = stats.HeartRate,
|
||||||
|
forceCurve = new List<float>() // Empty for normal stat updates
|
||||||
|
};
|
||||||
|
_currentSession.frames.Add(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecordForceCurve(List<float> curve)
|
||||||
|
{
|
||||||
|
if (!isRecording || curve == null || curve.Count == 0) return;
|
||||||
|
|
||||||
|
// We create a specific frame just for the force curve completion
|
||||||
|
RowingFrame frame = new RowingFrame
|
||||||
|
{
|
||||||
|
timestamp = Time.time - _recordingStartTime,
|
||||||
|
watts = PerformanceMonitorManager.Instance.Stats.Watts,
|
||||||
|
spm = PerformanceMonitorManager.Instance.Stats.SPM,
|
||||||
|
heartRate = PerformanceMonitorManager.Instance.Stats.HeartRate,
|
||||||
|
forceCurve = new List<float>(curve) // Clone the list!
|
||||||
|
};
|
||||||
|
_currentSession.frames.Add(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveSessionAsset()
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (_currentSession == null || _currentSession.frames.Count == 0) return;
|
||||||
|
|
||||||
|
// Create the Asset in your Project folder
|
||||||
|
string path = $"Assets/{saveFileName}.asset";
|
||||||
|
AssetDatabase.CreateAsset(_currentSession, AssetDatabase.GenerateUniqueAssetPath(path));
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
|
||||||
|
Debug.Log($"[Recorder] Session saved to {path} with {_currentSession.frames.Count} frames.");
|
||||||
|
_currentSession = null;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Input/RowingDataRecorder.cs.meta
Normal file
11
Assets/Scripts/Input/RowingDataRecorder.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a737e255a43774e5a8e57cb70c7f9f01
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
234
Assets/Scripts/Input/RowingInputManager.cs
Normal file
234
Assets/Scripts/Input/RowingInputManager.cs
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class RowingInputManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
public static RowingInputManager Instance;
|
||||||
|
|
||||||
|
public enum InputSource { LivePM5, Recorded, Simulated }
|
||||||
|
|
||||||
|
[Header("Data Source")]
|
||||||
|
public InputSource currentInputSource = InputSource.LivePM5;
|
||||||
|
|
||||||
|
[Header("Recorded Mode Controls")]
|
||||||
|
public List<RowingSessionData> recordedSessions;
|
||||||
|
public int currentSessionIndex = 0;
|
||||||
|
[Range(0.1f, 5f)] public float playbackSpeed = 1f;
|
||||||
|
public bool loopSimulation = true;
|
||||||
|
|
||||||
|
[Header("Simulated Mode (Button Mashing)")]
|
||||||
|
public KeyCode simulateStrokeKey = KeyCode.Space;
|
||||||
|
public float simulateWattJump = 50f; // Lowered slightly so you have to work for it
|
||||||
|
public float simulateWattDecayRate = 85f; // Increased so the "flywheel" slows down faster
|
||||||
|
public float simulateMaxWatts = 300f; // The new hard cap!
|
||||||
|
|
||||||
|
private float _currentSimulatedWatts = 0f;
|
||||||
|
private float _lastSimStrokeTime = 0f;
|
||||||
|
|
||||||
|
[Header("Game Physics Settings")]
|
||||||
|
public float maxGameSpeed = 50f;
|
||||||
|
public float wattsToSpeedMultiplier = 0.1f;
|
||||||
|
public float speedSmoothTime = 0.75f;
|
||||||
|
public int userMaxHR = 190;
|
||||||
|
|
||||||
|
[Header("Live Output (Read Only)")]
|
||||||
|
public float CurrentSmoothedVelocity;
|
||||||
|
public float LastStrokeImpulse;
|
||||||
|
public bool IsLastStrokeSmooth;
|
||||||
|
public int MaxPowerOutput;
|
||||||
|
public int CurrentHRZone;
|
||||||
|
public float LastStrokeRatio;
|
||||||
|
|
||||||
|
public Action<float> OnVelocityChanged;
|
||||||
|
public Action<float, bool, float> OnStrokeCompleted;
|
||||||
|
public Action<int> OnZoneChanged;
|
||||||
|
|
||||||
|
private float _targetVelocity;
|
||||||
|
private float _velocityVelocity;
|
||||||
|
|
||||||
|
// Recorded State
|
||||||
|
private float _playbackTimer = 0f;
|
||||||
|
private int _playbackFrameIndex = 0;
|
||||||
|
|
||||||
|
// Stroke Tracking State Machine
|
||||||
|
private enum StrokeState { Idle, Drive, Recovery }
|
||||||
|
private StrokeState _currentState = StrokeState.Idle;
|
||||||
|
private float _driveTimer = 0f;
|
||||||
|
private float _recoveryTimer = 0f;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (Instance == null) Instance = this;
|
||||||
|
else Destroy(gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
if (PerformanceMonitorManager.Instance != null)
|
||||||
|
{
|
||||||
|
PerformanceMonitorManager.Instance.OnStatsUpdated += (stats) => { if (currentInputSource == InputSource.LivePM5) ProcessStats(stats.Watts, stats.SPM, stats.HeartRate); };
|
||||||
|
PerformanceMonitorManager.Instance.OnForceCurveUpdated += (curve) => { if (currentInputSource == InputSource.LivePM5) ProcessForceCurve(curve); };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
HandleKeyboardControls();
|
||||||
|
|
||||||
|
if (currentInputSource == InputSource.Recorded)
|
||||||
|
{
|
||||||
|
RunRecordedSession();
|
||||||
|
}
|
||||||
|
else if (currentInputSource == InputSource.Simulated)
|
||||||
|
{
|
||||||
|
RunManualSimulation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Smoothing
|
||||||
|
CurrentSmoothedVelocity = Mathf.SmoothDamp(CurrentSmoothedVelocity, _targetVelocity, ref _velocityVelocity, speedSmoothTime);
|
||||||
|
OnVelocityChanged?.Invoke(CurrentSmoothedVelocity);
|
||||||
|
|
||||||
|
// Timers
|
||||||
|
if (_currentState == StrokeState.Drive) _driveTimer += Time.deltaTime;
|
||||||
|
else if (_currentState == StrokeState.Recovery) _recoveryTimer += Time.deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunManualSimulation()
|
||||||
|
{
|
||||||
|
// 1. Decay the watts over time (Simulating the flywheel slowing down)
|
||||||
|
_currentSimulatedWatts = Mathf.Max(0f, _currentSimulatedWatts - (simulateWattDecayRate * Time.deltaTime));
|
||||||
|
|
||||||
|
// 2. Handle the "Pull"
|
||||||
|
if (Input.GetKeyDown(simulateStrokeKey))
|
||||||
|
{
|
||||||
|
// Add power, but firmly cap it so we don't break the sound barrier
|
||||||
|
_currentSimulatedWatts = Mathf.Min(_currentSimulatedWatts + simulateWattJump, simulateMaxWatts);
|
||||||
|
|
||||||
|
// Calculate a fake SPM based on how fast you are mashing
|
||||||
|
float timeSinceLast = Time.time - _lastSimStrokeTime;
|
||||||
|
int fakeSPM = timeSinceLast > 0 ? Mathf.RoundToInt(60f / timeSinceLast) : 0;
|
||||||
|
_lastSimStrokeTime = Time.time;
|
||||||
|
|
||||||
|
// Fake HR that scales up gently with your power output
|
||||||
|
int fakeHR = Mathf.Clamp(100 + Mathf.RoundToInt(_currentSimulatedWatts / 4f), 70, 200);
|
||||||
|
|
||||||
|
ProcessStats(Mathf.RoundToInt(_currentSimulatedWatts), fakeSPM, fakeHR);
|
||||||
|
|
||||||
|
// Generate a fake, perfectly smooth force curve (a bell shape)
|
||||||
|
List<float> fakeCurve = new List<float> { 20f, 60f, 120f, 150f, 120f, 60f, 20f };
|
||||||
|
ProcessForceCurve(fakeCurve);
|
||||||
|
|
||||||
|
// Immediately send an empty list to signify the drive ended and trigger recovery
|
||||||
|
ProcessForceCurve(new List<float>());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If not pulling, just update the decaying watts
|
||||||
|
ProcessStats(Mathf.RoundToInt(_currentSimulatedWatts), 0, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunRecordedSession()
|
||||||
|
{
|
||||||
|
if (recordedSessions == null || recordedSessions.Count == 0 || recordedSessions[currentSessionIndex] == null) return;
|
||||||
|
|
||||||
|
var session = recordedSessions[currentSessionIndex];
|
||||||
|
|
||||||
|
_playbackTimer += Time.deltaTime * playbackSpeed;
|
||||||
|
|
||||||
|
while (_playbackFrameIndex < session.frames.Count && _playbackTimer >= session.frames[_playbackFrameIndex].timestamp)
|
||||||
|
{
|
||||||
|
RowingFrame frame = session.frames[_playbackFrameIndex];
|
||||||
|
|
||||||
|
ProcessStats(frame.watts, frame.spm, frame.heartRate);
|
||||||
|
|
||||||
|
if (frame.forceCurve != null && frame.forceCurve.Count > 0)
|
||||||
|
{
|
||||||
|
ProcessForceCurve(frame.forceCurve);
|
||||||
|
}
|
||||||
|
else if (frame.watts == 0 && frame.spm == 0)
|
||||||
|
{
|
||||||
|
ProcessForceCurve(new List<float>());
|
||||||
|
}
|
||||||
|
|
||||||
|
_playbackFrameIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_playbackFrameIndex >= session.frames.Count)
|
||||||
|
{
|
||||||
|
if (loopSimulation)
|
||||||
|
{
|
||||||
|
_playbackTimer = 0f;
|
||||||
|
_playbackFrameIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleKeyboardControls()
|
||||||
|
{
|
||||||
|
if (currentInputSource == InputSource.Recorded)
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.UpArrow)) playbackSpeed += 0.25f;
|
||||||
|
if (Input.GetKeyDown(KeyCode.DownArrow)) playbackSpeed = Mathf.Max(0.25f, playbackSpeed - 0.25f);
|
||||||
|
|
||||||
|
for (int i = 0; i < recordedSessions.Count; i++)
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.Alpha1 + i))
|
||||||
|
{
|
||||||
|
currentSessionIndex = i;
|
||||||
|
_playbackTimer = 0f;
|
||||||
|
_playbackFrameIndex = 0;
|
||||||
|
Debug.Log($"Switched to Session: {recordedSessions[i].sessionName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Core Logic (Shared by Live, Recorded, and Simulated) ---
|
||||||
|
|
||||||
|
private void ProcessStats(int watts, int spm, int hr)
|
||||||
|
{
|
||||||
|
_targetVelocity = Mathf.Clamp(watts * wattsToSpeedMultiplier, 0, maxGameSpeed);
|
||||||
|
if (watts > MaxPowerOutput) MaxPowerOutput = watts;
|
||||||
|
|
||||||
|
int newZone = CalculateHRZone(hr);
|
||||||
|
if (newZone != CurrentHRZone)
|
||||||
|
{
|
||||||
|
CurrentHRZone = newZone;
|
||||||
|
OnZoneChanged?.Invoke(CurrentHRZone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessForceCurve(List<float> points)
|
||||||
|
{
|
||||||
|
if (points == null || points.Count == 0)
|
||||||
|
{
|
||||||
|
if (_currentState == StrokeState.Drive)
|
||||||
|
{
|
||||||
|
_currentState = StrokeState.Recovery;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentState == StrokeState.Recovery || _currentState == StrokeState.Idle)
|
||||||
|
{
|
||||||
|
if (_recoveryTimer > 0)
|
||||||
|
{
|
||||||
|
LastStrokeRatio = _driveTimer / _recoveryTimer;
|
||||||
|
|
||||||
|
LastStrokeImpulse = CalculateImpulse(points);
|
||||||
|
IsLastStrokeSmooth = AnalyzeSmoothness(points);
|
||||||
|
|
||||||
|
OnStrokeCompleted?.Invoke(LastStrokeImpulse, IsLastStrokeSmooth, LastStrokeRatio);
|
||||||
|
}
|
||||||
|
_driveTimer = 0f;
|
||||||
|
_recoveryTimer = 0f;
|
||||||
|
_currentState = StrokeState.Drive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int CalculateHRZone(int currentHR) { return 1; /* Replace with your actual HR math from previous step */ }
|
||||||
|
private float CalculateImpulse(List<float> curve) { return 1f; /* Replace with your actual Impulse math */ }
|
||||||
|
private bool AnalyzeSmoothness(List<float> curve) { return true; /* Replace with your actual Smoothness math */ }
|
||||||
|
}
|
||||||
11
Assets/Scripts/Input/RowingInputManager.cs.meta
Normal file
11
Assets/Scripts/Input/RowingInputManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2ac4117e608824ebfb7e8f80008e8ca0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
19
Assets/Scripts/Input/RowingSessionData.cs
Normal file
19
Assets/Scripts/Input/RowingSessionData.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
public struct RowingFrame
|
||||||
|
{
|
||||||
|
public float timestamp; // Time since the recording started
|
||||||
|
public int watts;
|
||||||
|
public int spm;
|
||||||
|
public int heartRate;
|
||||||
|
public List<float> forceCurve; // Will be empty unless a stroke just finished
|
||||||
|
}
|
||||||
|
|
||||||
|
[CreateAssetMenu(fileName = "NewRowingSession", menuName = "Rowing/Session Data")]
|
||||||
|
public class RowingSessionData : ScriptableObject
|
||||||
|
{
|
||||||
|
public string sessionName = "New Session";
|
||||||
|
public List<RowingFrame> frames = new List<RowingFrame>();
|
||||||
|
}
|
||||||
11
Assets/Scripts/Input/RowingSessionData.cs.meta
Normal file
11
Assets/Scripts/Input/RowingSessionData.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ecde514822d9e4bdc89dcb697d725f65
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Scripts/PM5.meta
Normal file
8
Assets/Scripts/PM5.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5865157d44c834e7fb35c80be377143f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
381
Assets/Scripts/PM5/CSAFEProtocol.cs
Normal file
381
Assets/Scripts/PM5/CSAFEProtocol.cs
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
public static class CSAFEDictionary
|
||||||
|
{
|
||||||
|
public static readonly byte ExtendedFrameStartFlag = 0xF0;
|
||||||
|
public static readonly byte StandardFrameStartFlag = 0xF1;
|
||||||
|
public static readonly byte StopFrameFlag = 0xF2;
|
||||||
|
public static readonly byte ByteStuffingFlag = 0xF3;
|
||||||
|
|
||||||
|
public static readonly Dictionary<string, List<object>> Cmd = new Dictionary<string, List<object>>
|
||||||
|
{
|
||||||
|
// Standard Short Commands
|
||||||
|
{ "CSAFE_GETSTATUS_CMD", new List<object> { 0x80, new int[] { } } },
|
||||||
|
{ "CSAFE_RESET_CMD", new List<object> { 0x81, new int[] { } } },
|
||||||
|
{ "CSAFE_GOIDLE_CMD", new List<object> { 0x82, new int[] { } } },
|
||||||
|
{ "CSAFE_GOHAVEID_CMD", new List<object> { 0x83, new int[] { } } },
|
||||||
|
{ "CSAFE_GOINUSE_CMD", new List<object> { 0x85, new int[] { } } },
|
||||||
|
{ "CSAFE_GOFINISHED_CMD", new List<object> { 0x86, new int[] { } } },
|
||||||
|
{ "CSAFE_GOREADY_CMD", new List<object> { 0x87, new int[] { } } },
|
||||||
|
{ "CSAFE_BADID_CMD", new List<object> { 0x88, new int[] { } } },
|
||||||
|
{ "CSAFE_GETVERSION_CMD", new List<object> { 0x91, new int[] { } } },
|
||||||
|
{ "CSAFE_GETID_CMD", new List<object> { 0x92, new int[] { } } },
|
||||||
|
{ "CSAFE_GETUNITS_CMD", new List<object> { 0x93, new int[] { } } },
|
||||||
|
{ "CSAFE_GETSERIAL_CMD", new List<object> { 0x94, new int[] { } } },
|
||||||
|
{ "CSAFE_GETODOMETER_CMD", new List<object> { 0x9B, new int[] { } } },
|
||||||
|
{ "CSAFE_GETERRORCODE_CMD", new List<object> { 0x9C, new int[] { } } },
|
||||||
|
{ "CSAFE_GETTWORK_CMD", new List<object> { 0xA0, new int[] { } } },
|
||||||
|
{ "CSAFE_GETHORIZONTAL_CMD", new List<object> { 0xA1, new int[] { } } },
|
||||||
|
{ "CSAFE_GETCALORIES_CMD", new List<object> { 0xA3, new int[] { } } },
|
||||||
|
{ "CSAFE_GETPROGRAM_CMD", new List<object> { 0xA4, new int[] { } } },
|
||||||
|
{ "CSAFE_GETPACE_CMD", new List<object> { 0xA6, new int[] { } } },
|
||||||
|
{ "CSAFE_GETCADENCE_CMD", new List<object> { 0xA7, new int[] { } } },
|
||||||
|
{ "CSAFE_GETUSERINFO_CMD", new List<object> { 0xAB, new int[] { } } },
|
||||||
|
{ "CSAFE_GETHRCUR_CMD", new List<object> { 0xB0, new int[] { } } },
|
||||||
|
{ "CSAFE_GETPOWER_CMD", new List<object> { 0xB4, new int[] { } } },
|
||||||
|
|
||||||
|
// Configuration / Set Commands
|
||||||
|
{ "CSAFE_AUTOUPLOAD_CMD", new List<object> { 0x01, new int[] { 1 } } },
|
||||||
|
{ "CSAFE_IDDIGITS_CMD", new List<object> { 0x10, new int[] { 1 } } },
|
||||||
|
{ "CSAFE_SETTIME_CMD", new List<object> { 0x11, new int[] { 1, 1, 1 } } },
|
||||||
|
{ "CSAFE_SETDATE_CMD", new List<object> { 0x12, new int[] { 1, 1, 1 } } },
|
||||||
|
{ "CSAFE_SETTIMEOUT_CMD", new List<object> { 0x13, new int[] { 1 } } },
|
||||||
|
{ "CSAFE_SETUSERCFG1_CMD", new List<object> { 0x1A, new int[] { 0 } } },
|
||||||
|
{ "CSAFE_SETTWORK_CMD", new List<object> { 0x20, new int[] { 1, 1, 1 } } },
|
||||||
|
{ "CSAFE_SETHORIZONTAL_CMD", new List<object> { 0x21, new int[] { 2, 1 } } },
|
||||||
|
{ "CSAFE_SETCALORIES_CMD", new List<object> { 0x23, new int[] { 2 } } },
|
||||||
|
{ "CSAFE_SETPROGRAM_CMD", new List<object> { 0x24, new int[] { 1, 1 } } },
|
||||||
|
{ "CSAFE_SETPOWER_CMD", new List<object> { 0x34, new int[] { 2, 1 } } },
|
||||||
|
{ "CSAFE_GETCAPS_CMD", new List<object> { 0x70, new int[] { 1 } } },
|
||||||
|
|
||||||
|
// PM5 Specific Long Commands (Wrapper 0x1A)
|
||||||
|
{ "CSAFE_PM_GET_WORKOUTTYPE", new List<object> { 0x89, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_DRAGFACTOR", new List<object> { 0xC1, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_STROKESTATE", new List<object> { 0xBF, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_WORKTIME", new List<object> { 0xA0, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_WORKDISTANCE", new List<object> { 0xA3, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_ERRORVALUE", new List<object> { 0xC9, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_WORKOUTSTATE", new List<object> { 0x8D, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_WORKOUTINTERVALCOUNT", new List<object> { 0x9F, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_INTERVALTYPE", new List<object> { 0x8E, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_RESTTIME", new List<object> { 0xCF, new int[] { }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_SET_SPLITDURATION", new List<object> { 0x05, new int[] { 1, 4 }, 0x1A } }, // Fixed ID
|
||||||
|
{ "CSAFE_PM_GET_FORCEPLOTDATA", new List<object> { 0x6B, new int[] { 1 }, 0x1A } }, // Fixed to 0x6B, 1 arg (block size)
|
||||||
|
{ "CSAFE_PM_SET_SCREENERRORMODE", new List<object> { 0x27, new int[] { 1 }, 0x1A } },
|
||||||
|
{ "CSAFE_PM_GET_HEARTBEATDATA", new List<object> { 0x6C, new int[] { }, 0x1A } } // FIXED: No arguments required
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly Dictionary<int, List<object>> Resp = new Dictionary<int, List<object>>
|
||||||
|
{
|
||||||
|
{ 0x80, new List<object> { "CSAFE_GETSTATUS_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x81, new List<object> { "CSAFE_RESET_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x82, new List<object> { "CSAFE_GOIDLE_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x83, new List<object> { "CSAFE_GOHAVEID_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x85, new List<object> { "CSAFE_GOINUSE_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x86, new List<object> { "CSAFE_GOFINISHED_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x87, new List<object> { "CSAFE_GOREADY_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x88, new List<object> { "CSAFE_BADID_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x91, new List<object> { "CSAFE_GETVERSION_CMD", new int[] { 1, 1, 1, 2, 2 } } },
|
||||||
|
{ 0x92, new List<object> { "CSAFE_GETID_CMD", new int[] { -5 } } },
|
||||||
|
{ 0x93, new List<object> { "CSAFE_GETUNITS_CMD", new int[] { 1 } } },
|
||||||
|
{ 0x94, new List<object> { "CSAFE_GETSERIAL_CMD", new int[] { -9 } } },
|
||||||
|
{ 0x9B, new List<object> { "CSAFE_GETODOMETER_CMD", new int[] { 4, 1 } } },
|
||||||
|
{ 0x9C, new List<object> { "CSAFE_GETERRORCODE_CMD", new int[] { 3 } } },
|
||||||
|
{ 0xA0, new List<object> { "CSAFE_GETTWORK_CMD", new int[] { 1, 1, 1 } } },
|
||||||
|
{ 0xA1, new List<object> { "CSAFE_GETHORIZONTAL_CMD", new int[] { 2, 1 } } },
|
||||||
|
{ 0xA3, new List<object> { "CSAFE_GETCALORIES_CMD", new int[] { 2 } } },
|
||||||
|
{ 0xA4, new List<object> { "CSAFE_GETPROGRAM_CMD", new int[] { 1 } } },
|
||||||
|
{ 0xA6, new List<object> { "CSAFE_GETPACE_CMD", new int[] { 2, 1 } } },
|
||||||
|
{ 0xA7, new List<object> { "CSAFE_GETCADENCE_CMD", new int[] { 2, 1 } } },
|
||||||
|
{ 0xAB, new List<object> { "CSAFE_GETUSERINFO_CMD", new int[] { 2, 1, 1, 1 } } },
|
||||||
|
{ 0xB0, new List<object> { "CSAFE_GETHRCUR_CMD", new int[] { 1 } } },
|
||||||
|
{ 0xB4, new List<object> { "CSAFE_GETPOWER_CMD", new int[] { 2, 1 } } },
|
||||||
|
{ 0x01, new List<object> { "CSAFE_AUTOUPLOAD_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x10, new List<object> { "CSAFE_IDDIGITS_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x11, new List<object> { "CSAFE_SETTIME_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x12, new List<object> { "CSAFE_SETDATE_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x13, new List<object> { "CSAFE_SETTIMEOUT_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x1A, new List<object> { "CSAFE_SETUSERCFG1_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x20, new List<object> { "CSAFE_SETTWORK_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x21, new List<object> { "CSAFE_SETHORIZONTAL_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x23, new List<object> { "CSAFE_SETCALORIES_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x24, new List<object> { "CSAFE_SETPROGRAM_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x34, new List<object> { "CSAFE_SETPOWER_CMD", new int[] { 0 } } },
|
||||||
|
{ 0x70, new List<object> { "CSAFE_GETCAPS_CMD", new int[] { 11 } } },
|
||||||
|
|
||||||
|
{ 0x1A89, new List<object> { "CSAFE_PM_GET_WORKOUTTYPE", new int[] { 1 } } },
|
||||||
|
{ 0x1AC1, new List<object> { "CSAFE_PM_GET_DRAGFACTOR", new int[] { 1 } } },
|
||||||
|
{ 0x1ABF, new List<object> { "CSAFE_PM_GET_STROKESTATE", new int[] { 1 } } },
|
||||||
|
{ 0x1AA0, new List<object> { "CSAFE_PM_GET_WORKTIME", new int[] { 4, 1 } } },
|
||||||
|
{ 0x1AA3, new List<object> { "CSAFE_PM_GET_WORKDISTANCE", new int[] { 4, 1 } } },
|
||||||
|
{ 0x1AC9, new List<object> { "CSAFE_PM_GET_ERRORVALUE", new int[] { 2 } } },
|
||||||
|
{ 0x1A8D, new List<object> { "CSAFE_PM_GET_WORKOUTSTATE", new int[] { 1 } } },
|
||||||
|
{ 0x1A9F, new List<object> { "CSAFE_PM_GET_WORKOUTINTERVALCOUNT", new int[] { 1 } } },
|
||||||
|
{ 0x1A8E, new List<object> { "CSAFE_PM_GET_INTERVALTYPE", new int[] { 1 } } },
|
||||||
|
{ 0x1ACF, new List<object> { "CSAFE_PM_GET_RESTTIME", new int[] { 2 } } },
|
||||||
|
{ 0x1A05, new List<object> { "CSAFE_PM_SET_SPLITDURATION", new int[] { 0 } } },
|
||||||
|
{ 0x1A6B, new List<object> { "CSAFE_PM_GET_FORCEPLOTDATA", new int[] { -1 } } },
|
||||||
|
{ 0x1A27, new List<object> { "CSAFE_PM_SET_SCREENERRORMODE", new int[] { 0 } } },
|
||||||
|
{ 0x1A6C, new List<object> { "CSAFE_PM_GET_HEARTBEATDATA", new int[] { -1 } } }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CSAFECommand
|
||||||
|
{
|
||||||
|
public static List<byte> Write(string[] arguments)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
List<byte> message = new List<byte>();
|
||||||
|
List<byte> wrapper = new List<byte>();
|
||||||
|
int wrapped = 0;
|
||||||
|
|
||||||
|
while (i < arguments.Length)
|
||||||
|
{
|
||||||
|
string argument = arguments[i];
|
||||||
|
int commandPropertiesIdInt = Convert.ToInt32(CSAFEDictionary.Cmd[argument][0]);
|
||||||
|
int[] cmdProps = CSAFEDictionary.Cmd[argument][1] as int[];
|
||||||
|
|
||||||
|
int wrapperInt = 0;
|
||||||
|
if (CSAFEDictionary.Cmd[argument].Count > 2)
|
||||||
|
wrapperInt = Convert.ToInt32(CSAFEDictionary.Cmd[argument][2]);
|
||||||
|
|
||||||
|
List<byte> command = new List<byte>();
|
||||||
|
|
||||||
|
if (cmdProps.Length != 0)
|
||||||
|
{
|
||||||
|
for (int bytes = 0; bytes < cmdProps.Length; bytes++)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
int intValue = int.Parse(arguments[i]);
|
||||||
|
byte[] value = IntToBytes(intValue, cmdProps[bytes]);
|
||||||
|
command.AddRange(value);
|
||||||
|
}
|
||||||
|
command.Insert(0, Convert.ToByte(command.Count));
|
||||||
|
}
|
||||||
|
|
||||||
|
command.Insert(0, Convert.ToByte(commandPropertiesIdInt));
|
||||||
|
|
||||||
|
if (wrapperInt == 0x1A)
|
||||||
|
{
|
||||||
|
if (wrapped == 0)
|
||||||
|
{
|
||||||
|
wrapper.Insert(0, Convert.ToByte(wrapperInt));
|
||||||
|
wrapped = 1;
|
||||||
|
}
|
||||||
|
wrapper.AddRange(command);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message.AddRange(command);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapped == 1)
|
||||||
|
{
|
||||||
|
wrapper.Insert(1, Convert.ToByte(wrapper.Count - 1));
|
||||||
|
message.AddRange(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
int checksum = 0;
|
||||||
|
for (int j = 0; j < message.Count; j++)
|
||||||
|
{
|
||||||
|
checksum ^= message[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
int k = 0;
|
||||||
|
while (k < message.Count)
|
||||||
|
{
|
||||||
|
if (0xF0 <= message[k] && message[k] <= 0xF3)
|
||||||
|
{
|
||||||
|
byte stuffValue = Convert.ToByte(message[k] & 0x03);
|
||||||
|
message[k] = CSAFEDictionary.ByteStuffingFlag;
|
||||||
|
message.Insert(k + 1, stuffValue);
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0xF0 <= checksum && checksum <= 0xF3)
|
||||||
|
{
|
||||||
|
message.Add(CSAFEDictionary.ByteStuffingFlag);
|
||||||
|
message.Add(Convert.ToByte(checksum & 0x03));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message.Add(Convert.ToByte(checksum));
|
||||||
|
}
|
||||||
|
|
||||||
|
message.Insert(0, CSAFEDictionary.StandardFrameStartFlag);
|
||||||
|
message.Add(CSAFEDictionary.StopFrameFlag);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, List<string>> Read(byte[] transmission)
|
||||||
|
{
|
||||||
|
Dictionary<string, List<string>> response = new Dictionary<string, List<string>>();
|
||||||
|
List<byte> message = transmission.ToList();
|
||||||
|
|
||||||
|
int startFlagIndex = message.IndexOf(CSAFEDictionary.StandardFrameStartFlag);
|
||||||
|
int extStartFlagIndex = message.IndexOf(CSAFEDictionary.ExtendedFrameStartFlag);
|
||||||
|
int startIndex = Math.Max(startFlagIndex, extStartFlagIndex);
|
||||||
|
int stopFlagIndex = message.IndexOf(CSAFEDictionary.StopFrameFlag);
|
||||||
|
|
||||||
|
if (startIndex == -1 || stopFlagIndex == -1 || stopFlagIndex < startIndex)
|
||||||
|
return response;
|
||||||
|
|
||||||
|
message = message.GetRange(startIndex + 1, stopFlagIndex - startIndex - 1);
|
||||||
|
message = CheckMessage(message);
|
||||||
|
|
||||||
|
if (message == null || message.Count == 0) return response;
|
||||||
|
|
||||||
|
int k = 0;
|
||||||
|
int msgState = message[k];
|
||||||
|
response["CSAFE_GETSTATUS_CMD"] = new List<string> { msgState.ToString() };
|
||||||
|
k++;
|
||||||
|
|
||||||
|
// NEW: Track the end index of the PM5 wrapper block
|
||||||
|
int wrapperEndIndex = -1;
|
||||||
|
|
||||||
|
while (k < message.Count)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int k_before = k; // Track where this specific command started
|
||||||
|
int responseId = message[k];
|
||||||
|
k++;
|
||||||
|
|
||||||
|
// Wrapper Check
|
||||||
|
if (responseId == 0x1A)
|
||||||
|
{
|
||||||
|
if (k >= message.Count) break;
|
||||||
|
int wrapperLength = message[k];
|
||||||
|
k++;
|
||||||
|
|
||||||
|
if (wrapperLength > 0)
|
||||||
|
{
|
||||||
|
wrapperEndIndex = k + wrapperLength; // Mark the boundary of wrapped data
|
||||||
|
|
||||||
|
if (k < message.Count)
|
||||||
|
{
|
||||||
|
responseId = message[k];
|
||||||
|
responseId = responseId | (0x1A << 8);
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NEW: If we are still reading bytes that fall inside the wrapper length,
|
||||||
|
// automatically apply the 0x1A PM5 prefix!
|
||||||
|
else if (k_before < wrapperEndIndex)
|
||||||
|
{
|
||||||
|
responseId = responseId | (0x1A << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CSAFEDictionary.Resp.ContainsKey(responseId))
|
||||||
|
{
|
||||||
|
if (k < message.Count) k += message[k] + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<object> commandResponse = CSAFEDictionary.Resp[responseId];
|
||||||
|
string commandName = commandResponse[0] as string;
|
||||||
|
int[] commandByteCountArray = commandResponse[1] as int[];
|
||||||
|
|
||||||
|
List<string> result = new List<string>();
|
||||||
|
|
||||||
|
// SAFEGUARD: Prevent out of bounds
|
||||||
|
if (k >= message.Count) break;
|
||||||
|
int payloadLength = message[k];
|
||||||
|
k++;
|
||||||
|
|
||||||
|
int bytesReadForCmd = 0;
|
||||||
|
|
||||||
|
foreach (int commandByteCount in commandByteCountArray)
|
||||||
|
{
|
||||||
|
if (bytesReadForCmd >= payloadLength) break;
|
||||||
|
|
||||||
|
int bytesToRead = commandByteCount >= 0 ? commandByteCount : (payloadLength - bytesReadForCmd);
|
||||||
|
|
||||||
|
if (k + bytesToRead > message.Count) break;
|
||||||
|
|
||||||
|
byte[] rawBytes = message.GetRange(k, bytesToRead).ToArray();
|
||||||
|
string value = "";
|
||||||
|
|
||||||
|
if (commandByteCount >= 0)
|
||||||
|
{
|
||||||
|
int intValue = 0;
|
||||||
|
for (int b = 0; b < rawBytes.Length; b++)
|
||||||
|
{
|
||||||
|
intValue |= (rawBytes[b] << (8 * b));
|
||||||
|
}
|
||||||
|
value = intValue.ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = string.Join(",", rawBytes.Select(b => b.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(value);
|
||||||
|
k += bytesToRead;
|
||||||
|
bytesReadForCmd += bytesToRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesReadForCmd < payloadLength)
|
||||||
|
{
|
||||||
|
k += (payloadLength - bytesReadForCmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
response[commandName] = result;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<byte> CheckMessage(List<byte> message)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int checksum = 0;
|
||||||
|
|
||||||
|
while (i < message.Count)
|
||||||
|
{
|
||||||
|
if (message[i] == CSAFEDictionary.ByteStuffingFlag)
|
||||||
|
{
|
||||||
|
if (i + 1 >= message.Count) break;
|
||||||
|
byte stuffValue = message[i + 1];
|
||||||
|
message.RemoveAt(i + 1);
|
||||||
|
message[i] = Convert.ToByte(0xF0 | stuffValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum ^= message[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checksum != 0) return new List<byte>();
|
||||||
|
|
||||||
|
if (message.Count > 0) message.RemoveAt(message.Count - 1);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] IntToBytes(int value, int length)
|
||||||
|
{
|
||||||
|
byte[] result = new byte[length];
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
result[i] = (byte)((value >> (i * 8)) & 0xFF);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/PM5/CSAFEProtocol.cs.meta
Normal file
11
Assets/Scripts/PM5/CSAFEProtocol.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fcecce28b8a4248ac86b498638edf8ca
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
21
Assets/Scripts/PM5/PerfomanceMonitorDictionary.cs
Normal file
21
Assets/Scripts/PM5/PerfomanceMonitorDictionary.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
public static class PerformanceMonitorDictionary
|
||||||
|
{
|
||||||
|
// Concept2 PM5 UUIDs
|
||||||
|
public const string DeviceUUID = "ce060000-43e5-11e4-916c-0800200c9a66";
|
||||||
|
|
||||||
|
// Services
|
||||||
|
public const string DeviceInfoService = "ce060010-43e5-11e4-916c-0800200c9a66";
|
||||||
|
public const string ControlService = "ce060020-43e5-11e4-916c-0800200c9a66";
|
||||||
|
public const string RowingService = "ce060030-43e5-11e4-916c-0800200c9a66";
|
||||||
|
|
||||||
|
// Characteristics - Rowing Service
|
||||||
|
// 0x31: Row Status (Distance, Time)
|
||||||
|
// 0x32: Extra Status (Split, Interval)
|
||||||
|
// 0x35: Stroke Data (Watts, SPM, Drive Info)
|
||||||
|
// 0x3D: Multiplexed (The main firehose of data)
|
||||||
|
public const string MultiplexedCharacteristic = "ce060080-43e5-11e4-916c-0800200c9a66";
|
||||||
|
|
||||||
|
// Characteristics - Control Service (CSAFE)
|
||||||
|
public const string ControlReceiveChar = "ce060021-43e5-11e4-916c-0800200c9a66"; // Write here
|
||||||
|
public const string ControlTransmitChar = "ce060022-43e5-11e4-916c-0800200c9a66"; // Read here
|
||||||
|
}
|
||||||
11
Assets/Scripts/PM5/PerfomanceMonitorDictionary.cs.meta
Normal file
11
Assets/Scripts/PM5/PerfomanceMonitorDictionary.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ad857e139c72d41edb392b5719f88390
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
288
Assets/Scripts/PM5/PerformanceMonitorManager.cs
Normal file
288
Assets/Scripts/PM5/PerformanceMonitorManager.cs
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
public class PerformanceMonitorManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
public static PerformanceMonitorManager Instance;
|
||||||
|
|
||||||
|
public Action<RowingStats> OnStatsUpdated;
|
||||||
|
public Action<List<float>> OnForceCurveUpdated;
|
||||||
|
public Action<bool> OnConnectionStateChanged;
|
||||||
|
public Action<string> OnLog;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class RowingStats
|
||||||
|
{
|
||||||
|
public bool IsConnected;
|
||||||
|
public float Distance;
|
||||||
|
public float ElapsedTime;
|
||||||
|
public int Watts;
|
||||||
|
public int SPM;
|
||||||
|
public int HeartRate;
|
||||||
|
public int Calories;
|
||||||
|
public List<float> CurrentForceCurve = new List<float>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RowingStats Stats = new RowingStats();
|
||||||
|
|
||||||
|
[Header("Settings")]
|
||||||
|
[Tooltip("10Hz is the sweet spot for BLE. Faster may cause packet collisions.")]
|
||||||
|
[SerializeField] private float pollRate = 0.10f;
|
||||||
|
[SerializeField] private float activityTimeout = 3.5f; // Time before we consider the user "stopped"
|
||||||
|
private string _deviceAddress;
|
||||||
|
private bool _isSubscribedToControl = false;
|
||||||
|
private List<byte> _csafeBuffer = new List<byte>();
|
||||||
|
|
||||||
|
// Watchdog
|
||||||
|
private float _lastDataTime = 0f;
|
||||||
|
private int _lastStrokeState = -1;
|
||||||
|
|
||||||
|
public static class UUIDs
|
||||||
|
{
|
||||||
|
public const string Svc_Rowing = "ce060030-43e5-11e4-916c-0800200c9a66";
|
||||||
|
public const string Char_Multiplexed = "ce060080-43e5-11e4-916c-0800200c9a66";
|
||||||
|
public const string Svc_Control = "ce060020-43e5-11e4-916c-0800200c9a66";
|
||||||
|
public const string Char_ControlWrite = "ce060021-43e5-11e4-916c-0800200c9a66";
|
||||||
|
public const string Char_ControlRead = "ce060022-43e5-11e4-916c-0800200c9a66";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (Instance == null) Instance = this;
|
||||||
|
else Destroy(gameObject);
|
||||||
|
DontDestroyOnLoad(gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
InitializeBLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (Stats.IsConnected)
|
||||||
|
{
|
||||||
|
// Instead of checking SPM == 0, we check how long it's been since we got ANY multiplexed data.
|
||||||
|
// This prevents Watts dropping to 0 during a long, slow recovery phase (like in Stone Giant mode).
|
||||||
|
if (Time.time - _lastDataTime > activityTimeout)
|
||||||
|
{
|
||||||
|
if (Stats.Watts != 0 || Stats.SPM != 0)
|
||||||
|
{
|
||||||
|
Stats.Watts = 0;
|
||||||
|
Stats.SPM = 0;
|
||||||
|
OnStatsUpdated?.Invoke(Stats);
|
||||||
|
Log("Rowing stopped (Timeout). Resetting active stats.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeBLE()
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.Initialize(true, false, () => Log("BLE Init"), (err) => Log("Error: " + err));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartScan()
|
||||||
|
{
|
||||||
|
if (Stats.IsConnected) return;
|
||||||
|
Log("Scanning...");
|
||||||
|
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null, (address, name) => {
|
||||||
|
if (!string.IsNullOrEmpty(name) && (name.Contains("Concept2") || name.Contains("PM5")))
|
||||||
|
{
|
||||||
|
Log($"Found {name}");
|
||||||
|
_deviceAddress = address;
|
||||||
|
BluetoothLEHardwareInterface.StopScan();
|
||||||
|
StartCoroutine(ConnectSequence());
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator ConnectSequence()
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(0.5f);
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Connect()
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.ConnectToPeripheral(_deviceAddress, (addr) => {
|
||||||
|
Stats.IsConnected = true;
|
||||||
|
OnConnectionStateChanged?.Invoke(true);
|
||||||
|
Log("Connected!");
|
||||||
|
}, null, (addr, svc, chr) => {
|
||||||
|
|
||||||
|
if (svc.ToUpper().Contains("0030") && chr.ToUpper().Contains("0080"))
|
||||||
|
BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress(_deviceAddress, svc, chr, null, OnMultiplexedDataReceived);
|
||||||
|
|
||||||
|
if (svc.ToUpper().Contains("0020") && chr.ToUpper().Contains("0022"))
|
||||||
|
{
|
||||||
|
if (!_isSubscribedToControl)
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress(_deviceAddress, svc, chr, null, OnControlDataReceived);
|
||||||
|
_isSubscribedToControl = true;
|
||||||
|
StartCoroutine(CSAFEPollLoop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator CSAFEPollLoop()
|
||||||
|
{
|
||||||
|
while (_isSubscribedToControl && Stats.IsConnected)
|
||||||
|
{
|
||||||
|
// 1. Get Stroke State
|
||||||
|
// 2. Get Force Plot Data (Command 0x6B) with Block Size 32 (0x20)
|
||||||
|
// 3. Get Power
|
||||||
|
string[] commands = new string[]
|
||||||
|
{
|
||||||
|
"CSAFE_PM_GET_STROKESTATE",
|
||||||
|
"CSAFE_PM_GET_FORCEPLOTDATA", "32",
|
||||||
|
"CSAFE_GETPOWER_CMD",
|
||||||
|
"CSAFE_GETHRCUR_CMD"
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] payload = CSAFECommand.Write(commands).ToArray();
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.WriteCharacteristic(
|
||||||
|
_deviceAddress,
|
||||||
|
UUIDs.Svc_Control,
|
||||||
|
UUIDs.Char_ControlWrite,
|
||||||
|
payload,
|
||||||
|
payload.Length,
|
||||||
|
true,
|
||||||
|
(x) => {}
|
||||||
|
);
|
||||||
|
|
||||||
|
yield return new WaitForSeconds(pollRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMultiplexedDataReceived(string addr, string chr, byte[] data)
|
||||||
|
{
|
||||||
|
if (data == null || data.Length < 1) return;
|
||||||
|
_lastDataTime = Time.time;
|
||||||
|
|
||||||
|
int packetID = data[0];
|
||||||
|
switch (packetID)
|
||||||
|
{
|
||||||
|
case 0x31:
|
||||||
|
if (data.Length >= 7) {
|
||||||
|
Stats.ElapsedTime = (data[1] | (data[2] << 8) | (data[3] << 16)) * 0.01f;
|
||||||
|
Stats.Distance = (data[4] | (data[5] << 8) | (data[6] << 16)) * 0.1f;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x32:
|
||||||
|
if (data.Length >= 7) {
|
||||||
|
if (data[6] != 255) Stats.SPM = data[6];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x33:
|
||||||
|
if (data.Length >= 7) {
|
||||||
|
Stats.Calories = (data[5] | (data[6] << 8));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnControlDataReceived(string addr, string chr, byte[] data)
|
||||||
|
{
|
||||||
|
_csafeBuffer.AddRange(data);
|
||||||
|
while (_csafeBuffer.Contains(0xF2))
|
||||||
|
{
|
||||||
|
int stopIdx = _csafeBuffer.IndexOf(0xF2);
|
||||||
|
byte[] completeFrame = _csafeBuffer.GetRange(0, stopIdx + 1).ToArray();
|
||||||
|
_csafeBuffer.RemoveRange(0, stopIdx + 1);
|
||||||
|
ParseCSAFE(completeFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseCSAFE(byte[] frame)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var res = CSAFECommand.Read(frame);
|
||||||
|
|
||||||
|
// --- 1. STROKE STATE ---
|
||||||
|
if (res.ContainsKey("CSAFE_PM_GET_STROKESTATE"))
|
||||||
|
{
|
||||||
|
// We keep this variable for debugging, but no longer use it as the curve trigger
|
||||||
|
_lastStrokeState = int.Parse(res["CSAFE_PM_GET_STROKESTATE"][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 2. FORCE CURVE PARSING ---
|
||||||
|
if (res.ContainsKey("CSAFE_PM_GET_FORCEPLOTDATA"))
|
||||||
|
{
|
||||||
|
string rawCsvData = res["CSAFE_PM_GET_FORCEPLOTDATA"][0];
|
||||||
|
string[] byteStrings = rawCsvData.Split(',');
|
||||||
|
|
||||||
|
if (byteStrings.Length > 0)
|
||||||
|
{
|
||||||
|
int bytesRead = int.Parse(byteStrings[0]);
|
||||||
|
|
||||||
|
// NEW BULLETPROOF TRIGGER: The PM5 sends 0 bytes during the recovery phase.
|
||||||
|
// If bytesRead is 0, but we have data accumulated, the drive JUST finished.
|
||||||
|
if (bytesRead == 0)
|
||||||
|
{
|
||||||
|
if (Stats.CurrentForceCurve.Count > 0)
|
||||||
|
{
|
||||||
|
// 1. Emit an empty list to signal ForceCurveDataLogger to print the completed stroke
|
||||||
|
OnForceCurveUpdated?.Invoke(new List<float>());
|
||||||
|
|
||||||
|
// 2. Clear our internal buffer to prep for the next stroke
|
||||||
|
Stats.CurrentForceCurve.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bytesRead > 0 && byteStrings.Length >= bytesRead + 1)
|
||||||
|
{
|
||||||
|
List<float> newPoints = new List<float>();
|
||||||
|
|
||||||
|
// Start at index 1. Each point consists of 2 bytes.
|
||||||
|
for (int i = 1; i < bytesRead; i += 2)
|
||||||
|
{
|
||||||
|
if (i + 1 >= byteStrings.Length) break;
|
||||||
|
|
||||||
|
// ENDIANNESS FIX: Page 91 of CSAFE docs explicitly shows LSB first, then MSB.
|
||||||
|
int lsb = int.Parse(byteStrings[i]); // Byte 1: LSB
|
||||||
|
int msb = int.Parse(byteStrings[i + 1]); // Byte 2: MSB
|
||||||
|
|
||||||
|
// Combine MSB and LSB correctly (Little Endian)
|
||||||
|
int forceValue = (msb << 8) | lsb;
|
||||||
|
|
||||||
|
newPoints.Add(forceValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPoints.Count > 0)
|
||||||
|
{
|
||||||
|
Stats.CurrentForceCurve.AddRange(newPoints);
|
||||||
|
OnForceCurveUpdated?.Invoke(Stats.CurrentForceCurve); // Emit accumulated list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 3. POWER & HR ---
|
||||||
|
if (res.ContainsKey("CSAFE_GETPOWER_CMD"))
|
||||||
|
{
|
||||||
|
if (int.TryParse(res["CSAFE_GETPOWER_CMD"][0], out int watts))
|
||||||
|
Stats.Watts = watts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.ContainsKey("CSAFE_GETHRCUR_CMD"))
|
||||||
|
{
|
||||||
|
if (int.TryParse(res["CSAFE_GETHRCUR_CMD"][0], out int hr))
|
||||||
|
Stats.HeartRate = hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit the overall stats update
|
||||||
|
OnStatsUpdated?.Invoke(Stats);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Log("CSAFE Parse Error: " + e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void Log(string m) { Debug.Log("[PM5] " + m); OnLog?.Invoke(m); }
|
||||||
|
}
|
||||||
11
Assets/Scripts/PM5/PerformanceMonitorManager.cs.meta
Normal file
11
Assets/Scripts/PM5/PerformanceMonitorManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 598ae3859dc5b48b68087a6740b22b00
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Scripts/Utils.meta
Normal file
8
Assets/Scripts/Utils.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8508e66c93823466485cba7d42c1db82
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
51
Assets/Scripts/Utils/TPPCameraFollow.cs
Normal file
51
Assets/Scripts/Utils/TPPCameraFollow.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class TPPCameraFollow : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Target Tracking")]
|
||||||
|
public Transform target; // Drag your Moving Cube here
|
||||||
|
public Vector3 offset = new Vector3(0f, 3f, -6f); // Behind and slightly above
|
||||||
|
|
||||||
|
[Header("Dynamics")]
|
||||||
|
public float smoothTime = 0.3f;
|
||||||
|
public float dynamicFOVSpeedMultiplier = 0.5f; // How much the FOV stretches based on speed
|
||||||
|
public float baseFOV = 60f;
|
||||||
|
|
||||||
|
private Vector3 _velocity = Vector3.zero;
|
||||||
|
private Camera _cam;
|
||||||
|
private float _currentSpeed;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
_cam = GetComponent<Camera>();
|
||||||
|
|
||||||
|
if (RowingInputManager.Instance != null)
|
||||||
|
{
|
||||||
|
RowingInputManager.Instance.OnVelocityChanged += UpdateDynamicFOV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDynamicFOV(float speed)
|
||||||
|
{
|
||||||
|
_currentSpeed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LateUpdate()
|
||||||
|
{
|
||||||
|
if (target == null) return;
|
||||||
|
|
||||||
|
// 1. Smoothly follow the target's position
|
||||||
|
Vector3 targetPosition = target.position + target.TransformDirection(offset);
|
||||||
|
transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref _velocity, smoothTime);
|
||||||
|
|
||||||
|
// 2. Look at the target
|
||||||
|
transform.LookAt(target.position + Vector3.up * 1.5f); // Look slightly above the base of the cube
|
||||||
|
|
||||||
|
// 3. The "Juice": Dynamically widen the FOV as you row faster (Speed Sense)
|
||||||
|
if (_cam != null)
|
||||||
|
{
|
||||||
|
float targetFOV = baseFOV + (_currentSpeed * dynamicFOVSpeedMultiplier);
|
||||||
|
_cam.fieldOfView = Mathf.Lerp(_cam.fieldOfView, targetFOV, Time.deltaTime * 2f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Utils/TPPCameraFollow.cs.meta
Normal file
11
Assets/Scripts/Utils/TPPCameraFollow.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 80f384e7a225c4f98850a2f01f2b68ae
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
97
Assets/Scripts/Utils/UnityMainThreadDispatcher.cs
Normal file
97
Assets/Scripts/Utils/UnityMainThreadDispatcher.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
11
Assets/Scripts/Utils/UnityMainThreadDispatcher.cs.meta
Normal file
11
Assets/Scripts/Utils/UnityMainThreadDispatcher.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2ed9d3282e8634c038764ee594f764f8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Settings.meta
Normal file
8
Assets/Settings.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 709f11a7f3c4041caa4ef136ea32d874
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
123
Assets/Settings/SampleSceneProfile.asset
Normal file
123
Assets/Settings/SampleSceneProfile.asset
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &-7893295128165547882
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 3
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 0b2db86121404754db890f4c8dfe81b2, type: 3}
|
||||||
|
m_Name: Bloom
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
active: 1
|
||||||
|
m_AdvancedMode: 0
|
||||||
|
threshold:
|
||||||
|
m_OverrideState: 1
|
||||||
|
m_Value: 1
|
||||||
|
min: 0
|
||||||
|
intensity:
|
||||||
|
m_OverrideState: 1
|
||||||
|
m_Value: 1
|
||||||
|
min: 0
|
||||||
|
scatter:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: 0.7
|
||||||
|
min: 0
|
||||||
|
max: 1
|
||||||
|
clamp:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: 65472
|
||||||
|
min: 0
|
||||||
|
tint:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
hdr: 0
|
||||||
|
showAlpha: 0
|
||||||
|
showEyeDropper: 1
|
||||||
|
highQualityFiltering:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: 0
|
||||||
|
skipIterations:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: 1
|
||||||
|
min: 0
|
||||||
|
max: 16
|
||||||
|
dirtTexture:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: {fileID: 0}
|
||||||
|
dirtIntensity:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: 0
|
||||||
|
min: 0
|
||||||
|
--- !u!114 &-7011558710299706105
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 3
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 899c54efeace73346a0a16faa3afe726, type: 3}
|
||||||
|
m_Name: Vignette
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
active: 1
|
||||||
|
m_AdvancedMode: 0
|
||||||
|
color:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: {r: 0, g: 0, b: 0, a: 1}
|
||||||
|
hdr: 0
|
||||||
|
showAlpha: 0
|
||||||
|
showEyeDropper: 1
|
||||||
|
center:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: {x: 0.5, y: 0.5}
|
||||||
|
intensity:
|
||||||
|
m_OverrideState: 1
|
||||||
|
m_Value: 0.25
|
||||||
|
min: 0
|
||||||
|
max: 1
|
||||||
|
smoothness:
|
||||||
|
m_OverrideState: 1
|
||||||
|
m_Value: 0.4
|
||||||
|
min: 0.01
|
||||||
|
max: 1
|
||||||
|
rounded:
|
||||||
|
m_OverrideState: 0
|
||||||
|
m_Value: 0
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
|
||||||
|
m_Name: SampleSceneProfile
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
components:
|
||||||
|
- {fileID: 849379129802519247}
|
||||||
|
- {fileID: -7893295128165547882}
|
||||||
|
- {fileID: -7011558710299706105}
|
||||||
|
--- !u!114 &849379129802519247
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 3
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 97c23e3b12dc18c42a140437e53d3951, type: 3}
|
||||||
|
m_Name: Tonemapping
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
active: 1
|
||||||
|
m_AdvancedMode: 0
|
||||||
|
mode:
|
||||||
|
m_OverrideState: 1
|
||||||
|
m_Value: 1
|
||||||
8
Assets/Settings/SampleSceneProfile.asset.meta
Normal file
8
Assets/Settings/SampleSceneProfile.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a6560a915ef98420e9faacc1c7438823
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
81
Assets/Settings/URP-Balanced-Renderer.asset
Normal file
81
Assets/Settings/URP-Balanced-Renderer.asset
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &-1878332245247344467
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: f62c9c65cf3354c93be831c8bc075510, type: 3}
|
||||||
|
m_Name: SSAO
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Active: 1
|
||||||
|
m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3}
|
||||||
|
m_Settings:
|
||||||
|
Downsample: 1
|
||||||
|
AfterOpaque: 0
|
||||||
|
Source: 0
|
||||||
|
NormalSamples: 0
|
||||||
|
Intensity: 0.5
|
||||||
|
DirectLightingStrength: 0.25
|
||||||
|
Radius: 0.25
|
||||||
|
SampleCount: 4
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3}
|
||||||
|
m_Name: URP-Balanced-Renderer
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
debugShaders:
|
||||||
|
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7,
|
||||||
|
type: 3}
|
||||||
|
m_RendererFeatures:
|
||||||
|
- {fileID: -1878332245247344467}
|
||||||
|
m_RendererFeatureMap: adc0de57c6d2eee5
|
||||||
|
m_UseNativeRenderPass: 0
|
||||||
|
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
|
||||||
|
shaders:
|
||||||
|
blitPS: {fileID: 4800000, guid: c17132b1f77d20942aa75f8429c0f8bc, type: 3}
|
||||||
|
copyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3}
|
||||||
|
screenSpaceShadowPS: {fileID: 0}
|
||||||
|
samplingPS: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3}
|
||||||
|
stencilDeferredPS: {fileID: 4800000, guid: e9155b26e1bc55942a41e518703fe304, type: 3}
|
||||||
|
fallbackErrorPS: {fileID: 4800000, guid: e6e9a19c3678ded42a3bc431ebef7dbd, type: 3}
|
||||||
|
materialErrorPS: {fileID: 4800000, guid: 5fd9a8feb75a4b5894c241777f519d4e, type: 3}
|
||||||
|
coreBlitPS: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3}
|
||||||
|
coreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b,
|
||||||
|
type: 3}
|
||||||
|
cameraMotionVector: {fileID: 4800000, guid: c56b7e0d4c7cb484e959caeeedae9bbf,
|
||||||
|
type: 3}
|
||||||
|
objectMotionVector: {fileID: 4800000, guid: 7b3ede40266cd49a395def176e1bc486,
|
||||||
|
type: 3}
|
||||||
|
m_AssetVersion: 1
|
||||||
|
m_OpaqueLayerMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_TransparentLayerMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_DefaultStencilState:
|
||||||
|
overrideStencilState: 0
|
||||||
|
stencilReference: 0
|
||||||
|
stencilCompareFunction: 8
|
||||||
|
passOperation: 2
|
||||||
|
failOperation: 0
|
||||||
|
zFailOperation: 0
|
||||||
|
m_ShadowTransparentReceive: 1
|
||||||
|
m_RenderingMode: 0
|
||||||
|
m_DepthPrimingMode: 1
|
||||||
|
m_AccurateGbufferNormals: 0
|
||||||
|
m_ClusteredRendering: 0
|
||||||
|
m_TileSize: 32
|
||||||
|
m_IntermediateTextureMode: 0
|
||||||
8
Assets/Settings/URP-Balanced-Renderer.asset.meta
Normal file
8
Assets/Settings/URP-Balanced-Renderer.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e634585d5c4544dd297acaee93dc2beb
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
74
Assets/Settings/URP-Balanced.asset
Normal file
74
Assets/Settings/URP-Balanced.asset
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3}
|
||||||
|
m_Name: URP-Balanced
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
k_AssetVersion: 9
|
||||||
|
k_AssetPreviousVersion: 9
|
||||||
|
m_RendererType: 1
|
||||||
|
m_RendererData: {fileID: 0}
|
||||||
|
m_RendererDataList:
|
||||||
|
- {fileID: 11400000, guid: e634585d5c4544dd297acaee93dc2beb, type: 2}
|
||||||
|
m_DefaultRendererIndex: 0
|
||||||
|
m_RequireDepthTexture: 0
|
||||||
|
m_RequireOpaqueTexture: 0
|
||||||
|
m_OpaqueDownsampling: 1
|
||||||
|
m_SupportsTerrainHoles: 1
|
||||||
|
m_StoreActionsOptimization: 0
|
||||||
|
m_SupportsHDR: 1
|
||||||
|
m_MSAA: 1
|
||||||
|
m_RenderScale: 1
|
||||||
|
m_UpscalingFilter: 0
|
||||||
|
m_FsrOverrideSharpness: 0
|
||||||
|
m_FsrSharpness: 0.92
|
||||||
|
m_MainLightRenderingMode: 1
|
||||||
|
m_MainLightShadowsSupported: 1
|
||||||
|
m_MainLightShadowmapResolution: 1024
|
||||||
|
m_AdditionalLightsRenderingMode: 1
|
||||||
|
m_AdditionalLightsPerObjectLimit: 2
|
||||||
|
m_AdditionalLightShadowsSupported: 0
|
||||||
|
m_AdditionalLightsShadowmapResolution: 512
|
||||||
|
m_AdditionalLightsShadowResolutionTierLow: 128
|
||||||
|
m_AdditionalLightsShadowResolutionTierMedium: 256
|
||||||
|
m_AdditionalLightsShadowResolutionTierHigh: 512
|
||||||
|
m_ReflectionProbeBlending: 0
|
||||||
|
m_ReflectionProbeBoxProjection: 1
|
||||||
|
m_ShadowDistance: 50
|
||||||
|
m_ShadowCascadeCount: 1
|
||||||
|
m_Cascade2Split: 0.25
|
||||||
|
m_Cascade3Split: {x: 0.1, y: 0.3}
|
||||||
|
m_Cascade4Split: {x: 0.067, y: 0.2, z: 0.467}
|
||||||
|
m_CascadeBorder: 0.1
|
||||||
|
m_ShadowDepthBias: 1
|
||||||
|
m_ShadowNormalBias: 1
|
||||||
|
m_SoftShadowsSupported: 1
|
||||||
|
m_ConservativeEnclosingSphere: 0
|
||||||
|
m_NumIterationsEnclosingSphere: 64
|
||||||
|
m_AdditionalLightsCookieResolution: 512
|
||||||
|
m_AdditionalLightsCookieFormat: 1
|
||||||
|
m_UseSRPBatcher: 1
|
||||||
|
m_SupportsDynamicBatching: 0
|
||||||
|
m_MixedLightingSupported: 1
|
||||||
|
m_SupportsLightLayers: 0
|
||||||
|
m_DebugLevel: 0
|
||||||
|
m_UseAdaptivePerformance: 1
|
||||||
|
m_ColorGradingMode: 0
|
||||||
|
m_ColorGradingLutSize: 32
|
||||||
|
m_UseFastSRGBLinearConversion: 0
|
||||||
|
m_ShadowType: 1
|
||||||
|
m_LocalShadowsSupported: 0
|
||||||
|
m_LocalShadowsAtlasResolution: 256
|
||||||
|
m_MaxPixelLights: 0
|
||||||
|
m_ShadowAtlasResolution: 256
|
||||||
|
m_ShaderVariantLogLevel: 0
|
||||||
|
m_VolumeFrameworkUpdateMode: 0
|
||||||
|
m_ShadowCascades: 0
|
||||||
8
Assets/Settings/URP-Balanced.asset.meta
Normal file
8
Assets/Settings/URP-Balanced.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e1260c1148f6143b28bae5ace5e9c5d1
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
81
Assets/Settings/URP-HighFidelity-Renderer.asset
Normal file
81
Assets/Settings/URP-HighFidelity-Renderer.asset
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &-1878332245247344467
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: f62c9c65cf3354c93be831c8bc075510, type: 3}
|
||||||
|
m_Name: SSAO
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Active: 1
|
||||||
|
m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3}
|
||||||
|
m_Settings:
|
||||||
|
Downsample: 0
|
||||||
|
AfterOpaque: 0
|
||||||
|
Source: 1
|
||||||
|
NormalSamples: 1
|
||||||
|
Intensity: 0.5
|
||||||
|
DirectLightingStrength: 0.25
|
||||||
|
Radius: 0.25
|
||||||
|
SampleCount: 12
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3}
|
||||||
|
m_Name: URP-HighFidelity-Renderer
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
debugShaders:
|
||||||
|
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7,
|
||||||
|
type: 3}
|
||||||
|
m_RendererFeatures:
|
||||||
|
- {fileID: -1878332245247344467}
|
||||||
|
m_RendererFeatureMap: adc0de57c6d2eee5
|
||||||
|
m_UseNativeRenderPass: 0
|
||||||
|
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
|
||||||
|
shaders:
|
||||||
|
blitPS: {fileID: 4800000, guid: c17132b1f77d20942aa75f8429c0f8bc, type: 3}
|
||||||
|
copyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3}
|
||||||
|
screenSpaceShadowPS: {fileID: 0}
|
||||||
|
samplingPS: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3}
|
||||||
|
stencilDeferredPS: {fileID: 4800000, guid: e9155b26e1bc55942a41e518703fe304, type: 3}
|
||||||
|
fallbackErrorPS: {fileID: 4800000, guid: e6e9a19c3678ded42a3bc431ebef7dbd, type: 3}
|
||||||
|
materialErrorPS: {fileID: 4800000, guid: 5fd9a8feb75a4b5894c241777f519d4e, type: 3}
|
||||||
|
coreBlitPS: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3}
|
||||||
|
coreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b,
|
||||||
|
type: 3}
|
||||||
|
cameraMotionVector: {fileID: 4800000, guid: c56b7e0d4c7cb484e959caeeedae9bbf,
|
||||||
|
type: 3}
|
||||||
|
objectMotionVector: {fileID: 4800000, guid: 7b3ede40266cd49a395def176e1bc486,
|
||||||
|
type: 3}
|
||||||
|
m_AssetVersion: 1
|
||||||
|
m_OpaqueLayerMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_TransparentLayerMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_DefaultStencilState:
|
||||||
|
overrideStencilState: 0
|
||||||
|
stencilReference: 0
|
||||||
|
stencilCompareFunction: 8
|
||||||
|
passOperation: 2
|
||||||
|
failOperation: 0
|
||||||
|
zFailOperation: 0
|
||||||
|
m_ShadowTransparentReceive: 1
|
||||||
|
m_RenderingMode: 0
|
||||||
|
m_DepthPrimingMode: 1
|
||||||
|
m_AccurateGbufferNormals: 0
|
||||||
|
m_ClusteredRendering: 0
|
||||||
|
m_TileSize: 32
|
||||||
|
m_IntermediateTextureMode: 0
|
||||||
8
Assets/Settings/URP-HighFidelity-Renderer.asset.meta
Normal file
8
Assets/Settings/URP-HighFidelity-Renderer.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c40be3174f62c4acf8c1216858c64956
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
69
Assets/Settings/URP-HighFidelity.asset
Normal file
69
Assets/Settings/URP-HighFidelity.asset
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3}
|
||||||
|
m_Name: URP-HighFidelity
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
k_AssetVersion: 9
|
||||||
|
k_AssetPreviousVersion: 9
|
||||||
|
m_RendererType: 1
|
||||||
|
m_RendererData: {fileID: 0}
|
||||||
|
m_RendererDataList:
|
||||||
|
- {fileID: 11400000, guid: c40be3174f62c4acf8c1216858c64956, type: 2}
|
||||||
|
m_DefaultRendererIndex: 0
|
||||||
|
m_RequireDepthTexture: 0
|
||||||
|
m_RequireOpaqueTexture: 0
|
||||||
|
m_OpaqueDownsampling: 1
|
||||||
|
m_SupportsTerrainHoles: 1
|
||||||
|
m_StoreActionsOptimization: 0
|
||||||
|
m_SupportsHDR: 1
|
||||||
|
m_MSAA: 4
|
||||||
|
m_RenderScale: 1
|
||||||
|
m_MainLightRenderingMode: 1
|
||||||
|
m_MainLightShadowsSupported: 1
|
||||||
|
m_MainLightShadowmapResolution: 4096
|
||||||
|
m_AdditionalLightsRenderingMode: 1
|
||||||
|
m_AdditionalLightsPerObjectLimit: 8
|
||||||
|
m_AdditionalLightShadowsSupported: 1
|
||||||
|
m_AdditionalLightsShadowmapResolution: 4096
|
||||||
|
m_AdditionalLightsShadowResolutionTierLow: 128
|
||||||
|
m_AdditionalLightsShadowResolutionTierMedium: 256
|
||||||
|
m_AdditionalLightsShadowResolutionTierHigh: 512
|
||||||
|
m_ReflectionProbeBlending: 1
|
||||||
|
m_ReflectionProbeBoxProjection: 1
|
||||||
|
m_ShadowDistance: 150
|
||||||
|
m_ShadowCascadeCount: 4
|
||||||
|
m_Cascade2Split: 0.25
|
||||||
|
m_Cascade3Split: {x: 0.1, y: 0.3}
|
||||||
|
m_Cascade4Split: {x: 0.067, y: 0.2, z: 0.467}
|
||||||
|
m_CascadeBorder: 0.1
|
||||||
|
m_ShadowDepthBias: 1
|
||||||
|
m_ShadowNormalBias: 1
|
||||||
|
m_SoftShadowsSupported: 1
|
||||||
|
m_AdditionalLightsCookieResolution: 4096
|
||||||
|
m_AdditionalLightsCookieFormat: 4
|
||||||
|
m_UseSRPBatcher: 1
|
||||||
|
m_SupportsDynamicBatching: 0
|
||||||
|
m_MixedLightingSupported: 1
|
||||||
|
m_SupportsLightLayers: 0
|
||||||
|
m_DebugLevel: 0
|
||||||
|
m_UseAdaptivePerformance: 1
|
||||||
|
m_ColorGradingMode: 0
|
||||||
|
m_ColorGradingLutSize: 32
|
||||||
|
m_UseFastSRGBLinearConversion: 0
|
||||||
|
m_ShadowType: 1
|
||||||
|
m_LocalShadowsSupported: 0
|
||||||
|
m_LocalShadowsAtlasResolution: 256
|
||||||
|
m_MaxPixelLights: 0
|
||||||
|
m_ShadowAtlasResolution: 256
|
||||||
|
m_ShaderVariantLogLevel: 0
|
||||||
|
m_VolumeFrameworkUpdateMode: 0
|
||||||
|
m_ShadowCascades: 1
|
||||||
8
Assets/Settings/URP-HighFidelity.asset.meta
Normal file
8
Assets/Settings/URP-HighFidelity.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7b7fd9122c28c4d15b667c7040e3b3fd
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
57
Assets/Settings/URP-Performant-Renderer.asset
Normal file
57
Assets/Settings/URP-Performant-Renderer.asset
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3}
|
||||||
|
m_Name: URP-Performant-Renderer
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
debugShaders:
|
||||||
|
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7,
|
||||||
|
type: 3}
|
||||||
|
m_RendererFeatures: []
|
||||||
|
m_RendererFeatureMap:
|
||||||
|
m_UseNativeRenderPass: 0
|
||||||
|
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
|
||||||
|
shaders:
|
||||||
|
blitPS: {fileID: 4800000, guid: c17132b1f77d20942aa75f8429c0f8bc, type: 3}
|
||||||
|
copyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3}
|
||||||
|
screenSpaceShadowPS: {fileID: 0}
|
||||||
|
samplingPS: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3}
|
||||||
|
stencilDeferredPS: {fileID: 4800000, guid: e9155b26e1bc55942a41e518703fe304, type: 3}
|
||||||
|
fallbackErrorPS: {fileID: 4800000, guid: e6e9a19c3678ded42a3bc431ebef7dbd, type: 3}
|
||||||
|
materialErrorPS: {fileID: 4800000, guid: 5fd9a8feb75a4b5894c241777f519d4e, type: 3}
|
||||||
|
coreBlitPS: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3}
|
||||||
|
coreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b,
|
||||||
|
type: 3}
|
||||||
|
cameraMotionVector: {fileID: 4800000, guid: c56b7e0d4c7cb484e959caeeedae9bbf,
|
||||||
|
type: 3}
|
||||||
|
objectMotionVector: {fileID: 4800000, guid: 7b3ede40266cd49a395def176e1bc486,
|
||||||
|
type: 3}
|
||||||
|
m_AssetVersion: 1
|
||||||
|
m_OpaqueLayerMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_TransparentLayerMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_DefaultStencilState:
|
||||||
|
overrideStencilState: 0
|
||||||
|
stencilReference: 0
|
||||||
|
stencilCompareFunction: 8
|
||||||
|
passOperation: 2
|
||||||
|
failOperation: 0
|
||||||
|
zFailOperation: 0
|
||||||
|
m_ShadowTransparentReceive: 1
|
||||||
|
m_RenderingMode: 0
|
||||||
|
m_DepthPrimingMode: 1
|
||||||
|
m_AccurateGbufferNormals: 0
|
||||||
|
m_ClusteredRendering: 0
|
||||||
|
m_TileSize: 32
|
||||||
|
m_IntermediateTextureMode: 0
|
||||||
8
Assets/Settings/URP-Performant-Renderer.asset.meta
Normal file
8
Assets/Settings/URP-Performant-Renderer.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 707360a9c581a4bd7aa53bfeb1429f71
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
69
Assets/Settings/URP-Performant.asset
Normal file
69
Assets/Settings/URP-Performant.asset
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3}
|
||||||
|
m_Name: URP-Performant
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
k_AssetVersion: 9
|
||||||
|
k_AssetPreviousVersion: 9
|
||||||
|
m_RendererType: 1
|
||||||
|
m_RendererData: {fileID: 0}
|
||||||
|
m_RendererDataList:
|
||||||
|
- {fileID: 11400000, guid: 707360a9c581a4bd7aa53bfeb1429f71, type: 2}
|
||||||
|
m_DefaultRendererIndex: 0
|
||||||
|
m_RequireDepthTexture: 0
|
||||||
|
m_RequireOpaqueTexture: 0
|
||||||
|
m_OpaqueDownsampling: 1
|
||||||
|
m_SupportsTerrainHoles: 1
|
||||||
|
m_StoreActionsOptimization: 0
|
||||||
|
m_SupportsHDR: 0
|
||||||
|
m_MSAA: 1
|
||||||
|
m_RenderScale: 1
|
||||||
|
m_MainLightRenderingMode: 1
|
||||||
|
m_MainLightShadowsSupported: 0
|
||||||
|
m_MainLightShadowmapResolution: 1024
|
||||||
|
m_AdditionalLightsRenderingMode: 0
|
||||||
|
m_AdditionalLightsPerObjectLimit: 4
|
||||||
|
m_AdditionalLightShadowsSupported: 0
|
||||||
|
m_AdditionalLightsShadowmapResolution: 512
|
||||||
|
m_AdditionalLightsShadowResolutionTierLow: 128
|
||||||
|
m_AdditionalLightsShadowResolutionTierMedium: 256
|
||||||
|
m_AdditionalLightsShadowResolutionTierHigh: 512
|
||||||
|
m_ReflectionProbeBlending: 0
|
||||||
|
m_ReflectionProbeBoxProjection: 0
|
||||||
|
m_ShadowDistance: 50
|
||||||
|
m_ShadowCascadeCount: 1
|
||||||
|
m_Cascade2Split: 0.25
|
||||||
|
m_Cascade3Split: {x: 0.1, y: 0.3}
|
||||||
|
m_Cascade4Split: {x: 0.067, y: 0.2, z: 0.467}
|
||||||
|
m_CascadeBorder: 0.1
|
||||||
|
m_ShadowDepthBias: 1
|
||||||
|
m_ShadowNormalBias: 1
|
||||||
|
m_SoftShadowsSupported: 0
|
||||||
|
m_AdditionalLightsCookieResolution: 2048
|
||||||
|
m_AdditionalLightsCookieFormat: 3
|
||||||
|
m_UseSRPBatcher: 1
|
||||||
|
m_SupportsDynamicBatching: 0
|
||||||
|
m_MixedLightingSupported: 1
|
||||||
|
m_SupportsLightLayers: 0
|
||||||
|
m_DebugLevel: 0
|
||||||
|
m_UseAdaptivePerformance: 1
|
||||||
|
m_ColorGradingMode: 0
|
||||||
|
m_ColorGradingLutSize: 16
|
||||||
|
m_UseFastSRGBLinearConversion: 0
|
||||||
|
m_ShadowType: 1
|
||||||
|
m_LocalShadowsSupported: 0
|
||||||
|
m_LocalShadowsAtlasResolution: 256
|
||||||
|
m_MaxPixelLights: 0
|
||||||
|
m_ShadowAtlasResolution: 256
|
||||||
|
m_ShaderVariantLogLevel: 0
|
||||||
|
m_VolumeFrameworkUpdateMode: 0
|
||||||
|
m_ShadowCascades: 0
|
||||||
8
Assets/Settings/URP-Performant.asset.meta
Normal file
8
Assets/Settings/URP-Performant.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d0e2fc18fe036412f8223b3b3d9ad574
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Shatalmic.meta
Normal file
8
Assets/Shatalmic.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a9d38ae960f9b493eb7db6968da9b5f0
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
439
Assets/Shatalmic/Demo.unity
Normal file
439
Assets/Shatalmic/Demo.unity
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!29 &1
|
||||||
|
OcclusionCullingSettings:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 2
|
||||||
|
m_OcclusionBakeSettings:
|
||||||
|
smallestOccluder: 5
|
||||||
|
smallestHole: 0.25
|
||||||
|
backfaceThreshold: 100
|
||||||
|
m_SceneGUID: 00000000000000000000000000000000
|
||||||
|
m_OcclusionCullingData: {fileID: 0}
|
||||||
|
--- !u!104 &2
|
||||||
|
RenderSettings:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 9
|
||||||
|
m_Fog: 0
|
||||||
|
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||||
|
m_FogMode: 3
|
||||||
|
m_FogDensity: 0.01
|
||||||
|
m_LinearFogStart: 0
|
||||||
|
m_LinearFogEnd: 300
|
||||||
|
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||||
|
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||||
|
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||||
|
m_AmbientIntensity: 1
|
||||||
|
m_AmbientMode: 3
|
||||||
|
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||||
|
m_SkyboxMaterial: {fileID: 0}
|
||||||
|
m_HaloStrength: 0.5
|
||||||
|
m_FlareStrength: 1
|
||||||
|
m_FlareFadeSpeed: 3
|
||||||
|
m_HaloTexture: {fileID: 0}
|
||||||
|
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
|
m_DefaultReflectionMode: 0
|
||||||
|
m_DefaultReflectionResolution: 128
|
||||||
|
m_ReflectionBounces: 1
|
||||||
|
m_ReflectionIntensity: 1
|
||||||
|
m_CustomReflection: {fileID: 0}
|
||||||
|
m_Sun: {fileID: 0}
|
||||||
|
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
|
m_UseRadianceAmbientProbe: 0
|
||||||
|
--- !u!157 &3
|
||||||
|
LightmapSettings:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 11
|
||||||
|
m_GIWorkflowMode: 1
|
||||||
|
m_GISettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_BounceScale: 1
|
||||||
|
m_IndirectOutputScale: 1
|
||||||
|
m_AlbedoBoost: 1
|
||||||
|
m_EnvironmentLightingMode: 0
|
||||||
|
m_EnableBakedLightmaps: 0
|
||||||
|
m_EnableRealtimeLightmaps: 0
|
||||||
|
m_LightmapEditorSettings:
|
||||||
|
serializedVersion: 10
|
||||||
|
m_Resolution: 2
|
||||||
|
m_BakeResolution: 40
|
||||||
|
m_AtlasSize: 1024
|
||||||
|
m_AO: 0
|
||||||
|
m_AOMaxDistance: 1
|
||||||
|
m_CompAOExponent: 1
|
||||||
|
m_CompAOExponentDirect: 0
|
||||||
|
m_Padding: 2
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_LightmapsBakeMode: 1
|
||||||
|
m_TextureCompression: 1
|
||||||
|
m_FinalGather: 0
|
||||||
|
m_FinalGatherFiltering: 1
|
||||||
|
m_FinalGatherRayCount: 256
|
||||||
|
m_ReflectionCompression: 2
|
||||||
|
m_MixedBakeMode: 2
|
||||||
|
m_BakeBackend: 1
|
||||||
|
m_PVRSampling: 1
|
||||||
|
m_PVRDirectSampleCount: 32
|
||||||
|
m_PVRSampleCount: 500
|
||||||
|
m_PVRBounces: 2
|
||||||
|
m_PVRFilterTypeDirect: 0
|
||||||
|
m_PVRFilterTypeIndirect: 0
|
||||||
|
m_PVRFilterTypeAO: 0
|
||||||
|
m_PVRFilteringMode: 1
|
||||||
|
m_PVRCulling: 1
|
||||||
|
m_PVRFilteringGaussRadiusDirect: 1
|
||||||
|
m_PVRFilteringGaussRadiusIndirect: 5
|
||||||
|
m_PVRFilteringGaussRadiusAO: 2
|
||||||
|
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||||
|
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||||
|
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||||
|
m_ShowResolutionOverlay: 1
|
||||||
|
m_LightingDataAsset: {fileID: 0}
|
||||||
|
m_UseShadowmask: 1
|
||||||
|
--- !u!196 &4
|
||||||
|
NavMeshSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_BuildSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
agentTypeID: 0
|
||||||
|
agentRadius: 0.5
|
||||||
|
agentHeight: 2
|
||||||
|
agentSlope: 45
|
||||||
|
agentClimb: 0.4
|
||||||
|
ledgeDropHeight: 0
|
||||||
|
maxJumpAcrossDistance: 0
|
||||||
|
minRegionArea: 2
|
||||||
|
manualCellSize: 0
|
||||||
|
cellSize: 0.16666667
|
||||||
|
manualTileSize: 0
|
||||||
|
tileSize: 256
|
||||||
|
accuratePlacement: 0
|
||||||
|
debug:
|
||||||
|
m_Flags: 0
|
||||||
|
m_NavMeshData: {fileID: 0}
|
||||||
|
--- !u!1 &53270293
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 53270296}
|
||||||
|
- component: {fileID: 53270295}
|
||||||
|
- component: {fileID: 53270294}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: EventSystem
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &53270294
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 53270293}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_HorizontalAxis: Horizontal
|
||||||
|
m_VerticalAxis: Vertical
|
||||||
|
m_SubmitButton: Submit
|
||||||
|
m_CancelButton: Cancel
|
||||||
|
m_InputActionsPerSecond: 10
|
||||||
|
m_RepeatDelay: 0.5
|
||||||
|
m_ForceModuleActive: 0
|
||||||
|
--- !u!114 &53270295
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 53270293}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_FirstSelected: {fileID: 0}
|
||||||
|
m_sendNavigationEvents: 1
|
||||||
|
m_DragThreshold: 10
|
||||||
|
--- !u!4 &53270296
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 53270293}
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_RootOrder: 1
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &344612123
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 344612127}
|
||||||
|
- component: {fileID: 344612126}
|
||||||
|
- component: {fileID: 344612125}
|
||||||
|
- component: {fileID: 344612124}
|
||||||
|
m_Layer: 5
|
||||||
|
m_Name: Canvas
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &344612124
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 344612123}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_IgnoreReversedGraphics: 1
|
||||||
|
m_BlockingObjects: 0
|
||||||
|
m_BlockingMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
--- !u!114 &344612125
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 344612123}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_UiScaleMode: 1
|
||||||
|
m_ReferencePixelsPerUnit: 100
|
||||||
|
m_ScaleFactor: 1
|
||||||
|
m_ReferenceResolution: {x: 800, y: 600}
|
||||||
|
m_ScreenMatchMode: 0
|
||||||
|
m_MatchWidthOrHeight: 0
|
||||||
|
m_PhysicalUnit: 3
|
||||||
|
m_FallbackScreenDPI: 96
|
||||||
|
m_DefaultSpriteDPI: 96
|
||||||
|
m_DynamicPixelsPerUnit: 1
|
||||||
|
--- !u!223 &344612126
|
||||||
|
Canvas:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 344612123}
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 3
|
||||||
|
m_RenderMode: 0
|
||||||
|
m_Camera: {fileID: 0}
|
||||||
|
m_PlaneDistance: 100
|
||||||
|
m_PixelPerfect: 0
|
||||||
|
m_ReceivesEvents: 1
|
||||||
|
m_OverrideSorting: 0
|
||||||
|
m_OverridePixelPerfect: 0
|
||||||
|
m_SortingBucketNormalizedSize: 0
|
||||||
|
m_AdditionalShaderChannelsFlag: 0
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_TargetDisplay: 0
|
||||||
|
--- !u!224 &344612127
|
||||||
|
RectTransform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 344612123}
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||||
|
m_Children:
|
||||||
|
- {fileID: 601672577}
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_RootOrder: 2
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
m_AnchorMin: {x: 0, y: 0}
|
||||||
|
m_AnchorMax: {x: 0, y: 0}
|
||||||
|
m_AnchoredPosition: {x: 0, y: 0}
|
||||||
|
m_SizeDelta: {x: 0, y: 0}
|
||||||
|
m_Pivot: {x: 0, y: 0}
|
||||||
|
--- !u!1 &601672576
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 601672577}
|
||||||
|
- component: {fileID: 601672579}
|
||||||
|
- component: {fileID: 601672578}
|
||||||
|
m_Layer: 5
|
||||||
|
m_Name: Text
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!224 &601672577
|
||||||
|
RectTransform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 601672576}
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 344612127}
|
||||||
|
m_RootOrder: 0
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
m_AnchorMin: {x: 0, y: 0}
|
||||||
|
m_AnchorMax: {x: 1, y: 1}
|
||||||
|
m_AnchoredPosition: {x: 0, y: 0}
|
||||||
|
m_SizeDelta: {x: 0, y: 0}
|
||||||
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
|
--- !u!114 &601672578
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 601672576}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||||
|
m_RaycastTarget: 1
|
||||||
|
m_OnCullStateChanged:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
|
m_FontData:
|
||||||
|
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
|
m_FontSize: 72
|
||||||
|
m_FontStyle: 0
|
||||||
|
m_BestFit: 0
|
||||||
|
m_MinSize: 7
|
||||||
|
m_MaxSize: 72
|
||||||
|
m_Alignment: 4
|
||||||
|
m_AlignByGeometry: 0
|
||||||
|
m_RichText: 1
|
||||||
|
m_HorizontalOverflow: 0
|
||||||
|
m_VerticalOverflow: 0
|
||||||
|
m_LineSpacing: 1
|
||||||
|
m_Text: Due to the new Unity asset publishing requirements you must follow the instructions
|
||||||
|
in the README file to unpack this plugin.
|
||||||
|
--- !u!222 &601672579
|
||||||
|
CanvasRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 601672576}
|
||||||
|
m_CullTransparentMesh: 0
|
||||||
|
--- !u!1 &1655223073
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1655223076}
|
||||||
|
- component: {fileID: 1655223075}
|
||||||
|
- component: {fileID: 1655223074}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Main Camera
|
||||||
|
m_TagString: MainCamera
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!81 &1655223074
|
||||||
|
AudioListener:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1655223073}
|
||||||
|
m_Enabled: 1
|
||||||
|
--- !u!20 &1655223075
|
||||||
|
Camera:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1655223073}
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 2
|
||||||
|
m_ClearFlags: 1
|
||||||
|
m_BackGroundColor: {r: 1, g: 1, b: 1, a: 0}
|
||||||
|
m_projectionMatrixMode: 1
|
||||||
|
m_SensorSize: {x: 36, y: 24}
|
||||||
|
m_LensShift: {x: 0, y: 0}
|
||||||
|
m_GateFitMode: 2
|
||||||
|
m_FocalLength: 50
|
||||||
|
m_NormalizedViewPortRect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: 1
|
||||||
|
height: 1
|
||||||
|
near clip plane: 0.3
|
||||||
|
far clip plane: 1000
|
||||||
|
field of view: 60
|
||||||
|
orthographic: 1
|
||||||
|
orthographic size: 5
|
||||||
|
m_Depth: -1
|
||||||
|
m_CullingMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
m_RenderingPath: -1
|
||||||
|
m_TargetTexture: {fileID: 0}
|
||||||
|
m_TargetDisplay: 0
|
||||||
|
m_TargetEye: 3
|
||||||
|
m_HDR: 1
|
||||||
|
m_AllowMSAA: 1
|
||||||
|
m_AllowDynamicResolution: 0
|
||||||
|
m_ForceIntoRT: 0
|
||||||
|
m_OcclusionCulling: 1
|
||||||
|
m_StereoConvergence: 10
|
||||||
|
m_StereoSeparation: 0.022
|
||||||
|
--- !u!4 &1655223076
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1655223073}
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: -10}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_RootOrder: 0
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
7
Assets/Shatalmic/Demo.unity.meta
Normal file
7
Assets/Shatalmic/Demo.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0149d806ed996492787b9cb44993fd5b
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
5
Assets/Shatalmic/Documentation.meta
Normal file
5
Assets/Shatalmic/Documentation.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e909e63cd2d08421b8eb20bd0f6bd293
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9d0ae6162b1584e1c831a59e1ca00807
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6afd67adfd6b847968ad237eddbd5f2e
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
5
Assets/Shatalmic/Editor.meta
Normal file
5
Assets/Shatalmic/Editor.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 87247c18c42564aceaf454206a3ef873
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
29
Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs
Normal file
29
Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#if UNITY_IOS
|
||||||
|
using UnityEditor.Callbacks;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.iOS.Xcode;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
public class BluetoothPostProcessBuild
|
||||||
|
{
|
||||||
|
[PostProcessBuild]
|
||||||
|
public static void ChangeXcodePlist(BuildTarget buildTarget, string pathToBuiltProject)
|
||||||
|
{
|
||||||
|
if (buildTarget == BuildTarget.iOS)
|
||||||
|
{
|
||||||
|
// Get plist
|
||||||
|
string plistPath = pathToBuiltProject + "/Info.plist";
|
||||||
|
PlistDocument plist = new PlistDocument();
|
||||||
|
plist.ReadFromString(File.ReadAllText(plistPath));
|
||||||
|
|
||||||
|
// Get root
|
||||||
|
PlistElementDict rootDict = plist.root;
|
||||||
|
|
||||||
|
rootDict.SetString("NSBluetoothAlwaysUsageDescription", "Uses BLE to communicate with devices.");
|
||||||
|
|
||||||
|
// Write to file
|
||||||
|
File.WriteAllText(plistPath, plist.WriteToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
12
Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs.meta
Normal file
12
Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1695b640ed1354053a8cdd9e0186f57f
|
||||||
|
timeCreated: 1493664761
|
||||||
|
licenseType: Pro
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
93
Assets/Shatalmic/Editor/PostprocessBuildPlayer
Executable file
93
Assets/Shatalmic/Editor/PostprocessBuildPlayer
Executable file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
use File::Copy;
|
||||||
|
|
||||||
|
print ("\n*** Starting PostprocessBuildPlayer ***\n");
|
||||||
|
|
||||||
|
my $installPath = $ARGV[0];
|
||||||
|
|
||||||
|
#values to watch for already existing
|
||||||
|
my $requiredDeviceCapsString = "UIRequiredDeviceCapabilities";
|
||||||
|
my $backgroundModesString = "UIBackgroundModes";
|
||||||
|
my $bluetoothLEString = "bluetooth-le";
|
||||||
|
|
||||||
|
#go through the info.plist file line by line until you find this one:
|
||||||
|
my $endOfPlist = "</dict>";
|
||||||
|
|
||||||
|
# The type of player built:
|
||||||
|
# "dashboard", "standaloneWin32", "standaloneOSXIntel", "standaloneOSXPPC", "standaloneOSXUniversal", "webplayer", "iPhone"
|
||||||
|
my $target = $ARGV[1];
|
||||||
|
|
||||||
|
print ("\n*** PostprocessBuildPlayer - Building at '$installPath' with target: $target ***\n");
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# This modifies info.plist to add the external accessory #
|
||||||
|
# entries #
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
#open this file
|
||||||
|
|
||||||
|
$oplistPath = $installPath."/Info.plist";
|
||||||
|
$nplistPath = $installPath."/Info.plist.tmp";
|
||||||
|
|
||||||
|
open OLDPLIST, "<", $oplistPath or die("Cannot open Info.plist");
|
||||||
|
open NEWPLIST, ">", $nplistPath or die("Cannot create new Info.plist");
|
||||||
|
|
||||||
|
my $nextLine = 0;
|
||||||
|
my $requiredDeviceCapsFound = 0;
|
||||||
|
my $backgroundModesFound = 0;
|
||||||
|
|
||||||
|
while(<OLDPLIST>)
|
||||||
|
{
|
||||||
|
if ($nextLine == 1)
|
||||||
|
{
|
||||||
|
if ($_ =~ m/$bluetoothLEString/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$_ =~ s|</array>|\n\t<string>bluetooth-le</string></array>|
|
||||||
|
}
|
||||||
|
|
||||||
|
$nextLine = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Check for required device caps string already exists #
|
||||||
|
################################################################
|
||||||
|
if ($_ =~ m/$requiredDeviceCapsString/ )
|
||||||
|
{
|
||||||
|
$nextLine = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Check for background modes string already exists #
|
||||||
|
################################################################
|
||||||
|
if ($_ =~ m/$backgroundModesString/ )
|
||||||
|
{
|
||||||
|
$backgroundModesFound = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Add any key/value pairs you want at the end of Info.plist #
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
if ($_ =~ m/$endOfPlist/ && $backgrounModesFound == 0)
|
||||||
|
{
|
||||||
|
my $keys = "";
|
||||||
|
|
||||||
|
$keys .= " <key>UIBackgroundModes</key>\n";
|
||||||
|
$keys .= " <array>\n";
|
||||||
|
$keys .= " <string>bluetooth-central</string>\n";
|
||||||
|
$keys .= " <string>bluetooth-peripheral</string>\n";
|
||||||
|
$keys .= " </array>\n";
|
||||||
|
|
||||||
|
$_ = $keys . $_;
|
||||||
|
}
|
||||||
|
|
||||||
|
print NEWPLIST $_;
|
||||||
|
}
|
||||||
|
|
||||||
|
close OLDPLIST;
|
||||||
|
close NEWPLIST;
|
||||||
|
|
||||||
|
`mv \'$nplistPath\' \'$oplistPath\'`;
|
||||||
9
Assets/Shatalmic/Editor/PostprocessBuildPlayer.meta
Normal file
9
Assets/Shatalmic/Editor/PostprocessBuildPlayer.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3c726eecf85fe4a88a7e7d57812ff455
|
||||||
|
timeCreated: 1515198229
|
||||||
|
licenseType: Pro
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
5
Assets/Shatalmic/Example.meta
Normal file
5
Assets/Shatalmic/Example.meta
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a0f9eeb35659740279848981d7e33d41
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
8
Assets/Shatalmic/Example/ArduinoHM10Test.meta
Normal file
8
Assets/Shatalmic/Example/ArduinoHM10Test.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e20f0ba75b1964e6f91e927256eb6d22
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
273
Assets/Shatalmic/Example/ArduinoHM10Test/ArduinoHM10Test.cs
Normal file
273
Assets/Shatalmic/Example/ArduinoHM10Test/ArduinoHM10Test.cs
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
/* This is an example to show how to connect to 2 HM-10 devices
|
||||||
|
* that are connected together via their serial pins and send data
|
||||||
|
* back and forth between them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
public class ArduinoHM10Test : MonoBehaviour
|
||||||
|
{
|
||||||
|
public string DeviceName = "DSD TECH";
|
||||||
|
public string ServiceUUID = "FFE0";
|
||||||
|
public string Characteristic = "FFE1";
|
||||||
|
|
||||||
|
public Text HM10_Status;
|
||||||
|
public Text BluetoothStatus;
|
||||||
|
public GameObject PanelMiddle;
|
||||||
|
public Text TextToSend;
|
||||||
|
|
||||||
|
enum States
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Scan,
|
||||||
|
Connect,
|
||||||
|
RequestMTU,
|
||||||
|
Subscribe,
|
||||||
|
Unsubscribe,
|
||||||
|
Disconnect,
|
||||||
|
Communication,
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _workingFoundDevice = true;
|
||||||
|
private bool _connected = false;
|
||||||
|
private float _timeout = 0f;
|
||||||
|
private States _state = States.None;
|
||||||
|
private bool _foundID = false;
|
||||||
|
|
||||||
|
// this is our hm10 device
|
||||||
|
private string _hm10;
|
||||||
|
|
||||||
|
public void OnButton(Button button)
|
||||||
|
{
|
||||||
|
if (button.name.Contains ("Send"))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty (TextToSend.text))
|
||||||
|
{
|
||||||
|
BluetoothStatus.text = "Enter text to send...";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendString (TextToSend.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (button.name.Contains("Toggle"))
|
||||||
|
{
|
||||||
|
SendByte (0x01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset ()
|
||||||
|
{
|
||||||
|
_workingFoundDevice = false; // used to guard against trying to connect to a second device while still connecting to the first
|
||||||
|
_connected = false;
|
||||||
|
_timeout = 0f;
|
||||||
|
_state = States.None;
|
||||||
|
_foundID = false;
|
||||||
|
_hm10 = null;
|
||||||
|
PanelMiddle.SetActive (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetState (States newState, float timeout)
|
||||||
|
{
|
||||||
|
_state = newState;
|
||||||
|
_timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartProcess ()
|
||||||
|
{
|
||||||
|
BluetoothStatus.text = "Initializing...";
|
||||||
|
|
||||||
|
Reset ();
|
||||||
|
BluetoothLEHardwareInterface.Initialize (true, false, () => {
|
||||||
|
|
||||||
|
SetState (States.Scan, 0.1f);
|
||||||
|
BluetoothStatus.text = "Initialized";
|
||||||
|
|
||||||
|
}, (error) => {
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.Log ("Error: " + error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use this for initialization
|
||||||
|
void Start ()
|
||||||
|
{
|
||||||
|
HM10_Status.text = "";
|
||||||
|
|
||||||
|
StartProcess ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is called once per frame
|
||||||
|
void Update ()
|
||||||
|
{
|
||||||
|
if (_timeout > 0f)
|
||||||
|
{
|
||||||
|
_timeout -= Time.deltaTime;
|
||||||
|
if (_timeout <= 0f)
|
||||||
|
{
|
||||||
|
_timeout = 0f;
|
||||||
|
|
||||||
|
switch (_state)
|
||||||
|
{
|
||||||
|
case States.None:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case States.Scan:
|
||||||
|
BluetoothStatus.text = "Scanning for HM10 devices...";
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices (null, (address, name) => {
|
||||||
|
|
||||||
|
// we only want to look at devices that have the name we are looking for
|
||||||
|
// this is the best way to filter out devices
|
||||||
|
if (name.Contains (DeviceName))
|
||||||
|
{
|
||||||
|
_workingFoundDevice = true;
|
||||||
|
|
||||||
|
// it is always a good idea to stop scanning while you connect to a device
|
||||||
|
// and get things set up
|
||||||
|
BluetoothLEHardwareInterface.StopScan ();
|
||||||
|
BluetoothStatus.text = "";
|
||||||
|
|
||||||
|
// add it to the list and set to connect to it
|
||||||
|
_hm10 = address;
|
||||||
|
|
||||||
|
HM10_Status.text = "Found HM10";
|
||||||
|
|
||||||
|
SetState (States.Connect, 0.5f);
|
||||||
|
|
||||||
|
_workingFoundDevice = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}, null, false, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case States.Connect:
|
||||||
|
// set these flags
|
||||||
|
_foundID = false;
|
||||||
|
|
||||||
|
HM10_Status.text = "Connecting to HM10";
|
||||||
|
|
||||||
|
// note that the first parameter is the address, not the name. I have not fixed this because
|
||||||
|
// of backwards compatiblity.
|
||||||
|
// also note that I am note using the first 2 callbacks. If you are not looking for specific characteristics you can use one of
|
||||||
|
// the first 2, but keep in mind that the device will enumerate everything and so you will want to have a timeout
|
||||||
|
// large enough that it will be finished enumerating before you try to subscribe or do any other operations.
|
||||||
|
BluetoothLEHardwareInterface.ConnectToPeripheral (_hm10, null, null, (address, serviceUUID, characteristicUUID) => {
|
||||||
|
|
||||||
|
if (IsEqual (serviceUUID, ServiceUUID))
|
||||||
|
{
|
||||||
|
// if we have found the characteristic that we are waiting for
|
||||||
|
// set the state. make sure there is enough timeout that if the
|
||||||
|
// device is still enumerating other characteristics it finishes
|
||||||
|
// before we try to subscribe
|
||||||
|
if (IsEqual (characteristicUUID, Characteristic))
|
||||||
|
{
|
||||||
|
_connected = true;
|
||||||
|
SetState (States.RequestMTU, 2f);
|
||||||
|
|
||||||
|
HM10_Status.text = "Connected to HM10";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (disconnectedAddress) => {
|
||||||
|
BluetoothLEHardwareInterface.Log ("Device disconnected: " + disconnectedAddress);
|
||||||
|
HM10_Status.text = "Disconnected";
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case States.RequestMTU:
|
||||||
|
HM10_Status.text = "Requesting MTU";
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.RequestMtu(_hm10, 185, (address, newMTU) =>
|
||||||
|
{
|
||||||
|
HM10_Status.text = "MTU set to " + newMTU.ToString();
|
||||||
|
|
||||||
|
SetState(States.Subscribe, 0.1f);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case States.Subscribe:
|
||||||
|
HM10_Status.text = "Subscribing to HM10";
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress (_hm10, ServiceUUID, Characteristic, null, (address, characteristicUUID, bytes) => {
|
||||||
|
|
||||||
|
HM10_Status.text = "Received Serial: " + Encoding.UTF8.GetString (bytes);
|
||||||
|
});
|
||||||
|
|
||||||
|
// set to the none state and the user can start sending and receiving data
|
||||||
|
_state = States.None;
|
||||||
|
HM10_Status.text = "Waiting...";
|
||||||
|
|
||||||
|
PanelMiddle.SetActive (true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case States.Unsubscribe:
|
||||||
|
BluetoothLEHardwareInterface.UnSubscribeCharacteristic (_hm10, ServiceUUID, Characteristic, null);
|
||||||
|
SetState (States.Disconnect, 4f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case States.Disconnect:
|
||||||
|
if (_connected)
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.DisconnectPeripheral (_hm10, (address) => {
|
||||||
|
BluetoothLEHardwareInterface.DeInitialize (() => {
|
||||||
|
|
||||||
|
_connected = false;
|
||||||
|
_state = States.None;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BluetoothLEHardwareInterface.DeInitialize (() => {
|
||||||
|
|
||||||
|
_state = States.None;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string FullUUID (string uuid)
|
||||||
|
{
|
||||||
|
return "0000" + uuid + "-0000-1000-8000-00805F9B34FB";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsEqual(string uuid1, string uuid2)
|
||||||
|
{
|
||||||
|
if (uuid1.Length == 4)
|
||||||
|
uuid1 = FullUUID (uuid1);
|
||||||
|
if (uuid2.Length == 4)
|
||||||
|
uuid2 = FullUUID (uuid2);
|
||||||
|
|
||||||
|
return (uuid1.ToUpper().Equals(uuid2.ToUpper()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendString(string value)
|
||||||
|
{
|
||||||
|
var data = Encoding.UTF8.GetBytes (value);
|
||||||
|
// notice that the 6th parameter is false. this is because the HM10 doesn't support withResponse writing to its characteristic.
|
||||||
|
// some devices do support this setting and it is prefered when they do so that you can know for sure the data was received by
|
||||||
|
// the device
|
||||||
|
BluetoothLEHardwareInterface.WriteCharacteristic (_hm10, ServiceUUID, Characteristic, data, data.Length, false, (characteristicUUID) => {
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.Log ("Write Succeeded");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendByte (byte value)
|
||||||
|
{
|
||||||
|
byte[] data = new byte[] { value };
|
||||||
|
// notice that the 6th parameter is false. this is because the HM10 doesn't support withResponse writing to its characteristic.
|
||||||
|
// some devices do support this setting and it is prefered when they do so that you can know for sure the data was received by
|
||||||
|
// the device
|
||||||
|
BluetoothLEHardwareInterface.WriteCharacteristic (_hm10, ServiceUUID, Characteristic, data, data.Length, false, (characteristicUUID) => {
|
||||||
|
|
||||||
|
BluetoothLEHardwareInterface.Log ("Write Succeeded");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c177b6c23895344cabf54b99d00b3bbf
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
54
Assets/Shatalmic/Example/ArduinoHM10Test/ArduinoHM10Test.ino
Normal file
54
Assets/Shatalmic/Example/ArduinoHM10Test/ArduinoHM10Test.ino
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Arduino HM10 Bluetooth Test for Unity Plugin
|
||||||
|
Language: Wiring/Arduino
|
||||||
|
|
||||||
|
This program waits for input from the Unity app and reflects any strings back
|
||||||
|
to that app. If this app receives a single byte of 0x01 it will toggle the led
|
||||||
|
|
||||||
|
The circuit:
|
||||||
|
- connect HM10 BLE device to serial RX/TX lines
|
||||||
|
|
||||||
|
created 05 July 2018
|
||||||
|
by Tony Pitman
|
||||||
|
|
||||||
|
http://www.shatalmic.com/arduino-hm10-test
|
||||||
|
*/
|
||||||
|
|
||||||
|
int inByte = 0; // incoming serial byte
|
||||||
|
boolean ledStatus = false;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// start serial port at 9600 bps and wait for port to open:
|
||||||
|
Serial.begin(9600);
|
||||||
|
while (!Serial) {
|
||||||
|
; // wait for serial port to connect. Needed for native USB port only
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize digital pin LED_BUILTIN as an output.
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
digitalWrite(LED_BUILTIN, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// if we get a valid byte, read analog ins:
|
||||||
|
if (Serial.available() > 0) {
|
||||||
|
// get incoming byte:
|
||||||
|
inByte = Serial.read();
|
||||||
|
if (inByte == 0x01) {
|
||||||
|
if (ledStatus) {
|
||||||
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
|
Serial.print("Setting LED On");
|
||||||
|
ledStatus = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
digitalWrite(LED_BUILTIN, LOW);
|
||||||
|
Serial.print("Setting LED Off");
|
||||||
|
ledStatus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.write(inByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 116bc671bb8af49ecbca96eb04cfd24a
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
1548
Assets/Shatalmic/Example/ArduinoHM10Test/ArduinoHM10Test.unity
Normal file
1548
Assets/Shatalmic/Example/ArduinoHM10Test/ArduinoHM10Test.unity
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d8c17b59e0cb44d9aa604b9b8c326e9f
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Shatalmic/Example/ArduinoHM10Test/Pictures.meta
Normal file
8
Assets/Shatalmic/Example/ArduinoHM10Test/Pictures.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e398d089f71cf4f0ba4c14f839e78f96
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 259 KiB |
@@ -0,0 +1,84 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f04056b056bf3429ca31b53e4acc95e7
|
||||||
|
TextureImporter:
|
||||||
|
fileIDToRecycleName: {}
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 5
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 0
|
||||||
|
sRGBTexture: 1
|
||||||
|
linearTexture: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapsPreserveCoverage: 0
|
||||||
|
alphaTestReferenceValue: 0.5
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: 0.25
|
||||||
|
normalMapFilter: 0
|
||||||
|
isReadable: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 6
|
||||||
|
cubemapConvolution: 0
|
||||||
|
seamlessCubemap: 0
|
||||||
|
textureFormat: 1
|
||||||
|
maxTextureSize: 2048
|
||||||
|
textureSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
filterMode: -1
|
||||||
|
aniso: -1
|
||||||
|
mipBias: -1
|
||||||
|
wrapU: 1
|
||||||
|
wrapV: 1
|
||||||
|
wrapW: 1
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: 0.5, y: 0.5}
|
||||||
|
spritePixelsToUnits: 100
|
||||||
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
spriteGenerateFallbackPhysicsShape: 1
|
||||||
|
alphaUsage: 1
|
||||||
|
alphaIsTransparency: 1
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 8
|
||||||
|
textureShape: 1
|
||||||
|
singleChannelComponent: 0
|
||||||
|
maxTextureSizeSet: 0
|
||||||
|
compressionQualitySet: 0
|
||||||
|
textureFormatSet: 0
|
||||||
|
platformSettings:
|
||||||
|
- serializedVersion: 2
|
||||||
|
buildTarget: DefaultTexturePlatform
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 13b6775d0e8fe4a559994b06f3e90b76
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
spritePackingTag:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user