{"TotalCount":1,"Files":[{"Ident":"facepunch.motionpath","Path":"Code/MotionPath.cs","FileName":"MotionPath.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":56584,"Code":"using Sandbox;\r\nusing System;\r\nusing System.Linq;\r\n\r\n/// \u003Csummary\u003E\r\n/// Move an object around a path.\r\n/// \u003C/summary\u003E\r\n[Title( \u0022Motion Path\u0022 )]\r\npublic class MotionPath : Component, Component.ExecuteInEditor\r\n{\r\n\t/// \u003Csummary\u003E\r\n\t/// The object that moves around this path.\r\n\t/// \u003C/summary\u003E\r\n\t[Property]\r\n\tpublic GameObject Target { get; set; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Normalized time between 0 and 1 for how far the target is on this path.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, Range( 0.0f, 1.0f )]\r\n\tpublic float Time { get; set; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Rotate the target using the rotation of points, otherwise use the curvature of the path.\r\n\t/// \u003C/summary\u003E\r\n\t[Property]\r\n\tpublic bool UsePointRotation { get; set; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Time is controlled manually.\r\n\t/// \u003C/summary\u003E\r\n\t[Property]\r\n\tpublic bool Manual { get; set; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// How long the path takes to complete.\r\n\t/// \u003C/summary\u003E\r\n\t[Property, ShowIf( nameof( Manual ), false )]\r\n\tpublic float Duration { get; set; } = 10;\r\n\r\n\tpublic enum SplineType\r\n\t{\r\n\t\tTcb,\r\n\t\tCatmullRom\r\n\t};\r\n\r\n\t[Title( \u0022Mode\u0022 ), Group( \u0022Spline\u0022 )]\r\n\t[Property] public SplineType SplineMode { get; set; }\r\n\r\n\t[Title( \u0022Tension\u0022 ), Group( \u0022Spline\u0022 ), ShowIf( nameof( SplineMode ), SplineType.Tcb )]\r\n\t[Property, Range( -1, 1 )] public float SplineTension { get; set; }\r\n\r\n\t[Title( \u0022Continuity\u0022 ), Group( \u0022Spline\u0022 ), ShowIf( nameof( SplineMode ), SplineType.Tcb )]\r\n\t[Property, Range( -1, 1 )] public float SplineContinuity { get; set; }\r\n\r\n\t[Title( \u0022Bias\u0022 ), Group( \u0022Spline\u0022 ), ShowIf( nameof( SplineMode ), SplineType.Tcb )]\r\n\t[Property, Range( -1, 1 )] public float SplineBias { get; set; }\r\n\r\n\tprivate Vector3[] _previewPoints;\r\n\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tbase.OnStart();\r\n\r\n\t\tif ( Scene.IsEditor )\r\n\t\t{\r\n\t\t\tif ( !GameObject.GetAllObjects( true )\r\n\t\t\t\t.Select( x =\u003E x.Components.Get\u003CMotionPathPoint\u003E() )\r\n\t\t\t\t.Where( x =\u003E x.IsValid() )\r\n\t\t\t\t.Any() )\r\n\t\t\t{\r\n\t\t\t\tvar go = new GameObject( true, \u0022Point\u0022 );\r\n\t\t\t\tgo.SetParent( GameObject, false );\r\n\t\t\t\tgo.Components.Create\u003CMotionPathPoint\u003E( true );\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void DrawGizmos()\r\n\t{\r\n\t\tbase.DrawGizmos();\r\n\r\n\t\tif ( _previewPoints is null )\r\n\t\t\treturn;\r\n\r\n\t\tusing ( Gizmo.Hitbox.LineScope() )\r\n\t\t{\r\n\t\t\tfor ( var i = 0; i \u003C _previewPoints.Length - 1; i\u002B\u002B )\r\n\t\t\t{\r\n\t\t\t\tvar pointA = Transform.World.PointToLocal( _previewPoints[i] );\r\n\t\t\t\tvar pointB = Transform.World.PointToLocal( _previewPoints[i \u002B 1] );\r\n\t\t\t\tGizmo.Draw.Color = Gizmo.IsSelected ? Gizmo.Colors.Active : Gizmo.IsHovered ? Gizmo.Colors.Hovered : Color.White.WithAlpha( 0.5f );\r\n\t\t\t\tGizmo.Draw.LineThickness = Gizmo.IsHovered || Gizmo.IsSelected ? 2 : 1;\r\n\t\t\t\tGizmo.Draw.Line( pointA, pointB );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif ( Target.IsValid() )\r\n\t\t{\r\n\t\t\tGizmo.Draw.Color = Gizmo.Colors.Forward;\r\n\t\t\tvar pointA = Transform.World.PointToLocal( Target.Transform.Position );\r\n\t\t\tvar pointB = Transform.World.NormalToLocal( Target.Transform.Rotation.Forward ) * 5;\r\n\t\t\tGizmo.Draw.SolidCone( pointA, pointB, 2 );\r\n\t\t}\r\n\t}\r\n\r\n\tprivate Vector3 GetPointOnSpline( Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float delta )\r\n\t{\r\n\t\tif ( SplineMode == SplineType.Tcb )\r\n\t\t\treturn Vector3.TcbSpline( p0, p1, p2, p3, SplineTension, SplineContinuity, SplineBias, delta );\r\n\t\telse if ( SplineMode == SplineType.CatmullRom )\r\n\t\t\treturn Vector3.CatmullRomSpline( p0, p1, p2, p3, delta );\r\n\r\n\t\treturn default;\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tbase.OnUpdate();\r\n\r\n\t\tvar motionPoints = GameObject.GetAllObjects( true )\r\n\t\t\t.Select( x =\u003E x.Components.Get\u003CMotionPathPoint\u003E() )\r\n\t\t\t.Where( x =\u003E x.IsValid() )\r\n\t\t\t.ToArray();\r\n\r\n\t\tvar points = motionPoints.Select( x =\u003E x.Transform.Position )\r\n\t\t\t.ToArray();\r\n\r\n\t\tif ( SplineMode == SplineType.Tcb )\r\n\t\t\t_previewPoints = points.TcbSpline( 32, SplineTension, SplineContinuity, SplineBias ).ToArray();\r\n\t\telse if ( SplineMode == SplineType.CatmullRom )\r\n\t\t\t_previewPoints = points.CatmullRomSpline( 32 ).ToArray();\r\n\r\n\t\tif ( points.Length \u003C 2 )\r\n\t\t\treturn;\r\n\r\n\t\tif ( points.Length == 2 )\r\n\t\t\t_previewPoints = new Vector3[2] { points[0], points[1] };\r\n\r\n\t\tif ( Target.IsValid() )\r\n\t\t{\r\n\t\t\tvar pointCount = points.Length;\r\n\r\n\t\t\tif ( !Manual \u0026\u0026 !Duration.AlmostEqual( 0.0f ) )\r\n\t\t\t\tTime = Sandbox.Time.Now % Duration / Duration;\r\n\r\n\t\t\tvar time = Time.Clamp( 0.0f, 1.0f );\r\n\r\n\t\t\tvar segmentIndex = (int)((pointCount - 1) * time);\r\n\t\t\tsegmentIndex = Math.Min( segmentIndex, pointCount - 2 );\r\n\t\t\tvar delta = ((pointCount - 1) * time) - segmentIndex;\r\n\r\n\t\t\tvar p0 = segmentIndex \u003E 0 ? points[segmentIndex - 1] : points[0];\r\n\t\t\tvar p1 = points[segmentIndex];\r\n\t\t\tvar p2 = segmentIndex \u003C pointCount - 1 ? points[segmentIndex \u002B 1] : points[pointCount - 1];\r\n\t\t\tvar p3 = segmentIndex \u003C pointCount - 2 ? points[segmentIndex \u002B 2] : points[pointCount - 1];\r\n\r\n\t\t\tvar offset = time.AlmostEqual( 1.0f ) ? -0.01f : 0.01f;\r\n\t\t\tvar positionA = GetPointOnSpline( p0, p1, p2, p3, delta );\r\n\r\n\t\t\tif ( UsePointRotation )\r\n\t\t\t{\r\n\t\t\t\tvar r1 = motionPoints[segmentIndex];\r\n\t\t\t\tvar r2 = segmentIndex \u003C pointCount - 1 ? motionPoints[segmentIndex \u002B 1] : motionPoints[pointCount - 1];\r\n\t\t\t\tvar rotationStart = r1.Transform.Rotation;\r\n\t\t\t\tvar rotationEnd = r2.Transform.Rotation;\r\n\r\n\t\t\t\tif ( r1.LookAt.IsValid() )\r\n\t\t\t\t\trotationStart = Rotation.LookAt( (r1.LookAt.Transform.Position - r1.Transform.Position).Normal );\r\n\r\n\t\t\t\tif ( r2.LookAt.IsValid() )\r\n\t\t\t\t\trotationEnd = Rotation.LookAt( (r2.LookAt.Transform.Position - r2.Transform.Position).Normal );\r\n\r\n\t\t\t\tTarget.Transform.Rotation = Rotation.Slerp( rotationStart, rotationEnd, delta );\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tvar positionB = GetPointOnSpline( p0, p1, p2, p3, delta \u002B offset );\r\n\t\t\t\tvar forward = (positionB - positionA).Normal * MathF.Sign( offset );\r\n\t\t\t\tvar right = forward.Cross( Vector3.Up ).Normal;\r\n\t\t\t\tvar up = right.Cross( forward ).Normal;\r\n\r\n\t\t\t\tTarget.Transform.Rotation = Rotation.LookAt( forward, up );\r\n\t\t\t}\r\n\r\n\t\t\tTarget.Transform.Position = positionA;\r\n\t\t}\r\n\t}\r\n}\r\n\r\npublic class MotionPathPoint : Component, Component.ExecuteInEditor\r\n{\r\n\t/// \u003Csummary\u003E\r\n\t/// Look at this object.\r\n\t/// \u003C/summary\u003E\r\n\t[Property]\r\n\tpublic GameObject LookAt { get; set; }\r\n\r\n\tprotected override void DrawGizmos()\r\n\t{\r\n\t\tbase.DrawGizmos();\r\n\r\n\t\tconst float radius = 2.0f;\r\n\t\tGizmo.Hitbox.DepthBias = 0.1f;\r\n\t\tGizmo.Hitbox.Sphere( new Sphere( 0, radius ) );\r\n\t\tGizmo.Draw.Color = Gizmo.IsSelected ? Gizmo.Colors.Active : Gizmo.IsHovered ? Gizmo.Colors.Hovered : Color.White;\r\n\t\tGizmo.Draw.SolidSphere( 0, radius );\r\n\r\n\t\tif ( Gizmo.IsSelected || Gizmo.IsHovered )\r\n\t\t{\r\n\t\t\tvar end = LookAt.IsValid() ? Transform.World.PointToLocal( LookAt.Transform.Position ) : Vector3.Forward * 6;\r\n\t\t\tGizmo.Draw.Color = Gizmo.Colors.Forward;\r\n\t\t\tGizmo.Draw.Arrow( 0, end, 2, 1 );\r\n\t\t}\r\n\t}\r\n}\r\n"}]}