├── LICENSE.meta ├── README.md ├── README.md.meta ├── Resources.meta ├── Resources ├── DefaultLineMaterial.mat ├── DefaultLineMaterial.mat.meta ├── DefaultLineRenderer.prefab ├── DefaultLineRenderer.prefab.meta ├── Rope_albedo.png ├── Rope_albedo.png.meta ├── Rope_normal.png └── Rope_normal.png.meta ├── Runtime.meta ├── Runtime ├── Scripts.meta ├── Scripts │ ├── Rope Actors.meta │ ├── Rope Actors │ │ ├── RopeActorAttachments.cs │ │ ├── RopeActorAttachments.cs.meta │ │ ├── RopeActorBase.cs │ │ ├── RopeActorBase.cs.meta │ │ ├── RopeActorDebug.cs │ │ ├── RopeActorDebug.cs.meta │ │ ├── RopeActorForces.cs │ │ ├── RopeActorForces.cs.meta │ │ ├── RopeActorLength.cs │ │ ├── RopeActorLength.cs.meta │ │ ├── RopeActorLineRenderer.cs │ │ ├── RopeActorLineRenderer.cs.meta │ │ ├── RopeActorMotion.cs │ │ ├── RopeActorMotion.cs.meta │ │ ├── RopeActorRigidbody.cs │ │ ├── RopeActorRigidbody.cs.meta │ │ ├── RopeActorTension.cs │ │ ├── RopeActorTension.cs.meta │ │ ├── RopeActorTrigger.cs │ │ └── RopeActorTrigger.cs.meta │ ├── Rope Builders.meta │ ├── Rope Builders │ │ ├── RopeBuilder.cs │ │ └── RopeBuilder.cs.meta │ ├── Rope Components.meta │ ├── Rope Components │ │ ├── RopeComponentAttachments.cs │ │ ├── RopeComponentAttachments.cs.meta │ │ ├── RopeComponentBase.cs │ │ ├── RopeComponentBase.cs.meta │ │ ├── RopeComponentDebug.cs │ │ ├── RopeComponentDebug.cs.meta │ │ ├── RopeComponentForces.cs │ │ ├── RopeComponentForces.cs.meta │ │ ├── RopeComponentLength.cs │ │ ├── RopeComponentLength.cs.meta │ │ ├── RopeComponentLineRenderer.cs │ │ ├── RopeComponentLineRenderer.cs.meta │ │ ├── RopeComponentMotion.cs │ │ ├── RopeComponentMotion.cs.meta │ │ ├── RopeComponentRigidbody.cs │ │ ├── RopeComponentRigidbody.cs.meta │ │ ├── RopeComponentTension.cs │ │ ├── RopeComponentTension.cs.meta │ │ ├── RopeComponentTrigger.cs │ │ └── RopeComponentTrigger.cs.meta │ ├── Rope Helper.meta │ ├── Rope Helper │ │ ├── RopeCollisionMatrixLayerMask.cs │ │ └── RopeCollisionMatrixLayerMask.cs.meta │ ├── Rope Members.meta │ ├── Rope Members │ │ ├── RopeActionExecution.cs │ │ ├── RopeActionExecution.cs.meta │ │ ├── RopeAttachment.cs │ │ ├── RopeAttachment.cs.meta │ │ ├── RopeBody.cs │ │ ├── RopeBody.cs.meta │ │ ├── RopeCollisionEvents.cs │ │ ├── RopeCollisionEvents.cs.meta │ │ ├── RopeContact.cs │ │ ├── RopeContact.cs.meta │ │ ├── RopeDelegates.cs │ │ ├── RopeDelegates.cs.meta │ │ ├── RopeUpdater.cs │ │ └── RopeUpdater.cs.meta │ ├── Rope Simulators.meta │ ├── Rope Simulators │ │ ├── RopeSimulatorAttachments.cs │ │ ├── RopeSimulatorAttachments.cs.meta │ │ ├── RopeSimulatorDebug.cs │ │ ├── RopeSimulatorDebug.cs.meta │ │ ├── RopeSimulatorForces.cs │ │ ├── RopeSimulatorForces.cs.meta │ │ ├── RopeSimulatorLength.cs │ │ ├── RopeSimulatorLength.cs.meta │ │ ├── RopeSimulatorLineRenderer.cs │ │ ├── RopeSimulatorLineRenderer.cs.meta │ │ ├── RopeSimulatorMotion.cs │ │ ├── RopeSimulatorMotion.cs.meta │ │ ├── RopeSimulatorRigidbody.cs │ │ ├── RopeSimulatorRigidbody.cs.meta │ │ ├── RopeSimulatorTension.cs │ │ ├── RopeSimulatorTension.cs.meta │ │ ├── RopeSimulatorTrigger.cs │ │ └── RopeSimulatorTrigger.cs.meta │ ├── Rope.meta │ └── Rope │ │ ├── Rope.cs │ │ ├── Rope.cs.meta │ │ ├── RopeGameObject.cs │ │ └── RopeGameObject.cs.meta ├── weston-wright.rope-simulation.runtime.asmdef └── weston-wright.rope-simulation.runtime.asmdef.meta ├── license ├── package.json └── package.json.meta /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4d231676f4feb4445ab8438f956b8bbc 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 3D Rope Simulation for Unity 2 | This package contains some tools for setting up basic rope simulations with various features. Ropes can recieve forces, collide with the environment, and apply forces to rigidbodies in the scene. 3 | 4 | ![Rope Demo](https://user-images.githubusercontent.com/57042424/189775928-d6ddbd35-8757-41df-b5c1-ef6e4f6be1cc.gif) 5 | 6 | ## Check the [Wiki](https://github.com/westonwright/texturing-tools-unity/wiki)! 7 | Theres a lot of information there like: 8 | * [Features](https://github.com/westonwright/rope-simulation-unity/wiki/Features) 9 | * [How It Works](https://github.com/westonwright/rope-simulation-unity/wiki/How-It-Works) 10 | 11 | ## Supported Versions 12 | Unity 2020 and up 13 | 14 | ## Installation 15 | To install this package, add [package](https://github.com/westonwright/rope-simulation-unity.git) from git URL in Unity's package manager 16 | 17 | ## Setup 18 | 19 | ## Follow My Stuff :) 20 | Hey, I'm Weston! Thanks for checking out one of my projects! 21 | 22 | * [**twitter**](https://twitter.com/WestonWright_): twitter shenanigans! 23 | 24 | * [**github**](https://github.com/westonwright): more packages and projects! 25 | 26 | * [**itch.io**](https://westonwright.itch.io/): games I've made! 27 | 28 | ## Contributing 29 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 30 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 27820684d07f3d84790175430b15aea7 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 32091ef7e66f71c4abf397b1303defc6 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/DefaultLineMaterial.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 8 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: DefaultLineMaterial 11 | m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} 12 | m_ValidKeywords: 13 | - _ALPHATEST_ON 14 | - _NORMALMAP 15 | m_InvalidKeywords: [] 16 | m_LightmapFlags: 4 17 | m_EnableInstancingVariants: 0 18 | m_DoubleSidedGI: 0 19 | m_CustomRenderQueue: 2450 20 | stringTagMap: 21 | RenderType: TransparentCutout 22 | disabledShaderPasses: [] 23 | m_SavedProperties: 24 | serializedVersion: 3 25 | m_TexEnvs: 26 | - _BumpMap: 27 | m_Texture: {fileID: 2800000, guid: d8d22205030ac2b46a0a8e44f170a478, type: 3} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailAlbedoMap: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailMask: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _DetailNormalMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _EmissionMap: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 0.25, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MainTex: 47 | m_Texture: {fileID: 2800000, guid: d2a401cd2914da545a4135e86ce9dfac, type: 3} 48 | m_Scale: {x: 0.25, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _MetallicGlossMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _OcclusionMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | - _ParallaxMap: 59 | m_Texture: {fileID: 0} 60 | m_Scale: {x: 1, y: 1} 61 | m_Offset: {x: 0, y: 0} 62 | m_Ints: [] 63 | m_Floats: 64 | - _BumpScale: 1 65 | - _Cutoff: 0.5 66 | - _DetailNormalMapScale: 1 67 | - _DstBlend: 0 68 | - _GlossMapScale: 1 69 | - _Glossiness: 0 70 | - _GlossyReflections: 1 71 | - _Metallic: 0 72 | - _Mode: 1 73 | - _OcclusionStrength: 1 74 | - _Parallax: 0.02 75 | - _SmoothnessTextureChannel: 0 76 | - _SpecularHighlights: 1 77 | - _SrcBlend: 1 78 | - _UVSec: 0 79 | - _ZWrite: 1 80 | m_Colors: 81 | - _Color: {r: 1, g: 1, b: 1, a: 1} 82 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 83 | m_BuildTextureStacks: [] 84 | -------------------------------------------------------------------------------- /Resources/DefaultLineMaterial.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 440b0dfb234cc7a4987b4083bbaf6821 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 2100000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/DefaultLineRenderer.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &1075378164419489765 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 5270091057921862681} 12 | - component: {fileID: 5473742686985714981} 13 | m_Layer: 0 14 | m_Name: DefaultLineRenderer 15 | m_TagString: Untagged 16 | m_Icon: {fileID: 0} 17 | m_NavMeshLayer: 0 18 | m_StaticEditorFlags: 0 19 | m_IsActive: 1 20 | --- !u!4 &5270091057921862681 21 | Transform: 22 | m_ObjectHideFlags: 0 23 | m_CorrespondingSourceObject: {fileID: 0} 24 | m_PrefabInstance: {fileID: 0} 25 | m_PrefabAsset: {fileID: 0} 26 | m_GameObject: {fileID: 1075378164419489765} 27 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 28 | m_LocalPosition: {x: 6.1085954, y: 0.1657548, z: 26.702343} 29 | m_LocalScale: {x: 1, y: 1, z: 1} 30 | m_ConstrainProportionsScale: 0 31 | m_Children: [] 32 | m_Father: {fileID: 0} 33 | m_RootOrder: 0 34 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 35 | --- !u!120 &5473742686985714981 36 | LineRenderer: 37 | m_ObjectHideFlags: 0 38 | m_CorrespondingSourceObject: {fileID: 0} 39 | m_PrefabInstance: {fileID: 0} 40 | m_PrefabAsset: {fileID: 0} 41 | m_GameObject: {fileID: 1075378164419489765} 42 | m_Enabled: 1 43 | m_CastShadows: 0 44 | m_ReceiveShadows: 1 45 | m_DynamicOccludee: 1 46 | m_StaticShadowCaster: 0 47 | m_MotionVectors: 0 48 | m_LightProbeUsage: 0 49 | m_ReflectionProbeUsage: 0 50 | m_RayTracingMode: 0 51 | m_RayTraceProcedural: 0 52 | m_RenderingLayerMask: 1 53 | m_RendererPriority: 0 54 | m_Materials: [] 55 | m_StaticBatchInfo: 56 | firstSubMesh: 0 57 | subMeshCount: 0 58 | m_StaticBatchRoot: {fileID: 0} 59 | m_ProbeAnchor: {fileID: 0} 60 | m_LightProbeVolumeOverride: {fileID: 0} 61 | m_ScaleInLightmap: 1 62 | m_ReceiveGI: 1 63 | m_PreserveUVs: 0 64 | m_IgnoreNormalsForChartDetection: 0 65 | m_ImportantGI: 0 66 | m_StitchLightmapSeams: 1 67 | m_SelectedEditorRenderState: 3 68 | m_MinimumChartSize: 4 69 | m_AutoUVMaxDistance: 0.5 70 | m_AutoUVMaxAngle: 89 71 | m_LightmapParameters: {fileID: 0} 72 | m_SortingLayerID: 0 73 | m_SortingLayer: 0 74 | m_SortingOrder: 0 75 | m_Positions: 76 | - {x: 0, y: 0, z: 0} 77 | - {x: 0, y: 0, z: 1} 78 | m_Parameters: 79 | serializedVersion: 3 80 | widthMultiplier: 0.5 81 | widthCurve: 82 | serializedVersion: 2 83 | m_Curve: 84 | - serializedVersion: 3 85 | time: 0 86 | value: 1 87 | inSlope: 0 88 | outSlope: 0 89 | tangentMode: 0 90 | weightedMode: 0 91 | inWeight: 0.33333334 92 | outWeight: 0.33333334 93 | m_PreInfinity: 2 94 | m_PostInfinity: 2 95 | m_RotationOrder: 4 96 | colorGradient: 97 | serializedVersion: 2 98 | key0: {r: 1, g: 1, b: 1, a: 1} 99 | key1: {r: 1, g: 1, b: 1, a: 1} 100 | key2: {r: 0, g: 0, b: 0, a: 0} 101 | key3: {r: 0, g: 0, b: 0, a: 0} 102 | key4: {r: 0, g: 0, b: 0, a: 0} 103 | key5: {r: 0, g: 0, b: 0, a: 0} 104 | key6: {r: 0, g: 0, b: 0, a: 0} 105 | key7: {r: 0, g: 0, b: 0, a: 0} 106 | ctime0: 0 107 | ctime1: 65535 108 | ctime2: 0 109 | ctime3: 0 110 | ctime4: 0 111 | ctime5: 0 112 | ctime6: 0 113 | ctime7: 0 114 | atime0: 0 115 | atime1: 65535 116 | atime2: 0 117 | atime3: 0 118 | atime4: 0 119 | atime5: 0 120 | atime6: 0 121 | atime7: 0 122 | m_Mode: 0 123 | m_NumColorKeys: 2 124 | m_NumAlphaKeys: 2 125 | numCornerVertices: 0 126 | numCapVertices: 0 127 | alignment: 0 128 | textureMode: 3 129 | shadowBias: 0.5 130 | generateLightingData: 1 131 | m_UseWorldSpace: 1 132 | m_Loop: 0 133 | -------------------------------------------------------------------------------- /Resources/DefaultLineRenderer.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8570021eb6bae69418f3857b7ee64860 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/Rope_albedo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westonwright/rope-simulation-unity/b0856438516abd42f315ea305eb88d9b0a878313/Resources/Rope_albedo.png -------------------------------------------------------------------------------- /Resources/Rope_albedo.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d2a401cd2914da545a4135e86ce9dfac 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | platformSettings: 67 | - serializedVersion: 3 68 | buildTarget: DefaultTexturePlatform 69 | maxTextureSize: 2048 70 | resizeAlgorithm: 0 71 | textureFormat: -1 72 | textureCompression: 1 73 | compressionQuality: 50 74 | crunchedCompression: 0 75 | allowsAlphaSplitting: 0 76 | overridden: 0 77 | androidETC2FallbackOverride: 0 78 | forceMaximumCompressionQuality_BC6H_BC7: 0 79 | spriteSheet: 80 | serializedVersion: 2 81 | sprites: [] 82 | outline: [] 83 | physicsShape: [] 84 | bones: [] 85 | spriteID: 86 | internalID: 0 87 | vertices: [] 88 | indices: 89 | edges: [] 90 | weights: [] 91 | secondaryTextures: [] 92 | nameFileIdTable: {} 93 | spritePackingTag: 94 | pSDRemoveMatte: 0 95 | pSDShowRemoveMatteOption: 0 96 | userData: 97 | assetBundleName: 98 | assetBundleVariant: 99 | -------------------------------------------------------------------------------- /Resources/Rope_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westonwright/rope-simulation-unity/b0856438516abd42f315ea305eb88d9b0a878313/Resources/Rope_normal.png -------------------------------------------------------------------------------- /Resources/Rope_normal.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d8d22205030ac2b46a0a8e44f170a478 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 0 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 1 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | platformSettings: 67 | - serializedVersion: 3 68 | buildTarget: DefaultTexturePlatform 69 | maxTextureSize: 2048 70 | resizeAlgorithm: 0 71 | textureFormat: -1 72 | textureCompression: 1 73 | compressionQuality: 50 74 | crunchedCompression: 0 75 | allowsAlphaSplitting: 0 76 | overridden: 0 77 | androidETC2FallbackOverride: 0 78 | forceMaximumCompressionQuality_BC6H_BC7: 0 79 | - serializedVersion: 3 80 | buildTarget: Standalone 81 | maxTextureSize: 2048 82 | resizeAlgorithm: 0 83 | textureFormat: -1 84 | textureCompression: 1 85 | compressionQuality: 50 86 | crunchedCompression: 0 87 | allowsAlphaSplitting: 0 88 | overridden: 0 89 | androidETC2FallbackOverride: 0 90 | forceMaximumCompressionQuality_BC6H_BC7: 0 91 | - serializedVersion: 3 92 | buildTarget: Server 93 | maxTextureSize: 2048 94 | resizeAlgorithm: 0 95 | textureFormat: -1 96 | textureCompression: 1 97 | compressionQuality: 50 98 | crunchedCompression: 0 99 | allowsAlphaSplitting: 0 100 | overridden: 0 101 | androidETC2FallbackOverride: 0 102 | forceMaximumCompressionQuality_BC6H_BC7: 0 103 | spriteSheet: 104 | serializedVersion: 2 105 | sprites: [] 106 | outline: [] 107 | physicsShape: [] 108 | bones: [] 109 | spriteID: 110 | internalID: 0 111 | vertices: [] 112 | indices: 113 | edges: [] 114 | weights: [] 115 | secondaryTextures: [] 116 | nameFileIdTable: {} 117 | spritePackingTag: 118 | pSDRemoveMatte: 0 119 | pSDShowRemoveMatteOption: 0 120 | userData: 121 | assetBundleName: 122 | assetBundleVariant: 123 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 78c2252b96aff3044a97a9b7c44a421a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b7256e5a5abdd94d9cd037e1824b799 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9f138c916f360c54bb155e162686f8ce 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorAttachments.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeActorAttachments : RopeActorBase 6 | { 7 | private RopeSimulatorAttachments ropeSimulatorAttachments; 8 | public RopeSimulatorAttachments RopeSimulatorAttachments { get { return ropeSimulatorAttachments; } } 9 | 10 | 11 | public RopeActorAttachments( 12 | Rope rope, 13 | IEnumerable initialAttachments 14 | ) : base(rope) 15 | { 16 | ropeSimulatorAttachments = new RopeSimulatorAttachments(rope.RopeBody, initialAttachments); 17 | actionExecutions.Add(new RopeActionExecution(ExecutionOrder, MovePointsToAttachments)); 18 | } 19 | public override int ExecutionOrder { get { return -15; } } 20 | 21 | public void MovePointsToAttachments() 22 | { 23 | ropeSimulatorAttachments.MovePointsToAttachments(); 24 | } 25 | 26 | public void RemoveAttachment(int index) 27 | { 28 | ropeSimulatorAttachments.RemoveAttachment(index); 29 | } 30 | 31 | public void AddAttachment(RopeAttachment attachment) 32 | { 33 | ropeSimulatorAttachments.AddAttachment(attachment); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorAttachments.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3a82cb6ddd52b9e409ab56b4850948e3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System; 4 | 5 | public abstract class RopeActorBase 6 | { 7 | private Rope rope; 8 | public Rope Rope { get { return rope; } } 9 | 10 | public abstract int ExecutionOrder { get; } 11 | 12 | protected List actionExecutions = new List(); 13 | 14 | public RopeActorBase(Rope rope) 15 | { 16 | this.rope = rope; 17 | } 18 | 19 | public virtual void EnableActor() 20 | { 21 | foreach(RopeActionExecution actionExecution in actionExecutions) 22 | { 23 | rope.RopeUpdater.AddActionExecution(actionExecution); 24 | } 25 | } 26 | 27 | public virtual void DisableActor() 28 | { 29 | foreach (RopeActionExecution actionExecution in actionExecutions) 30 | { 31 | rope.RopeUpdater.RemoveActionExecution(actionExecution); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7a843aa18472a4441be382d965c096ba 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorDebug.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeActorDebug : RopeActorBase 6 | { 7 | private RopeSimulatorDebug ropeSimulatorDebug; 8 | public RopeSimulatorDebug RopeSimulatorDebug { get { return ropeSimulatorDebug; } } 9 | 10 | private RopeActorLength ropeActorLength; 11 | public RopeActorLength RopeActorLength { get { return ropeActorLength; } } 12 | 13 | public RopeActorDebug( 14 | Rope rope, 15 | RopeActorLength ropeActorLength, 16 | Color lineColor 17 | ) 18 | : base(rope) 19 | { 20 | this.ropeActorLength = ropeActorLength; 21 | this.ropeSimulatorDebug = new RopeSimulatorDebug( 22 | lineColor 23 | ); 24 | 25 | actionExecutions.Add(new RopeActionExecution(ExecutionOrder, DrawLines)); 26 | } 27 | 28 | public override int ExecutionOrder { get { return int.MaxValue; } } 29 | 30 | public Color LineColor { get => ropeSimulatorDebug.LineColor; set => ropeSimulatorDebug.LineColor = value; } 31 | 32 | private void DrawLines() 33 | { 34 | ropeSimulatorDebug.DrawLines(ropeActorLength.RopeSimulatorLength); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorDebug.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cedf13ede0bb975458bb03883519d067 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorForces.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeActorForces : RopeActorBase 6 | { 7 | private RopeSimulatorForces ropeSimulatorForces; 8 | public RopeSimulatorForces RopeSimulatorForces { get { return ropeSimulatorForces; } } 9 | 10 | private RopeActorMotion ropeActorMotion; 11 | public RopeActorMotion RopeActorMotion { get { return ropeActorMotion; } } 12 | 13 | public RopeActorForces( 14 | Rope rope, 15 | float pointMass 16 | ) 17 | : base(rope) 18 | { 19 | this.ropeSimulatorForces = new RopeSimulatorForces( 20 | pointMass 21 | ); 22 | } 23 | 24 | public override int ExecutionOrder { get { return -10; } } 25 | 26 | public float PointMass { get => ropeSimulatorForces.PointMass; set => ropeSimulatorForces.PointMass = value; } 27 | 28 | // could accumulate forces over a frame then apply them but not doing that yet 29 | public void ApplyForce(Vector3 forceVector, ForceMode forceMode, float timeStep) => 30 | ropeSimulatorForces.AccumulateForce(forceVector, forceMode, timeStep, Rope.RopeBody); 31 | 32 | public void ApplyForce(Vector3 forceVector, ForceMode forceMode, float timeStep, RopePoint point) => 33 | ropeSimulatorForces.AccumulateForce(forceVector, forceMode, timeStep, point); 34 | } 35 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorForces.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cd5ccfef6434db344b9d20d0e8d771ee 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorLength.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeActorLength : RopeActorBase 6 | { 7 | private RopeSimulatorLength ropeSimulatorLength; 8 | public RopeSimulatorLength RopeSimulatorLength { get { return ropeSimulatorLength; } } 9 | 10 | public RopeActorLength( 11 | Rope rope, 12 | float goalLength = 5, 13 | float maxRopeLength = 10, 14 | float minRopeLength = .1f, 15 | float segmentLength = .5f 16 | ) : base(rope) 17 | { 18 | ropeSimulatorLength = new RopeSimulatorLength( 19 | rope.RopeBody, 20 | goalLength, 21 | maxRopeLength, 22 | minRopeLength, 23 | segmentLength 24 | ); 25 | 26 | actionExecutions.Add(new RopeActionExecution(ExecutionOrder, ApplyLength)); 27 | } 28 | 29 | public override int ExecutionOrder { get { return -20; } } 30 | 31 | public float GoalLength { set => ropeSimulatorLength.GoalLength = value; } 32 | 33 | public float CurrentLength { get => ropeSimulatorLength.CurrentLength; } 34 | 35 | public float MaxRopeLength { get => ropeSimulatorLength.MaxRopeLength; set => ropeSimulatorLength.MaxRopeLength = value; } 36 | 37 | public float MinRopeLength { get => ropeSimulatorLength.MinRopeLength; set => ropeSimulatorLength.MinRopeLength = value; } 38 | 39 | public float SegmentLength { get => ropeSimulatorLength.SegmentLength; set => ropeSimulatorLength.SegmentLength = value; } 40 | 41 | public float PercentReleased { get => ropeSimulatorLength.PercentReleased; set => ropeSimulatorLength.PercentReleased = value; } 42 | 43 | private void ApplyLength() 44 | { 45 | ropeSimulatorLength.ApplyLength(); 46 | } 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorLength.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f47bf4a6e91931349ba0b930436497e5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorLineRenderer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeActorLineRenderer : RopeActorBase 6 | { 7 | private RopeSimulatorLineRenderer ropeSimulatorLineRenderer; 8 | public RopeSimulatorLineRenderer RopeSimulatorLineRenderer { get { return ropeSimulatorLineRenderer; } } 9 | 10 | public RopeActorLineRenderer( 11 | Rope rope, 12 | LineRenderer lineRenderer, 13 | float smoothingResolution 14 | ) : base(rope) 15 | { 16 | ropeSimulatorLineRenderer = new RopeSimulatorLineRenderer( 17 | lineRenderer, 18 | smoothingResolution 19 | ); 20 | 21 | actionExecutions.Add(new RopeActionExecution(ExecutionOrder, UpdateLineRenderer)); 22 | } 23 | 24 | public override int ExecutionOrder { get { return int.MaxValue; } } 25 | 26 | public float SmoothingResolution { get => ropeSimulatorLineRenderer.SmoothingResolution; set => ropeSimulatorLineRenderer.SmoothingResolution = value; } 27 | 28 | private void UpdateLineRenderer() 29 | { 30 | ropeSimulatorLineRenderer.UpdateLineRenderer(Rope.RopeBody); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorLineRenderer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0684caacf8bc2254580599f2e35d1e7e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorMotion.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeActorMotion : RopeActorBase 6 | { 7 | private RopeSimulatorMotion ropeSimulatorMotion; 8 | public RopeSimulatorMotion RopeSimulatorMotion { get { return ropeSimulatorMotion; } } 9 | 10 | private RopeActorLength ropeActorLength; 11 | public RopeActorLength RopeActorLength { get { return ropeActorLength; } } 12 | 13 | public RopeActorMotion( 14 | Rope rope, 15 | RopeActorLength ropeActorLength, 16 | int movementIterations, 17 | float maximumAngle, 18 | float ropeDrag 19 | ) 20 | : base(rope) 21 | { 22 | this.ropeSimulatorMotion = new RopeSimulatorMotion( 23 | movementIterations, 24 | maximumAngle, 25 | ropeDrag 26 | ); 27 | 28 | this.ropeActorLength = ropeActorLength; 29 | actionExecutions.Add(new RopeActionExecution(ExecutionOrder, UpdateRope)); 30 | } 31 | public override int ExecutionOrder { get { return 0; } } 32 | 33 | public int MovementIterations { get => ropeSimulatorMotion.MovementIterations; set => ropeSimulatorMotion.MovementIterations = value; } 34 | public float MaximumAngle { get => ropeSimulatorMotion.MaximumAngle; set => ropeSimulatorMotion.MaximumAngle = value; } 35 | public float RopeDrag { get => ropeSimulatorMotion.RopeDrag; set => ropeSimulatorMotion.RopeDrag = value; } 36 | 37 | private void UpdateRope() 38 | { 39 | ropeSimulatorMotion.ApplyVelocity(Rope.RopeBody); 40 | ropeSimulatorMotion.ApplyMotion(ropeActorLength.RopeSimulatorLength); 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorMotion.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6af8b01435357fd41aa2ca425b6cb934 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorRigidbody.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeActorRigidbody : RopeActorBase 6 | { 7 | private RopeSimulatorRigidbody ropeSimulatorRigidbody; 8 | public RopeSimulatorRigidbody RopeSimulatorRigidbody { get { return ropeSimulatorRigidbody; } } 9 | 10 | private RopeActorMotion ropeActorMotion; 11 | public RopeActorMotion RopeActorMotion { get { return ropeActorMotion; } } 12 | 13 | private RopeActorAttachments ropeActorAttachments; 14 | public RopeActorAttachments RopeActorAttachments { get { return ropeActorAttachments; } } 15 | 16 | private List oldContacts = new List(); 17 | 18 | public RopeActorRigidbody( 19 | Rope rope, 20 | RopeActorMotion ropeActorMotion, 21 | RopeActorAttachments ropeActorAttachments, 22 | int collisionIterations 23 | ) : base(rope) 24 | { 25 | this.ropeActorMotion = ropeActorMotion; 26 | this.ropeActorAttachments = ropeActorAttachments; 27 | ropeSimulatorRigidbody = new RopeSimulatorRigidbody( 28 | this.ropeActorMotion.RopeSimulatorMotion, 29 | CalculateLayermask(), 30 | collisionIterations 31 | ); 32 | 33 | actionExecutions.Add(new RopeActionExecution(ExecutionOrder - 1, RefreshContacts)); 34 | actionExecutions.Add(new RopeActionExecution(ExecutionOrder + 1, SendCollisionEvents)); 35 | } 36 | 37 | public override int ExecutionOrder { get { return RopeActorMotion.ExecutionOrder; } } 38 | 39 | public int CollisionIterations { get => ropeSimulatorRigidbody.CollisionIterations; set => ropeSimulatorRigidbody.CollisionIterations = value; } 40 | 41 | 42 | // before motion 43 | private void RefreshContacts() 44 | { 45 | oldContacts = ropeSimulatorRigidbody.Contacts; 46 | ropeSimulatorRigidbody.RefreshContacts(ropeActorAttachments.RopeSimulatorAttachments, Rope.RopeBody.Count); 47 | } 48 | // after motion 49 | private void SendCollisionEvents() 50 | { 51 | List newContacts = ropeSimulatorRigidbody.Contacts; 52 | RefineContacts(oldContacts, out List oldC); 53 | RefineContacts(newContacts, out List newC); 54 | SendCollisionEvents(oldC, newC); 55 | // dont need to copy new list to old list because the componentRigidbody handles that 56 | } 57 | 58 | public void UpdateLayermask() 59 | { 60 | ropeSimulatorRigidbody.LayerMask = CalculateLayermask(); 61 | } 62 | 63 | private LayerMask CalculateLayermask() 64 | { 65 | return RopeCollisionMatrixLayerMask.MaskForLayer(Rope.RopeGameObject.gameObject.layer); 66 | } 67 | 68 | private void RefineContacts(List contacts, out List contactCollisions) 69 | { 70 | contactCollisions = new List(); 71 | foreach (RopeContact contact in contacts) 72 | { 73 | if (contact.GetType() == typeof(RopeContactCollision)) 74 | { 75 | contactCollisions.Add((contact as RopeContactCollision)); 76 | } 77 | } 78 | } 79 | private void SendCollisionEvents(List oldContacts, List newContacts) 80 | { 81 | foreach (RopeContactCollision c in newContacts) 82 | { 83 | if (c.Rigidbody == null) continue; 84 | if (oldContacts.Find(x => x.Rigidbody == c.Rigidbody) == null) 85 | { 86 | SendCollisionEnter(c); 87 | } 88 | else 89 | { 90 | SendCollisionStay(c); 91 | } 92 | } 93 | foreach (RopeContactCollision c in oldContacts) 94 | { 95 | if (c.Rigidbody == null) continue; 96 | if (newContacts.Find(x => x.Rigidbody == c.Rigidbody) == null) 97 | { 98 | SendCollisionExit(c); 99 | } 100 | } 101 | 102 | } 103 | 104 | private void SendCollisionEnter(RopeContactCollision contact) 105 | { 106 | if (contact.Rigidbody != null) 107 | { 108 | RopeCollisionEvent rc = new RopeCollisionEvent(Rope, contact); 109 | contact.Rigidbody.gameObject.SendMessage("OnRopeCollisionEnter", rc, SendMessageOptions.DontRequireReceiver); 110 | Rope.RopeGameObject.gameObject.SendMessage("OnRopeCollisionEnter", rc, SendMessageOptions.DontRequireReceiver); 111 | } 112 | } 113 | private void SendCollisionStay(RopeContactCollision contact) 114 | { 115 | if (contact.Rigidbody != null) 116 | { 117 | RopeCollisionEvent rc = new RopeCollisionEvent(Rope, contact); 118 | contact.Rigidbody.gameObject.SendMessage("OnRopeCollisionStay", rc, SendMessageOptions.DontRequireReceiver); 119 | Rope.RopeGameObject.gameObject.SendMessage("OnRopeCollisionStay", rc, SendMessageOptions.DontRequireReceiver); 120 | } 121 | } 122 | private void SendCollisionExit(RopeContactCollision contact) 123 | { 124 | if (contact.Rigidbody != null) 125 | { 126 | RopeCollisionEvent rc = new RopeCollisionEvent(Rope, contact); 127 | contact.Rigidbody.gameObject.SendMessage("OnRopeCollisionExit", rc, SendMessageOptions.DontRequireReceiver); 128 | Rope.RopeGameObject.gameObject.SendMessage("OnRopeCollisionExit", rc, SendMessageOptions.DontRequireReceiver); 129 | } 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorRigidbody.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 040a638f500290645aad800b008757bb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorTension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeActorTension : RopeActorBase 6 | { 7 | private RopeSimulatorTension ropeSimulatorTension; 8 | public RopeSimulatorTension RopeSimulatorTension { get { return ropeSimulatorTension; } } 9 | 10 | private RopeActorRigidbody ropeActorRigidbody; 11 | public RopeActorRigidbody RopeActorRigidbody { get { return ropeActorRigidbody; } } 12 | 13 | 14 | public RopeActorTension( 15 | Rope rope, 16 | RopeActorRigidbody ropeActorRigidbody, 17 | float thresholdTendsion, 18 | float springStrength, 19 | float dampingStrength 20 | ) : base(rope) 21 | { 22 | this.ropeActorRigidbody = ropeActorRigidbody; 23 | this.ropeSimulatorTension = new RopeSimulatorTension( 24 | this.ropeActorRigidbody.RopeSimulatorRigidbody, 25 | thresholdTendsion, 26 | springStrength, 27 | dampingStrength 28 | ); 29 | 30 | actionExecutions.Add(new RopeActionExecution(ExecutionOrder, ExertForce)); 31 | } 32 | 33 | public override int ExecutionOrder { get { return ropeActorRigidbody.ExecutionOrder + 2; } } 34 | 35 | public float ThresholdTension { get => ropeSimulatorTension.ThresholdTension; set => ropeSimulatorTension.ThresholdTension = value; } 36 | public float SpringStrength { get => ropeSimulatorTension.SpringStrength; set => ropeSimulatorTension.SpringStrength = value; } 37 | public float DampingStrength { get => ropeSimulatorTension.DampingStrength; set => ropeSimulatorTension.DampingStrength = value; } 38 | 39 | 40 | private void ExertForce() 41 | { 42 | ropeSimulatorTension.ExertForces(RopeActorRigidbody.RopeActorMotion.RopeActorLength.RopeSimulatorLength, Rope.RopeUpdater.CurrentTimeStep); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorTension.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cda964ddaea3036439f190af9fa82bb4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorTrigger.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | public class RopeActorTrigger : RopeActorBase 6 | { 7 | private RopeSimulatorTrigger ropeSimulatorTrigger; 8 | public RopeSimulatorTrigger RopeSimulatorTrigger { get { return ropeSimulatorTrigger; } } 9 | 10 | private RopeActorLength ropeActorLength; 11 | public RopeActorLength RopeActorLength { get { return ropeActorLength; } } 12 | 13 | List oldContacts; 14 | List newContacts; 15 | 16 | public RopeActorTrigger( 17 | Rope rope, 18 | RopeActorLength ropeActorLength, 19 | bool triggerEnabled 20 | ) : base(rope) 21 | { 22 | this.ropeActorLength = ropeActorLength; 23 | this.ropeSimulatorTrigger = new RopeSimulatorTrigger( 24 | CalculateLayermask(), 25 | triggerEnabled 26 | ); 27 | 28 | actionExecutions.Add(new RopeActionExecution(ExecutionOrder, DetectTrigger)); 29 | } 30 | 31 | public bool TriggerEnabled { set => ropeSimulatorTrigger.TriggerEnabled = value; } 32 | 33 | public override int ExecutionOrder { get { return 2; } } 34 | 35 | public void UpdateLayermask() 36 | { 37 | ropeSimulatorTrigger.LayerMask = CalculateLayermask(); 38 | } 39 | 40 | private LayerMask CalculateLayermask() 41 | { 42 | return RopeCollisionMatrixLayerMask.MaskForLayer(Rope.RopeGameObject.gameObject.layer); 43 | } 44 | 45 | /// 46 | /// call this every fixed update 47 | /// 48 | private void DetectTrigger() 49 | { 50 | oldContacts = newContacts.ToList(); 51 | newContacts = ropeSimulatorTrigger.GatherContacts(ropeActorLength.RopeSimulatorLength); 52 | SendCollisionEvents(); 53 | } 54 | 55 | private void SendCollisionEvents() 56 | { 57 | foreach (RopeContactTrigger c in newContacts) 58 | { 59 | if (c.Rigidbody == null) continue; 60 | if (oldContacts.Find(x => x.Rigidbody == c.Rigidbody) == null) 61 | { 62 | SendTriggerEnter(c); 63 | } 64 | else 65 | { 66 | SendTriggerStay(c); 67 | } 68 | } 69 | foreach (RopeContactTrigger c in oldContacts) 70 | { 71 | if (c.Rigidbody == null) continue; 72 | if (newContacts.Find(x => x.Rigidbody == c.Rigidbody) == null) 73 | { 74 | SendTriggerExit(c); 75 | } 76 | } 77 | 78 | } 79 | private void SendTriggerEnter(RopeContactTrigger contact) 80 | { 81 | RopeTriggerEvent triggerEvent = new RopeTriggerEvent(Rope, contact); 82 | if (triggerEvent.Contact.Rigidbody != null) 83 | { 84 | triggerEvent.Contact.Rigidbody.gameObject.SendMessage("OnRopeTriggerEnter", triggerEvent, SendMessageOptions.DontRequireReceiver); 85 | Rope.RopeGameObject.gameObject.SendMessage("OnRopeTriggerEnter", triggerEvent, SendMessageOptions.DontRequireReceiver); 86 | } 87 | } 88 | private void SendTriggerStay(RopeContactTrigger contact) 89 | { 90 | RopeTriggerEvent triggerEvent = new RopeTriggerEvent(Rope, contact); 91 | if (triggerEvent.Contact.Rigidbody != null) 92 | { 93 | triggerEvent.Contact.Rigidbody.gameObject.SendMessage("OnRopeTriggerStay", triggerEvent, SendMessageOptions.DontRequireReceiver); 94 | Rope.RopeGameObject.gameObject.SendMessage("OnRopeTriggerStay", triggerEvent, SendMessageOptions.DontRequireReceiver); 95 | } 96 | } 97 | private void SendTriggerExit(RopeContactTrigger contact) 98 | { 99 | RopeTriggerEvent triggerEvent = new RopeTriggerEvent(Rope, contact); 100 | if (triggerEvent.Contact.Rigidbody != null) 101 | { 102 | triggerEvent.Contact.Rigidbody.gameObject.SendMessage("OnRopeTriggerExit", triggerEvent, SendMessageOptions.DontRequireReceiver); 103 | Rope.RopeGameObject.gameObject.SendMessage("OnRopeTriggerExit", triggerEvent, SendMessageOptions.DontRequireReceiver); 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Actors/RopeActorTrigger.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e5ec147b85b30de4d8fe4627efedef7a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Builders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0a39360db08c06e478dfd6134a3ce754 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Builders/RopeBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public abstract class RopeBuilder 6 | { 7 | //BuildRope(Mathf.FloorToInt(totalLength / segmentLength), segmentLength); 8 | 9 | protected readonly Vector3 DEFAULT_DIRECTION = Vector3.down; 10 | protected readonly Vector3 DEFAULT_POSITION = Vector3.zero; 11 | 12 | public RopeBuilder(){} 13 | 14 | public abstract ListBuildRope(); 15 | 16 | /* 17 | RopeAttachment[] attachments = new RopeAttachment[attachmentBodies.Length]; 18 | for(int i = 0; i() != null) 21 | { 22 | attachments[i] = new RopeAttachmentRigidbody( 23 | attachmentIndexes[i], 24 | attachmentBodies[i].GetComponent().transform, 25 | Vector3.zero, 26 | attachmentBodies[i].GetComponent() 27 | );} 28 | } 29 | 30 | else 31 | { 32 | attachments[i] = new RopeAttachmentTransform( 33 | attachmentIndexes[i], 34 | attachmentBodies[i], 35 | Vector3.up 36 | ); 37 | } 38 | } 39 | */ 40 | } 41 | 42 | public class RopeBuilderAttachments : RopeBuilder 43 | { 44 | private RopeAttachment[] attachments; 45 | private int segments; 46 | public RopeBuilderAttachments( 47 | RopeAttachment[] attachments, 48 | int segments 49 | ) 50 | { 51 | this.attachments = attachments; 52 | this.segments = segments; 53 | } 54 | 55 | public override ListBuildRope() 56 | { 57 | List points = new List(); 58 | 59 | int attachmentIndex = 0; 60 | Vector3 previousPosition = DEFAULT_POSITION; 61 | Vector3 nextPosition = attachments.Length > 0 ? attachments[0].Position : DEFAULT_POSITION; 62 | int currentPoint = 0; 63 | int pointsInSection = 1; 64 | int previousPointIndex = 0; 65 | int nextPointIndex = attachments.Length > 0 ? attachments[0].CorrectedPointIndex(segments + 1) : 0; 66 | for (int i = 0; i < segments + 1; i++) 67 | { 68 | //Debug.Log("i: " + i); 69 | //Debug.Log("raw index: " + attachments[Mathf.Min(attachmentIndex, attachments.Count - 1)].RawPointIndex); 70 | //Debug.Log("corrected index: " + attachments[Mathf.Min(attachmentIndex, attachments.Count - 1)].CorrectedPointIndex(points.Count)); 71 | if (attachmentIndex + 1 > attachments.Length) 72 | { 73 | points.Add(new RopePoint(previousPosition + new Vector3(UnityEngine.Random.Range(0f, 1f), UnityEngine.Random.Range(0f, 1f), UnityEngine.Random.Range(0f, 1f)).normalized)); 74 | //Debug.Log("Add 1: " + previousPosition); 75 | continue; 76 | } 77 | if (nextPointIndex <= i) 78 | { 79 | previousPosition = nextPosition; 80 | 81 | attachmentIndex++; 82 | if (attachmentIndex + 1 > attachments.Length) 83 | { 84 | points.Add(new RopePoint(nextPosition)); 85 | //Debug.Log("Add 2: " + nextPosition); 86 | continue; 87 | } 88 | previousPointIndex = nextPointIndex; 89 | nextPointIndex = attachments[attachmentIndex].CorrectedPointIndex(segments + 1); 90 | 91 | nextPosition = attachments[attachmentIndex].Position; 92 | points.Add(new RopePoint(previousPosition)); 93 | //Debug.Log("Add 3: " + previousPosition); 94 | currentPoint = 1; 95 | 96 | pointsInSection = nextPointIndex - previousPointIndex; 97 | //Debug.Log("points in section: " + pointsInSection); 98 | continue; 99 | } 100 | //Debug.Log("prev: " + previousPosition + " next: " + nextPosition); 101 | //Debug.Log("Lerp: " + (float)currentPoint / (float)pointsInSection); 102 | points.Add(new RopePoint(Vector3.Lerp(previousPosition, nextPosition, (float)currentPoint / (float)pointsInSection))); 103 | //Debug.Log("Add 4: " + Vector3.Lerp(previousPosition, nextPosition, (float)currentPoint / (float)pointsInSection)); 104 | currentPoint++; 105 | } 106 | 107 | return points; 108 | } 109 | } 110 | 111 | 112 | public class RopeBuilderFromTo : RopeBuilder 113 | { 114 | private Vector3 positionA; 115 | private Vector3 positionB; 116 | private int points; 117 | 118 | public RopeBuilderFromTo( 119 | Vector3 positionA, 120 | Vector3 positionB, 121 | int points 122 | ) 123 | { 124 | this.positionA = positionA; 125 | this.positionB = positionB; 126 | this.points = points; 127 | } 128 | 129 | public override List BuildRope() 130 | { 131 | List pointsList = new List(); 132 | 133 | if(points == 1) 134 | { 135 | pointsList.Add(new RopePoint(positionA)); 136 | return pointsList; 137 | } 138 | for (int i = 0; i < points; i++) 139 | { 140 | pointsList.Add(new RopePoint( 141 | Vector3.Lerp(positionA, positionB, (float)i / points - 1) 142 | )); 143 | } 144 | return pointsList; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Builders/RopeBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 92ace684e15814346b929e950e67bcda 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a5f89659fe8372946bdbc05f90fb5fac 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentAttachments.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | 6 | public class RopeComponentAttachments : RopeComponentBase 7 | { 8 | [SerializeField] 9 | private Transform[] initialAttachmentBodies; 10 | [SerializeField] 11 | private int[] initialAttachmentIndexes; 12 | private RopeAttachment[] initialAttachments; 13 | public RopeAttachment[] InitialAttachments { 14 | get 15 | { 16 | if(initialAttachments == null) 17 | { 18 | List ropeAttachments = new List(); 19 | for (int i = 0; i < initialAttachmentBodies.Length; i++) 20 | { 21 | int attachIndex = -1; 22 | if (i < initialAttachmentIndexes.Length) 23 | { 24 | attachIndex = initialAttachmentIndexes[i]; 25 | } 26 | Rigidbody rb = initialAttachmentBodies[i].GetComponent(); 27 | RopeAttachment ropeAttachment = null; 28 | if (rb != null) 29 | { 30 | ropeAttachment = new RopeAttachmentRigidbody(attachIndex, initialAttachmentBodies[i], Vector3.up * .5f, rb); 31 | } 32 | else 33 | { 34 | ropeAttachment = new RopeAttachmentTransform(attachIndex, initialAttachmentBodies[i], Vector3.zero); 35 | } 36 | ropeAttachments.Add(ropeAttachment); 37 | } 38 | initialAttachments = ropeAttachments.ToArray(); 39 | } 40 | return initialAttachments; 41 | } 42 | } 43 | 44 | private RopeActorAttachments ropeActorAttachments; 45 | public override RopeActorBase RopeActor { get { return ropeActorAttachments; } } 46 | 47 | public override void Initialize(Rope rope, IEnumerable requiredActors) 48 | { 49 | ropeActorAttachments = new RopeActorAttachments( 50 | rope, 51 | InitialAttachments 52 | ); 53 | foreach (RopeAttachment ropeAttachment in initialAttachments) 54 | { 55 | ropeActorAttachments.AddAttachment(ropeAttachment); 56 | } 57 | ropeActorAttachments.EnableActor(); 58 | 59 | } 60 | 61 | public void RemoveAttachment(int index) 62 | { 63 | ropeActorAttachments.RemoveAttachment(index); 64 | } 65 | 66 | public void AddAttachment(int pointIndex, Vector3 position) 67 | { 68 | ropeActorAttachments.AddAttachment(new RopeAttachmentPoint(pointIndex, position)); 69 | } 70 | 71 | public void AddAttachment(int pointIndex, Transform transform, Vector3 offset) 72 | { 73 | ropeActorAttachments.AddAttachment(new RopeAttachmentTransform(pointIndex, transform, offset)); 74 | } 75 | 76 | public void AddAttachment(int pointIndex, Transform transform, Vector3 offset, Rigidbody rigidbody) 77 | { 78 | ropeActorAttachments.AddAttachment(new RopeAttachmentRigidbody(pointIndex, transform, offset, rigidbody)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentAttachments.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9d66d0f843945f54197ea355a3afb6bf 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System; 4 | using UnityEngine; 5 | 6 | [RequireComponent(typeof(RopeGameObject))] 7 | public abstract class RopeComponentBase : MonoBehaviour 8 | { 9 | protected RopeGameObject ropeGameObject; 10 | public RopeGameObject RopeGameObject { get { return ropeGameObject; } } 11 | 12 | public abstract RopeActorBase RopeActor { get; } 13 | 14 | //protected List incompatibleTypes = new List(); 15 | //public List IncompatibleTypes { get { return incompatibleTypes; } } 16 | 17 | protected List requiredActorTypes = new List(); 18 | public List RequiredActorTypes { get { return requiredActorTypes; } } 19 | 20 | protected virtual void Reset() 21 | { 22 | if(GetComponents(this.GetType()).Length > 1) 23 | { 24 | Debug.LogWarning("Don't add two of same type of Rope Components!"); 25 | Debug.LogWarning("Destroying duplicate Component", this.gameObject); 26 | DestroyImmediate(this); 27 | } 28 | } 29 | 30 | protected virtual void OnValidate() 31 | { 32 | 33 | } 34 | 35 | protected virtual void Start() 36 | { 37 | ropeGameObject = GetComponent(); 38 | ropeGameObject.RequestComponentInitialization(this); 39 | } 40 | 41 | public abstract void Initialize(Rope rope, IEnumerable requiredActors); 42 | 43 | protected virtual void OnDisable() 44 | { 45 | ropeGameObject.DisableComponent(this); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4aa2ed572fcbc5d4983fcbc8d8cc5d58 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentDebug.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | 6 | [RequireComponent(typeof(RopeGameObject), typeof(RopeComponentLength))] 7 | public class RopeComponentDebug : RopeComponentBase 8 | { 9 | [SerializeField] 10 | private Color lineColor = Color.red; 11 | 12 | private RopeActorDebug ropeActorDebug; 13 | public override RopeActorBase RopeActor { get { return ropeActorDebug; } } 14 | 15 | protected override void Start() 16 | { 17 | requiredActorTypes.Add(typeof(RopeActorLength)); 18 | base.Start(); 19 | } 20 | 21 | public override void Initialize(Rope rope, IEnumerable requiredActors) 22 | { 23 | RopeActorLength ropeActorLength = requiredActors.FirstOrDefault(x => x.GetType() == typeof(RopeActorLength)) as RopeActorLength; 24 | 25 | ropeActorDebug = new RopeActorDebug( 26 | rope, 27 | ropeActorLength, 28 | lineColor 29 | ); 30 | ropeActorDebug.EnableActor(); 31 | } 32 | 33 | public Color LineColor { get => ropeActorDebug.LineColor; set => ropeActorDebug.LineColor = value; } 34 | } 35 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentDebug.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d1790293303c2b64584c888ec48f54de 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentForces.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [RequireComponent(typeof(RopeGameObject), typeof(RopeComponentMotion))] 6 | public class RopeComponentForces : RopeComponentBase 7 | { 8 | [SerializeField] 9 | private float pointMass = .01f; 10 | 11 | private RopeActorForces ropeActorForces; 12 | public override RopeActorBase RopeActor { get { return ropeActorForces; } } 13 | 14 | protected override void Start() 15 | { 16 | requiredActorTypes.Add(typeof(RopeActorMotion)); 17 | base.Start(); 18 | } 19 | 20 | public override void Initialize(Rope rope, IEnumerable requiredActors) 21 | { 22 | // even though motion is required we dont actually use it so we can just ignore it 23 | ropeActorForces = new RopeActorForces( 24 | rope, 25 | pointMass 26 | ); 27 | ropeActorForces.EnableActor(); 28 | } 29 | 30 | protected override void OnValidate() 31 | { 32 | pointMass = Mathf.Max(0, pointMass); 33 | } 34 | 35 | public float PointMass { get => ropeActorForces.PointMass; set => ropeActorForces.PointMass = value; } 36 | 37 | public void ApplyForce(Vector3 forceVector, ForceMode forceMode, float timeStep) => 38 | ropeActorForces.ApplyForce(forceVector, forceMode, timeStep); 39 | 40 | public void ApplyForceInterpolated( 41 | int fromIndex, 42 | int toIndex, 43 | Vector3 fromVector, 44 | Vector3 toVector, 45 | ForceMode forceMode, 46 | float timeStep 47 | ) 48 | { 49 | fromIndex = Mathf.Clamp(fromIndex, 0, ropeActorForces.Rope.RopeBody.Count - 1); 50 | toIndex = Mathf.Clamp(toIndex, 0, ropeActorForces.Rope.RopeBody.Count - 1); 51 | 52 | if (toIndex == fromIndex) 53 | { 54 | ropeActorForces.ApplyForce(Vector3.Lerp(fromVector, toVector, .5f), forceMode, timeStep); 55 | return; 56 | } 57 | 58 | if (fromIndex > toIndex) 59 | { 60 | int tempIndex = fromIndex; 61 | fromIndex = toIndex; 62 | toIndex = tempIndex; 63 | Vector3 tempVector = fromVector; 64 | fromVector = toVector; 65 | toVector = tempVector; 66 | } 67 | 68 | int indexDifference = Mathf.Abs(fromIndex - toIndex); 69 | int count = 0; 70 | for (int i = fromIndex; i < toIndex + 1; i++) 71 | { 72 | ropeActorForces.ApplyForce( 73 | Vector3.Lerp(fromVector, toVector, (float)count / indexDifference), 74 | forceMode, 75 | timeStep, 76 | ropeActorForces.Rope.RopeBody[i] 77 | ); 78 | 79 | count++; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentForces.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 47eb1b09961555b4690ae9b13a371795 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentLength.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | /* 5 | public interface IRopeLength 6 | { 7 | /// 8 | /// The initial length of the rope 9 | /// 10 | float CurrentLength { get; set; } 11 | /// 12 | /// The maximum total length allowed for the rope 13 | /// 14 | float MaxRopeLength { get; set; } 15 | /// 16 | /// The minimum total length allowed for the rope 17 | /// 18 | float MinRopeLength { get; set; } 19 | /// 20 | /// How long a single segment or "stick" of rope is. aka: the distance between any two points 21 | /// 22 | float SegmentLength { get; set; } 23 | /// 24 | /// What percent of the total length of the rope is out 25 | /// 26 | float PercentReleased { get; set; } 27 | } 28 | */ 29 | [RequireComponent(typeof(RopeGameObject))] 30 | public class RopeComponentLength : RopeComponentBase 31 | { 32 | [SerializeField] 33 | private float goalLength = 5f; 34 | [SerializeField] 35 | private float maxRopeLength = 10f; 36 | [SerializeField] 37 | private float minRopeLength = .1f; 38 | [SerializeField] 39 | private float segmentLength = .2f; 40 | 41 | private RopeActorLength ropeActorLength; 42 | public override RopeActorBase RopeActor { get { return ropeActorLength; } } 43 | 44 | public override void Initialize(Rope rope, IEnumerable requiredActors) 45 | { 46 | ropeActorLength = new RopeActorLength( 47 | rope, 48 | goalLength, 49 | maxRopeLength, 50 | minRopeLength, 51 | segmentLength 52 | ); 53 | ropeActorLength.EnableActor(); 54 | } 55 | 56 | protected override void OnValidate() 57 | { 58 | goalLength = Mathf.Clamp(goalLength, minRopeLength, maxRopeLength); 59 | maxRopeLength = Mathf.Max(maxRopeLength, Mathf.Max(0, minRopeLength) + .1f); 60 | minRopeLength = Mathf.Min(Mathf.Max(minRopeLength, 0), maxRopeLength); 61 | } 62 | 63 | public float CurentLength { get => ropeActorLength.CurrentLength; } 64 | public float GoalLength { set => ropeActorLength.GoalLength = value; } 65 | public float PercentReleased { get => ropeActorLength.PercentReleased; set => ropeActorLength.PercentReleased = value; } 66 | public float MaxRopeLength { get => ropeActorLength.MaxRopeLength; set => ropeActorLength.MaxRopeLength = value; } 67 | public float MinRopeLength { get => ropeActorLength.MinRopeLength; set => ropeActorLength.MinRopeLength = value; } 68 | public float SegmentLength { get => ropeActorLength.SegmentLength; set => ropeActorLength.SegmentLength = value; } 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentLength.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 217c5f7e7e4ac744f9c833dcb5c041c1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentLineRenderer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [RequireComponent(typeof(RopeGameObject))] 6 | public class RopeComponentLineRenderer : RopeComponentBase 7 | { 8 | #region Constant properties 9 | const string _linePrefabName = "DefaultLineRenderer"; 10 | const string _lineMaterialName = "DefaultLineMaterial"; 11 | #endregion 12 | [SerializeField] 13 | private LineRenderer lineRendererPrefab; 14 | [SerializeField] 15 | private Material lineMaterial; 16 | [SerializeField] 17 | [Range(.05f, 1f)] 18 | [Tooltip("Lower is higher quality")] 19 | private float smoothingResolution = .5f; 20 | [SerializeField] 21 | [Tooltip("This is optional")] 22 | private Transform parentTransform = null; 23 | 24 | private LineRenderer lineRenderer; 25 | 26 | private RopeActorLineRenderer ropeActorLineRenderer; 27 | public override RopeActorBase RopeActor { get { return ropeActorLineRenderer; } } 28 | 29 | public override void Initialize(Rope rope, IEnumerable requiredActors) 30 | { 31 | lineRenderer = Instantiate(lineRendererPrefab, parentTransform ?? transform); 32 | lineRenderer.material = lineMaterial; 33 | ropeActorLineRenderer = new RopeActorLineRenderer( 34 | rope, 35 | lineRenderer, 36 | smoothingResolution 37 | ); 38 | ropeActorLineRenderer.EnableActor(); 39 | } 40 | 41 | protected override void Reset() 42 | { 43 | base.Reset(); 44 | lineRendererPrefab = Resources.Load(_linePrefabName); 45 | lineMaterial = Resources.Load(_lineMaterialName); 46 | // set default value for line renderer here! 47 | } 48 | 49 | public float SmoothingResolution { get => ropeActorLineRenderer.SmoothingResolution; set => ropeActorLineRenderer.SmoothingResolution = value; } 50 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentLineRenderer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 46965819e811f2b4bb73dfbb11bdde43 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentMotion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | /* 6 | public interface IRopeMotion 7 | { 8 | /// 9 | /// How many iterations the rope will calculate per frame for movement. 10 | /// Higher leads to smoother results and less stretch 11 | /// 12 | public int MovementIterations { get; set; } 13 | 14 | /// 15 | /// How far the rope is able to bend between segments in degrees 16 | /// 17 | public float MaximumAngle { get; set; } 18 | 19 | /// 20 | /// How much drag will be exerted on the rope while moving through the air 21 | /// Must be between 0 and 1 22 | /// 23 | public float RopeDrag { get; set; } 24 | 25 | /// 26 | /// The mass of a single segment on the rope. 27 | /// 28 | public float PointMass { get; set; } 29 | 30 | void UpdateRope(float timeStep); 31 | 32 | void ApplyForce(Vector3 forceVector, ForceMode forceMode, float timeStep); 33 | // different version for applying for to single points or interpolating force across points? 34 | } 35 | */ 36 | 37 | [RequireComponent(typeof(RopeGameObject), typeof(RopeComponentLength))] 38 | public class RopeComponentMotion : RopeComponentBase 39 | { 40 | [SerializeField] 41 | private int movementIterations = 10; 42 | [SerializeField] 43 | [Range(.1f, 359.9f)] 44 | private float maximumAngle = 60f; 45 | [SerializeField] 46 | [Range(0, 1)] 47 | private float ropeDrag = .01f; 48 | 49 | private RopeActorMotion ropeActorMotion; 50 | public override RopeActorBase RopeActor { get { return ropeActorMotion; } } 51 | 52 | protected override void Start() 53 | { 54 | requiredActorTypes.Add(typeof(RopeActorLength)); 55 | base.Start(); 56 | } 57 | 58 | public override void Initialize(Rope rope, IEnumerable requiredActors) 59 | { 60 | RopeActorLength ropeActorLength = requiredActors.FirstOrDefault(x => x.GetType() == typeof(RopeActorLength)) as RopeActorLength; 61 | ropeActorMotion = new RopeActorMotion( 62 | rope, 63 | ropeActorLength, 64 | movementIterations, 65 | maximumAngle, 66 | ropeDrag 67 | ); 68 | ropeActorMotion.EnableActor(); 69 | } 70 | 71 | protected override void OnValidate() 72 | { 73 | movementIterations = Mathf.Max(movementIterations, 1); 74 | } 75 | 76 | public int MovementIterations { get => ropeActorMotion.MovementIterations; set => ropeActorMotion.MovementIterations = value; } 77 | public float MaximumAngle { get => ropeActorMotion.MaximumAngle; set => ropeActorMotion.MaximumAngle = value; } 78 | public float RopeDrag { get => ropeActorMotion.RopeDrag; set => ropeActorMotion.RopeDrag = value; } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentMotion.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d599ce0fc72d8324d90084adc4a8e273 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentRigidbody.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | /* 6 | public interface IRopeRigidbody 7 | { 8 | 9 | public bool CollisionEnabled { get; set; } 10 | /// 11 | /// How many times collision will be checked for an optimal result during each movement iteration 12 | /// 13 | public int CollisionIterations { get; set; } 14 | 15 | /// 16 | /// call this if the game objects layer has changed or the collision matrix has changed 17 | /// 18 | public void UpdateLayermask(); 19 | } 20 | */ 21 | [RequireComponent(typeof(RopeGameObject), typeof(RopeComponentMotion), typeof(RopeComponentAttachments))] 22 | public class RopeComponentRigidbody : RopeComponentBase 23 | { 24 | [SerializeField] 25 | private int collisionIterations = 2; 26 | 27 | private RopeActorRigidbody ropeActorRigidbody; 28 | public override RopeActorBase RopeActor { get { return ropeActorRigidbody; } } 29 | 30 | protected override void Start() 31 | { 32 | requiredActorTypes.Add(typeof(RopeActorMotion)); 33 | requiredActorTypes.Add(typeof(RopeActorAttachments)); 34 | base.Start(); 35 | } 36 | 37 | public override void Initialize(Rope rope, IEnumerable requiredActors) 38 | { 39 | RopeActorMotion ropeActorMotion = requiredActors.FirstOrDefault(x => x.GetType() == typeof(RopeActorMotion)) as RopeActorMotion; 40 | RopeActorAttachments ropeActorAttachments = requiredActors.FirstOrDefault(x => x.GetType() == typeof(RopeActorAttachments)) as RopeActorAttachments; 41 | // if (ropeMotion == null) 42 | ropeActorRigidbody = new RopeActorRigidbody( 43 | rope, 44 | ropeActorMotion, 45 | ropeActorAttachments, 46 | collisionIterations 47 | ); 48 | ropeActorRigidbody.EnableActor(); 49 | } 50 | protected override void OnValidate() 51 | { 52 | collisionIterations = Mathf.Max(collisionIterations, 1); 53 | } 54 | 55 | public int CollisionIterations { get => ropeActorRigidbody.CollisionIterations; set => ropeActorRigidbody.CollisionIterations = value; } 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentRigidbody.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a80f74dc4a2646041b830986a54e8259 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentTension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | /* 6 | public interface IRopeTension 7 | { 8 | public bool TensionEnabled { get; set; } 9 | /// 10 | /// How far the rope needs to be stretched beyond its maximum to start exerting force 11 | /// 12 | public float ThresholdTension { get; set; } 13 | /// 14 | /// How strongly the rope pulls back when the threshold tension is reached 15 | /// 16 | public float SpringStrength { get; set; } 17 | /// 18 | /// How strongly outward movement is reduced on rigidbodies attached to the tips of the rope depending on threshold tension. 19 | /// 20 | public float DampingStrength { get; set; } 21 | } 22 | */ 23 | 24 | [RequireComponent(typeof(RopeGameObject), typeof(RopeComponentRigidbody))] 25 | public class RopeComponentTension : RopeComponentBase 26 | { 27 | [SerializeField] 28 | private float thresholdTension = 1.33f; 29 | [SerializeField] 30 | private float springStrength = 100f; 31 | [SerializeField] 32 | [Range(0f, 1f)] 33 | private float dampingStrength = .1f; 34 | 35 | private RopeActorTension ropeActorTension; 36 | public override RopeActorBase RopeActor { get { return ropeActorTension; } } 37 | 38 | protected override void Start() 39 | { 40 | requiredActorTypes.Add(typeof(RopeActorRigidbody)); 41 | base.Start(); 42 | } 43 | 44 | public override void Initialize(Rope rope, IEnumerable requiredActors) 45 | { 46 | RopeActorRigidbody ropeActorRigidbody = requiredActors.FirstOrDefault(x => x.GetType() == typeof(RopeActorRigidbody)) as RopeActorRigidbody; 47 | // if (ropeRigidbody == null) 48 | ropeActorTension = new RopeActorTension( 49 | rope, 50 | ropeActorRigidbody, 51 | thresholdTension, 52 | springStrength, 53 | dampingStrength 54 | ); 55 | ropeActorTension.EnableActor(); 56 | } 57 | 58 | protected override void OnValidate() 59 | { 60 | thresholdTension = Mathf.Max(1, thresholdTension); 61 | springStrength = Mathf.Max(0, springStrength); 62 | } 63 | 64 | public float ThresholdTension { get => ropeActorTension.ThresholdTension; set => ropeActorTension.ThresholdTension = value; } 65 | public float SpringStrength { get => ropeActorTension.SpringStrength; set => ropeActorTension.SpringStrength = value; } 66 | public float DampingStrength { get => ropeActorTension.DampingStrength; set => ropeActorTension.DampingStrength = value; } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentTension.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 608362ea03eaa1e45ab37a2c268d30f4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentTrigger.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | 6 | /* 7 | interface IRopeTrigger 8 | { 9 | // Enable/Disable Trigger detection? 10 | bool TriggerEnabled { set; } 11 | void UpdateLayermask(); 12 | void DetectTrigger(); 13 | } 14 | */ 15 | [RequireComponent(typeof(RopeGameObject), typeof(RopeComponentLength))] 16 | public class RopeComponentTrigger : RopeComponentBase 17 | { 18 | [SerializeField] 19 | private bool triggerEnabled = true; 20 | 21 | private RopeActorTrigger ropeActorTrigger; 22 | public override RopeActorBase RopeActor { get { return ropeActorTrigger; } } 23 | 24 | protected override void Start() 25 | { 26 | requiredActorTypes.Add(typeof(RopeActorLength)); 27 | base.Start(); 28 | } 29 | 30 | public override void Initialize(Rope rope, IEnumerable requiredActors) 31 | { 32 | RopeActorLength ropeActorLength = requiredActors.FirstOrDefault(x => x.GetType() == typeof(RopeActorLength)) as RopeActorLength; 33 | 34 | ropeActorTrigger = new RopeActorTrigger( 35 | rope, 36 | ropeActorLength, 37 | triggerEnabled 38 | ); 39 | ropeActorTrigger.EnableActor(); 40 | } 41 | 42 | public bool TriggerEnabled { set => ropeActorTrigger.TriggerEnabled = value; } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Components/RopeComponentTrigger.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c8b80526bc017c446b0f333bfa5e708e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Helper.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a18c928378d9033408b95aa50f3a08f3 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Helper/RopeCollisionMatrixLayerMask.cs: -------------------------------------------------------------------------------- 1 | // PhysicsCollisionMatrixLayerMasks code from bellicapax at https://forum.unity.com/threads/is-there-a-way-to-get-the-layer-collision-matrix.260744/ 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | public static class RopeCollisionMatrixLayerMask 7 | { 8 | private static Dictionary _masksByLayer; 9 | 10 | public static void Init() 11 | { 12 | _masksByLayer = new Dictionary(); 13 | for (int i = 0; i < 32; i++) 14 | { 15 | int mask = 0; 16 | for (int j = 0; j < 32; j++) 17 | { 18 | if (!Physics.GetIgnoreLayerCollision(i, j)) 19 | { 20 | mask |= 1 << j; 21 | } 22 | } 23 | _masksByLayer.Add(i, mask); 24 | } 25 | } 26 | 27 | public static int MaskForLayer(int layer) 28 | { 29 | if (_masksByLayer == null) Init(); 30 | return _masksByLayer[layer]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Helper/RopeCollisionMatrixLayerMask.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bf42582be0499054c9ddcddf9f1b1fc7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 346ff1728d56d5348bcfac3756fa6a08 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeActionExecution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class RopeActionExecution 4 | { 5 | private int order; 6 | private Action action; 7 | public int Order { get { return order; } } 8 | public Action Action { get { return action; } } 9 | 10 | public RopeActionExecution(int order, Action action) 11 | { 12 | this.order = order; 13 | this.action = action; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeActionExecution.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aec42847933d43d4687fc84824c7b083 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeAttachment.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public abstract class RopeAttachment 4 | { 5 | private int pointIndex; 6 | public int RawPointIndex { get => pointIndex; } // this can be negative 7 | public int CorrectedPointIndex(int pointCount) 8 | { 9 | return pointIndex >= 0 ? pointIndex : Mathf.Max(pointCount + pointIndex, 0); 10 | } 11 | 12 | public abstract Vector3 Position { get; } 13 | public abstract Rigidbody Rigidbody { get; } 14 | 15 | public RopeAttachment(int pointIndex) 16 | { 17 | this.pointIndex = pointIndex; 18 | } 19 | } 20 | 21 | public class RopeAttachmentPoint : RopeAttachment 22 | { 23 | private Vector3 position; 24 | 25 | public RopeAttachmentPoint(int pointIndex, Vector3 position) : base(pointIndex) 26 | { 27 | this.position = position; 28 | } 29 | 30 | public override Vector3 Position { get => position; } 31 | public override Rigidbody Rigidbody { get => null; } 32 | } 33 | 34 | public class RopeAttachmentTransform : RopeAttachment 35 | { 36 | private Transform transform; 37 | private Vector3 offset; 38 | 39 | public RopeAttachmentTransform(int pointIndex, Transform transform, Vector3 offset) : base(pointIndex) 40 | { 41 | this.transform = transform; 42 | this.offset = offset; 43 | } 44 | 45 | public override Vector3 Position { get => transform.TransformPoint(offset); } 46 | public override Rigidbody Rigidbody { get => null; } 47 | } 48 | 49 | public class RopeAttachmentRigidbody : RopeAttachmentTransform 50 | { 51 | private Rigidbody rigidbody; 52 | 53 | public RopeAttachmentRigidbody(int pointIndex, Transform transform, Vector3 offset, Rigidbody rigidbody) : base(pointIndex, transform, offset) 54 | { 55 | this.rigidbody = rigidbody; 56 | } 57 | 58 | public override Rigidbody Rigidbody { get => rigidbody; } 59 | } 60 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeAttachment.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b1accabbe89f06642ab330d683ef1f50 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeBody.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System; 4 | using UnityEngine; 5 | 6 | public class RopeBody : IList 7 | { 8 | private List points = new List(); 9 | 10 | private List ropeBodyListners = new List(); 11 | 12 | public RopeBody(RopeBuilder ropeBuilder) 13 | { 14 | points = ropeBuilder.BuildRope(); 15 | } 16 | 17 | public int Count => points.Count; 18 | 19 | public bool IsReadOnly => true; 20 | 21 | public RopePoint this[int index] { get { return points[index]; } set { points[index] = value; } } 22 | 23 | public void AddListner(IRopeBodyListner listner) 24 | { 25 | if (!ropeBodyListners.Contains(listner)) 26 | { 27 | ropeBodyListners.Add(listner); 28 | } 29 | } 30 | 31 | public void RemoveListner(IRopeBodyListner listner) 32 | { 33 | ropeBodyListners.Remove(listner); 34 | } 35 | 36 | /// 37 | /// Adds a point to the beginning if true or end if false 38 | /// 39 | /// 40 | public void AddRopePoint(RopePoint point, bool direction) 41 | { 42 | if (direction) points.Insert(0, point); 43 | else points.Add(point); 44 | 45 | foreach (IRopeBodyListner ropeBodyListner in ropeBodyListners) 46 | { 47 | if(ropeBodyListner != null) ropeBodyListner.RopePointAdded(direction); 48 | } 49 | } 50 | /// 51 | /// Removes a point from the beginning if true or the end if false 52 | /// 53 | /// 54 | public void RemoveRopePoint(bool direction) 55 | { 56 | if (direction) points.RemoveAt(0); 57 | else points.RemoveAt(points.Count - 1); 58 | 59 | foreach (IRopeBodyListner ropeBodyListner in ropeBodyListners) 60 | { 61 | if (ropeBodyListner != null) ropeBodyListner.RopePointRemoved(direction); 62 | } 63 | } 64 | 65 | public IEnumerator GetEnumerator() 66 | { 67 | foreach(RopePoint point in points) 68 | { 69 | yield return point; 70 | } 71 | } 72 | 73 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 74 | 75 | public int IndexOf(RopePoint item) => points.IndexOf(item); 76 | 77 | public void Insert(int index, RopePoint item) => points.Insert(index, item); 78 | 79 | public void RemoveAt(int index) => points.RemoveAt(index); 80 | 81 | public void Add(RopePoint item) => points.Add(item); 82 | 83 | public void Clear() => points.Clear(); 84 | 85 | public bool Contains(RopePoint item) => points.Contains(item); 86 | 87 | public void CopyTo(RopePoint[] array, int arrayIndex) => points.CopyTo(array, arrayIndex); 88 | 89 | public bool Remove(RopePoint item) => points.Remove(item); 90 | } 91 | 92 | public interface IRopeBodyListner 93 | { 94 | void RopePointAdded(bool direction); 95 | void RopePointRemoved(bool driection); 96 | } 97 | 98 | public interface IRopePointPosition 99 | { 100 | public Vector3 Position { get; set; } 101 | } 102 | 103 | public interface IRopePointPrevPosition 104 | { 105 | public Vector3 PrevPosition { get; set; } 106 | 107 | } 108 | 109 | public interface IRopePointAccumulatedForce 110 | { 111 | public Vector3 AccumulatedForce { get; set; } 112 | } 113 | 114 | public interface IRopePointFriction 115 | { 116 | public float Friction { get; set; } 117 | } 118 | 119 | public interface IRopePointIsAttached 120 | { 121 | public bool IsAttached { get; set; } 122 | } 123 | 124 | public class RopePoint : 125 | IRopePointPosition, 126 | IRopePointPrevPosition, 127 | IRopePointAccumulatedForce, 128 | IRopePointFriction, 129 | IRopePointIsAttached 130 | { 131 | private Vector3 position, prevPosition, accumulatedForce = Vector3.zero; 132 | public Vector3 Position { get => position; set => position = value; } 133 | public Vector3 PrevPosition { get => prevPosition; set => prevPosition = value; } 134 | public Vector3 AccumulatedForce { get => accumulatedForce; set => accumulatedForce = value; } 135 | 136 | private float friction = 0; 137 | public float Friction { get => friction; set => friction = value; } 138 | private bool attached = false; 139 | public bool IsAttached { get => attached; set => attached = value; } 140 | public RopePoint(Vector3 position) 141 | { 142 | this.position = position; 143 | this.prevPosition = position; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeBody.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cacf55167b4199e4eab0ea63da79e8fd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeCollisionEvents.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeCollisionEvent 6 | { 7 | private Rope rope; 8 | public Rope Rope { get { return rope; } } 9 | 10 | private RopeContactCollision contact; 11 | public RopeContactCollision Contact { get { return contact; } } 12 | 13 | public RopeCollisionEvent(Rope rope, RopeContactCollision contact) 14 | { 15 | this.rope = rope; 16 | this.contact = contact; 17 | } 18 | } 19 | 20 | public class RopeTriggerEvent 21 | { 22 | private Rope rope; 23 | public Rope Rope { get { return rope; } } 24 | 25 | RopeContactTrigger contact; 26 | public RopeContactTrigger Contact { get { return contact; } } 27 | 28 | public RopeTriggerEvent(Rope rope, RopeContactTrigger contact) 29 | { 30 | this.rope = rope; 31 | this.contact = contact; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeCollisionEvents.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5c72cb613bf1d93468996cf27ab8eb7b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeContact.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public abstract class RopeContact 6 | { 7 | private int pointIndex; 8 | public int PointIndex { get => pointIndex; } 9 | 10 | protected Vector3 position; 11 | public Vector3 Position { get => position; } 12 | 13 | private Rigidbody rigidbody; 14 | public Rigidbody Rigidbody { get => rigidbody; } 15 | 16 | //low contact is closwer to the rope tip, high contact is closer to the player along the rope 17 | private RopeContact lowContact; 18 | public RopeContact LowContact { get => lowContact; } 19 | 20 | private RopeContact highContact; 21 | public RopeContact HighContact { get => highContact; } 22 | 23 | public abstract bool Replacable { get; } 24 | 25 | public RopeContact(int pointIndex, Vector3 position, Rigidbody rigidbody = null) 26 | { 27 | this.pointIndex = pointIndex; 28 | this.position = position; 29 | this.rigidbody = rigidbody; 30 | } 31 | 32 | public void InsertAboveContact(RopeContact lowContact) 33 | { 34 | if (lowContact != null) 35 | { 36 | if (lowContact.highContact != null) 37 | { 38 | lowContact.highContact.lowContact = this; 39 | this.highContact = lowContact.highContact; 40 | } 41 | lowContact.highContact = this; 42 | this.lowContact = lowContact; 43 | } 44 | } 45 | 46 | public void InsertBelowContact(RopeContact highContact) 47 | { 48 | if (highContact != null) 49 | { 50 | if (highContact.lowContact != null) 51 | { 52 | highContact.lowContact.highContact = this; 53 | this.lowContact = highContact.lowContact; 54 | } 55 | highContact.lowContact = this; 56 | this.highContact = highContact; 57 | } 58 | } 59 | 60 | public void ReplaceContact(RopeContact oldContact) 61 | { 62 | if (!oldContact.Replacable) return; 63 | if (oldContact.lowContact != null) 64 | { 65 | this.lowContact = oldContact.lowContact; 66 | this.lowContact.highContact = this; 67 | } 68 | if (oldContact.highContact != null) 69 | { 70 | this.highContact = oldContact.highContact; 71 | this.highContact.lowContact = this; 72 | } 73 | } 74 | 75 | public abstract void LimitVelocity(float strength, float vectorLerp); 76 | 77 | public abstract void ApplyForce(float strength, float vectorLerp, float timeStep); 78 | } 79 | 80 | public class RopeContactCollision : RopeContact 81 | { 82 | private Vector3 lowCollisionVector; 83 | public Vector3 LowCollisionVector 84 | { 85 | get => lowCollisionVector; 86 | 87 | } 88 | private Vector3 highCollisionVector; 89 | public Vector3 HighCollisionVector { get => highCollisionVector; } 90 | 91 | public override bool Replacable => true; 92 | 93 | public RopeContactCollision(int pointIndex, Vector3 position, Vector3 lowCollisionVector, Vector3 highCollisionVector, Rigidbody rb = null) : base(pointIndex, position, rb) 94 | { 95 | this.lowCollisionVector = lowCollisionVector; 96 | this.highCollisionVector = highCollisionVector; 97 | } 98 | 99 | public override void LimitVelocity(float strength, float vectorLerp) 100 | { 101 | if (Rigidbody == null) return; 102 | 103 | // Dont need anything here 104 | } 105 | public override void ApplyForce(float strength, float vectorLerp, float timeStep) 106 | { 107 | if (Rigidbody == null) return; 108 | 109 | //Rigidbody.AddForceAtPosition(CollisionVector * strength * timeStep, Position, ForceMode.Impulse); 110 | Rigidbody.AddForceAtPosition(Vector3.Lerp(LowCollisionVector, HighCollisionVector, vectorLerp).normalized * strength * timeStep, Position, ForceMode.Impulse); 111 | } 112 | } 113 | 114 | public class RopeContactAttachment : RopeContact 115 | { 116 | /* 117 | private Vector3 attachmentVector = Vector3.zero; 118 | public Vector3 AttachmentVector 119 | { 120 | get 121 | { 122 | // calculate attachment vector if it doesnt exist 123 | if (attachmentVector == Vector3.zero) 124 | { 125 | if (LowContact == null) 126 | { 127 | if (HighContact == null) 128 | { 129 | attachmentVector = Vector3.zero; 130 | } 131 | attachmentVector = (HighContact.Position - Position).normalized; 132 | return attachmentVector; 133 | } 134 | if (HighContact == null) 135 | { 136 | if (LowContact == null) 137 | { 138 | attachmentVector = Vector3.zero; 139 | } 140 | attachmentVector = (LowContact.Position - Position).normalized; 141 | return attachmentVector; 142 | } 143 | 144 | attachmentVector = Vector3.Slerp((LowContact.Position - Position).normalized, (HighContact.Position - Position).normalized, .5f); 145 | return attachmentVector; 146 | } 147 | return attachmentVector; 148 | } 149 | }*/ 150 | 151 | private Vector3 lowAttachmentVector = Vector3.zero; 152 | public Vector3 LowAttachmentVector 153 | { 154 | get 155 | { 156 | if (lowAttachmentVector == Vector3.zero) 157 | { 158 | if (LowContact == null) lowAttachmentVector = Vector3.zero; 159 | else lowAttachmentVector = (LowContact.Position - Position).normalized; 160 | } 161 | return lowAttachmentVector; 162 | } 163 | } 164 | 165 | private Vector3 highAttachmentVector = Vector3.zero; 166 | public Vector3 HighAttachmentVector 167 | { 168 | get 169 | { 170 | if (highAttachmentVector == Vector3.zero) 171 | { 172 | if (HighContact == null) highAttachmentVector = Vector3.zero; 173 | else highAttachmentVector = (HighContact.Position - Position).normalized; 174 | } 175 | return highAttachmentVector; 176 | } 177 | } 178 | 179 | public override bool Replacable => false; 180 | 181 | public RopeContactAttachment(int pointIndex, Vector3 position, Rigidbody rb = null) : base(pointIndex, position, rb) 182 | { 183 | } 184 | 185 | public override void LimitVelocity(float strength, float vectorLerp) 186 | { 187 | if (Rigidbody == null) return; 188 | Vector3 attachmentVector = Vector3.Lerp(LowAttachmentVector, HighAttachmentVector, vectorLerp); 189 | //Vector3 dampenedVelocity = Vector3.Lerp(contact.rb.velocity, Vector3.ProjectOnPlane(contact.rb.velocity, contact.contactVector), tensionDamping); 190 | Vector3 velocity = Rigidbody.velocity; 191 | float velcoityMagnitude = velocity.magnitude; 192 | Vector3 tempAV = attachmentVector; 193 | Vector3.OrthoNormalize(ref tempAV, ref velocity); 194 | Vector3 tangentVelocity = velocity * velcoityMagnitude; 195 | //Debug.DrawRay(contact.rb.position, tangentVelocity, Color.blue); 196 | //float t = 1 - Mathf.Abs(Vector3.Dot(contact.contactVector, contact.rb.velocity.normalized)); 197 | float t = -Vector3.Dot(attachmentVector, Rigidbody.velocity.normalized); 198 | t = t < 0 ? 0 : 1 - t; 199 | tangentVelocity = Vector3.Lerp(Vector3.ProjectOnPlane(Rigidbody.velocity, attachmentVector), tangentVelocity, t); 200 | //Debug.DrawRay(contact.rb.position, tangentVelocity * 3, Color.black); 201 | 202 | Vector3 dampenedVelocity = Vector3.Lerp(Rigidbody.velocity, tangentVelocity, strength); // strength here was initially called tension damping 203 | //Debug.DrawRay(Rigidbody.position, dampenedVelocity.normalized * 2, Color.blue); 204 | //Debug.DrawRay(contact.rb.position, dampenedVelocity, Color.green); 205 | //Debug.Log("limit: " + Rigidbody.position + " force: " + dampenedVelocity); 206 | 207 | Rigidbody.velocity = dampenedVelocity; 208 | } 209 | 210 | public override void ApplyForce(float strength, float vectorLerp, float timeStep) 211 | { 212 | 213 | if (Rigidbody == null) return; 214 | //Debug.DrawRay(Rigidbody.transform.position, AttachmentVector.normalized * 2, Color.green); 215 | if (LowAttachmentVector == Vector3.zero && HighAttachmentVector == Vector3.zero) return; 216 | //Rigidbody.AddForceAtPosition(AttachmentVector * strength * timeStep, Position, ForceMode.Impulse); 217 | 218 | Rigidbody.AddForceAtPosition(Vector3.Lerp(LowAttachmentVector, HighAttachmentVector, vectorLerp).normalized * strength * timeStep, Position, ForceMode.Impulse); 219 | } 220 | } 221 | 222 | public class RopeContactTrigger : RopeContact 223 | { 224 | public RopeContactTrigger(int pointIndex, Vector3 position, Rigidbody rigidbody = null) : base(pointIndex, position, rigidbody) 225 | { 226 | } 227 | 228 | public override void ApplyForce(float strength, float vectorLerp, float timeStep) 229 | { 230 | } 231 | 232 | public override void LimitVelocity(float strength, float vectorLerp) 233 | { 234 | } 235 | public override bool Replacable { get { return true; } } 236 | 237 | } 238 | 239 | public class RopeContactChain : IEnumerable 240 | { 241 | private class InsertReturn 242 | { 243 | private int index; 244 | /// 245 | /// The index the contact was inserted at 246 | /// 247 | public int Index { get => index; } 248 | 249 | private bool replacedContact; 250 | /// 251 | /// If the contact replaced a different one 252 | /// 253 | public bool ReplacedContact { get => replacedContact; } 254 | 255 | public InsertReturn(int index, bool replacedContact) 256 | { 257 | this.index = index; 258 | this.replacedContact = replacedContact; 259 | } 260 | } 261 | 262 | private RopeContact startContact; 263 | private int count = 0; 264 | public RopeContactChain() { } 265 | 266 | public RopeContactChain(RopeContact baseContact) 267 | { 268 | Insert(baseContact); 269 | } 270 | 271 | public void Clear() 272 | { 273 | startContact = null; 274 | count = 0; 275 | } 276 | 277 | public void Insert(RopeContact newContact) 278 | { 279 | InsertUpdate(newContact, SearchInsert(newContact, startContact, 0)); 280 | } 281 | 282 | /// 283 | /// Inserts the contact into the chain, searching up from the start. Returns the inserted index and if something was overwritten 284 | /// 285 | /// 286 | /// 287 | /// when first called, the starting index of the search 288 | /// 289 | private InsertReturn SearchInsert(RopeContact newContact, RopeContact searchContact, int index) 290 | { 291 | if (searchContact == null) 292 | { 293 | return new InsertReturn(index, false); 294 | } 295 | if (newContact.PointIndex < searchContact.PointIndex) 296 | { 297 | if (searchContact.LowContact == null) 298 | { 299 | newContact.InsertBelowContact(searchContact); 300 | return new InsertReturn(0, false); 301 | } 302 | else 303 | { 304 | if (newContact.PointIndex > searchContact.LowContact.PointIndex) 305 | { 306 | newContact.InsertBelowContact(searchContact); 307 | return new InsertReturn(index, false); 308 | } 309 | else 310 | { 311 | return SearchInsert(newContact, searchContact.LowContact, index - 1); 312 | } 313 | } 314 | } 315 | else if (newContact.PointIndex == searchContact.PointIndex) 316 | { 317 | if (searchContact.Replacable) 318 | { 319 | newContact.ReplaceContact(searchContact); 320 | return new InsertReturn(index, true); 321 | } 322 | else 323 | { 324 | return new InsertReturn(-1, true); 325 | } 326 | } 327 | else 328 | { 329 | if (searchContact.HighContact == null) 330 | { 331 | newContact.InsertAboveContact(searchContact); 332 | return new InsertReturn(index + 1, false); 333 | } 334 | else 335 | { 336 | if (newContact.PointIndex < searchContact.HighContact.PointIndex) 337 | { 338 | newContact.InsertAboveContact(searchContact); 339 | return new InsertReturn(index + 1, false); 340 | } 341 | else 342 | { 343 | return SearchInsert(newContact, searchContact.HighContact, index + 1); 344 | } 345 | } 346 | } 347 | } 348 | 349 | private void InsertUpdate(RopeContact newContact, InsertReturn ir) 350 | { 351 | // don't do anything if it was less than 0 (-1) because nothing was added 352 | if (ir.Index < 0) return; 353 | if (!ir.ReplacedContact) count++; 354 | if (ir.Index == 0) startContact = newContact; 355 | } 356 | 357 | public RopeContact FindNearest(int searchIndex, RopeContact searchContact) 358 | { 359 | if (searchContact == null) 360 | { 361 | return null; 362 | } 363 | int lowDist = searchContact.LowContact == null ? int.MaxValue : Mathf.Abs(searchContact.LowContact.PointIndex - searchIndex); 364 | int highDist = searchContact.HighContact == null ? int.MaxValue : Mathf.Abs(searchContact.HighContact.PointIndex - searchIndex); 365 | int centerDist = Mathf.Abs(searchContact.PointIndex - searchIndex); 366 | if (lowDist < highDist) 367 | { 368 | if (lowDist < centerDist) 369 | { 370 | return FindNearest(searchIndex, searchContact.LowContact); 371 | } 372 | else 373 | { 374 | return searchContact; 375 | } 376 | } 377 | else 378 | { 379 | if (highDist < centerDist) 380 | { 381 | return FindNearest(searchIndex, searchContact.HighContact); 382 | } 383 | else 384 | { 385 | return searchContact; 386 | } 387 | } 388 | } 389 | 390 | public (RopeContact lowContactPoint, RopeContact highContactPoint) FindNearestPair(int searchIndex) 391 | { 392 | RopeContact nearest = FindNearest(searchIndex, startContact); 393 | RopeContact pair; 394 | if (nearest.LowContact == null) 395 | { 396 | pair = nearest.HighContact == null ? nearest : nearest.HighContact; 397 | return (nearest, pair); 398 | } 399 | if (nearest.HighContact == null) 400 | { 401 | pair = nearest.LowContact == null ? nearest : nearest.LowContact; 402 | return (pair, nearest); 403 | } 404 | 405 | if (searchIndex > nearest.PointIndex) 406 | { 407 | pair = nearest.HighContact; 408 | return (nearest, pair); 409 | } 410 | else 411 | { 412 | pair = nearest.LowContact; 413 | return (pair, nearest); 414 | } 415 | 416 | } 417 | 418 | public IEnumerator GetEnumerator() 419 | { 420 | RopeContact contact = startContact; 421 | while (contact != null) 422 | { 423 | yield return contact; 424 | contact = contact.HighContact; 425 | } 426 | } 427 | 428 | IEnumerator IEnumerable.GetEnumerator() 429 | { 430 | return GetEnumerator(); 431 | } 432 | } -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeContact.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ad012ff6e2961984ea637ee676a30c6a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeDelegates.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public delegate void SetStickPosition1Delegate(ref RopeStick s, ref Vector3 stickCenter, ref Vector3 stickDirection, bool order, int stickIndex = 0); 6 | 7 | public delegate void SetStickPosition2Delegate(ref RopeStick s, ref Vector3 stickCenter, ref Vector3 stickDirection, bool order); 8 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeDelegates.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: afee506963d673c41afb22afe47cd92c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeUpdater.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeUpdater 6 | { 7 | private List actionExecutions = new List(); 8 | 9 | protected float currentTimeStep; 10 | public float CurrentTimeStep { get { return currentTimeStep; } } 11 | 12 | public void AddActionExecution(RopeActionExecution actionExecution) 13 | { 14 | actionExecutions.Add(actionExecution); 15 | actionExecutions.Sort((x, y) => x.Order.CompareTo(y.Order)); 16 | } 17 | 18 | public void RemoveActionExecution(RopeActionExecution actionExecution) 19 | { 20 | actionExecutions.Remove(actionExecution); 21 | } 22 | 23 | public void UpdateRope(float timeStep) 24 | { 25 | currentTimeStep = timeStep; 26 | // move this into action executions too? 27 | 28 | // execute the actions of each component in order 29 | foreach (RopeActionExecution actionExecution in actionExecutions) 30 | { 31 | actionExecution.Action(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Members/RopeUpdater.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 409f372b013398946a361919288c47dc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0d6d40037358ba04aa22f58b07fec14b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorAttachments.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeSimulatorAttachments : IList, IRopeBodyListner 6 | { 7 | private RopeBody ropeBody; 8 | 9 | private List ropeAttachments = new List(); 10 | 11 | public int Count => ropeAttachments.Count; 12 | 13 | public bool IsReadOnly => true; 14 | 15 | public RopeAttachment this[int index] { get => ropeAttachments[index]; set => ropeAttachments[index] = value; } 16 | 17 | public RopeSimulatorAttachments(RopeBody ropeBody, IEnumerable initialAttachments) 18 | { 19 | this.ropeBody = ropeBody; 20 | // TODO: add automatic removal of listner when disabled 21 | this.ropeBody.AddListner(this); 22 | foreach (RopeAttachment attachment in initialAttachments) 23 | { 24 | AddAttachment(attachment); 25 | } 26 | } 27 | 28 | public void MovePointsToAttachments() 29 | { 30 | //Debug.Log("point count: " + points.Count); 31 | foreach (IRopePointIsAttached point in ropeBody) 32 | { 33 | point.IsAttached = false; 34 | } 35 | foreach (RopeAttachment attachment in ropeAttachments) 36 | { 37 | ropeBody[attachment.CorrectedPointIndex(ropeBody.Count)].Position = attachment.Position; 38 | ropeBody[attachment.CorrectedPointIndex(ropeBody.Count)].IsAttached = true; 39 | } 40 | } 41 | 42 | //TODO: Fill out these directional functions 43 | 44 | public void RopePointAdded(bool direction) 45 | { 46 | ValidateAttachments(); 47 | } 48 | 49 | public void RopePointRemoved(bool driection) 50 | { 51 | ValidateAttachments(); 52 | } 53 | 54 | // TODO: Make this direction dependent 55 | public void AddAttachment(RopeAttachment attachment) 56 | { 57 | // check if something is already attached to that point and overwrite it 58 | // then sort the list of attachments 59 | 60 | //add contact 61 | if (ropeAttachments.Count == 0) 62 | { 63 | ropeAttachments.Add(attachment); 64 | return; 65 | } 66 | 67 | int matchingIndex = ropeAttachments.FindIndex(x => x.RawPointIndex == attachment.RawPointIndex); 68 | if (matchingIndex == -1) 69 | { 70 | if (attachment.RawPointIndex >= 0) 71 | { 72 | int aIndex = 0; 73 | float otherRaw = -1; 74 | while (true) 75 | { 76 | otherRaw = ropeAttachments[aIndex].RawPointIndex; 77 | if (otherRaw < 0) 78 | { 79 | ropeAttachments.Insert(aIndex, attachment); 80 | break; 81 | } 82 | if (otherRaw > attachment.RawPointIndex) 83 | { 84 | ropeAttachments.Insert(aIndex, attachment); 85 | break; 86 | } 87 | aIndex++; 88 | if (aIndex >= ropeAttachments.Count) 89 | { 90 | ropeAttachments.Add(attachment); 91 | break; 92 | } 93 | } 94 | } 95 | else 96 | { 97 | int aIndex = ropeAttachments.Count - 1; 98 | float otherRaw = 0; 99 | while (true) 100 | { 101 | otherRaw = ropeAttachments[aIndex].RawPointIndex; 102 | if (otherRaw >= 0) 103 | { 104 | ropeAttachments.Insert(aIndex + 1, attachment); 105 | break; 106 | } 107 | if (otherRaw < attachment.RawPointIndex) 108 | { 109 | ropeAttachments.Insert(aIndex + 1, attachment); 110 | break; 111 | } 112 | aIndex--; 113 | if (aIndex < 0) 114 | { 115 | ropeAttachments.Insert(0, attachment); 116 | break; 117 | } 118 | } 119 | } 120 | ValidateAttachments(); 121 | } 122 | //replace contact 123 | else 124 | { 125 | ropeAttachments[matchingIndex] = attachment; 126 | } 127 | } 128 | 129 | public void RemoveAttachment(int index) 130 | { 131 | if (index > 0 && index < ropeAttachments.Count) ropeAttachments.RemoveAt(index); 132 | } 133 | 134 | /// 135 | /// Removes any attachments that are connected to the same point index 136 | /// 137 | protected void ValidateAttachments() 138 | { 139 | // index then count 140 | Dictionary repeatedIndexes = new Dictionary(); 141 | foreach (RopeAttachment a in ropeAttachments) 142 | { 143 | int correctedIndex = a.CorrectedPointIndex(ropeBody.Count); 144 | if (repeatedIndexes.ContainsKey(correctedIndex)) 145 | { 146 | repeatedIndexes[correctedIndex]++; 147 | } 148 | else 149 | { 150 | repeatedIndexes[correctedIndex] = 1; 151 | } 152 | } 153 | 154 | foreach (KeyValuePair indexCount in repeatedIndexes) 155 | { 156 | int count = indexCount.Value; 157 | while (count > 1) 158 | { 159 | int removeIndex = ropeAttachments.FindLastIndex(x => x.CorrectedPointIndex(ropeBody.Count) == indexCount.Key); 160 | if (removeIndex < 0) break; 161 | ropeAttachments.RemoveAt(removeIndex); 162 | count--; 163 | } 164 | } 165 | } 166 | 167 | public int IndexOf(RopeAttachment item) => ropeAttachments.IndexOf(item); 168 | 169 | public void Insert(int index, RopeAttachment item) => ropeAttachments.Insert(index, item); 170 | 171 | public void RemoveAt(int index) => ropeAttachments.RemoveAt(index); 172 | 173 | public void Add(RopeAttachment item) => ropeAttachments.Add(item); 174 | 175 | public void Clear() => ropeAttachments.Clear(); 176 | 177 | public bool Contains(RopeAttachment item) => ropeAttachments.Contains(item); 178 | 179 | public void CopyTo(RopeAttachment[] array, int arrayIndex) => ropeAttachments.CopyTo(array, arrayIndex); 180 | 181 | public bool Remove(RopeAttachment item) => ropeAttachments.Remove(item); 182 | 183 | public IEnumerator GetEnumerator() 184 | { 185 | foreach(RopeAttachment ropeAttachment in ropeAttachments) 186 | { 187 | yield return ropeAttachment; 188 | } 189 | } 190 | 191 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 192 | } 193 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorAttachments.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 65c6145ba8a9d1f4590a8f0f319bc422 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorDebug.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeSimulatorDebug 6 | { 7 | private Color lineColor; 8 | public Color LineColor { get => lineColor; set => lineColor = value; } 9 | 10 | public RopeSimulatorDebug(Color lineColor) 11 | { 12 | this.lineColor = lineColor; 13 | } 14 | 15 | public void DrawLines(IEnumerable sticks) 16 | { 17 | foreach(RopeStick s in sticks) 18 | { 19 | Debug.DrawLine(s.PointA.Position, s.PointB.Position, LineColor); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorDebug.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b4914d21feaf34e4c9f59884bffca04f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorForces.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeSimulatorForces 6 | { 7 | private float pointMass = .01f; 8 | public float PointMass { get => pointMass; set => pointMass = Mathf.Max(.01f, value); } 9 | 10 | public RopeSimulatorForces( 11 | float pointMass 12 | ) 13 | { 14 | this.pointMass = pointMass; 15 | } 16 | 17 | /// 18 | /// Applies a force to a point. Must be run before ApplyForce! 19 | /// 20 | /// The direciton and strength of the force 21 | /// How to apply the force 22 | /// The delta time since last rope update 23 | /// The point to be modified 24 | public void AccumulateForce(Vector3 forceVector, ForceMode forceMode, float timeStep, T point) 25 | where T : IRopePointAccumulatedForce, IRopePointIsAttached 26 | { 27 | if (!point.IsAttached) 28 | { 29 | switch (forceMode) 30 | { 31 | case ForceMode.Force: 32 | point.AccumulatedForce += (forceVector / pointMass) * timeStep * timeStep; 33 | break; 34 | case ForceMode.Acceleration: 35 | point.AccumulatedForce += forceVector * timeStep * timeStep; 36 | break; 37 | case ForceMode.Impulse: 38 | point.AccumulatedForce += forceVector / pointMass; 39 | break; 40 | case ForceMode.VelocityChange: 41 | point.AccumulatedForce += forceVector; 42 | break; 43 | } 44 | } 45 | } 46 | 47 | /// 48 | /// Applies a force to the entire rope. Must be run before ApplyForce 49 | /// 50 | /// The direciton and strength of the force 51 | /// How to apply the force 52 | /// The delta time since last rope update 53 | /// The list of points to be modified 54 | public void AccumulateForce(Vector3 forceVector, ForceMode forceMode, float timeStep, IEnumerable points) 55 | where T : IRopePointAccumulatedForce, IRopePointIsAttached 56 | { 57 | foreach (T p in points) 58 | { 59 | AccumulateForce(forceVector, forceMode, timeStep, p); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorForces.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dfb7181404fe1ca4783d9adb63156af0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorLength.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System; 5 | 6 | public class RopeSimulatorLength : IList 7 | { 8 | private List ropeSticks = new List(); 9 | 10 | private RopeBody ropeBody; 11 | 12 | private readonly float MIN_SEGMENT_LENGTH = .1f; 13 | 14 | private float goalLength = 5f; 15 | public float GoalLength 16 | { 17 | set 18 | { 19 | // add debug log if value is set too high or too low? 20 | goalLength = Mathf.Clamp(value, minRopeLength, maxRopeLength); 21 | } 22 | get 23 | { 24 | return goalLength; 25 | } 26 | } 27 | 28 | private float currentLength = 5f; 29 | public float CurrentLength 30 | { 31 | get => currentLength; 32 | } 33 | 34 | private float maxRopeLength = 10f; 35 | public float MaxRopeLength 36 | { 37 | get => maxRopeLength; 38 | set 39 | { 40 | maxRopeLength = Mathf.Clamp(value, MinRopeLength, Mathf.Infinity); 41 | if (CurrentLength > maxRopeLength) 42 | { 43 | GoalLength = maxRopeLength; 44 | } 45 | } 46 | } 47 | 48 | private float minRopeLength = .1f; 49 | public float MinRopeLength 50 | { 51 | get => minRopeLength; 52 | set 53 | { 54 | minRopeLength = Mathf.Clamp(value, 0, MaxRopeLength); 55 | if (CurrentLength < minRopeLength) 56 | { 57 | GoalLength = minRopeLength; 58 | } 59 | } 60 | } 61 | 62 | private float segmentLength = .2f; 63 | public float SegmentLength 64 | { 65 | get => segmentLength; 66 | set 67 | { 68 | // must adjust length to get rope back to proper length now 69 | // that segments have changes size 70 | float newLength = Mathf.Clamp(value, MIN_SEGMENT_LENGTH, maxRopeLength); 71 | float ratio = newLength / segmentLength; 72 | 73 | segmentLength = newLength; 74 | GoalLength = CurrentLength * ratio; 75 | } 76 | } 77 | 78 | public float PercentReleased 79 | { 80 | get 81 | { 82 | return Mathf.Clamp01((goalLength - MinRopeLength) / (MaxRopeLength - MinRopeLength)); 83 | } 84 | set 85 | { 86 | float percent = Mathf.Clamp01(value); 87 | GoalLength = ((MaxRopeLength - MinRopeLength) * percent) + MinRopeLength; 88 | } 89 | } 90 | 91 | public int Count => ropeSticks.Count; 92 | 93 | public bool IsReadOnly => true; 94 | 95 | public RopeStick this[int index] { get => ropeSticks[index]; set => ropeSticks[index] = value; } 96 | 97 | public RopeSimulatorLength( 98 | RopeBody ropeBody, 99 | float goalLength, 100 | float maxRopeLength, 101 | float minRopeLength, 102 | float segmentLength 103 | ) 104 | { 105 | this.ropeBody = ropeBody; 106 | this.goalLength = goalLength; 107 | this.maxRopeLength = maxRopeLength; 108 | this.minRopeLength = minRopeLength; 109 | this.segmentLength = segmentLength; 110 | 111 | int stickCount = Mathf.Max(ropeBody.Count - 1, 0); 112 | for(int i = 0; i < stickCount; i++) 113 | { 114 | ropeSticks.Add(new RopeStick(ropeBody[i], ropeBody[i + 1], this.segmentLength)); 115 | } 116 | this.currentLength = stickCount * this.segmentLength; 117 | 118 | ApplyLength(); 119 | } 120 | 121 | /// 122 | /// Call this onece per fixed update to apply changes in length 123 | /// 124 | // TODO: Make this directional? 125 | public void ApplyLength() 126 | { 127 | Debug.Log("apply length!"); 128 | while (currentLength < goalLength) 129 | { 130 | if ((ropeSticks.Count > 0) && ((currentLength - ropeSticks[0].Length) + segmentLength >= goalLength)) 131 | { 132 | currentLength -= ropeSticks[0].Length; 133 | ropeSticks[0].Length = goalLength - (currentLength - ropeSticks[0].Length); 134 | currentLength += ropeSticks[0].Length; 135 | } 136 | else 137 | { 138 | if (currentLength + MIN_SEGMENT_LENGTH > goalLength) break; 139 | 140 | if (ropeSticks.Count > 0) 141 | { 142 | currentLength -= ropeSticks[0].Length; 143 | ropeSticks[0].Length = SegmentLength; 144 | currentLength += ropeSticks[0].Length; 145 | } 146 | 147 | // makes sure the tip point stays locked 148 | if (ropeBody.Count > 0) 149 | { 150 | //Rope.Points[0].IsLocked = false; 151 | ropeBody[0].Position += Vector3.down * .1f; 152 | // COME UP WITH BETTER SOLUTION THAN THIS 153 | } 154 | 155 | ropeBody.AddRopePoint(new RopePoint(ropeBody[0].Position - Vector3.down * .1f), true); 156 | ropeSticks.Add(new RopeStick(ropeBody[0], ropeBody[1], segmentLength)); 157 | 158 | //InsertPoint(0, ropeBody[0].Position - Vector3.down * .1f, segmentLength); 159 | 160 | currentLength += SegmentLength; 161 | } 162 | } 163 | while (currentLength > goalLength) 164 | { 165 | //Debug.Log("prev: " + prevRopeLength); 166 | //Debug.Log("new: " + newRopeLength); 167 | //Debug.Log("stick length: " + sticks[0].Length); 168 | //Debug.Log("stick count: " + sticks.Count); 169 | //Debug.Log("point count: " + rope.Points.Count); 170 | if (ropeSticks.Count == 0) break; 171 | 172 | //if ((prevRopeLength - sticks[0].Length) > newRopeLength) 173 | if ((currentLength - ropeSticks[0].Length) > goalLength) 174 | { 175 | currentLength -= ropeSticks[0].Length; 176 | ropeBody.RemoveRopePoint(true); 177 | //RemovePointAt(0); 178 | // remove attachments if their point index is higher than the number of points that exist 179 | // either that or move them down by one? 180 | // probably better not to move them down and just implement -1 for something that is always supposed to 181 | // be attached to the end. 182 | //if(rope.Points.Count < rope.Attachments[]) 183 | //rope.Points[0].IsLocked = true; 184 | } 185 | else 186 | { 187 | //if (newRopeLength - (prevRopeLength - sticks[0].Length) < MIN_SEGMENT_LENGTH) break; 188 | if (goalLength - (currentLength - ropeSticks[0].Length) < MIN_SEGMENT_LENGTH) 189 | { 190 | currentLength -= (ropeSticks[0].Length - MIN_SEGMENT_LENGTH); 191 | //Debug.Log("stick diff: " + (sticks[0].Length - MIN_SEGMENT_LENGTH) + " prev len: " + prevRopeLength); 192 | ropeSticks[0].Length = MIN_SEGMENT_LENGTH; 193 | break; 194 | } 195 | currentLength -= ropeSticks[0].Length; 196 | ropeSticks[0].Length = goalLength - currentLength; 197 | currentLength += ropeSticks[0].Length; 198 | } 199 | } 200 | 201 | // dont return anything because current length is stored as a class variable 202 | } 203 | 204 | public IEnumerator GetEnumerator() 205 | { 206 | foreach(RopeStick s in ropeSticks) 207 | { 208 | yield return s; 209 | } 210 | } 211 | 212 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 213 | 214 | public int IndexOf(RopeStick item) => ropeSticks.IndexOf(item); 215 | 216 | public void Insert(int index, RopeStick item) => ropeSticks.Insert(index, item); 217 | 218 | public void RemoveAt(int index) => ropeSticks.RemoveAt(index); 219 | 220 | public void Add(RopeStick item) => ropeSticks.Add(item); 221 | 222 | public void Clear() => ropeSticks.Clear(); 223 | 224 | public bool Contains(RopeStick item) => ropeSticks.Contains(item); 225 | 226 | public void CopyTo(RopeStick[] array, int arrayIndex) => ropeSticks.CopyTo(array, arrayIndex); 227 | 228 | public bool Remove(RopeStick item) => ropeSticks.Remove(item); 229 | } 230 | 231 | public class RopeStick 232 | { 233 | private RopePoint pointA, pointB; 234 | public RopePoint PointA { get => pointA; } 235 | public RopePoint PointB { get => pointB; } 236 | 237 | private float length; 238 | public float Length { get => length; set => length = value; } 239 | 240 | public RopeStick(RopePoint pointA, RopePoint pointB, float length) 241 | { 242 | this.pointA = pointA; 243 | this.pointB = pointB; 244 | this.length = length; 245 | } 246 | public RopeStick(RopePoint pointA, RopeStick stickToReplace) 247 | { 248 | this.pointA = pointA; 249 | this.pointB = stickToReplace.pointB; 250 | this.length = stickToReplace.length; 251 | } 252 | public RopeStick(RopeStick stickToReplace, RopePoint pointB) 253 | { 254 | this.pointA = stickToReplace.pointA; 255 | this.pointB = pointB; 256 | this.length = stickToReplace.length; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorLength.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7cad55437564e054d92c856f0ff1a257 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorLineRenderer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | // with help from https://www.habrador.com/tutorials/interpolation/1-catmull-rom-splines/ 5 | 6 | public class RopeSimulatorLineRenderer 7 | { 8 | private LineRenderer lineRenderer; 9 | 10 | private float smoothingResolution; 11 | public float SmoothingResolution { get { return smoothingResolution; } set { smoothingResolution = Mathf.Max(value, 0); } } 12 | 13 | 14 | public RopeSimulatorLineRenderer(LineRenderer lineRenderer, float smoothingResolution) 15 | { 16 | this.lineRenderer = lineRenderer; 17 | this.smoothingResolution = smoothingResolution; 18 | } 19 | 20 | public void UpdateLineRenderer(IEnumerable ropePoints) 21 | { 22 | List basePoints = new List(); 23 | foreach (IRopePointPosition ropePoint in ropePoints) basePoints.Add(ropePoint.Position); 24 | 25 | Vector3[] newPoints = SmoothedPositions(basePoints); 26 | lineRenderer.positionCount = newPoints.Length; 27 | lineRenderer.SetPositions(newPoints); 28 | 29 | //foreach(Vector3 v3 in newPoints) Debug.DrawRay(v3, Vector3.up, Color.magenta); 30 | //for(int i = 0; i < newPoints.Length; i++) Debug.DrawRay(newPoints[i], Vector3.up, Color.Lerp(Color.black, Color.magenta, (float) i / newPoints.Length)); 31 | } 32 | 33 | private Vector3[] SmoothedPositions(IList basePoints) 34 | { 35 | if (smoothingResolution == 1 || basePoints.Count <= 2) 36 | { 37 | Vector3[] output = new Vector3[basePoints.Count]; 38 | for (int i = 0; i < basePoints.Count; i++) output[i] = basePoints[i]; 39 | // just return the unaltered list 40 | return output; 41 | } 42 | /* 43 | if (basePoints.Count == 3) 44 | { 45 | Vector3 middlePoint = basePoints[1]; 46 | basePoints[1] = Vector3.Lerp(basePoints[0], middlePoint, .5f); 47 | basePoints.Insert(2, Vector3.Lerp(middlePoint, basePoints[2], .5f)); 48 | } 49 | */ 50 | 51 | // simulates points added to the beginning and end of the rope 52 | // so that smoothing can be applied to the whole rope 53 | basePoints.Insert(0, basePoints[0] + (basePoints[0] - basePoints[1]).normalized); 54 | basePoints.Add(basePoints[basePoints.Count - 1] + (basePoints[basePoints.Count - 1] - basePoints[basePoints.Count - 2]).normalized); 55 | 56 | List newPoints = new List(); 57 | newPoints.Add(basePoints[1]); // need to add this here or it will get skipped later 58 | 59 | for (int i = 0; i < basePoints.Count; i++) 60 | { 61 | if (i == 0 || i == basePoints.Count - 2 || i == basePoints.Count - 1) 62 | { 63 | continue; 64 | } 65 | CalculatePoints(basePoints, newPoints, i); 66 | } 67 | 68 | return newPoints.ToArray(); 69 | } 70 | 71 | private void CalculatePoints(IList basePoints, IList newPoints, int index) 72 | { 73 | Vector3 p0 = basePoints[index - 1]; 74 | Vector3 p1 = basePoints[index]; 75 | Vector3 p2 = basePoints[index + 1]; 76 | Vector3 p3 = basePoints[index + 2]; 77 | 78 | Vector3 lastPos = p1; 79 | 80 | int loops = Mathf.FloorToInt(1f / smoothingResolution); 81 | float resolution = 1f / loops; 82 | 83 | for(int i = 1; i <= loops; i++) 84 | { 85 | float t = i * resolution; 86 | newPoints.Add(GetCatmullRomPosition(t, p0, p1, p2, p3)); 87 | } 88 | } 89 | 90 | private Vector3 GetCatmullRomPosition(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) 91 | { 92 | //The coefficients of the cubic polynomial (except the 0.5f * which I added later for performance) 93 | Vector3 a = 2f * p1; 94 | Vector3 b = p2 - p0; 95 | Vector3 c = 2f * p0 - 5f * p1 + 4f * p2 - p3; 96 | Vector3 d = -p0 + 3f * p1 - 3f * p2 + p3; 97 | 98 | //The cubic polynomial: a + b * t + c * t^2 + d * t^3 99 | Vector3 pos = 0.5f * (a + (b * t) + (c * t * t) + (d * t * t * t)); 100 | 101 | return pos; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorLineRenderer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2068b260cba99f743b8f09de3ef33afb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorMotion.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeSimulatorMotion 6 | { 7 | private int movementIterations = 10; 8 | public int MovementIterations { get => movementIterations; set => movementIterations = Mathf.Max(1, value); } 9 | 10 | private float maximumAngle = 60f; 11 | public float MaximumAngle { get => maximumAngle; set => maximumAngle = Mathf.Clamp(value, 0, 359); } 12 | 13 | private float ropeDrag = .1f; 14 | public float RopeDrag { get => ropeDrag; set => ropeDrag = Mathf.Clamp01(value); } 15 | 16 | private SetStickPosition1Delegate setStickPosition1; 17 | public SetStickPosition1Delegate SetStickPosition1 { 18 | get 19 | { 20 | if(setStickPosition1 == null) 21 | { 22 | setStickPosition1 = DefaultSetStickPosition1; 23 | } 24 | return setStickPosition1; 25 | } 26 | set 27 | { 28 | if(value == null) 29 | { 30 | setStickPosition1 = DefaultSetStickPosition1; 31 | } 32 | else 33 | { 34 | setStickPosition1 = value; 35 | } 36 | } 37 | } 38 | public SetStickPosition1Delegate GetDefaultSetStickPosition1{ get { return DefaultSetStickPosition1; } } 39 | 40 | private SetStickPosition2Delegate setStickPosition2; 41 | public SetStickPosition2Delegate SetStickPosition2 { 42 | get 43 | { 44 | if(setStickPosition2 == null) 45 | { 46 | setStickPosition2 = DefaultSetStickPosition2; 47 | } 48 | return setStickPosition2; 49 | } 50 | set 51 | { 52 | if(value == null) 53 | { 54 | setStickPosition2 = DefaultSetStickPosition2; 55 | } 56 | else 57 | { 58 | setStickPosition2 = value; 59 | } 60 | } 61 | } 62 | public SetStickPosition2Delegate GetDefaultSetStickPosition2 { get { return DefaultSetStickPosition2; } } 63 | 64 | 65 | public RopeSimulatorMotion( 66 | int movementIterations = 10, 67 | float maximumAngle = 60f, 68 | float ropeDrag = .1f 69 | ) 70 | { 71 | this.movementIterations = movementIterations; 72 | this.maximumAngle = maximumAngle; 73 | this.ropeDrag = ropeDrag; 74 | } 75 | 76 | /// 77 | /// Applies forces and velocity to the rope after theyve been accumulated. 78 | /// This should be called every time the rope is updated 79 | /// 80 | /// The list of points to be modified 81 | public void ApplyVelocity(IEnumerable points) 82 | where T : IRopePointPosition, IRopePointPrevPosition, IRopePointAccumulatedForce, IRopePointIsAttached 83 | { 84 | foreach (T p in points) 85 | { 86 | if (!p.IsAttached) 87 | { 88 | Vector3 positionBeforeUpdate = p.Position; 89 | // adds current velocity 90 | p.Position += (p.Position - p.PrevPosition) * (1 - ropeDrag); //multiply for drag 91 | p.Position += p.AccumulatedForce; 92 | p.PrevPosition = positionBeforeUpdate; 93 | // reset accumulated force to start building for next update 94 | p.AccumulatedForce = Vector3.zero; 95 | } 96 | } 97 | 98 | } 99 | /// 100 | /// 101 | /// 102 | /// The list of sticks to be modified 103 | public void ApplyMotion(IList sticks) 104 | { 105 | for (int i = 0; i < movementIterations; i++) 106 | { 107 | bool order = i % 2 == 0; 108 | Vector3 prevStickDir = Vector3.zero; 109 | //read forwards then backwards along the rope to improve stability 110 | for ( 111 | int j = order ? (sticks.Count - 1) : 0; 112 | order ? (j >= 0) : (j < sticks.Count); 113 | j += order ? -1 : 1 114 | ) 115 | { 116 | RopeStick s = sticks[j]; 117 | RopePoint p1 = order ? s.PointA : s.PointB; 118 | RopePoint p2 = order ? s.PointB : s.PointA; 119 | 120 | float stickDist = Vector3.Distance(p2.Position, p1.Position); 121 | Vector3 stickDir; 122 | if (stickDist != 0) 123 | { 124 | stickDir = (p1.Position - p2.Position).normalized; 125 | } 126 | else 127 | { 128 | stickDir = Vector3.down; 129 | } 130 | Vector3 stickCenter = (p1.Position + p2.Position) / 2; 131 | 132 | if (!p1.IsAttached) 133 | { 134 | //limit angle 135 | if (prevStickDir != Vector3.zero && Vector3.Angle(prevStickDir, stickDir) > maximumAngle) 136 | { 137 | stickDir = Quaternion.AngleAxis(maximumAngle, (Vector3.Cross(prevStickDir, stickDir))) * prevStickDir; 138 | } 139 | SetStickPosition1(ref s, ref stickCenter, ref stickDir, order, sticks.Count - j); 140 | } 141 | 142 | if (!p2.IsAttached) 143 | { 144 | SetStickPosition2(ref s, ref stickCenter, ref stickDir, order); 145 | } 146 | 147 | prevStickDir = stickDir; 148 | 149 | /* 150 | Debug.DrawRay(p2.Position, stickDir * s.Length, Color.Lerp(Color.black, order ? Color.green : Color.red, (float)i / movementIterations)); 151 | if (i == movementIterations - 1) 152 | { 153 | //Debug.DrawRay(p2.Position, stickDir * s.Length, Color.blue); 154 | } 155 | */ 156 | } 157 | } 158 | } 159 | 160 | /// 161 | /// Set the final stick position for an iteration. This is for the first point on the stick 162 | /// 163 | /// The stick to use 164 | /// The center of the stick 165 | /// The direction of the stick 166 | /// Are we reading forward or backward 167 | private void DefaultSetStickPosition1(ref RopeStick s, ref Vector3 stickCenter, ref Vector3 stickDirection, bool order, int stickIndex = 0) 168 | { 169 | IRopePointPosition p = order ? s.PointA : s.PointB; 170 | p.Position = stickCenter + stickDirection * s.Length / 2; 171 | } 172 | /// 173 | /// Set the final stick position for an iteration. This is for the second point on the stick 174 | /// 175 | /// The stick to use 176 | /// The center of the stick 177 | /// The direction of the stick 178 | /// Are we reading forward or backward 179 | private void DefaultSetStickPosition2(ref RopeStick s, ref Vector3 stickCenter, ref Vector3 stickDirection, bool order) 180 | { 181 | IRopePointPosition p = order ? s.PointB : s.PointA; 182 | p.Position = stickCenter - stickDirection * s.Length / 2; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorMotion.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: faee87e02c278fb43bdfa238fe994a21 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorRigidbody.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | using System; 6 | 7 | public class RopeSimulatorRigidbody 8 | { 9 | private readonly float MIN_COLLISION_ROTATION = 1f; 10 | private RopeSimulatorMotion ropeSimulatorMotion; 11 | public RopeSimulatorMotion RopeSimulatorMotion { get { return ropeSimulatorMotion; } } 12 | 13 | private LayerMask layerMask; 14 | public LayerMask LayerMask { set => layerMask = value; } 15 | 16 | private int collisionIterations = 2; 17 | public int CollisionIterations { get => collisionIterations; set => collisionIterations = Mathf.Max(value, 1); } 18 | 19 | private RopeContactChain contacts = new RopeContactChain(); 20 | public List Contacts { get { return contacts.ToList(); } } 21 | 22 | //private RaycastHit hit, inverseHit; 23 | 24 | public RopeSimulatorRigidbody( 25 | RopeSimulatorMotion ropeSimulatorMotion, 26 | LayerMask layerMask, 27 | int collisionIterations = 2 28 | ) 29 | { 30 | this.ropeSimulatorMotion = ropeSimulatorMotion; 31 | this.ropeSimulatorMotion.SetStickPosition1 = SetStickPosition1; 32 | this.ropeSimulatorMotion.SetStickPosition2 = SetStickPosition2; 33 | 34 | this.layerMask = layerMask; 35 | this.collisionIterations = collisionIterations; 36 | } 37 | 38 | /// 39 | /// Sets up contacts for a new time step 40 | /// 41 | public void RefreshContacts(IEnumerable attachments, int pointCount) 42 | { 43 | // contacts are recalculated each update. attachments must be manually added each frame 44 | contacts.Clear(); 45 | 46 | foreach(RopeAttachment attachment in attachments) 47 | { 48 | contacts.Insert(new RopeContactAttachment(attachment.CorrectedPointIndex(pointCount), attachment.Position, attachment.Rigidbody)); 49 | } 50 | } 51 | 52 | private void SetStickPosition1(ref RopeStick s, ref Vector3 stickCenter, ref Vector3 stickDirection, bool order, int stickIndex = 0) 53 | { 54 | RopePoint p1 = order ? s.PointA : s.PointB; 55 | RopePoint p2 = order ? s.PointB : s.PointA; 56 | StickCollision(stickIndex, order, ref stickCenter, ref p2, ref p1, ref stickDirection, out RopeContact contact); 57 | if (contact != null) 58 | { 59 | contacts.Insert(contact); 60 | } 61 | 62 | p1.Position = Vector3.Lerp(stickCenter + stickDirection * s.Length / 2, p1.Position, p1.Friction); 63 | } 64 | 65 | private void SetStickPosition2(ref RopeStick s, ref Vector3 stickCenter, ref Vector3 stickDirection, bool order) 66 | { 67 | RopePoint p = order ? s.PointB : s.PointA; 68 | p.Position = Vector3.Lerp(stickCenter - stickDirection * s.Length / 2, p.Position, p.Friction); 69 | } 70 | 71 | /// 72 | /// Calculates collision with a stick 73 | /// 74 | private void StickCollision(int pointIndex, bool order, ref Vector3 stickCenter, ref T startPoint, ref T endPoint, ref Vector3 stickDir, out RopeContact contact) 75 | where T : IRopePointPosition, IRopePointPrevPosition, IRopePointFriction 76 | { 77 | float stickLength = Vector3.Distance(startPoint.Position, endPoint.Position); 78 | contact = null; 79 | int collisionsCount = 0; 80 | while (true) 81 | { 82 | Ray ray = new Ray(startPoint.Position, stickDir); 83 | if (Physics.Raycast(ray, out RaycastHit hit, stickLength, layerMask)) 84 | { 85 | //special case if opposite ray hits a wall too, calculate minimum rotation to resolve the collision 86 | //could calculate this with binary search just rotating the vector until found 87 | //or could do some trigonometry but potentially be a little less accurate in some cases. 88 | Ray inverseRay = new Ray(endPoint.Position, -stickDir); 89 | if (Physics.Raycast(inverseRay, out RaycastHit inverseHit, stickLength, layerMask)) 90 | { 91 | endPoint.Friction = 1 - (hit.distance / stickLength); 92 | 93 | stickDir = BinarySearchCollision(startPoint.Position, stickDir, stickLength, TriangleCollisionVector(hit, stickDir, stickLength)); 94 | //contact point can only update around corners 95 | contact = new RopeContactCollision 96 | ( 97 | pointIndex, 98 | (hit.point + inverseHit.point) / 2, 99 | -(order ? hit.normal : inverseHit.normal), 100 | -(order ? inverseHit.normal : hit.normal), 101 | hit.collider.attachedRigidbody 102 | ); 103 | } 104 | else 105 | { 106 | stickDir = TriangleCollisionVector(hit, stickDir, stickLength); 107 | } 108 | } 109 | else 110 | { 111 | if (collisionsCount > 0) 112 | { 113 | //Debug.DrawRay(ray.origin, ray.direction * stickLength, Color.red); 114 | endPoint.Position = startPoint.Position + stickDir * stickLength; 115 | //Debug.DrawRay(endPoint.position, Vector3.up * .1f, Color.yellow); 116 | //Debug.DrawRay(endPoint.prevPosition, Vector3.up * .1f, Color.green); 117 | endPoint.PrevPosition = endPoint.Position; 118 | Vector3 vectorToCenter = (stickDir * Vector3.Distance(startPoint.Position, stickCenter)); 119 | stickCenter = startPoint.Position + vectorToCenter; 120 | } 121 | else 122 | { 123 | endPoint.Friction = 0; 124 | } 125 | break; 126 | } 127 | //need to come up with a better solution for what to do in this case 128 | if (collisionsCount >= collisionIterations) 129 | { 130 | endPoint.Position = startPoint.Position + stickDir * stickLength; 131 | endPoint.PrevPosition = endPoint.Position; 132 | Vector3 vectorToCenter = (stickDir * Vector3.Distance(startPoint.Position, stickCenter)); 133 | stickCenter = startPoint.Position + vectorToCenter; 134 | break; 135 | } 136 | 137 | collisionsCount++; 138 | } 139 | } 140 | 141 | private Vector3 TriangleCollisionVector(RaycastHit hit, Vector3 stickDir, float stickLength) 142 | { 143 | //Debug.DrawRay(hit.point, hit.normal * .2f, Color.green); 144 | Vector3 fTan = Vector3.Cross(hit.normal, stickDir); 145 | Vector3 rTan = Vector3.Cross(hit.normal, fTan); 146 | //Debug.DrawRay(hit.point, fTan * .2f, Color.magenta); 147 | //Debug.DrawRay(hit.point, rTan * .2f, Color.cyan); 148 | //angle from plane of initial triangle 149 | float sA = Mathf.Abs(Vector3.SignedAngle(rTan, -stickDir, fTan)); 150 | //angle to plane of initial triangle 151 | float sB = 90 - sA; 152 | //height from plane for both triangles 153 | float a = Mathf.Sin(Mathf.Deg2Rad * sA) * hit.distance; 154 | //a^2 + b^2 = c^2 155 | //c^2 - a^2 = b^2 156 | //length along plane of final triangle 157 | //angle to plane of final triangle 158 | float fB = Mathf.Rad2Deg * Mathf.Acos(a / stickLength); 159 | 160 | float rotAngle = (fB - sB) + MIN_COLLISION_ROTATION;//add one just to make sure its rotated enough to be outside the collider 161 | return Quaternion.AngleAxis(rotAngle, -fTan) * stickDir; 162 | } 163 | 164 | private Vector3 TriangleSurfaceTangent(RaycastHit hit, Vector3 stickDir) 165 | { 166 | Vector3 fTan = Vector3.Cross(hit.normal, stickDir); 167 | return Vector3.Cross(hit.normal, fTan).normalized; 168 | } 169 | 170 | private Vector3 BinarySearchCollision(Vector3 stickStart, Vector3 stickDir, float stickLength, Vector3 maxStickDir) 171 | { 172 | Vector3 bestDir = maxStickDir; 173 | //with 5 iterations we can get near 1 degree of accuracy 174 | int iterations = 6; 175 | float max = 1; 176 | float min = 0; 177 | for (int i = 0; i < iterations; i++) 178 | { 179 | float percent = min + ((max - min) / 2); 180 | Vector3 currentDir = Vector3.Slerp(stickDir, maxStickDir, percent); 181 | Ray searchRay = new Ray(stickStart, currentDir); 182 | //recalculate if the ray made a collision 183 | if (Physics.Raycast(searchRay, stickLength, layerMask)) 184 | { 185 | min = percent; 186 | } 187 | else 188 | { 189 | max = percent; 190 | bestDir = currentDir; 191 | } 192 | } 193 | return bestDir; 194 | } 195 | 196 | private Vector3 BinarySearchTriangle(Vector3 stickStart, Vector3 stickDir, float stickLength, Vector3 stickDown, Vector3 rightMaxStickDir, Vector3 leftMaxStickDir) 197 | { 198 | int iterations = 6; 199 | 200 | Vector3 rightBestDir = rightMaxStickDir; 201 | float rMax = 1; 202 | float rMin = 0; 203 | for (int i = 0; i < iterations; i++) 204 | { 205 | float percent = rMin + ((rMax - rMin) / 2); 206 | Vector3 currentDir = Vector3.Slerp(stickDir, rightMaxStickDir, percent); 207 | Ray searchRay = new Ray(stickStart, currentDir); 208 | //recalculate if the ray made a collision 209 | if (Physics.Raycast(searchRay, stickLength, layerMask)) 210 | { 211 | rMin = percent; 212 | } 213 | else 214 | { 215 | rMax = percent; 216 | rightBestDir = currentDir; 217 | } 218 | } 219 | 220 | Vector3 leftBestDir = leftMaxStickDir; 221 | float lMax = 1; 222 | float lMin = 0; 223 | for (int i = 0; i < iterations; i++) 224 | { 225 | float percent = lMin + ((lMax - lMin) / 2); 226 | Vector3 currentDir = Vector3.Slerp(stickDir, leftMaxStickDir, percent); 227 | Ray searchRay = new Ray(stickStart, currentDir); 228 | //recalculate if the ray made a collision 229 | if (Physics.Raycast(searchRay, stickLength, layerMask)) 230 | { 231 | lMin = percent; 232 | } 233 | else 234 | { 235 | lMax = percent; 236 | leftBestDir = currentDir; 237 | } 238 | } 239 | 240 | float rBestAngle = Vector3.Angle(stickDown, rightBestDir); 241 | float lBestAngle = Vector3.Angle(stickDown, leftBestDir); 242 | Vector3 bestDir = lBestAngle < rBestAngle ? leftBestDir : rightBestDir; 243 | 244 | return bestDir; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorRigidbody.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c06fbee86e6c10f4ca7de8175d6d889e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorTension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeSimulatorTension 6 | { 7 | private readonly float MAX_TENSION_CONTRIBUTION = 5f; 8 | 9 | private RopeSimulatorRigidbody ropeSimulatorRigidbody; 10 | public RopeSimulatorRigidbody RopeSimulatorRigidbody { get { return ropeSimulatorRigidbody; } } 11 | 12 | private float thresholdTension = 1.33f; 13 | public float ThresholdTension { get => thresholdTension; set => thresholdTension = value; } 14 | 15 | private float springStrength = 100f; 16 | public float SpringStrength { get => springStrength; set => springStrength = value; } 17 | 18 | private float dampingStrength = 1; 19 | public float DampingStrength { get => dampingStrength; set => dampingStrength = value; } 20 | 21 | public RopeSimulatorTension( 22 | RopeSimulatorRigidbody ropeSimulatorRigidbody, 23 | float thresholdTension = 1.33f, 24 | float springStrength = 100f, 25 | float dampingStrength = 1 26 | ) 27 | { 28 | this.ropeSimulatorRigidbody = ropeSimulatorRigidbody; 29 | this.thresholdTension = thresholdTension; 30 | this.springStrength = springStrength; 31 | this.dampingStrength = dampingStrength; 32 | } 33 | 34 | /// 35 | /// Uses tension and contacts to exert forces along the rope 36 | /// 37 | /// 38 | public void ExertForces(IList sticks, float timeStep) 39 | { 40 | /* 41 | if (tensionAcrossRope > 0) 42 | { 43 | float tensionDamping = tensionAcrossRope * dampingStrength; 44 | float tensionSpring = tensionAcrossRope * springStrength; 45 | foreach (Contact contact in contacts) 46 | { 47 | contact.LimitVelocity(tensionDamping); 48 | contact.ApplyForce(tensionSpring, timeStep); 49 | } 50 | } 51 | */ 52 | 53 | foreach (RopeContact contact in ropeSimulatorRigidbody.Contacts) 54 | { 55 | float lowSectionTension = 0; 56 | float highSectionTension = 0; 57 | if (contact.LowContact != null) 58 | { 59 | int count = 0; 60 | for (int i = contact.LowContact.PointIndex; i < contact.PointIndex; i++) 61 | { 62 | lowSectionTension += Mathf.Clamp( 63 | (Vector3.Distance(sticks[i].PointA.Position, sticks[i].PointB.Position) / sticks[i].Length), 64 | 0, 65 | MAX_TENSION_CONTRIBUTION 66 | ); 67 | count++; 68 | } 69 | lowSectionTension /= count; 70 | lowSectionTension = Mathf.Max((lowSectionTension / thresholdTension) - 1, 0); 71 | } 72 | 73 | if (contact.HighContact != null) 74 | { 75 | int count = 0; 76 | for (int i = contact.PointIndex; i < contact.HighContact.PointIndex; i++) 77 | { 78 | highSectionTension += Mathf.Clamp( 79 | (Vector3.Distance(sticks[i].PointA.Position, sticks[i].PointB.Position) / sticks[i].Length), 80 | 0, 81 | MAX_TENSION_CONTRIBUTION 82 | ); 83 | count++; 84 | } 85 | highSectionTension /= count; 86 | highSectionTension = Mathf.Max((highSectionTension / thresholdTension) - 1, 0); 87 | } 88 | 89 | if (lowSectionTension > 0 || highSectionTension > 0) 90 | { 91 | float tensionDamping = (lowSectionTension * dampingStrength) + (highSectionTension * dampingStrength); 92 | float tensionSpring = (lowSectionTension * springStrength) + (highSectionTension * springStrength); 93 | float vectorLerp = highSectionTension / (lowSectionTension + highSectionTension); 94 | contact.LimitVelocity(tensionDamping, vectorLerp); 95 | contact.ApplyForce(tensionSpring, vectorLerp, timeStep); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorTension.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19c5a8b9ea1e5ce428aec1cd4e58e970 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorTrigger.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RopeSimulatorTrigger 6 | { 7 | private LayerMask layerMask; 8 | public LayerMask LayerMask { set => layerMask = value; } 9 | 10 | private bool triggerEnabled = true; 11 | public bool TriggerEnabled { set { triggerEnabled = value; } } 12 | 13 | public RopeSimulatorTrigger( 14 | LayerMask layerMask, 15 | bool triggerEnabled = true 16 | ) 17 | { 18 | this.layerMask = layerMask; 19 | this.triggerEnabled = triggerEnabled; 20 | } 21 | 22 | public List GatherContacts(IEnumerable sticks) 23 | { 24 | List contacts = new List(); 25 | if (!triggerEnabled) return contacts; 26 | 27 | int count = 0; 28 | foreach (RopeStick stick in sticks) 29 | { 30 | Ray ray = new Ray(stick.PointA.Position, (stick.PointB.Position - stick.PointA.Position).normalized); 31 | if (Physics.Raycast(ray, out RaycastHit hit, stick.Length, layerMask)) 32 | { 33 | Rigidbody rb = hit.transform.GetComponent(); 34 | if (rb == null) continue; 35 | contacts.Add(new RopeContactTrigger(count, hit.point, rb)); 36 | GameObject go = hit.transform.gameObject; 37 | } 38 | count++; 39 | } 40 | return contacts; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope Simulators/RopeSimulatorTrigger.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 33401efaac6077c4abadd26a266d6610 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e6b346999b8b8c542bd15b83774eb915 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope/Rope.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | using System; 6 | 7 | // TODO: with only one collision, find a better method for determining which direciton to rotate 8 | // TODO: with two collisions on a sharp surface, determin which direciton to rotate in 9 | 10 | public class Rope 11 | { 12 | private RopeGameObject ropeGameObject; 13 | public RopeGameObject RopeGameObject { get { return ropeGameObject; } } 14 | 15 | private RopeBody ropeBody; 16 | public RopeBody RopeBody { get { return ropeBody; } } 17 | 18 | private RopeUpdater ropeUpdater; 19 | public RopeUpdater RopeUpdater { get { return ropeUpdater; } } 20 | 21 | public Rope( 22 | RopeGameObject ropeGameObject, 23 | RopeBuilder ropeBuilder 24 | ) 25 | { 26 | this.ropeGameObject = ropeGameObject; 27 | this.ropeBody = new RopeBody(ropeBuilder); 28 | this.ropeUpdater = new RopeUpdater(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope/Rope.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ec0c96db5e2c2b4c9d9bada9f5dfd32 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope/RopeGameObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System; 4 | using UnityEngine; 5 | using System.Linq; 6 | 7 | public class RopeGameObject : MonoBehaviour 8 | { 9 | [SerializeField] 10 | private int initialSegmentCount = 20; 11 | 12 | private Rope rope; 13 | 14 | private List activeComponents = new List(); 15 | private List waitingComponents = new List(); 16 | 17 | /* 18 | public T GetRopeComponent() where T : RopeActorBase 19 | { 20 | return (T)ropeComponents.FirstOrDefault(x => x.GetType() == typeof(T)); 21 | } 22 | */ 23 | 24 | public void RequestComponentInitialization(RopeComponentBase ropeComponent) 25 | { 26 | // check availability will always be true if there are 0 27 | // required components 28 | if(CheckAvailability(ropeComponent, out IEnumerable requiredComponents)) 29 | { 30 | if(InitializeComponent(ropeComponent, requiredComponents)) CheckWaitingList(); 31 | } 32 | else 33 | { 34 | AddToWaitingList(ropeComponent); 35 | } 36 | } 37 | 38 | private bool InitializeComponent(RopeComponentBase addedComponent, IEnumerable requiredActors) 39 | { 40 | // make sure it cant add twice 41 | if (activeComponents.Find(x => x.GetType() == addedComponent.GetType()) == null) 42 | { 43 | addedComponent.Initialize(rope, requiredActors); 44 | activeComponents.Add(addedComponent); 45 | return true; 46 | } 47 | else return false; 48 | // log error for trying to add multiple of the same? 49 | } 50 | 51 | private bool AddToWaitingList(RopeComponentBase waitingComponent) 52 | { 53 | // make sure it cant add twice 54 | if (waitingComponents.Find(x => x.GetType() == waitingComponent.GetType()) == null) 55 | { 56 | waitingComponents.Add(waitingComponent); 57 | return true; 58 | } 59 | // log error for trying to add multiple of the same? 60 | else return false; 61 | } 62 | 63 | private void CheckWaitingList() 64 | { 65 | List componentsToSwap = new List(); 66 | IEnumerable requiredActors = new List(); 67 | foreach (RopeComponentBase waitingComponent in waitingComponents) 68 | { 69 | if(CheckAvailability(waitingComponent, out requiredActors)) componentsToSwap.Add(waitingComponent); 70 | } 71 | foreach(RopeComponentBase swapComponent in componentsToSwap) 72 | { 73 | waitingComponents.Remove(swapComponent); 74 | InitializeComponent(swapComponent, requiredActors); 75 | } 76 | if(componentsToSwap.Count > 0) CheckWaitingList(); 77 | } 78 | 79 | private bool CheckAvailability(RopeComponentBase checkingComponent, out IEnumerable requiredActors) 80 | { 81 | List requiredList = new List(); 82 | 83 | foreach (Type t in checkingComponent.RequiredActorTypes) 84 | { 85 | /* 86 | foreach(RopeComponentBase rcb in activeComponents) 87 | { 88 | Debug.Log("Goal type: " + t); 89 | Debug.Log("Actual type: " + rcb.RopeActor.GetType()); 90 | Debug.Log("match? " + (rcb.RopeActor.GetType() == t)); 91 | } 92 | Debug.Log(activeComponents.FirstOrDefault(x => x.RopeActor.GetType() == t)); 93 | */ 94 | RopeComponentBase match = activeComponents.FirstOrDefault(x => x.RopeActor.GetType() == t); 95 | if(match == null) 96 | { 97 | requiredActors = requiredList; 98 | return false; 99 | } 100 | else 101 | { 102 | requiredList.Add(match.RopeActor); 103 | } 104 | } 105 | requiredActors = requiredList; 106 | return true; 107 | } 108 | 109 | 110 | public void DisableComponent(RopeComponentBase ropeComponent) 111 | { 112 | activeComponents.Remove(ropeComponent); 113 | // TODO: create a safe solution for disabling components 114 | 115 | } 116 | 117 | // Start is called before the first frame update 118 | // TODO: Move this off of this gameobject? 119 | void Awake() 120 | { 121 | RopeComponentAttachments attachmentComponent = GetComponent(); 122 | RopeBuilder ropeBuilder; 123 | if (attachmentComponent != null) 124 | { 125 | ropeBuilder = new RopeBuilderAttachments(attachmentComponent.InitialAttachments, initialSegmentCount); 126 | } 127 | else 128 | { 129 | ropeBuilder = new RopeBuilderFromTo(Vector3.one * -5, Vector3.one * 5, initialSegmentCount); 130 | } 131 | 132 | rope = new Rope( 133 | this, 134 | ropeBuilder 135 | ); 136 | } 137 | 138 | // Update is called once per frame 139 | void FixedUpdate() 140 | { 141 | rope.RopeUpdater.UpdateRope(Time.fixedDeltaTime); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /Runtime/Scripts/Rope/RopeGameObject.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3d5e526753d503a4492584fb16c672ba 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/weston-wright.rope-simulation.runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weston-wright.rope-simulation." 3 | } 4 | -------------------------------------------------------------------------------- /Runtime/weston-wright.rope-simulation.runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 223d7de3a5952fa4aac9541c5b86d384 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Weston Wright 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.weston-wright.rope-simulation-unity", 3 | "description": "3D simulation of ropes with optional components to control behaviors", 4 | "version": "0.9.0", 5 | "unity": "2021.3", 6 | "unityRelease": "3f1", 7 | "displayName": "Rope Simulation", 8 | "dependencies": { 9 | }, 10 | "keywords": [ 11 | "rope", 12 | "simulation", 13 | "physics", 14 | "collision", 15 | "chain" 16 | ], 17 | "type": "sample", 18 | "hideInEditor": false 19 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e0a2a243007838f47ba618dfb0f85a78 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------