修改
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.Editor
|
||||
{
|
||||
public class ChangeDeviceMode
|
||||
{
|
||||
public static DeviceMode DeviceMode
|
||||
{
|
||||
get => _deviceMode;
|
||||
set => SetDeviceMode(value);
|
||||
}
|
||||
|
||||
private static DeviceMode _deviceMode;
|
||||
|
||||
private const string EditorRokidMode = "Evo/Schema/ChangeDevice/Rokid";
|
||||
private const string EditorXrealMode = "Evo/Schema/ChangeDevice/Xreal";
|
||||
|
||||
[MenuItem(EditorRokidMode)]
|
||||
private static void SetRokidMode() => SetDeviceMode(DeviceMode.Evo_Rokid);
|
||||
|
||||
[MenuItem(EditorXrealMode)]
|
||||
private static void SetXrealMode() => SetDeviceMode(DeviceMode.Evo_Xreal);
|
||||
|
||||
// [MenuItem(WebPlayMode)]
|
||||
// private static void SetWebMode() => SetPlayerMode(HotUpdateMode.WEB_PLAYMODE);
|
||||
|
||||
[MenuItem(EditorRokidMode, true)]
|
||||
private static bool ValidateModeMenu()
|
||||
{
|
||||
string platform = CustomEditorPrefs.GetString("ChangeDeviceSchema");
|
||||
Menu.SetChecked(EditorRokidMode, platform == DeviceMode.Evo_Rokid.ToString());
|
||||
Menu.SetChecked(EditorXrealMode, platform == DeviceMode.Evo_Xreal.ToString());
|
||||
//Menu.SetChecked(WebPlayMode, platform == HotUpdateMode.WEB_PLAYMODE.ToString());
|
||||
Debug.Log($"ChangeDeviceSchema:{platform}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SetDeviceMode(DeviceMode mode)
|
||||
{
|
||||
// 清除所有旧模式定义
|
||||
var currentTarget = EditorUserBuildSettings.selectedBuildTargetGroup;
|
||||
if (currentTarget == BuildTargetGroup.Unknown) return;
|
||||
|
||||
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTarget)
|
||||
.Split(';')
|
||||
.Where(d => !Enum.GetNames(typeof(DeviceMode)).Contains(d))
|
||||
.ToList();
|
||||
|
||||
// 添加新模式
|
||||
defines.Add(mode.ToString());
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(currentTarget, string.Join(";", defines));
|
||||
_deviceMode = mode;
|
||||
CustomEditorPrefs.SetString("ChangeDeviceSchema", _deviceMode.ToString());
|
||||
|
||||
ValidateModeMenu();
|
||||
AssetDatabase.Refresh();
|
||||
// 添加解决方案文件重新生成逻辑
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
|
||||
Debug.Log($"当前编译符号: {string.Join(";", defines)}"); // 添加调试日志
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum DeviceMode
|
||||
{
|
||||
//Xreal,
|
||||
Evo_Xreal,
|
||||
//Rokid,
|
||||
Evo_Rokid,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b531f86493d647afbf2f37c523e62bcb
|
||||
timeCreated: 1774426824
|
||||
@@ -34,7 +34,7 @@ namespace Stary.Evo.Editor
|
||||
Menu.SetChecked(EditorNotUpdateMode, platform == HotUpdateMode.NotUpdate.ToString());
|
||||
Menu.SetChecked(EditorHotUpdateMode, platform == HotUpdateMode.HotUpdate.ToString());
|
||||
//Menu.SetChecked(WebPlayMode, platform == HotUpdateMode.WEB_PLAYMODE.ToString());
|
||||
Debug.LogError("ChangeHotUpdateSchema:"+platform);
|
||||
Debug.Log("ChangeHotUpdateSchema:"+platform);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Stary.Evo.Editor
|
||||
Menu.SetChecked(HostPlayMode, platform == PLayerMode.HOST_PLAYMODE.ToString());
|
||||
Menu.SetChecked(LocalPlayMode, platform == PLayerMode.LOCAL_PLAYMODE.ToString());
|
||||
//Menu.SetChecked(WebPlayMode, platform == PLayerMode.WEB_PLAYMODE.ToString());
|
||||
Debug.LogError($"ChangePlayerSchema:{platform}");
|
||||
Debug.Log($"ChangePlayerSchema:{platform}");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Stary.Evo.Editor
|
||||
Menu.SetChecked(EditorNotMode, platform == PointClondMode.NotPointClond.ToString());
|
||||
Menu.SetChecked(EditorImmersalMode, platform == PointClondMode.Immersal.ToString());
|
||||
//Menu.SetChecked(WebPlayMode, platform == HotUpdateMode.WEB_PLAYMODE.ToString());
|
||||
Debug.LogError("ChangePoindClondSchema");
|
||||
Debug.Log($"ChangePoindClondSchema:{platform}");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@ namespace Stary.Evo
|
||||
//所以为了防止quitting和update两个回调被重复添加,需要先移除后添加
|
||||
EditorApplication.quitting -= OnEditorQuit;
|
||||
EditorApplication.quitting += OnEditorQuit;
|
||||
|
||||
Debug.Log(" 自动运行 ");
|
||||
|
||||
|
||||
if (!CustomEditorPrefs.HasKey("StartUp"))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "com.staryevo.tools",
|
||||
"version": "1.3.27",
|
||||
"version": "1.3.28",
|
||||
"displayName": "00.StaryEvo.Tools",
|
||||
"description": "This is an Framework package(后台服务器版本,端口9527)",
|
||||
"unity": "2021.3",
|
||||
|
||||
@@ -4,6 +4,7 @@ using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.RKTools
|
||||
{
|
||||
#if Evo_Rokid
|
||||
[CustomEditor(typeof(ARTrackedImageEvoObj))]
|
||||
public class ARTrackedImageEvoObjEditor : UnityEditor.Editor
|
||||
{
|
||||
@@ -49,4 +50,5 @@ namespace Stary.Evo.RKTools
|
||||
base.OnInspectorGUI();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -2,17 +2,8 @@
|
||||
"name": "com.rktools.editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"com.rktools.runtime",
|
||||
"com.audiocore.runtime",
|
||||
"com.stary.evo.runtime",
|
||||
"com.staryevo.tools.runtime",
|
||||
"DOTween.Modules",
|
||||
"HybridCLR.Editor",
|
||||
"HybridCLR.Runtime",
|
||||
"ImmersalSDK",
|
||||
"Informationsave.runtime",
|
||||
"UniTask",
|
||||
"com.staryevo.tools.editor"
|
||||
"GUID:10c9b58b77ad42b4193e2a393b1a9899",
|
||||
"GUID:5c6af7aee383dac46b510d588d1b015d"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Rokid.UXR.Interaction;
|
||||
using Stary.Evo;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.RKTools
|
||||
@@ -19,7 +17,6 @@ namespace Stary.Evo.RKTools
|
||||
float TouchScale = 1.2f)
|
||||
{
|
||||
Collider ObjectCollider = Touchedobject.GetComponent<Collider>();
|
||||
GrabInteractable GrabInteractable = Touchedobject.GetComponent<GrabInteractable>();
|
||||
|
||||
// 原有的组件设置优先级最高
|
||||
if (ObjectCollider == null)
|
||||
@@ -27,15 +24,33 @@ namespace Stary.Evo.RKTools
|
||||
ObjectCollider = Touchedobject.AddComponent<BoxCollider>();
|
||||
ObjectCollider.isTrigger = true;
|
||||
}
|
||||
#if Evo_Rokid
|
||||
var GrabInteractable = Touchedobject.GetComponent<Rokid.UXR.Interaction.GrabInteractable>();
|
||||
|
||||
if (GrabInteractable == null)
|
||||
{
|
||||
GrabInteractable = Touchedobject.AddComponent<GrabInteractable>();
|
||||
GrabInteractable = Touchedobject.AddComponent<Rokid.UXR.Interaction.GrabInteractable>();
|
||||
GrabInteractable.rate = TouchScale;
|
||||
}
|
||||
GrabInteractable.OnHoverBegin.AddListener(() => TouchEvent?.Invoke(Touchedobject));
|
||||
#elif Evo_Xreal
|
||||
var GrabInteractable =
|
||||
Touchedobject.GetComponent<UnityEngine.XR.Interaction.Toolkit.Interactables.XRGrabInteractable>();
|
||||
|
||||
if (GrabInteractable == null)
|
||||
{
|
||||
GrabInteractable =
|
||||
Touchedobject.AddComponent<UnityEngine.XR.Interaction.Toolkit.Interactables.XRGrabInteractable>();
|
||||
GrabInteractable.smoothScale = true;
|
||||
GrabInteractable.smoothScaleAmount = TouchScale; // 低数值 = 高惯性
|
||||
GrabInteractable.tightenScale = 0.1f; // 强平滑效果
|
||||
}
|
||||
GrabInteractable.throwOnDetach = false;
|
||||
GrabInteractable.GetComponent<Rigidbody>().useGravity = false;
|
||||
GrabInteractable.hoverEntered.AddListener((a) => TouchEvent?.Invoke(Touchedobject));
|
||||
|
||||
ObjectCollider.enabled = true;
|
||||
GrabInteractable.OnHoverBegin.AddListener(() => TouchEvent?.Invoke(Touchedobject));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -70,13 +85,25 @@ namespace Stary.Evo.RKTools
|
||||
/// <param name="Touchedobject">被触碰物体</param>
|
||||
public static void ObjectRemoveTouchEvent(this GameObject Touchedobject)
|
||||
{
|
||||
GrabInteractable GrabInteractable = Touchedobject.GetComponent<GrabInteractable>();
|
||||
#if Evo_Rokid
|
||||
Rokid.UXR.Interaction.GrabInteractable GrabInteractable =
|
||||
Touchedobject.GetComponent<Rokid.UXR.Interaction.GrabInteractable>();
|
||||
|
||||
if (GrabInteractable != null)
|
||||
{
|
||||
GrabInteractable.OnHoverBegin.RemoveAllListeners();
|
||||
Object.Destroy(GrabInteractable);
|
||||
}
|
||||
#elif Evo_Xreal
|
||||
var GrabInteractable =
|
||||
Touchedobject.GetComponent<UnityEngine.XR.Interaction.Toolkit.Interactables.XRGrabInteractable>();
|
||||
|
||||
if (GrabInteractable != null)
|
||||
{
|
||||
GrabInteractable.hoverEntered.RemoveAllListeners();
|
||||
Object.Destroy(GrabInteractable);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -91,10 +118,12 @@ namespace Stary.Evo.RKTools
|
||||
public static void objectAddDrag(this GameObject Dragedobject, System.Action<GameObject> DragingEvent = null,
|
||||
float DragScale = 1.2f)
|
||||
{
|
||||
#if Evo_Rokid
|
||||
Collider ObjectCollider = Dragedobject.GetComponent<Collider>();
|
||||
GrabInteractable GrabInteractable = Dragedobject.GetComponent<GrabInteractable>();
|
||||
Rokid.UXR.Interaction.GrabInteractable GrabInteractable =
|
||||
Dragedobject.GetComponent<Rokid.UXR.Interaction.GrabInteractable>();
|
||||
Rigidbody Rigidbody = Dragedobject.GetComponent<Rigidbody>();
|
||||
Throwable Throwable = Dragedobject.GetComponent<Throwable>();
|
||||
Rokid.UXR.Interaction.Throwable Throwable = Dragedobject.GetComponent<Rokid.UXR.Interaction.Throwable>();
|
||||
|
||||
// 原有的组件设置优先级最高
|
||||
if (ObjectCollider == null)
|
||||
@@ -105,7 +134,7 @@ namespace Stary.Evo.RKTools
|
||||
|
||||
if (GrabInteractable == null)
|
||||
{
|
||||
GrabInteractable = Dragedobject.AddComponent<GrabInteractable>();
|
||||
GrabInteractable = Dragedobject.AddComponent<Rokid.UXR.Interaction.GrabInteractable>();
|
||||
GrabInteractable.rate = DragScale;
|
||||
}
|
||||
|
||||
@@ -117,10 +146,27 @@ namespace Stary.Evo.RKTools
|
||||
|
||||
if (Throwable == null)
|
||||
{
|
||||
Throwable = Dragedobject.AddComponent<Throwable>();
|
||||
Throwable = Dragedobject.AddComponent<Rokid.UXR.Interaction.Throwable>();
|
||||
}
|
||||
|
||||
GrabInteractable.OnHeldUpdate.AddListener(() => DragingEvent?.Invoke(Dragedobject));
|
||||
#elif Evo_Xreal
|
||||
var Throwable =
|
||||
Dragedobject.GetComponent<UnityEngine.XR.Interaction.Toolkit.Interactables.XRGrabInteractable>();
|
||||
|
||||
if (Throwable == null)
|
||||
{
|
||||
Throwable =
|
||||
Dragedobject.AddComponent<UnityEngine.XR.Interaction.Toolkit.Interactables.XRGrabInteractable>();
|
||||
Throwable.throwOnDetach = true;
|
||||
Throwable.throwVelocityScale = DragScale; // 低数值 = 高惯性
|
||||
}
|
||||
|
||||
Throwable.GetComponent<Rigidbody>().useGravity = false;
|
||||
Throwable.selectEntered.AddListener((a) => DragingEvent?.Invoke(Dragedobject));
|
||||
|
||||
Throwable.enabled = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -145,14 +191,26 @@ namespace Stary.Evo.RKTools
|
||||
|
||||
public static void objectRemoveDrag(this GameObject Dragedobject)
|
||||
{
|
||||
Throwable Throwable = Dragedobject.GetComponent<Throwable>();
|
||||
GrabInteractable GrabInteractable = Dragedobject.GetComponent<GrabInteractable>();
|
||||
#if Evo_Rokid
|
||||
Rokid.UXR.Interaction.Throwable Throwable = Dragedobject.GetComponent<Rokid.UXR.Interaction.Throwable>();
|
||||
Rokid.UXR.Interaction.GrabInteractable GrabInteractable =
|
||||
Dragedobject.GetComponent<Rokid.UXR.Interaction.GrabInteractable>();
|
||||
if (Throwable != null) Object.Destroy(Throwable);
|
||||
if (GrabInteractable != null)
|
||||
{
|
||||
GrabInteractable.OnHeldUpdate.RemoveAllListeners();
|
||||
Object.Destroy(GrabInteractable);
|
||||
}
|
||||
#elif Evo_Xreal
|
||||
var Throwable =
|
||||
Dragedobject.GetComponent<UnityEngine.XR.Interaction.Toolkit.Interactables.XRGrabInteractable>();
|
||||
|
||||
if (Throwable != null)
|
||||
{
|
||||
Throwable.selectEntered.RemoveAllListeners();
|
||||
Object.Destroy(Throwable);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Rokid.UXR.Interaction;
|
||||
|
||||
namespace Stary.Evo.RKTools
|
||||
{
|
||||
public abstract class GestureBase : MonoBehaviour
|
||||
{
|
||||
private GestureBean leftBean = null;
|
||||
private GestureBean rightBean = null;
|
||||
|
||||
protected void OnEnable()
|
||||
{
|
||||
GesEventInput.OnTrackedSuccess += OnTrackedSuccess;
|
||||
GesEventInput.OnTrackedFailed += OnTrackedFailed;
|
||||
GesEventInput.OnRenderHand += OnRenderHand;
|
||||
}
|
||||
|
||||
protected void OnDisable()
|
||||
{
|
||||
GesEventInput.OnTrackedSuccess -= OnTrackedSuccess;
|
||||
GesEventInput.OnTrackedFailed -= OnTrackedFailed;
|
||||
GesEventInput.OnRenderHand -= OnRenderHand;
|
||||
}
|
||||
|
||||
protected void FixedUpdate()
|
||||
{
|
||||
if (leftBean != null)
|
||||
{
|
||||
GestureType type = GesEventInput.Instance.GetGestureType(HandType.LeftHand);
|
||||
Vector3 handForward = GetSkeletonPose(SkeletonIndexFlag.PALM, HandType.LeftHand).forward;
|
||||
GestureLeftSuccess(type, handForward);
|
||||
}
|
||||
else
|
||||
{
|
||||
GestureLeftFail();
|
||||
}
|
||||
|
||||
if (rightBean != null)
|
||||
{
|
||||
GestureType type = GesEventInput.Instance.GetGestureType(HandType.RightHand);
|
||||
Vector3 handForward = GetSkeletonPose(SkeletonIndexFlag.PALM, HandType.RightHand).forward;
|
||||
GestureRightSuccess(type, handForward);
|
||||
}
|
||||
else
|
||||
{
|
||||
GestureRightFail();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTrackedSuccess(HandType handType)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void OnTrackedFailed(HandType handType)
|
||||
{
|
||||
if (handType == HandType.None)
|
||||
{
|
||||
leftBean = null;
|
||||
rightBean = null;
|
||||
}
|
||||
|
||||
if (handType == HandType.RightHand)
|
||||
{
|
||||
rightBean = null;
|
||||
}
|
||||
|
||||
if (handType == HandType.LeftHand)
|
||||
{
|
||||
leftBean = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRenderHand(HandType handType, GestureBean gestureBean)
|
||||
{
|
||||
if (handType == HandType.RightHand)
|
||||
{
|
||||
rightBean = gestureBean;
|
||||
}
|
||||
|
||||
if (handType == HandType.LeftHand)
|
||||
{
|
||||
leftBean = gestureBean;
|
||||
}
|
||||
|
||||
if (handType == HandType.None)
|
||||
{
|
||||
rightBean = null;
|
||||
leftBean = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取骨架点位置
|
||||
/// </summary>
|
||||
/// <param name="index">骨架序号</param>
|
||||
/// <param name="hand">哪只手</param>
|
||||
/// <returns></returns>
|
||||
public Pose GetSkeletonPose(SkeletonIndexFlag index, HandType hand)
|
||||
{
|
||||
return GesEventInput.Instance.GetSkeletonPose(index, hand);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取手地朝向
|
||||
/// </summary>
|
||||
/// <param name="handType">哪只手</param>
|
||||
/// <returns></returns>
|
||||
public Vector3 GetHandForward(HandType handType)
|
||||
{
|
||||
Vector3 handForward = (GetSkeletonPose(SkeletonIndexFlag.MIDDLE_FINGER_MCP, handType).position -
|
||||
GetSkeletonPose(SkeletonIndexFlag.WRIST, handType).position);
|
||||
return handForward;
|
||||
}
|
||||
|
||||
|
||||
#region 需要被重写的方法
|
||||
|
||||
/// <summary>
|
||||
/// 左手成功识别到手势不断执行操作
|
||||
/// </summary>
|
||||
/// <param name="gestureType">左手手势类型</param>
|
||||
/// <param name="handForward">左手掌心朝向</param>
|
||||
public abstract void GestureLeftSuccess(GestureType gestureType, Vector3 handForward);
|
||||
|
||||
/// <summary>
|
||||
/// 右手成功识别到手势不断执行操作
|
||||
/// </summary>
|
||||
/// <param name="gestureType">右手手势类型</param>
|
||||
/// <param name="handForward">右手掌心朝向</param>
|
||||
public abstract void GestureRightSuccess(GestureType gestureType, Vector3 handForward);
|
||||
|
||||
/// <summary>
|
||||
/// 左手未识别到手势执行操作
|
||||
/// </summary>
|
||||
public abstract void GestureLeftFail();
|
||||
|
||||
/// <summary>
|
||||
/// 右手未识别到手势执行操作
|
||||
/// </summary>
|
||||
public abstract void GestureRightFail();
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
153
Assets/07.RKTools/RunTime/GestureRecognition/GestureRokidBase.cs
Normal file
153
Assets/07.RKTools/RunTime/GestureRecognition/GestureRokidBase.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.RKTools
|
||||
{
|
||||
#if Evo_Rokid
|
||||
public abstract class GestureRokidBase : MonoBehaviour
|
||||
{
|
||||
private Rokid.UXR.Interaction.GestureBean leftBean = null;
|
||||
private Rokid.UXR.Interaction.GestureBean rightBean = null;
|
||||
protected void OnEnable()
|
||||
{
|
||||
Rokid.UXR.Interaction.GesEventInput.OnTrackedSuccess += OnTrackedSuccess;
|
||||
Rokid.UXR.Interaction.GesEventInput.OnTrackedFailed += OnTrackedFailed;
|
||||
Rokid.UXR.Interaction.GesEventInput.OnRenderHand += OnRenderHand;
|
||||
}
|
||||
|
||||
protected void OnDisable()
|
||||
{
|
||||
Rokid.UXR.Interaction.GesEventInput.OnTrackedSuccess -= OnTrackedSuccess;
|
||||
Rokid.UXR.Interaction.GesEventInput.OnTrackedFailed -= OnTrackedFailed;
|
||||
Rokid.UXR.Interaction.GesEventInput.OnRenderHand -= OnRenderHand;
|
||||
}
|
||||
|
||||
protected void FixedUpdate()
|
||||
{
|
||||
if (leftBean != null)
|
||||
{
|
||||
Rokid.UXR.Interaction.GestureType type =
|
||||
Rokid.UXR.Interaction.GesEventInput.Instance.GetGestureType(Rokid.UXR.Interaction.HandType.LeftHand);
|
||||
Vector3 handForward =
|
||||
GetSkeletonPose(Rokid.UXR.Interaction.SkeletonIndexFlag.PALM, Rokid.UXR.Interaction.HandType.LeftHand).forward;
|
||||
GestureLeftSuccess(type, handForward);
|
||||
}
|
||||
else
|
||||
{
|
||||
GestureLeftFail();
|
||||
}
|
||||
|
||||
if (rightBean != null)
|
||||
{
|
||||
Rokid.UXR.Interaction.GestureType type =
|
||||
Rokid.UXR.Interaction.GesEventInput.Instance.GetGestureType(Rokid.UXR.Interaction.HandType.RightHand);
|
||||
Vector3 handForward =
|
||||
GetSkeletonPose(Rokid.UXR.Interaction.SkeletonIndexFlag.PALM, Rokid.UXR.Interaction.HandType.RightHand).forward;
|
||||
GestureRightSuccess(type, handForward);
|
||||
}
|
||||
else
|
||||
{
|
||||
GestureRightFail();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTrackedSuccess(Rokid.UXR.Interaction.HandType handType)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void OnTrackedFailed(Rokid.UXR.Interaction.HandType handType)
|
||||
{
|
||||
if (handType == Rokid.UXR.Interaction.HandType.None)
|
||||
{
|
||||
leftBean = null;
|
||||
rightBean = null;
|
||||
}
|
||||
|
||||
if (handType == Rokid.UXR.Interaction.HandType.RightHand)
|
||||
{
|
||||
rightBean = null;
|
||||
}
|
||||
|
||||
if (handType == Rokid.UXR.Interaction.HandType.LeftHand)
|
||||
{
|
||||
leftBean = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRenderHand(Rokid.UXR.Interaction.HandType handType, Rokid.UXR.Interaction.GestureBean gestureBean)
|
||||
{
|
||||
if (handType == Rokid.UXR.Interaction.HandType.RightHand)
|
||||
{
|
||||
rightBean = gestureBean;
|
||||
}
|
||||
|
||||
if (handType == Rokid.UXR.Interaction.HandType.LeftHand)
|
||||
{
|
||||
leftBean = gestureBean;
|
||||
}
|
||||
|
||||
if (handType == Rokid.UXR.Interaction.HandType.None)
|
||||
{
|
||||
rightBean = null;
|
||||
leftBean = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取骨架点位置
|
||||
/// </summary>
|
||||
/// <param name="index">骨架序号</param>
|
||||
/// <param name="hand">哪只手</param>
|
||||
/// <returns></returns>
|
||||
public Pose GetSkeletonPose(Rokid.UXR.Interaction.SkeletonIndexFlag index, Rokid.UXR.Interaction.HandType hand)
|
||||
{
|
||||
return Rokid.UXR.Interaction.GesEventInput.Instance.GetSkeletonPose(index, hand);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取手地朝向
|
||||
/// </summary>
|
||||
/// <param name="handType">哪只手</param>
|
||||
/// <returns></returns>
|
||||
public Vector3 GetHandForward(Rokid.UXR.Interaction.HandType handType)
|
||||
{
|
||||
Vector3 handForward =
|
||||
(GetSkeletonPose(Rokid.UXR.Interaction.SkeletonIndexFlag.MIDDLE_FINGER_MCP, handType).position -
|
||||
GetSkeletonPose(Rokid.UXR.Interaction.SkeletonIndexFlag.WRIST, handType).position);
|
||||
return handForward;
|
||||
}
|
||||
|
||||
|
||||
#region 需要被重写的方法
|
||||
|
||||
/// <summary>
|
||||
/// 左手成功识别到手势不断执行操作
|
||||
/// </summary>
|
||||
/// <param name="gestureType">左手手势类型</param>
|
||||
/// <param name="handForward">左手掌心朝向</param>
|
||||
public abstract void GestureLeftSuccess(Rokid.UXR.Interaction.GestureType gestureType, Vector3 handForward);
|
||||
|
||||
/// <summary>
|
||||
/// 右手成功识别到手势不断执行操作
|
||||
/// </summary>
|
||||
/// <param name="gestureType">右手手势类型</param>
|
||||
/// <param name="handForward">右手掌心朝向</param>
|
||||
public abstract void GestureRightSuccess(Rokid.UXR.Interaction.GestureType gestureType, Vector3 handForward);
|
||||
|
||||
/// <summary>
|
||||
/// 左手未识别到手势执行操作
|
||||
/// </summary>
|
||||
public abstract void GestureLeftFail();
|
||||
|
||||
/// <summary>
|
||||
/// 右手未识别到手势执行操作
|
||||
/// </summary>
|
||||
public abstract void GestureRightFail();
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
126
Assets/07.RKTools/RunTime/GestureRecognition/GestureXrealBase.cs
Normal file
126
Assets/07.RKTools/RunTime/GestureRecognition/GestureXrealBase.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.RKTools
|
||||
{
|
||||
#if Evo_Xreal
|
||||
public abstract class GestureXrealBase : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private UnityEngine.XR.Hands.Handedness m_Handedness;
|
||||
|
||||
private UnityEngine.XR.Hands.XRHandTrackingEvents handTrackingEvents = null;
|
||||
|
||||
[SerializeField] [Tooltip("The hand shape or pose that must be detected for the gesture to be performed.")]
|
||||
private ScriptableObject m_HandShapeOrPose;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip(
|
||||
"The minimum amount of time the hand must be held in the required shape and orientation for the gesture to be performed.")]
|
||||
float m_MinimumHoldTime = 0.2f;
|
||||
|
||||
[SerializeField] [Tooltip("The interval at which the gesture detection is performed.")]
|
||||
private float m_GestureDetectionInterval = 0.1f;
|
||||
|
||||
private float m_TimeOfLastConditionCheck;
|
||||
private UnityEngine.XR.Hands.Gestures.XRHandShape m_HandShape;
|
||||
private UnityEngine.XR.Hands.Gestures.XRHandPose m_HandPose;
|
||||
|
||||
private bool m_WasDetected; // 上一帧是否检测到手势
|
||||
private bool m_PerformedTriggered; // 手势执行事件是否已触发
|
||||
private float m_HoldStartTime; // 手势开始保持的时间
|
||||
|
||||
protected void OnEnable()
|
||||
{
|
||||
var trackingEvents = GameObject.FindObjectsOfType<UnityEngine.XR.Hands.XRHandTrackingEvents>();
|
||||
if (handTrackingEvents == null)
|
||||
{
|
||||
foreach (var track in trackingEvents)
|
||||
{
|
||||
if (track.handedness == m_Handedness)
|
||||
{
|
||||
handTrackingEvents = track;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handTrackingEvents.jointsUpdated.AddListener(OnJointsUpdated);
|
||||
m_HandShape = m_HandShapeOrPose as UnityEngine.XR.Hands.Gestures.XRHandShape;
|
||||
m_HandPose = m_HandShapeOrPose as UnityEngine.XR.Hands.Gestures.XRHandPose;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
handTrackingEvents.jointsUpdated.RemoveListener(OnJointsUpdated);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手部关节数据更新时的回调方法
|
||||
/// </summary>
|
||||
/// <param name="eventArgs">关节更新事件参数,包含手部关节数据</param>
|
||||
void OnJointsUpdated(UnityEngine.XR.Hands.XRHandJointsUpdatedEventArgs eventArgs)
|
||||
{
|
||||
// 检查是否在检测间隔内,避免过于频繁的检测
|
||||
if (!isActiveAndEnabled ||
|
||||
Time.timeSinceLevelLoad < m_TimeOfLastConditionCheck + m_GestureDetectionInterval)
|
||||
return;
|
||||
|
||||
// 检查手部是否被跟踪,以及是否满足形状或姿势条件
|
||||
var detected =
|
||||
handTrackingEvents.handIsTracked &&
|
||||
(m_HandShape != null && m_HandShape.CheckConditions(eventArgs) ||
|
||||
m_HandPose != null && m_HandPose.CheckConditions(eventArgs));
|
||||
|
||||
// 处理检测状态的变化
|
||||
if (!m_WasDetected && detected)
|
||||
{
|
||||
// 开始检测到手势,记录开始时间
|
||||
m_HoldStartTime = Time.timeSinceLevelLoad;
|
||||
}
|
||||
else if (m_WasDetected && !detected)
|
||||
{
|
||||
// 手势结束,重置触发状态并触发结束事件
|
||||
m_PerformedTriggered = false;
|
||||
GestureFail();
|
||||
// m_Background.color = m_BackgroundDefaultColor;
|
||||
}
|
||||
|
||||
// 更新检测状态
|
||||
m_WasDetected = detected;
|
||||
|
||||
// 检查是否满足保持时间要求
|
||||
if (!m_PerformedTriggered && detected)
|
||||
{
|
||||
var holdTimer = Time.timeSinceLevelLoad - m_HoldStartTime;
|
||||
if (holdTimer > m_MinimumHoldTime)
|
||||
{
|
||||
// 满足保持时间,触发手势执行事件
|
||||
GestureSuccess();
|
||||
m_PerformedTriggered = true;
|
||||
// m_Background.color = m_BackgroundHiglightColor;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新上次检查时间
|
||||
m_TimeOfLastConditionCheck = Time.timeSinceLevelLoad;
|
||||
}
|
||||
|
||||
#region 需要被重写的方法
|
||||
|
||||
/// <summary>
|
||||
/// 左手成功识别到手势不断执行操作
|
||||
/// </summary>
|
||||
/// <param name="gestureType">左手手势类型</param>
|
||||
/// <param name="handForward">左手掌心朝向</param>
|
||||
public abstract void GestureSuccess();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 左手未识别到手势执行操作
|
||||
/// </summary>
|
||||
public abstract void GestureFail();
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 962d6dd939fb40158b6e408420acdf6f
|
||||
timeCreated: 1774261529
|
||||
@@ -2,10 +2,10 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Stary.Evo;
|
||||
|
||||
namespace Stary.Evo.RKTools
|
||||
{
|
||||
#if Evo_Rokid
|
||||
public interface IVoiceCommandSystem : ISystem
|
||||
{
|
||||
public void AddVoiceCommand(string content, string spell, Action action);
|
||||
@@ -67,4 +67,5 @@ namespace Stary.Evo.RKTools
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -2,12 +2,11 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Stary.Evo;
|
||||
using Rokid.UXR.Module;
|
||||
using UnityEngine.Android;
|
||||
|
||||
namespace Stary.Evo.RKTools
|
||||
{
|
||||
#if Evo_Rokid
|
||||
public class VoiceCommandController : MonoBehaviour
|
||||
{
|
||||
public Dictionary<string, Action> VoiceCommands = new Dictionary<string, Action>();
|
||||
@@ -39,8 +38,8 @@ namespace Stary.Evo.RKTools
|
||||
Permission.RequestUserPermission("android.permission.RECORD_AUDIO");
|
||||
}
|
||||
|
||||
ModuleManager.Instance.RegistModule("com.rokid.voicecommand.VoiceCommandHelper", false);
|
||||
OfflineVoiceModule.Instance.ChangeVoiceCommandLanguage(LANGUAGE.CHINESE);
|
||||
Rokid.UXR.Module.ModuleManager.Instance.RegistModule("com.rokid.voicecommand.VoiceCommandHelper", false);
|
||||
Rokid.UXR.Module.OfflineVoiceModule.Instance.ChangeVoiceCommandLanguage(Rokid.UXR.Module.LANGUAGE.CHINESE);
|
||||
isInit = true;
|
||||
}
|
||||
|
||||
@@ -54,9 +53,9 @@ namespace Stary.Evo.RKTools
|
||||
return;
|
||||
}
|
||||
|
||||
OfflineVoiceModule.Instance.AddInstruct(LANGUAGE.CHINESE, content, spell, this.gameObject.name,
|
||||
Rokid.UXR.Module.OfflineVoiceModule.Instance.AddInstruct(Rokid.UXR.Module.LANGUAGE.CHINESE, content, spell, this.gameObject.name,
|
||||
"OnReceive");
|
||||
OfflineVoiceModule.Instance.Commit();
|
||||
Rokid.UXR.Module.OfflineVoiceModule.Instance.Commit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,8 +80,8 @@ namespace Stary.Evo.RKTools
|
||||
public void ClearAllVoiceCommand()
|
||||
{
|
||||
VoiceCommands.Clear();
|
||||
OfflineVoiceModule.Instance.ClearAllInstruct();
|
||||
OfflineVoiceModule.Instance.Commit();
|
||||
Rokid.UXR.Module.OfflineVoiceModule.Instance.ClearAllInstruct();
|
||||
Rokid.UXR.Module.OfflineVoiceModule.Instance.Commit();
|
||||
}
|
||||
|
||||
void OnReceive(string msg)
|
||||
@@ -94,4 +93,6 @@ namespace Stary.Evo.RKTools
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using Rokid.UXR.Module;
|
||||
using Stary.Evo;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Android;
|
||||
using ModuleManager = Rokid.UXR.Module.ModuleManager;
|
||||
|
||||
|
||||
public class VoiceSwitchingScene : MonoBehaviour
|
||||
{
|
||||
#if Evo_Rokid
|
||||
private String[] _sceneNames = new string[]
|
||||
{
|
||||
"打开第一个场景",
|
||||
@@ -74,18 +74,18 @@ public class VoiceSwitchingScene : MonoBehaviour
|
||||
Permission.RequestUserPermission("android.permission.RECORD_AUDIO");
|
||||
}
|
||||
|
||||
ModuleManager.Instance.RegistModule("com.rokid.voicecommand.VoiceCommandHelper", false);
|
||||
OfflineVoiceModule.Instance.ChangeVoiceCommandLanguage(LANGUAGE.CHINESE);
|
||||
Rokid.UXR.Module.ModuleManager.Instance.RegistModule("com.rokid.voicecommand.VoiceCommandHelper", false);
|
||||
Rokid.UXR.Module.OfflineVoiceModule.Instance.ChangeVoiceCommandLanguage(Rokid.UXR.Module.LANGUAGE.CHINESE);
|
||||
|
||||
mainDomainAll = Resources.Load<MainDomainAll>("MainDomainAll");
|
||||
for (int i = 0; i < mainDomainAll.domainAll.Length; i++)
|
||||
{
|
||||
if (mainDomainAll.domainAll[i].isVideo)
|
||||
OfflineVoiceModule.Instance.AddInstruct(LANGUAGE.CHINESE, _sceneNames[i], _sceneNamesSpell[i],
|
||||
Rokid.UXR.Module.OfflineVoiceModule.Instance.AddInstruct(Rokid.UXR.Module.LANGUAGE.CHINESE, _sceneNames[i], _sceneNamesSpell[i],
|
||||
this.gameObject.name, "OnReceive");
|
||||
}
|
||||
|
||||
OfflineVoiceModule.Instance.Commit();
|
||||
Rokid.UXR.Module.OfflineVoiceModule.Instance.Commit();
|
||||
}
|
||||
|
||||
|
||||
@@ -118,4 +118,5 @@ public class VoiceSwitchingScene : MonoBehaviour
|
||||
{
|
||||
AppConfig.OpenDomainType = OpenDomainType.PointCloud;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,38 +1,38 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Rokid.UXR.Module;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.RKTools
|
||||
{
|
||||
public class ARTrackedImageEvoObj : ARTrackedImageObj
|
||||
#if Evo_Rokid
|
||||
public class ARTrackedImageEvoObj : Rokid.UXR.Module.ARTrackedImageObj
|
||||
{
|
||||
[HideInInspector]
|
||||
public string domain;
|
||||
[HideInInspector]
|
||||
public Transform trackedTransform;
|
||||
[HideInInspector] public string domain;
|
||||
[HideInInspector] public Transform trackedTransform;
|
||||
|
||||
public bool isStartTrackedImage = false;
|
||||
|
||||
public bool isStartTrackedImage=false;
|
||||
public void Awake()
|
||||
{
|
||||
if (trackedTransform == null)
|
||||
{
|
||||
trackedTransform=this.transform;
|
||||
trackedTransform = this.transform;
|
||||
Debug.Log("StaryEvo:TrackedImageEvoObj: trackedTransform 未赋值,以自动赋值为自身");
|
||||
}
|
||||
|
||||
OnARTrackedImageAdded.AddListener(OnARTrackedImageAddedEvent);
|
||||
OnARTrackedImageUpdated.AddListener(OnARTrackedImageUpdateEvent);
|
||||
OnARTrackedImageRemoved.AddListener(OnARTrackedImageRemovedEvent);
|
||||
}
|
||||
|
||||
private void OnARTrackedImageAddedEvent(ARTrackedImageObj obj)
|
||||
private void OnARTrackedImageAddedEvent(Rokid.UXR.Module.ARTrackedImageObj obj)
|
||||
{
|
||||
TrackedImageEvoManager trackedImageEvoManager = ARTrackedImageManager.Instance as TrackedImageEvoManager;
|
||||
trackedImageEvoManager.SetTrackedImageEvoData(trackedImageIndex,domain,trackedTransform);
|
||||
TrackedImageEvoManager trackedImageEvoManager =
|
||||
Rokid.UXR.Module.ARTrackedImageManager.Instance as TrackedImageEvoManager;
|
||||
trackedImageEvoManager.SetTrackedImageEvoData(trackedImageIndex, domain, trackedTransform);
|
||||
isStartTrackedImage = true;
|
||||
}
|
||||
private void OnARTrackedImageUpdateEvent(ARTrackedImageObj obj)
|
||||
|
||||
private void OnARTrackedImageUpdateEvent(Rokid.UXR.Module.ARTrackedImageObj obj)
|
||||
{
|
||||
if (isStartTrackedImage)
|
||||
{
|
||||
@@ -43,14 +43,18 @@ namespace Stary.Evo.RKTools
|
||||
|
||||
}
|
||||
}
|
||||
private void OnARTrackedImageRemovedEvent(ARTrackedImageObj obj)
|
||||
|
||||
private void OnARTrackedImageRemovedEvent(Rokid.UXR.Module.ARTrackedImageObj obj)
|
||||
{
|
||||
isStartTrackedImage = false;
|
||||
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
OnARTrackedImageAdded.RemoveListener(OnARTrackedImageAddedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Rokid.UXR.Module;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.RKTools
|
||||
{
|
||||
public class TrackedImageEvoManager : ARTrackedImageManager
|
||||
#if Evo_Rokid
|
||||
public class TrackedImageEvoManager : Rokid.UXR.Module.ARTrackedImageManager
|
||||
{
|
||||
[TableList] public List<TarkedImageEvoData> TrackedImages;
|
||||
#if ODIN_INSPECTOR
|
||||
[Sirenix.OdinInspector.TableList]
|
||||
#endif
|
||||
public List<TarkedImageEvoData> TrackedImages;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
@@ -24,13 +26,14 @@ namespace Stary.Evo.RKTools
|
||||
return imageEvoData;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.LogError($"StaryEvo:未找到对应的图片的id数据,请检查是否进行前置识别 index:{imageIndex}");
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetTrackedImageEvoData(int imageIndex,string domain, Transform transform)
|
||||
public void SetTrackedImageEvoData(int imageIndex, string domain, Transform transform)
|
||||
{
|
||||
bool isExit=false;
|
||||
bool isExit = false;
|
||||
foreach (var imageEvoData in TrackedImages)
|
||||
{
|
||||
if (imageEvoData.imageIndex == imageIndex)
|
||||
@@ -38,7 +41,7 @@ namespace Stary.Evo.RKTools
|
||||
imageEvoData.position = transform.position;
|
||||
imageEvoData.rotation = transform.eulerAngles;
|
||||
imageEvoData.scale = transform.localScale;
|
||||
isExit=true;
|
||||
isExit = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +67,7 @@ namespace Stary.Evo.RKTools
|
||||
return imageEvoData;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.LogError($"StaryEvo:未找到对应的domain的id数据,请检查是否进行前置识别 domain:{domain}");
|
||||
return null;
|
||||
}
|
||||
@@ -72,11 +76,26 @@ namespace Stary.Evo.RKTools
|
||||
[Serializable]
|
||||
public class TarkedImageEvoData
|
||||
{
|
||||
[VerticalGroup("key")] public string domain;
|
||||
[VerticalGroup("key")] public int imageIndex;
|
||||
|
||||
[VerticalGroup("transform")] public Vector3 position;
|
||||
[VerticalGroup("transform")] public Vector3 rotation;
|
||||
[VerticalGroup("transform")] public Vector3 scale;
|
||||
#if ODIN_INSPECTOR
|
||||
[Sirenix.OdinInspector.VerticalGroup("key")]
|
||||
#endif
|
||||
public string domain;
|
||||
#if ODIN_INSPECTOR
|
||||
[Sirenix.OdinInspector.VerticalGroup("key")]
|
||||
#endif
|
||||
public int imageIndex;
|
||||
#if ODIN_INSPECTOR
|
||||
[Sirenix.OdinInspector.VerticalGroup("transform")]
|
||||
#endif
|
||||
public Vector3 position;
|
||||
#if ODIN_INSPECTOR
|
||||
[Sirenix.OdinInspector.VerticalGroup("transform")]
|
||||
#endif
|
||||
public Vector3 rotation;
|
||||
#if ODIN_INSPECTOR
|
||||
[Sirenix.OdinInspector.VerticalGroup("transform")]
|
||||
#endif
|
||||
public Vector3 scale;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -4,14 +4,12 @@
|
||||
"references": [
|
||||
"Rokid.Openxr",
|
||||
"Rokid.Unity.XR",
|
||||
"com.stary.evo.runtime",
|
||||
"com.staryevo.tools.runtime",
|
||||
"com.audiocore.runtime",
|
||||
"DOTween.Modules",
|
||||
"HybridCLR.Runtime",
|
||||
"ImmersalSDK",
|
||||
"Informationsave.runtime",
|
||||
"UniTask"
|
||||
"UniTask",
|
||||
"Unity.XR.Hands",
|
||||
"Unity.XR.Interaction.Toolkit",
|
||||
"com.stary.evo.runtime",
|
||||
"com.staryevo.tools.runtime"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "com.staryevo.rktools",
|
||||
"version": "1.1.5",
|
||||
"version": "1.1.6",
|
||||
"displayName": "07.RKTools",
|
||||
"description": "Rokid工具",
|
||||
"unity": "2021.3",
|
||||
|
||||
Reference in New Issue
Block a user