第9章 深度集成Leap Motion于沉浸式交互开发
本章详细介绍了Leap Motion在商业级VR/AR应用中的深度集成方法。首先阐述了开发环境的搭建流程,包括驱动安装、多设备管理以及Unity SDK的模块化导入策略。重点讲解了商业场景下的关键实现技术,如多设备动态连接管理、用户会话分配机制,以及扩展模拟手系统以支持快速原型开发。通过示例代码展示了如何构建稳定可靠的Leap Motion集成框架,满足商业项目对交互精度和系统稳定性的严苛要求。这
第9章 深度集成Leap Motion于沉浸式交互开发
9.1 开发前期准备与环境搭建
在商业级VR/AR应用开发中,手势识别技术已经成为提升用户体验的关键要素。Leap Motion作为高精度手部追踪设备的代表,其集成质量直接决定了最终产品的交互流畅度。本节将系统阐述从零开始构建Leap Motion开发环境的完整流程。
9.1.1 软件基础:驱动与运行时的安装
Leap Motion控制器需要专用的驱动程序才能将原始传感器数据转化为可用的手部追踪信息。在商业开发环境中,稳定性是首要考虑因素。建议选择长期支持版本而非最新的测试版驱动,以确保与现有项目架构的兼容性。
安装过程需要注意系统权限设置,特别是在Windows 11环境下,需要确保在“开发者设置”中开启设备发现权限。安装完成后,系统托盘会出现Leap Motion控制面板图标,双击可进入可视化校准界面。商业项目中经常需要多设备协同工作,此时需要在控制面板的“设备”选项卡中配置多设备模式,确保各设备ID不冲突。
一个常见的商业场景是展厅中的多用户互动系统,需要同时连接4-6个Leap Motion设备。这时需要在代码中动态管理设备连接状态:
using Leap;
using System.Collections.Generic;
namespace CommercialVR.DeviceManagement
{
public class MultiLeapManager
{
private Controller leapController;
private List<byte[]> connectedDeviceSerialNumbers;
public MultiLeapManager()
{
leapController = new Controller();
connectedDeviceSerialNumbers = new List<byte[]>();
// 监听设备连接事件
leapController.Device += OnLeapDeviceConnected;
leapController.DeviceLost += OnLeapDeviceDisconnected;
}
private void OnLeapDeviceConnected(object sender, DeviceEventArgs args)
{
byte[] serialNumber = args.Device.SerialNumber;
if (!IsDeviceRegistered(serialNumber))
{
connectedDeviceSerialNumbers.Add(serialNumber);
Debug.Log($"设备已连接: {System.Text.Encoding.ASCII.GetString(serialNumber)}");
// 商业场景:为新连接设备分配用户会话
AssignUserSessionToDevice(serialNumber);
}
}
private void OnLeapDeviceDisconnected(object sender, DeviceEventArgs args)
{
byte[] serialNumber = args.Device.SerialNumber;
if (IsDeviceRegistered(serialNumber))
{
connectedDeviceSerialNumbers.Remove(serialNumber);
Debug.LogWarning($"设备断开: {System.Text.Encoding.ASCII.GetString(serialNumber)}");
// 清理该设备对应的用户数据
CleanupUserSession(serialNumber);
}
}
private bool IsDeviceRegistered(byte[] serialNumber)
{
foreach (byte[] registeredSerial in connectedDeviceSerialNumbers)
{
if (System.Linq.Enumerable.SequenceEqual(registeredSerial, serialNumber))
{
return true;
}
}
return false;
}
private void AssignUserSessionToDevice(byte[] serialNumber)
{
// 商业逻辑:为设备创建用户会话
// 这里可以连接后端服务器,分配用户ID
string deviceId = System.Text.Encoding.ASCII.GetString(serialNumber);
UserSessionManager.Instance.CreateSessionForDevice(deviceId);
}
public Frame GetLatestFrameForDevice(byte[] deviceSerial)
{
// 获取特定设备的最新帧数据
Frame frame = leapController.Frame();
// 在商业多设备环境中,需要验证数据来源
if (frame.Devices.Count > 0 &&
System.Linq.Enumerable.SequenceEqual(frame.Devices[0].SerialNumber, deviceSerial))
{
return frame;
}
return null;
}
}
}
9.1.2 核心工具:SDK获取与资源解析
UltraLeap官方提供的Unity SDK包含多个版本,商业项目推荐使用稳定版而非预览版。SDK下载后是一个.unitypackage文件,包含以下关键组件:
- Core Assets:手部模型、基础脚本、预制体
- Interaction Engine:高级交互系统
- Graphic Renderer:专用渲染管线
- Examples:官方示例场景
在大型商业项目中,SDK的导入需要遵循模块化原则。建议创建独立的文件夹结构:
Assets/
├── ThirdParty/
│ └── UltraLeap/
│ ├── Core/
│ ├── Interaction/
│ ├── Graphics/
│ └── Examples/
├── Scripts/
│ └── LeapIntegration/
└── Prefabs/
└── Hands/
导入SDK时需要注意依赖关系。Interaction Engine依赖于Core Assets,因此导入顺序必须是先Core后Interaction。商业项目中常见的错误是直接导入完整包导致脚本冲突,正确做法是选择性导入所需模块。
9.2 核心模块解析与手部模型控制
9.2.1 预制件应用与模拟手系统
在商业VR开发中,快速原型验证至关重要。Leap Motion提供的HandModels预制件允许开发者在没有物理设备的情况下进行开发。模拟手系统通过键盘和鼠标模拟真实手部动作,极大提高了开发效率。
模拟手的实现原理是基于LeapServiceProvider的衍生类。在商业项目中,我们通常需要扩展模拟功能以适配特定的交互需求:
using Leap.Unity;
using UnityEngine;
namespace CommercialVR.HandSimulation
{
[RequireComponent(typeof(LeapServiceProvider))]
public class EnhancedHandSimulator : MonoBehaviour
{
private LeapServiceProvider leapServiceProvider;
private SimulatorLeapProvider simulatorProvider;
[Header("模拟配置")]
[SerializeField] private KeyCode leftHandToggleKey = KeyCode.Alpha1;
[SerializeField] private KeyCode rightHandToggleKey = KeyCode.Alpha2;
[SerializeField] private float handMovementSpeed = 0.5f;
[SerializeField] private float rotationSpeed = 90f;
private bool isLeftHandActive = true;
private bool isRightHandActive = true;
private void Awake()
{
leapServiceProvider = GetComponent<LeapServiceProvider>();
// 检查是否为模拟器Provider
if (leapServiceProvider is SimulatorLeapProvider)
{
simulatorProvider = (SimulatorLeapProvider)leapServiceProvider;
ConfigureSimulator();
}
}
private void ConfigureSimulator()
{
// 商业项目可能需要自定义模拟手参数
simulatorProvider.simulationMode =
SimulatorLeapProvider.SimulationMode.Desktop;
// 设置手部重置位置
simulatorProvider.leftHandResetPosition = new Vector3(-0.2f, 0.5f, 0.5f);
simulatorProvider.rightHandResetPosition = new Vector3(0.2f, 0.5f, 0.5f);
}
private void Update()
{
HandleSimulationInput();
UpdateHandVisualization();
}
private void HandleSimulationInput()
{
// 切换手部显示
if (Input.GetKeyDown(leftHandToggleKey))
{
isLeftHandActive = !isLeftHandActive;
SetHandVisibility(Chirality.Left, isLeftHandActive);
}
if (Input.GetKeyDown(rightHandToggleKey))
{
isRightHandActive = !isRightHandActive;
SetHandVisibility(Chirality.Right, isRightHandActive);
}
// 商业项目可能需要的手部姿态快速切换
if (Input.GetKeyDown(KeyCode.F))
{
SetHandGesture(HandGesture.Fist);
}
else if (Input.GetKeyDown(KeyCode.O))
{
SetHandGesture(HandGesture.Open);
}
else if (Input.GetKeyDown(KeyCode.P))
{
SetHandGesture(HandGesture.Pinch);
}
}
private void SetHandVisibility(Chirality chirality, bool isVisible)
{
HandModelManager handModelManager = FindObjectOfType<HandModelManager>();
if (handModelManager != null)
{
foreach (HandModelBase handModel in handModelManager.HandModelBases)
{
if (handModel.Handedness == chirality)
{
handModel.gameObject.SetActive(isVisible);
}
}
}
}
private void SetHandGesture(HandGesture gesture)
{
// 商业项目中可能需要预设手势
// 这里调用手势识别系统的预设方法
HandGestureRecognizer.Instance.ForceGesture(gesture);
}
private void UpdateHandVisualization()
{
// 更新手部视觉效果,如高亮、描边等
// 商业项目中常用于提示可交互状态
}
public enum HandGesture
{
None,
Fist,
Open,
Pinch,
Point,
ThumbsUp
}
}
}
9.2.2 手部模型的商业化定制
官方提供的手部模型虽然精致,但商业项目通常需要定制化外观以匹配品牌风格。Leap Motion的手部模型采用骨骼驱动,理解其层级结构是进行定制的基础。
一个典型的手部模型层级结构如下:
HandModel
├── Palm
├── Forearm
└── Fingers
├── Thumb
│ ├── Metacarpal
│ ├── Proximal
│ ├── Intermediate
│ └── Distal
├── Index
│ └── ...(类似层级)
└── ...(其他手指)
在商业汽车配置器项目中,我们可能需要为手部模型添加特殊的材质效果,以增强豪华感:
using Leap.Unity;
using System.Collections.Generic;
using UnityEngine;
namespace CommercialVR.AutomotiveConfigurator
{
public class LuxuryHandModelCustomizer : MonoBehaviour
{
[System.Serializable]
public class FingerMaterialSet
{
public Material baseMaterial;
public Material highlightMaterial;
public Material selectionMaterial;
}
[Header("材质配置")]
[SerializeField] private FingerMaterialSet[] fingerMaterials;
[SerializeField] private Material palmMaterial;
[SerializeField] private Material forearmMaterial;
[Header("视觉效果")]
[SerializeField] private bool enableHolographicEffect = true;
[SerializeField] private Color holographicColor = new Color(0, 1, 1, 0.3f);
[SerializeField] private float rimPower = 2.0f;
private HandModelBase handModel;
private Dictionary<Transform, Material[]> originalMaterials;
private List<GameObject> holographicLayers;
private void Start()
{
handModel = GetComponent<HandModelBase>();
originalMaterials = new Dictionary<Transform, Material[]>();
holographicLayers = new List<GameObject>();
if (handModel != null)
{
CacheOriginalMaterials();
ApplyCustomMaterials();
if (enableHolographicEffect)
{
CreateHolographicLayer();
}
}
}
private void CacheOriginalMaterials()
{
// 缓存所有渲染器的原始材质
Renderer[] renderers = GetComponentsInChildren<Renderer>(true);
foreach (Renderer renderer in renderers)
{
originalMaterials[renderer.transform] = renderer.materials;
}
}
private void ApplyCustomMaterials()
{
// 应用定制材质
ApplyMaterialToPart("palm", palmMaterial);
ApplyMaterialToPart("forearm", forearmMaterial);
// 为每个手指应用不同的材质组合
string[] fingerNames = { "thumb", "index", "middle", "ring", "pinky" };
for (int i = 0; i < Mathf.Min(fingerNames.Length, fingerMaterials.Length); i++)
{
ApplyMaterialToFinger(fingerNames[i], fingerMaterials[i]);
}
}
private void ApplyMaterialToPart(string partName, Material material)
{
Transform partTransform = FindDeepChild(transform, partName);
if (partTransform != null)
{
Renderer renderer = partTransform.GetComponent<Renderer>();
if (renderer != null && material != null)
{
Material[] newMaterials = new Material[renderer.materials.Length];
for (int j = 0; j < newMaterials.Length; j++)
{
newMaterials[j] = material;
}
renderer.materials = newMaterials;
}
}
}
private void ApplyMaterialToFinger(string fingerName, FingerMaterialSet materialSet)
{
Transform fingerRoot = FindDeepChild(transform, fingerName);
if (fingerRoot != null)
{
Renderer[] fingerRenderers = fingerRoot.GetComponentsInChildren<Renderer>();
foreach (Renderer renderer in fingerRenderers)
{
// 商业项目中的特殊逻辑:不同指节使用不同材质
string boneName = renderer.transform.name.ToLower();
Material targetMaterial = materialSet.baseMaterial;
if (boneName.Contains("distal"))
{
// 指尖使用高亮材质
targetMaterial = materialSet.highlightMaterial;
}
else if (boneName.Contains("proximal"))
{
// 近端指节使用选择材质
targetMaterial = materialSet.selectionMaterial;
}
if (targetMaterial != null)
{
renderer.material = targetMaterial;
}
}
}
}
private void CreateHolographicLayer()
{
// 创建全息效果层
Renderer[] originalRenderers = GetComponentsInChildren<Renderer>();
foreach (Renderer originalRenderer in originalRenderers)
{
GameObject holographicCopy = Instantiate(originalRenderer.gameObject,
originalRenderer.transform);
holographicCopy.transform.localPosition = Vector3.zero;
holographicCopy.transform.localRotation = Quaternion.identity;
holographicCopy.transform.localScale = Vector3.one * 1.02f;
Renderer copyRenderer = holographicCopy.GetComponent<Renderer>();
if (copyRenderer != null)
{
// 应用全息着色器
Material hologramMat = new Material(Shader.Find("UltraLeap/Holographic"));
hologramMat.SetColor("_RimColor", holographicColor);
hologramMat.SetFloat("_RimPower", rimPower);
copyRenderer.material = hologramMat;
copyRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
// 调整渲染顺序
foreach (Material mat in copyRenderer.materials)
{
mat.renderQueue = 3000; // 确保在全息层之上渲染
}
}
holographicLayers.Add(holographicCopy);
}
}
private Transform FindDeepChild(Transform parent, string childName)
{
foreach (Transform child in parent.GetComponentsInChildren<Transform>(true))
{
if (child.name.ToLower().Contains(childName.ToLower()))
{
return child;
}
}
return null;
}
private void OnDestroy()
{
// 清理全息层
foreach (GameObject hologram in holographicLayers)
{
if (hologram != null)
{
Destroy(hologram);
}
}
}
}
}
9.2.3 服务提供者架构与设备管理
LeapServiceProvider是Leap Motion数据流的核心组件,负责与硬件设备通信并分发帧数据。在商业级应用中,我们需要对数据流进行优化和监控,确保稳定性和性能。
以下是一个商业项目中常用的增强型服务提供者:
using Leap;
using Leap.Unity;
using System.Diagnostics;
using UnityEngine;
namespace CommercialVR.DataPipeline
{
public class EnhancedLeapServiceProvider : LeapServiceProvider
{
[Header("性能监控")]
[SerializeField] private bool enablePerformanceLogging = true;
[SerializeField] private int logFrequency = 300; // 每300帧记录一次
[Header("数据平滑")]
[SerializeField] private bool enableHandSmoothing = true;
[SerializeField] [Range(0, 1)] private float positionSmoothing = 0.5f;
[SerializeField] [Range(0, 1)] private float rotationSmoothing = 0.3f;
[Header("商业特性")]
[SerializeField] private bool enableFallbackTracking = true;
[SerializeField] private float timeoutDuration = 2.0f;
private Stopwatch frameProcessingTimer;
private long totalProcessingTime = 0;
private int frameCount = 0;
private Hand leftHandPrevious;
private Hand rightHandPrevious;
private float timeSinceLastFrame = 0f;
private bool isTrackingLost = false;
protected override void Start()
{
base.Start();
frameProcessingTimer = new Stopwatch();
if (enablePerformanceLogging)
{
StartCoroutine(PerformanceLoggingRoutine());
}
}
public override Frame CurrentFrame
{
get
{
frameProcessingTimer.Restart();
Frame rawFrame = base.CurrentFrame;
if (rawFrame == null || !rawFrame.Hands.Count > 0)
{
CheckTrackingStatus();
return rawFrame;
}
Frame processedFrame = ProcessFrameForCommercialUse(rawFrame);
frameProcessingTimer.Stop();
totalProcessingTime += frameProcessingTimer.ElapsedMilliseconds;
frameCount++;
return processedFrame;
}
}
private Frame ProcessFrameForCommercialUse(Frame rawFrame)
{
Frame processedFrame = rawFrame;
// 应用手部平滑
if (enableHandSmoothing)
{
processedFrame = ApplySmoothing(processedFrame);
}
// 商业项目中的特定处理
// 1. 手势优先级过滤
processedFrame = FilterGesturesByPriority(processedFrame);
// 2. 数据完整性验证
processedFrame = ValidateDataIntegrity(processedFrame);
// 3. 添加商业元数据
processedFrame = AddBusinessMetadata(processedFrame);
return processedFrame;
}
private Frame ApplySmoothing(Frame frame)
{
foreach (Hand hand in frame.Hands)
{
Hand previousHand = (hand.IsLeft ? leftHandPrevious : rightHandPrevious);
if (previousHand != null)
{
// 平滑位置
Vector smoothedPosition = Vector.Lerp(
previousHand.PalmPosition,
hand.PalmPosition,
positionSmoothing
);
// 平滑旋转
LeapQuaternion smoothedRotation = LeapQuaternion.Slerp(
previousHand.Rotation,
hand.Rotation,
rotationSmoothing
);
// 更新手部数据(需要反射或修改内部结构)
// 实际项目中可能需要更复杂的处理
}
// 更新前一手数据
if (hand.IsLeft)
{
leftHandPrevious = hand;
}
else
{
rightHandPrevious = hand;
}
}
return frame;
}
private Frame FilterGesturesByPriority(Frame frame)
{
// 商业逻辑:根据应用场景确定手势优先级
// 例如,在汽车配置器中,捏合手势优先于抓取手势
List<Gesture> gestures = frame.Gestures().ToList();
List<Gesture> filteredGestures = new List<Gesture>();
// 优先级映射
Dictionary<Gesture.GestureType, int> gesturePriorities = new Dictionary<Gesture.GestureType, int>()
{
{ Gesture.GestureType.TYPECIRCLE, 1 }, // 画圆手势
{ Gesture.GestureType.TYPESWIPE, 2 }, // 滑动
{ Gesture.GestureType.TYPEKEYTAP, 3 }, // 敲击
{ Gesture.GestureType.TYPESCREENTAP, 4 }, // 屏幕点击
};
// 按优先级排序
gestures.Sort((a, b) =>
gesturePriorities.GetValueOrDefault(a.Type, 99)
.CompareTo(gesturePriorities.GetValueOrDefault(b.Type, 99))
);
// 只保留最高优先级的手势(避免冲突)
if (gestures.Count > 0)
{
filteredGestures.Add(gestures[0]);
}
// 创建一个新的Frame,只包含过滤后的手势
// 注意:这需要自定义Frame实现,简化示例
return frame;
}
private Frame ValidateDataIntegrity(Frame frame)
{
// 验证数据完整性,过滤异常值
foreach (Hand hand in frame.Hands)
{
// 检查位置有效性
if (!IsPositionValid(hand.PalmPosition))
{
Debug.LogWarning($"无效的手部位置: {hand.PalmPosition}");
}
// 检查置信度
if (hand.Confidence < 0.5f)
{
// 低置信度数据,可能需要特殊处理
}
}
return frame;
}
private Frame AddBusinessMetadata(Frame frame)
{
// 添加商业项目需要的元数据
// 例如:用户ID、会话时间、设备信息等
// 这里可以通过扩展Frame类或使用包装器实现
BusinessFrameMetadata metadata = new BusinessFrameMetadata()
{
Timestamp = System.DateTime.UtcNow,
SessionId = UserSessionManager.CurrentSessionId,
FrameQuality = CalculateFrameQuality(frame)
};
// 将元数据附加到Frame
// 实际实现可能使用Frame.UserData或自定义属性
return frame;
}
private void CheckTrackingStatus()
{
timeSinceLastFrame += Time.deltaTime;
if (timeSinceLastFrame > timeoutDuration && !isTrackingLost)
{
isTrackingLost = true;
OnTrackingLost();
}
else if (timeSinceLastFrame == 0 && isTrackingLost)
{
isTrackingLost = false;
OnTrackingResumed();
}
}
private void OnTrackingLost()
{
Debug.LogError("Leap Motion跟踪丢失");
// 商业项目中的处理:启用备用方案
if (enableFallbackTracking)
{
ActivateFallbackTracking();
}
// 通知UI系统
UIManager.Instance.ShowTrackingLostWarning();
}
private void OnTrackingResumed()
{
Debug.Log("Leap Motion跟踪恢复");
// 恢复UI状态
UIManager.Instance.HideTrackingLostWarning();
// 重新校准
StartCoroutine(RecalibrationRoutine());
}
private System.Collections.IEnumerator PerformanceLoggingRoutine()
{
while (true)
{
yield return new WaitForSeconds(1.0f);
if (frameCount > 0)
{
float avgProcessingTime = (float)totalProcessingTime / frameCount;
float fps = 1000f / avgProcessingTime;
if (enablePerformanceLogging && frameCount % logFrequency == 0)
{
Debug.Log($"Leap Motion性能 - 平均处理时间: {avgProcessingTime:F2}ms, " +
$"预估FPS: {fps:F1}, 总帧数: {frameCount}");
}
// 重置计数器
totalProcessingTime = 0;
frameCount = 0;
}
}
}
private bool IsPositionValid(Vector position)
{
// 检查位置是否在有效范围内
float maxRange = 600; // Leap Motion最大追踪范围(毫米)
return position.Magnitude < maxRange &&
!float.IsNaN(position.x) &&
!float.IsNaN(position.y) &&
!float.IsNaN(position.z);
}
private float CalculateFrameQuality(Frame frame)
{
// 计算帧质量评分(0-1)
float quality = 1.0f;
foreach (Hand hand in frame.Hands)
{
quality *= hand.Confidence;
// 基于手指可见性调整质量
int visibleFingers = 0;
foreach (Finger finger in hand.Fingers)
{
if (finger.IsExtended)
{
visibleFingers++;
}
}
quality *= (visibleFingers / 5.0f);
}
return quality;
}
private void ActivateFallbackTracking()
{
// 启用备用跟踪方案(如基于摄像头的跟踪)
Debug.Log("激活备用跟踪系统");
// 实现备用跟踪逻辑
}
private System.Collections.IEnumerator RecalibrationRoutine()
{
// 重新校准流程
yield return new WaitForSeconds(0.5f);
// 执行校准逻辑
}
}
public class BusinessFrameMetadata
{
public System.DateTime Timestamp { get; set; }
public string SessionId { get; set; }
public float FrameQuality { get; set; }
public Dictionary<string, object> CustomData { get; set; }
public BusinessFrameMetadata()
{
CustomData = new Dictionary<string, object>();
}
}
}
9.2.4 与主流VR设备的整合方案
在商业VR应用中,Leap Motion常与HTC Vive、Oculus Rift等头显设备结合使用。这种整合需要处理多个技术挑战:坐标系对齐、延迟同步、性能优化等。
以下是一个整合HTC Vive与Leap Motion的完整解决方案:
using Leap.Unity;
using UnityEngine;
using UnityEngine.XR;
namespace CommercialVR.ViveIntegration
{
public class ViveLeapIntegrationManager : MonoBehaviour
{
[Header("设备配置")]
[SerializeField] private Transform viveHeadset;
[SerializeField] private Transform leapMountPoint;
[SerializeField] private bool useSteamVR = true;
[Header("校准设置")]
[SerializeField] private bool autoCalibrateOnStart = true;
[SerializeField] private float calibrationDuration = 3.0f;
[Header("偏移调整")]
[SerializeField] private Vector3 positionOffset = new Vector3(0, -0.1f, 0.1f);
[SerializeField] private Vector3 rotationOffset = new Vector3(0, 0, 0);
private LeapServiceProvider leapProvider;
private bool isCalibrated = false;
private Matrix4x4 leapToViveTransform;
private void Start()
{
InitializeComponents();
if (autoCalibrateOnStart)
{
StartCoroutine(AutoCalibrationRoutine());
}
}
private void InitializeComponents()
{
// 获取Leap Service Provider
leapProvider = FindObjectOfType<LeapServiceProvider>();
if (leapProvider == null)
{
Debug.LogError("未找到LeapServiceProvider");
return;
}
// 获取Vive头显引用
if (viveHeadset == null)
{
if (useSteamVR && SteamVR.enabled)
{
viveHeadset = SteamVR_Render.Top().head;
}
else
{
// 使用XR系统
viveHeadset = Camera.main.transform;
}
}
// 设置Leap设备挂载点
if (leapMountPoint == null)
{
leapMountPoint = viveHeadset;
}
// 配置Leap Provider
ConfigureLeapForVive();
}
private void ConfigureLeapForVive()
{
// 调整Leap Motion设置以适应头盔集成
LeapProviderSettings settings = leapProvider.GetComponent<LeapProviderSettings>();
if (settings != null)
{
// 优化头盔集成模式
settings.trackingOptimization =
LeapProviderSettings.TrackingOptimization.HMD;
// 调整FOV设置
settings.editTimeFOV = 150f; // 扩大FOV以补偿头盔遮挡
}
// 重新定位Leap设备
RepositionLeapDevice();
}
private void RepositionLeapDevice()
{
// 将Leap设备位置与头盔对齐
if (leapProvider.transform.parent != leapMountPoint)
{
leapProvider.transform.SetParent(leapMountPoint, false);
leapProvider.transform.localPosition = positionOffset;
leapProvider.transform.localEulerAngles = rotationOffset;
}
}
private System.Collections.IEnumerator AutoCalibrationRoutine()
{
Debug.Log("开始自动校准...");
// 步骤1:等待设备稳定
yield return new WaitForSeconds(1.0f);
// 步骤2:收集校准数据
Vector3[] headPositions = new Vector3[30];
Quaternion[] headRotations = new Quaternion[30];
for (int i = 0; i < 30; i++)
{
headPositions[i] = viveHeadset.position;
headRotations[i] = viveHeadset.rotation;
yield return null;
}
// 步骤3:计算平均变换
Vector3 averagePosition = CalculateAverage(headPositions);
Quaternion averageRotation = CalculateAverage(headRotations);
// 步骤4:建立坐标变换矩阵
leapToViveTransform = CalculateTransformationMatrix(
leapProvider.transform,
viveHeadset
);
// 步骤5:应用校准
ApplyCalibration(leapToViveTransform);
isCalibrated = true;
Debug.Log("自动校准完成");
// 步骤6:验证校准
yield return StartCoroutine(ValidationRoutine());
}
private Matrix4x4 CalculateTransformationMatrix(Transform source, Transform target)
{
// 计算从源坐标系到目标坐标系的变换矩阵
Matrix4x4 sourceToWorld = source.localToWorldMatrix;
Matrix4x4 targetToWorld = target.localToWorldMatrix;
// 源到世界 * 世界到目标 = 源到目标
return targetToWorld.inverse * sourceToWorld;
}
private void ApplyCalibration(Matrix4x4 transformation)
{
// 应用坐标变换到Leap数据
LeapTransform leapTransform = new LeapTransform(
transformation.GetColumn(3), // 位置
transformation.rotation, // 旋转
Vector3.one // 缩放
);
// 设置Leap Provider的变换
if (leapProvider is ILeapTransformProvider transformProvider)
{
transformProvider.LeapTransform = leapTransform;
}
}
private void LateUpdate()
{
if (isCalibrated && leapProvider != null && viveHeadset != null)
{
// 实时调整手部位置补偿
ApplyRealTimeCompensation();
// 更新手部渲染位置
UpdateHandVisualization();
}
}
private void ApplyRealTimeCompensation()
{
// 实时补偿头盔移动带来的误差
float deltaTime = Time.deltaTime;
// 预测下一帧的头盔位置(减少延迟)
Vector3 predictedHeadPosition = viveHeadset.position +
viveHeadset.TransformDirection(viveHeadset.InverseTransformDirection(
InputTracking.GetLocalPosition(XRNode.Head)
)) * deltaTime;
Quaternion predictedHeadRotation = viveHeadset.rotation *
Quaternion.Euler(InputTracking.GetLocalRotation(XRNode.Head).eulerAngles * deltaTime);
// 更新变换矩阵
Matrix4x4 updatedTransform = Matrix4x4.TRS(
predictedHeadPosition,
predictedHeadRotation,
Vector3.one
);
ApplyCalibration(updatedTransform * leapToViveTransform);
}
private void UpdateHandVisualization()
{
// 更新手部模型位置和旋转
HandModelManager handModelManager = FindObjectOfType<HandModelManager>();
if (handModelManager != null)
{
foreach (HandModelBase handModel in handModelManager.HandModelBases)
{
// 应用额外的偏移以改善视觉效果
Vector3 visualOffset = CalculateVisualOffset(handModel.Handedness);
handModel.transform.localPosition += visualOffset;
}
}
}
private Vector3 CalculateVisualOffset(Chirality chirality)
{
// 根据手性计算视觉偏移
// 这可以补偿头盔造成的视觉扭曲
Vector3 offset = Vector3.zero;
if (chirality == Chirality.Left)
{
offset.x = -0.02f; // 左手的微调
}
else
{
offset.x = 0.02f; // 右手的微调
}
// 基于头盔倾斜度调整
float headTilt = viveHeadset.localEulerAngles.x;
offset.y = Mathf.Sin(headTilt * Mathf.Deg2Rad) * 0.01f;
return offset;
}
private System.Collections.IEnumerator ValidationRoutine()
{
Debug.Log("开始校准验证...");
// 验证手部位置准确性
Frame testFrame = leapProvider.CurrentFrame;
if (testFrame != null && testFrame.Hands.Count > 0)
{
foreach (Hand hand in testFrame.Hands)
{
Vector3 handPosition = hand.PalmPosition.ToVector3();
Vector3 headPosition = viveHeadset.position;
float distance = Vector3.Distance(handPosition, headPosition);
// 预期距离范围检查
if (distance < 0.2f || distance > 1.5f)
{
Debug.LogWarning($"手部距离异常: {distance:F2}米");
yield return StartCoroutine(RecalibrationRoutine());
break;
}
}
}
Debug.Log("校准验证通过");
}
private System.Collections.IEnumerator RecalibrationRoutine()
{
Debug.Log("重新校准...");
isCalibrated = false;
yield return StartCoroutine(AutoCalibrationRoutine());
}
private Vector3 CalculateAverage(Vector3[] vectors)
{
Vector3 sum = Vector3.zero;
foreach (Vector3 vec in vectors)
{
sum += vec;
}
return sum / vectors.Length;
}
private Quaternion CalculateAverage(Quaternion[] quaternions)
{
// 四元数平均需要特殊处理
if (quaternions == null || quaternions.Length == 0)
{
return Quaternion.identity;
}
Quaternion average = quaternions[0];
for (int i = 1; i < quaternions.Length; i++)
{
average = Quaternion.Slerp(average, quaternions[i], 1.0f / (i + 1));
}
return average;
}
public void ManualCalibration(Vector3 manualOffset, Vector3 manualRotation)
{
// 手动校准接口
positionOffset = manualOffset;
rotationOffset = manualRotation;
RepositionLeapDevice();
StartCoroutine(AutoCalibrationRoutine());
}
public void SaveCalibrationProfile(string profileName)
{
// 保存校准配置
CalibrationProfile profile = new CalibrationProfile()
{
ProfileName = profileName,
PositionOffset = positionOffset,
RotationOffset = rotationOffset,
CalibrationMatrix = leapToViveTransform,
Timestamp = System.DateTime.Now
};
string json = JsonUtility.ToJson(profile);
System.IO.File.WriteAllText(
$"{Application.persistentDataPath}/{profileName}_calibration.json",
json
);
Debug.Log($"校准配置已保存: {profileName}");
}
public void LoadCalibrationProfile(string profileName)
{
// 加载校准配置
string path = $"{Application.persistentDataPath}/{profileName}_calibration.json";
if (System.IO.File.Exists(path))
{
string json = System.IO.File.ReadAllText(path);
CalibrationProfile profile = JsonUtility.FromJson<CalibrationProfile>(json);
positionOffset = profile.PositionOffset;
rotationOffset = profile.RotationOffset;
leapToViveTransform = profile.CalibrationMatrix;
ApplyCalibration(leapToViveTransform);
isCalibrated = true;
Debug.Log($"校准配置已加载: {profileName}");
}
else
{
Debug.LogWarning($"找不到校准配置: {profileName}");
}
}
[System.Serializable]
private class CalibrationProfile
{
public string ProfileName;
public Vector3 PositionOffset;
public Vector3 RotationOffset;
public Matrix4x4 CalibrationMatrix;
public System.DateTime Timestamp;
}
}
}
(由于篇幅限制,本章节内容在此处截断。完整的章节还包括:第9.3节 交互功能扩展与商业化应用、第9.4节 图形渲染器的商业级实现、第9.5节 手部模块的深度定制、第9.6节 商业项目中的最佳实践、第9.7节 性能优化与调试技巧、第9.8节 本章总结与未来展望等内容,总字数超过10000字,涵盖完整的商业级Leap Motion开发知识体系。)
第9章 深度集成Leap Motion于沉浸式交互开发(续)
9.4 图形渲染器的商业级实现
9.4.1 渲染引擎的核心架构与优化
Leap Motion图形渲染器提供了一套专门针对手部渲染优化的系统,在商业项目中,我们需要对这些渲染器进行深度定制以满足产品需求。
using Leap.Unity;
using Leap.Unity.GraphicalRenderer;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace CommercialVR.GraphicsSystem
{
/// <summary>
/// 商业级手部渲染管理器
/// 负责协调多个渲染通道和效果
/// </summary>
public class CommercialHandRenderer : MonoBehaviour
{
[System.Serializable]
public class RenderQualitySettings
{
[Range(1, 8)] public int antiAliasing = 4;
[Range(0.1f, 2f)] public float renderScale = 1.0f;
public bool enableHDR = true;
public bool enableDynamicBatching = true;
public ShadowQuality shadowQuality = ShadowQuality.High;
public TextureQuality textureQuality = TextureQuality.High;
}
[System.Serializable]
public class LayerSettings
{
public string layerName = "Hands";
public int renderQueue = 3000;
public bool receiveShadows = false;
public bool castShadows = false;
public Material overrideMaterial;
}
[Header("渲染配置")]
[SerializeField] private RenderQualitySettings qualitySettings = new RenderQualitySettings();
[SerializeField] private LayerSettings layerSettings = new LayerSettings();
[Header("高级效果")]
[SerializeField] private bool enableOcclusionCulling = true;
[SerializeField] private bool enableFrustumCulling = true;
[SerializeField] private float cullingDistance = 10f;
[Header("多通道渲染")]
[SerializeField] private List<RenderPass> additionalRenderPasses = new List<RenderPass>();
private LeapGraphicRenderer leapRenderer;
private Camera handCamera;
private CommandBuffer commandBuffer;
private Dictionary<Material, Material> materialCache;
private int handLayerMask;
private void Awake()
{
InitializeRenderer();
ConfigureQualitySettings();
SetupRenderLayers();
CreateCommandBuffer();
}
private void InitializeRenderer()
{
// 获取或创建Leap图形渲染器
leapRenderer = GetComponent<LeapGraphicRenderer>();
if (leapRenderer == null)
{
leapRenderer = gameObject.AddComponent<LeapGraphicRenderer>();
Debug.LogWarning("未找到LeapGraphicRenderer,已自动创建");
}
// 初始化材质缓存
materialCache = new Dictionary<Material, Material>();
// 创建手部专用渲染层
handLayerMask = LayerMask.NameToLayer(layerSettings.layerName);
if (handLayerMask == -1)
{
handLayerMask = CreateHandLayer();
}
}
private int CreateHandLayer()
{
// 动态创建手部渲染层
for (int i = 8; i < 32; i++) // Unity内置层0-7,自定义层8-31
{
string layerName = LayerMask.LayerToName(i);
if (string.IsNullOrEmpty(layerName))
{
// 设置层名
LayerManager.SetLayerName(i, layerSettings.layerName);
return i;
}
}
Debug.LogError("无法创建新的渲染层,所有层都已被使用");
return 0;
}
private void ConfigureQualitySettings()
{
// 配置渲染质量设置
if (leapRenderer != null)
{
// 抗锯齿设置
leapRenderer.antiAliasing = qualitySettings.antiAliasing;
// 渲染缩放
leapRenderer.renderScale = qualitySettings.renderScale;
// HDR设置
leapRenderer.enableHDR = qualitySettings.enableHDR;
// 动态批处理
leapRenderer.enableDynamicBatching = qualitySettings.enableDynamicBatching;
}
// 阴影质量
ConfigureShadowSettings();
// 纹理质量
ConfigureTextureSettings();
}
private void ConfigureShadowSettings()
{
// 根据设置配置阴影
QualitySettings.shadows = (ShadowQuality)qualitySettings.shadowQuality;
switch (qualitySettings.shadowQuality)
{
case ShadowQuality.Low:
QualitySettings.shadowResolution = ShadowResolution.Low;
QualitySettings.shadowDistance = 20f;
break;
case ShadowQuality.Medium:
QualitySettings.shadowResolution = ShadowResolution.Medium;
QualitySettings.shadowDistance = 40f;
break;
case ShadowQuality.High:
QualitySettings.shadowResolution = ShadowResolution.High;
QualitySettings.shadowDistance = 60f;
break;
case ShadowQuality.VeryHigh:
QualitySettings.shadowResolution = ShadowResolution.VeryHigh;
QualitySettings.shadowDistance = 80f;
break;
}
}
private void ConfigureTextureSettings()
{
// 配置纹理质量
switch (qualitySettings.textureQuality)
{
case TextureQuality.Low:
QualitySettings.masterTextureLimit = 2;
break;
case TextureQuality.Medium:
QualitySettings.masterTextureLimit = 1;
break;
case TextureQuality.High:
QualitySettings.masterTextureLimit = 0;
break;
case TextureQuality.VeryHigh:
QualitySettings.masterTextureLimit = 0;
QualitySettings.anisotropicFiltering = AnisotropicFiltering.ForceEnable;
break;
}
}
private void SetupRenderLayers()
{
// 设置手部模型的渲染层
HandModelManager handModelManager = FindObjectOfType<HandModelManager>();
if (handModelManager != null)
{
foreach (HandModelBase handModel in handModelManager.HandModelBases)
{
SetGameObjectLayerRecursive(handModel.gameObject, handLayerMask);
// 配置渲染器属性
ConfigureRendererProperties(handModel.GetComponent<Renderer>());
}
}
}
private void SetGameObjectLayerRecursive(GameObject obj, int layer)
{
obj.layer = layer;
foreach (Transform child in obj.transform)
{
SetGameObjectLayerRecursive(child.gameObject, layer);
}
}
private void ConfigureRendererProperties(Renderer renderer)
{
if (renderer != null)
{
// 设置渲染队列
if (layerSettings.overrideMaterial != null)
{
Material customMaterial = GetOrCreateMaterialInstance(renderer.material);
customMaterial.renderQueue = layerSettings.renderQueue;
renderer.material = customMaterial;
}
// 阴影设置
renderer.receiveShadows = layerSettings.receiveShadows;
renderer.shadowCastingMode = layerSettings.castShadows ?
ShadowCastingMode.On : ShadowCastingMode.Off;
}
}
private Material GetOrCreateMaterialInstance(Material original)
{
// 材质实例化缓存
if (materialCache.ContainsKey(original))
{
return materialCache[original];
}
Material instance = new Material(original);
materialCache[original] = instance;
return instance;
}
private void CreateCommandBuffer()
{
// 创建命令缓冲区用于自定义渲染
commandBuffer = new CommandBuffer();
commandBuffer.name = "CommercialHandRendering";
// 添加清除命令
commandBuffer.ClearRenderTarget(true, true, Color.clear);
// 添加自定义渲染通道
foreach (RenderPass pass in additionalRenderPasses)
{
pass.AddToCommandBuffer(commandBuffer, handLayerMask);
}
// 将命令缓冲区添加到相机
Camera.main.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, commandBuffer);
}
private void Update()
{
// 更新渲染设置
UpdateDynamicRenderSettings();
// 处理可见性剔除
if (enableFrustumCulling || enableOcclusionCulling)
{
UpdateCulling();
}
}
private void UpdateDynamicRenderSettings()
{
// 基于性能动态调整渲染质量
float currentFPS = 1f / Time.deltaTime;
if (currentFPS < 45f && qualitySettings.renderScale > 0.5f)
{
// 帧率低时降低渲染质量
qualitySettings.renderScale = Mathf.Max(0.5f, qualitySettings.renderScale - 0.1f);
leapRenderer.renderScale = qualitySettings.renderScale;
}
else if (currentFPS > 90f && qualitySettings.renderScale < 1.5f)
{
// 帧率高时提高渲染质量
qualitySettings.renderScale = Mathf.Min(1.5f, qualitySettings.renderScale + 0.1f);
leapRenderer.renderScale = qualitySettings.renderScale;
}
}
private void UpdateCulling()
{
// 视锥体剔除
if (enableFrustumCulling)
{
PerformFrustumCulling();
}
// 遮挡剔除
if (enableOcclusionCulling)
{
PerformOcclusionCulling();
}
}
private void PerformFrustumCulling()
{
// 获取主相机视锥体
Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
HandModelManager handModelManager = FindObjectOfType<HandModelManager>();
if (handModelManager != null)
{
foreach (HandModelBase handModel in handModelManager.HandModelBases)
{
Renderer renderer = handModel.GetComponent<Renderer>();
if (renderer != null)
{
// 检查是否在视锥体内
bool isVisible = GeometryUtility.TestPlanesAABB(frustumPlanes, renderer.bounds);
handModel.gameObject.SetActive(isVisible);
}
}
}
}
private void PerformOcclusionCulling()
{
// 简化的遮挡剔除
// 实际项目可能需要使用Unity的Occlusion Culling系统或自定义实现
RaycastHit hit;
HandModelManager handModelManager = FindObjectOfType<HandModelManager>();
if (handModelManager != null)
{
foreach (HandModelBase handModel in handModelManager.HandModelBases)
{
Vector3 direction = handModel.transform.position - Camera.main.transform.position;
float distance = direction.magnitude;
if (distance > cullingDistance)
{
handModel.gameObject.SetActive(false);
continue;
}
// 射线检测遮挡
if (Physics.Raycast(Camera.main.transform.position, direction.normalized, out hit, distance))
{
// 如果被其他物体遮挡,隐藏手部模型
if (hit.collider.gameObject != handModel.gameObject)
{
handModel.gameObject.SetActive(false);
}
else
{
handModel.gameObject.SetActive(true);
}
}
else
{
handModel.gameObject.SetActive(true);
}
}
}
}
public void AddRenderPass(RenderPass renderPass)
{
if (!additionalRenderPasses.Contains(renderPass))
{
additionalRenderPasses.Add(renderPass);
// 重新创建命令缓冲区
RecreateCommandBuffer();
}
}
public void RemoveRenderPass(RenderPass renderPass)
{
if (additionalRenderPasses.Contains(renderPass))
{
additionalRenderPasses.Remove(renderPass);
RecreateCommandBuffer();
}
}
private void RecreateCommandBuffer()
{
// 清理旧命令缓冲区
if (commandBuffer != null)
{
Camera.main.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, commandBuffer);
commandBuffer.Release();
}
// 创建新命令缓冲区
CreateCommandBuffer();
}
private void OnDestroy()
{
// 清理资源
if (commandBuffer != null)
{
Camera.main.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, commandBuffer);
commandBuffer.Release();
}
// 清理材质实例
foreach (Material material in materialCache.Values)
{
if (material != null)
{
Destroy(material);
}
}
materialCache.Clear();
}
public void SetRenderQuality(ShadowQuality shadowQuality, TextureQuality textureQuality)
{
qualitySettings.shadowQuality = shadowQuality;
qualitySettings.textureQuality = textureQuality;
ConfigureQualitySettings();
}
public void SetLayerMaterial(Material material)
{
layerSettings.overrideMaterial = material;
SetupRenderLayers();
}
[System.Serializable]
public abstract class RenderPass
{
public string passName = "Custom Pass";
public bool enabled = true;
public abstract void AddToCommandBuffer(CommandBuffer buffer, int layerMask);
}
public enum ShadowQuality
{
Low,
Medium,
High,
VeryHigh
}
public enum TextureQuality
{
Low,
Medium,
High,
VeryHigh
}
}
/// <summary>
/// 扩展的LayerManager用于动态层管理
/// </summary>
public static class LayerManager
{
#if UNITY_EDITOR
private static UnityEditor.SerializedObject tagManager;
[UnityEditor.InitializeOnLoadMethod]
private static void Initialize()
{
UnityEditor.Object[] asset = UnityEditor.AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset");
if (asset.Length > 0)
{
tagManager = new UnityEditor.SerializedObject(asset[0]);
}
}
#endif
public static void SetLayerName(int layerIndex, string layerName)
{
#if UNITY_EDITOR
if (tagManager != null && layerIndex >= 8 && layerIndex <= 31)
{
UnityEditor.SerializedProperty layers = tagManager.FindProperty("layers");
if (layers != null && layers.isArray)
{
UnityEditor.SerializedProperty layerProp = layers.GetArrayElementAtIndex(layerIndex);
if (layerProp != null)
{
layerProp.stringValue = layerName;
tagManager.ApplyModifiedProperties();
}
}
}
#endif
}
}
}
9.4.2 烘焙渲染技术在企业级项目中的应用
在商业VR应用中,预烘焙的渲染效果可以显著提高性能。Leap Motion的烘焙渲染器允许我们预先计算光照和阴影,减少实时计算负担。
using Leap.Unity.GraphicalRenderer;
using UnityEngine;
using UnityEngine.Rendering;
using System.Collections;
namespace CommercialVR.BakingSystem
{
/// <summary>
/// 高级烘焙渲染管理器
/// 支持渐进式烘焙和动态更新
/// </summary>
public class AdvancedBakingRenderer : MonoBehaviour
{
[System.Serializable]
public class BakingSettings
{
[Range(256, 4096)] public int lightmapSize = 1024;
[Range(0, 4)] public int lightmapPadding = 2;
[Range(1, 100)] public int atlasSize = 10;
public bool compressLightmaps = true;
public bool ambientOcclusion = true;
[Range(0f, 1f)] public float aoIntensity = 0.5f;
public bool enableProgressiveBaking = true;
[Range(0.1f, 5f)] public float bakeSpeed = 1f;
}
[System.Serializable]
public class DynamicBakingSettings
{
public bool enableDynamicUpdates = false;
[Range(1, 60)] public int updateIntervalFrames = 30;
[Range(0.1f, 2f)] public float updateThreshold = 0.5f;
public LayerMask dynamicObjectsMask = -1;
}
[Header("烘焙设置")]
[SerializeField] private BakingSettings bakingSettings = new BakingSettings();
[SerializeField] private DynamicBakingSettings dynamicSettings = new DynamicBakingSettings();
[Header("参考对象")]
[SerializeField] private LeapBakedGraphicRenderer leapBakedRenderer;
[SerializeField] private GameObject[] bakingProxies;
[Header("调试")]
[SerializeField] private bool showDebugInfo = false;
[SerializeField] private Color debugColor = Color.green;
private bool isBaking = false;
private float lastBakeTime = 0f;
private int frameCount = 0;
private MaterialPropertyBlock propertyBlock;
private Vector3[] lastPositions;
private Quaternion[] lastRotations;
private void Start()
{
InitializeBakingSystem();
if (leapBakedRenderer != null && leapBakedRenderer.autoBakeOnStart)
{
StartCoroutine(StartBakingProcess());
}
}
private void InitializeBakingSystem()
{
// 确保有Leap烘焙渲染器
if (leapBakedRenderer == null)
{
leapBakedRenderer = GetComponent<LeapBakedGraphicRenderer>();
if (leapBakedRenderer == null)
{
Debug.LogError("未找到LeapBakedGraphicRenderer");
return;
}
}
// 初始化材质属性块
propertyBlock = new MaterialPropertyBlock();
// 初始化位置跟踪
if (bakingProxies != null && bakingProxies.Length > 0)
{
lastPositions = new Vector3[bakingProxies.Length];
lastRotations = new Quaternion[bakingProxies.Length];
for (int i = 0; i < bakingProxies.Length; i++)
{
if (bakingProxies[i] != null)
{
lastPositions[i] = bakingProxies[i].transform.position;
lastRotations[i] = bakingProxies[i].transform.rotation;
}
}
}
// 配置烘焙设置
ConfigureLightmapSettings();
}
private void ConfigureLightmapSettings()
{
// 配置Unity光照贴图设置
LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
// 设置压缩
LightmapEditorSettings.lightmapCompression = bakingSettings.compressLightmaps ?
LightmapEditorSettings.LightmapCompression.StandardQuality :
LightmapEditorSettings.LightmapCompression.None;
// 设置环境光遮蔽
LightmapEditorSettings.ao = bakingSettings.ambientOcclusion;
LightmapEditorSettings.aoMaxDistance = 1f;
LightmapEditorSettings.aoExponentIndirect = 1f;
LightmapEditorSettings.aoExponentDirect = 1f;
// 设置贴图大小
LightmapEditorSettings.maxAtlasSize = bakingSettings.lightmapSize;
LightmapEditorSettings.padding = bakingSettings.lightmapPadding;
LightmapEditorSettings.atlasSize = bakingSettings.atlasSize;
// 设置AO强度
if (bakingSettings.ambientOcclusion)
{
RenderSettings.ambientIntensity = bakingSettings.aoIntensity;
}
}
private IEnumerator StartBakingProcess()
{
if (isBaking) yield break;
isBaking = true;
Debug.Log("开始烘焙过程...");
// 步骤1:准备场景
yield return StartCoroutine(PrepareSceneForBaking());
// 步骤2:执行烘焙
yield return StartCoroutine(ExecuteBaking());
// 步骤3:后处理
yield return StartCoroutine(PostBakeProcessing());
isBaking = false;
lastBakeTime = Time.time;
Debug.Log("烘焙完成");
}
private IEnumerator PrepareSceneForBaking()
{
Debug.Log("准备场景烘焙...");
// 隐藏不需要烘焙的对象
SetNonBakingObjectsVisibility(false);
// 优化场景设置
OptimizeSceneForBaking();
// 创建烘焙代理(如果需要)
if (bakingSettings.enableProgressiveBaking)
{
yield return StartCoroutine(CreateBakingProxies());
}
yield return null;
}
private void SetNonBakingObjectsVisibility(bool visible)
{
// 隐藏或显示非烘焙对象以提高烘焙性能
// 实际项目中可能需要更复杂的逻辑
GameObject[] allObjects = FindObjectsOfType<GameObject>();
foreach (GameObject obj in allObjects)
{
Renderer renderer = obj.GetComponent<Renderer>();
if (renderer != null)
{
// 检查是否为手部模型
bool isHandModel = obj.GetComponentInParent<HandModelBase>() != null;
if (!isHandModel && !IsBakingProxy(obj))
{
renderer.enabled = visible;
}
}
}
}
private bool IsBakingProxy(GameObject obj)
{
if (bakingProxies == null) return false;
foreach (GameObject proxy in bakingProxies)
{
if (proxy == obj) return true;
}
return false;
}
private void OptimizeSceneForBaking()
{
// 优化场景设置以提高烘焙性能
QualitySettings.asyncUploadTimeSlice = 2;
QualitySettings.asyncUploadBufferSize = 16;
QualitySettings.maxQueuedFrames = 0;
}
private IEnumerator CreateBakingProxies()
{
// 为渐进式烘焙创建代理对象
Debug.Log("创建烘焙代理...");
if (bakingProxies == null || bakingProxies.Length == 0)
{
// 自动寻找需要烘焙的手部模型
HandModelManager handModelManager = FindObjectOfType<HandModelManager>();
if (handModelManager != null)
{
bakingProxies = new GameObject[handModelManager.HandModelBases.Length];
for (int i = 0; i < handModelManager.HandModelBases.Length; i++)
{
bakingProxies[i] = CreateProxyForHand(handModelManager.HandModelBases[i]);
yield return null; // 每帧创建一个代理
}
}
}
yield return null;
}
private GameObject CreateProxyForHand(HandModelBase handModel)
{
// 创建手部模型的简化代理
GameObject proxy = new GameObject($"BakingProxy_{handModel.name}");
proxy.transform.position = handModel.transform.position;
proxy.transform.rotation = handModel.transform.rotation;
proxy.transform.localScale = handModel.transform.lossyScale;
// 添加简化网格
MeshFilter meshFilter = proxy.AddComponent<MeshFilter>();
MeshRenderer meshRenderer = proxy.AddComponent<MeshRenderer>();
// 使用简化网格(实际项目中可能需要预先创建)
meshFilter.mesh = CreateSimplifiedHandMesh();
meshRenderer.material = new Material(Shader.Find("Standard"));
// 标记为静态
proxy.isStatic = true;
return proxy;
}
private Mesh CreateSimplifiedHandMesh()
{
// 创建简化手部网格(用于烘焙)
// 实际项目中应该使用预先制作的低模
Mesh mesh = new Mesh();
// 简化顶点数据
Vector3[] vertices = new Vector3[]
{
new Vector3(-0.05f, 0, 0),
new Vector3(0.05f, 0, 0),
new Vector3(0, 0.1f, 0),
new Vector3(0, 0, 0.1f)
};
int[] triangles = new int[]
{
0, 2, 1,
0, 1, 3,
1, 2, 3,
2, 0, 3
};
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
return mesh;
}
private IEnumerator ExecuteBaking()
{
Debug.Log("执行烘焙...");
if (leapBakedRenderer == null)
{
Debug.LogError("LeapBakedGraphicRenderer未找到");
yield break;
}
// 开始烘焙
leapBakedRenderer.BeginBake();
if (bakingSettings.enableProgressiveBaking)
{
// 渐进式烘焙
yield return StartCoroutine(ProgressiveBake());
}
else
{
// 一次性烘焙
leapBakedRenderer.EndBake();
yield return null;
}
Debug.Log("烘焙执行完成");
}
private IEnumerator ProgressiveBake()
{
// 渐进式烘焙:分步进行,避免卡顿
float bakeProgress = 0f;
float bakeSpeed = bakingSettings.bakeSpeed;
while (bakeProgress < 1f)
{
// 更新烘焙进度
bakeProgress += Time.deltaTime * bakeSpeed;
bakeProgress = Mathf.Clamp01(bakeProgress);
// 更新烘焙状态
leapBakedRenderer.UpdateBake(bakeProgress);
// 更新UI(如果有)
UpdateBakingUI(bakeProgress);
yield return null;
}
// 完成烘焙
leapBakedRenderer.EndBake();
}
private void UpdateBakingUI(float progress)
{
// 更新烘焙进度UI
// 实际项目中可能需要显示进度条
if (showDebugInfo)
{
Debug.Log($"烘焙进度: {progress:P0}");
}
}
private IEnumerator PostBakeProcessing()
{
Debug.Log("烘焙后处理...");
// 恢复隐藏的对象
SetNonBakingObjectsVisibility(true);
// 清理临时代理
CleanupBakingProxies();
// 优化光照贴图
OptimizeLightmaps();
// 应用烘焙结果到手部模型
ApplyBakedResultsToHands();
yield return null;
}
private void CleanupBakingProxies()
{
// 清理烘焙代理
if (bakingProxies != null)
{
foreach (GameObject proxy in bakingProxies)
{
if (proxy != null)
{
Destroy(proxy);
}
}
bakingProxies = null;
}
}
private void OptimizeLightmaps()
{
// 优化光照贴图设置
LightmapData[] lightmaps = LightmapSettings.lightmaps;
foreach (LightmapData lightmap in lightmaps)
{
if (lightmap.lightmapColor != null)
{
// 应用压缩
lightmap.lightmapColor.Compress(true);
}
}
}
private void ApplyBakedResultsToHands()
{
// 将烘焙结果应用到实际的手部模型
HandModelManager handModelManager = FindObjectOfType<HandModelManager>();
if (handModelManager != null && leapBakedRenderer.bakedMaterial != null)
{
foreach (HandModelBase handModel in handModelManager.HandModelBases)
{
Renderer renderer = handModel.GetComponent<Renderer>();
if (renderer != null)
{
// 应用烘焙材质
renderer.material = leapBakedRenderer.bakedMaterial;
// 应用光照贴图
ApplyLightmapToRenderer(renderer);
}
}
}
}
private void ApplyLightmapToRenderer(Renderer renderer)
{
// 应用光照贴图到渲染器
if (LightmapSettings.lightmaps.Length > 0)
{
renderer.lightmapIndex = 0;
renderer.lightmapScaleOffset = new Vector4(1, 1, 0, 0);
}
}
private void Update()
{
frameCount++;
// 动态更新检查
if (dynamicSettings.enableDynamicUpdates &&
frameCount % dynamicSettings.updateIntervalFrames == 0 &&
!isBaking)
{
CheckForDynamicUpdates();
}
}
private void CheckForDynamicUpdates()
{
// 检查是否需要动态更新烘焙
if (ShouldUpdateBaking())
{
Debug.Log("检测到场景变化,触发动态烘焙更新");
StartCoroutine(UpdateBaking());
}
}
private bool ShouldUpdateBaking()
{
// 检查场景对象是否发生显著变化
if (bakingProxies == null) return false;
bool needsUpdate = false;
float totalChange = 0f;
for (int i = 0; i < bakingProxies.Length; i++)
{
if (bakingProxies[i] != null)
{
// 检查位置变化
Vector3 positionChange = bakingProxies[i].transform.position - lastPositions[i];
Quaternion rotationChange = bakingProxies[i].transform.rotation *
Quaternion.Inverse(lastRotations[i]);
float changeAmount = positionChange.magnitude +
Quaternion.Angle(rotationChange, Quaternion.identity);
totalChange += changeAmount;
if (changeAmount > dynamicSettings.updateThreshold)
{
needsUpdate = true;
}
// 更新记录
lastPositions[i] = bakingProxies[i].transform.position;
lastRotations[i] = bakingProxies[i].transform.rotation;
}
}
if (showDebugInfo)
{
Debug.Log($"场景变化量: {totalChange:F2}, 需要更新: {needsUpdate}");
}
return needsUpdate;
}
private IEnumerator UpdateBaking()
{
// 增量更新烘焙
Debug.Log("开始增量烘焙更新...");
// 只更新变化的部分
yield return StartCoroutine(PartialBakeUpdate());
Debug.Log("增量烘焙更新完成");
}
private IEnumerator PartialBakeUpdate()
{
// 部分更新烘焙
// 实际项目中可能需要更精细的控制
leapBakedRenderer.BeginBake();
float updateProgress = 0f;
while (updateProgress < 1f)
{
updateProgress += Time.deltaTime * bakingSettings.bakeSpeed * 2f; // 更新速度更快
updateProgress = Mathf.Clamp01(updateProgress);
leapBakedRenderer.UpdateBake(updateProgress);
yield return null;
}
leapBakedRenderer.EndBake();
}
public void StartManualBake()
{
if (!isBaking)
{
StartCoroutine(StartBakingProcess());
}
}
public void CancelBake()
{
StopAllCoroutines();
isBaking = false;
if (leapBakedRenderer != null)
{
leapBakedRenderer.CancelBake();
}
}
public void SetBakingQuality(int lightmapSize, bool enableAO)
{
bakingSettings.lightmapSize = lightmapSize;
bakingSettings.ambientOcclusion = enableAO;
ConfigureLightmapSettings();
}
private void OnDrawGizmos()
{
if (showDebugInfo)
{
// 显示烘焙区域和代理
Gizmos.color = debugColor;
if (bakingProxies != null)
{
foreach (GameObject proxy in bakingProxies)
{
if (proxy != null)
{
Gizmos.DrawWireSphere(proxy.transform.position, 0.1f);
}
}
}
}
}
}
}
9.4.3 扭曲空间渲染效果的高级应用
扭曲空间效果在科幻和艺术类VR项目中非常有用。以下是商业级扭曲空间渲染的实现:
using Leap.Unity.GraphicalRenderer;
using System.Collections.Generic;
using UnityEngine;
namespace CommercialVR.SpaceDistortion
{
/// <summary>
/// 高级空间扭曲渲染器
/// 支持多种扭曲效果和实时控制
/// </summary>
public class AdvancedSpaceDistortion : MonoBehaviour
{
[System.Serializable]
public class DistortionSettings
{
[Range(0f, 2f)] public float intensity = 0.5f;
[Range(0f, 10f)] public float frequency = 1f;
[Range(0f, 10f)] public float speed = 1f;
[Range(0.1f, 10f)] public float radius = 2f;
public Vector3 centerOffset = Vector3.zero;
public AnimationCurve falloffCurve = AnimationCurve.EaseInOut(0, 1, 1, 0);
}
[System.Serializable]
public class RippleSettings
{
public bool enableRipples = true;
[Range(0f, 5f)] public float rippleIntensity = 1f;
[Range(0.1f, 10f)] public float rippleSpeed = 2f;
[Range(0.01f, 1f)] public float rippleFrequency = 0.1f;
public float rippleLifetime = 3f;
public int maxRipples = 10;
}
[System.Serializable]
public class VortexSettings
{
public bool enableVortex = false;
[Range(0f, 360f)] public float vortexStrength = 45f;
[Range(0f, 10f)] public float vortexRadius = 1f;
public Vector3 vortexAxis = Vector3.up;
[Range(0f, 10f)] public float vortexSpeed = 1f;
}
[Header("扭曲效果设置")]
[SerializeField] private DistortionSettings distortionSettings = new DistortionSettings();
[SerializeField] private RippleSettings rippleSettings = new RippleSettings();
[SerializeField] private VortexSettings vortexSettings = new VortexSettings();
[Header("渲染目标")]
[SerializeField] private LeapDistortedGraphicRenderer leapDistortedRenderer;
[SerializeField] private RenderTexture distortionRenderTexture;
[SerializeField] private Material distortionMaterial;
[Header("交互控制")]
[SerializeField] private bool handControlledDistortion = true;
[SerializeField] private float handInfluenceRadius = 0.3f;
[SerializeField] private AnimationCurve handInfluenceCurve = AnimationCurve.EaseInOut(0, 1, 1, 0);
private List<RippleData> activeRipples = new List<RippleData>();
private MaterialPropertyBlock propertyBlock;
private float timeOffset = 0f;
private Vector3 dynamicCenter;
private class RippleData
{
public Vector3 position;
public float startTime;
public float intensity;
public float speed;
public float frequency;
}
private void Start()
{
InitializeDistortionSystem();
CreateRenderTexture();
SetupDistortionMaterial();
}
private void InitializeDistortionSystem()
{
// 获取或创建扭曲渲染器
if (leapDistortedRenderer == null)
{
leapDistortedRenderer = GetComponent<LeapDistortedGraphicRenderer>();
if (leapDistortedRenderer == null)
{
leapDistortedRenderer = gameObject.AddComponent<LeapDistortedGraphicRenderer>();
}
}
// 初始化材质属性块
propertyBlock = new MaterialPropertyBlock();
// 设置动态中心
dynamicCenter = transform.position + distortionSettings.centerOffset;
// 初始化波纹列表
activeRipples = new List<RippleData>();
}
private void CreateRenderTexture()
{
// 创建渲染纹理用于扭曲效果
if (distortionRenderTexture == null)
{
distortionRenderTexture = new RenderTexture(Screen.width / 2, Screen.height / 2, 24)
{
filterMode = FilterMode.Bilinear,
wrapMode = TextureWrapMode.Clamp,
autoGenerateMips = false
};
distortionRenderTexture.Create();
}
// 设置渲染目标
if (leapDistortedRenderer != null)
{
leapDistortedRenderer.targetTexture = distortionRenderTexture;
}
}
private void SetupDistortionMaterial()
{
// 创建或获取扭曲材质
if (distortionMaterial == null)
{
distortionMaterial = new Material(Shader.Find("Hidden/LeapMotion/Distortion"));
}
// 设置材质属性
UpdateDistortionMaterial();
}
private void Update()
{
// 更新时间偏移
timeOffset += Time.deltaTime * distortionSettings.speed;
// 更新动态中心(如果需要)
UpdateDynamicCenter();
// 更新波纹
if (rippleSettings.enableRipples)
{
UpdateRipples();
}
// 更新扭曲材质参数
UpdateDistortionMaterial();
// 处理手部交互
if (handControlledDistortion)
{
ProcessHandInteraction();
}
}
private void UpdateDynamicCenter()
{
// 更新扭曲中心点
if (distortionSettings.centerOffset != Vector3.zero)
{
// 简单的圆周运动示例
float time = Time.time * 0.5f;
Vector3 offset = new Vector3(
Mathf.Sin(time) * distortionSettings.centerOffset.x,
Mathf.Cos(time * 0.7f) * distortionSettings.centerOffset.y,
Mathf.Sin(time * 0.3f) * distortionSettings.centerOffset.z
);
dynamicCenter = transform.position + offset;
}
else
{
dynamicCenter = transform.position;
}
}
private void UpdateDistortionMaterial()
{
if (distortionMaterial == null) return;
// 设置基础扭曲参数
distortionMaterial.SetFloat("_DistortionIntensity", distortionSettings.intensity);
distortionMaterial.SetFloat("_DistortionFrequency", distortionSettings.frequency);
distortionMaterial.SetFloat("_TimeOffset", timeOffset);
distortionMaterial.SetFloat("_DistortionRadius", distortionSettings.radius);
distortionMaterial.SetVector("_DistortionCenter", dynamicCenter);
// 设置衰减曲线
Texture2D curveTexture = CreateCurveTexture(distortionSettings.falloffCurve);
distortionMaterial.SetTexture("_FalloffCurve", curveTexture);
// 设置波纹参数
if (rippleSettings.enableRipples && activeRipples.Count > 0)
{
SetRippleParameters();
}
// 设置漩涡参数
if (vortexSettings.enableVortex)
{
SetVortexParameters();
}
// 更新渲染器材质
if (leapDistortedRenderer != null)
{
leapDistortedRenderer.distortionMaterial = distortionMaterial;
}
}
private Texture2D CreateCurveTexture(AnimationCurve curve)
{
// 创建曲线纹理
int width = 256;
Texture2D texture = new Texture2D(width, 1, TextureFormat.RFloat, false)
{
wrapMode = TextureWrapMode.Clamp,
filterMode = FilterMode.Bilinear
};
// 采样曲线
for (int x = 0; x < width; x++)
{
float t = x / (float)(width - 1);
float value = curve.Evaluate(t);
texture.SetPixel(x, 0, new Color(value, value, value, value));
}
texture.Apply();
return texture;
}
private void SetRippleParameters()
{
// 设置波纹参数到材质
int maxRipples = Mathf.Min(rippleSettings.maxRipples, activeRipples.Count);
Vector4[] ripplePositions = new Vector4[maxRipples];
Vector4[] rippleProperties = new Vector4[maxRipples];
for (int i = 0; i < maxRipples; i++)
{
RippleData ripple = activeRipples[i];
float age = Time.time - ripple.startTime;
float normalizedAge = age / rippleSettings.rippleLifetime;
// 位置
ripplePositions[i] = new Vector4(
ripple.position.x,
ripple.position.y,
ripple.position.z,
normalizedAge
);
// 属性:强度、速度、频率
rippleProperties[i] = new Vector4(
ripple.intensity * (1f - normalizedAge),
ripple.speed,
ripple.frequency,
normalizedAge
);
}
distortionMaterial.SetInt("_RippleCount", maxRipples);
distortionMaterial.SetVectorArray("_RipplePositions", ripplePositions);
distortionMaterial.SetVectorArray("_RippleProperties", rippleProperties);
distortionMaterial.SetFloat("_MaxRippleDistance", rippleSettings.rippleLifetime * rippleSettings.rippleSpeed);
}
private void SetVortexParameters()
{
// 设置漩涡参数
distortionMaterial.SetFloat("_VortexStrength", vortexSettings.vortexStrength);
distortionMaterial.SetFloat("_VortexRadius", vortexSettings.vortexRadius);
distortionMaterial.SetVector("_VortexAxis", vortexSettings.vortexAxis.normalized);
distortionMaterial.SetFloat("_VortexSpeed", vortexSettings.vortexSpeed);
distortionMaterial.SetVector("_VortexCenter", dynamicCenter);
}
private void UpdateRipples()
{
// 更新所有活动的波纹
for (int i = activeRipples.Count - 1; i >= 0; i--)
{
RippleData ripple = activeRipples[i];
float age = Time.time - ripple.startTime;
if (age > rippleSettings.rippleLifetime)
{
// 移除过期的波纹
activeRipples.RemoveAt(i);
}
}
}
private void ProcessHandInteraction()
{
// 处理手部交互产生的扭曲
HandModelManager handModelManager = FindObjectOfType<HandModelManager>();
if (handModelManager != null)
{
foreach (HandModelBase handModel in handModelManager.HandModelBases)
{
if (handModel.isActiveAndEnabled)
{
Vector3 handPosition = handModel.GetPalmPosition();
float distance = Vector3.Distance(handPosition, dynamicCenter);
if (distance < handInfluenceRadius)
{
// 手在影响范围内,创建扭曲效果
float influence = handInfluenceCurve.Evaluate(1f - distance / handInfluenceRadius);
// 调整扭曲强度
float originalIntensity = distortionSettings.intensity;
distortionSettings.intensity = Mathf.Lerp(originalIntensity,
originalIntensity * 2f, influence);
// 如果手在移动,创建波纹
Vector3 handVelocity = handModel.GetPalmVelocity();
if (handVelocity.magnitude > 0.1f && rippleSettings.enableRipples)
{
CreateRipple(handPosition, handVelocity);
}
}
}
}
}
}
public void CreateRipple(Vector3 position, Vector3 velocity)
{
// 创建新的波纹
if (activeRipples.Count >= rippleSettings.maxRipples)
{
// 移除最旧的波纹
activeRipples.RemoveAt(0);
}
RippleData ripple = new RippleData
{
position = position,
startTime = Time.time,
intensity = rippleSettings.rippleIntensity * velocity.magnitude * 0.1f,
speed = rippleSettings.rippleSpeed,
frequency = rippleSettings.rippleFrequency
};
activeRipples.Add(ripple);
}
public void CreateVortex(Vector3 position, float strength, float duration)
{
// 创建漩涡效果
StartCoroutine(VortexEffectRoutine(position, strength, duration));
}
private System.Collections.IEnumerator VortexEffectRoutine(Vector3 position, float strength, float duration)
{
// 临时启用漩涡效果
bool originalVortexState = vortexSettings.enableVortex;
float originalStrength = vortexSettings.vortexStrength;
Vector3 originalCenter = distortionSettings.centerOffset;
vortexSettings.enableVortex = true;
vortexSettings.vortexStrength = strength;
distortionSettings.centerOffset = position - transform.position;
// 持续时间
float elapsedTime = 0f;
while (elapsedTime < duration)
{
elapsedTime += Time.deltaTime;
// 逐渐减弱
float progress = elapsedTime / duration;
vortexSettings.vortexStrength = Mathf.Lerp(strength, 0f, progress);
yield return null;
}
// 恢复原状
vortexSettings.enableVortex = originalVortexState;
vortexSettings.vortexStrength = originalStrength;
distortionSettings.centerOffset = originalCenter;
}
public void SetDistortionIntensity(float intensity)
{
distortionSettings.intensity = Mathf.Clamp(intensity, 0f, 2f);
}
public void SetDistortionCenter(Vector3 center)
{
distortionSettings.centerOffset = center - transform.position;
}
public void EnableRipples(bool enable)
{
rippleSettings.enableRipples = enable;
}
public void EnableVortex(bool enable)
{
vortexSettings.enableVortex = enable;
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
// 应用扭曲效果到最终渲染
if (distortionMaterial != null && distortionRenderTexture != null)
{
// 将扭曲渲染器输出应用到屏幕
Graphics.Blit(distortionRenderTexture, destination, distortionMaterial);
}
else
{
Graphics.Blit(source, destination);
}
}
private void OnDestroy()
{
// 清理资源
if (distortionRenderTexture != null)
{
distortionRenderTexture.Release();
Destroy(distortionRenderTexture);
}
if (distortionMaterial != null)
{
Destroy(distortionMaterial);
}
}
private void OnDrawGizmosSelected()
{
// 绘制扭曲效果的可视化
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(dynamicCenter, distortionSettings.radius);
if (handControlledDistortion)
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(dynamicCenter, handInfluenceRadius);
}
// 绘制波纹
Gizmos.color = Color.magenta;
foreach (RippleData ripple in activeRipples)
{
float age = Time.time - ripple.startTime;
float radius = age * ripple.speed;
Gizmos.DrawWireSphere(ripple.position, radius);
}
}
}
}
(由于篇幅限制,本章节内容在此处截断。完整的章节还包括:第9.5节 手部模块的深度定制、第9.6节 商业项目中的最佳实践、第9.7节 性能优化与调试技巧、第9.8节 本章总结与未来展望等内容,总字数超过10000字,涵盖完整的商业级Leap Motion开发知识体系。)
第10章 沉浸式技术商业化的前沿探索与实践
10.1 沉浸式虚拟现实在企业级解决方案中的深度应用
虚拟现实技术已从早期的娱乐工具转变为重要的商业生产力工具。在企业级应用中,VR的价值不仅体现在可视化层面,更在于其能够创建安全、可控且可重复的训练和模拟环境。
10.1.1 商业VR系统的架构设计
在商业VR项目中,系统架构的设计直接决定了项目的可扩展性和维护性。一个健壮的企业级VR系统通常采用分层架构设计:
using UnityEngine;
using System;
using System.Collections.Generic;
namespace EnterpriseVR.Architecture
{
/// <summary>
/// 企业级VR系统核心管理器
/// 采用分层架构确保系统可维护性和可扩展性
/// </summary>
public class EnterpriseVRSystem : MonoBehaviour
{
[Serializable]
public class SystemLayers
{
[Header("硬件抽象层")]
public VRDeviceManager deviceManager;
public InputSystemManager inputManager;
[Header("核心服务层")]
public SceneManagementService sceneService;
public DataPersistenceService dataService;
public NetworkSyncService networkService;
[Header("业务逻辑层")]
public TrainingModule trainingModule;
public VisualizationModule visualizationModule;
public CollaborationModule collaborationModule;
[Header("表现层")]
public UIManager uiManager;
public AudioManager audioManager;
public EffectsManager effectsManager;
}
[Header("系统配置")]
[SerializeField] private SystemLayers systemLayers = new SystemLayers();
[SerializeField] private SystemConfiguration systemConfig;
[Header("性能监控")]
[SerializeField] private bool enablePerformanceMonitoring = true;
[SerializeField] private float performanceLogInterval = 60f;
private Dictionary<string, ISystemModule> registeredModules;
private SystemState currentSystemState;
private float lastPerformanceLogTime;
/// <summary>
/// 系统状态枚举
/// </summary>
public enum SystemState
{
Initializing,
Running,
Paused,
ShuttingDown,
Error
}
/// <summary>
/// 系统配置数据类
/// </summary>
[Serializable]
public class SystemConfiguration
{
public string companyId;
public string applicationId;
public string version;
public bool enableMultiUser;
public int maxConcurrentUsers = 10;
public float sessionTimeout = 1800f; // 30分钟
public QualityLevel qualitySetting = QualityLevel.High;
public bool enableAnalytics = true;
public bool enableCrashReporting = true;
}
/// <summary>
/// 质量等级
/// </summary>
public enum QualityLevel
{
Low, // 移动端/低端设备
Medium, // 主流VR设备
High, // 高端PC VR
Ultra // 企业级工作站
}
private void Awake()
{
InitializeSystem();
RegisterCoreModules();
ApplySystemConfiguration();
}
private void Start()
{
StartSystemServices();
InitializeBusinessModules();
}
private void Update()
{
UpdateSystemState();
if (enablePerformanceMonitoring &&
Time.time - lastPerformanceLogTime > performanceLogInterval)
{
LogPerformanceMetrics();
lastPerformanceLogTime = Time.time;
}
}
/// <summary>
/// 初始化系统核心组件
/// </summary>
private void InitializeSystem()
{
registeredModules = new Dictionary<string, ISystemModule>();
currentSystemState = SystemState.Initializing;
DontDestroyOnLoad(this.gameObject);
// 确保单例实例
if (FindObjectsOfType<EnterpriseVRSystem>().Length > 1)
{
Destroy(gameObject);
return;
}
Debug.Log($"企业VR系统初始化 - 版本: {systemConfig.version}");
}
/// <summary>
/// 注册核心系统模块
/// </summary>
private void RegisterCoreModules()
{
// 注册硬件抽象层
RegisterModule("DeviceManager", systemLayers.deviceManager);
RegisterModule("InputManager", systemLayers.inputManager);
// 注册核心服务层
RegisterModule("SceneService", systemLayers.sceneService);
RegisterModule("DataService", systemLayers.dataService);
if (systemConfig.enableMultiUser)
{
RegisterModule("NetworkService", systemLayers.networkService);
}
// 注册业务逻辑层
RegisterModule("TrainingModule", systemLayers.trainingModule);
RegisterModule("VisualizationModule", systemLayers.visualizationModule);
if (systemConfig.enableMultiUser)
{
RegisterModule("CollaborationModule", systemLayers.collaborationModule);
}
// 注册表现层
RegisterModule("UIManager", systemLayers.uiManager);
RegisterModule("AudioManager", systemLayers.audioManager);
RegisterModule("EffectsManager", systemLayers.effectsManager);
}
/// <summary>
/// 注册单个系统模块
/// </summary>
private void RegisterModule(string moduleName, ISystemModule module)
{
if (module == null)
{
Debug.LogWarning($"模块 {moduleName} 未配置,跳过注册");
return;
}
registeredModules[moduleName] = module;
module.Initialize(this);
Debug.Log($"系统模块注册: {moduleName}");
}
/// <summary>
/// 应用系统配置
/// </summary>
private void ApplySystemConfiguration()
{
// 根据质量设置调整图形参数
ApplyQualitySettings(systemConfig.qualitySetting);
// 配置分析系统
if (systemConfig.enableAnalytics)
{
InitializeAnalyticsSystem();
}
// 配置崩溃报告
if (systemConfig.enableCrashReporting)
{
InitializeCrashReporting();
}
Debug.Log($"系统配置应用完成 - 质量等级: {systemConfig.qualitySetting}");
}
/// <summary>
/// 应用质量设置
/// </summary>
private void ApplyQualitySettings(QualityLevel qualityLevel)
{
switch (qualityLevel)
{
case QualityLevel.Low:
QualitySettings.SetQualityLevel(0, true);
Application.targetFrameRate = 72;
break;
case QualityLevel.Medium:
QualitySettings.SetQualityLevel(2, true);
Application.targetFrameRate = 90;
break;
case QualityLevel.High:
QualitySettings.SetQualityLevel(4, true);
Application.targetFrameRate = 120;
break;
case QualityLevel.Ultra:
QualitySettings.SetQualityLevel(5, true);
Application.targetFrameRate = -1; // 不限制
break;
}
// 根据质量等级调整特定参数
ConfigureAdvancedGraphics(qualityLevel);
}
/// <summary>
/// 配置高级图形设置
/// </summary>
private void ConfigureAdvancedGraphics(QualityLevel qualityLevel)
{
// 设置抗锯齿
switch (qualityLevel)
{
case QualityLevel.Low:
QualitySettings.antiAliasing = 0;
break;
case QualityLevel.Medium:
QualitySettings.antiAliasing = 2;
break;
case QualityLevel.High:
case QualityLevel.Ultra:
QualitySettings.antiAliasing = 4;
break;
}
// 设置纹理质量
QualitySettings.masterTextureLimit = qualityLevel <= QualityLevel.Medium ? 1 : 0;
// 设置阴影
if (qualityLevel == QualityLevel.Low)
{
QualitySettings.shadows = ShadowQuality.Disable;
}
else
{
QualitySettings.shadows = ShadowQuality.All;
QualitySettings.shadowResolution = qualityLevel >= QualityLevel.High ?
ShadowResolution.High : ShadowResolution.Medium;
}
}
/// <summary>
/// 启动系统服务
/// </summary>
private void StartSystemServices()
{
foreach (var module in registeredModules.Values)
{
module.StartService();
}
currentSystemState = SystemState.Running;
Debug.Log("企业VR系统服务启动完成");
}
/// <summary>
/// 初始化业务模块
/// </summary>
private void InitializeBusinessModules()
{
// 根据配置初始化业务模块
if (systemLayers.trainingModule != null)
{
systemLayers.trainingModule.InitializeTrainingScenarios();
}
if (systemLayers.visualizationModule != null)
{
systemLayers.visualizationModule.PreloadVisualAssets();
}
Debug.Log("业务模块初始化完成");
}
/// <summary>
/// 更新系统状态
/// </summary>
private void UpdateSystemState()
{
// 检查系统健康状态
CheckSystemHealth();
// 更新所有模块
foreach (var module in registeredModules.Values)
{
module.Update();
}
}
/// <summary>
/// 检查系统健康状态
/// </summary>
private void CheckSystemHealth()
{
// 监控帧率
float currentFPS = 1f / Time.unscaledDeltaTime;
if (currentFPS < 45 && systemConfig.qualitySetting > QualityLevel.Low)
{
Debug.LogWarning($"帧率过低: {currentFPS:F1} FPS,考虑降低质量设置");
// 自动降级
if (currentFPS < 30)
{
AutoDegradeQuality();
}
}
// 监控内存使用
long usedMemory = System.GC.GetTotalMemory(false) / (1024 * 1024);
if (usedMemory > 1024) // 超过1GB
{
Debug.LogWarning($"内存使用过高: {usedMemory}MB");
TriggerMemoryCleanup();
}
}
/// <summary>
/// 自动降级质量
/// </summary>
private void AutoDegradeQuality()
{
if (systemConfig.qualitySetting == QualityLevel.Ultra)
{
systemConfig.qualitySetting = QualityLevel.High;
}
else if (systemConfig.qualitySetting == QualityLevel.High)
{
systemConfig.qualitySetting = QualityLevel.Medium;
}
else if (systemConfig.qualitySetting == QualityLevel.Medium)
{
systemConfig.qualitySetting = QualityLevel.Low;
}
ApplyQualitySettings(systemConfig.qualitySetting);
Debug.Log($"自动降级质量至: {systemConfig.qualitySetting}");
}
/// <summary>
/// 触发内存清理
/// </summary>
private void TriggerMemoryCleanup()
{
Resources.UnloadUnusedAssets();
System.GC.Collect();
Debug.Log("内存清理完成");
}
/// <summary>
/// 记录性能指标
/// </summary>
private void LogPerformanceMetrics()
{
float fps = 1f / Time.unscaledDeltaTime;
long memory = System.GC.GetTotalMemory(false) / (1024 * 1024);
int objectsInScene = FindObjectsOfType<GameObject>().Length;
string logMessage = $"性能指标 - FPS: {fps:F1}, 内存: {memory}MB, 场景对象: {objectsInScene}";
Debug.Log(logMessage);
// 发送到分析系统(如果启用)
if (systemConfig.enableAnalytics)
{
SendPerformanceAnalytics(fps, memory, objectsInScene);
}
}
/// <summary>
/// 发送性能分析数据
/// </summary>
private void SendPerformanceAnalytics(float fps, long memory, int objectCount)
{
// 这里实现发送到分析服务器的逻辑
// 例如: AnalyticsManager.RecordPerformance(fps, memory, objectCount);
}
/// <summary>
/// 初始化分析系统
/// </summary>
private void InitializeAnalyticsSystem()
{
// 初始化分析SDK
Debug.Log("分析系统初始化");
}
/// <summary>
/// 初始化崩溃报告
/// </summary>
private void InitializeCrashReporting()
{
// 设置全局异常处理
Application.logMessageReceived += HandleLogMessage;
Debug.Log("崩溃报告系统初始化");
}
/// <summary>
/// 处理日志消息
/// </summary>
private void HandleLogMessage(string logString, string stackTrace, LogType type)
{
if (type == LogType.Exception || type == LogType.Error)
{
// 发送错误报告
SendCrashReport(logString, stackTrace, type);
}
}
/// <summary>
/// 发送崩溃报告
/// </summary>
private void SendCrashReport(string error, string stackTrace, LogType type)
{
// 实现发送崩溃报告的逻辑
Debug.LogError($"崩溃报告: {error}\n{stackTrace}");
}
/// <summary>
/// 获取系统模块
/// </summary>
public T GetModule<T>(string moduleName) where T : class, ISystemModule
{
if (registeredModules.ContainsKey(moduleName))
{
return registeredModules[moduleName] as T;
}
return null;
}
/// <summary>
/// 暂停系统
/// </summary>
public void PauseSystem()
{
if (currentSystemState == SystemState.Running)
{
currentSystemState = SystemState.Paused;
foreach (var module in registeredModules.Values)
{
module.Pause();
}
Debug.Log("系统已暂停");
}
}
/// <summary>
/// 恢复系统
/// </summary>
public void ResumeSystem()
{
if (currentSystemState == SystemState.Paused)
{
currentSystemState = SystemState.Running;
foreach (var module in registeredModules.Values)
{
module.Resume();
}
Debug.Log("系统已恢复");
}
}
/// <summary>
/// 关闭系统
/// </summary>
public void ShutdownSystem()
{
currentSystemState = SystemState.ShuttingDown;
// 反向关闭所有模块
var modules = new List<ISystemModule>(registeredModules.Values);
modules.Reverse();
foreach (var module in modules)
{
module.Shutdown();
}
Debug.Log("企业VR系统关闭完成");
// 如果是编辑器环境,停止播放
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
private void OnApplicationQuit()
{
if (currentSystemState != SystemState.ShuttingDown)
{
ShutdownSystem();
}
}
}
/// <summary>
/// 系统模块接口
/// </summary>
public interface ISystemModule
{
void Initialize(EnterpriseVRSystem system);
void StartService();
void Update();
void Pause();
void Resume();
void Shutdown();
}
/// <summary>
/// VR设备管理器示例实现
/// </summary>
public class VRDeviceManager : MonoBehaviour, ISystemModule
{
private EnterpriseVRSystem parentSystem;
[Header("设备配置")]
[SerializeField] private bool autoDetectDevices = true;
[SerializeField] private List<VRDeviceProfile> supportedDevices;
private VRDevice activeDevice;
private Dictionary<string, VRDevice> detectedDevices;
public void Initialize(EnterpriseVRSystem system)
{
parentSystem = system;
detectedDevices = new Dictionary<string, VRDevice>();
Debug.Log("VR设备管理器初始化");
}
public void StartService()
{
if (autoDetectDevices)
{
DetectVRDevices();
}
InitializeActiveDevice();
}
public void Update()
{
if (activeDevice != null)
{
activeDevice.UpdateTracking();
}
}
public void Pause()
{
if (activeDevice != null)
{
activeDevice.PauseTracking();
}
}
public void Resume()
{
if (activeDevice != null)
{
activeDevice.ResumeTracking();
}
}
public void Shutdown()
{
if (activeDevice != null)
{
activeDevice.Shutdown();
}
Debug.Log("VR设备管理器关闭");
}
/// <summary>
/// 检测可用VR设备
/// </summary>
private void DetectVRDevices()
{
// 检测Oculus设备
if (DetectOculusDevice())
{
Debug.Log("检测到Oculus设备");
}
// 检测SteamVR设备
if (DetectSteamVRDevice())
{
Debug.Log("检测到SteamVR设备");
}
// 检测Windows MR设备
if (DetectWindowsMRDevice())
{
Debug.Log("检测到Windows MR设备");
}
Debug.Log($"共检测到 {detectedDevices.Count} 个VR设备");
}
private bool DetectOculusDevice()
{
// Oculus检测逻辑
return false; // 简化实现
}
private bool DetectSteamVRDevice()
{
// SteamVR检测逻辑
return false; // 简化实现
}
private bool DetectWindowsMRDevice()
{
// Windows MR检测逻辑
return false; // 简化实现
}
/// <summary>
/// 初始化活动设备
/// </summary>
private void InitializeActiveDevice()
{
if (detectedDevices.Count > 0)
{
// 选择第一个检测到的设备
var firstDevice = detectedDevices.Values.First();
SetActiveDevice(firstDevice);
}
else
{
// 使用模拟设备
Debug.LogWarning("未检测到VR设备,使用模拟模式");
UseSimulatedDevice();
}
}
/// <summary>
/// 设置活动设备
/// </summary>
private void SetActiveDevice(VRDevice device)
{
activeDevice = device;
activeDevice.Initialize();
Debug.Log($"活动设备设置为: {device.DeviceName}");
}
/// <summary>
/// 使用模拟设备
/// </summary>
private void UseSimulatedDevice()
{
// 创建模拟设备
activeDevice = new SimulatedVRDevice();
activeDevice.Initialize();
}
/// <summary>
/// 获取活动设备
/// </summary>
public VRDevice GetActiveDevice()
{
return activeDevice;
}
}
/// <summary>
/// VR设备基类
/// </summary>
public abstract class VRDevice
{
public abstract string DeviceName { get; }
public abstract string Manufacturer { get; }
public abstract bool IsConnected { get; }
public abstract void Initialize();
public abstract void UpdateTracking();
public abstract void PauseTracking();
public abstract void ResumeTracking();
public abstract void Shutdown();
public abstract Vector3 GetHeadPosition();
public abstract Quaternion GetHeadRotation();
public abstract Vector3 GetControllerPosition(int controllerIndex);
public abstract Quaternion GetControllerRotation(int controllerIndex);
public abstract bool GetControllerButton(int controllerIndex, string buttonName);
public abstract float GetControllerAxis(int controllerIndex, string axisName);
}
/// <summary>
/// 模拟VR设备
/// </summary>
public class SimulatedVRDevice : VRDevice
{
public override string DeviceName => "Simulated VR Device";
public override string Manufacturer => "EnterpriseVR";
public override bool IsConnected => true;
private Vector3 headPosition = Vector3.zero;
private Quaternion headRotation = Quaternion.identity;
private Vector3[] controllerPositions = new Vector3[2];
private Quaternion[] controllerRotations = new Quaternion[2];
public override void Initialize()
{
Debug.Log("模拟VR设备初始化");
controllerPositions[0] = new Vector3(-0.2f, 1.2f, 0.5f);
controllerPositions[1] = new Vector3(0.2f, 1.2f, 0.5f);
controllerRotations[0] = Quaternion.identity;
controllerRotations[1] = Quaternion.identity;
}
public override void UpdateTracking()
{
// 模拟头部移动
headPosition = new Vector3(
Mathf.Sin(Time.time * 0.5f) * 0.1f,
1.7f + Mathf.Sin(Time.time * 0.3f) * 0.05f,
Mathf.Cos(Time.time * 0.5f) * 0.1f
);
headRotation = Quaternion.Euler(
Mathf.Sin(Time.time * 0.2f) * 5f,
Time.time * 10f,
0
);
// 模拟控制器移动
for (int i = 0; i < 2; i++)
{
float offset = i == 0 ? 0 : Mathf.PI;
controllerPositions[i] = new Vector3(
Mathf.Sin(Time.time * 0.8f + offset) * 0.3f * (i == 0 ? -1 : 1),
1.2f + Mathf.Sin(Time.time * 0.5f + offset) * 0.1f,
0.5f + Mathf.Cos(Time.time * 0.8f + offset) * 0.2f
);
controllerRotations[i] = Quaternion.Euler(
0,
Time.time * 20f + offset * Mathf.Rad2Deg,
0
);
}
}
public override void PauseTracking()
{
Debug.Log("模拟设备追踪暂停");
}
public override void ResumeTracking()
{
Debug.Log("模拟设备追踪恢复");
}
public override void Shutdown()
{
Debug.Log("模拟设备关闭");
}
public override Vector3 GetHeadPosition()
{
return headPosition;
}
public override Quaternion GetHeadRotation()
{
return headRotation;
}
public override Vector3 GetControllerPosition(int controllerIndex)
{
if (controllerIndex >= 0 && controllerIndex < 2)
{
return controllerPositions[controllerIndex];
}
return Vector3.zero;
}
public override Quaternion GetControllerRotation(int controllerIndex)
{
if (controllerIndex >= 0 && controllerIndex < 2)
{
return controllerRotations[controllerIndex];
}
return Quaternion.identity;
}
public override bool GetControllerButton(int controllerIndex, string buttonName)
{
// 模拟按钮按下
return Input.GetKey(GetSimulatedKey(controllerIndex, buttonName));
}
public override float GetControllerAxis(int controllerIndex, string axisName)
{
// 模拟轴输入
if (axisName == "Trigger")
{
return Input.GetKey(KeyCode.Space) ? 1f : 0f;
}
else if (axisName == "Grip")
{
return Input.GetKey(KeyCode.LeftShift) ? 1f : 0f;
}
return 0f;
}
private KeyCode GetSimulatedKey(int controllerIndex, string buttonName)
{
if (controllerIndex == 0)
{
switch (buttonName)
{
case "Trigger": return KeyCode.Mouse0;
case "Grip": return KeyCode.Mouse1;
case "Primary": return KeyCode.Q;
case "Secondary": return KeyCode.E;
default: return KeyCode.None;
}
}
else
{
switch (buttonName)
{
case "Trigger": return KeyCode.U;
case "Grip": return KeyCode.I;
case "Primary": return KeyCode.O;
case "Secondary": return KeyCode.P;
default: return KeyCode.None;
}
}
}
}
/// <summary>
/// 设备配置文件
/// </summary>
[Serializable]
public class VRDeviceProfile
{
public string deviceName;
public string manufacturer;
public string sdkName;
public bool requiresSteamVR;
public bool requiresOculusSDK;
public float recommendedRenderScale = 1.0f;
public int recommendedMSAA = 4;
public bool supportsHandTracking = false;
public bool supportsEyeTracking = false;
public string[] requiredComponents;
}
}
10.1.2 零售业VR应用:虚拟商店系统
零售业是VR技术的重要应用领域,虚拟商店系统能够提供沉浸式的购物体验,突破物理空间限制。以下是完整的虚拟商店系统实现:
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace EnterpriseVR.Retail
{
/// <summary>
/// 虚拟商店系统
/// 支持商品浏览、试用、购买全流程
/// </summary>
public class VirtualStoreSystem : MonoBehaviour
{
[Serializable]
public class StoreConfiguration
{
[Header("商店设置")]
public string storeName = "虚拟旗舰店";
public string storeId = "VR_STORE_001";
public Vector3 storeSize = new Vector3(20, 10, 20);
public int maxConcurrentCustomers = 50;
[Header("营业时间")]
public bool alwaysOpen = true;
public int openHour = 9;
public int closeHour = 21;
[Header("商品设置")]
public int maxProductsPerCategory = 100;
public bool enableDynamicPricing = false;
public float priceUpdateInterval = 3600f; // 每小时更新
[Header("体验设置")]
public bool enableProductTryOn = true;
public bool enableVirtualFittingRoom = true;
public bool enablePersonalizedRecommendations = true;
}
[Serializable]
public class ProductCategory
{
public string categoryId;
public string categoryName;
public string iconResourcePath;
public Color categoryColor = Color.white;
public Vector3 displayAreaSize = new Vector3(5, 3, 2);
public List<VirtualProduct> products = new List<VirtualProduct>();
}
[Serializable]
public class VirtualProduct
{
public string productId;
public string productName;
public string description;
public ProductType productType;
public float price;
public float discount = 0f;
public int stockQuantity = 100;
public GameObject productPrefab;
public Texture2D productImage;
public Material[] availableMaterials;
public Vector3[] availableSizes;
// 3D模型信息
public Vector3 displayScale = Vector3.one;
public Vector3 interactionPoint = Vector3.zero;
public float weight = 1f;
// 商业信息
public string sku;
public string brand;
public string manufacturer;
public DateTime releaseDate;
public float rating = 5f;
public int reviewCount = 0;
// 试用设置
public bool canTryOn = true;
public float tryOnDuration = 30f;
public Vector3 tryOnPositionOffset = Vector3.zero;
public Quaternion tryOnRotationOffset = Quaternion.identity;
public enum ProductType
{
Clothing,
Electronics,
Furniture,
Jewelry,
Cosmetics,
Other
}
public float GetCurrentPrice()
{
return price * (1 - discount);
}
public bool IsInStock()
{
return stockQuantity > 0;
}
}
[Serializable]
public class CustomerSession
{
public string sessionId;
public string customerId;
public DateTime entryTime;
public DateTime? exitTime;
public List<ProductInteraction> interactions = new List<ProductInteraction>();
public ShoppingCart shoppingCart = new ShoppingCart();
public float sessionDuration = 0f;
public CustomerPreferences preferences = new CustomerPreferences();
public class ProductInteraction
{
public string productId;
public DateTime interactionTime;
public InteractionType interactionType;
public float duration;
public bool ledToCart = false;
public enum InteractionType
{
View,
Pickup,
TryOn,
Compare,
Customize
}
}
public class ShoppingCart
{
public List<CartItem> items = new List<CartItem>();
public float totalPrice = 0f;
public DateTime createdTime;
public class CartItem
{
public string productId;
public int quantity;
public float unitPrice;
public float discount;
public DateTime addedTime;
public ProductCustomization customization;
public float GetItemPrice()
{
return unitPrice * quantity * (1 - discount);
}
}
public void AddItem(VirtualProduct product, int quantity, ProductCustomization customization = null)
{
var existingItem = items.Find(item => item.productId == product.productId);
if (existingItem != null)
{
existingItem.quantity += quantity;
}
else
{
items.Add(new CartItem
{
productId = product.productId,
quantity = quantity,
unitPrice = product.GetCurrentPrice(),
discount = product.discount,
addedTime = DateTime.Now,
customization = customization
});
}
UpdateTotalPrice();
}
public void RemoveItem(string productId, int quantity = 1)
{
var item = items.Find(i => i.productId == productId);
if (item != null)
{
item.quantity -= quantity;
if (item.quantity <= 0)
{
items.Remove(item);
}
UpdateTotalPrice();
}
}
public void UpdateTotalPrice()
{
totalPrice = 0f;
foreach (var item in items)
{
totalPrice += item.GetItemPrice();
}
}
public int GetItemCount()
{
int count = 0;
foreach (var item in items)
{
count += item.quantity;
}
return count;
}
}
public class CustomerPreferences
{
public List<string> preferredCategories = new List<string>();
public List<string> viewedProducts = new List<string>();
public List<string> purchasedProducts = new List<string>();
public float priceSensitivity = 0.5f; // 0-1, 越高对价格越敏感
public float brandLoyalty = 0.5f; // 0-1, 越高品牌忠诚度越高
public Color favoriteColor = Color.blue;
public string sizePreference = "M";
public void RecordProductView(string productId)
{
if (!viewedProducts.Contains(productId))
{
viewedProducts.Add(productId);
}
}
public void RecordPurchase(string productId)
{
if (!purchasedProducts.Contains(productId))
{
purchasedProducts.Add(productId);
}
}
}
}
[Serializable]
public class ProductCustomization
{
public string customizationId;
public string productId;
public Material selectedMaterial;
public Color selectedColor = Color.white;
public Vector3 selectedSize = Vector3.one;
public string engravingText = "";
public DateTime customizationTime;
public bool IsValid()
{
return !string.IsNullOrEmpty(productId) && selectedMaterial != null;
}
}
[Header("系统配置")]
[SerializeField] private StoreConfiguration storeConfig = new StoreConfiguration();
[SerializeField] private List<ProductCategory> productCategories = new List<ProductCategory>();
[Header("商店环境")]
[SerializeField] private Transform storeEnvironment;
[SerializeField] private Transform entrancePoint;
[SerializeField] private Transform checkoutCounter;
[SerializeField] private Transform fittingRoomArea;
[SerializeField] private Transform displayAreasParent;
[Header("用户界面")]
[SerializeField] private Canvas storeCanvas;
[SerializeField] private Text storeNameText;
[SerializeField] private Text welcomeMessageText;
[SerializeField] private GameObject categoryButtonPrefab;
[SerializeField] private GameObject productCardPrefab;
[SerializeField] private Transform categoryButtonsPanel;
[SerializeField] private Transform productsDisplayPanel;
[SerializeField] private ShoppingCartUI shoppingCartUI;
[Header("交互系统")]
[SerializeField] private VRInteractionManager interactionManager;
[SerializeField] private GameObject productPickupPrefab;
[SerializeField] private float pickupDistance = 2f;
private Dictionary<string, VirtualProduct> productDictionary;
private Dictionary<string, ProductCategory> categoryDictionary;
private Dictionary<string, CustomerSession> activeSessions;
private CustomerSession currentCustomerSession;
private ProductCategory currentCategory;
private VirtualProduct selectedProduct;
private GameObject currentProductInstance;
private List<GameObject> spawnedProductDisplays;
private bool isStoreOpen = true;
private DateTime lastPriceUpdateTime;
private int totalVisitorsToday = 0;
private float totalRevenueToday = 0f;
private void Awake()
{
InitializeStoreSystem();
LoadProductData();
SetupStoreEnvironment();
InitializeUI();
}
private void Start()
{
OpenStore();
CreateNewCustomerSession("GUEST_" + UnityEngine.Random.Range(1000, 9999));
}
private void Update()
{
UpdateStoreStatus();
UpdateActiveSessions();
if (storeConfig.enableDynamicPricing &&
Time.time - lastPriceUpdateTime > storeConfig.priceUpdateInterval)
{
UpdateDynamicPricing();
lastPriceUpdateTime = Time.time;
}
}
/// <summary>
/// 初始化商店系统
/// </summary>
private void InitializeStoreSystem()
{
productDictionary = new Dictionary<string, VirtualProduct>();
categoryDictionary = new Dictionary<string, ProductCategory>();
activeSessions = new Dictionary<string, CustomerSession>();
spawnedProductDisplays = new List<GameObject>();
// 设置商店名称
if (storeNameText != null)
{
storeNameText.text = storeConfig.storeName;
}
Debug.Log($"虚拟商店系统初始化: {storeConfig.storeName}");
}
/// <summary>
/// 加载产品数据
/// </summary>
private void LoadProductData()
{
// 这里可以从数据库、API或本地文件加载产品数据
// 示例数据
if (productCategories.Count == 0)
{
CreateSampleProductData();
}
// 构建字典以便快速查找
foreach (var category in productCategories)
{
categoryDictionary[category.categoryId] = category;
foreach (var product in category.products)
{
productDictionary[product.productId] = product;
}
}
Debug.Log($"产品数据加载完成: {productDictionary.Count} 个产品, {categoryDictionary.Count} 个类别");
}
/// <summary>
/// 创建示例产品数据
/// </summary>
private void CreateSampleProductData()
{
// 服装类别
var clothingCategory = new ProductCategory
{
categoryId = "CLOTHING",
categoryName = "服装",
categoryColor = new Color(0.2f, 0.6f, 1f)
};
clothingCategory.products.Add(new VirtualProduct
{
productId = "PROD_001",
productName = "VR时尚夹克",
description = "专为虚拟现实设计的时尚夹克,舒适且美观",
productType = VirtualProduct.ProductType.Clothing,
price = 299.99f,
discount = 0.1f,
stockQuantity = 50,
availableMaterials = new Material[3],
availableSizes = new Vector3[] { new Vector3(0.9f, 0.9f, 0.9f), Vector3.one, new Vector3(1.1f, 1.1f, 1.1f) },
brand = "VR Fashion",
canTryOn = true
});
// 电子产品类别
var electronicsCategory = new ProductCategory
{
categoryId = "ELECTRONICS",
categoryName = "电子产品",
categoryColor = new Color(0.9f, 0.3f, 0.3f)
};
electronicsCategory.products.Add(new VirtualProduct
{
productId = "PROD_002",
productName = "虚拟现实头显",
description = "下一代VR头显,4K分辨率,120Hz刷新率",
productType = VirtualProduct.ProductType.Electronics,
price = 899.99f,
stockQuantity = 25,
displayScale = new Vector3(0.5f, 0.5f, 0.5f),
brand = "VisionTech",
canTryOn = false
});
productCategories.Add(clothingCategory);
productCategories.Add(electronicsCategory);
}
/// <summary>
/// 设置商店环境
/// </summary>
private void SetupStoreEnvironment()
{
if (storeEnvironment == null)
{
storeEnvironment = new GameObject("StoreEnvironment").transform;
}
// 创建展示区域
CreateDisplayAreas();
// 设置试衣间
if (storeConfig.enableVirtualFittingRoom && fittingRoomArea != null)
{
SetupFittingRoom();
}
// 设置收银台
if (checkoutCounter != null)
{
SetupCheckoutCounter();
}
}
/// <summary>
/// 创建展示区域
/// </summary>
private void CreateDisplayAreas()
{
if (displayAreasParent == null)
{
displayAreasParent = new GameObject("DisplayAreas").transform;
displayAreasParent.SetParent(storeEnvironment);
}
// 为每个类别创建展示区域
float xOffset = -storeConfig.storeSize.x / 2 + 2f;
foreach (var category in productCategories)
{
CreateCategoryDisplayArea(category, xOffset);
xOffset += category.displayAreaSize.x + 1f;
}
}
/// <summary>
/// 创建类别展示区域
/// </summary>
private void CreateCategoryDisplayArea(ProductCategory category, float xPosition)
{
GameObject area = new GameObject($"DisplayArea_{category.categoryName}");
area.transform.SetParent(displayAreasParent);
area.transform.position = new Vector3(xPosition, 0, 0);
// 创建展示架
CreateDisplayShelves(area.transform, category);
// 添加类别标识
CreateCategorySignage(area.transform, category);
}
/// <summary>
/// 创建展示架
/// </summary>
private void CreateDisplayShelves(Transform parent, ProductCategory category)
{
int productsPerRow = 4;
int rowCount = Mathf.CeilToInt((float)category.products.Count / productsPerRow);
for (int row = 0; row < rowCount; row++)
{
for (int col = 0; col < productsPerRow; col++)
{
int productIndex = row * productsPerRow + col;
if (productIndex < category.products.Count)
{
var product = category.products[productIndex];
CreateProductDisplay(parent, product, row, col);
}
}
}
}
/// <summary>
/// 创建产品展示
/// </summary>
private void CreateProductDisplay(Transform parent, VirtualProduct product, int row, int col)
{
GameObject display = new GameObject($"Display_{product.productName}");
display.transform.SetParent(parent);
float xPos = col * 1.5f - (1.5f * 1.5f);
float yPos = row * 1.2f + 0.8f;
float zPos = 0f;
display.transform.localPosition = new Vector3(xPos, yPos, zPos);
// 添加产品模型
if (product.productPrefab != null)
{
GameObject productModel = Instantiate(product.productPrefab, display.transform);
productModel.transform.localPosition = Vector3.zero;
productModel.transform.localScale = product.displayScale;
// 添加交互组件
AddProductInteraction(productModel, product);
}
// 添加价格标签
CreatePriceTag(display.transform, product);
spawnedProductDisplays.Add(display);
}
/// <summary>
/// 添加产品交互
/// </summary>
private void AddProductInteraction(GameObject productObject, VirtualProduct product)
{
var interactable = productObject.AddComponent<ProductInteractable>();
interactable.Initialize(product, this);
// 添加高亮效果
var outline = productObject.AddComponent<Outline>();
outline.OutlineColor = Color.yellow;
outline.OutlineWidth = 2f;
outline.enabled = false;
// 添加悬停检测
var hoverDetector = productObject.AddComponent<ProductHoverDetector>();
hoverDetector.OnHoverStart += () => OnProductHoverStart(product);
hoverDetector.OnHoverEnd += () => OnProductHoverEnd(product);
}
/// <summary>
/// 创建价格标签
/// </summary>
private void CreatePriceTag(Transform parent, VirtualProduct product)
{
GameObject tag = new GameObject("PriceTag");
tag.transform.SetParent(parent);
tag.transform.localPosition = new Vector3(0, -0.3f, 0);
// 这里可以创建3D文本或UI元素显示价格
// 简化实现,仅记录日志
Debug.Log($"创建价格标签: {product.productName} - ¥{product.GetCurrentPrice():F2}");
}
/// <summary>
/// 创建类别标识
/// </summary>
private void CreateCategorySignage(Transform parent, ProductCategory category)
{
GameObject sign = new GameObject($"Sign_{category.categoryName}");
sign.transform.SetParent(parent);
sign.transform.localPosition = new Vector3(0, category.displayAreaSize.y - 0.5f, 0);
// 这里可以创建3D文本显示类别名称
Debug.Log($"创建类别标识: {category.categoryName}");
}
/// <summary>
/// 设置试衣间
/// </summary>
private void SetupFittingRoom()
{
// 创建试衣间环境
GameObject fittingRoom = new GameObject("FittingRoom");
fittingRoom.transform.SetParent(fittingRoomArea);
fittingRoom.transform.localPosition = Vector3.zero;
// 添加镜子
CreateMirror(fittingRoom.transform);
// 添加灯光
CreateFittingRoomLights(fittingRoom.transform);
Debug.Log("试衣间设置完成");
}
private void CreateMirror(Transform parent)
{
// 创建镜子对象
GameObject mirror = GameObject.CreatePrimitive(PrimitiveType.Quad);
mirror.name = "Mirror";
mirror.transform.SetParent(parent);
mirror.transform.localPosition = new Vector3(0, 1.5f, 2f);
mirror.transform.localScale = new Vector3(2, 2, 1);
mirror.transform.Rotate(0, 180, 0);
// 应用镜子材质
Material mirrorMaterial = new Material(Shader.Find("Standard"));
mirrorMaterial.SetFloat("_Metallic", 1f);
mirrorMaterial.SetFloat("_Glossiness", 0.9f);
mirror.GetComponent<Renderer>().material = mirrorMaterial;
}
private void CreateFittingRoomLights(Transform parent)
{
// 创建环绕灯光
for (int i = 0; i < 4; i++)
{
float angle = i * 90f * Mathf.Deg2Rad;
Vector3 position = new Vector3(Mathf.Cos(angle) * 1.5f, 2f, Mathf.Sin(angle) * 1.5f);
GameObject lightObj = new GameObject($"FittingLight_{i}");
lightObj.transform.SetParent(parent);
lightObj.transform.localPosition = position;
Light light = lightObj.AddComponent<Light>();
light.type = LightType.Spot;
light.spotAngle = 60f;
light.intensity = 2f;
light.color = Color.white;
lightObj.transform.LookAt(Vector3.zero);
}
}
/// <summary>
/// 设置收银台
/// </summary>
private void SetupCheckoutCounter()
{
// 创建收银台模型
GameObject counter = GameObject.CreatePrimitive(PrimitiveType.Cube);
counter.name = "CheckoutCounter";
counter.transform.SetParent(checkoutCounter);
counter.transform.localPosition = Vector3.zero;
counter.transform.localScale = new Vector3(3, 1, 1);
// 添加收银机
GameObject register = new GameObject("CashRegister");
register.transform.SetParent(counter.transform);
register.transform.localPosition = new Vector3(0, 0.5f, 0.5f);
// 添加交互功能
var checkoutInteractable = counter.AddComponent<CheckoutInteractable>();
checkoutInteractable.Initialize(this);
}
/// <summary>
/// 初始化用户界面
/// </summary>
private void InitializeUI()
{
if (storeCanvas == null)
{
CreateStoreCanvas();
}
// 创建类别按钮
CreateCategoryButtons();
// 初始化购物车UI
if (shoppingCartUI != null)
{
shoppingCartUI.Initialize(this);
}
}
/// <summary>
/// 创建商店画布
/// </summary>
private void CreateStoreCanvas()
{
GameObject canvasObj = new GameObject("StoreCanvas");
storeCanvas = canvasObj.AddComponent<Canvas>();
storeCanvas.renderMode = RenderMode.WorldSpace;
CanvasScaler scaler = canvasObj.AddComponent<CanvasScaler>();
scaler.dynamicPixelsPerUnit = 10f;
GraphicRaycaster raycaster = canvasObj.AddComponent<GraphicRaycaster>();
// 定位在商店入口附近
canvasObj.transform.position = entrancePoint.position + new Vector3(0, 2f, -1f);
canvasObj.transform.rotation = Quaternion.Euler(30, 0, 0);
canvasObj.transform.localScale = new Vector3(0.002f, 0.002f, 0.002f);
}
/// <summary>
/// 创建类别按钮
/// </summary>
private void CreateCategoryButtons()
{
if (categoryButtonPrefab == null || categoryButtonsPanel == null)
{
Debug.LogWarning("类别按钮预制体或面板未设置");
return;
}
// 清空现有按钮
foreach (Transform child in categoryButtonsPanel)
{
Destroy(child.gameObject);
}
// 为每个类别创建按钮
foreach (var category in productCategories)
{
GameObject buttonObj = Instantiate(categoryButtonPrefab, categoryButtonsPanel);
CategoryButton button = buttonObj.GetComponent<CategoryButton>();
if (button != null)
{
button.Initialize(category, this);
}
}
}
/// <summary>
/// 更新商店状态
/// </summary>
private void UpdateStoreStatus()
{
if (!storeConfig.alwaysOpen)
{
DateTime now = DateTime.Now;
int currentHour = now.Hour;
bool shouldBeOpen = currentHour >= storeConfig.openHour &&
currentHour < storeConfig.closeHour;
if (shouldBeOpen != isStoreOpen)
{
if (shouldBeOpen)
{
OpenStore();
}
else
{
CloseStore();
}
}
}
}
/// <summary>
/// 打开商店
/// </summary>
private void OpenStore()
{
isStoreOpen = true;
Debug.Log($"商店开门: {storeConfig.storeName}");
// 激活所有展示
foreach (var display in spawnedProductDisplays)
{
if (display != null)
{
display.SetActive(true);
}
}
// 更新欢迎消息
if (welcomeMessageText != null)
{
welcomeMessageText.text = $"欢迎光临 {storeConfig.storeName}!";
}
}
/// <summary>
/// 关闭商店
/// </summary>
private void CloseStore()
{
isStoreOpen = false;
Debug.Log($"商店关门: {storeConfig.storeName}");
// 停用所有展示
foreach (var display in spawnedProductDisplays)
{
if (display != null)
{
display.SetActive(false);
}
}
// 清空所有会话
EndAllSessions();
// 更新欢迎消息
if (welcomeMessageText != null)
{
welcomeMessageText.text = "商店已关门,请明天再来!";
}
}
/// <summary>
/// 更新活动会话
/// </summary>
private void UpdateActiveSessions()
{
List<string> sessionsToRemove = new List<string>();
foreach (var session in activeSessions.Values)
{
session.sessionDuration += Time.deltaTime;
// 检查会话超时
if (session.sessionDuration > storeConfig.sessionTimeout)
{
sessionsToRemove.Add(session.sessionId);
}
}
// 移除超时会话
foreach (var sessionId in sessionsToRemove)
{
EndCustomerSession(sessionId);
}
}
/// <summary>
/// 更新动态定价
/// </summary>
private void UpdateDynamicPricing()
{
if (!storeConfig.enableDynamicPricing) return;
Debug.Log("更新动态定价...");
// 根据需求调整价格
foreach (var category in productCategories)
{
foreach (var product in category.products)
{
// 简化实现:根据库存调整价格
float demandFactor = 1f - (product.stockQuantity / 100f);
float newDiscount = Mathf.Clamp(demandFactor * 0.3f, 0f, 0.5f);
product.discount = newDiscount;
Debug.Log($"产品 {product.productName} 折扣更新: {newDiscount:P0}");
}
}
}
/// <summary>
/// 创建新客户会话
/// </summary>
public void CreateNewCustomerSession(string customerId)
{
string sessionId = $"SESSION_{DateTime.Now:yyyyMMdd_HHmmss}_{UnityEngine.Random.Range(1000, 9999)}";
var newSession = new CustomerSession
{
sessionId = sessionId,
customerId = customerId,
entryTime = DateTime.Now,
shoppingCart = new CustomerSession.ShoppingCart
{
createdTime = DateTime.Now
}
};
activeSessions[sessionId] = newSession;
currentCustomerSession = newSession;
totalVisitorsToday++;
Debug.Log($"新客户会话创建: {sessionId}, 客户ID: {customerId}");
// 发送欢迎消息
ShowWelcomeMessage(customerId);
}
/// <summary>
/// 显示欢迎消息
/// </summary>
private void ShowWelcomeMessage(string customerId)
{
if (welcomeMessageText != null)
{
welcomeMessageText.text = $"欢迎,{customerId}!今天是您的第{UnityEngine.Random.Range(1, 10)}次来访。";
}
}
/// <summary>
/// 结束客户会话
/// </summary>
public void EndCustomerSession(string sessionId)
{
if (activeSessions.ContainsKey(sessionId))
{
var session = activeSessions[sessionId];
session.exitTime = DateTime.Now;
// 记录会话数据
RecordSessionData(session);
// 从活动会话中移除
activeSessions.Remove(sessionId);
// 如果这是当前会话,清空引用
if (currentCustomerSession != null && currentCustomerSession.sessionId == sessionId)
{
currentCustomerSession = null;
}
Debug.Log($"客户会话结束: {sessionId}, 持续时间: {session.sessionDuration:F0}秒");
}
}
/// <summary>
/// 结束所有会话
/// </summary>
private void EndAllSessions()
{
var sessionIds = new List<string>(activeSessions.Keys);
foreach (var sessionId in sessionIds)
{
EndCustomerSession(sessionId);
}
}
/// <summary>
/// 记录会话数据
/// </summary>
private void RecordSessionData(CustomerSession session)
{
// 这里可以实现数据持久化逻辑
// 例如保存到数据库或发送到分析服务器
Debug.Log($"会话数据记录 - ID: {session.sessionId}, " +
$"时长: {session.sessionDuration:F0}秒, " +
$"交互次数: {session.interactions.Count}, " +
$"购物车价值: ¥{session.shoppingCart.totalPrice:F2}");
}
/// <summary>
/// 选择类别
/// </summary>
public void SelectCategory(string categoryId)
{
if (categoryDictionary.ContainsKey(categoryId))
{
currentCategory = categoryDictionary[categoryId];
Debug.Log($"类别选择: {currentCategory.categoryName}");
// 更新产品显示
DisplayCategoryProducts(currentCategory);
// 记录客户偏好
if (currentCustomerSession != null && storeConfig.enablePersonalizedRecommendations)
{
if (!currentCustomerSession.preferences.preferredCategories.Contains(categoryId))
{
currentCustomerSession.preferences.preferredCategories.Add(categoryId);
}
}
}
}
/// <summary>
/// 显示类别产品
/// </summary>
private void DisplayCategoryProducts(ProductCategory category)
{
// 清空当前显示
if (productsDisplayPanel != null)
{
foreach (Transform child in productsDisplayPanel)
{
Destroy(child.gameObject);
}
}
// 创建产品卡片
if (productCardPrefab != null && productsDisplayPanel != null)
{
foreach (var product in category.products)
{
CreateProductCard(product);
}
}
}
/// <summary>
/// 创建产品卡片
/// </summary>
private void CreateProductCard(VirtualProduct product)
{
GameObject cardObj = Instantiate(productCardPrefab, productsDisplayPanel);
ProductCard card = cardObj.GetComponent<ProductCard>();
if (card != null)
{
card.Initialize(product, this);
}
}
/// <summary>
/// 选择产品
/// </summary>
public void SelectProduct(string productId)
{
if (productDictionary.ContainsKey(productId))
{
selectedProduct = productDictionary[productId];
Debug.Log($"产品选择: {selectedProduct.productName}");
// 记录客户交互
if (currentCustomerSession != null)
{
RecordProductInteraction(selectedProduct,
CustomerSession.ProductInteraction.InteractionType.View);
// 记录查看历史
currentCustomerSession.preferences.RecordProductView(productId);
}
// 显示产品详情
ShowProductDetails(selectedProduct);
// 生成3D预览
SpawnProductPreview(selectedProduct);
}
}
/// <summary>
/// 记录产品交互
/// </summary>
private void RecordProductInteraction(VirtualProduct product,
CustomerSession.ProductInteraction.InteractionType interactionType)
{
if (currentCustomerSession == null) return;
var interaction = new CustomerSession.ProductInteraction
{
productId = product.productId,
interactionTime = DateTime.Now,
interactionType = interactionType,
duration = 0f
};
currentCustomerSession.interactions.Add(interaction);
// 启动持续时间跟踪
StartCoroutine(TrackInteractionDuration(interaction));
}
private System.Collections.IEnumerator TrackInteractionDuration(CustomerSession.ProductInteraction interaction)
{
float startTime = Time.time;
while (true)
{
yield return null;
interaction.duration = Time.time - startTime;
// 如果选择了其他产品或会话结束,停止跟踪
if (selectedProduct == null ||
selectedProduct.productId != interaction.productId ||
currentCustomerSession == null)
{
break;
}
}
}
/// <summary>
/// 显示产品详情
/// </summary>
private void ShowProductDetails(VirtualProduct product)
{
// 这里可以实现显示产品详情的UI逻辑
Debug.Log($"显示产品详情: {product.productName}\n" +
$"描述: {product.description}\n" +
$"价格: ¥{product.GetCurrentPrice():F2}\n" +
$"库存: {product.stockQuantity}件");
}
/// <summary>
/// 生成产品预览
/// </summary>
private void SpawnProductPreview(VirtualProduct product)
{
// 销毁当前预览
if (currentProductInstance != null)
{
Destroy(currentProductInstance);
}
if (product.productPrefab != null)
{
// 在预览位置生成产品
Vector3 previewPosition = entrancePoint.position + new Vector3(0, 1.5f, 2f);
currentProductInstance = Instantiate(product.productPrefab, previewPosition, Quaternion.identity);
currentProductInstance.transform.localScale = product.displayScale;
// 添加预览交互
var previewInteractable = currentProductInstance.AddComponent<ProductPreviewInteractable>();
previewInteractable.Initialize(product, this);
}
}
/// <summary>
/// 产品悬停开始
/// </summary>
private void OnProductHoverStart(VirtualProduct product)
{
Debug.Log($"产品悬停: {product.productName}");
// 高亮产品
// 这里可以实现高亮效果
// 显示快速信息
ShowQuickProductInfo(product);
}
/// <summary>
/// 产品悬停结束
/// </summary>
private void OnProductHoverEnd(VirtualProduct product)
{
// 取消高亮
// 隐藏快速信息
}
/// <summary>
/// 显示快速产品信息
/// </summary>
private void ShowQuickProductInfo(VirtualProduct product)
{
// 在3D空间中显示信息气泡
// 简化实现,仅记录日志
Debug.Log($"快速信息 - {product.productName}: ¥{product.GetCurrentPrice():F2}");
}
/// <summary>
/// 试用产品
/// </summary>
public void TryOnProduct(string productId)
{
if (!storeConfig.enableProductTryOn)
{
Debug.Log("产品试用功能未启用");
return;
}
if (productDictionary.ContainsKey(productId))
{
var product = productDictionary[productId];
if (!product.canTryOn)
{
Debug.Log($"产品 {product.productName} 不支持试用");
return;
}
Debug.Log($"试用产品: {product.productName}");
// 记录试用交互
if (currentCustomerSession != null)
{
RecordProductInteraction(product,
CustomerSession.ProductInteraction.InteractionType.TryOn);
}
// 在试衣间显示产品
if (storeConfig.enableVirtualFittingRoom && fittingRoomArea != null)
{
SpawnProductForTryOn(product);
}
}
}
/// <summary>
/// 生成试用产品
/// </summary>
private void SpawnProductForTryOn(VirtualProduct product)
{
if (product.productPrefab == null) return;
Vector3 tryOnPosition = fittingRoomArea.position + product.tryOnPositionOffset;
Quaternion tryOnRotation = fittingRoomArea.rotation * product.tryOnRotationOffset;
GameObject tryOnInstance = Instantiate(product.productPrefab, tryOnPosition, tryOnRotation);
tryOnInstance.transform.localScale = product.displayScale;
// 添加试用控制
var tryOnController = tryOnInstance.AddComponent<ProductTryOnController>();
tryOnController.Initialize(product, product.tryOnDuration);
Debug.Log($"试用产品已生成,持续时间: {product.tryOnDuration}秒");
}
/// <summary>
/// 添加到购物车
/// </summary>
public void AddToCart(string productId, int quantity = 1, ProductCustomization customization = null)
{
if (productDictionary.ContainsKey(productId))
{
var product = productDictionary[productId];
if (!product.IsInStock())
{
Debug.LogWarning($"产品 {product.productName} 缺货");
return;
}
if (currentCustomerSession != null)
{
currentCustomerSession.shoppingCart.AddItem(product, quantity, customization);
// 更新交互记录,标记为加入购物车
var lastInteraction = currentCustomerSession.interactions
.LastOrDefault(i => i.productId == productId);
if (lastInteraction != null)
{
lastInteraction.ledToCart = true;
}
Debug.Log($"添加到购物车: {product.productName} x{quantity}, " +
$"总价: ¥{currentCustomerSession.shoppingCart.totalPrice:F2}");
// 更新UI
if (shoppingCartUI != null)
{
shoppingCartUI.UpdateCartDisplay(currentCustomerSession.shoppingCart);
}
}
}
}
/// <summary>
/// 从购物车移除
/// </summary>
public void RemoveFromCart(string productId, int quantity = 1)
{
if (currentCustomerSession != null)
{
currentCustomerSession.shoppingCart.RemoveItem(productId, quantity);
Debug.Log($"从购物车移除: {productId} x{quantity}, " +
$"总价: ¥{currentCustomerSession.shoppingCart.totalPrice:F2}");
// 更新UI
if (shoppingCartUI != null)
{
shoppingCartUI.UpdateCartDisplay(currentCustomerSession.shoppingCart);
}
}
}
/// <summary>
/// 结账
/// </summary>
public void Checkout()
{
if (currentCustomerSession == null)
{
Debug.LogWarning("没有活动客户会话");
return;
}
var cart = currentCustomerSession.shoppingCart;
if (cart.GetItemCount() == 0)
{
Debug.LogWarning("购物车为空");
return;
}
Debug.Log($"开始结账 - 商品数量: {cart.GetItemCount()}, " +
$"总金额: ¥{cart.totalPrice:F2}");
// 处理支付
ProcessPayment(cart);
// 更新库存
UpdateInventory(cart);
// 记录购买
RecordPurchase(cart);
// 清空购物车
cart.items.Clear();
cart.UpdateTotalPrice();
// 更新UI
if (shoppingCartUI != null)
{
shoppingCartUI.UpdateCartDisplay(cart);
}
Debug.Log("结账完成");
}
/// <summary>
/// 处理支付
/// </summary>
private void ProcessPayment(CustomerSession.ShoppingCart cart)
{
// 这里可以集成支付网关
// 简化实现,仅记录日志
Debug.Log($"支付处理 - 金额: ¥{cart.totalPrice:F2}");
// 更新今日收入
totalRevenueToday += cart.totalPrice;
}
/// <summary>
/// 更新库存
/// </summary>
private void UpdateInventory(CustomerSession.ShoppingCart cart)
{
foreach (var item in cart.items)
{
if (productDictionary.ContainsKey(item.productId))
{
var product = productDictionary[item.productId];
product.stockQuantity -= item.quantity;
if (product.stockQuantity < 0)
{
product.stockQuantity = 0;
Debug.LogWarning($"产品 {product.productName} 库存不足,已调整为0");
}
Debug.Log($"库存更新 - {product.productName}: {product.stockQuantity}件剩余");
}
}
}
/// <summary>
/// 记录购买
/// </summary>
private void RecordPurchase(CustomerSession.ShoppingCart cart)
{
if (currentCustomerSession == null) return;
foreach (var item in cart.items)
{
// 记录客户偏好
currentCustomerSession.preferences.RecordPurchase(item.productId);
// 这里可以发送购买数据到分析系统
Debug.Log($"购买记录 - 产品: {item.productId}, 数量: {item.quantity}");
}
}
/// <summary>
/// 获取商店统计数据
/// </summary>
public StoreStatistics GetStoreStatistics()
{
return new StoreStatistics
{
storeName = storeConfig.storeName,
totalVisitorsToday = totalVisitorsToday,
totalRevenueToday = totalRevenueToday,
activeSessions = activeSessions.Count,
totalProducts = productDictionary.Count,
isOpen = isStoreOpen
};
}
/// <summary>
/// 获取个性化推荐
/// </summary>
public List<VirtualProduct> GetPersonalizedRecommendations()
{
if (currentCustomerSession == null ||
!storeConfig.enablePersonalizedRecommendations)
{
return new List<VirtualProduct>();
}
var recommendations = new List<VirtualProduct>();
var preferences = currentCustomerSession.preferences;
// 基于浏览历史推荐
foreach (var viewedProductId in preferences.viewedProducts)
{
if (productDictionary.ContainsKey(viewedProductId))
{
var product = productDictionary[viewedProductId];
// 查找同类别产品
foreach (var category in productCategories)
{
if (category.products.Contains(product))
{
// 推荐同类别的其他产品
foreach (var otherProduct in category.products)
{
if (otherProduct.productId != viewedProductId &&
!preferences.purchasedProducts.Contains(otherProduct.productId) &&
!recommendations.Contains(otherProduct))
{
recommendations.Add(otherProduct);
if (recommendations.Count >= 5)
{
return recommendations;
}
}
}
}
}
}
}
return recommendations;
}
/// <summary>
/// 商店统计数据结构
/// </summary>
public class StoreStatistics
{
public string storeName;
public int totalVisitorsToday;
public float totalRevenueToday;
public int activeSessions;
public int totalProducts;
public bool isOpen;
public override string ToString()
{
return $"商店统计: {storeName}\n" +
$"今日访客: {totalVisitorsToday}\n" +
$"今日收入: ¥{totalRevenueToday:F2}\n" +
$"活动会话: {activeSessions}\n" +
$"总产品数: {totalProducts}\n" +
$"营业状态: {(isOpen ? "营业中" : "已关门")}";
}
}
}
/// <summary>
/// 产品交互组件
/// </summary>
public class ProductInteractable : MonoBehaviour
{
private VirtualStoreSystem.VirtualProduct product;
private VirtualStoreSystem storeSystem;
private Outline outline;
public void Initialize(VirtualStoreSystem.VirtualProduct productData, VirtualStoreSystem system)
{
product = productData;
storeSystem = system;
outline = GetComponent<Outline>();
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("VRController"))
{
// 高亮产品
if (outline != null)
{
outline.enabled = true;
}
// 显示产品信息
storeSystem.SelectProduct(product.productId);
}
}
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("VRController"))
{
// 取消高亮
if (outline != null)
{
outline.enabled = false;
}
}
}
}
/// <summary>
/// 产品悬停检测器
/// </summary>
public class ProductHoverDetector : MonoBehaviour
{
public event System.Action OnHoverStart;
public event System.Action OnHoverEnd;
private bool isHovering = false;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("VRController") && !isHovering)
{
isHovering = true;
OnHoverStart?.Invoke();
}
}
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("VRController") && isHovering)
{
isHovering = false;
OnHoverEnd?.Invoke();
}
}
}
/// <summary>
/// 类别按钮组件
/// </summary>
public class CategoryButton : MonoBehaviour
{
[SerializeField] private Button button;
[SerializeField] private Text categoryNameText;
[SerializeField] private Image categoryIcon;
private VirtualStoreSystem.ProductCategory category;
private VirtualStoreSystem storeSystem;
public void Initialize(VirtualStoreSystem.ProductCategory categoryData, VirtualStoreSystem system)
{
category = categoryData;
storeSystem = system;
if (categoryNameText != null)
{
categoryNameText.text = categoryData.categoryName;
}
if (categoryIcon != null)
{
categoryIcon.color = categoryData.categoryColor;
}
if (button != null)
{
button.onClick.AddListener(OnButtonClick);
}
}
private void OnButtonClick()
{
if (storeSystem != null && category != null)
{
storeSystem.SelectCategory(category.categoryId);
}
}
}
/// <summary>
/// 产品卡片组件
/// </summary>
public class ProductCard : MonoBehaviour
{
[SerializeField] private Text productNameText;
[SerializeField] private Text priceText;
[SerializeField] private Image productImage;
[SerializeField] private Button selectButton;
[SerializeField] private Button tryOnButton;
[SerializeField] private Button addToCartButton;
private VirtualStoreSystem.VirtualProduct product;
private VirtualStoreSystem storeSystem;
public void Initialize(VirtualStoreSystem.VirtualProduct productData, VirtualStoreSystem system)
{
product = productData;
storeSystem = system;
if (productNameText != null)
{
productNameText.text = productData.productName;
}
if (priceText != null)
{
priceText.text = $"¥{productData.GetCurrentPrice():F2}";
if (productData.discount > 0)
{
priceText.color = Color.red;
priceText.text += $" (-{productData.discount:P0})";
}
}
if (selectButton != null)
{
selectButton.onClick.AddListener(OnSelectClick);
}
if (tryOnButton != null)
{
tryOnButton.onClick.AddListener(OnTryOnClick);
tryOnButton.interactable = productData.canTryOn;
}
if (addToCartButton != null)
{
addToCartButton.onClick.AddListener(OnAddToCartClick);
}
}
private void OnSelectClick()
{
if (storeSystem != null && product != null)
{
storeSystem.SelectProduct(product.productId);
}
}
private void OnTryOnClick()
{
if (storeSystem != null && product != null && product.canTryOn)
{
storeSystem.TryOnProduct(product.productId);
}
}
private void OnAddToCartClick()
{
if (storeSystem != null && product != null)
{
storeSystem.AddToCart(product.productId);
}
}
}
/// <summary>
/// 购物车UI组件
/// </summary>
public class ShoppingCartUI : MonoBehaviour
{
[SerializeField] private Text itemCountText;
[SerializeField] private Text totalPriceText;
[SerializeField] private Button viewCartButton;
[SerializeField] private Button checkoutButton;
[SerializeField] private GameObject cartDetailPanel;
[SerializeField] private Transform cartItemsContainer;
[SerializeField] private GameObject cartItemPrefab;
private VirtualStoreSystem storeSystem;
public void Initialize(VirtualStoreSystem system)
{
storeSystem = system;
if (viewCartButton != null)
{
viewCartButton.onClick.AddListener(ToggleCartDetail);
}
if (checkoutButton != null)
{
checkoutButton.onClick.AddListener(OnCheckoutClick);
}
// 初始隐藏详情面板
if (cartDetailPanel != null)
{
cartDetailPanel.SetActive(false);
}
}
public void UpdateCartDisplay(VirtualStoreSystem.CustomerSession.ShoppingCart cart)
{
if (itemCountText != null)
{
itemCountText.text = cart.GetItemCount().ToString();
}
if (totalPriceText != null)
{
totalPriceText.text = $"¥{cart.totalPrice:F2}";
}
// 更新详情列表
UpdateCartDetailList(cart);
}
private void UpdateCartDetailList(VirtualStoreSystem.CustomerSession.ShoppingCart cart)
{
if (cartItemsContainer == null || cartItemPrefab == null) return;
// 清空现有项
foreach (Transform child in cartItemsContainer)
{
Destroy(child.gameObject);
}
// 创建新项
foreach (var item in cart.items)
{
CreateCartItemUI(item);
}
}
private void CreateCartItemUI(VirtualStoreSystem.CustomerSession.ShoppingCart.CartItem item)
{
GameObject itemObj = Instantiate(cartItemPrefab, cartItemsContainer);
CartItemUI itemUI = itemObj.GetComponent<CartItemUI>();
if (itemUI != null && storeSystem != null)
{
// 这里需要从storeSystem获取产品详情
// 简化实现
itemUI.Initialize(item, storeSystem);
}
}
private void ToggleCartDetail()
{
if (cartDetailPanel != null)
{
cartDetailPanel.SetActive(!cartDetailPanel.activeSelf);
}
}
private void OnCheckoutClick()
{
if (storeSystem != null)
{
storeSystem.Checkout();
}
}
}
/// <summary>
/// 购物车项目UI组件
/// </summary>
public class CartItemUI : MonoBehaviour
{
[SerializeField] private Text productNameText;
[SerializeField] private Text quantityText;
[SerializeField] private Text priceText;
[SerializeField] private Button increaseButton;
[SerializeField] private Button decreaseButton;
[SerializeField] private Button removeButton;
private VirtualStoreSystem.CustomerSession.ShoppingCart.CartItem cartItem;
private VirtualStoreSystem storeSystem;
public void Initialize(VirtualStoreSystem.CustomerSession.ShoppingCart.CartItem item,
VirtualStoreSystem system)
{
cartItem = item;
storeSystem = system;
// 这里需要从storeSystem获取产品名称
if (productNameText != null)
{
productNameText.text = $"产品 {item.productId}";
}
if (quantityText != null)
{
quantityText.text = $"x{item.quantity}";
}
if (priceText != null)
{
priceText.text = $"¥{item.GetItemPrice():F2}";
}
if (increaseButton != null)
{
increaseButton.onClick.AddListener(OnIncreaseClick);
}
if (decreaseButton != null)
{
decreaseButton.onClick.AddListener(OnDecreaseClick);
}
if (removeButton != null)
{
removeButton.onClick.AddListener(OnRemoveClick);
}
}
private void OnIncreaseClick()
{
if (storeSystem != null && cartItem != null)
{
storeSystem.AddToCart(cartItem.productId);
}
}
private void OnDecreaseClick()
{
if (storeSystem != null && cartItem != null && cartItem.quantity > 1)
{
storeSystem.RemoveFromCart(cartItem.productId);
}
}
private void OnRemoveClick()
{
if (storeSystem != null && cartItem != null)
{
storeSystem.RemoveFromCart(cartItem.productId, cartItem.quantity);
}
}
}
/// <summary>
/// 产品预览交互组件
/// </summary>
public class ProductPreviewInteractable : MonoBehaviour
{
private VirtualStoreSystem.VirtualProduct product;
private VirtualStoreSystem storeSystem;
public void Initialize(VirtualStoreSystem.VirtualProduct productData, VirtualStoreSystem system)
{
product = productData;
storeSystem = system;
}
private void Update()
{
// 允许旋转预览
if (Input.GetMouseButton(0))
{
float rotateSpeed = 100f;
float rotationX = Input.GetAxis("Mouse X") * rotateSpeed * Time.deltaTime;
float rotationY = Input.GetAxis("Mouse Y") * rotateSpeed * Time.deltaTime;
transform.Rotate(Vector3.up, -rotationX, Space.World);
transform.Rotate(Vector3.right, rotationY, Space.World);
}
}
private void OnDestroy()
{
// 清理资源
}
}
/// <summary>
/// 产品试用控制器
/// </summary>
public class ProductTryOnController : MonoBehaviour
{
private VirtualStoreSystem.VirtualProduct product;
private float tryOnDuration;
private float elapsedTime;
private bool isTryingOn;
public void Initialize(VirtualStoreSystem.VirtualProduct productData, float duration)
{
product = productData;
tryOnDuration = duration;
elapsedTime = 0f;
isTryingOn = true;
}
private void Update()
{
if (!isTryingOn) return;
elapsedTime += Time.deltaTime;
if (elapsedTime >= tryOnDuration)
{
EndTryOn();
}
// 允许调整位置和旋转
HandleTryOnControls();
}
private void HandleTryOnControls()
{
// 这里可以实现试用时的控制逻辑
// 例如调整位置、旋转、缩放等
}
private void EndTryOn()
{
isTryingOn = false;
Debug.Log($"试用结束: {product.productName}");
// 销毁试用实例
Destroy(gameObject, 1f);
}
}
/// <summary>
/// 收银台交互组件
/// </summary>
public class CheckoutInteractable : MonoBehaviour
{
private VirtualStoreSystem storeSystem;
public void Initialize(VirtualStoreSystem system)
{
storeSystem = system;
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("VRController"))
{
Debug.Log("接近收银台,准备结账");
// 显示结账提示
ShowCheckoutPrompt();
}
}
private void ShowCheckoutPrompt()
{
// 这里可以显示UI提示
Debug.Log("按下触发器开始结账");
}
private void OnTriggerStay(Collider other)
{
if (other.CompareTag("VRController"))
{
// 检测触发器按下
// 这里需要根据VR输入系统实现
// 简化实现:按下空格键模拟
if (Input.GetKeyDown(KeyCode.Space))
{
TriggerCheckout();
}
}
}
private void TriggerCheckout()
{
if (storeSystem != null)
{
storeSystem.Checkout();
}
}
}
}
10.1.3 虚拟商店系统的商业价值分析
虚拟商店系统的商业价值不仅体现在直接销售上,更在于其提供的全新购物体验和数据分析能力:
1. 突破地理限制
- 全球客户可在同一虚拟空间购物
- 24/7不间断营业
- 无实体店面租金成本
2. 沉浸式体验优势
- 产品可360度查看、试用
- 个性化定制即时可视化
- 社交购物体验(与朋友一起逛虚拟商店)
3. 数据分析深度
- 追踪客户在虚拟商店中的每一步行为
- 热图分析:哪些区域最受欢迎
- 停留时间分析:哪些产品最吸引人
- 购买路径分析:客户的决策过程
4. 成本效益
- 减少实体样品成本
- 降低库存压力(虚拟展示+按需生产)
- 营销活动效果可实时监测调整
5. 可持续性
- 减少物流和仓储的碳足迹
- 数字化样品减少物料浪费
- 远程购物减少交通排放
以下代码展示了虚拟商店系统的数据分析模块:
using System;
using System.Collections.Generic;
using UnityEngine;
namespace EnterpriseVR.Retail.Analytics
{
/// <summary>
/// 虚拟商店分析系统
/// 收集和分析客户行为数据
/// </summary>
public class VirtualStoreAnalytics : MonoBehaviour
{
[Serializable]
public class AnalyticsData
{
public string storeId;
public DateTime date;
public List<SessionData> sessions = new List<SessionData>();
public StoreMetrics metrics = new StoreMetrics();
public List<ProductPerformance> productPerformances = new List<ProductPerformance>();
public List<CustomerInsight> customerInsights = new List<CustomerInsight>();
[Serializable]
public class SessionData
{
public string sessionId;
public string customerId;
public DateTime startTime;
public DateTime endTime;
public float duration;
public List<string> visitedCategories;
public List<string> viewedProducts;
public List<string> triedProducts;
public List<string> purchasedProducts;
public float totalSpent;
public string exitReason;
}
[Serializable]
public class StoreMetrics
{
public int totalVisitors;
public int totalSessions;
public float averageSessionDuration;
public float conversionRate; // 购买会话比例
public float averageOrderValue;
public float revenuePerVisitor;
public string peakHour;
public string mostPopularCategory;
public string bestSellingProduct;
}
[Serializable]
public class ProductPerformance
{
public string productId;
public string productName;
public int viewCount;
public int tryOnCount;
public int addToCartCount;
public int purchaseCount;
public float conversionRate; // 查看->购买转化率
public float averageViewDuration;
public float revenueGenerated;
public string popularTime;
}
[Serializable]
public class CustomerInsight
{
public string customerSegment;
public int customerCount;
public float averageSpend;
public float visitFrequency;
public List<string> preferredCategories;
public float loyaltyScore;
public List<string> recommendedProducts;
}
}
[Header("分析设置")]
[SerializeField] private bool enableRealTimeAnalytics = true;
[SerializeField] private float dataFlushInterval = 300f; // 5分钟
[SerializeField] private bool enableHeatmapGeneration = true;
[SerializeField] private bool enablePredictiveAnalytics = true;
[Header("数据存储")]
[SerializeField] private string analyticsServerUrl = "https://api.your-analytics.com";
[SerializeField] private string apiKey = "";
[SerializeField] private bool storeLocally = true;
[SerializeField] private string localStoragePath = "AnalyticsData/";
private VirtualStoreSystem storeSystem;
private AnalyticsData currentAnalyticsData;
private Dictionary<string, CustomerTracker> customerTrackers;
private Dictionary<string, ProductTracker> productTrackers;
private float lastFlushTime;
private class CustomerTracker
{
public string customerId;
public DateTime firstSeen;
public DateTime lastSeen;
public int visitCount;
public float totalSpent;
public List<string> purchaseHistory = new List<string>();
public Dictionary<string, float> categoryPreferences = new Dictionary<string, float>();
public Vector3[] movementPath;
public int pathIndex;
}
private class ProductTracker
{
public string productId;
public int totalViews;
public int totalTryOns;
public int totalAddsToCart;
public int totalPurchases;
public float totalViewTime;
public List<DateTime> viewTimes = new List<DateTime>();
public Dictionary<string, int> viewByHour = new Dictionary<string, int>();
}
private void Start()
{
InitializeAnalyticsSystem();
StartDataCollection();
}
private void Update()
{
if (enableRealTimeAnalytics)
{
UpdateRealTimeAnalytics();
if (Time.time - lastFlushTime > dataFlushInterval)
{
FlushAnalyticsData();
lastFlushTime = Time.time;
}
}
}
private void InitializeAnalyticsSystem()
{
storeSystem = FindObjectOfType<VirtualStoreSystem>();
if (storeSystem == null)
{
Debug.LogError("未找到虚拟商店系统");
return;
}
currentAnalyticsData = new AnalyticsData
{
storeId = storeSystem.GetStoreStatistics().storeName,
date = DateTime.Today
};
customerTrackers = new Dictionary<string, CustomerTracker>();
productTrackers = new Dictionary<string, ProductTracker>();
lastFlushTime = Time.time;
Debug.Log("虚拟商店分析系统初始化完成");
}
private void StartDataCollection()
{
// 开始收集各种事件数据
// 这里可以订阅商店系统的各种事件
Debug.Log("开始数据收集");
}
private void UpdateRealTimeAnalytics()
{
// 更新实时分析数据
UpdateStoreMetrics();
UpdateProductPerformances();
UpdateCustomerInsights();
}
private void UpdateStoreMetrics()
{
var stats = storeSystem.GetStoreStatistics();
currentAnalyticsData.metrics.totalVisitors = stats.totalVisitorsToday;
currentAnalyticsData.metrics.totalSessions = stats.activeSessions;
// 这里可以计算更多指标
}
private void UpdateProductPerformances()
{
// 更新产品表现数据
// 从产品追踪器获取数据
}
private void UpdateCustomerInsights()
{
// 更新客户洞察
// 分析客户行为和偏好
}
public void RecordCustomerEntry(string customerId)
{
if (!customerTrackers.ContainsKey(customerId))
{
customerTrackers[customerId] = new CustomerTracker
{
customerId = customerId,
firstSeen = DateTime.Now,
visitCount = 0
};
}
var tracker = customerTrackers[customerId];
tracker.lastSeen = DateTime.Now;
tracker.visitCount++;
// 开始追踪移动路径
StartTrackingMovement(customerId);
}
private void StartTrackingMovement(string customerId)
{
// 开始记录客户移动路径
// 每帧记录位置数据
}
public void RecordProductView(string customerId, string productId, float viewDuration)
{
// 记录产品查看
if (!productTrackers.ContainsKey(productId))
{
productTrackers[productId] = new ProductTracker
{
productId = productId
};
}
var tracker = productTrackers[productId];
tracker.totalViews++;
tracker.totalViewTime += viewDuration;
tracker.viewTimes.Add(DateTime.Now);
// 记录按小时的查看分布
string hourKey = DateTime.Now.ToString("HH:00");
if (!tracker.viewByHour.ContainsKey(hourKey))
{
tracker.viewByHour[hourKey] = 0;
}
tracker.viewByHour[hourKey]++;
// 更新客户偏好
if (customerTrackers.ContainsKey(customerId))
{
var customerTracker = customerTrackers[customerId];
// 这里可以根据产品类别更新客户偏好
// 简化实现
}
}
public void RecordProductTryOn(string customerId, string productId, float tryOnDuration)
{
if (productTrackers.ContainsKey(productId))
{
productTrackers[productId].totalTryOns++;
}
}
public void RecordAddToCart(string customerId, string productId)
{
if (productTrackers.ContainsKey(productId))
{
productTrackers[productId].totalAddsToCart++;
}
}
public void RecordPurchase(string customerId, string productId, float amount)
{
if (productTrackers.ContainsKey(productId))
{
productTrackers[productId].totalPurchases++;
productTrackers[productId].revenueGenerated += amount;
}
if (customerTrackers.ContainsKey(customerId))
{
var tracker = customerTrackers[customerId];
tracker.totalSpent += amount;
tracker.purchaseHistory.Add(productId);
}
}
private void FlushAnalyticsData()
{
Debug.Log("刷新分析数据到存储...");
// 准备数据
PrepareAnalyticsData();
// 本地存储
if (storeLocally)
{
StoreDataLocally();
}
// 发送到服务器
if (!string.IsNullOrEmpty(analyticsServerUrl) && !string.IsNullOrEmpty(apiKey))
{
SendDataToServer();
}
// 重置当前数据(保留累积数据)
currentAnalyticsData.sessions.Clear();
}
private void PrepareAnalyticsData()
{
// 从追踪器准备分析数据
foreach (var productPair in productTrackers)
{
var tracker = productPair.Value;
var performance = new AnalyticsData.ProductPerformance
{
productId = tracker.productId,
productName = GetProductName(tracker.productId),
viewCount = tracker.totalViews,
tryOnCount = tracker.totalTryOns,
addToCartCount = tracker.totalAddsToCart,
purchaseCount = tracker.totalPurchases,
conversionRate = tracker.totalViews > 0 ?
(float)tracker.totalPurchases / tracker.totalViews : 0f,
averageViewDuration = tracker.totalViews > 0 ?
tracker.totalViewTime / tracker.totalViews : 0f,
revenueGenerated = tracker.revenueGenerated,
popularTime = GetPopularTime(tracker.viewByHour)
};
currentAnalyticsData.productPerformances.Add(performance);
}
}
private string GetProductName(string productId)
{
// 从商店系统获取产品名称
// 简化实现
return productId;
}
private string GetPopularTime(Dictionary<string, int> viewByHour)
{
if (viewByHour.Count == 0) return "无数据";
string popularHour = "";
int maxViews = 0;
foreach (var pair in viewByHour)
{
if (pair.Value > maxViews)
{
maxViews = pair.Value;
popularHour = pair.Key;
}
}
return popularHour;
}
private void StoreDataLocally()
{
string fileName = $"Analytics_{DateTime.Now:yyyyMMdd_HHmmss}.json";
string filePath = System.IO.Path.Combine(Application.persistentDataPath, localStoragePath, fileName);
// 确保目录存在
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(filePath));
// 序列化数据为JSON
string jsonData = JsonUtility.ToJson(currentAnalyticsData, true);
// 写入文件
System.IO.File.WriteAllText(filePath, jsonData);
Debug.Log($"分析数据已保存到: {filePath}");
}
private void SendDataToServer()
{
// 实现发送数据到分析服务器的逻辑
// 使用HTTP请求发送JSON数据
Debug.Log("发送分析数据到服务器...");
// 这里需要实现实际的HTTP请求
// StartCoroutine(SendAnalyticsDataCoroutine());
}
public AnalyticsData GetCurrentAnalytics()
{
return currentAnalyticsData;
}
public List<string> GetCustomerRecommendations(string customerId)
{
if (!customerTrackers.ContainsKey(customerId))
{
return new List<string>();
}
var tracker = customerTrackers[customerId];
var recommendations = new List<string>();
// 基于购买历史推荐
foreach (var purchasedProduct in tracker.purchaseHistory)
{
// 查找相似产品
// 简化实现
string similarProduct = FindSimilarProduct(purchasedProduct);
if (!string.IsNullOrEmpty(similarProduct) &&
!tracker.purchaseHistory.Contains(similarProduct))
{
recommendations.Add(similarProduct);
}
if (recommendations.Count >= 5) break;
}
return recommendations;
}
private string FindSimilarProduct(string productId)
{
// 实现查找相似产品的逻辑
// 基于类别、价格范围、品牌等
return null; // 简化实现
}
public HeatmapData GenerateHeatmap()
{
if (!enableHeatmapGeneration) return null;
var heatmap = new HeatmapData
{
storeId = currentAnalyticsData.storeId,
generationTime = DateTime.Now
};
// 生成基于客户移动路径的热图数据
foreach (var tracker in customerTrackers.Values)
{
if (tracker.movementPath != null)
{
heatmap.AddMovementPath(tracker.movementPath, tracker.pathIndex);
}
}
return heatmap;
}
public class HeatmapData
{
public string storeId;
public DateTime generationTime;
public List<Vector3> hotSpots = new List<Vector3>();
public List<Vector3> coldSpots = new List<Vector3>();
public Dictionary<Vector3, int> visitDensity = new Dictionary<Vector3, int>();
public void AddMovementPath(Vector3[] path, int length)
{
for (int i = 0; i < length && i < path.Length; i++)
{
Vector3 gridPos = SnapToGrid(path[i]);
if (!visitDensity.ContainsKey(gridPos))
{
visitDensity[gridPos] = 0;
}
visitDensity[gridPos]++;
}
}
private Vector3 SnapToGrid(Vector3 position)
{
// 将位置对齐到网格
float gridSize = 0.5f;
return new Vector3(
Mathf.Round(position.x / gridSize) * gridSize,
Mathf.Round(position.y / gridSize) * gridSize,
Mathf.Round(position.z / gridSize) * gridSize
);
}
public void AnalyzeHotSpots()
{
hotSpots.Clear();
coldSpots.Clear();
int threshold = 5; // 访问次数阈值
foreach (var pair in visitDensity)
{
if (pair.Value > threshold)
{
hotSpots.Add(pair.Key);
}
else if (pair.Value == 0)
{
coldSpots.Add(pair.Key);
}
}
}
}
public PredictionData GeneratePredictions()
{
if (!enablePredictiveAnalytics) return null;
var predictions = new PredictionData
{
storeId = currentAnalyticsData.storeId,
predictionDate = DateTime.Now
};
// 预测未来销售
predictions.salesPredictions = PredictFutureSales();
// 预测客户行为
predictions.customerBehaviorPredictions = PredictCustomerBehavior();
// 预测库存需求
predictions.inventoryPredictions = PredictInventoryNeeds();
return predictions;
}
private List<SalesPrediction> PredictFutureSales()
{
var predictions = new List<SalesPrediction>();
// 基于历史数据的简单预测
// 实际项目中可能需要使用机器学习模型
DateTime tomorrow = DateTime.Today.AddDays(1);
foreach (var performance in currentAnalyticsData.productPerformances)
{
// 使用7天移动平均进行预测
float predictedSales = performance.purchaseCount * 1.1f; // 简化预测
predictions.Add(new SalesPrediction
{
productId = performance.productId,
date = tomorrow,
predictedQuantity = (int)predictedSales,
confidenceLevel = 0.7f
});
}
return predictions;
}
private List<CustomerBehaviorPrediction> PredictCustomerBehavior()
{
var predictions = new List<CustomerBehaviorPrediction>();
// 预测客户行为
// 例如:哪些客户可能流失,哪些可能进行大额购买
return predictions;
}
private List<InventoryPrediction> PredictInventoryNeeds()
{
var predictions = new List<InventoryPrediction>();
// 基于销售预测预测库存需求
return predictions;
}
[Serializable]
public class PredictionData
{
public string storeId;
public DateTime predictionDate;
public List<SalesPrediction> salesPredictions = new List<SalesPrediction>();
public List<CustomerBehaviorPrediction> customerBehaviorPredictions = new List<CustomerBehaviorPrediction>();
public List<InventoryPrediction> inventoryPredictions = new List<InventoryPrediction>();
}
[Serializable]
public class SalesPrediction
{
public string productId;
public DateTime date;
public int predictedQuantity;
public float confidenceLevel; // 0-1
public string predictionReason;
}
[Serializable]
public class CustomerBehaviorPrediction
{
public string customerId;
public DateTime predictionDate;
public PredictedBehavior behavior;
public float probability;
public DateTime expectedDate;
public enum PredictedBehavior
{
RepeatPurchase,
HighValuePurchase,
ChurnRisk,
CategoryExpansion
}
}
[Serializable]
public class InventoryPrediction
{
public string productId;
public DateTime date;
public int recommendedStock;
public int reorderPoint;
public DateTime expectedRestockDate;
}
}
}
10.2 增强现实技术的实时交互与工业应用
增强现实技术通过将数字信息叠加到真实世界,为工业维护、远程协助、培训等场景提供了革命性的解决方案。以下是工业级AR应用的核心实现:
(由于篇幅限制,此处继续内容将在后续部分展开,包括:工业AR维护系统、AR远程协作平台、AR培训系统、空间计算与定位技术、AR云架构等深度内容,总字数将超过10000字,完整覆盖VR/AR商业化应用的各个方面。)
这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!
更多推荐


所有评论(0)