第9章 汽车工业混合现实展示系统深度开发实战
汽车工业混合现实展示系统开发实战 本章介绍了工业级汽车混合现实展示系统的架构设计与开发实践。系统采用模块化设计,整合了汽车模型控制、可视化、交互和多用户协作等核心功能,支持营销展示、技术培训、设计评审等多种应用场景。关键技术包括: 企业级系统架构,支持高精度模型加载与实时交互 多模式切换(营销/培训/设计/维护) 多用户协同功能 企业数据连接与安全验证 性能监控与日志系统 系统通过Unity和MR
第9章 汽车工业混合现实展示系统深度开发实战
9.1 工业级汽车展示系统架构设计与商业价值分析
在汽车工业领域,混合现实展示系统已经超越了简单的产品展示功能,成为集营销、培训、设计和售后支持于一体的综合性解决方案。工业级汽车展示系统需要处理高精度三维模型、实时物理交互、多用户协同以及与企业数据系统的深度集成。
以下是一个工业级汽车展示系统的整体架构设计:
using System;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.Input;
namespace AutomotiveIndustryMR
{
/// <summary>
/// 工业级汽车展示系统主控制器
/// 负责协调所有子系统,管理应用状态和企业级功能
/// </summary>
public class IndustrialCarPresentationSystem : MonoBehaviour
{
[System.Serializable]
public class SystemConfiguration
{
public string SystemId;
public string EnterpriseLicenseKey;
public bool EnableMultiUserMode;
public bool EnableAnalyticsTracking;
public bool EnableRemoteAssistance;
public int MaxConcurrentUsers = 5;
public DataSyncMode DataSyncMode;
public PresentationMode DefaultMode;
}
public enum DataSyncMode
{
RealTime,
OnDemand,
Batch
}
public enum PresentationMode
{
MarketingDemo,
TechnicalTraining,
DesignReview,
MaintenanceGuide,
CustomizationStudio
}
private SystemConfiguration currentConfig;
private PresentationMode currentMode;
private Dictionary<string, CarModelData> loadedModels;
private List<UserSession> activeSessions;
private EnterpriseDataConnector dataConnector;
private SystemPerformanceMonitor performanceMonitor;
private SecurityManager securityManager;
private CarModelController currentCarModel;
private VisualizationManager visualizationManager;
private InteractionSystem interactionSystem;
private CollaborationManager collaborationManager;
private float systemUptime;
private DateTime systemStartTime;
private int totalInteractions;
private float averageSessionDuration;
private void Awake()
{
InitializeEnterpriseSystem();
LoadSystemConfiguration();
InitializeSubsystems();
StartPerformanceMonitoring();
ConnectToEnterpriseServices();
}
private void InitializeEnterpriseSystem()
{
// 初始化企业级系统组件
DontDestroyOnLoad(gameObject);
systemStartTime = DateTime.Now;
// 创建企业级数据连接器
dataConnector = gameObject.AddComponent<EnterpriseDataConnector>();
// 初始化安全管理器
securityManager = gameObject.AddComponent<SecurityManager>();
securityManager.Initialize(OnSecurityEvent);
// 设置企业级日志系统
ConfigureEnterpriseLogging();
}
private void LoadSystemConfiguration()
{
// 从企业配置服务器加载系统配置
string configJson = EnterpriseConfigService.LoadConfiguration("AutomotiveMRSystem");
if (!string.IsNullOrEmpty(configJson))
{
currentConfig = JsonUtility.FromJson<SystemConfiguration>(configJson);
}
else
{
// 使用默认配置
currentConfig = CreateDefaultConfiguration();
}
currentMode = currentConfig.DefaultMode;
// 验证企业许可证
ValidateEnterpriseLicense();
}
private SystemConfiguration CreateDefaultConfiguration()
{
return new SystemConfiguration
{
SystemId = GenerateSystemId(),
EnterpriseLicenseKey = "EVAL-2024-001",
EnableMultiUserMode = true,
EnableAnalyticsTracking = true,
EnableRemoteAssistance = true,
MaxConcurrentUsers = 5,
DataSyncMode = DataSyncMode.RealTime,
DefaultMode = PresentationMode.MarketingDemo
};
}
private void InitializeSubsystems()
{
// 初始化汽车模型控制器
currentCarModel = gameObject.AddComponent<CarModelController>();
currentCarModel.OnModelLoaded += OnCarModelLoaded;
currentCarModel.OnModelError += OnModelError;
// 初始化可视化管理器
visualizationManager = gameObject.AddComponent<VisualizationManager>();
visualizationManager.Initialize(currentConfig);
// 初始化交互系统
interactionSystem = gameObject.AddComponent<InteractionSystem>();
interactionSystem.Initialize(this);
// 初始化协作管理器(如果启用多用户模式)
if (currentConfig.EnableMultiUserMode)
{
collaborationManager = gameObject.AddComponent<CollaborationManager>();
collaborationManager.Initialize(currentConfig.MaxConcurrentUsers);
}
// 初始化分析系统
if (currentConfig.EnableAnalyticsTracking)
{
InitializeAnalyticsSystem();
}
}
private void StartPerformanceMonitoring()
{
performanceMonitor = gameObject.AddComponent<SystemPerformanceMonitor>();
performanceMonitor.StartMonitoring(OnPerformanceAlert);
// 定期报告系统状态
InvokeRepeating("ReportSystemStatus", 60f, 300f); // 每5分钟报告一次
}
private void ConnectToEnterpriseServices()
{
// 连接企业数据服务
StartCoroutine(dataConnector.ConnectToEnterpriseServices(
OnDataConnected,
OnDataConnectionFailed
));
// 加载汽车模型目录
LoadCarModelCatalog();
}
/// <summary>
/// 加载汽车模型目录
/// 从企业服务器获取可用的汽车模型列表
/// </summary>
private void LoadCarModelCatalog()
{
StartCoroutine(dataConnector.LoadCarModelCatalog(
OnCatalogLoaded,
OnCatalogLoadFailed
));
}
private void OnCatalogLoaded(List<CarModelCatalogItem> catalog)
{
loadedModels = new Dictionary<string, CarModelData>();
foreach (var item in catalog)
{
// 预加载模型元数据
loadedModels[item.ModelId] = new CarModelData
{
ModelId = item.ModelId,
ModelName = item.ModelName,
Manufacturer = item.Manufacturer,
Year = item.Year,
BasePrice = item.BasePrice,
AvailableColors = item.AvailableColors,
AvailableFeatures = item.AvailableFeatures,
ModelPath = item.ModelPath,
ModelSizeMB = item.ModelSizeMB,
LastUpdated = item.LastUpdated
};
}
Debug.Log($"Loaded {catalog.Count} car models from enterprise catalog");
// 触发UI更新
UIManager.Instance.UpdateCarModelCatalog(catalog);
}
/// <summary>
/// 加载指定的汽车模型
/// </summary>
public void LoadCarModel(string modelId, Action<CarModelController> onComplete = null)
{
if (!loadedModels.ContainsKey(modelId))
{
Debug.LogError($"Car model {modelId} not found in catalog");
return;
}
CarModelData modelData = loadedModels[modelId];
// 记录分析事件
AnalyticsService.RecordEvent("CarModelLoadRequested", new
{
ModelId = modelId,
ModelName = modelData.ModelName,
Timestamp = DateTime.UtcNow,
UserId = UserSession.Current.UserId
});
// 加载模型
StartCoroutine(currentCarModel.LoadModel(
modelData,
(success) =>
{
if (success)
{
OnCarModelLoadedSuccess(modelData);
onComplete?.Invoke(currentCarModel);
}
}
));
}
private void OnCarModelLoadedSuccess(CarModelData modelData)
{
// 应用当前演示模式配置
ConfigureForPresentationMode(currentMode);
// 初始化交互功能
interactionSystem.InitializeModelInteractions(currentCarModel);
// 通知协作系统(如果启用)
if (collaborationManager != null)
{
collaborationManager.OnModelLoaded(currentCarModel, modelData);
}
// 记录成功加载
EnterpriseLoggingService.LogModelLoad(
modelData.ModelId,
UserSession.Current.UserId,
true,
null
);
}
/// <summary>
/// 切换演示模式
/// </summary>
public void SwitchPresentationMode(PresentationMode newMode)
{
PresentationMode previousMode = currentMode;
currentMode = newMode;
// 重新配置系统
ConfigureForPresentationMode(newMode);
// 记录模式切换
AnalyticsService.RecordEvent("PresentationModeChanged", new
{
PreviousMode = previousMode.ToString(),
NewMode = newMode.ToString(),
Timestamp = DateTime.UtcNow,
CarModelId = currentCarModel?.CurrentModelId
});
// 通知UI更新
UIManager.Instance.OnPresentationModeChanged(previousMode, newMode);
}
private void ConfigureForPresentationMode(PresentationMode mode)
{
if (currentCarModel == null) return;
switch (mode)
{
case PresentationMode.MarketingDemo:
ConfigureMarketingDemoMode();
break;
case PresentationMode.TechnicalTraining:
ConfigureTechnicalTrainingMode();
break;
case PresentationMode.DesignReview:
ConfigureDesignReviewMode();
break;
case PresentationMode.MaintenanceGuide:
ConfigureMaintenanceGuideMode();
break;
case PresentationMode.CustomizationStudio:
ConfigureCustomizationStudioMode();
break;
}
}
private void ConfigureMarketingDemoMode()
{
// 营销演示模式:高视觉质量,简化交互
visualizationManager.SetVisualQuality(VisualQuality.High);
visualizationManager.EnableSpecialEffects(true);
interactionSystem.EnableBasicInteractions(true);
interactionSystem.EnableAdvancedInteractions(false);
interactionSystem.SetInteractionSpeed(1.0f);
currentCarModel.ConfigureForMarketing();
// 显示营销信息叠加层
UIManager.Instance.ShowMarketingOverlay(true);
}
private void ConfigureTechnicalTrainingMode()
{
// 技术培训模式:详细标注,分步指导
visualizationManager.SetVisualQuality(VisualQuality.Medium);
visualizationManager.EnableSpecialEffects(false);
interactionSystem.EnableBasicInteractions(true);
interactionSystem.EnableAdvancedInteractions(true);
interactionSystem.SetInteractionSpeed(0.7f);
currentCarModel.ConfigureForTraining();
// 显示技术文档和标注
UIManager.Instance.ShowTechnicalAnnotations(true);
}
/// <summary>
/// 启动远程协助会话
/// </summary>
public void StartRemoteAssistanceSession(string expertId)
{
if (!currentConfig.EnableRemoteAssistance)
{
Debug.LogWarning("Remote assistance is not enabled in current configuration");
return;
}
RemoteAssistanceSession session = new RemoteAssistanceSession
{
SessionId = Guid.NewGuid().ToString(),
UserId = UserSession.Current.UserId,
ExpertId = expertId,
CarModelId = currentCarModel?.CurrentModelId,
StartTime = DateTime.UtcNow,
Status = SessionStatus.Connecting
};
StartCoroutine(RemoteAssistanceService.StartSession(
session,
OnRemoteAssistanceConnected,
OnRemoteAssistanceFailed
));
}
private void OnRemoteAssistanceConnected(RemoteAssistanceSession session)
{
// 启用协作功能
if (collaborationManager != null)
{
collaborationManager.EnableRemoteControl(true);
collaborationManager.SetExpertUser(session.ExpertId);
}
// 通知用户
UIManager.Instance.ShowNotification(
$"Connected to expert: {session.ExpertId}",
NotificationType.Success,
3f
);
// 记录会话开始
AnalyticsService.RecordEvent("RemoteAssistanceStarted", new
{
SessionId = session.SessionId,
ExpertId = session.ExpertId,
Timestamp = DateTime.UtcNow
});
}
/// <summary>
/// 导出当前配置为报价单
/// </summary>
public void ExportCurrentConfigurationAsQuote()
{
if (currentCarModel == null)
{
Debug.LogWarning("No car model loaded");
return;
}
CarConfiguration config = currentCarModel.GetCurrentConfiguration();
CustomerQuote quote = new CustomerQuote
{
QuoteId = GenerateQuoteId(),
CustomerId = UserSession.Current.UserId,
CarModelId = config.ModelId,
BasePrice = config.BasePrice,
SelectedOptions = config.SelectedOptions,
TotalPrice = config.CalculateTotalPrice(),
CreatedDate = DateTime.UtcNow,
ValidUntil = DateTime.UtcNow.AddDays(30)
};
// 生成PDF报价单
StartCoroutine(QuoteGenerator.GeneratePDFQuote(
quote,
OnQuoteGenerated,
OnQuoteGenerationFailed
));
}
private void OnQuoteGenerated(byte[] pdfData)
{
// 保存到本地
string filePath = Path.Combine(
Application.persistentDataPath,
$"Quote_{DateTime.Now:yyyyMMdd_HHmmss}.pdf"
);
File.WriteAllBytes(filePath, pdfData);
// 上传到企业CRM系统
StartCoroutine(CRMIntegrationService.UploadQuote(
pdfData,
OnQuoteUploaded,
OnQuoteUploadFailed
));
// 显示成功消息
UIManager.Instance.ShowNotification(
"Quote generated and saved successfully",
NotificationType.Success,
3f
);
}
/// <summary>
/// 生成系统性能报告
/// </summary>
public SystemPerformanceReport GeneratePerformanceReport()
{
systemUptime = (float)(DateTime.Now - systemStartTime).TotalHours;
return new SystemPerformanceReport
{
ReportId = Guid.NewGuid().ToString(),
GenerationTime = DateTime.Now,
SystemUptimeHours = systemUptime,
TotalSessions = UserSessionManager.TotalSessions,
AverageSessionDuration = averageSessionDuration,
TotalInteractions = totalInteractions,
PerformanceMetrics = performanceMonitor.GetCurrentMetrics(),
LoadedModelsCount = loadedModels?.Count ?? 0,
ActiveSessionsCount = activeSessions?.Count ?? 0,
SystemConfiguration = currentConfig
};
}
private void ReportSystemStatus()
{
SystemPerformanceReport report = GeneratePerformanceReport();
// 发送到监控服务器
StartCoroutine(MonitoringService.ReportSystemStatus(
report,
OnStatusReported,
OnStatusReportFailed
));
}
private void OnDestroy()
{
// 清理资源
if (currentCarModel != null)
{
currentCarModel.Cleanup();
}
if (performanceMonitor != null)
{
performanceMonitor.StopMonitoring();
}
// 报告系统关闭
ReportSystemShutdown();
}
private void ReportSystemShutdown()
{
SystemShutdownReport report = new SystemShutdownReport
{
ShutdownTime = DateTime.Now,
TotalUptimeHours = systemUptime,
FinalPerformanceMetrics = performanceMonitor?.GetCurrentMetrics(),
Reason = "NormalShutdown"
};
// 异步发送关闭报告
StartCoroutine(MonitoringService.ReportSystemShutdown(report));
}
}
/// <summary>
/// 汽车模型数据类
/// </summary>
[System.Serializable]
public class CarModelData
{
public string ModelId;
public string ModelName;
public string Manufacturer;
public int Year;
public decimal BasePrice;
public List<CarColor> AvailableColors;
public List<CarFeature> AvailableFeatures;
public string ModelPath;
public float ModelSizeMB;
public DateTime LastUpdated;
public Dictionary<string, object> TechnicalSpecifications;
public List<CarComponent> Components;
}
/// <summary>
/// 汽车颜色配置
/// </summary>
[System.Serializable]
public class CarColor
{
public string ColorId;
public string ColorName;
public Color UnityColor;
public string PaintMaterialId;
public decimal PricePremium;
public bool IsMetallic;
public bool IsPearlescent;
public List<string> AvailableForModels;
}
/// <summary>
/// 汽车功能特性
/// </summary>
[System.Serializable]
public class CarFeature
{
public string FeatureId;
public string FeatureName;
public FeatureCategory Category;
public decimal Price;
public bool IsStandard;
public List<string> IncompatibleFeatures;
public GameObject FeaturePrefab;
public string Description;
public List<string> TechnicalDetails;
}
public enum FeatureCategory
{
Exterior,
Interior,
Performance,
Safety,
Technology,
Comfort
}
/// <summary>
/// 汽车组件定义
/// </summary>
[System.Serializable]
public class CarComponent
{
public string ComponentId;
public string ComponentName;
public ComponentType Type;
public GameObject ModelPrefab;
public Vector3 DefaultPosition;
public Quaternion DefaultRotation;
public bool IsRemovable;
public bool IsInteractable;
public List<ComponentAttachmentPoint> AttachmentPoints;
public TechnicalSpecification Specifications;
}
public enum ComponentType
{
Engine,
Transmission,
Suspension,
Brakes,
Wheels,
BodyPanel,
InteriorPanel,
ElectronicModule,
Lighting,
Other
}
}
9.2 工业级工程架构与资源管理系统
工业级汽车展示项目需要管理大量高精度资源,包括汽车模型、材质、音频、视频以及配置数据。以下是资源管理系统的实现:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
namespace AutomotiveIndustryMR
{
/// <summary>
/// 工业级资源管理器
/// 负责加载、缓存和管理所有项目资源
/// </summary>
public class IndustrialResourceManager : MonoBehaviour
{
[System.Serializable]
public class ResourceCatalog
{
public string CatalogVersion;
public DateTime LastUpdated;
public List<ModelResource> CarModels;
public List<MaterialResource> Materials;
public List<TextureResource> Textures;
public List<AudioResource> AudioClips;
public List<VideoResource> Videos;
public List<PrefabResource> Prefabs;
public List<ConfigurationResource> Configurations;
}
[System.Serializable]
public class ModelResource
{
public string ResourceId;
public string ModelName;
public string FilePath;
public long FileSizeBytes;
public string Checksum;
public DateTime LastModified;
public List<string> Dependencies;
public ModelLoadSettings LoadSettings;
public Dictionary<string, string> Metadata;
}
[System.Serializable]
public class ModelLoadSettings
{
public bool LoadMaterials = true;
public bool LoadAnimations = true;
public bool LoadColliders = true;
public float ScaleFactor = 1.0f;
public bool GenerateLightmapUVs = false;
public int MaxLODLevel = 3;
public bool OptimizeMesh = true;
}
private ResourceCatalog currentCatalog;
private Dictionary<string, ResourceCacheEntry> resourceCache;
private Dictionary<string, GameObject> instantiatedModels;
private Dictionary<string, Material> materialCache;
private Dictionary<string, AudioClip> audioCache;
private Dictionary<string, Texture2D> textureCache;
private string cacheDirectory;
private long maxCacheSizeBytes = 1024L * 1024L * 1024L * 2; // 2GB
private long currentCacheSizeBytes = 0;
private bool isInitialized = false;
private List<ResourceLoadRequest> pendingRequests;
private List<ResourceLoadRequest> activeRequests;
private int maxConcurrentLoads = 3;
private void Awake()
{
DontDestroyOnLoad(gameObject);
InitializeCache();
LoadResourceCatalog();
StartCacheMaintenance();
}
private void InitializeCache()
{
cacheDirectory = Path.Combine(Application.persistentDataPath, "ResourceCache");
if (!Directory.Exists(cacheDirectory))
{
Directory.CreateDirectory(cacheDirectory);
}
resourceCache = new Dictionary<string, ResourceCacheEntry>();
instantiatedModels = new Dictionary<string, GameObject>();
materialCache = new Dictionary<string, Material>();
audioCache = new Dictionary<string, AudioClip>();
textureCache = new Dictionary<string, Texture2D>();
pendingRequests = new List<ResourceLoadRequest>();
activeRequests = new List<ResourceLoadRequest>();
// 加载现有缓存索引
LoadCacheIndex();
}
private void LoadCacheIndex()
{
string indexFile = Path.Combine(cacheDirectory, "CacheIndex.json");
if (File.Exists(indexFile))
{
try
{
string json = File.ReadAllText(indexFile);
CacheIndex index = JsonUtility.FromJson<CacheIndex>(json);
currentCacheSizeBytes = index.TotalSizeBytes;
foreach (var entry in index.Entries)
{
resourceCache[entry.ResourceId] = entry;
// 验证缓存文件是否存在
string cachedPath = GetCachedPath(entry.ResourceId);
if (!File.Exists(cachedPath))
{
resourceCache.Remove(entry.ResourceId);
currentCacheSizeBytes -= entry.SizeBytes;
}
}
Debug.Log($"Loaded cache index with {resourceCache.Count} entries, total size: {FormatBytes(currentCacheSizeBytes)}");
}
catch (Exception ex)
{
Debug.LogError($"Failed to load cache index: {ex.Message}");
}
}
}
private async void LoadResourceCatalog()
{
try
{
// 首先尝试从本地加载
string localCatalogPath = Path.Combine(cacheDirectory, "ResourceCatalog.json");
if (File.Exists(localCatalogPath))
{
string localJson = await File.ReadAllTextAsync(localCatalogPath);
currentCatalog = JsonUtility.FromJson<ResourceCatalog>(localJson);
// 检查是否需要更新
if (await CheckCatalogUpdateNeeded())
{
await DownloadUpdatedCatalog();
}
}
else
{
await DownloadUpdatedCatalog();
}
isInitialized = true;
OnCatalogLoaded?.Invoke(true);
}
catch (Exception ex)
{
Debug.LogError($"Failed to load resource catalog: {ex.Message}");
OnCatalogLoaded?.Invoke(false);
}
}
private async Task<bool> CheckCatalogUpdateNeeded()
{
if (currentCatalog == null) return true;
try
{
string serverCatalogUrl = EnterpriseConfigService.GetResourceCatalogUrl();
using (UnityWebRequest request = UnityWebRequest.Get(serverCatalogUrl))
{
request.timeout = 10;
var operation = request.SendWebRequest();
while (!operation.isDone)
{
await Task.Yield();
}
if (request.result == UnityWebRequest.Result.Success)
{
string serverJson = request.downloadHandler.text;
ResourceCatalog serverCatalog = JsonUtility.FromJson<ResourceCatalog>(serverJson);
return serverCatalog.LastUpdated > currentCatalog.LastUpdated;
}
}
}
catch
{
// 网络错误时使用本地目录
}
return false;
}
/// <summary>
/// 异步加载汽车模型
/// </summary>
public async Task<GameObject> LoadCarModelAsync(string modelId, Action<float> progressCallback = null)
{
if (!isInitialized)
{
throw new InvalidOperationException("Resource manager not initialized");
}
// 检查是否已实例化
if (instantiatedModels.ContainsKey(modelId))
{
return instantiatedModels[modelId];
}
// 查找模型资源
ModelResource modelResource = currentCatalog.CarModels.FirstOrDefault(m => m.ResourceId == modelId);
if (modelResource == null)
{
throw new ArgumentException($"Model resource not found: {modelId}");
}
// 创建加载请求
var request = new ResourceLoadRequest
{
ResourceId = modelId,
ResourceType = ResourceType.Model,
Priority = LoadPriority.High,
ProgressCallback = progressCallback
};
// 添加到待处理队列
pendingRequests.Add(request);
// 等待请求完成
await request.CompletionTask;
if (!request.IsSuccessful)
{
throw new Exception($"Failed to load model {modelId}: {request.ErrorMessage}");
}
// 实例化模型
GameObject modelInstance = Instantiate(request.LoadedObject as GameObject);
instantiatedModels[modelId] = modelInstance;
// 应用加载设置
ApplyModelLoadSettings(modelInstance, modelResource.LoadSettings);
return modelInstance;
}
private async Task ProcessLoadRequest(ResourceLoadRequest request)
{
try
{
// 检查缓存
string cachedPath = GetCachedPath(request.ResourceId);
if (File.Exists(cachedPath) && resourceCache.ContainsKey(request.ResourceId))
{
// 从缓存加载
await LoadFromCache(request, cachedPath);
}
else
{
// 从服务器下载
await DownloadAndCacheResource(request);
}
request.IsSuccessful = true;
request.CompletionSource.SetResult(true);
}
catch (Exception ex)
{
request.IsSuccessful = false;
request.ErrorMessage = ex.Message;
request.CompletionSource.SetException(ex);
}
finally
{
activeRequests.Remove(request);
ProcessNextRequest();
}
}
private async Task DownloadAndCacheResource(ResourceLoadRequest request)
{
// 获取资源URL
string downloadUrl = GetResourceDownloadUrl(request.ResourceId, request.ResourceType);
// 创建临时文件路径
string tempPath = Path.Combine(cacheDirectory, $"temp_{Guid.NewGuid()}");
try
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(downloadUrl))
{
// 设置下载处理器
var downloadHandler = new DownloadHandlerFile(tempPath);
webRequest.downloadHandler = downloadHandler;
// 发送请求
var operation = webRequest.SendWebRequest();
// 更新进度
while (!operation.isDone)
{
await Task.Yield();
request.ProgressCallback?.Invoke(webRequest.downloadProgress);
}
if (webRequest.result != UnityWebRequest.Result.Success)
{
throw new Exception($"Download failed: {webRequest.error}");
}
// 验证文件
FileInfo fileInfo = new FileInfo(tempPath);
if (fileInfo.Length == 0)
{
throw new Exception("Downloaded file is empty");
}
// 计算校验和
string checksum = CalculateFileChecksum(tempPath);
// 验证校验和(如果资源目录中有提供)
if (ShouldValidateChecksum(request.ResourceId))
{
string expectedChecksum = GetExpectedChecksum(request.ResourceId);
if (checksum != expectedChecksum)
{
throw new Exception($"Checksum mismatch for resource {request.ResourceId}");
}
}
// 移动到缓存位置
string cachedPath = GetCachedPath(request.ResourceId);
File.Move(tempPath, cachedPath, true);
// 添加到缓存索引
var cacheEntry = new ResourceCacheEntry
{
ResourceId = request.ResourceId,
CachePath = cachedPath,
SizeBytes = fileInfo.Length,
LastAccessed = DateTime.Now,
Checksum = checksum
};
resourceCache[request.ResourceId] = cacheEntry;
currentCacheSizeBytes += fileInfo.Length;
// 加载资源到内存
await LoadResourceFromFile(request, cachedPath);
// 保存缓存索引
SaveCacheIndex();
// 清理缓存(如果需要)
CleanupCacheIfNeeded();
}
}
finally
{
// 清理临时文件
if (File.Exists(tempPath))
{
File.Delete(tempPath);
}
}
}
private async Task LoadResourceFromFile(ResourceLoadRequest request, string filePath)
{
switch (request.ResourceType)
{
case ResourceType.Model:
await LoadModelFromFile(request, filePath);
break;
case ResourceType.Material:
await LoadMaterialFromFile(request, filePath);
break;
case ResourceType.Texture:
await LoadTextureFromFile(request, filePath);
break;
case ResourceType.Audio:
await LoadAudioFromFile(request, filePath);
break;
case ResourceType.Video:
await LoadVideoFromFile(request, filePath);
break;
}
}
private async Task LoadModelFromFile(ResourceLoadRequest request, string filePath)
{
// 这里需要根据实际文件格式实现模型加载
// 例如:GLB, FBX, OBJ等格式
string extension = Path.GetExtension(filePath).ToLower();
switch (extension)
{
case ".glb":
case ".gltf":
await LoadGLTFModel(request, filePath);
break;
case ".fbx":
await LoadFBXModel(request, filePath);
break;
case ".obj":
await LoadOBJModel(request, filePath);
break;
default:
throw new NotSupportedException($"Unsupported model format: {extension}");
}
}
private async Task LoadGLTFModel(ResourceLoadRequest request, string filePath)
{
// 使用GLTF插件加载模型
// 这里假设使用了UnityGLTF插件
try
{
/*
// 实际代码需要根据使用的GLTF插件调整
var gltfImporter = new GLTFImporter();
await gltfImporter.LoadAsync(filePath);
GameObject model = gltfImporter.GetSceneObject();
request.LoadedObject = model;
*/
// 临时实现:创建一个立方体作为占位符
await Task.Delay(100); // 模拟加载延迟
GameObject placeholder = GameObject.CreatePrimitive(PrimitiveType.Cube);
placeholder.name = $"Model_{request.ResourceId}";
request.LoadedObject = placeholder;
request.ProgressCallback?.Invoke(1.0f);
}
catch (Exception ex)
{
throw new Exception($"Failed to load GLTF model: {ex.Message}", ex);
}
}
private void ApplyModelLoadSettings(GameObject model, ModelLoadSettings settings)
{
if (model == null || settings == null) return;
// 应用缩放
model.transform.localScale = Vector3.one * settings.ScaleFactor;
// 配置LOD
if (settings.MaxLODLevel > 0)
{
ConfigureModelLOD(model, settings.MaxLODLevel);
}
// 优化网格
if (settings.OptimizeMesh)
{
OptimizeModelMeshes(model);
}
// 配置碰撞体
if (settings.LoadColliders)
{
AddMissingColliders(model);
}
else
{
RemoveAllColliders(model);
}
}
private void ConfigureModelLOD(GameObject model, int maxLODLevel)
{
// 为模型配置LOD组
LODGroup lodGroup = model.GetComponent<LODGroup>();
if (lodGroup == null)
{
lodGroup = model.AddComponent<LODGroup>();
}
// 创建LOD级别
List<LOD> lods = new List<LOD>();
// 获取所有渲染器
Renderer[] allRenderers = model.GetComponentsInChildren<Renderer>();
// 创建不同细节级别的LOD
for (int i = 0; i < maxLODLevel; i++)
{
float screenRelativeHeight = 1.0f - (i * 0.2f); // 每个级别减少20%
screenRelativeHeight = Mathf.Max(screenRelativeHeight, 0.05f); // 最小5%
// 根据级别过滤渲染器(简化实现)
Renderer[] levelRenderers = allRenderers; // 实际应该根据细节级别筛选
lods.Add(new LOD(screenRelativeHeight, levelRenderers));
}
lodGroup.SetLODs(lods.ToArray());
}
/// <summary>
/// 预加载常用资源
/// </summary>
public async Task PreloadCommonResources()
{
List<Task> preloadTasks = new List<Task>();
// 预加载常用材质
var commonMaterials = currentCatalog.Materials
.Where(m => m.PreloadPriority > 0)
.OrderByDescending(m => m.PreloadPriority)
.Take(10);
foreach (var material in commonMaterials)
{
preloadTasks.Add(LoadMaterialAsync(material.ResourceId));
}
// 预加载常用纹理
var commonTextures = currentCatalog.Textures
.Where(t => t.PreloadPriority > 0)
.OrderByDescending(t => t.PreloadPriority)
.Take(20);
foreach (var texture in commonTextures)
{
preloadTasks.Add(LoadTextureAsync(texture.ResourceId));
}
// 并行预加载
await Task.WhenAll(preloadTasks);
Debug.Log($"Preloaded {preloadTasks.Count} common resources");
}
/// <summary>
/// 清理未使用的资源
/// </summary>
public void CleanupUnusedResources()
{
// 清理未使用的模型实例
List<string> modelsToRemove = new List<string>();
foreach (var kvp in instantiatedModels)
{
if (kvp.Value == null || !kvp.Value.activeInHierarchy)
{
modelsToRemove.Add(kvp.Key);
Destroy(kvp.Value);
}
}
foreach (string modelId in modelsToRemove)
{
instantiatedModels.Remove(modelId);
}
// 清理材质缓存
CleanupUnusedMaterials();
// 清理纹理缓存
CleanupUnusedTextures();
// 清理音频缓存
CleanupUnusedAudio();
// 强制执行垃圾回收
System.GC.Collect();
Resources.UnloadUnusedAssets();
Debug.Log($"Cleaned up unused resources. Remaining models: {instantiatedModels.Count}");
}
private void CleanupCacheIfNeeded()
{
if (currentCacheSizeBytes <= maxCacheSizeBytes) return;
// 按最后访问时间排序,清理最旧的缓存项
var oldEntries = resourceCache.Values
.OrderBy(e => e.LastAccessed)
.ToList();
long sizeToFree = currentCacheSizeBytes - maxCacheSizeBytes / 2; // 清理到50%容量
long freedSize = 0;
foreach (var entry in oldEntries)
{
if (freedSize >= sizeToFree) break;
try
{
if (File.Exists(entry.CachePath))
{
File.Delete(entry.CachePath);
}
resourceCache.Remove(entry.ResourceId);
freedSize += entry.SizeBytes;
currentCacheSizeBytes -= entry.SizeBytes;
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to delete cache file {entry.CachePath}: {ex.Message}");
}
}
SaveCacheIndex();
Debug.Log($"Cleaned up cache. Freed {FormatBytes(freedSize)}, remaining cache size: {FormatBytes(currentCacheSizeBytes)}");
}
private void StartCacheMaintenance()
{
// 每5分钟检查一次缓存
InvokeRepeating("PerformCacheMaintenance", 300f, 300f);
}
private void PerformCacheMaintenance()
{
// 验证缓存完整性
ValidateCacheIntegrity();
// 清理过期缓存
CleanupExpiredCache();
// 压缩缓存(如果支持)
CompressCacheIfNeeded();
}
public event Action<bool> OnCatalogLoaded;
}
/// <summary>
/// 资源加载请求
/// </summary>
public class ResourceLoadRequest
{
public string ResourceId;
public ResourceType ResourceType;
public LoadPriority Priority;
public Action<float> ProgressCallback;
public object LoadedObject;
public bool IsSuccessful;
public string ErrorMessage;
public TaskCompletionSource<bool> CompletionSource;
public Task<bool> CompletionTask => CompletionSource.Task;
public ResourceLoadRequest()
{
CompletionSource = new TaskCompletionSource<bool>();
}
}
public enum ResourceType
{
Model,
Material,
Texture,
Audio,
Video,
Prefab,
Configuration
}
public enum LoadPriority
{
Low,
Normal,
High,
Critical
}
/// <summary>
/// 缓存索引
/// </summary>
[System.Serializable]
public class CacheIndex
{
public List<ResourceCacheEntry> Entries;
public long TotalSizeBytes;
public DateTime LastUpdated;
}
/// <summary>
/// 缓存条目
/// </summary>
[System.Serializable]
public class ResourceCacheEntry
{
public string ResourceId;
public string CachePath;
public long SizeBytes;
public DateTime LastAccessed;
public string Checksum;
}
/// <summary>
/// 材质资源
/// </summary>
[System.Serializable]
public class MaterialResource
{
public string ResourceId;
public string MaterialName;
public string ShaderName;
public string BaseTextureId;
public List<string> AdditionalTextureIds;
public MaterialProperties Properties;
public int PreloadPriority;
}
/// <summary>
/// 纹理资源
/// </summary>
[System.Serializable]
public class TextureResource
{
public string ResourceId;
public string TextureName;
public TextureFormat Format;
public int Width;
public int Height;
public bool GenerateMipMaps;
public bool IsNormalMap;
public int PreloadPriority;
}
/// <summary>
/// 音频资源
/// </summary>
[System.Serializable]
public class AudioResource
{
public string ResourceId;
public string ClipName;
public AudioType Type;
public float Duration;
public int SampleRate;
public int Channels;
}
}
9.3 工业级视频播放与多媒体演示系统
工业级汽车展示需要支持高质量的视频播放,包括产品介绍视频、技术讲解、广告片等。以下是多媒体演示系统的实现:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Video;
using Microsoft.MixedReality.Toolkit.Input;
namespace AutomotiveIndustryMR
{
/// <summary>
/// 工业级视频播放管理器
/// 支持多种视频格式、流媒体、交互式视频和同步播放
/// </summary>
public class IndustrialVideoPlayer : MonoBehaviour
{
[System.Serializable]
public class VideoPlaylist
{
public string PlaylistId;
public string PlaylistName;
public List<VideoItem> Videos;
public PlayMode PlayMode;
public bool LoopPlaylist;
public float CrossfadeDuration = 1.0f;
}
[System.Serializable]
public class VideoItem
{
public string VideoId;
public string VideoName;
public string VideoPath;
public VideoSource SourceType;
public float Duration;
public VideoResolution Resolution;
public List<VideoChapter> Chapters;
public Dictionary<string, string> Metadata;
public VideoPlaybackSettings PlaybackSettings;
}
[System.Serializable]
public class VideoChapter
{
public string ChapterId;
public string Title;
public float StartTime;
public float EndTime;
public List<ChapterInteraction> Interactions;
}
[System.Serializable]
public class ChapterInteraction
{
public float TimePosition;
public InteractionType Type;
public string TargetObjectId;
public string ActionData;
public bool PauseOnInteraction;
}
public enum VideoSource
{
LocalFile,
StreamingURL,
EmbeddedResource,
AssetBundle
}
public enum VideoResolution
{
SD,
HD,
FullHD,
UltraHD,
Custom
}
public enum PlayMode
{
Sequential,
Random,
Shuffle
}
public enum InteractionType
{
ShowAnnotation,
HighlightComponent,
ChangeCamera,
LoadModel,
PlayAudio,
ShowText,
OpenURL
}
private VideoPlayer mainVideoPlayer;
private VideoPlayer secondaryVideoPlayer; // 用于交叉淡入淡出
private AudioSource audioOutput;
private RenderTexture videoRenderTexture;
private VideoPlaylist currentPlaylist;
private VideoItem currentVideo;
private int currentVideoIndex = -1;
private bool isPlaying = false;
private bool isBuffering = false;
private float playbackStartTime;
private Dictionary<string, VideoItem> videoCache = new Dictionary<string, VideoItem>();
private List<VideoChapter> activeChapters = new List<VideoChapter>();
private List<ChapterInteraction> pendingInteractions = new List<ChapterInteraction>();
private VideoDisplayController displayController;
private VideoInteractionHandler interactionHandler;
private VideoAnalyticsTracker analyticsTracker;
private float bufferProgress = 0f;
private float playbackProgress = 0f;
private long totalBytesRead = 0;
private float averageDownloadSpeed = 0f;
private void Awake()
{
InitializeVideoSystem();
SetupDisplayController();
InitializeAnalytics();
}
private void InitializeVideoSystem()
{
// 创建主视频播放器
GameObject videoPlayerObj = new GameObject("MainVideoPlayer");
videoPlayerObj.transform.SetParent(transform);
mainVideoPlayer = videoPlayerObj.AddComponent<VideoPlayer>();
ConfigureVideoPlayer(mainVideoPlayer);
// 创建辅助视频播放器(用于平滑过渡)
GameObject secondaryPlayerObj = new GameObject("SecondaryVideoPlayer");
secondaryPlayerObj.transform.SetParent(transform);
secondaryVideoPlayer = secondaryPlayerObj.AddComponent<VideoPlayer>();
ConfigureVideoPlayer(secondaryVideoPlayer);
secondaryVideoPlayer.gameObject.SetActive(false);
// 创建音频输出
audioOutput = gameObject.AddComponent<AudioSource>();
audioOutput.spatialize = true;
audioOutput.spatialBlend = 1.0f;
audioOutput.rolloffMode = AudioRolloffMode.Logarithmic;
mainVideoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;
mainVideoPlayer.SetTargetAudioSource(0, audioOutput);
// 创建渲染纹理
videoRenderTexture = new RenderTexture(1920, 1080, 24, RenderTextureFormat.ARGB32);
videoRenderTexture.Create();
mainVideoPlayer.targetTexture = videoRenderTexture;
// 设置事件处理
mainVideoPlayer.prepareCompleted += OnVideoPrepared;
mainVideoPlayer.seekCompleted += OnSeekCompleted;
mainVideoPlayer.loopPointReached += OnVideoEnded;
mainVideoPlayer.errorReceived += OnVideoError;
// 初始化交互处理器
interactionHandler = gameObject.AddComponent<VideoInteractionHandler>();
interactionHandler.Initialize(this);
}
private void ConfigureVideoPlayer(VideoPlayer player)
{
player.playOnAwake = false;
player.waitForFirstFrame = true;
player.skipOnDrop = true;
player.renderMode = VideoRenderMode.RenderTexture;
player.aspectRatio = VideoAspectRatio.FitVertically;
player.isLooping = false;
player.controlledAudioTrackCount = 1;
}
private void SetupDisplayController()
{
// 创建视频显示控制器
displayController = gameObject.AddComponent<VideoDisplayController>();
displayController.Initialize(videoRenderTexture);
// 设置显示模式
displayController.SetDisplayMode(VideoDisplayMode.FloatingScreen);
displayController.SetScreenSize(3.0f, 1.6875f); // 16:9 aspect ratio
displayController.SetScreenPosition(new Vector3(0, 1.5f, 3.0f));
// 配置交互
displayController.EnableInteractions(true);
}
private void InitializeAnalytics()
{
analyticsTracker = gameObject.AddComponent<VideoAnalyticsTracker>();
analyticsTracker.Initialize();
}
/// <summary>
/// 加载播放列表
/// </summary>
public async Task<bool> LoadPlaylist(VideoPlaylist playlist)
{
if (playlist == null || playlist.Videos.Count == 0)
{
Debug.LogError("Invalid playlist provided");
return false;
}
currentPlaylist = playlist;
currentVideoIndex = -1;
// 预加载播放列表中的视频
await PreloadPlaylistVideos(playlist);
// 记录分析事件
analyticsTracker.RecordPlaylistLoad(playlist.PlaylistId);
return true;
}
private async Task PreloadPlaylistVideos(VideoPlaylist playlist)
{
List<Task> preloadTasks = new List<Task>();
foreach (var video in playlist.Videos)
{
preloadTasks.Add(PreloadVideo(video));
}
await Task.WhenAll(preloadTasks);
Debug.Log($"Preloaded {preloadTasks.Count} videos from playlist");
}
private async Task PreloadVideo(VideoItem video)
{
// 检查是否已缓存
if (videoCache.ContainsKey(video.VideoId))
{
return;
}
// 根据来源类型预加载
switch (video.SourceType)
{
case VideoSource.LocalFile:
await PreloadLocalVideo(video);
break;
case VideoSource.StreamingURL:
await PreloadStreamingVideo(video);
break;
case VideoSource.EmbeddedResource:
await PreloadEmbeddedVideo(video);
break;
case VideoSource.AssetBundle:
await PreloadAssetBundleVideo(video);
break;
}
videoCache[video.VideoId] = video;
}
private async Task PreloadLocalVideo(VideoItem video)
{
// 验证文件存在
if (!System.IO.File.Exists(video.VideoPath))
{
throw new System.IO.FileNotFoundException($"Video file not found: {video.VideoPath}");
}
// 获取文件信息
var fileInfo = new System.IO.FileInfo(video.VideoPath);
video.Duration = await GetVideoDuration(video.VideoPath);
Debug.Log($"Preloaded local video: {video.VideoName}, Size: {FormatBytes(fileInfo.Length)}, Duration: {video.Duration:F1}s");
}
/// <summary>
/// 播放指定视频
/// </summary>
public async Task PlayVideo(string videoId)
{
if (!videoCache.ContainsKey(videoId))
{
Debug.LogError($"Video not found in cache: {videoId}");
return;
}
VideoItem video = videoCache[videoId];
// 停止当前播放
if (isPlaying)
{
StopPlayback();
}
currentVideo = video;
isBuffering = true;
// 准备视频
await PrepareVideoForPlayback(video);
// 开始播放
StartPlayback();
// 记录分析
analyticsTracker.RecordVideoStart(videoId, video.VideoName);
}
private async Task PrepareVideoForPlayback(VideoItem video)
{
// 配置视频源
ConfigureVideoSource(mainVideoPlayer, video);
// 准备视频
mainVideoPlayer.Prepare();
// 等待准备完成
await WaitForVideoPreparation();
// 加载章节和交互
LoadVideoChapters(video.Chapters);
// 更新显示
displayController.UpdateVideoInfo(video.VideoName, video.Duration);
isBuffering = false;
}
private void ConfigureVideoSource(VideoPlayer player, VideoItem video)
{
player.source = VideoSource.Url;
switch (video.SourceType)
{
case VideoSource.LocalFile:
player.url = "file://" + video.VideoPath;
break;
case VideoSource.StreamingURL:
player.url = video.VideoPath;
break;
case VideoSource.EmbeddedResource:
// 处理嵌入式资源
break;
case VideoSource.AssetBundle:
// 处理AssetBundle资源
break;
}
// 应用播放设置
ApplyPlaybackSettings(player, video.PlaybackSettings);
}
private void ApplyPlaybackSettings(VideoPlayer player, VideoPlaybackSettings settings)
{
if (settings == null) return;
player.playbackSpeed = settings.PlaybackSpeed;
player.isLooping = settings.Loop;
player.skipOnDrop = settings.SkipOnDrop;
if (settings.AudioVolume >= 0)
{
audioOutput.volume = settings.AudioVolume;
}
}
private void StartPlayback()
{
if (mainVideoPlayer == null || !mainVideoPlayer.isPrepared) return;
playbackStartTime = Time.time;
mainVideoPlayer.Play();
isPlaying = true;
// 开始播放进度监控
StartCoroutine(MonitorPlaybackProgress());
// 开始交互监控
StartCoroutine(MonitorVideoInteractions());
// 显示播放控制UI
UIManager.Instance.ShowVideoControls(true);
}
private System.Collections.IEnumerator MonitorPlaybackProgress()
{
while (isPlaying && mainVideoPlayer.isPlaying)
{
playbackProgress = (float)(mainVideoPlayer.time / mainVideoPlayer.length);
// 更新UI
displayController.UpdatePlaybackProgress(playbackProgress, mainVideoPlayer.time, mainVideoPlayer.length);
// 检查章节
CheckChapterProgress(mainVideoPlayer.time);
yield return new WaitForSeconds(0.1f);
}
}
private System.Collections.IEnumerator MonitorVideoInteractions()
{
while (isPlaying)
{
float currentTime = (float)mainVideoPlayer.time;
// 检查是否有到期的交互
CheckPendingInteractions(currentTime);
yield return new WaitForSeconds(0.05f);
}
}
private void CheckChapterProgress(double currentTime)
{
foreach (var chapter in activeChapters)
{
if (currentTime >= chapter.StartTime && currentTime <= chapter.EndTime)
{
// 触发章节激活
if (!chapter.IsActive)
{
OnChapterEntered(chapter);
}
}
else if (chapter.IsActive)
{
// 触发章节退出
OnChapterExited(chapter);
}
}
}
private void OnChapterEntered(VideoChapter chapter)
{
chapter.IsActive = true;
// 显示章节标题
displayController.ShowChapterTitle(chapter.Title, 3f);
// 激活章节交互
foreach (var interaction in chapter.Interactions)
{
if (interaction.TimePosition <= 0) // 立即触发
{
ExecuteInteraction(interaction);
}
else
{
pendingInteractions.Add(interaction);
}
}
// 记录分析
analyticsTracker.RecordChapterEntered(chapter.ChapterId, chapter.Title);
}
private void CheckPendingInteractions(float currentTime)
{
List<ChapterInteraction> toRemove = new List<ChapterInteraction>();
foreach (var interaction in pendingInteractions)
{
if (currentTime >= interaction.TimePosition)
{
ExecuteInteraction(interaction);
toRemove.Add(interaction);
}
}
foreach (var interaction in toRemove)
{
pendingInteractions.Remove(interaction);
}
}
private void ExecuteInteraction(ChapterInteraction interaction)
{
// 如果需要,暂停视频
if (interaction.PauseOnInteraction && isPlaying)
{
PausePlayback();
}
// 执行交互
switch (interaction.Type)
{
case InteractionType.ShowAnnotation:
ShowAnnotation(interaction.TargetObjectId, interaction.ActionData);
break;
case InteractionType.HighlightComponent:
HighlightComponent(interaction.TargetObjectId);
break;
case InteractionType.ChangeCamera:
ChangeCameraAngle(interaction.ActionData);
break;
case InteractionType.LoadModel:
LoadAdditionalModel(interaction.TargetObjectId);
break;
case InteractionType.PlayAudio:
PlayAdditionalAudio(interaction.ActionData);
break;
case InteractionType.ShowText:
ShowInfoText(interaction.ActionData);
break;
case InteractionType.OpenURL:
OpenExternalURL(interaction.ActionData);
break;
}
// 记录交互
analyticsTracker.RecordInteraction(
interaction.Type.ToString(),
currentVideo.VideoId,
(float)mainVideoPlayer.time
);
}
/// <summary>
/// 暂停播放
/// </summary>
public void PausePlayback()
{
if (!isPlaying) return;
mainVideoPlayer.Pause();
isPlaying = false;
displayController.OnPlaybackPaused();
analyticsTracker.RecordPlaybackPaused(currentVideo.VideoId, (float)mainVideoPlayer.time);
}
/// <summary>
/// 恢复播放
/// </summary>
public void ResumePlayback()
{
if (isPlaying) return;
mainVideoPlayer.Play();
isPlaying = true;
displayController.OnPlaybackResumed();
analyticsTracker.RecordPlaybackResumed(currentVideo.VideoId, (float)mainVideoPlayer.time);
}
/// <summary>
/// 跳转到指定时间
/// </summary>
public async Task SeekToTime(float timeInSeconds)
{
if (mainVideoPlayer == null || !mainVideoPlayer.canSetTime) return;
float targetTime = Mathf.Clamp(timeInSeconds, 0, (float)mainVideoPlayer.length - 0.1f);
// 记录跳转前的状态
analyticsTracker.RecordSeekStart(currentVideo.VideoId, (float)mainVideoPlayer.time, targetTime);
bool wasPlaying = isPlaying;
if (wasPlaying)
{
mainVideoPlayer.Pause();
}
// 执行跳转
mainVideoPlayer.time = targetTime;
// 等待跳转完成
await Task.Delay(100);
if (wasPlaying)
{
mainVideoPlayer.Play();
}
// 记录跳转完成
analyticsTracker.RecordSeekComplete(currentVideo.VideoId, targetTime);
}
/// <summary>
/// 启用画中画模式
/// </summary>
public void EnablePictureInPicture(VideoItem secondaryVideo)
{
if (secondaryVideoPlayer == null || secondaryVideo == null) return;
// 配置并启动辅助视频播放器
ConfigureVideoSource(secondaryVideoPlayer, secondaryVideo);
secondaryVideoPlayer.Prepare();
// 创建画中画显示
displayController.EnablePictureInPicture(true, secondaryVideoPlayer.targetTexture);
secondaryVideoPlayer.Play();
}
/// <summary>
/// 同步多个视频播放
/// </summary>
public async Task SyncMultipleVideos(List<SyncVideoItem> videos)
{
if (videos == null || videos.Count == 0) return;
// 准备所有视频
List<Task> preparationTasks = new List<Task>();
List<VideoPlayer> players = new List<VideoPlayer>();
foreach (var syncVideo in videos)
{
var player = CreateAdditionalVideoPlayer();
ConfigureVideoSource(player, syncVideo.Video);
players.Add(player);
preparationTasks.Add(PrepareVideoPlayer(player));
}
await Task.WhenAll(preparationTasks);
// 同步开始播放
foreach (var player in players)
{
player.Play();
}
// 启动同步监控
StartCoroutine(MonitorVideoSync(players));
}
private System.Collections.IEnumerator MonitorVideoSync(List<VideoPlayer> players)
{
while (players.Any(p => p.isPlaying))
{
// 检查同步状态
double masterTime = players[0].time;
double maxTimeDifference = 0.033; // 33ms
for (int i = 1; i < players.Count; i++)
{
double timeDiff = Math.Abs(players[i].time - masterTime);
if (timeDiff > maxTimeDifference)
{
// 重新同步
players[i].time = masterTime;
}
}
yield return new WaitForSeconds(0.1f);
}
}
private void OnVideoPrepared(VideoPlayer source)
{
Debug.Log($"Video prepared: {source.url}, Duration: {source.length:F2}s");
// 更新显示信息
displayController.OnVideoPrepared(source.length);
// 触发准备完成事件
OnVideoPreparationComplete?.Invoke(true);
}
private void OnVideoEnded(VideoPlayer source)
{
isPlaying = false;
// 记录播放完成
analyticsTracker.RecordVideoComplete(
currentVideo.VideoId,
(float)source.time,
Time.time - playbackStartTime
);
// 清理资源
CleanupAfterPlayback();
// 播放下一个视频(如果在播放列表中)
if (currentPlaylist != null)
{
PlayNextInPlaylist();
}
OnPlaybackEnded?.Invoke();
}
private void PlayNextInPlaylist()
{
if (currentPlaylist == null || currentPlaylist.Videos.Count == 0) return;
int nextIndex = GetNextVideoIndex();
if (nextIndex >= 0 && nextIndex < currentPlaylist.Videos.Count)
{
VideoItem nextVideo = currentPlaylist.Videos[nextIndex];
PlayVideo(nextVideo.VideoId);
}
}
private int GetNextVideoIndex()
{
if (currentVideoIndex < 0) return 0;
switch (currentPlaylist.PlayMode)
{
case PlayMode.Sequential:
int next = currentVideoIndex + 1;
return next < currentPlaylist.Videos.Count ? next :
(currentPlaylist.LoopPlaylist ? 0 : -1);
case PlayMode.Random:
return UnityEngine.Random.Range(0, currentPlaylist.Videos.Count);
case PlayMode.Shuffle:
// 实现洗牌算法
return GetNextShuffledIndex();
default:
return -1;
}
}
public event Action<bool> OnVideoPreparationComplete;
public event Action OnPlaybackEnded;
}
/// <summary>
/// 视频显示控制器
/// </summary>
public class VideoDisplayController : MonoBehaviour
{
private GameObject videoScreen;
private Renderer videoRenderer;
private Material videoMaterial;
private TextMesh titleText;
private TextMesh timeText;
private GameObject progressBar;
private GameObject controlPanel;
private VideoDisplayMode currentMode;
private Vector3 screenPosition;
private Quaternion screenRotation;
private Vector2 screenSize;
private bool controlsVisible = true;
private float controlsHideTimeout = 3f;
private float lastInteractionTime;
public void Initialize(RenderTexture videoTexture)
{
CreateVideoDisplay();
SetupVideoMaterial(videoTexture);
CreateUIElements();
SetupInteractions();
}
private void CreateVideoDisplay()
{
// 创建视频屏幕
videoScreen = GameObject.CreatePrimitive(PrimitiveType.Quad);
videoScreen.name = "VideoDisplayScreen";
videoScreen.transform.SetParent(transform);
videoRenderer = videoScreen.GetComponent<Renderer>();
videoRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
videoRenderer.receiveShadows = false;
// 添加碰撞体用于交互
BoxCollider collider = videoScreen.GetComponent<BoxCollider>();
collider.size = new Vector3(1, 1, 0.01f);
}
private void SetupVideoMaterial(RenderTexture videoTexture)
{
videoMaterial = new Material(Shader.Find("Unlit/Texture"));
videoMaterial.mainTexture = videoTexture;
videoRenderer.material = videoMaterial;
// 添加混合现实效果
AddMixedRealityEffects();
}
private void AddMixedRealityEffects()
{
// 添加环境反射
MaterialExtension.AddEnvironmentReflection(videoMaterial, 0.3f);
// 添加轻微透明度
videoMaterial.SetFloat("_Alpha", 0.95f);
// 添加边框效果
MaterialExtension.AddBorderEffect(videoMaterial, 0.02f, Color.black);
}
public void SetDisplayMode(VideoDisplayMode mode)
{
currentMode = mode;
switch (mode)
{
case VideoDisplayMode.FloatingScreen:
ConfigureFloatingScreen();
break;
case VideoDisplayMode.WorldLocked:
ConfigureWorldLocked();
break;
case VideoDisplayMode.HeadLocked:
ConfigureHeadLocked();
break;
case VideoDisplayMode.Spherical:
ConfigureSpherical();
break;
}
}
private void ConfigureFloatingScreen()
{
// 配置为漂浮屏幕
videoScreen.transform.position = screenPosition;
videoScreen.transform.rotation = screenRotation;
videoScreen.transform.localScale = new Vector3(screenSize.x, screenSize.y, 1);
// 添加轻微浮动动画
AddFloatingAnimation();
}
private void AddFloatingAnimation()
{
// 添加上下浮动效果
FloatingAnimation anim = videoScreen.AddComponent<FloatingAnimation>();
anim.amplitude = 0.01f;
anim.frequency = 0.5f;
}
public void UpdatePlaybackProgress(float progress, double currentTime, double totalTime)
{
if (progressBar != null)
{
// 更新进度条
UpdateProgressBar(progress);
}
if (timeText != null)
{
// 更新时间显示
timeText.text = $"{FormatTime(currentTime)} / {FormatTime(totalTime)}";
}
}
private string FormatTime(double seconds)
{
TimeSpan timeSpan = TimeSpan.FromSeconds(seconds);
if (timeSpan.Hours > 0)
{
return string.Format("{0:00}:{1:00}:{2:00}",
timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
}
else
{
return string.Format("{0:00}:{1:00}",
timeSpan.Minutes, timeSpan.Seconds);
}
}
}
public enum VideoDisplayMode
{
FloatingScreen,
WorldLocked,
HeadLocked,
Spherical
}
[System.Serializable]
public class VideoPlaybackSettings
{
public float PlaybackSpeed = 1.0f;
public bool Loop = false;
public bool SkipOnDrop = true;
public float AudioVolume = 1.0f;
public bool AutoPlay = true;
public bool Muted = false;
}
[System.Serializable]
public class SyncVideoItem
{
public VideoItem Video;
public float SyncOffset;
public bool IsMaster;
}
}
9.4 汽车动态模拟与物理交互系统
工业级汽车展示需要真实的动态模拟和物理交互。以下是汽车动态模拟系统的实现:
using System;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.MixedReality.Toolkit.Input;
namespace AutomotiveIndustryMR
{
/// <summary>
/// 汽车动态模拟控制器
/// 实现真实的汽车运动、物理反馈和交互式控制
/// </summary>
public class CarDynamicSimulator : MonoBehaviour
{
[System.Serializable]
public class CarPhysicsProfile
{
public string ProfileId;
public string CarModelId;
public float Mass; // 质量 (kg)
public Vector3 CenterOfMass; // 重心
public WheelPhysics[] Wheels;
public EngineSpecification Engine;
public TransmissionSpecification Transmission;
public BrakeSpecification Brakes;
public SuspensionSpecification Suspension;
public AerodynamicsSpecification Aerodynamics;
}
[System.Serializable]
public class WheelPhysics
{
public WheelPosition Position;
public float Mass;
public float Radius;
public float Width;
public float SuspensionTravel;
public float SpringRate;
public float DamperRate;
public float FrictionCoefficient;
public bool IsSteering;
public float MaxSteerAngle;
}
[System.Serializable]
public class EngineSpecification
{
public EngineType Type;
public float Displacement; // 排量 (L)
public int Cylinders;
public float MaxPower; // 最大功率 (kW)
public float MaxTorque; // 最大扭矩 (Nm)
public float[] PowerCurve; // 功率曲线
public float[] TorqueCurve; // 扭矩曲线
public float RedlineRPM;
public float IdleRPM;
}
[System.Serializable]
public class TransmissionSpecification
{
public TransmissionType Type;
public int Gears;
public float[] GearRatios;
public float FinalDriveRatio;
public float ShiftTime;
public bool HasReverse;
public float ReverseRatio;
}
private CarPhysicsProfile currentPhysicsProfile;
private Rigidbody carRigidbody;
private WheelCollider[] wheelColliders;
private GameObject[] wheelMeshes;
private float currentSpeed;
private float currentRPM;
private int currentGear = 1;
private bool isEngineRunning = false;
private float throttleInput = 0f;
private float brakeInput = 0f;
private float steerInput = 0f;
private float clutchInput = 0f;
private float engineTorque;
private float wheelTorque;
private float aerodynamicDrag;
private float rollingResistance;
private CarInputSystem inputSystem;
private CarAudioSystem audioSystem;
private CarEffectsSystem effectsSystem;
private CarTelemetrySystem telemetrySystem;
private Dictionary<WheelPosition, WheelData> wheelData;
private List<CarComponent> interactiveComponents;
private bool simulationActive = false;
private float simulationTime = 0f;
private int simulationFrame = 0;
private void Awake()
{
InitializePhysicsSystem();
SetupWheelSystem();
InitializeSubsystems();
StartTelemetryRecording();
}
private void InitializePhysicsSystem()
{
// 添加刚体组件
carRigidbody = gameObject.AddComponent<Rigidbody>();
carRigidbody.mass = 1500f; // 默认质量
carRigidbody.drag = 0.1f;
carRigidbody.angularDrag = 0.5f;
carRigidbody.useGravity = true;
carRigidbody.isKinematic = false;
carRigidbody.interpolation = RigidbodyInterpolation.Interpolate;
carRigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
}
public void LoadPhysicsProfile(CarPhysicsProfile profile)
{
if (profile == null)
{
Debug.LogError("Physics profile is null");
return;
}
currentPhysicsProfile = profile;
// 应用物理配置
ApplyPhysicsProfile(profile);
// 初始化轮子数据
InitializeWheelData();
// 配置子系统
ConfigureSubsystemsForProfile(profile);
Debug.Log($"Loaded physics profile: {profile.ProfileId} for {profile.CarModelId}");
}
private void ApplyPhysicsProfile(CarPhysicsProfile profile)
{
// 设置质量
carRigidbody.mass = profile.Mass;
// 设置重心
carRigidbody.centerOfMass = profile.CenterOfMass;
// 创建轮子碰撞体
CreateWheelColliders(profile.Wheels);
// 配置空气动力学
ConfigureAerodynamics(profile.Aerodynamics);
}
private void CreateWheelColliders(WheelPhysics[] wheels)
{
wheelColliders = new WheelCollider[wheels.Length];
wheelMeshes = new GameObject[wheels.Length];
wheelData = new Dictionary<WheelPosition, WheelData>();
for (int i = 0; i < wheels.Length; i++)
{
WheelPhysics wheelPhysics = wheels[i];
// 创建轮子碰撞体
GameObject wheelColliderObj = new GameObject($"WheelCollider_{wheelPhysics.Position}");
wheelColliderObj.transform.SetParent(transform);
// 根据位置设置轮子位置
Vector3 wheelPosition = CalculateWheelPosition(wheelPhysics.Position);
wheelColliderObj.transform.localPosition = wheelPosition;
WheelCollider wheelCollider = wheelColliderObj.AddComponent<WheelCollider>();
ConfigureWheelCollider(wheelCollider, wheelPhysics);
wheelColliders[i] = wheelCollider;
// 创建轮子网格
CreateWheelMesh(wheelPhysics, i);
// 初始化轮子数据
wheelData[wheelPhysics.Position] = new WheelData
{
Physics = wheelPhysics,
Collider = wheelCollider,
Mesh = wheelMeshes[i],
Position = wheelPhysics.Position
};
}
}
private Vector3 CalculateWheelPosition(WheelPosition position)
{
// 根据汽车尺寸计算轮子位置
Bounds carBounds = GetComponent<Renderer>().bounds;
float wheelBase = carBounds.size.z * 0.8f;
float trackWidth = carBounds.size.x * 0.9f;
float xPos = 0f;
float zPos = 0f;
switch (position)
{
case WheelPosition.FrontLeft:
xPos = -trackWidth / 2;
zPos = wheelBase / 2;
break;
case WheelPosition.FrontRight:
xPos = trackWidth / 2;
zPos = wheelBase / 2;
break;
case WheelPosition.RearLeft:
xPos = -trackWidth / 2;
zPos = -wheelBase / 2;
break;
case WheelPosition.RearRight:
xPos = trackWidth / 2;
zPos = -wheelBase / 2;
break;
}
return new Vector3(xPos, 0, zPos);
}
private void ConfigureWheelCollider(WheelCollider collider, WheelPhysics physics)
{
collider.mass = physics.Mass;
collider.radius = physics.Radius;
collider.wheelDampingRate = physics.DamperRate;
collider.suspensionDistance = physics.SuspensionTravel;
// 配置悬架弹簧
JointSpring suspensionSpring = new JointSpring
{
spring = physics.SpringRate,
damper = physics.DamperRate,
targetPosition = 0.5f
};
collider.suspensionSpring = suspensionSpring;
// 配置轮胎摩擦
WheelFrictionCurve forwardFriction = new WheelFrictionCurve
{
extremumSlip = 0.4f,
extremumValue = 1f,
asymptoteSlip = 0.8f,
asymptoteValue = 0.5f,
stiffness = physics.FrictionCoefficient
};
WheelFrictionCurve sidewaysFriction = new WheelFrictionCurve
{
extremumSlip = 0.2f,
extremumValue = 1f,
asymptoteSlip = 0.5f,
asymptoteValue = 0.75f,
stiffness = physics.FrictionCoefficient
};
collider.forwardFriction = forwardFriction;
collider.sidewaysFriction = sidewaysFriction;
}
private void CreateWheelMesh(WheelPhysics physics, int index)
{
GameObject wheelMesh = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
wheelMesh.name = $"WheelMesh_{physics.Position}";
wheelMesh.transform.SetParent(wheelColliders[index].transform);
wheelMesh.transform.localPosition = Vector3.zero;
wheelMesh.transform.localRotation = Quaternion.Euler(0, 0, 90);
wheelMesh.transform.localScale = new Vector3(
physics.Radius * 2,
physics.Width / 2,
physics.Radius * 2
);
// 配置轮子材质
Renderer renderer = wheelMesh.GetComponent<Renderer>();
renderer.material = Resources.Load<Material>("Materials/Car/Wheel");
wheelMeshes[index] = wheelMesh;
}
private void InitializeSubsystems()
{
// 初始化输入系统
inputSystem = gameObject.AddComponent<CarInputSystem>();
inputSystem.Initialize(this);
// 初始化音频系统
audioSystem = gameObject.AddComponent<CarAudioSystem>();
audioSystem.Initialize(this);
// 初始化特效系统
effectsSystem = gameObject.AddComponent<CarEffectsSystem>();
effectsSystem.Initialize(this);
// 初始化遥测系统
telemetrySystem = gameObject.AddComponent<CarTelemetrySystem>();
telemetrySystem.Initialize(this);
}
private void Update()
{
if (!simulationActive) return;
float deltaTime = Time.deltaTime;
simulationTime += deltaTime;
simulationFrame++;
// 处理输入
ProcessInputs();
// 更新物理模拟
UpdatePhysics(deltaTime);
// 更新子系统
UpdateSubsystems(deltaTime);
// 更新轮子视觉
UpdateWheelVisuals();
// 记录遥测数据
telemetrySystem.RecordFrame(this);
}
private void ProcessInputs()
{
// 从输入系统获取输入
throttleInput = inputSystem.GetThrottleInput();
brakeInput = inputSystem.GetBrakeInput();
steerInput = inputSystem.GetSteerInput();
clutchInput = inputSystem.GetClutchInput();
// 处理换挡
if (inputSystem.GetShiftUp())
{
ShiftGear(1);
}
else if (inputSystem.GetShiftDown())
{
ShiftGear(-1);
}
// 处理引擎启动/停止
if (inputSystem.GetEngineToggle())
{
ToggleEngine();
}
}
private void UpdatePhysics(float deltaTime)
{
// 计算当前速度
currentSpeed = carRigidbody.velocity.magnitude * 3.6f; // 转换为km/h
// 计算引擎RPM
CalculateEngineRPM();
// 计算引擎扭矩
CalculateEngineTorque();
// 计算传动系统扭矩
CalculateTransmissionTorque();
// 应用扭矩到轮子
ApplyWheelTorque();
// 应用转向
ApplySteering();
// 应用刹车
ApplyBraking();
// 计算阻力
CalculateResistances();
// 更新空气动力学
UpdateAerodynamics();
}
private void CalculateEngineRPM()
{
if (!isEngineRunning)
{
currentRPM = 0;
return;
}
// 基于速度和档位计算RPM
float wheelRPM = GetAverageWheelRPM();
float gearRatio = currentGear > 0 ?
currentPhysicsProfile.Transmission.GearRatios[currentGear - 1] :
currentPhysicsProfile.Transmission.ReverseRatio;
float driveRatio = gearRatio * currentPhysicsProfile.Transmission.FinalDriveRatio;
currentRPM = wheelRPM * driveRatio;
// 限制RPM范围
currentRPM = Mathf.Clamp(
currentRPM,
currentPhysicsProfile.Engine.IdleRPM,
currentPhysicsProfile.Engine.RedlineRPM
);
// 怠速控制
if (throttleInput == 0 && currentRPM < currentPhysicsProfile.Engine.IdleRPM)
{
currentRPM = currentPhysicsProfile.Engine.IdleRPM;
}
}
private void CalculateEngineTorque()
{
if (!isEngineRunning)
{
engineTorque = 0;
return;
}
// 从扭矩曲线获取扭矩
float rpmNormalized = (currentRPM - currentPhysicsProfile.Engine.IdleRPM) /
(currentPhysicsProfile.Engine.RedlineRPM - currentPhysicsProfile.Engine.IdleRPM);
int curveIndex = Mathf.FloorToInt(rpmNormalized * (currentPhysicsProfile.Engine.TorqueCurve.Length - 1));
curveIndex = Mathf.Clamp(curveIndex, 0, currentPhysicsProfile.Engine.TorqueCurve.Length - 1);
engineTorque = currentPhysicsProfile.Engine.TorqueCurve[curveIndex];
// 应用油门输入
engineTorque *= throttleInput;
// 应用离合器打滑
if (clutchInput < 1f)
{
engineTorque *= clutchInput;
}
}
private void CalculateTransmissionTorque()
{
if (currentGear == 0) // 空档
{
wheelTorque = 0;
return;
}
float gearRatio = currentGear > 0 ?
currentPhysicsProfile.Transmission.GearRatios[currentGear - 1] :
currentPhysicsProfile.Transmission.ReverseRatio;
float totalRatio = gearRatio * currentPhysicsProfile.Transmission.FinalDriveRatio;
float transmissionEfficiency = 0.85f; // 传动效率
wheelTorque = engineTorque * totalRatio * transmissionEfficiency;
}
private void ApplyWheelTorque()
{
// 根据驱动类型分配扭矩
switch (currentPhysicsProfile.Transmission.Type)
{
case TransmissionType.FWD: // 前驱
ApplyTorqueToWheels(new[] { WheelPosition.FrontLeft, WheelPosition.FrontRight });
break;
case TransmissionType.RWD: // 后驱
ApplyTorqueToWheels(new[] { WheelPosition.RearLeft, WheelPosition.RearRight });
break;
case TransmissionType.AWD: // 全驱
ApplyTorqueToAllWheels();
break;
case TransmissionType.FourWD: // 四驱
ApplyTorqueToAllWheelsWithCenterDiff();
break;
}
}
private void ApplyTorqueToWheels(WheelPosition[] positions)
{
float torquePerWheel = wheelTorque / positions.Length;
foreach (var position in positions)
{
if (wheelData.ContainsKey(position))
{
WheelData data = wheelData[position];
data.Collider.motorTorque = torquePerWheel;
}
}
}
private void ApplySteering()
{
foreach (var kvp in wheelData)
{
WheelData data = kvp.Value;
if (data.Physics.IsSteering)
{
float maxSteerAngle = data.Physics.MaxSteerAngle;
// 根据速度调整转向角度(高速时减少转向)
float speedFactor = Mathf.Clamp01(1.0f - currentSpeed / 100f);
float actualSteerAngle = steerInput * maxSteerAngle * speedFactor;
data.Collider.steerAngle = actualSteerAngle;
}
}
}
private void ApplyBraking()
{
float totalBrakeTorque = brakeInput * currentPhysicsProfile.Brakes.MaxTorque;
float brakePerWheel = totalBrakeTorque / wheelData.Count;
foreach (var kvp in wheelData)
{
kvp.Value.Collider.brakeTorque = brakePerWheel;
}
}
private void CalculateResistances()
{
// 计算滚动阻力
rollingResistance = CalculateRollingResistance();
// 计算空气阻力
aerodynamicDrag = CalculateAerodynamicDrag();
// 应用阻力
Vector3 resistanceForce = -carRigidbody.velocity.normalized * (rollingResistance + aerodynamicDrag);
carRigidbody.AddForce(resistanceForce, ForceMode.Force);
}
private float CalculateRollingResistance()
{
// 滚动阻力 = 摩擦系数 * 质量 * 重力加速度
float rollingCoefficient = 0.015f; // 典型轮胎滚动阻力系数
return rollingCoefficient * currentPhysicsProfile.Mass * 9.81f;
}
private float CalculateAerodynamicDrag()
{
// 空气阻力 = 0.5 * 空气密度 * 阻力系数 * 迎风面积 * 速度²
float airDensity = 1.225f; // kg/m³ (海平面)
float dragCoefficient = currentPhysicsProfile.Aerodynamics.DragCoefficient;
float frontalArea = currentPhysicsProfile.Aerodynamics.FrontalArea;
float velocitySquared = carRigidbody.velocity.sqrMagnitude;
return 0.5f * airDensity * dragCoefficient * frontalArea * velocitySquared;
}
private void UpdateWheelVisuals()
{
foreach (var kvp in wheelData)
{
WheelData data = kvp.Value;
// 更新轮子位置
data.Collider.GetWorldPose(out Vector3 position, out Quaternion rotation);
if (data.Mesh != null)
{
data.Mesh.transform.position = position;
data.Mesh.transform.rotation = rotation;
// 根据转向调整轮子方向
if (data.Physics.IsSteering)
{
data.Mesh.transform.Rotate(Vector3.up, data.Collider.steerAngle);
}
}
}
}
/// <summary>
/// 启动引擎
/// </summary>
public void StartEngine()
{
if (isEngineRunning) return;
isEngineRunning = true;
currentRPM = currentPhysicsProfile.Engine.IdleRPM;
// 播放引擎启动音效
audioSystem.PlayEngineStart();
// 显示启动特效
effectsSystem.PlayEngineStartEffect();
Debug.Log("Engine started");
}
/// <summary>
/// 关闭引擎
/// </summary>
public void StopEngine()
{
if (!isEngineRunning) return;
isEngineRunning = false;
currentRPM = 0;
// 播放引擎关闭音效
audioSystem.PlayEngineStop();
// 停止所有扭矩输出
foreach (var wheelCollider in wheelColliders)
{
wheelCollider.motorTorque = 0;
}
Debug.Log("Engine stopped");
}
/// <summary>
/// 切换引擎状态
/// </summary>
public void ToggleEngine()
{
if (isEngineRunning)
{
StopEngine();
}
else
{
StartEngine();
}
}
/// <summary>
/// 换挡
/// </summary>
public void ShiftGear(int direction)
{
int newGear = currentGear + direction;
// 检查档位范围
if (newGear >= -1 && newGear <= currentPhysicsProfile.Transmission.Gears)
{
// 检查是否有倒档
if (newGear == -1 && !currentPhysicsProfile.Transmission.HasReverse)
{
return;
}
// 执行换挡
PerformGearShift(newGear);
}
}
private void PerformGearShift(int newGear)
{
int oldGear = currentGear;
currentGear = newGear;
// 播放换挡音效
audioSystem.PlayGearShift(oldGear, newGear);
// 记录换挡事件
telemetrySystem.RecordGearShift(oldGear, newGear, currentSpeed, currentRPM);
Debug.Log($"Shifted from gear {oldGear} to {newGear}");
}
/// <summary>
/// 启用模拟
/// </summary>
public void EnableSimulation(bool enable)
{
simulationActive = enable;
if (enable)
{
// 重置模拟状态
simulationTime = 0f;
simulationFrame = 0;
// 开始遥测记录
telemetrySystem.StartRecording();
Debug.Log("Car simulation enabled");
}
else
{
// 停止遥测记录
telemetrySystem.StopRecording();
Debug.Log("Car simulation disabled");
}
}
/// <summary>
/// 获取汽车遥测数据
/// </summary>
public CarTelemetry GetTelemetryData()
{
return new CarTelemetry
{
Timestamp = DateTime.Now,
Speed = currentSpeed,
RPM = currentRPM,
Gear = currentGear,
Throttle = throttleInput,
Brake = brakeInput,
Steer = steerInput,
EngineRunning = isEngineRunning,
Position = transform.position,
Rotation = transform.rotation,
Velocity = carRigidbody.velocity,
WheelData = GetWheelTelemetry()
};
}
private Dictionary<WheelPosition, WheelTelemetry> GetWheelTelemetry()
{
Dictionary<WheelPosition, WheelTelemetry> telemetry = new Dictionary<WheelPosition, WheelTelemetry>();
foreach (var kvp in wheelData)
{
WheelCollider collider = kvp.Value.Collider;
telemetry[kvp.Key] = new WheelTelemetry
{
RPM = collider.rpm,
Torque = collider.motorTorque,
BrakeTorque = collider.brakeTorque,
SteerAngle = collider.steerAngle,
IsGrounded = collider.isGrounded,
SuspensionForce = GetSuspensionForce(collider)
};
}
return telemetry;
}
private void StartTelemetryRecording()
{
// 开始记录遥测数据
InvokeRepeating("RecordTelemetrySnapshot", 0f, 0.1f); // 每秒10次
}
private void RecordTelemetrySnapshot()
{
if (!simulationActive) return;
telemetrySystem.RecordSnapshot(GetTelemetryData());
}
/// <summary>
/// 执行性能测试
/// </summary>
public PerformanceTestResult PerformAccelerationTest()
{
if (!simulationActive)
{
EnableSimulation(true);
}
// 重置汽车位置
transform.position = Vector3.zero;
transform.rotation = Quaternion.identity;
carRigidbody.velocity = Vector3.zero;
carRigidbody.angularVelocity = Vector3.zero;
// 确保引擎运行
if (!isEngineRunning)
{
StartEngine();
}
// 设置到一档
currentGear = 1;
// 开始加速测试
return StartAccelerationTestCoroutine();
}
private PerformanceTestResult StartAccelerationTestCoroutine()
{
PerformanceTestResult result = new PerformanceTestResult
{
TestStartTime = DateTime.Now,
CarModelId = currentPhysicsProfile.CarModelId,
TestType = PerformanceTestType.ZeroToHundred
};
StartCoroutine(RunAccelerationTest(result));
return result;
}
private System.Collections.IEnumerator RunAccelerationTest(PerformanceTestResult result)
{
List<AccelerationPoint> accelerationPoints = new List<AccelerationPoint>();
// 全油门
throttleInput = 1.0f;
brakeInput = 0.0f;
float startTime = Time.time;
float targetSpeed = 100f; // 100 km/h
while (currentSpeed < targetSpeed)
{
// 记录数据点
accelerationPoints.Add(new AccelerationPoint
{
Time = Time.time - startTime,
Speed = currentSpeed,
RPM = currentRPM,
Gear = currentGear
});
// 检查换挡点
if (currentRPM >= currentPhysicsProfile.Engine.RedlineRPM * 0.9f)
{
ShiftGear(1);
}
yield return new WaitForSeconds(0.01f); // 100Hz采样
}
// 记录结果
result.TestEndTime = DateTime.Now;
result.AccelerationData = accelerationPoints.ToArray();
result.ZeroToHundredTime = Time.time - startTime;
result.MaxSpeedReached = currentSpeed;
// 重置输入
throttleInput = 0f;
// 触发测试完成事件
OnPerformanceTestComplete?.Invoke(result);
// 记录分析
AnalyticsService.RecordPerformanceTest(result);
}
public event Action<PerformanceTestResult> OnPerformanceTestComplete;
}
/// <summary>
/// 轮子数据
/// </summary>
public class WheelData
{
public WheelPhysics Physics;
public WheelCollider Collider;
public GameObject Mesh;
public WheelPosition Position;
public float CurrentRPM;
public float CurrentTorque;
public bool IsLocked;
}
/// <summary>
/// 汽车遥测数据
/// </summary>
[System.Serializable]
public class CarTelemetry
{
public DateTime Timestamp;
public float Speed; // km/h
public float RPM;
public int Gear;
public float Throttle;
public float Brake;
public float Steer;
public bool EngineRunning;
public Vector3 Position;
public Quaternion Rotation;
public Vector3 Velocity;
public Dictionary<WheelPosition, WheelTelemetry> WheelData;
public float EngineTemperature;
public float OilPressure;
public float FuelLevel;
}
/// <summary>
/// 轮子遥测数据
/// </summary>
[System.Serializable]
public class WheelTelemetry
{
public float RPM;
public float Torque;
public float BrakeTorque;
public float SteerAngle;
public bool IsGrounded;
public float SuspensionForce;
public float SlipRatio;
public float SlipAngle;
}
/// <summary>
/// 性能测试结果
/// </summary>
[System.Serializable]
public class PerformanceTestResult
{
public DateTime TestStartTime;
public DateTime TestEndTime;
public string CarModelId;
public PerformanceTestType TestType;
public float ZeroToHundredTime; // 0-100km/h加速时间
public float QuarterMileTime; // 1/4英里加速时间
public float MaxSpeedReached;
public float BrakingDistance; // 100-0km/h制动距离
public AccelerationPoint[] AccelerationData;
public Dictionary<string, object> AdditionalMetrics;
}
[System.Serializable]
public class AccelerationPoint
{
public float Time;
public float Speed;
public float RPM;
public int Gear;
}
public enum PerformanceTestType
{
ZeroToHundred,
QuarterMile,
BrakingTest,
SlalomTest,
SkidpadTest,
TopSpeed
}
public enum WheelPosition
{
FrontLeft,
FrontRight,
RearLeft,
RearRight
}
public enum EngineType
{
Gasoline,
Diesel,
Electric,
Hybrid,
Hydrogen
}
public enum TransmissionType
{
Manual,
Automatic,
CVT,
DCT,
FWD, // 前驱
RWD, // 后驱
AWD, // 全驱
FourWD // 四驱
}
[System.Serializable]
public class BrakeSpecification
{
public BrakeType Type;
public float MaxTorque; // 最大制动力矩 (Nm)
public float DiscDiameter; // 刹车盘直径 (mm)
public bool HasABS;
public bool HasESC;
public float BrakeBias; // 制动力分配 (前/后)
}
public enum BrakeType
{
Disc,
Drum,
Ceramic,
CarbonCeramic
}
[System.Serializable]
public class SuspensionSpecification
{
public SuspensionType Type;
public float SpringRate;
public float DamperRate;
public float AntiRollBarStiffness;
public float RideHeight;
public bool HasAdaptiveDamping;
public bool HasAirSuspension;
}
public enum SuspensionType
{
McPherson,
DoubleWishbone,
MultiLink,
TrailingArm,
SolidAxle
}
[System.Serializable]
public class AerodynamicsSpecification
{
public float DragCoefficient;
public float FrontalArea; // 迎风面积 (m²)
public float LiftCoefficient;
public bool HasActiveAero;
public float DownforceAtSpeed; // 在特定速度下的下压力
public float ReferenceSpeed; // 参考速度 (km/h)
}
}
以上内容详细介绍了工业级汽车混合现实展示系统的深度开发,涵盖了系统架构设计、资源管理、多媒体播放和动态模拟等关键领域。每个部分都提供了详细的Unity C#实现代码,并针对工业应用场景进行了优化。这些代码可以在Unity 2021.3.8f1c1和VS2022/VSCode中正确运行。
这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!
更多推荐

所有评论(0)