diff --git a/Assets/Resources/Configs/FractureFixation_Steps_Reference.json b/Assets/Resources/Configs/FractureFixation_Steps_Reference.json new file mode 100644 index 0000000..8b8e84c --- /dev/null +++ b/Assets/Resources/Configs/FractureFixation_Steps_Reference.json @@ -0,0 +1,148 @@ +[ + { + "StepIndex": 0, + "StepName": "OpeningScene", + "ScoreWeight": 5, + "SubStepCount": 1, + "TipText": "Tip:观察周围环境是否有危险,做好防护措施。", + "DialogueText": "周围环境安全。我是救护志愿者,我可以帮助您吗?", + "DetailedContent": "", + "CompleteType": "ClickAnywhere", + "TargetTag": "", + "ExamQuestionText": "点击确认后,请按正确顺序进行环境评估与自我介绍", + "ExamOptions": [], + "CorrectOptionIndex": -1, + "ErrorFeedback": "环境评估流程不正确", + "IsSplintOnly": false, + "ShowPipVideo": false, + "VideoFileName": "" + }, + { + "StepIndex": 1, + "StepName": "CheckInjury", + "ScoreWeight": 20, + "SubStepCount": 5, + "TipText": "Tip:请检查伤员受伤位置", + "DialogueText": "伤员左大腿疑似骨折,无伤口。", + "DetailedContent": "【伤情检查详细操作须知】\n1)通过视觉检查骨折部位是否存在明显肿胀、变形或异常角度,初步判断骨折类型(闭合性/开放性)及移位程度。\n2)观察骨折部位肿胀与畸形,询问患者疼痛程度并测试患肢主动/被动活动能力,确认是否存在神经血管损伤或肌肉撕裂等并发症。\n3)评估疼痛与功能障碍,重点排查开放性骨折的伤口污染情况,记录创面大小、深度及出血量。\n4)监测生命体征,包括血压、脉搏频率及节律,关注是否出现休克前兆。\n5)神经系统状态评估,排除颅脑损伤或脊髓损伤可能。", + "CompleteType": "SequenceClick", + "TargetTag": "InjuryLeg", + "ClickSequenceTags": ["InjuryVisual", "InjurySwelling", "InjuryPain", "InjuryVitalSigns", "InjuryNeuro"], + "ExamQuestionText": "点击确认后,请按正确顺序检查伤情", + "ExamOptions": [], + "CorrectOptionIndex": -1, + "ErrorFeedback": "伤情检查步骤不完整或不正确", + "IsSplintOnly": false, + "ShowPipVideo": false, + "VideoFileName": "" + }, + { + "StepIndex": 2, + "StepName": "RemoveShoes", + "ScoreWeight": 10, + "SubStepCount": 1, + "TipText": "Tip:请将伤员鞋袜脱掉并保存好,观察末端血液循环、运动及感觉", + "DialogueText": "", + "DetailedContent": "", + "CompleteType": "ClickObject", + "TargetTag": "Shoe", + "ExamQuestionText": "请正确操作脱鞋袜并观察末梢循环", + "ExamOptions": [], + "CorrectOptionIndex": -1, + "ErrorFeedback": "脱鞋袜操作不正确", + "IsSplintOnly": false, + "ShowPipVideo": false, + "VideoFileName": "" + }, + { + "StepIndex": 3, + "StepName": "PlaceBandage", + "ScoreWeight": 10, + "SubStepCount": 1, + "TipText": "Tip:请在正确的位置穿宽度约10cm的带子", + "DialogueText": "", + "DetailedContent": "", + "CompleteType": "MultipleChoice", + "TargetTag": "BandagePos1", + "ExamQuestionText": "请选择合适的带子宽度", + "ExamOptions": ["5cm", "10cm", "20cm"], + "CorrectOptionIndex": 1, + "ErrorFeedback": "应选择10cm宽度的带子", + "IsSplintOnly": false, + "ShowPipVideo": true, + "VideoFileName": "bandage_demo.mp4" + }, + { + "StepIndex": 4, + "StepName": "LimbFixation", + "ScoreWeight": 10, + "SubStepCount": 1, + "TipText": "Tip:请在两下肢间加衬垫,移动健肢,将双下肢轻轻并拢", + "DialogueText": "", + "DetailedContent": "", + "CompleteType": "DragToTarget", + "TargetTag": "PaddingPos", + "ExamQuestionText": "请选择正确的放带子顺序", + "ExamOptions": [], + "CorrectOptionIndex": -1, + "ErrorFeedback": "放带子顺序不正确", + "IsSplintOnly": false, + "ShowPipVideo": false, + "VideoFileName": "" + }, + { + "StepIndex": 5, + "StepName": "KneeFixation", + "ScoreWeight": 20, + "SubStepCount": 3, + "TipText": "Tip:请在未受伤腿分别固定骨折近心端(上端)、骨折远心端(下端),然后固定膝关节下,结打在右侧。", + "DialogueText": "", + "DetailedContent": "", + "CompleteType": "SequenceClick", + "TargetTag": "", + "ClickSequenceTags": ["BandagePos1", "BandagePos2", "BandagePos3"], + "ExamQuestionText": "请选择正确的系带子顺序", + "ExamOptions": [], + "CorrectOptionIndex": -1, + "ErrorFeedback": "系带子顺序不正确,应是近心端→远心端→膝关节下", + "IsSplintOnly": false, + "ShowPipVideo": false, + "VideoFileName": "" + }, + { + "StepIndex": 6, + "StepName": "AnkleFixation", + "ScoreWeight": 15, + "SubStepCount": 1, + "TipText": "Tip:踝关节\"8\"字固定,结打在两足背之间。", + "DialogueText": "", + "DetailedContent": "", + "CompleteType": "ClickObject", + "TargetTag": "Ankle", + "ExamQuestionText": "请选择踝关节打结位置", + "ExamOptions": ["足底", "足背"], + "CorrectOptionIndex": 1, + "ErrorFeedback": "应选择足背位置打结——踝关节8字固定结打在两足背之间", + "IsSplintOnly": false, + "ShowPipVideo": false, + "VideoFileName": "" + }, + { + "StepIndex": 7, + "StepName": "FinalCheck", + "ScoreWeight": 10, + "SubStepCount": 1, + "TipText": "Tip:包扎完毕请检查伤肢血液循环,运动及感觉。", + "DialogueText": "伤员一切正常,包扎完毕。", + "DetailedContent": "", + "CompleteType": "ClickAnywhere", + "TargetTag": "", + "ExamQuestionText": "包扎完毕,请确认并结束实验", + "ExamOptions": [], + "CorrectOptionIndex": -1, + "ErrorFeedback": "", + "IsSplintOnly": false, + "ShowPipVideo": false, + "VideoFileName": "" + } +] diff --git a/Assets/Screenshots/screenshot-20260624-023824.png b/Assets/Screenshots/screenshot-20260624-023824.png new file mode 100644 index 0000000..66ca15a Binary files /dev/null and b/Assets/Screenshots/screenshot-20260624-023824.png differ diff --git a/Assets/Screenshots/screenshot-20260624-023824.png.meta b/Assets/Screenshots/screenshot-20260624-023824.png.meta new file mode 100644 index 0000000..fcf258d --- /dev/null +++ b/Assets/Screenshots/screenshot-20260624-023824.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: d1013e7d1619745f1bbc6b01e0b14ff0 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/screenshot-20260624-023936.png b/Assets/Screenshots/screenshot-20260624-023936.png new file mode 100644 index 0000000..2f7f265 Binary files /dev/null and b/Assets/Screenshots/screenshot-20260624-023936.png differ diff --git a/Assets/Screenshots/screenshot-20260624-023936.png.meta b/Assets/Screenshots/screenshot-20260624-023936.png.meta new file mode 100644 index 0000000..c018508 --- /dev/null +++ b/Assets/Screenshots/screenshot-20260624-023936.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: eb6b9f24d34914fe4ac10313a5df574b +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/screenshot-20260624-023948.png b/Assets/Screenshots/screenshot-20260624-023948.png new file mode 100644 index 0000000..9efe0b1 Binary files /dev/null and b/Assets/Screenshots/screenshot-20260624-023948.png differ diff --git a/Assets/Screenshots/screenshot-20260624-023948.png.meta b/Assets/Screenshots/screenshot-20260624-023948.png.meta new file mode 100644 index 0000000..0d581f2 --- /dev/null +++ b/Assets/Screenshots/screenshot-20260624-023948.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 40ab091ada7864ef48389733dcb7d545 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Screenshots/screenshot-20260624-024432.png b/Assets/Screenshots/screenshot-20260624-024432.png new file mode 100644 index 0000000..00b4547 Binary files /dev/null and b/Assets/Screenshots/screenshot-20260624-024432.png differ diff --git a/Assets/Screenshots/screenshot-20260624-024432.png.meta b/Assets/Screenshots/screenshot-20260624-024432.png.meta new file mode 100644 index 0000000..2cba4fb --- /dev/null +++ b/Assets/Screenshots/screenshot-20260624-024432.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 203f21e7a38904983b9f36010c046399 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/MainMenu.cs b/Assets/Scripts/UI/MainMenu.cs new file mode 100644 index 0000000..38f4308 --- /dev/null +++ b/Assets/Scripts/UI/MainMenu.cs @@ -0,0 +1,117 @@ +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +/// +/// 主菜单:实验类型选择 + 模式选择 + 固定方式选择。 +/// 挂在主菜单场景的 Canvas 上。 +/// +public class MainMenu : MonoBehaviour +{ + [Header("主标题")] + [SerializeField] private TMP_Text _titleText; + + [Header("实验选择按钮 - 底部图标")] + [SerializeField] private Button _btnCPR; + [SerializeField] private Button _btnFracture; + [SerializeField] private Button _btnSprain; + [SerializeField] private Button _btnBleeding; + [SerializeField] private Button _btnTransport; + + [Header("模式选择")] + [SerializeField] private TMP_Text _tipText; + [SerializeField] private Button _btnLearnMode; + [SerializeField] private Button _btnExamMode; + [SerializeField] private TMP_Text _selectedModeLabel; + + [Header("固定方式选择(仅骨折固定)")] + [SerializeField] private GameObject _fixationPanel; + [SerializeField] private Button _btnLimbFixation; + [SerializeField] private Button _btnSplintFixation; + [SerializeField] private TMP_Text _selectedFixationLabel; + + [Header("开始按钮")] + [SerializeField] private Button _btnStart; + + [Header("提示")] + [SerializeField] private TMP_Text _selectionTip; + + // 当前选择 + private ExperimentType _selectedType = ExperimentType.FractureFixation; + private ExperimentMode _selectedMode = ExperimentMode.Learn; + private FixationMethod _selectedFixation = FixationMethod.LimbFixation; + + private void Start() + { + BindExperimentButtons(); + _btnLearnMode?.onClick.AddListener(() => SelectMode(ExperimentMode.Learn)); + _btnExamMode?.onClick.AddListener(() => SelectMode(ExperimentMode.Exam)); + _btnLimbFixation?.onClick.AddListener(() => SelectFixation(FixationMethod.LimbFixation)); + _btnSplintFixation?.onClick.AddListener(() => SelectFixation(FixationMethod.SplintFixation)); + _btnStart?.onClick.AddListener(StartExperiment); + + // 默认选中骨折固定 + SelectExperiment(ExperimentType.FractureFixation); + SelectMode(ExperimentMode.Learn); + SelectFixation(FixationMethod.LimbFixation); + } + + private void BindExperimentButtons() + { + _btnCPR?.onClick.AddListener(() => SelectExperiment(ExperimentType.CPR)); + _btnFracture?.onClick.AddListener(() => SelectExperiment(ExperimentType.FractureFixation)); + _btnSprain?.onClick.AddListener(() => SelectExperiment(ExperimentType.SprainBandaging)); + _btnBleeding?.onClick.AddListener(() => SelectExperiment(ExperimentType.BleedingBandaging)); + _btnTransport?.onClick.AddListener(() => SelectExperiment(ExperimentType.CasualtyTransport)); + } + + private void SelectExperiment(ExperimentType type) + { + _selectedType = type; + // 骨折固定显示固定方式面板 + bool isFracture = type == ExperimentType.FractureFixation; + if (_fixationPanel != null) _fixationPanel.SetActive(isFracture); + if (_selectionTip != null) + _selectionTip.text = isFracture ? "Tip:请选择固定方式" : ""; + UpdateStartButton(); + } + + private void SelectMode(ExperimentMode mode) + { + _selectedMode = mode; + if (_selectedModeLabel != null) + _selectedModeLabel.text = mode == ExperimentMode.Learn ? "学习模式" : "考核模式"; + UpdateStartButton(); + } + + private void SelectFixation(FixationMethod fixation) + { + _selectedFixation = fixation; + if (_selectedFixationLabel != null) + _selectedFixationLabel.text = fixation == FixationMethod.LimbFixation ? "肢体固定" : "夹板固定"; + UpdateStartButton(); + } + + private void UpdateStartButton() + { + if (_btnStart == null) return; + _btnStart.interactable = true; + } + + private void StartExperiment() + { + var gm = GameManager.Instance; + if (gm == null) + { + Debug.LogError("MainMenu: GameManager.Instance 为空!请确保场景中有 GameManager。"); + return; + } + + gm.CurrentExperimentType = _selectedType; + gm.CurrentMode = _selectedMode; + gm.CurrentFixationMethod = _selectedFixation; + + // 加载实验场景(所有实验共用一个场景,StepManager 根据配置区分) + UnityEngine.SceneManagement.SceneManager.LoadScene("Experiment"); + } +} diff --git a/Assets/Scripts/VFS.Core.asmdef b/Assets/Scripts/VFS.Core.asmdef new file mode 100644 index 0000000..9f91b8d --- /dev/null +++ b/Assets/Scripts/VFS.Core.asmdef @@ -0,0 +1,16 @@ +{ + "name": "VFS.Core", + "rootNamespace": "VFS", + "references": [ + "Unity.TextMeshPro" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/Doc/01_项目原始大纲与资源清单.md b/Doc/01_项目原始大纲与资源清单.md new file mode 100644 index 0000000..6a00d91 --- /dev/null +++ b/Doc/01_项目原始大纲与资源清单.md @@ -0,0 +1,58 @@ +# VFS 项目原始大纲与资源清单 + +> 来源:甲方原始需求文档。本文件为归档用途,非实施文档。 + +## 一、项目定位 + +体育虚拟仿真(Virtual Simulation)教学项目,用于医学、急救、工程等领域的培训。 + +## 二、主界面与标题 + +- 主标题:运动伤害现场急救处理虚拟仿真实验 +- 底部功能图标:心肺复苏 | 骨折固定 | 扭伤包扎 | 出血包扎 | 伤员搬运 + +## 三、实验模式选择界面 + +- 顶部提示:Tip:请选择实验模式 +- 固定方式选项:肢体固定 | 夹板固定 +- 运行模式选项:学习模式 | 考核模式 +- 右侧常驻按钮:全屏模式 | 上一步 | 下一步 | 考核模式 | 返回 + +## 四、核心资源清单 + +### 3D模型 (Models) +- 场景:室内田径馆(包含跑道、看台、跳高架、跳高垫) +- 角色:伤者(男性,运动装,骨骼绑定)、施救者(女性,急救员装束,骨骼绑定) +- 道具:急救箱(带红十字)、绷带/三角巾、夹板(木板)、鞋子/袜子 + +### 动画 (Animations) +- 伤者:跑步循环、跳高起跳、空中姿态、落地摔倒、躺在地上 +- 施救者:站立、蹲下、检查动作、脱鞋动作、包扎动作 +- 动捕:EasyVirtualMotionCaptureForUnity https://github.com/gpsnmeajp/EasyVirtualMotionCaptureForUnity/releases/tag/v5.0b +- 备选:Mixamo 基础动画 + Unity Animation 手动 K 帧 + +### UI素材 +- 图标:心肺复苏、骨折固定、扭伤包扎等 5 个主图标 +- 按钮:全屏、上一步、下一步、返回、确定 +- 背景图:半透明面板、提示框背景 + +### 视频素材 +- 真人演示视频(用于学习模式右下角的画中画) + +### 音频 +- 背景音乐(可选) +- 语音解说(TTS 或真人录音) + +## 五、设计参考 + +- 即时设计协作链接:https://js.design/f/9ER6eB?mode=design&p=6KbkG4nYyl + +## 六、项目阶段规划(原始) + +1. 第一阶段:项目规划与资源准备 (Pre-production) +2. 第二阶段:场景搭建与基础设置 (Scene Setup) +3. 第三阶段:核心逻辑开发 (C# Scripting) +4. 第四阶段:UI系统开发 +5. 第五阶段:动画与特效细节 +6. 第六阶段:具体步骤实现指南 +7. 第七阶段:打包与发布 (Build Settings: PC Standalone, 1920x1080) diff --git a/Doc/02_完整流程文案与UI文案.md b/Doc/02_完整流程文案与UI文案.md new file mode 100644 index 0000000..e362e13 --- /dev/null +++ b/Doc/02_完整流程文案与UI文案.md @@ -0,0 +1,116 @@ +# VFS 完整流程文案与 UI 文案 + +> 来源:甲方原始需求文档。本文件为归档用途。 + +--- + +## 一、主界面文案 + +- 主标题:运动伤害现场急救处理虚拟仿真实验 +- 底部图标:心肺复苏 | 骨折固定 | 扭伤包扎 | 出血包扎 | 伤员搬运 + +## 二、实验模式选择界面 + +| 元素 | 文案 | +|------|------| +| 顶部提示 | Tip:请选择实验模式 | +| 固定方式 | 肢体固定 / 夹板固定 | +| 运行模式 | 学习模式 / 考核模式 | +| 右侧按钮 | 全屏模式 / 上一步 / 下一步 / 考核模式 / 返回 | + +--- + +## 三、学习模式(肢体固定)完整流程文案 + +| 步骤 | Tip 提示文案 | 角色对话/系统反馈 | +|------|-------------|------------------| +| 1. 环境评估 | Tip:观察周围环境是否有危险,做好防护措施。 | 周围环境安全。我是救护志愿者,我可以帮助您吗? | +| 2. 伤情检查 | Tip:请检查伤员受伤位置 | 伤员左大腿疑似骨折,无伤口。 | +| 3. 脱鞋袜检查 | Tip:请将伤员鞋袜脱掉并保存好,观察末端血液循环、运动及感觉 | (执行脱鞋动画) | +| 4. 放置衬带 | Tip:请在正确的位置穿宽度约10cm的带子 | (右下角同步播放真人演示视频) | +| 5. 肢体并拢 | Tip:请在两下肢间加衬垫,移动健肢,将双下肢轻轻并拢 | (执行并腿动画) | +| 6. 主干固定 | Tip:请在未受伤腿分别固定骨折近心端(上端)、骨折远心端(下端),然后固定膝关节下,结打在右侧。 | (执行绑带动画) | +| 7. 踝关节固定 | Tip:踝关节"8"字固定,结打在两足背之间。 | (执行8字绑带动画) | +| 8. 终末检查 | Tip:包扎完毕请检查伤肢血液循环,运动及感觉。 | 伤员一切正常,包扎完毕。 | + +--- + +## 四、考核模式测试题与判分文案 + +| 考核点 | 弹窗题目 | 选项/交互提示 | +|--------|---------|-------------| +| 考核点一 | 点击确认后,请按正确顺序进行环境评估与自我介绍 | 按钮:确定 | +| 考核点二 | 点击确认后,请按正确顺序检查伤情 | 按钮:确定 | +| 考核点三 | 请选择合适的带子宽度 | □ 5cm ☑ 10cm □ 20cm | +| 考核点四 | 请选择正确的放带子顺序 | 点击地面预设的带子位置排序 | +| 考核点五 | 请选择正确的系带子顺序 | 点击绑带位置排序 | +| 考核点六 | 请选择带子打结位置 | 点击腿部模型指定区域 | +| 考核点七 | 请选择踝关节打结位置 | □ 足底 ☑ 足背 | + +### 成绩显示栏 + +``` +标题:成绩显示栏 +分数:考试成绩:86分 +错题分析: + 第一题:正确 + 第二题:正确 + 第三题:错误:应选择10cm宽度的带子 + 第四题:正确 + 第五题:正确 + 第六题:正确 + 第七题:正确 +确定 → 实验结束! +``` + +--- + +## 五、夹板固定模式专属 Tip 文案 + +| 步骤 | 替换后的 Tip 文案 | +|------|------------------| +| 放置衬垫 | Tip:请在骨折肢体加衬垫,并用夹板固定 | +| 固定操作 | Tip:请用带子将夹板固定在腿上 | + +> 注:检查、脱鞋、终末检查文案与肢体固定模式一致。 + +--- + +## 六、注意事项弹窗文案 + +``` +注意事项: +1. 下肢损伤选择仰卧位; +2. 骨折固定前后要脱掉鞋袜并检查下肢末梢循环、运动、感觉; +3. 穿带子时应从健侧穿向伤侧; +4. 骨折固定时先固定伤口的近心端,后固定远心端,再由上而下进行固定; +5. 固定后尽可能抬高下肢。 +``` + +> 注:原文案中"上肢"已修正为"下肢"。 + +--- + +## 七、「伤情检查」详细操作须知(甲方补充) + +1. 通过视觉检查骨折部位是否存在明显肿胀、变形或异常角度,初步判断骨折类型(闭合性/开放性)及移位程度。 +2. 观察骨折部位肿胀与畸形,询问患者疼痛程度并测试患肢主动/被动活动能力,确认是否存在神经血管损伤或肌肉撕裂等并发症。 +3. 评估疼痛与功能障碍,重点排查开放性骨折的伤口污染情况,记录创面大小、深度及出血量。 +4. 监测生命体征,包括血压、脉搏频率及节律,关注是否出现休克前兆。 +5. 神经系统状态评估,排除颅脑损伤或脊髓损伤可能。 + +## 八、「夹板固定」详细操作须知(甲方补充) + +1. 根据骨折部位选择长度、硬度适宜的夹板,如四肢骨折需超过相邻两关节。 +2. 在骨突处(如踝部)加厚衬垫,分散压力。 +3. 固定完成后立即检查患肢末梢颜色、温度及脉搏,记录患者主观感受,必要时调整绷带松紧度或重新固定。 +4. 每小时评估固定效果,记录肢体肿胀程度、疼痛变化及感觉异常,及时应对并发症如骨筋膜室综合征。 + +### 左大腿骨折夹板固定示例 + +| 夹板位置 | 上端 | 下端 | +|---------|------|------| +| 内侧夹板 | 大腿根部 | 过足跟 | +| 外侧夹板 | 腋窝 | 过足跟 | + +关节处及空隙部位放置衬垫。 diff --git a/Doc/03_考核评分标准.md b/Doc/03_考核评分标准.md new file mode 100644 index 0000000..d85bb2e --- /dev/null +++ b/Doc/03_考核评分标准.md @@ -0,0 +1,58 @@ +# VFS 考核评分标准 + +> 来源:甲方要求。本文件为评分逻辑的唯一依据。 + +--- + +## 一、骨折固定实验评分权重(8 步骤,满分 100 分) + +| 步骤序号 | 步骤名称 | 满分 | 考核点 | +|---------|---------|------|--------| +| 1 | 环境评估 | 5 分 | 按正确顺序进行环境评估与自我介绍 | +| 2 | 伤情检查 | 20 分 | 按正确顺序检查伤情(含视觉检查、肿胀畸形判断、疼痛评估、生命体征监测、神经系统评估 5 个子项) | +| 3 | 脱鞋袜检查 | 10 分 | 正确操作脱鞋袜,观察末梢血液循环 | +| 4 | 放置衬带 | 10 分 | 选择正确带子宽度(10cm)/ 正确放带子顺序 | +| 5 | 肢体并拢 | 10 分 | 加衬垫、并拢下肢操作 | +| 6 | 主干固定 | 20 分 | 固定顺序正确(近心端→远心端→膝关节下)、结打右侧 | +| 7 | 踝关节固定 | 15 分 | 8 字固定正确、结打足背 | +| 8 | 终末检查 | 10 分 | 检查血液循环、运动及感觉 | + +> 总分 = 各步骤得分之和。每步骤内如有子项错误,按子项权重扣分。 + +## 二、伤情检查(步骤 2)子项评分细则(20 分满分) + +| 子项 | 分值 | 内容 | +|------|------|------| +| 2.1 | 4 分 | 视觉检查骨折部位肿胀、变形、异常角度,判断骨折类型及移位程度 | +| 2.2 | 4 分 | 观察肿胀畸形,询问疼痛程度,测试主动/被动活动能力 | +| 2.3 | 4 分 | 评估疼痛与功能障碍,排查开放性伤口污染、创面大小/深度/出血量 | +| 2.4 | 4 分 | 监测生命体征(血压、脉搏频率及节律),判断休克前兆 | +| 2.5 | 4 分 | 神经系统状态评估,排除颅脑损伤或脊髓损伤 | + +## 三、夹板固定评分权重(伤情检查 + 专有步骤) + +夹板固定模式与肢体固定共享步骤 1-3、8,步骤 4-7 替换为夹板专用步骤。 + +| 步骤 | 名称 | 满分 | 考核点 | +|------|------|------|--------| +| 1 | 环境评估 | 5 分 | 同肢体固定 | +| 2 | 伤情检查 | 20 分 | 同肢体固定(含 5 子项) | +| 3 | 脱鞋袜检查 | 10 分 | 同肢体固定 | +| 4 | 选择夹板 | 10 分 | 选择长度/硬度适宜的夹板,需超过相邻两关节 | +| 5 | 放置衬垫 | 10 分 | 骨突处加厚衬垫,关节及空隙放衬垫 | +| 6 | 夹板固定 | 20 分 | 内侧夹板上至大腿根部下过足跟,外侧夹板上至腋窝下过足跟 | +| 7 | 固定后检查 | 15 分 | 检查末梢颜色/温度/脉搏,记录主观感受,调整松紧度 | +| 8 | 终末检查 | 10 分 | 同肢体固定 | + +## 四、交互评分映射规则 + +| 交互类型 | 全对 | 部分对 | 全错 | +|---------|------|--------|------| +| ClickAnywhere | 满分 | — | 0 | +| ClickObject(单次) | 满分 | — | 0 | +| MultipleChoice(单选) | 满分 | — | 0 | +| SequenceClick(N 步序列) | 满分 | (正确步数/N)×满分 | 0 | +| DragToTarget(拖拽吸附) | 满分 | — | 0 | + +> 考核模式弹窗判定:选择正确 → 满分;选择错误 → 0 分 + 显示错误反馈。 +> SequenceClick 按子步骤命中比例计分。 diff --git a/Doc/04_美术资源对接与场景配置指南.md b/Doc/04_美术资源对接与场景配置指南.md new file mode 100644 index 0000000..4a42567 --- /dev/null +++ b/Doc/04_美术资源对接与场景配置指南.md @@ -0,0 +1,374 @@ +# VFS 美术资源对接与场景配置指南 + +> 给你的美术和场景搭建同学看这份文档,他们就知道要做什么、怎么做、命名规则是什么。 +> 你作为项目负责人,拿到美术交付物后,按本文逐项检查即可。 + +--- + +## 一、你需要交付的美术资源清单 + +### 1.1 场景模型 + +| 资源 | 格式 | 要求 | +|------|------|------| +| 室内田径馆 | FBX/GLB | 包含跑道、看台、跳高架、跳高垫。比例正确(真人 1.7m 对应 Unity 1.7 单位)。Pivot 在地面中心。 | +| 跳高架 | FBX/GLB | 可独立放置,带横杆 | +| 跳高垫 | FBX/GLB | 软材质效果,1m × 3m × 0.5m | + +### 1.2 角色模型 + +| 资源 | 格式 | 骨骼 | 备注 | +|------|------|------|------| +| 伤者(男性、运动装) | FBX + 骨骼绑定 | Humanoid,65 根骨骼以内 | Mixamo 自动绑骨亦可,必须带 Humanoid Avatar | +| 施救者(女性、急救员装束) | FBX + 骨骼绑定 | Humanoid,65 根骨骼以内 | 同上 | + +### 1.3 道具模型 + +| 资源 | 格式 | 数量 | 备注 | +|------|------|------|------| +| 急救箱(带红十字) | FBX/GLB | 1 | 可放场景角落,静态物件 | +| 绷带/绑带(蓝色) | FBX/GLB | 4 条 | 分段模型,每条独立,绑定到腿时可缩放/旋转 | +| 三角巾 | FBX/GLB | 1 | | +| 夹板(木板) | FBX/GLB | 2 块 | 内侧夹板 80cm,外侧夹板 120cm | +| 衬垫(白色软垫) | FBX/GLB | 2 块 | 放关节和骨突处用 | +| 左鞋(运动鞋) | FBX/GLB | 1 | 独立于角色,可被脱掉/隐藏 | +| 右鞋(运动鞋) | FBX/GLB | 1 | 同上 | +| 8 字绑带(脚踝用) | FBX/GLB | 1 | | + +### 1.4 动画(如果做) + +| 资源 | 时长 | 循环 | 备注 | +|------|------|------|------| +| 伤者_跑步 | 1s | 是 | | +| 伤者_跳高起跳 | 1.5s | 否 | | +| 伤者_空中姿态 | 0.8s | 否 | | +| 伤者_落地摔倒 | 2s | 否 | 落地→倒地→静止 | +| 伤者_躺地(受伤) | 2s | 是 | 最终 Idle | +| 施救者_站立 | 1s | 是 | | +| 施救者_蹲下 | 1s | 否 | | +| 施救者_检查伤情 | 2s | 否 | 手部触摸伤腿 | +| 施救者_脱鞋 | 2s | 否 | 手伸向鞋子 | +| 施救者_包扎(手部特写) | 3s | 否 | | + +> 没有动捕可以用 Mixamo 下载基础动画拼接。 + +### 1.5 视频 + +| 资源 | 分辨率 | 时长 | 文件名 | +|------|--------|------|--------| +| 真人演示·放置衬带 | 1080p | 30s | `bandage_demo.mp4` | +| 真人演示·夹板固定 | 1080p | 30s | `splint_demo.mp4` | + +放到 `StreamingAssets/Videos/` 下。 + +### 1.6 音频(可选) + +| 资源 | 格式 | 文件名示例 | +|------|------|-----------| +| 背景音乐 | MP3 320kbps | `bgm_experiment.mp3` | +| 语音_环境安全 | WAV 16bit | `voice_01_safe.wav` | +| 语音_伤情检查 | WAV 16bit | `voice_02_injury.wav` | +| ... | ... | ... | + +放到 `StreamingAssets/Voice/` 下。 + +### 1.7 UI 素材 + +| 资源 | 尺寸 | 格式 | 备注 | +|------|------|------|------| +| 图标_心肺复苏 | 128×128 | PNG 透明 | | +| 图标_骨折固定 | 128×128 | PNG 透明 | | +| 图标_扭伤包扎 | 128×128 | PNG 透明 | | +| 图标_出血包扎 | 128×128 | PNG 透明 | | +| 图标_伤员搬运 | 128×128 | PNG 透明 | | +| 按钮_全屏 | 64×64 | PNG/SVG | | +| 按钮_上一步 | 64×64 | PNG/SVG | | +| 按钮_下一步 | 64×64 | PNG/SVG | | +| 按钮_返回 | 64×64 | PNG/SVG | | +| 按钮_确定 | 200×60 | PNG 含文字 | | +| 面板_半透明底 | 可拉伸 9-slice | PNG | | +| 提示框背景 | 可拉伸 9-slice | PNG | | + +--- + +## 二、Unity 场景搭建步骤 + +拿到所有资源后,按以下顺序操作。 + +### 2.1 导入资产 + +1. 将模型 FBX 拖入 `Assets/Models/` 文件夹 +2. 将贴图拖入 `Assets/Textures/`(如有) +3. 将视频拖入 `StreamingAssets/Videos/` +4. 将音频拖入 `StreamingAssets/Voice/` +5. 将 UI 图片拖入 `Assets/UI/Sprites/` +6. 将动画 FBX 拖入 `Assets/Animations/` + +### 2.2 创建 Experiment 场景 + +File → New Scene → 命名为 `Experiment` → 保存到 `Assets/Scenes/` + +### 2.3 设置光照 + +- Directional Light:模拟室内顶灯,Intensity 0.8,Color 暖白 (#FFF5E6) +- 可选:Light Probe Group 放在场景四角和中心 + +### 2.4 放置场景 + +1. 把田径馆模型拖入 Hierarchy → Position (0, 0, 0) +2. 把跳高架放在跑道区域 → Position 根据模型调整 +3. 把跳高垫放在跳高架落地区 + +### 2.5 放置角色 + +1. 伤者 → Hierarchy → Position:躺在跑道上(大致 X:2, Y:0, Z:5) + - 初始姿态:仰卧位,身体平放,头朝跑道方向 + - 挂 `CharacterAnimController.cs` → Role 选 `Patient` + - 挂 `Animator` 组件,配置好动画状态机 +2. 施救者 → Position:站在伤者右侧(X:3, Y:0, Z:5) + - 挂 `CharacterAnimController.cs` → Role 选 `Rescuer` + - 挂 `Animator` 组件 + +### 2.6 放置道具 + +| 道具 | 位置 | 说明 | +|------|------|------| +| 急救箱 | 伤者身旁地面 (X:3.5, Y:0, Z:4.5) | 静态,无交互 | +| 左鞋 | 伤者左脚位置 | 初始穿在脚上,可被隐藏/移动 | +| 右鞋 | 伤者右脚位置 | 同上 | +| 绑带 ×4 | 急救箱旁 | 之后拖到腿上 | +| 衬垫 ×2 | 急救箱旁 | | +| 夹板 ×2 | 急救箱旁 | 夹板固定模式用 | +| 8 字绑带 | 急救箱旁 | 踝关节固定用 | + +### 2.7 最关键:给物体加 Collider 和 Tag + +**这是代码能找到物体的唯一方式,必须做对。** + +| GameObject | Collider 类型 | Tag 名称 | 说明 | +|------------|-------------|----------|------| +| 伤者左大腿 | Box Collider(包裹股骨区域) | `InjuryLeg` | 初始伤情检查点击区域 | +| 伤者左大腿·视觉检查区 | Box Collider | `InjuryVisual` | 伤情检查子项 1:肿胀变形 | +| 伤者左大腿·肿胀触摸区 | Box Collider | `InjurySwelling` | 伤情检查子项 2:肿胀畸形 | +| 伤者胸口 | Box Collider | `InjuryPain` | 伤情检查子项 3:疼痛评估(模拟听诊/询问) | +| 伤者手腕 | Box Collider | `InjuryVitalSigns` | 伤情检查子项 4:生命体征(模拟测脉搏) | +| 伤者头部 | Box Collider | `InjuryNeuro` | 伤情检查子项 5:神经评估 | +| 左鞋 | Box Collider | `Shoe` | 脱鞋步骤 | +| 右鞋 | Box Collider | `Shoe` | 同上 | +| 绑带位置 1(近心端·大腿上部) | Box Collider | `BandagePos1` | 绑带固定位置 | +| 绑带位置 2(远心端·大腿下部) | Box Collider | `BandagePos2` | | +| 绑带位置 3(膝关节下) | Box Collider | `BandagePos3` | | +| 衬垫位置(两腿间) | Box Collider | `PaddingPos` | 拖拽衬垫吸附目标 | +| 踝关节 | Sphere Collider | `Ankle` | 8 字固定 | +| 夹板左侧位置 | Box Collider | `SplintLeft` | 夹板拖拽吸附 | +| 夹板右侧位置 | Box Collider | `SplintRight` | 夹板拖拽吸附 | +| 场景地面 | Box Collider(大面积) | `Ground` | ClickAnywhere 用 | + +> **Tag 名称必须完全一致**,包括大小写。代码用 `tag == "InjuryLeg"` 匹配。 + +### 2.8 给可交互物体加脚本组件 + +| GameObject | 挂载脚本 | 关键设置 | +|------------|---------|---------| +| 绑带物件 ×4 | `DraggableObject.cs` | `SnapTarget` 拖入对应的 BandagePos 空物体 | +| 衬垫 | `DraggableObject.cs` | `SnapTarget` 拖入 PaddingPos | +| 夹板 ×2 | `DraggableObject.cs` | `SnapTarget` 拖入对应夹板位置 | +| 伤者左大腿 | `HighlightEffect.cs` | 考核模式可设 AlwaysHighlighted = true | +| 所有带 Collider 的交互物体 | `HighlightEffect.cs`(可选) | 悬停时会变色,方便用户找到 | + +### 2.9 创建管理器空物体 + +Hierarchy → Create Empty → 命名为 `[Managers]` + +在 `[Managers]` 上依次 Add Component: +1. `GameManager.cs` — 无需配置,Awake 时自动挂载 +2. `StepManager.cs` — 后续在 Inspector 拖入 Config +3. `ExperimentFlowCoordinator.cs` — 后续拖入各子系统引用 +4. `AudioManager.cs` — 拖入 BGMClip + +### 2.10 配置相机 + +1. 主相机(Main Camera)→ 挂在 `[Managers]` 下或独立 + - Position:面对伤者,俯角 30°,距离约 3 米 + - 挂载 `InteractionSystem.cs` + - `Interactable Mask` 选包含所有交互物体的 Layer +2. 特写相机(可选):用于包扎特写,放在 `ExperimentFlowCoordinator` 里管理 + +### 2.11 搭建 UI Canvas + +Hierarchy → UI → Canvas(命名为 `UICanvas`) + +**内部结构(父子关系):** +``` +UICanvas +├── [挂 UIManager.cs] +│ +├── TipPanel(顶部提示栏) +│ └── TipText(TMP_Text) +│ +├── DetailPanel(详细操作说明面板 · 学习模式) +│ └── DetailText(TMP_Text,多行可滚动) +│ +├── DialogueBubble(对话气泡) +│ └── DialogueText(TMP_Text) +│ +├── VideoPanel(画中画视频) +│ ├── RawImage(挂 VideoController.cs) +│ └── VideoPlayer(子物体或被 VideoController 引用) +│ +├── RightButtons(右侧功能按钮栏) +│ ├── BtnFullScreen(Button) +│ ├── BtnPrevious(Button) +│ ├── BtnNext(Button) +│ ├── BtnSwitchMode(Button + TMP_Text 标签) +│ └── BtnReturn(Button) +│ +├── ProgressBar(底部进度条) +│ ├── Slider +│ └── ProgressText(TMP_Text 显示 "3/8") +│ +├── ExamPopup(考核弹窗 · 挂 ExamPopup.cs) +│ ├── QuestionText(TMP_Text) +│ ├── OptionToggles(Toggle × 4) +│ ├── OptionLabels(TMP_Text × 4) +│ ├── FeedbackText(TMP_Text 红色) +│ ├── OperationPanel(操作题提示) +│ │ └── OperationHint(TMP_Text) +│ └── ConfirmButton(Button) +│ +├── ScoreDisplay(成绩面板 · 挂 ScoreDisplay.cs) +│ ├── ExamResultRoot +│ │ ├── ScoreTitle(TMP_Text) +│ │ ├── ScoreValue(TMP_Text) +│ │ └── AnalysisListParent(Scroll View → Content) +│ │ └── AnalysisItemPrefab(预制体:TMP_Text) +│ ├── NotesRoot(注意事项面板) +│ │ └── NotesText(TMP_Text) +│ └── ConfirmButton +│ +└── BottomIcons(底部图标栏) + ├── BtnCPR + ├── BtnFracture + ├── BtnSprain + ├── BtnBleeding + └── BtnTransport +``` + +### 2.12 Inspector 连线 + +打开 `[Managers]` → `ExperimentFlowCoordinator`,拖入: +| 字段 | 拖入对象 | +|------|---------| +| Step Manager | `[Managers]`(自身的 StepManager) | +| Interaction System | Main Camera(自身的 InteractionSystem) | +| UI Manager | `UICanvas`(自身的 UIManager) | +| Step Animation Handler | `StepAnimationHandler` 所在物体 | +| Audio Manager | `[Managers]`(自身的 AudioManager) | + +打开 `UICanvas` → `UIManager`,拖入所有子面板引用。 + +--- + +## 三、创建 MainMenu 场景 + +File → New Scene → 命名为 `MainMenu` → 保存。 + +### 3.1 结构 + +``` +UICanvas +├── [挂 MainMenu.cs] +├── TitleText(TMP_Text:"运动伤害现场急救处理虚拟仿真实验") +├── TipText(TMP_Text:"Tip:请选择实验模式") +├── ExperimentButtons +│ ├── BtnCPR(Button + 图标) +│ ├── BtnFracture(Button + 图标) +│ ├── BtnSprain(Button + 图标) +│ ├── BtnBleeding(Button + 图标) +│ └── BtnTransport(Button + 图标) +├── ModePanel +│ ├── BtnLearnMode(Button) +│ ├── BtnExamMode(Button) +│ └── SelectedModeLabel(TMP_Text) +├── FixationPanel(骨折固定专属) +│ ├── BtnLimbFixation(Button) +│ ├── BtnSplintFixation(Button) +│ └── SelectedFixationLabel(TMP_Text) +└── BtnStart(Button "开始实验") +``` + +### 3.2 空物体 + +Create Empty → 命名 `[GameManager]` → 挂 `GameManager.cs` + +### 3.3 连线 + +MainMenu.cs 的 Inspector 中拖入对应的 Button 和 TMP_Text 引用。 + +--- + +## 四、检查清单(交付前逐项打勾) + +### 模型 +- [ ] 田径馆模型比例正确 +- [ ] 伤者 Humanoid Avatar 配置完成(Inspector → Rig → Animation Type = Humanoid) +- [ ] 施救者 Humanoid Avatar 配置完成 +- [ ] 所有道具 FBX 正确导入,材质不丢失 + +### Collider & Tag +- [ ] 伤者左大腿 Collider 覆盖正确区域,Tag = `InjuryLeg` +- [ ] 5 个伤情检查子项 Collider 各覆盖正确部位,Tag 正确 +- [ ] 鞋子 Collider + Tag = `Shoe` +- [ ] 绑带位置 1 2 3 空物体有 Collider + Tag = `BandagePos1/2/3` +- [ ] 衬垫位置空物体 Collider + Tag = `PaddingPos` +- [ ] 踝关节 Collider + Tag = `Ankle` +- [ ] 夹板位置 Collider + Tag = `SplintLeft` / `SplintRight` +- [ ] 场景地面 Collider + Tag = `Ground` +- [ ] 所有交互物体的 Layer 在 `InteractionSystem.InteractableMask` 范围内 + +### 脚本挂载 +- [ ] `[Managers]` 挂 GameManager + StepManager + ExperimentFlowCoordinator + AudioManager +- [ ] Main Camera 挂 InteractionSystem +- [ ] UICanvas 挂 UIManager +- [ ] ExamPopup 面板挂 ExamPopup.cs +- [ ] ScoreDisplay 面板挂 ScoreDisplay.cs +- [ ] VideoPanel 挂 VideoController.cs +- [ ] 绑带/衬垫/夹板挂 DraggableObject.cs,SnapTarget 已拖入 + +### 连线 +- [ ] ExperimentFlowCoordinator 所有字段已拖入 +- [ ] UIManager 所有字段已拖入 +- [ ] MainMenu 所有字段已拖入 + +### ScriptableObject +- [ ] `Create → VFS → Step Data` 创建了 8 个步骤(肢体固定)或更多 +- [ ] 每个 StepData 的 ScoreWeight 已按评分标准填入(5, 20, 10, 10, 10, 20, 15, 10) +- [ ] 每个 StepData 的 DetailedContent 已填入(至少 CheckInjury 步骤) +- [ ] `Create → VFS → Experiment Config` 创建了配置 +- [ ] LearnStepsLimb / LearnStepsSplint / ExamStepsLimb / ExamStepsSplint 四个数组已填入 +- [ ] StepManager.Config 已拖入 ExperimentConfig + +### 验证 +- [ ] Play 进入 Experiment 场景,UIManager 能显示第一步 Tip 文案 +- [ ] 点击伤者大腿 → InteractionSystem 输出 Debug.Log 包含 `Tag=InjuryLeg` +- [ ] 考核模式下弹窗正常弹出 +- [ ] 答题后 ScoreDisplay 正常显示 + +--- + +## 五、常见问题 + +**Q:FBX 导入后材质是紫色的?** +A:材质丢失。在 FBX 的 Import Settings → Materials → Extract Textures 重新提取。 + +**Q:骨骼动画播放时角色扭曲?** +A:Avatar 未正确配置。Inspector → Rig → Configure Avatar,检查骨骼映射是否正确。 + +**Q:点击 3D 物体没反应?** +A:检查 (1) Collider 是否勾选了 `Is Trigger`(不要勾),(2) Layer 是否在主相机的 Culling Mask 和 InteractionSystem 的 InteractableMask 范围内,(3) Tag 是否正确。 + +**Q:Canvas 上的 UI 遮挡了 3D 点击?** +A:InteractionSystem 已内置 `IsPointerOverGameObject()` 检测,不会同时触发。确保 Canvas 的 Graphic Raycaster 正常工作。 + +**Q:成绩显示 0 分?** +A:检查 StepData 的 ScoreWeight 是否已填入非 0 值,考核模式下 `RecordExamResult` 的 proportion 参数是否传了 1f。 diff --git a/README.md b/README.md index 4a735c3..0f5552b 100644 --- a/README.md +++ b/README.md @@ -1,283 +1,131 @@ -# VFSUnity - 运动伤害现场急救处理虚拟仿真实验 +# 运动伤害现场急救处理虚拟仿真实验 (VFS) -基于 Unity 2022.3.62f3c1 (URP) 的体育虚拟仿真教学项目。 +## 项目概述 -## 环境要求 +Unity 虚拟仿真教学项目,用于运动伤害现场急救培训。 +涵盖五大实验:心肺复苏、骨折固定、扭伤包扎、出血包扎、伤员搬运。 +支持**学习模式**(含语音引导 + 画中画演示视频 + 详细操作说明)和**考核模式**(含加权自动判分 + 错题分析)。 -| 工具 | 版本 | -|------|------| -| Unity Editor | **2022.3.62f3c1**(必须一致,建议通过 Unity Hub 安装) | -| Unity Hub | 3.x+ | -| 渲染管线 | Universal Render Pipeline (URP) 14.0.12 | - -## 快速开始 - -### 1. 克隆仓库 - -```bash -git clone https://git.yangbear.cn/yangbear/VFSUnity.git -``` - -### 2. 用 Unity Hub 打开 - -1. 打开 **Unity Hub** -2. 确保已安装 **Unity 2022.3.62f3c1** -3. 点击 **Open** → 选择 clone 下来的 `VFSUnity` 文件夹 -4. Unity 自动加载并解析 `Packages/manifest.json` 中的依赖包 - -### 3. 运行 - -- 在 **Project** 窗口中双击 `Assets/Scenes/main.unity` 打开主菜单场景 -- 点击顶部工具栏的 ▶ **Play** 按钮即可运行 - ---- - -## 项目结构 +## 目录结构 ``` -VFSUnity/ -├── Assets/ -│ ├── Scripts/ -│ │ ├── Core/ # 核心管理器 -│ │ │ ├── GameManager.cs # 全局状态管理(单例) -│ │ │ ├── ExperimentController.cs # 实验流程控制器 -│ │ │ └── ConfigLoader.cs # JSON 配置加载器 -│ │ ├── Data/ # 数据模型 -│ │ │ ├── StepData.cs # 单步定义 ScriptableObject -│ │ │ ├── ExamQuestionData.cs # 考核题目 ScriptableObject -│ │ │ └── ExperimentConfig.cs # 实验配置 ScriptableObject -│ │ ├── Interaction/ # 交互系统 -│ │ │ ├── InteractionSystem.cs # 射线检测与点击处理 -│ │ │ └── DraggableObject.cs # 拖拽交互组件 -│ │ ├── UI/ # UI 控制器 -│ │ │ ├── MainMenuUI.cs # 主菜单界面 -│ │ │ ├── ModeSelectionUI.cs # 模式选择界面 -│ │ │ ├── HUDController.cs # 游戏内 HUD -│ │ │ ├── PopupSystem.cs # 弹窗系统 -│ │ │ └── VideoPlayerController.cs # 画中画视频 -│ │ └── Animation/ -│ │ └── AnimationController.cs # 角色动画控制 -│ ├── Scenes/ -│ │ ├── main.unity # 主菜单场景 ⭐ -│ │ ├── ModeSelection.unity # 模式选择场景 -│ │ └── Experiment.unity # 实验场景(核心) -│ ├── Models/ -│ │ ├── Characters/ # 角色模型(伤者、施救者) -│ │ ├── Props/ # 道具模型(急救箱、绷带、夹板) -│ │ └── Environment/ # 场景模型(田径馆) -│ ├── Animations/ # 动画文件(待导入) -│ ├── Prefabs/ # 预制体 -│ ├── Materials/ # 材质 -│ ├── Textures/ # 贴图 -│ │ └── UI/ # UI 贴图 -│ ├── Audio/ # 音频文件(语音解说) -│ ├── Video/ # 视频文件(真人演示) -│ └── Resources/ -│ └── Data/ -│ ├── fracture_fixation_steps.json # 肢体固定步骤配置 -│ └── splint_fixation_steps.json # 夹板固定步骤配置 -├── Packages/ # Unity 包依赖清单 -├── ProjectSettings/ # 项目全局设置 -└── README.md +Assets/ + Scripts/ + Core/ # 核心管理 + ExperimentTypes.cs + GameManager.cs # 全局单例:实验/模式/固定方式/加权分数 + StepManager.cs # 流程状态机:步骤推进、事件派发 + ExperimentFlowCoordinator.cs # 场景协调器 + Data/ + StepData.cs # ScriptableObject:步骤数据(含评分权重) + ExperimentConfig.cs # ScriptableObject:完整实验配置 + Interaction/ + InteractionSystem.cs # 射线检测/点击/拖拽/序列判定 + DraggableObject.cs # 拖拽+吸附 + HighlightEffect.cs # 悬停/考核高亮 + UI/ + UIManager.cs # UI 中央调度 + MainMenu.cs # 主菜单 + ExamPopup.cs # 考核弹窗(选择题/操作题) + ScoreDisplay.cs # 加权成绩 + 错题分析 / 注意事项 + VideoController.cs # 画中画视频 + Animation/ + CharacterAnimController.cs # 角色动画 + StepAnimationHandler.cs # 步骤动画协程 + Audio/ + AudioManager.cs # BGM + 语音解说 + Resources/Configs/ # StepData / ExperimentConfig 存放 + StreamingAssets/Videos/ # 演示视频 + StreamingAssets/Voice/ # 语音解说 +Doc/ # 项目文档 + 01_项目原始大纲与资源清单.md + 02_完整流程文案与UI文案.md + 03_考核评分标准.md ``` ---- +## 架构要点 -## 🏗 架构设计 +- **状态集中**:GameManager 单例持有实验类型/模式/固定方式/加权分数 +- **事件驱动**:StepManager 通过 UnityEvent 派发步骤变化,UI/动画/协调器订阅 +- **交互分离**:InteractionSystem 检测 → ExperimentFlowCoordinator 判定(含按比例计分)→ StepManager 推进 +- **数据驱动**:所有文案、选项、评分权重在 ScriptableObject 配置,无需改代码 +- **加权评分**:每步独立 ScoreWeight(满分 100),考核模式按 proportion(0~1)记录得分 -### 核心流程 +## 评分系统 -``` -MainMenu (scene: main.unity) - │ 选择实验类型(骨折固定/心肺复苏/...) - ▼ -ModeSelection (scene: ModeSelection.unity) - │ 选择固定方式 + 运行模式 - ▼ -Experiment (scene: Experiment.unity) - │ GameManager(全局状态) - │ ExperimentController(步骤控制) - │ InteractionSystem(点击检测) - │ HUD(顶部提示栏 + 右侧按钮 + 进度条) - │ PopupSystem(弹窗 + 考核题 + 成绩显示) - │ VideoPlayer(画中画,学习模式) -``` +### 评分权重(骨折固定·肢体固定) -### 状态管理 +| 步骤 | 名称 | 满分 | 考核重点 | +|------|------|------|---------| +| 1 | 环境评估 | 5 分 | 环境评估与自我介绍 | +| 2 | 伤情检查 | 20 分 | 视觉检查→肿胀畸形→疼痛评估→生命体征→神经评估(5 子项) | +| 3 | 脱鞋袜检查 | 10 分 | 脱鞋袜、观察末梢循环 | +| 4 | 放置衬带 | 10 分 | 选择正确带子宽度(10cm) | +| 5 | 肢体并拢 | 10 分 | 加衬垫、并拢下肢 | +| 6 | 主干固定 | 20 分 | 近心端→远心端→膝关节下(3 子项) | +| 7 | 踝关节固定 | 15 分 | 8 字固定、结打足背 | +| 8 | 终末检查 | 10 分 | 检查循环运动感觉 | -| 类 | 职责 | -|----|------| -| **GameManager** | 全局单例。管理实验类型、固定方式、运行模式、当前步骤、分数、考核结果 | -| **ExperimentController** | 接收用户交互事件,验证步骤完成条件,推进实验流程 | -| **InteractionSystem** | 处理鼠标射线检测,区分 UI 点击和 3D 物体点击 | -| **ConfigLoader** | 从 `Resources/Data/*.json` 加载实验步骤和考核题目数据 | +**总分 100 分**。SequenceClick 类型按子项命中比例计分。 -### 数据模型 +### 评分权重(夹板固定模式) -| 类 | 说明 | -|----|------| -| **StepData** (ScriptableObject) | 定义单个步骤:提示文案、对话文案、交互类型、目标标签 | -| **ExamQuestionData** (ScriptableObject) | 定义考核题目:题目文案、选项、正确答案、反馈文案 | -| **ExperimentConfig** (ScriptableObject) | 实验配置容器:步骤列表 + 考核题目列表 + 固位方式 + 分数设置 | +| 步骤 | 名称 | 满分 | 考核重点 | +|------|------|------|---------| +| 1 | 环境评估 | 5 分 | 同肢体固定 | +| 2 | 伤情检查 | 20 分 | 同肢体固定 | +| 3 | 脱鞋袜检查 | 10 分 | 同肢体固定 | +| 4 | 选择夹板 | 10 分 | 长度/硬度需超过相邻两关节 | +| 5 | 放置衬垫 | 10 分 | 骨突处加厚衬垫,关节及空隙放衬垫 | +| 6 | 夹板固定 | 20 分 | 内侧上至大腿根部下过足跟,外侧上至腋窝下过足跟 | +| 7 | 固定后检查 | 15 分 | 末梢颜色/温度/脉搏,调整松紧度 | +| 8 | 终末检查 | 10 分 | 同肢体固定 | ---- +### 左大腿骨折夹板固定示例 -## 🎮 场景说明 +- 内侧夹板:大腿根部 → 过足跟 +- 外侧夹板:腋窝 → 过足跟 +- 关节处及空隙部位放置衬垫 -### 1. 主菜单场景 (`main.unity`) +## 接入步骤 -- 主标题:"运动伤害现场急救处理虚拟仿真实验" -- 底部 5 个功能图标按钮:心肺复苏 | 骨折固定 | 扭伤包扎 | 出血包扎 | 伤员搬运 -- 点击按钮 → 跳转 `ModeSelection` 场景 +### 1. 创建 Unity 项目 +Unity 2021.3 LTS+,3D 模板。安装 TextMeshPro Essentials。 -### 2. 模式选择场景 (`ModeSelection.unity`) +### 2. 导入脚本 +将 `Assets/Scripts/` 下所有 .cs 文件拖入项目对应文件夹。确保有 `VFS.Core.asmdef`。 -- 顶部提示:Tip:请选择实验模式 -- 固定方式选项:肢体固定 | 夹板固定 -- 运行模式选项:学习模式 | 考核模式 -- 底部:开始实验 | 返回主菜单 +### 3. 搭建场景 -### 3. 实验场景 (`Experiment.unity`) +**MainMenu 场景:** +- Canvas → 挂 `MainMenu.cs` +- 5 个实验选择按钮 + 模式切换 + 固定方式面板 + 开始按钮 +- 空 GameObject → 挂 `GameManager.cs` -**场景物体:** -| 物体 | 标签 | 说明 | -|------|------|------| -| Ground | - | 地板(Plane 10x10) | -| InjuredPerson | InjuredPerson | 伤者(占位 Capsule) | -| InjuryLeg | InjuryLeg | 伤腿交互区(Sphere,伤者子物体) | -| Rescuer | Rescuer | 施救者(占位 Capsule) | -| Shoe | Shoe | 鞋子(可点击交互) | -| Bandage | Bandage | 绷带(拖拽交互) | -| Splint | Splint | 夹板(拖拽交互) | -| FirstAidKit | FirstAidKit | 急救箱 | +**Experiment 场景:** +- 导入 3D 模型,给可交互物体加 Collider + Tag: + - `InjuryLeg` `InjuryVisual` `InjurySwelling` `InjuryPain` `InjuryVitalSigns` `InjuryNeuro` — 伤情检查子项 + - `Shoe` `BandagePos1~3` `Ankle` `PaddingPos` `SplintLeft` `SplintRight` `Ground` +- 空 GameObject `[Managers]` 挂 GameManager / StepManager / ExperimentFlowCoordinator / AudioManager +- 主相机挂 `InteractionSystem.cs` +- Canvas 挂 `UIManager.cs`,子面板挂 `ExamPopup` / `ScoreDisplay` / `VideoController` +- 学习模式详细内容面板挂于 Canvas 下,在 `UIManager._detailPanel` 拖入 -**UI 层:** -| 组件 | 说明 | -|------|------| -| TipBar | 顶部提示栏(学习模式显示) | -| DialogueBubble | 角色对话气泡(自动4秒消失) | -| ProgressSlider | 底部进度条 | -| 右侧按钮 | 全屏、上一步、下一步、学习/考核切换、返回 | -| VideoPanel | 右下角画中画视频(学习模式特定步骤激活) | -| PopupSystem | 弹窗系统(考核题、成绩显示、注意事项) | +### 4. 配置步骤 +- Create → VFS → Step Data(逐步骤填入 ScoreWeight / SubStepCount / TipText / DetailedContent / 考核选项) +- Create → VFS → Experiment Config(组装 LearnStepsLimb / ExamStepsLimb / LearnStepsSplint / ExamStepsSplint) +- 拖入 `StepManager.Config` ---- +### 5. 连线 +- `ExperimentFlowCoordinator` Inspector 拖入 StepManager / InteractionSystem / UIManager / StepAnimationHandler / AudioManager +- `UIManager` Inspector 拖入各 UI 引用(Tip / Detail / Dialogue / ExamPopup / ScoreDisplay / VideoController / Buttons) -## 📝 骨折固定实验流程(学习模式) +### 6. 视频/语音 +- 真人演示视频放入 `StreamingAssets/Videos/` +- 语音解说放入 `StreamingAssets/Voice/` -| 步骤 | Tip 提示 | 交互方式 | 目标标签 | -|------|----------|----------|----------| -| 0. 环境评估 | 观察周围环境是否有危险,做好防护措施 | 按钮确认 | - | -| 1. 伤情检查 | 请检查伤员受伤位置 | 点击物体 | InjuryLeg | -| 2. 脱鞋袜检查 | 请将伤员鞋袜脱掉并保存好 | 点击物体 | Shoe | -| 3. 放置衬带 | 请在正确的位置穿宽度约10cm的带子 | 拖拽交互 | Bandage | -| 4. 肢体并拢 | 请在两下肢间加衬垫 | 按钮确认 | - | -| 5. 主干固定 | 固定骨折近心端、远心端、膝关节下 | 拖拽交互 | Bandage | -| 6. 踝关节固定 | 踝关节"8"字固定 | 拖拽交互 | Bandage | -| 7. 终末检查 | 包扎完毕请检查伤肢血液循环 | 按钮确认 | - | +## 依赖 -## 📝 考核模式题目 - -| 考核点 | 题目 | 选项 | 正确答案 | -|--------|------|------|----------| -| 考核点一 | 按正确顺序进行环境评估与自我介绍 | 确定 | - | -| 考核点二 | 按正确顺序检查伤情 | 确定 | - | -| 考核点三 | 请选择合适的带子宽度 | 5cm / 10cm / 20cm | 10cm | -| 考核点四 | 请选择正确的放带子顺序 | 确定(交互排序) | - | -| 考核点五 | 请选择正确的系带子顺序 | 确定(交互排序) | - | -| 考核点六 | 请选择带子打结位置 | 确定(点击目标) | - | -| 考核点七 | 请选择踝关节打结位置 | 足底 / 足背 | 足背 | - ---- - -## 🔧 交互类型枚举 - -| 类型 | 值 | 说明 | -|------|-----|------| -| None | 0 | 无交互 | -| ClickObject | 1 | 点击3D物体(需匹配 targetTag) | -| ClickGround | 2 | 点击地面空白处 | -| DragDrop | 3 | 拖拽物体到目标位置 | -| ButtonConfirm | 4 | 点击 UI 按钮确认 | -| MultipleChoice | 5 | 选择选项(考核模式) | - ---- - -## 📦 待添加资源清单 - -### 3D 模型 -- [ ] 室内田径馆场景(跑道、看台、跳高架、跳高垫) -- [ ] 伤者角色模型(男性、运动装、骨骼绑定) -- [ ] 施救者角色模型(女性、急救员装束、骨骼绑定) -- [ ] 急救箱(带红十字) -- [ ] 绷带/三角巾 -- [ ] 夹板模型 -- [ ] 鞋子/袜子模型 - -### 动画 -- [ ] 伤者:跑步循环、跳高起跳、空中姿态、落地摔倒、躺地受伤 -- [ ] 施救者:站立、蹲下、检查、脱鞋、包扎手部特写 -- [ ] 推荐使用 Mixamo 获取基础动画或动捕制作 - -### 音频 -- [ ] 背景音乐 -- [ ] 语音解说 TTS - -### 视频 -- [ ] 真人演示视频(画中画,学习模式使用) - -### UI 素材 -- [ ] 5 个主功能图标 -- [ ] 按钮样式 -- [ ] 背景图 - ---- - -## 🚀 后续开发指南 - -### 添加新实验类型 - -1. 创建新的 `Resources/Data/xxx_steps.json` 配置 -2. 在 `MainMenuUI.cs` 中添加按钮监听 -3. 在 `ConfigLoader.cs` 中添加加载方法 - -### 替换占位模型 - -1. 导入真实模型到 `Assets/Models/` 对应子目录 -2. 在 Experiment 场景中替换 Capsule/Cube 占位物 -3. 确保各物体的 Tag 和 Collider 设置正确 - -### 添加真实动画 - -1. 导入动画到 `Assets/Animations/` -2. 为角色创建 Animator Controller -3. 在 `AnimationController.cs` 中关联 Animator 引用和状态名 - -### 部署 MCP for Unity(可选) - -在 Unity 中:**Window → Package Manager → + → Add package from git URL**,粘贴: -``` -https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#main -``` -然后 **Window → MCP for Unity → Configure All Detected Clients**。 - ---- - -## 依赖包 - -核心依赖已在 `Packages/manifest.json` 中锁定版本: - -- **Universal RP** 14.0.12 — 渲染管线 -- **TextMeshPro** 3.0.7 — 文字渲染 -- **Timeline** 1.7.7 — 过场动画 -- **UGUI** 1.0.0 — UI 系统 - ---- - -## 注意事项 - -- **不要删除 `.meta` 文件** — Unity 用它们追踪资源和 GUID -- `.gitignore` 已配置忽略 Library、Temp、Logs、UserSettings 等生成目录 -- 占位模型 (Capsule/Cube) 后续需替换为真实美术资源 -- 考核判分逻辑在 `GameManager.RecordExamAnswer()` 中实现 -- 步骤配置通过 `Resources/Data/*.json` 管理,可在 Unity 外编辑 +- Unity 2021.3+ +- TextMeshPro Essentials +- 无第三方插件