{"TotalCount":11,"Files":[{"Ident":"slfy.tiaotiao","Path":"Jump/JumpPiece.cs","FileName":"JumpPiece.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"using System;\n\n/// \u003Csummary\u003E\n/// \u8DF3\u4E00\u8DF3\u7684\u0022\u68CB\u5B50\u0022\uFF1A\u7403\u4F53\u6A21\u578B\uFF0C\u4F4D\u7F6E\u7531 JumpGame \u9A71\u52A8\uFF08BasePosition = \u6A21\u578B\u5E95\u90E8\u4E2D\u5FC3\uFF09\uFF0C\n/// \u8FD9\u91CC\u53EA\u8D1F\u8D23\u84C4\u529B\u538B\u6241 / \u8D77\u8DF3\u62C9\u4F38 / \u843D\u5730\u56DE\u5F39\u7684\u5F62\u53D8\u8868\u73B0\u3002\n/// \u003C/summary\u003E\npublic sealed class JumpPiece : Component\n{\n\tconst float PieceDiameter = 54f;\n\tconst float Stiffness = 170f;\n\tconst float Damping = 15f;\n\n\t/// \u003Csummary\u003E \u68CB\u5B50\u534A\u5F84\uFF08\u4E16\u754C\u5355\u4F4D\uFF09 \u003C/summary\u003E\n\tpublic float Radius { get; private set; } = 27f;\n\n\tModelRenderer _renderer;\n\tVector3 _baseScale = Vector3.One;\n\tfloat _charge;\n\tfloat _squash;\n\tfloat _squashVel;\n\n\t/// \u003Csummary\u003E \u6A21\u578B\u5E95\u90E8\u4E2D\u5FC3\u7684\u4E16\u754C\u5750\u6807 \u003C/summary\u003E\n\tpublic Vector3 BasePosition =\u003E GameObject.WorldPosition - Vector3.Up * Radius;\n\n\tpublic void SetBasePosition( Vector3 basePos ) =\u003E GameObject.WorldPosition = basePos \u002B Vector3.Up * Radius;\n\n\t/// \u003Csummary\u003E \u653E\u5230\u67D0\u4E2A\u5E73\u53F0\u7684\u4E2D\u5FC3\uFF08\u843D\u5730\u5438\u9644\u7528\uFF09 \u003C/summary\u003E\n\tpublic void PlaceOn( JumpPlatform platform ) =\u003E SetBasePosition( new Vector3( platform.CenterX, platform.CenterY, platform.TopZ ) );\n\n\tpublic void Init( JumpPlatform platform )\n\t{\n\t\t_renderer = GameObject.AddComponent\u003CModelRenderer\u003E();\n\t\t_renderer.Model = Model.Load( \u0022models/dev/sphere.vmdl\u0022 );\n\t\t_renderer.Tint = new Color( 0.16f, 0.17f, 0.20f );\n\n\t\tvar size = _renderer.Model.Bounds.Size;\n\t\tvar s = PieceDiameter / size.x;\n\t\t_baseScale = new Vector3( s, s, s );\n\t\tRadius = size.z * s * 0.5f;\n\n\t\tGameObject.LocalScale = _baseScale;\n\t\tPlaceOn( platform );\n\t}\n\n\t/// \u003Csummary\u003E \u6BCF\u5E27\u84C4\u529B\u503C\uFF080~1\uFF09\uFF0CJumpGame \u5728\u84C4\u529B\u65F6\u6301\u7EED\u66F4\u65B0 \u003C/summary\u003E\n\tpublic void SetCharge( float power ) =\u003E _charge = power;\n\n\t/// \u003Csummary\u003E \u5F62\u53D8\u51B2\u91CF\uFF1A\u6B63 = \u62C9\u4F38\uFF08\u8D77\u8DF3\uFF09\uFF0C\u8D1F = \u538B\u6241\uFF08\u843D\u5730\uFF09 \u003C/summary\u003E\n\tpublic void Impulse( float amount ) =\u003E _squashVel \u002B= amount;\n\n\tprotected override void OnUpdate()\n\t{\n\t\t// \u5F62\u53D8\u5F39\u7C27\uFF1A\u5411\u81EA\u7136\u72B6\u6001\u56DE\u843D\n\t\t_squashVel \u002B= ( -Stiffness * _squash - Damping * _squashVel ) * Time.Delta;\n\t\t_squash \u002B= _squashVel * Time.Delta;\n\n\t\tvar total = Math.Clamp( _squash - _charge * 0.42f, -0.55f, 0.6f );\n\t\tvar xz = 1f - total * 0.45f;\n\n\t\tGameObject.LocalScale = new Vector3( _baseScale.x * xz, _baseScale.y * xz, _baseScale.z * ( 1f \u002B total ) );\n\t}\n}"},{"Ident":"slfy.tiaotiao","Path":"Jump/JumpScores.cs","FileName":"JumpScores.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\n/// \u003Csummary\u003E\n/// \u6210\u7EE9\u8BB0\u5F55\uFF1A\u672C\u5730\u6392\u884C\u699C\uFF08FileSystem.Data \u6301\u4E45\u5316\uFF0C\u59CB\u7EC8\u53EF\u7528\uFF09\n/// \u002B \u5B98\u65B9\u4E91\u7AEF\u6392\u884C\u699C\uFF08Sandbox.Services \u7684 Stats/Leaderboards\uFF0C\u5C3D\u529B\u800C\u4E3A\u2014\u2014\n///   \u5305\u672A\u53D1\u5E03/\u79BB\u7EBF\u65F6\u540E\u7AEF\u6CA1\u6709\u8EAB\u4EFD\uFF0C\u62FF\u4E0D\u5230\u6570\u636E\u5C31\u9759\u9ED8\u964D\u7EA7\u4E3A\u7EAF\u672C\u5730\uFF09\u3002\n/// \u003C/summary\u003E\npublic static class JumpScores\n{\n\tconst string SaveFile = \u0022jump_scores.json\u0022;\n\tconst int MaxKeep = 30;\n\n\t/// \u003Csummary\u003E \u4E00\u6761\u672C\u5730\u6210\u7EE9 \u003C/summary\u003E\n\tpublic class ScoreEntry\n\t{\n\t\tpublic int Score { get; set; }\n\t\tpublic int Difficulty { get; set; }   // 0 \u7B80\u5355 1 \u5BB9\u6613 2 \u56F0\u96BE\n\t\tpublic string Date { get; set; } = \u0022\u0022;\n\t}\n\n\tstatic List\u003CScoreEntry\u003E _scores = new();\n\tstatic bool _loaded;\n\n\t/// \u003Csummary\u003E\n\t/// \u6BCF\u4E2A\u96BE\u5EA6\u5BF9\u5E94\u7684\u540E\u7AEF\u6307\u6807\u3002\n\t///\n\t/// \u7B80\u5355\u6A21\u5F0F\u6CBF\u7528\u65E9\u671F\u5C31\u5B58\u5728\u3001\u5DF2\u7ECF\u6709\u5168\u90E8\u73A9\u5BB6\u5386\u53F2\u6210\u7EE9\u7684 \u003Cc\u003Ebest_score\u003C/c\u003E\uFF1B\n\t/// \u666E\u901A\u548C\u56F0\u96BE\u5404\u5199\u81EA\u5DF1\u7684\u65B0\u6307\u6807\uFF08\u90A3\u4E24\u4E2A\u699C\u53EA\u6709\u73A9\u8FC7\u65B0\u7248\u672C\u7684\u73A9\u5BB6\uFF0C\u6B63\u5E38\uFF09\u3002\n\t/// \u8BFB\u53D6\u65F6\u6309\u5F53\u524D\u96BE\u5EA6\u53D6\u5BF9\u5E94\u699C\uFF0C\u6240\u4EE5\u7B80\u5355\u96BE\u5EA6\u770B\u5230\u7684\u662F\u90A3 15 \u4E2A\u8001\u73A9\u5BB6\uFF0C\u666E\u901A/\u56F0\u96BE\u662F\u5404\u81EA\u7684\u699C\u3002\n\t/// \u003C/summary\u003E\n\tpublic static readonly (JumpDifficulty Difficulty, string Stat, string Name)[] Boards =\n\t{\n\t\t( JumpDifficulty.Easy,   \u0022best_score\u0022,        \u0022Easy\u0022 ),\n\t\t( JumpDifficulty.Normal, \u0022best_score_normal\u0022, \u0022Normal\u0022 ),\n\t\t( JumpDifficulty.Hard,   \u0022best_score_hard\u0022,   \u0022Hard\u0022 ),\n\t};\n\n\t/// \u003Csummary\u003E \u5F53\u524D\u96BE\u5EA6\u7684\u6307\u6807\u540D\u4E0E\u663E\u793A\u540D \u003C/summary\u003E\n\tpublic static (string Stat, string Name) BoardFor( JumpDifficulty difficulty )\n\t{\n\t\tforeach ( var b in Boards )\n\t\t{\n\t\t\tif ( b.Difficulty == difficulty ) return (b.Stat, b.Name);\n\t\t}\n\n\t\treturn (Boards[0].Stat, Boards[0].Name);\n\t}\n\n\t/// \u003Csummary\u003E \u63D0\u4EA4\u65F6\u968F\u6210\u7EE9\u9644\u5E26\u7684\u6570\u636E\uFF08\u96BE\u5EA6\u4EE5\u8FD9\u4E2A\u5B57\u7B26\u4E32\u843D\u5E93\uFF09 \u003C/summary\u003E\n\tpublic static Dictionary\u003Cstring, object\u003E StatData( JumpDifficulty difficulty )\n\t\t=\u003E new() { { \u0022difficulty\u0022, difficulty.ToString() } };\n\n\t/// \u003Csummary\u003E\n\t/// \u4E91\u7AEF\u6392\u884C\u699C\uFF0C\u6309\u96BE\u5EA6\u5206\u522B\u7F13\u5B58\u3002\n\t///\n\t/// \u4EE5\u524D\u662F\u5355\u4E2A\u4E0D\u5206\u96BE\u5EA6\u7684 List\uFF1A\u5207\u96BE\u5EA6\u540E\u4E0A\u4E00\u6B21\u7684\u7ED3\u679C\u8FD8\u5728\uFF0C\u6807\u9898\u5374\u5199\u5F53\u524D\u96BE\u5EA6\uFF0C\n\t/// \u4E8E\u662F\u0022Normal \u00B7 Global Best\u0022\u5E95\u4E0B\u663E\u793A\u7684\u662F Hard \u7684\u699C\uFF1B\u62C9\u53D6\u5931\u8D25\u65F6\u66F4\u662F\u7EE7\u7EED\u663E\u793A\u522B\u7684\u96BE\u5EA6\u3002\n\t/// \u7F13\u5B58\u653E\u5728\u8FD9\u91CC\u6309\u96BE\u5EA6\u53D6\uFF0C\u6E32\u67D3\u65B9\u62FF\u5230\u7684\u5FC5\u7136\u662F\u5F53\u524D\u96BE\u5EA6\u7684\u6570\u636E\u3002\n\t/// \u003C/summary\u003E\n\tstatic readonly Dictionary\u003CJumpDifficulty, List\u003C(long Rank, string Name, int Score)\u003E\u003E _cloud = new();\n\n\t/// \u003Csummary\u003E \u6307\u5B9A\u96BE\u5EA6\u7684\u4E91\u7AEF\u699C\uFF08\u65E0\u6570\u636E = \u8FD8\u6CA1\u62C9\u5230\u6216\u4E0D\u9002\u7528\uFF09 \u003C/summary\u003E\n\tpublic static IReadOnlyList\u003C(long Rank, string Name, int Score)\u003E Cloud( JumpDifficulty difficulty )\n\t\t=\u003E _cloud.TryGetValue( difficulty, out var list ) ? list : Array.Empty\u003C(long, string, int)\u003E();\n\n\t/// \u003Csummary\u003E \u8BE5\u96BE\u5EA6\u662F\u5426\u6210\u529F\u62C9\u53D6\u8FC7\u4E91\u7AEF\u699C \u003C/summary\u003E\n\tpublic static bool CloudLoaded( JumpDifficulty difficulty ) =\u003E _cloud.ContainsKey( difficulty );\n\n\tstatic JumpScores()\n\t{\n\t\tLoad();\n\t}\n\n\tstatic void Load()\n\t{\n\t\tif ( _loaded ) return;\n\n\t\ttry\n\t\t{\n\t\t\t// \u7F16\u8F91\u5668\u65E9\u671F\uFF08\u7F16\u8F91\u6A21\u5F0F/\u573A\u666F\u52A0\u8F7D\u4E2D\uFF09FileSystem.Data \u8FD8\u6CA1\u5C31\u7EEA\u4F1A\u629B NRE\uFF0C\n\t\t\t// \u6B64\u65F6\u4E0D\u80FD\u628A _loaded \u7F6E\u4F4D\uFF0C\u7559\u7ED9\u6E38\u620F\u8FD0\u884C\u671F\u95F4\u7684\u4E0B\u6B21\u8C03\u7528\u91CD\u8BD5\n\t\t\tif ( FileSystem.Data is null ) return;\n\n\t\t\tif ( FileSystem.Data.FileExists( SaveFile ) )\n\t\t\t{\n\t\t\t\t_scores = Json.Deserialize\u003CList\u003CScoreEntry\u003E\u003E( FileSystem.Data.ReadAllText( SaveFile ) ) ?? new List\u003CScoreEntry\u003E();\n\t\t\t}\n\t\t\t_loaded = true;\n\t\t}\n\t\tcatch ( Exception e )\n\t\t{\n\t\t\tLog.Warning( $\u0022JumpScores load: {e.Message}\u0022 );\n\t\t}\n\t}\n\n\t/// \u003Csummary\u003E \u6307\u5B9A\u96BE\u5EA6\u7684\u5386\u53F2\u6700\u9AD8\u5206\uFF08_scores \u6574\u4F53\u964D\u5E8F\uFF0C\u6240\u4EE5\u9996\u4E2A\u5339\u914D\u5C31\u662F\u6700\u5927\u503C\uFF09 \u003C/summary\u003E\n\tpublic static int Best( JumpDifficulty difficulty )\n\t{\n\t\tLoad();\n\t\treturn _scores.FirstOrDefault( x =\u003E x.Difficulty == (int)difficulty )?.Score ?? 0;\n\t}\n\n\t/// \u003Csummary\u003E \u6307\u5B9A\u96BE\u5EA6\u7684\u672C\u5730\u524D N \u540D\uFF08\u964D\u5E8F\uFF09 \u003C/summary\u003E\n\tpublic static List\u003CScoreEntry\u003E Top( int count, JumpDifficulty difficulty )\n\t{\n\t\tLoad();\n\t\treturn _scores.Where( x =\u003E x.Difficulty == (int)difficulty ).Take( count ).ToList();\n\t}\n\n\t/// \u003Csummary\u003E\n\t/// \u5C40\u7EC8\u8BB0\u5F55\u6210\u7EE9\uFF08\u4E91\u7AEF\u4E0A\u62A5\u987A\u5E26\u89E6\u53D1\uFF09\u3002\n\t/// \u622A\u65AD\u6309\u96BE\u5EA6\u5206\u522B\u505A\uFF1A\u5168\u5C40 Take(MaxKeep) \u4F1A\u8BA9\u9AD8\u5206\u96BE\u5EA6\u628A\u522B\u7684\u96BE\u5EA6\u5168\u6324\u51FA\u53BB\uFF0C\n\t/// \u4F4E\u5206\u96BE\u5EA6\u7684\u672C\u5730\u699C\u6700\u540E\u4E00\u6761\u90FD\u4E0D\u5269\u3002\n\t/// \u003C/summary\u003E\n\tpublic static void Record( int score, JumpDifficulty difficulty )\n\t{\n\t\tif ( score \u003C= 0 ) return;\n\t\tLoad();\n\n\t\t_scores.Add( new ScoreEntry { Score = score, Difficulty = (int)difficulty, Date = DateTime.Now.ToString( \u0022MM-dd\u0022 ) } );\n\t\t_scores = Trim( _scores );\n\n\t\tSave();\n\n\t\tSubmitCloud( score, difficulty );\n\t}\n\n\t/// \u003Csummary\u003E\n\t/// \u6392\u884C\u4FDD\u7559\u89C4\u5219\uFF1A\u6BCF\u4E2A\u96BE\u5EA6\u5404\u7559 MaxKeep \u6761\uFF0C\u6574\u4F53\u518D\u6309\u5206\u6570\u964D\u5E8F\u3002\n\t///\n\t/// \u4E0D\u80FD\u5BF9\u5168\u8868\u505A OrderByDescending().Take(MaxKeep)\uFF1A\u90A3 30 \u4E2A\u540D\u989D\u662F\u6240\u6709\u96BE\u5EA6\u5171\u7528\u7684\uFF0C\n\t/// \u4E00\u4E2A\u96BE\u5EA6\u5206\u9AD8\u5C31\u628A\u522B\u7684\u96BE\u5EA6\u5168\u6324\u51FA\u53BB\uFF08\u5B9E\u6D4B\u5B58\u4E0B\u6765 30 \u6761\u91CC 28 \u6761 Easy\u30012 \u6761 Hard\u3001\n\t/// Normal \u4E00\u6761\u4E0D\u5269\uFF0CNormal \u7684\u672C\u5730\u699C\u6574\u4E2A\u662F\u7A7A\u7684\uFF09\u3002\n\t/// \u6574\u4F53\u964D\u5E8F\u662F Best/Top \u8BFB\u53D6\u65F6\u0022\u7B2C\u4E00\u4E2A\u5339\u914D\u5373\u6700\u5927\u503C\u0022\u7684\u524D\u63D0\u3002\n\t/// \u003C/summary\u003E\n\tpublic static List\u003CScoreEntry\u003E Trim( List\u003CScoreEntry\u003E scores )\n\t{\n\t\treturn scores\n\t\t\t.GroupBy( x =\u003E x.Difficulty )\n\t\t\t.SelectMany( g =\u003E g.OrderByDescending( x =\u003E x.Score ).Take( MaxKeep ) )\n\t\t\t.OrderByDescending( x =\u003E x.Score )\n\t\t\t.ToList();\n\t}\n\n\tstatic void Save()\n\t{\n\t\ttry\n\t\t{\n\t\t\tFileSystem.Data.WriteAllText( SaveFile, Json.Serialize( _scores ) );\n\t\t}\n\t\tcatch ( Exception e )\n\t\t{\n\t\t\tLog.Warning( $\u0022JumpScores save: {e.Message}\u0022 );\n\t\t}\n\t}\n\n\t// ---- \u4E91\u7AEF\uFF08\u5B98\u65B9 Services\uFF0C\u5C3D\u529B\u800C\u4E3A\uFF09----\n\n\t/// \u003Csummary\u003E\n\t/// \u4E0A\u62A5\u6210\u7EE9\u5230\u5B98\u65B9\u4E91\u7AEF\uFF08\u6253\u5305\u53D1\u5E03\u540E\u751F\u6548\uFF1B\u672C\u5730\u5F00\u53D1\u5305\u53EF\u80FD\u6CA1\u6709\u540E\u7AEF\u8EAB\u4EFD\uFF09\u3002\n\t/// \u6309\u96BE\u5EA6\u5199\u5404\u81EA\u7684\u6307\u6807\uFF1A\u7B80\u5355 \u2192 best_score\uFF08\u8001\u699C\uFF09\uFF0C\u666E\u901A/\u56F0\u96BE \u2192 \u5404\u81EA\u7684\u65B0\u699C\u3002\n\t/// \u003C/summary\u003E\n\tpublic static void SubmitCloud( int score, JumpDifficulty difficulty )\n\t{\n\t\ttry\n\t\t{\n\t\t\tvar ( stat, _ ) = BoardFor( difficulty );\n\n\t\t\tSandbox.Services.Stats.SetValue( stat, score, StatData( difficulty ) );\n\t\t\t_ = FlushAndReloadCloud( difficulty );\n\t\t}\n\t\tcatch ( Exception e )\n\t\t{\n\t\t\tLog.Warning( $\u0022JumpScores cloud submit: {e.Message}\u0022 );\n\t\t}\n\t}\n\n\t/// \u003Csummary\u003E \u62C9\u53D6\u6307\u5B9A\u96BE\u5EA6\u7684\u4E91\u7AEF\u6392\u884C\u699C\uFF08\u805A\u5408 = \u6BCF\u4EBA\u5386\u53F2\u6700\u9AD8\u5206\uFF0C\u964D\u5E8F\uFF09 \u003C/summary\u003E\n\tpublic static async Task RefreshCloud( JumpDifficulty difficulty )\n\t{\n\t\ttry\n\t\t{\n\t\t\tvar ( stat, _ ) = BoardFor( difficulty );\n\n\t\t\tvar board = Sandbox.Services.Leaderboards.GetFromStat( stat );\n\t\t\tboard.SetAggregationMax();\n\t\t\tboard.MaxEntries = 10;\n\n\t\t\tawait board.Refresh();\n\n\t\t\t// \u62C9\u6210\u529F\u624D\u8986\u76D6\u8BE5\u96BE\u5EA6\u7684\u7F13\u5B58\uFF1A\u5931\u8D25\u65F6\u4FDD\u7559\u4E0A\u4E00\u6B21\u7684\u7ED3\u679C\uFF0C\u91CD\u8BD5\u6210\u529F\u540E\u81EA\u7136\u88AB\u66FF\u6362\u3002\n\t\t\t// \u6CE8\u610F\u522B\u6E05\u7A7A\u7F13\u5B58\u2014\u2014\u6E05\u6389\u5C31\u628A\u0022\u4E0A\u6B21\u770B\u5230\u7684\u699C\u0022\u4E5F\u4E00\u8D77\u4E22\u4E86\uFF0C\u754C\u9762\u76F4\u63A5\u53D8\u5360\u4F4D\n\t\t\t_cloud[difficulty] = board.Entries\n\t\t\t\t.Select( e =\u003E ( e.Rank, e.DisplayName ?? \u0022???\u0022, (int)e.Value ) )\n\t\t\t\t.ToList();\n\t\t}\n\t\tcatch ( Exception e )\n\t\t{\n\t\t\t// \u672C\u5730\u5305 / \u79BB\u7EBF\uFF1A\u4E91\u7AEF\u4E0D\u53EF\u7528\uFF0C\u964D\u7EA7\u4E3A\u7EAF\u672C\u5730\u6392\u884C\uFF0C\u4F46\u8981\u8BF4\u4E00\u58F0\u662F\u5728\u54EA\u4E00\u6B65\u6302\u7684\n\t\t\tLog.Warning( $\u0022JumpScores cloud refresh ({difficulty}): {e.Message}\u0022 );\n\t\t}\n\t}\n\n\t/// \u003Csummary\u003E\n\t/// \u4E0A\u62A5\u540E\u628A\u6210\u7EE9\u63A8\u7ED9\u540E\u7AEF\u5E76\u91CD\u65B0\u62C9\u699C\u3002\n\t///\n\t/// \u5FC5\u987B\u7528 FlushAndWaitAsync\uFF1AFlushAsync \u662F\u0022\u53D1\u51FA\u5373\u5FD8\u0022\uFF08\u5F15\u64CE\u6CE8\u91CA\uFF1ADon\u0027t wait for confirmation\n\t/// of ingestion\uFF09\uFF0C\u7D27\u63A5\u7740\u67E5\u699C\u65F6\u8FD9\u4E00\u5C40\u7684\u6210\u7EE9\u901A\u5E38\u8FD8\u6CA1\u5165\u5E93\uFF0C\u7ED3\u7B97\u753B\u9762\u5C31\u6C38\u8FDC\u770B\u4E0D\u5230\u81EA\u5DF1\u521A\u6253\u7684\u5206\u6570\u3002\n\t/// \u003C/summary\u003E\n\tstatic async Task FlushAndReloadCloud( JumpDifficulty difficulty )\n\t{\n\t\ttry\n\t\t{\n\t\t\tawait Sandbox.Services.Stats.FlushAndWaitAsync();\n\t\t\tawait RefreshCloud( difficulty );\n\t\t}\n\t\tcatch ( Exception e )\n\t\t{\n\t\t\tLog.Warning( $\u0022JumpScores cloud flush ({difficulty}): {e.Message}\u0022 );\n\t\t}\n\t}\n}\n"},{"Ident":"slfy.tiaotiao","Path":"Jump/JumpCamera.cs","FileName":"JumpCamera.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"using System;\r\n\r\n/// \u003Csummary\u003E\r\n/// \u8DF3\u4E00\u8DF3\u7684\u8DDF\u968F\u76F8\u673A\uFF1A\u56FA\u5B9A\u89D2\u5EA6\u4FEF\u89C6\uFF0C\u5E73\u6ED1\u8DDF\u968F\u68CB\u5B50\u3002\r\n/// \u89C6\u7EBF\u5927\u4F53\u6CBF \u002BX \u65B9\u5411\uFF0C\u5E73\u53F0\u86C7\u5F62\u524D\u8FDB\u65F6\u5728\u753B\u9762\u4E0A\u5448\u73B0\u5DE6\u524D/\u53F3\u524D\u7684\u659C\u7EBF\u8FD0\u52A8\u3002\r\n/// \u003C/summary\u003E\r\npublic sealed class JumpCamera : Component\r\n{\r\n\t/// \u003Csummary\u003E \u76F8\u673A\u76F8\u5BF9\u6CE8\u89C6\u70B9\u7684\u504F\u79FB \u003C/summary\u003E\r\n\t[Property] public Vector3 Offset { get; set; } = new Vector3( -500f, 0f, 640f );\r\n\r\n\t/// \u003Csummary\u003E \u6CE8\u89C6\u70B9\u76F8\u5BF9\u68CB\u5B50\u7684\u524D\u63A2\u91CF\uFF08\u628A\u68CB\u5B50\u538B\u5230\u753B\u9762\u4E0B\u65B9\uFF09 \u003C/summary\u003E\r\n\t[Property] public Vector3 LookAhead { get; set; } = new Vector3( 300f, 0f, -40f );\r\n\r\n\t/// \u003Csummary\u003E \u8DDF\u968F\u901F\u5EA6\uFF08\u8D8A\u5927\u8D8A\u7D27\uFF09 \u003C/summary\u003E\r\n\t[Property] public float FollowSpeed { get; set; } = 5f;\r\n\r\n\tVector3 _focus;\r\n\tbool _hasFocus;\r\n\r\n\tpublic void SnapTo( Vector3 pos )\r\n\t{\r\n\t\t_focus = pos;\r\n\t\t_hasFocus = true;\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tvar game = JumpGame.Current;\r\n\t\tif ( game is null || !_hasFocus ) return;\r\n\r\n\t\t// \u5931\u8D25\uFF08\u4E0B\u843D/\u7ED3\u675F\uFF09\u540E\u505C\u4F4F\uFF0C\u4E0D\u8DDF\u7740\u68CB\u5B50\u5760\u51FA\u753B\u9762\uFF0C\u8BA9\u73A9\u5BB6\u770B\u6E05\u5931\u8BEF\u4F4D\u7F6E\r\n\t\tif ( game.IsGameOver || game.IsFalling ) return;\r\n\r\n\t\tvar t = 1f - MathF.Exp( -FollowSpeed * Time.Delta );\r\n\t\t_focus = Vector3.Lerp( _focus, game.PiecePosition, t );\r\n\r\n\t\tGameObject.WorldPosition = _focus \u002B Offset;\r\n\t\tGameObject.WorldRotation = Rotation.LookAt( ( _focus \u002B LookAhead - GameObject.WorldPosition ).Normal );\r\n\t}\r\n}"},{"Ident":"slfy.tiaotiao","Path":"Jump/JumpAchievements.cs","FileName":"JumpAchievements.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\n/// \u003Csummary\u003E\n/// \u6210\u5C31\u4E0E\u7D2F\u8BA1\u8FDB\u5EA6\u3002\n///\n/// \u6210\u5C31\u672C\u8EAB\u8981\u5728 sbox \u7F51\u7AD9\u540E\u53F0\u6309 id \u5EFA\u597D\uFF08\u89E3\u9501\u6A21\u5F0F\u9009 Manual\uFF09\uFF0C\u4EE3\u7801\u91CC\u53EA\u8D1F\u8D23\u5728\u6761\u4EF6\u6EE1\u8DB3\u65F6\u8C03\u7528\n/// Achievements.Unlock(id)\u3002\u540E\u53F0\u6CA1\u5EFA\u8FC7\u7684 id \u8C03\u7528\u4F1A\u88AB\u5B89\u9759\u5FFD\u7565\uFF0C\u4E0D\u4F1A\u62A5\u9519\u3002\n///\n/// \u7D2F\u8BA1\u8BA1\u6570\uFF08\u603B\u8DF3\u8DC3\u6B21\u6570\u7B49\uFF09\u5B58\u5728\u672C\u5730\u6587\u4EF6\u91CC\uFF1B\u78C1\u76D8\u5199\u5165\u53EA\u5728\u0022\u4E00\u5C40\u7ED3\u675F\u0022\u548C\u0022\u89E3\u9501\u6210\u5C31\u0022\u65F6\u53D1\u751F\u2014\u2014\n/// \u6BCF\u6B21\u8D77\u8DF3\u90FD\u5199\u76D8\u592A\u6D6A\u8D39\uFF0C\u6240\u4EE5\u8BA1\u6570\u5148\u7559\u5728\u5185\u5B58\uFF0C\u5C40\u7EC8\u518D\u843D\u76D8\u3002\n/// \u003C/summary\u003E\npublic static class JumpAchievements\n{\n\t/// \u003Csummary\u003E \u4E00\u6761\u6210\u5C31\u5B9A\u4E49\uFF08id / \u6807\u9898 / \u63CF\u8FF0\uFF09\uFF0C\u540E\u53F0\u7167\u7740\u8FD9\u4E2A\u8868\u586B \u003C/summary\u003E\n\tpublic readonly record struct Info( string Id, string Title, string Description );\n\n\t/// \u003Csummary\u003E \u5168\u90E8\u6210\u5C31\u3002id \u662F\u540E\u7AEF\u6807\u8BC6\uFF0C\u4E00\u65E6\u4E0A\u4F20\u5C31\u522B\u518D\u6539 \u003C/summary\u003E\n\tpublic static readonly Info[] All =\n\t{\n\t\tnew( \u0022first_jump\u0022,       \u0022First Step\u0022,        \u0022Make your first jump.\u0022 ),\n\t\tnew( \u0022first_landing\u0022,    \u0022Safe Landing\u0022,      \u0022Land on a platform.\u0022 ),\n\t\tnew( \u0022score_10\u0022,         \u0022Getting Warm\u0022,      \u0022Score 10 in a single run.\u0022 ),\n\t\tnew( \u0022score_30\u0022,         \u0022Steady Hand\u0022,       \u0022Score 30 in a single run.\u0022 ),\n\t\tnew( \u0022score_50\u0022,         \u0022Half Century\u0022,      \u0022Score 50 in a single run.\u0022 ),\n\t\tnew( \u0022score_100\u0022,        \u0022Centurion\u0022,         \u0022Score 100 in a single run.\u0022 ),\n\t\tnew( \u0022perfect_3\u0022,        \u0022Triple Perfect\u0022,    \u0022Land 3 perfect jumps in a row.\u0022 ),\n\t\tnew( \u0022perfect_10\u0022,       \u0022Perfect Ten\u0022,       \u0022Land 10 perfect jumps in a row.\u0022 ),\n\t\tnew( \u0022hard_20\u0022,          \u0022Steady On Hard\u0022,    \u0022Score 20 or more on Hard.\u0022 ),\n\t\tnew( \u0022backjump\u0022,         \u0022Homeward\u0022,          \u0022Jump back onto the platform you came from.\u0022 ),\n\t\tnew( \u0022total_100_jumps\u0022,  \u0022Hundred Leaps\u0022,     \u0022Jump 100 times in total.\u0022 ),\n\t\tnew( \u0022total_50_games\u0022,   \u0022Persistence\u0022,       \u0022Play 50 games in total.\u0022 ),\n\t\tnew( \u0022total_500_perfect\u0022,\u0022Sharpshooter\u0022,      \u0022Land 500 perfect jumps in total.\u0022 ),\n\t};\n\n\t// ---- \u7D2F\u8BA1\u8FDB\u5EA6\uFF08\u6301\u4E45\u5316\uFF09 ----\n\n\tpublic class Progress\n\t{\n\t\tpublic int TotalJumps { get; set; }\n\t\tpublic int TotalPerfect { get; set; }\n\t\tpublic int TotalGames { get; set; }\n\t\tpublic int TotalBackJumps { get; set; }\n\t\tpublic int BestStreak { get; set; }\n\t}\n\n\tconst string SaveFile = \u0022jump_progress.json\u0022;\n\n\tstatic Progress _progress = new();\n\tstatic bool _loaded;\n\tstatic bool _dirty;\n\n\tpublic static Progress Data\n\t{\n\t\tget { Load(); return _progress; }\n\t}\n\n\tstatic void Load()\n\t{\n\t\tif ( _loaded ) return;\n\n\t\ttry\n\t\t{\n\t\t\t// \u7F16\u8F91\u5668\u65E9\u671F FileSystem.Data \u8FD8\u6CA1\u5C31\u7EEA\u4F1A\u629B\u5F02\u5E38\uFF0C\u6B64\u65F6\u4E0D\u7F6E\u4F4D _loaded\uFF0C\u7559\u7ED9\u4E0B\u6B21\u91CD\u8BD5\n\t\t\tif ( FileSystem.Data is null ) return;\n\n\t\t\tif ( FileSystem.Data.FileExists( SaveFile ) )\n\t\t\t{\n\t\t\t\t_progress = Json.Deserialize\u003CProgress\u003E( FileSystem.Data.ReadAllText( SaveFile ) ) ?? new Progress();\n\t\t\t}\n\n\t\t\t_loaded = true;\n\t\t}\n\t\tcatch ( Exception e )\n\t\t{\n\t\t\tLog.Warning( $\u0022JumpAchievements load: {e.Message}\u0022 );\n\t\t}\n\t}\n\n\tpublic static void Save()\n\t{\n\t\tif ( !_dirty ) return;\n\t\tLoad();\n\n\t\ttry\n\t\t{\n\t\t\tFileSystem.Data.WriteAllText( SaveFile, Json.Serialize( _progress ) );\n\t\t\t_dirty = false;\n\t\t}\n\t\tcatch ( Exception e )\n\t\t{\n\t\t\tLog.Warning( $\u0022JumpAchievements save: {e.Message}\u0022 );\n\t\t}\n\t}\n\n\t// ---- \u4E8B\u4EF6\u5165\u53E3\uFF08\u90FD\u7531 JumpGame \u8C03\u7528\uFF1B\u83DC\u5355\u6F14\u793A\u4E0E\u65B0\u624B\u6F14\u793A\u4E0D\u8BA1\u5165\uFF09 ----\n\n\t/// \u003Csummary\u003E \u8D77\u8DF3 \u003C/summary\u003E\n\tpublic static void OnJump()\n\t{\n\t\tLoad();\n\n\t\t_progress.TotalJumps\u002B\u002B;\n\t\t_dirty = true;\n\n\t\tUnlock( \u0022first_jump\u0022 );\n\n\t\tif ( _progress.TotalJumps \u003E= 100 ) Unlock( \u0022total_100_jumps\u0022 );\n\t}\n\n\t/// \u003Csummary\u003E \u843D\u5728\u666E\u901A\u5E73\u53F0\u4E0A \u003C/summary\u003E\n\tpublic static void OnLanding() =\u003E Unlock( \u0022first_landing\u0022 );\n\n\t/// \u003Csummary\u003E \u5B8C\u7F8E\u843D\u5730\uFF0Cstreak \u4E3A\u5F53\u524D\u8FDE\u51FB\u6570 \u003C/summary\u003E\n\tpublic static void OnPerfect( int streak )\n\t{\n\t\tLoad();\n\n\t\t_progress.TotalPerfect\u002B\u002B;\n\t\t_progress.BestStreak = Math.Max( _progress.BestStreak, streak );\n\t\t_dirty = true;\n\n\t\tif ( streak \u003E= 3 ) Unlock( \u0022perfect_3\u0022 );\n\t\tif ( streak \u003E= 10 ) Unlock( \u0022perfect_10\u0022 );\n\t\tif ( _progress.TotalPerfect \u003E= 500 ) Unlock( \u0022total_500_perfect\u0022 );\n\t}\n\n\t/// \u003Csummary\u003E \u5012\u8DF3\u843D\u56DE\u8EAB\u540E\u7684\u5E73\u53F0 \u003C/summary\u003E\n\tpublic static void OnBackJump()\n\t{\n\t\tLoad();\n\n\t\t_progress.TotalBackJumps\u002B\u002B;\n\t\t_dirty = true;\n\n\t\tUnlock( \u0022backjump\u0022 );\n\t}\n\n\t/// \u003Csummary\u003E \u4E00\u5C40\u7ED3\u675F\u3002\u8BA1\u6570\u5728\u8FD9\u91CC\u843D\u76D8 \u003C/summary\u003E\n\tpublic static void OnGameEnd( int score, JumpDifficulty difficulty )\n\t{\n\t\tLoad();\n\n\t\t_progress.TotalGames\u002B\u002B;\n\t\t_dirty = true;\n\n\t\tif ( score \u003E= 10 ) Unlock( \u0022score_10\u0022 );\n\t\tif ( score \u003E= 30 ) Unlock( \u0022score_30\u0022 );\n\t\tif ( score \u003E= 50 ) Unlock( \u0022score_50\u0022 );\n\t\tif ( score \u003E= 100 ) Unlock( \u0022score_100\u0022 );\n\t\tif ( difficulty == JumpDifficulty.Hard \u0026\u0026 score \u003E= 20 ) Unlock( \u0022hard_20\u0022 );\n\n\t\tif ( _progress.TotalGames \u003E= 50 ) Unlock( \u0022total_50_games\u0022 );\n\n\t\tSave();\n\t}\n\n\t/// \u003Csummary\u003E \u8BE5\u6210\u5C31\u662F\u5426\u5DF2\u89E3\u9501\uFF08\u672C\u5730\u7F13\u5B58\uFF0C\u672A\u62C9\u5230\u540E\u53F0\u6570\u636E\u65F6\u4E00\u5F8B\u6309\u672A\u89E3\u9501\u5904\u7406\uFF09 \u003C/summary\u003E\n\tpublic static bool IsUnlocked( string id )\n\t{\n\t\ttry\n\t\t{\n\t\t\treturn Sandbox.Services.Achievements.All.FirstOrDefault( x =\u003E x.Name == id )?.IsUnlocked ?? false;\n\t\t}\n\t\tcatch ( Exception )\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/// \u003Csummary\u003E \u89E3\u9501\uFF08\u91CD\u590D\u8C03\u7528\u4F1A\u88AB\u5F15\u64CE\u5FFD\u7565\uFF09\u3002\u6210\u529F\u540E\u7ACB\u5373\u843D\u76D8\u7D2F\u8BA1\u8BA1\u6570 \u003C/summary\u003E\n\tstatic void Unlock( string id )\n\t{\n\t\ttry\n\t\t{\n\t\t\tSandbox.Services.Achievements.Unlock( id );\n\t\t\tSave();\n\t\t}\n\t\tcatch ( Exception e )\n\t\t{\n\t\t\tLog.Warning( $\u0022JumpAchievements unlock {id}: {e.Message}\u0022 );\n\t\t}\n\t}\n}\n"},{"Ident":"slfy.tiaotiao","Path":"Jump/JumpDifficulty.cs","FileName":"JumpDifficulty.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"/// \u003Csummary\u003E\n/// \u96BE\u5EA6\u6863\u4F4D\uFF1A\u7B80\u5355\uFF08\u84C4\u529B\u65F6\u663E\u793A\u8F85\u52A9\u7EBF\uFF09/ \u5BB9\u6613\uFF08\u6807\u51C6\uFF09/ \u56F0\u96BE\uFF08\u843D\u70B9\u4E0D\u5438\u9644\u5E73\u53F0\u4E2D\u5FC3\uFF09\u3002\n/// \u003C/summary\u003E\npublic enum JumpDifficulty\n{\n\tEasy,\n\tNormal,\n\tHard\n}"},{"Ident":"slfy.tiaotiao","Path":"Jump/JumpPlatform.cs","FileName":"JumpPlatform.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"using System;\r\n\r\n/// \u003Csummary\u003E\r\n/// \u8DF3\u4E00\u8DF3\u5E73\u53F0\uFF1A\u7EAF\u89C6\u89C9\uFF08\u6A21\u578B \u002B \u51FA\u751F\u5F39\u51FA\u52A8\u753B\uFF09\uFF0C\u78B0\u649E\u5224\u5B9A\u7531 JumpGame \u7528\u6570\u5B66\u77E9\u5F62\u5B8C\u6210\u3002\r\n/// \u003C/summary\u003E\r\npublic sealed class JumpPlatform : Component\r\n{\r\n\tpublic float CenterX { get; private set; }\r\n\tpublic float CenterY { get; private set; }\r\n\tpublic float TopZ { get; private set; }\r\n\tpublic float HalfX { get; private set; }\r\n\tpublic float HalfY { get; private set; }\r\n\r\n\t/// \u003Csummary\u003E \u662F\u5426\u4E3A\u5F53\u524D\u76EE\u6807\u5E73\u53F0\uFF08\u4E2D\u5FC3\u6807\u8BB0\u4F1A\u547C\u5438\u8109\u51B2\u63D0\u793A\u843D\u70B9\uFF09 \u003C/summary\u003E\r\n\tpublic bool IsTarget { get; set; }\r\n\r\n\tpublic Vector3 Center =\u003E new Vector3( CenterX, CenterY, TopZ );\r\n\r\n\tGameObject _marker;\r\n\tVector3 _baseScale;\r\n\tVector3 _baseMarkerScale;\r\n\tTimeSince _born;\r\n\tbool _spawned;\r\n\r\n\tstatic readonly Model BoxModel = Model.Load( \u0022models/dev/box.vmdl\u0022 );\r\n\r\n\tpublic void Init( Vector2 center, float halfX, float halfY, float topZ, Color color )\r\n\t{\r\n\t\tCenterX = center.x;\r\n\t\tCenterY = center.y;\r\n\t\tTopZ = topZ;\r\n\t\tHalfX = halfX;\r\n\t\tHalfY = halfY;\r\n\r\n\t\tvar height = topZ;\r\n\t\tvar size = BoxModel.Bounds.Size;\r\n\r\n\t\tGameObject.WorldPosition = new Vector3( CenterX, CenterY, height * 0.5f );\r\n\t\t_baseScale = new Vector3( halfX * 2f / size.x, halfY * 2f / size.y, height / size.z );\r\n\t\tGameObject.LocalScale = _baseScale;\r\n\r\n\t\tvar renderer = GameObject.AddComponent\u003CModelRenderer\u003E();\r\n\t\trenderer.Model = BoxModel;\r\n\t\trenderer.Tint = color;\r\n\r\n\t\t// \u9876\u9762\u4E2D\u5FC3\u7684\u0022\u5B8C\u7F8E\u843D\u70B9\u0022\u6807\u8BB0\uFF08\u8584\u677F\uFF09\uFF0C\u8DDF\u968F\u5E73\u53F0\u7F29\u653E\u5F39\u51FA\r\n\t\t_marker = new GameObject( true, \u0022CenterMark\u0022 );\r\n\t\t_marker.Parent = GameObject;\r\n\t\t_marker.LocalPosition = new Vector3( 0f, 0f, ( height * 0.5f \u002B 1.3f ) / _baseScale.z );\r\n\t\t_baseMarkerScale = new Vector3( 30f / ( halfX * 2f ), 30f / ( halfY * 2f ), 2.6f / height );\r\n\t\t_marker.LocalScale = _baseMarkerScale;\r\n\r\n\t\tvar markerRenderer = _marker.AddComponent\u003CModelRenderer\u003E();\r\n\t\tmarkerRenderer.Model = BoxModel;\r\n\t\tmarkerRenderer.Tint = color * 0.55f;\r\n\r\n\t\t_born = 0;\r\n\t\t_spawned = true;\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tif ( !_spawned ) return;\r\n\r\n\t\t// \u51FA\u751F\u52A8\u753B\uFF1A\u4ECE\u538B\u6241\u72B6\u6001\u5E26\u4E00\u70B9\u56DE\u5F39\u5730\u957F\u51FA\u6765\r\n\t\tvar t = Math.Clamp( (float)_born / 0.3f, 0f, 1f );\r\n\t\tif ( t \u003E= 1f )\r\n\t\t{\r\n\t\t\t_spawned = false;\r\n\t\t\tGameObject.LocalScale = _baseScale;\r\n\t\t\tApplyMarkerScale( 1f );\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst float c1 = 1.70158f;\r\n\t\tconst float c3 = c1 \u002B 1f;\r\n\t\tvar f = 1f \u002B c3 * MathF.Pow( t - 1f, 3f ) \u002B c1 * MathF.Pow( t - 1f, 2f );\r\n\r\n\t\tGameObject.LocalScale = _baseScale * f;\r\n\t\tApplyMarkerScale( f );\r\n\t}\r\n\r\n\tvoid ApplyMarkerScale( float factor )\r\n\t{\r\n\t\tif ( !_marker.IsValid() ) return;\r\n\r\n\t\t// \u76EE\u6807\u5E73\u53F0\u7684\u6807\u8BB0\u8F7B\u5FAE\u547C\u5438\uFF0C\u5E2E\u52A9\u8BC6\u522B\u843D\u70B9\r\n\t\tvar pulse = IsTarget \u0026\u0026 !_spawned ? 1f \u002B 0.14f * MathF.Sin( Time.Now * 5f ) : 1f;\r\n\t\t_marker.LocalScale = _baseMarkerScale * factor * pulse;\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E \u9500\u6BC1\u5E73\u53F0\u672C\u4F53\u548C\u4E2D\u5FC3\u6807\u8BB0 \u003C/summary\u003E\r\n\tpublic void DestroyAll()\r\n\t{\r\n\t\tif ( _marker.IsValid() ) _marker.Destroy();\r\n\t\tGameObject.Destroy();\r\n\t}\r\n}"},{"Ident":"slfy.tiaotiao","Path":".obj/__compiler_extra.cs","FileName":"__compiler_extra.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"global using static Sandbox.Internal.GlobalGameNamespace;\r\nglobal using Microsoft.AspNetCore.Components;\r\nglobal using Microsoft.AspNetCore.Components.Rendering;\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonTitle\u0022, \u0022tiaotiao\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonIdent\u0022, \u0022tiaotiao\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022OrgIdent\u0022, \u0022slfy\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022Ident\u0022, \u0022slfy.tiaotiao\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineVersion\u0022, \u002229\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineMinorVersion\u0022, \u00221\u0022 )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \u0022.NETCoreApp,Version=v9.0\u0022, FrameworkDisplayName = \u0022.NET 9.0\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022CompileTime\u0022, \u00222026-09-12T12:41:12.2306560Z\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\u00220.0.249.0\u0022)]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\u00220.0.249.0\u0022)]"},{"Ident":"slfy.tiaotiao","Path":"Assembly.cs","FileName":"Assembly.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"global using System;\r\nglobal using Sandbox;\r\nglobal using System.Collections.Generic;\r\nglobal using System.Linq;\r\n"},{"Ident":"slfy.tiaotiao","Path":"jump/jumphud.cs.scss","FileName":"jumphud.cs.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"JumpHud\r\n{\r\n\tposition: absolute;\r\n\ttop: 0;\r\n\tleft: 0;\r\n\tright: 0;\r\n\tbottom: 0;\r\n\tfont-family: Inter;\r\n\tcolor: #3c4249;\r\n\tpointer-events: none;\r\n\r\n\t.score\r\n\t{\r\n\t\tposition: absolute;\r\n\t\ttop: 26px;\r\n\t\tleft: 0;\r\n\t\tright: 0;\r\n\t\tfont-size: 96px;\r\n\t\tfont-weight: 800;\r\n\t\tcolor: #343a41;\r\n\t\ttext-align: center;\r\n\t}\r\n\r\n\t.streak\r\n\t{\r\n\t\tposition: absolute;\r\n\t\ttop: 150px;\r\n\t\tleft: 0;\r\n\t\tright: 0;\r\n\t\tfont-size: 34px;\r\n\t\tfont-weight: 700;\r\n\t\tcolor: #e8590c;\r\n\t\ttext-align: center;\r\n\t}\r\n\r\n\t.hint\r\n\t{\r\n\t\tposition: absolute;\r\n\t\tbottom: 76px;\r\n\t\tleft: 0;\r\n\t\tright: 0;\r\n\t\tfont-size: 22px;\r\n\t\tcolor: #7a828c;\r\n\t\ttext-align: center;\r\n\t}\r\n\r\n\t.tutorial\r\n\t{\r\n\t\tposition: absolute;\r\n\t\tbottom: 130px;\r\n\t\tleft: 0;\r\n\t\tright: 0;\r\n\t\tjustify-content: center;\r\n\r\n\t\t.tutorial-text\r\n\t\t{\r\n\t\t\tbackground-color: rgba(255, 255, 255, 0.92);\r\n\t\t\tborder-radius: 16px;\r\n\t\t\tpadding: 14px 30px;\r\n\t\t\tfont-size: 26px;\r\n\t\t\tfont-weight: 600;\r\n\t\t\tcolor: #343a41;\r\n\t\t}\r\n\t}\r\n\r\n\t.diff\r\n\t{\r\n\t\tposition: absolute;\r\n\t\ttop: 16px;\r\n\t\tright: 26px;\r\n\t\tfont-size: 24px;\r\n\t\tcolor: #565d66;\r\n\t}\r\n\r\n\t.gameover\r\n\t{\r\n\t\tposition: absolute;\r\n\t\ttop: 0;\r\n\t\tleft: 0;\r\n\t\tright: 0;\r\n\t\tbottom: 0;\r\n\t\tbackground-color: rgba(255, 255, 255, 0.74);\r\n\t\talign-items: center;\r\n\t\tjustify-content: center;\r\n\t\tflex-direction: column;\r\n\t\tcursor: default;\r\n\r\n\t\t.title\r\n\t\t{\r\n\t\t\tfont-size: 54px;\r\n\t\t\tfont-weight: 800;\r\n\t\t\tletter-spacing: 10px;\r\n\t\t\tcolor: #565d66;\r\n\t\t}\r\n\r\n\t\t.finalscore\r\n\t\t{\r\n\t\t\tfont-size: 190px;\r\n\t\t\tfont-weight: 800;\r\n\t\t\tline-height: 1;\r\n\t\t\tmargin-top: 14px;\r\n\t\t\tcolor: #e8590c;\r\n\t\t}\r\n\r\n\t\t.board-wrap\r\n\t\t{\r\n\t\t\tflex-direction: row;\r\n\t\t\tjustify-content: center;\r\n\t\t\tmargin-top: 22px;\r\n\r\n\t\t\t.board-col\r\n\t\t\t{\r\n\t\t\t\tflex-direction: column;\r\n\t\t\t\tmin-width: 300px;\r\n\t\t\t\tmargin: 0 12px;\r\n\t\t\t\tpadding: 16px 22px;\r\n\t\t\t\tbackground-color: rgba(255, 255, 255, 0.82);\r\n\t\t\t\tborder-radius: 14px;\r\n\r\n\t\t\t\t.board-title\r\n\t\t\t\t{\r\n\t\t\t\t\tfont-size: 21px;\r\n\t\t\t\t\tfont-weight: 700;\r\n\t\t\t\t\tcolor: #e8590c;\r\n\t\t\t\t\tmargin-bottom: 14px;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.board-empty\r\n\t\t\t\t{\r\n\t\t\t\t\tfont-size: 19px;\r\n\t\t\t\t\tcolor: #7a828c;\r\n\t\t\t\t\tpadding: 8px 0;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t.board-row\r\n\t\t\t\t{\r\n\t\t\t\t\tflex-direction: row;\r\n\t\t\t\t\talign-items: center;\r\n\t\t\t\t\tmargin-bottom: 10px;\r\n\r\n\t\t\t\t\t.cell-rank\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twidth: 28px;\r\n\t\t\t\t\t\tcolor: #7a828c;\r\n\t\t\t\t\t\tfont-style: italic;\r\n\t\t\t\t\t\tfont-weight: 700;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.avatar\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twidth: 36px;\r\n\t\t\t\t\t\theight: 36px;\r\n\t\t\t\t\t\tborder-radius: 9px;\r\n\t\t\t\t\t\talign-items: center;\r\n\t\t\t\t\t\tjustify-content: center;\r\n\t\t\t\t\t\tmargin-right: 12px;\r\n\r\n\t\t\t\t\t\t.avatar-char\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcolor: #ffffff;\r\n\t\t\t\t\t\t\tfont-size: 17px;\r\n\t\t\t\t\t\t\tfont-weight: 700;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.cell-name\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twidth: 150px;\r\n\t\t\t\t\t\tcolor: #343a41;\r\n\t\t\t\t\t\tfont-size: 19px;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t.cell-score\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twidth: 70px;\r\n\t\t\t\t\t\ttext-align: right;\r\n\t\t\t\t\t\tcolor: #e8590c;\r\n\t\t\t\t\t\tfont-size: 24px;\r\n\t\t\t\t\t\tfont-weight: 800;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t.sub\r\n\t\t{\r\n\t\t\tfont-size: 26px;\r\n\t\t\tmargin-top: 30px;\r\n\t\t\tcolor: #7a828c;\r\n\t\t}\r\n\t}\r\n\r\n\t.menu\r\n\t{\r\n\t\tposition: absolute;\r\n\t\ttop: 0;\r\n\t\tleft: 0;\r\n\t\tright: 0;\r\n\t\tbottom: 0;\r\n\t\tbackground-color: rgba(30, 36, 44, 0.35);\r\n\t\tflex-direction: column;\r\n\t\talign-items: center;\r\n\t\tjustify-content: center;\r\n\t\tpointer-events: all;\r\n\t\tcursor: default;\r\n\r\n\t\t.menu-title\r\n\t\t{\r\n\t\t\tfont-size: 120px;\r\n\t\t\tfont-weight: 800;\r\n\t\t\tletter-spacing: 12px;\r\n\t\t\tcolor: #2f343a;\r\n\t\t}\r\n\r\n\t\t.menu-sub\r\n\t\t{\r\n\t\t\tfont-size: 28px;\r\n\t\t\tcolor: #5c636b;\r\n\t\t\tmargin-top: 4px;\r\n\t\t\tmargin-bottom: 44px;\r\n\t\t}\r\n\r\n\t\t.menu-btn-row\r\n\t\t{\r\n\t\t\tflex-direction: row;\r\n\r\n\t\t\t.menu-btn\r\n\t\t\t{\r\n\t\t\t\tpadding: 18px 46px;\r\n\t\t\t\tmargin: 0 14px;\r\n\t\t\t\tborder-radius: 14px;\r\n\t\t\t\tbackground-color: rgba(255, 255, 255, 0.94);\r\n\t\t\t\tcolor: #343a41;\r\n\t\t\t\tfont-size: 30px;\r\n\t\t\t\tfont-weight: 700;\r\n\t\t\t\tcursor: pointer;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t.menu-best\r\n\t\t{\r\n\t\t\tfont-size: 26px;\r\n\t\t\tcolor: #c8ced6;\r\n\t\t\tmargin-top: 34px;\r\n\t\t}\r\n\t}\r\n}"},{"Ident":"slfy.tiaotiao","Path":"Jump/JumpHud.cs","FileName":"JumpHud.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"using System;\nusing Sandbox.UI;\n// board\n\n/// \u003Csummary\u003E\n/// \u8DF3\u4E00\u8DF3 HUD\uFF1A\u5F00\u59CB\u83DC\u5355\uFF08AI \u80CC\u666F\u6F14\u793A\uFF09\u3001\u8BA1\u5206\u3001\u8FDE\u51FB\u63D0\u793A\u3001\u64CD\u4F5C\u63D0\u793A\u3001\u65B0\u624B\u6F14\u793A\u5B57\u5E55\u3001\u7ED3\u675F\u753B\u9762\u3001\u96BE\u5EA6\u89D2\u6807\u3002\n/// \u7528\u7EAF C# Panel \u6784\u5EFA\uFF08\u7F16\u8F91\u5668\u70ED\u91CD\u8F7D\u4E0D\u4F1A\u4E3A\u65B0\u52A0\u7684 .razor \u91CD\u65B0\u7F16\u8BD1\uFF0CC# \u9762\u677F\u53EF\u4EE5\u5373\u6539\u5373\u751F\u6548\uFF09\uFF0C\n/// \u6837\u5F0F\u5728\u540C\u540D\u6837\u5F0F\u8868 JumpHud.cs.scss \u91CC\uFF08PanelComponent \u4F1A\u6309\u0022\u7C7B\u540D.cs.scss\u0022\u81EA\u52A8\u52A0\u8F7D\uFF09\u3002\n/// \u003C/summary\u003E\npublic sealed class JumpHud : PanelComponent\n{\n\tLabel _score;\n\tLabel _streak;\n\tLabel _hint;\n\tPanel _tutorialWrap;\n\tLabel _tutorialText;\n\tPanel _gameOver;\n\tLabel _gameOverScore;\n\tPanel _menuWrap;\n\tLabel _menuBest;\n\tLabel _difficultyLabel;\n\tPanel _boardWrap;\n\tPanel[] _localRows;\n\tLabel[] _localRank, _localAvatarChar, _localNameLabels, _localScore;\n\tPanel[] _localAvatar;\n\tPanel[] _cloudRows;\n\tLabel[] _cloudRank, _cloudAvatarChar, _cloudName, _cloudScore;\n\tPanel[] _cloudAvatar;\n\tLabel _localEmpty;\n\tLabel _cloudEmpty;\n\tconst int BoardRows = 5;\n\tstatic string _localName;\n\n\t/// \u003Csummary\u003E \u672C\u5730\u73A9\u5BB6\u540D\u5B57\uFF08\u62FF\u4E0D\u5230\u5C31\u663E\u793A\u0022\u4F60\u0022\uFF09 \u003C/summary\u003E\n\tstatic string LocalName()\n\t{\n\t\tif ( _localName is not null ) return _localName;\n\t\ttry { _localName = string.IsNullOrWhiteSpace( Connection.Local?.DisplayName ) ? \u0022You\u0022 : Connection.Local.DisplayName; }\n\t\tcatch { _localName = \u0022You\u0022; }\n\t\treturn _localName;\n\t}\n\n\tstatic readonly Color[] AvatarColors =\n\t{\n\t\tnew Color( 0.86f, 0.47f, 0.28f ),\n\t\tnew Color( 0.36f, 0.62f, 0.86f ),\n\t\tnew Color( 0.44f, 0.72f, 0.50f ),\n\t\tnew Color( 0.72f, 0.50f, 0.86f ),\n\t\tnew Color( 0.90f, 0.72f, 0.30f ),\n\t};\n\tLabel _localTitle;\n\tLabel _cloudTitle;\n\tstring _menuBoardRequested;\n\tbool _built;\n\n\tprotected override void OnStart()\n\t{\n\t\tbase.OnStart();\n\t\tBuildUi();\n\t}\n\n\tvoid BuildUi()\n\t{\n\t\tvar root = Panel;\n\t\tif ( root is null ) return;\n\n\t\t// \u5DF2\u5EFA\u597D\u4E14\u6240\u6709\u5F15\u7528\u6709\u6548 \u2192 \u4E0D\u91CD\u590D\u5EFA\u3002\n\t\t// \u70ED\u91CD\u8F7D\u53EF\u80FD\u4FDD\u7559\u65E7\u5B9E\u4F8B\uFF08\u8001\u5B57\u6BB5\u6709\u6548\u4F46\u65B0\u589E\u5B57\u6BB5\u4E3A null\uFF09\uFF0C\u6216\u6784\u5EFA\u4E2D\u9014\u88AB\u6253\u65AD\n\t\t// \uFF08\u6570\u7EC4\u534A\u6EE1\uFF09\uFF0C\u4EFB\u4F55\u4E00\u9879\u5931\u6548\u90FD\u6574\u4F53\u6E05\u7A7A\u91CD\u5EFA\n\t\tvar healthy = _built \u0026\u0026 _score.IsValid() \u0026\u0026 _menuBest.IsValid() \u0026\u0026 _menuWrap.IsValid()\n\t\t\t\u0026\u0026 _tutorialWrap.IsValid() \u0026\u0026 _localTitle.IsValid() \u0026\u0026 _cloudTitle.IsValid()\n\t\t\t\u0026\u0026 _localEmpty.IsValid() \u0026\u0026 _cloudEmpty.IsValid()\n\t\t\t\u0026\u0026 ( _localAvatarChar?.All( x =\u003E x is not null ) ?? false )\n\t\t\t\u0026\u0026 ( _localNameLabels?.All( x =\u003E x is not null ) ?? false )\n\t\t\t\u0026\u0026 ( _cloudName?.All( x =\u003E x is not null ) ?? false )\n\t\t\t\u0026\u0026 root.ChildrenCount \u003E 0;\n\t\tif ( healthy ) return;\n\n\t\troot.DeleteChildren( true );\n\n\t\troot.Style.Position = PositionMode.Absolute;\n\t\troot.Style.Left = 0f;\n\t\troot.Style.Top = 0f;\n\t\troot.Style.Right = 0f;\n\t\troot.Style.Bottom = 0f;\n\t\troot.Style.PointerEvents = PointerEvents.None;\n\n\t\t_score = new Label() { Classes = \u0022score\u0022 };\n\n\t\t_streak = new Label() { Classes = \u0022streak\u0022 };\n\t\t_streak.Style.Display = DisplayMode.None;\n\n\t\t_hint = new Label() { Classes = \u0022hint\u0022 };\n\t\t_hint.Text = \u0022Hold Left Mouse to charge  \u00B7  release to jump  \u00B7  hold S to jump back  \u00B7  Q \u2014 Menu\u0022;\n\t\t_hint.Style.Display = DisplayMode.None;\n\n\t\t_tutorialWrap = new Panel() { Classes = \u0022tutorial\u0022 };\n\t\t_tutorialText = new Label() { Classes = \u0022tutorial-text\u0022 };\n\t\t_tutorialWrap.AddChild( _tutorialText );\n\t\t_tutorialWrap.Style.Display = DisplayMode.None;\n\n\t\t_gameOver = new Panel() { Classes = \u0022gameover\u0022 };\n\t\t_gameOver.Style.Display = DisplayMode.None;\n\n\t\tvar title = new Label() { Classes = \u0022title\u0022 };\n\t\ttitle.Text = \u0022GAME OVER\u0022;\n\t\t_gameOver.AddChild( title );\n\n\t\t_gameOverScore = new Label() { Classes = \u0022finalscore\u0022 };\n\t\t_gameOver.AddChild( _gameOverScore );\n\n\t\t_boardWrap = new Panel() { Classes = \u0022board-wrap\u0022 };\n\n\t\tvar localCol = new Panel() { Classes = \u0022board-col\u0022 };\n\t\t_localTitle = new Label() { Classes = \u0022board-title\u0022 };\n\t\tlocalCol.AddChild( _localTitle );\n\t\t_localRows = new Panel[BoardRows];\n\t\t_localRank = new Label[BoardRows];\n\t\t_localAvatar = new Panel[BoardRows];\n\t\t_localAvatarChar = new Label[BoardRows];\n\t\t_localNameLabels = new Label[BoardRows];\n\t\t_localScore = new Label[BoardRows];\n\t\tfor ( int i = 0; i \u003C BoardRows; i\u002B\u002B )\n\t\t{\n\t\t\t( _localRows[i], _localRank[i], _localAvatar[i], _localAvatarChar[i], _localNameLabels[i], _localScore[i] ) = MakeBoardRow( localCol );\n\t\t}\n\t\t_localEmpty = new Label() { Classes = \u0022board-empty\u0022 };\n\t\t_localEmpty.Style.Display = DisplayMode.None;\n\t\tlocalCol.AddChild( _localEmpty );\n\t\t_boardWrap.AddChild( localCol );\n\n\t\tvar cloudCol = new Panel() { Classes = \u0022board-col\u0022 };\n\t\t_cloudTitle = new Label() { Classes = \u0022board-title\u0022 };\n\t\tcloudCol.AddChild( _cloudTitle );\n\t\t_cloudRows = new Panel[BoardRows];\n\t\t_cloudRank = new Label[BoardRows];\n\t\t_cloudAvatar = new Panel[BoardRows];\n\t\t_cloudAvatarChar = new Label[BoardRows];\n\t\t_cloudName = new Label[BoardRows];\n\t\t_cloudScore = new Label[BoardRows];\n\t\tfor ( int i = 0; i \u003C BoardRows; i\u002B\u002B )\n\t\t{\n\t\t\t( _cloudRows[i], _cloudRank[i], _cloudAvatar[i], _cloudAvatarChar[i], _cloudName[i], _cloudScore[i] ) = MakeBoardRow( cloudCol );\n\t\t}\n\t\t_cloudEmpty = new Label() { Classes = \u0022board-empty\u0022 };\n\t\t_cloudEmpty.Style.Display = DisplayMode.None;\n\t\tcloudCol.AddChild( _cloudEmpty );\n\t\t_boardWrap.AddChild( cloudCol );\n\n\t\t_gameOver.AddChild( _boardWrap );\n\n\t\tvar goHint = new Label() { Classes = \u0022sub\u0022 };\n\t\tgoHint.Text = \u0022Click to restart  \u00B7  Q \u2014 Menu\u0022;\n\t\t_gameOver.AddChild( goHint );\n\n\t\t_menuWrap = new Panel() { Classes = \u0022menu\u0022 };\n\n\t\tvar menuTitle = new Label() { Classes = \u0022menu-title\u0022 };\n\t\tmenuTitle.Text = \u0022Jump Jump\u0022;\n\t\t_menuWrap.AddChild( menuTitle );\n\n\t\tvar menuSub = new Label() { Classes = \u0022menu-sub\u0022 };\n\t\tmenuSub.Text = \u0022Select Difficulty  \u00B7  press 1 / 2 / 3\u0022;\n\t\t_menuWrap.AddChild( menuSub );\n\n\t\tvar menuBtnRow = new Panel() { Classes = \u0022menu-btn-row\u0022 };\n\t\tAddMenuButton( menuBtnRow, \u0022Easy\u0022, JumpDifficulty.Easy );\n\t\tAddMenuButton( menuBtnRow, \u0022Normal\u0022, JumpDifficulty.Normal );\n\t\tAddMenuButton( menuBtnRow, \u0022Hard\u0022, JumpDifficulty.Hard );\n\t\t_menuWrap.AddChild( menuBtnRow );\n\n\t\t_menuBest = new Label() { Classes = \u0022menu-best\u0022 };\n\t\t_menuWrap.AddChild( _menuBest );\n\n\t\t_difficultyLabel = new Label() { Classes = \u0022diff\u0022 };\n\n\t\troot.AddChild( _score );\n\t\troot.AddChild( _streak );\n\t\troot.AddChild( _hint );\n\t\troot.AddChild( _tutorialWrap );\n\t\troot.AddChild( _gameOver );\n\t\troot.AddChild( _difficultyLabel );\n\t\troot.AddChild( _menuWrap );\n\n\t\t_built = true;\n\t}\n\n\tvoid AddMenuButton( Panel parent, string text, JumpDifficulty difficulty )\n\t{\n\t\tvar btn = new Label() { Classes = \u0022menu-btn\u0022 };\n\t\tbtn.Text = text;\n\t\tbtn.AddEventListener( \u0022onclick\u0022, () =\u003E\n\t\t{\n\t\t\tSound.Play( \u0022sounds/kenney/ui/ui.button.press.sound\u0022 );\n\t\t\tJumpGame.Current?.StartGame( difficulty );\n\t\t} );\n\t\tparent.AddChild( btn );\n\t}\n\n\tstatic (Panel row, Label rank, Panel avatar, Label avatarChar, Label name, Label score) MakeBoardRow( Panel parent )\n\t{\n\t\tvar row = new Panel() { Classes = \u0022board-row\u0022 };\n\n\t\tvar rank = new Label() { Classes = \u0022cell-rank\u0022 };\n\t\trow.AddChild( rank );\n\n\t\tvar avatar = new Panel() { Classes = \u0022avatar\u0022 };\n\t\tvar avatarChar = new Label() { Classes = \u0022avatar-char\u0022 };\n\t\tavatar.AddChild( avatarChar );\n\t\trow.AddChild( avatar );\n\n\t\tvar name = new Label() { Classes = \u0022cell-name\u0022 };\n\t\trow.AddChild( name );\n\n\t\tvar score = new Label() { Classes = \u0022cell-score\u0022 };\n\t\trow.AddChild( score );\n\n\t\tparent.AddChild( row );\n\t\treturn ( row, rank, avatar, avatarChar, name, score );\n\t}\n\n\tstatic void SetVisible( Panel panel, bool visible )\n\t{\n\t\tpanel.Style.Display = visible ? DisplayMode.Flex : DisplayMode.None;\n\t}\n\n\tstatic string DifficultyName( JumpDifficulty difficulty ) =\u003E difficulty switch\n\t{\n\t\tJumpDifficulty.Easy =\u003E \u0022Easy\u0022,\n\t\tJumpDifficulty.Normal =\u003E \u0022Normal\u0022,\n\t\t_ =\u003E \u0022Hard\u0022\n\t};\n\n\tprotected override void OnUpdate()\n\t{\n\t\tBuildUi();\n\n\t\tvar game = JumpGame.Current;\n\t\tif ( game is null || !_built || _score is null || _tutorialText is null || _localAvatarChar is null ) return;\n\n\t\t_score.Text = game.Score.ToString();\n\t\t_streak.Text = $\u0022PERFECT \u00D7{game.Streak}\u0022;\n\t\t_gameOverScore.Text = game.Score.ToString();\n\t\t_tutorialText.Text = game.TutorialText ?? \u0022\u0022;\n\n\t\tvar inMenu = game.InMenu;\n\n\t\t_difficultyLabel.Text = DifficultyName( game.Difficulty ) \u002B \u0022  \u00B7  Q \u2014 Menu\u0022;\n\n\t\t// \u56DE\u5230\u83DC\u5355\u65F6\u62C9\u4E00\u6B21\u4E91\u7AEF\u699C\uFF08\u5F02\u6B65\u3001\u5C3D\u529B\u800C\u4E3A\uFF09\uFF1B\u952E\u4E0A\u5E26\u96BE\u5EA6\uFF0C\u6362\u96BE\u5EA6\u5C31\u91CD\u62C9\n\t\tvar boardKey = $\u0022{inMenu}:{game.Difficulty}\u0022;\n\t\tif ( inMenu \u0026\u0026 _menuBoardRequested != boardKey )\n\t\t{\n\t\t\t_menuBoardRequested = boardKey;\n\t\t\t_ = JumpScores.RefreshCloud( game.Difficulty );\n\t\t}\n\t\telse if ( !inMenu )\n\t\t{\n\t\t\t_menuBoardRequested = null;\n\t\t}\n\n\t\t// \u83DC\u5355\u91CC\u7684\u0022\u6700\u4F73\u0022\u8DDF\u5F53\u524D\u96BE\u5EA6\uFF08\u96BE\u5EA6\u6309\u94AE/\u6570\u5B57\u952E\u7ACB\u5373\u6539\u5199 game.Difficulty\uFF09\u3002\n\t\t// \u539F\u6765\u7528\u7684\u662F\u4E0D\u5206\u96BE\u5EA6\u7684 _scores[0]\uFF0C\u663E\u793A\u7684\u662F\u522B\u7684\u96BE\u5EA6\u7684\u6700\u9AD8\u5206\n\t\tvar best = JumpScores.Best( game.Difficulty );\n\t\t_menuBest.Text = best \u003E 0 ? $\u0022{DifficultyName( game.Difficulty )} Best {best}\u0022 : \u0022Set your first record!\u0022;\n\n\t\t// \u5F00\u59CB\u83DC\u5355\u76D6\u4F4F\u6E38\u620F HUD\uFF0C\u53EA\u4FDD\u7559\u80CC\u666F\u6F14\u793A\n\t\tSetVisible( _menuWrap, inMenu );\n\t\tSetVisible( _score, !game.IsGameOver \u0026\u0026 !inMenu );\n\t\tSetVisible( _streak, !game.IsGameOver \u0026\u0026 game.Streak \u003E= 2 \u0026\u0026 !inMenu );\n\t\tSetVisible( _hint, !game.HasJumped \u0026\u0026 !game.IsGameOver \u0026\u0026 !game.IsTutorial \u0026\u0026 !inMenu );\n\t\tSetVisible( _tutorialWrap, !inMenu \u0026\u0026 !string.IsNullOrEmpty( game.TutorialText ) );\n\t\tSetVisible( _gameOver, game.IsGameOver \u0026\u0026 !inMenu );\n\t\tSetVisible( _difficultyLabel, !inMenu );\n\n\t\t// \u7ED3\u7B97\u753B\u9762\u663E\u793A\u6392\u884C\u699C\uFF1A\u5DE6\u672C\u5730 / \u53F3\u5E73\u53F0\uFF0C\u4E24\u8FB9\u90FD\u6309\u5F53\u524D\u96BE\u5EA6\u53D6\n\t\tvar diffName = DifficultyName( game.Difficulty );\n\t\t_localTitle.Text = $\u0022{diffName} \u00B7 Local Best\u0022;\n\t\t_cloudTitle.Text = $\u0022{diffName} \u00B7 Global Best\u0022;\n\n\t\t// \u53EA\u663E\u793A\u5F53\u524D\u96BE\u5EA6\u7684\u672C\u5730\u6392\u884C\uFF08\u5934\u50CF\u5757 = \u672C\u5730\u73A9\u5BB6\uFF09\n\t\ttry\n\t\t{\n\t\t\tvar top = JumpScores.Top( BoardRows, game.Difficulty );\n\t\t\tfor ( int i = 0; i \u003C BoardRows; i\u002B\u002B )\n\t\t\t{\n\t\t\t\tvar row = _localRows[i];\n\t\t\t\tif ( i \u003C top.Count )\n\t\t\t\t{\n\t\t\t\t\tvar e = top[i];\n\t\t\t\t\tvar name = LocalName();\n\t\t\t\t\trow.Style.Display = DisplayMode.Flex;\n\t\t\t\t\t_localRank[i].Text = $\u0022{i \u002B 1}.\u0022;\n\t\t\t\t\t_localAvatarChar[i].Text = name.Substring( 0, 1 );\n\t\t\t\t\t_localAvatar[i].Style.BackgroundColor = AvatarColors[Math.Abs( name.GetHashCode() ) % AvatarColors.Length];\n\t\t\t\t\t_localNameLabels[i].Text = name;\n\t\t\t\t\t_localScore[i].Text = e.Score.ToString();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\trow.Style.Display = DisplayMode.None;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_localEmpty.Text = \u0022No records yet\u0022;\n\t\t\t_localEmpty.Style.Display = top.Count == 0 ? DisplayMode.Flex : DisplayMode.None;\n\t\t}\n\t\tcatch ( Exception )\n\t\t{\n\t\t\t_built = false;   // \u7ED3\u6784\u5F02\u5E38 \u2192 \u4E0B\u5E27\u6574\u4F53\u91CD\u5EFA\n\t\t}\n\n\t\t// \u5E73\u53F0\u6392\u884C\uFF1A\u672C\u5730\u5305\u6CA1\u6709\u4E91\u7AEF\u8EAB\u4EFD\u3001\u6216\u8BE5\u96BE\u5EA6\u8FD8\u6CA1\u62C9\u5230\u65F6\u663E\u793A\u5360\u4F4D\u3002\n\t\t// \u53D6\u7684\u662F\u6309\u96BE\u5EA6\u7F13\u5B58\u7684\u5217\u8868\uFF0C\u4E0D\u4F1A\u4E32\u5230\u522B\u7684\u96BE\u5EA6\n\t\tvar cloud = JumpScores.Cloud( game.Difficulty );\n\t\tif ( cloud.Count == 0 )\n\t\t{\n\t\t\tfor ( int i = 0; i \u003C BoardRows; i\u002B\u002B ) _cloudRows[i].Style.Display = DisplayMode.None;\n\t\t\t_cloudEmpty.Text = \u0022No records yet\u0022;\n\t\t\t_cloudEmpty.Style.Display = DisplayMode.Flex;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t_cloudEmpty.Style.Display = DisplayMode.None;\n\t\t\tfor ( int i = 0; i \u003C BoardRows; i\u002B\u002B )\n\t\t\t{\n\t\t\t\tvar row = _cloudRows[i];\n\t\t\t\tif ( i \u003C cloud.Count )\n\t\t\t\t{\n\t\t\t\t\tvar (rank, name, score) = cloud[i];\n\t\t\t\t\trow.Style.Display = DisplayMode.Flex;\n\t\t\t\t\t_cloudRank[i].Text = $\u0022{rank}.\u0022;\n\t\t\t\t\t_cloudAvatarChar[i].Text = string.IsNullOrEmpty( name ) ? \u0022?\u0022 : name.Substring( 0, 1 );\n\t\t\t\t\t_cloudAvatar[i].Style.BackgroundColor = AvatarColors[Math.Abs( name.GetHashCode() ) % AvatarColors.Length];\n\t\t\t\t\t_cloudName[i].Text = name;\n\t\t\t\t\t_cloudScore[i].Text = score.ToString();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\trow.Style.Display = DisplayMode.None;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}"},{"Ident":"slfy.tiaotiao","Path":"Jump/JumpGame.cs","FileName":"JumpGame.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":380088,"IsPrivate":false,"Code":"using System;\r\n\r\n/// \u003Csummary\u003E\r\n/// \u8DF3\u4E00\u8DF3\u4E3B\u903B\u8F91\uFF1A\u5E73\u53F0\u751F\u6210\u3001\u84C4\u529B\u8DF3\u8DC3\u3001\u843D\u70B9\u5224\u5B9A\u4E0E\u8BA1\u5206\u3002\r\n/// GameObjectSystem \u4F1A\u5728\u573A\u666F\u52A0\u8F7D\u65F6\u81EA\u52A8\u5B9E\u4F8B\u5316\uFF08\u65E0\u9700\u6302\u8F7D\u5230\u573A\u666F\uFF09\uFF0C\r\n/// \u4F46\u7F16\u8F91\u5668\u7684\u7F16\u8F91\u6A21\u5F0F\u4E0B\u4E5F\u4F1A tick\uFF0C\u6240\u4EE5\u6240\u6709\u903B\u8F91\u90FD\u7528 Game.IsPlaying \u62E6\u622A\u3002\r\n/// \u6574\u4E2A\u4E16\u754C\u5728\u8FD0\u884C\u65F6\u751F\u6210\uFF0C\u4E0D\u4F9D\u8D56\u573A\u666F\u91CC\u5DF2\u6709\u7684\u5185\u5BB9\uFF08\u6A21\u677F\u5BF9\u8C61\u4F1A\u88AB\u5C4F\u853D\uFF09\u3002\r\n/// \u003C/summary\u003E\r\npublic sealed class JumpGame : GameObjectSystem\u003CJumpGame\u003E\r\n{\r\n\t// ---- \u624B\u611F\u53C2\u6570\uFF08\u53EF\u5728 \u9879\u76EE\u8BBE\u7F6E \u2192 Systems \u91CC\u8986\u76D6\uFF09----\r\n\r\n\t/// \u003Csummary\u003E \u84C4\u529B\u4ECE 0 \u5230 1 \u9700\u8981\u7684\u79D2\u6570 \u003C/summary\u003E\r\n\t[Property] public float ChargeTime { get; set; } = 2.0f;\r\n\r\n\t/// \u003Csummary\u003E \u8F7B\u70B9\u65F6\u7684\u8DF3\u8DC3\u8DDD\u79BB \u003C/summary\u003E\r\n\t[Property] public float MinJumpDistance { get; set; } = 130f;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// \u84C4\u6EE1\u65F6\u7684\u8DF3\u8DC3\u8DDD\u79BB\u3002\u5FC5\u987B\u8D34\u7740 CenterDistance.y\uFF08\u5E73\u53F0\u4E2D\u5FC3\u8DDD\u4E0A\u9650\uFF09\u53D6\u503C\uFF1A\r\n\t/// \u5E73\u53F0\u6700\u8FDC\u53EA\u751F\u6210\u5230 470\uFF0C\u82E5\u84C4\u6EE1\u80FD\u98DE 780\uFF0C\u5219\u84C4\u529B\u6761\u6709\u8FD1\u4E00\u534A\u7684\u91CF\u7A0B\u6C38\u8FDC\u4E0D\u8BE5\u88AB\u7528\u5230\uFF0C\r\n\t/// \u800C\u591A\u84C4\u4E00\u70B9\u70B9\u5C31\u76F4\u63A5\u98DE\u8FC7\u5934\u3002\u53D6 470 \u002B \u5E73\u53F0\u534A\u5BBD \u002B \u5224\u5B9A\u4F59\u91CF \u2248 560\uFF0C\r\n\t/// \u84C4\u6EE1\u6B63\u597D\u662F\u0022\u6700\u8FDC\u7684\u5E73\u53F0\u64E6\u8FB9\u591F\u5230\u0022\uFF0C\u6574\u6761\u84C4\u529B\u6761\u90FD\u6709\u7528\u3002\r\n\t/// \u003C/summary\u003E\r\n\t[Property] public float MaxJumpDistance { get; set; } = 560f;\r\n\r\n\t/// \u003Csummary\u003E \u843D\u70B9\u8DDD\u5E73\u53F0\u4E2D\u5FC3\u5C0F\u4E8E\u8BE5\u503C\u7B97\u0022\u5B8C\u7F8E\u0022\uFF0C\u89E6\u53D1\u8FDE\u51FB\u52A0\u5206 \u003C/summary\u003E\r\n\t[Property] public float PerfectRadius { get; set; } = 20f;\r\n\r\n\t/// \u003Csummary\u003E \u5E73\u53F0\u9876\u9762\u534A\u5BBD\u7684\u968F\u673A\u8303\u56F4 \u003C/summary\u003E\r\n\t[Property] public Vector2 PlatformHalfSize { get; set; } = new Vector2( 48, 90 );\r\n\r\n\t/// \u003Csummary\u003E \u76F8\u90BB\u5E73\u53F0\u4E2D\u5FC3\u8DDD\u79BB\u7684\u968F\u673A\u8303\u56F4 \u003C/summary\u003E\r\n\t[Property] public Vector2 CenterDistance { get; set; } = new Vector2( 250, 470 );\r\n\r\n\t/// \u003Csummary\u003E \u6BCF\u4E00\u6B65\u65B9\u5411\u7684\u968F\u673A\u504F\u8F6C\u89D2\uFF08\u5EA6\uFF0C\u5DE6\u53F3\u4EA4\u66FF\u65BD\u52A0\uFF09 \u003C/summary\u003E\r\n\t[Property] public Vector2 TurnAngle { get; set; } = new Vector2( 14, 34 );\r\n\r\n\t// ---- \u5E38\u91CF ----\r\n\r\n\tconst float KillDepth = 420f;   // \u4F4E\u4E8E\u9644\u8FD1\u5E73\u53F0\u9876\u9762\u591A\u5C11\u7B97\u5760\u843D\u51FA\u5C40\r\n\tconst float Gravity = 2400f;    // \u81EA\u7531\u843D\u4F53\u9636\u6BB5\u7684\u91CD\u529B\uFF08\u4EC5\u7528\u4E8E\u5760\u843D\u8868\u73B0\uFF09\r\n\r\n\tstatic readonly Color[] PlatformColors =\r\n\t{\r\n\t\tnew Color( 0.98f, 0.74f, 0.62f ),\r\n\t\tnew Color( 0.64f, 0.86f, 0.74f ),\r\n\t\tnew Color( 0.99f, 0.88f, 0.58f ),\r\n\t\tnew Color( 0.66f, 0.80f, 0.95f ),\r\n\t\tnew Color( 0.93f, 0.77f, 0.90f ),\r\n\t\tnew Color( 0.96f, 0.96f, 0.94f ),\r\n\t};\r\n\r\n\t// ---- \u5BF9\u5916\u72B6\u6001\uFF08HUD / \u76F8\u673A\u8BFB\u53D6\uFF09----\r\n\r\n\tpublic int Score { get; private set; }\r\n\tpublic int Streak { get; private set; }\r\n\tpublic bool HasJumped { get; private set; }\r\n\tpublic bool IsGameOver { get; private set; }\r\n\tpublic bool IsFalling =\u003E _falling;\r\n\tpublic bool IsTutorial =\u003E _tutorialActive;\r\n\r\n\t/// \u003Csummary\u003E \u5F53\u524D\u96BE\u5EA6\uFF08\u5F00\u59CB\u83DC\u5355\u9009\u62E9\uFF09 \u003C/summary\u003E\r\n\tpublic JumpDifficulty Difficulty { get; private set; } = JumpDifficulty.Normal;\r\n\r\n\t/// \u003Csummary\u003E \u662F\u5426\u5728\u5F00\u59CB\u83DC\u5355\uFF08\u80CC\u666F\u7531 AI \u81EA\u52A8\u6F14\u793A\u8DF3\u8DC3\uFF09 \u003C/summary\u003E\r\n\tpublic bool InMenu =\u003E _menuMode;\r\n\r\n\t/// \u003Csummary\u003E \u65B0\u624B\u6F14\u793A\u5B57\u5E55\uFF08\u975E\u7A7A\u65F6 HUD \u663E\u793A\uFF09 \u003C/summary\u003E\r\n\tpublic string TutorialText { get; private set; }\r\n\r\n\tpublic Vector3 PiecePosition { get; private set; }\r\n\r\n\t// ---- \u5185\u90E8\u72B6\u6001 ----\r\n\r\n\tGameObject _root;\r\n\tJumpPiece _piece;\r\n\tJumpCamera _camera;\r\n\treadonly List\u003CJumpPlatform\u003E _platforms = new();\r\n\treadonly List\u003C(GameObject Go, TimeSince Born, Vector3 Scale)\u003E _dots = new();\r\n\r\n\tstatic GameObject _sharedRoot;   // \u70ED\u91CD\u8F7D/\u91CD\u5EFA\u65F6\u590D\u7528\u540C\u4E00\u4E2A\u4E16\u754C\u6839\uFF0C\u907F\u514D\u5806\u53E0\u591A\u5957\u573A\u666F\r\n\r\n\tJumpPlatform _current;\r\n\tJumpPlatform _target;\r\n\tJumpPlatform _previous;   // \u8EAB\u540E\u90A3\u4E00\u5757\uFF08\u5012\u8DF3\u76EE\u6807\uFF09\r\n\r\n\tbool _worldReady;\r\n\tbool _wasDown;\r\n\tbool _charging;\r\n\tfloat _power;\r\n\tfloat _dirAngle;\r\n\tfloat _turnSide = 1f;\r\n\tbool _menuMode = true;   // \u5F00\u5C40\u8FDB\u5165\u5F00\u59CB\u83DC\u5355\uFF0C\u80CC\u666F AI \u81EA\u52A8\u8DF3\r\n\r\n\t// \u7B80\u5355\u6A21\u5F0F\u8F85\u52A9\u7EBF\uFF08\u84C4\u529B\u65F6\u663E\u793A\u9884\u6D4B\u5F39\u9053\u4E0E\u843D\u70B9\u73AF\uFF09\r\n\tconst int AssistDotCount = 14;\r\n\tstatic readonly Color AssistHitColor = new Color( 0.30f, 0.78f, 0.42f );\r\n\tstatic readonly Color AssistMissColor = new Color( 0.92f, 0.32f, 0.28f );\r\n\treadonly List\u003CGameObject\u003E _assistDots = new();\r\n\tGameObject _assistRing;\r\n\tModelRenderer _assistRingRenderer;\r\n\tbool _assistVisible;\r\n\tstatic readonly Model BoxModel = Model.Load( \u0022models/dev/box.vmdl\u0022 );\r\n\r\n\t// \u65B0\u624B\u6F14\u793A\r\n\tbool _tutorialActive;\r\n\tbool _tutorialDone;      // \u6BCF\u4E2A play \u4F1A\u8BDD\u53EA\u6F14\u793A\u4E00\u6B21\uFF0C\u91CD\u5F00\u4E0D\u518D\u64AD\r\n\tbool _waitingForInput;   // \u5F00\u5C40\u7B49\u5F85\u671F\uFF1A\u73A9\u5BB6 3 \u79D2\u5185\u6309\u4E0B\u5219\u8DF3\u8FC7\u6F14\u793A\uFF0C\u95F2\u7F6E\u5219\u64AD\u653E\r\n\tTimeSince _sinceSpawn;\r\n\tfloat _demoPower;\r\n\tTimeSince _tutorialT;\r\n\tTimeSince _sinceTutorialEnd;\r\n\tTimeSince _dotTimer;\r\n\tstatic readonly Model SphereModel = Model.Load( \u0022models/dev/sphere.vmdl\u0022 );\r\n\r\n\t// \u98DE\u884C\u72B6\u6001\r\n\tbool _flying;\r\n\tbool _falling;\r\n\tVector3 _jumpFrom;\r\n\tVector3 _jumpTo;\r\n\tfloat _jumpTime;\r\n\tfloat _jumpDuration;\r\n\tfloat _jumpHeight;\r\n\tfloat _prevBaseZ;\r\n\tfloat _killZ;\r\n\tVector3 _fallVelocity;\r\n\r\n\tpublic JumpGame( Scene scene ) : base( scene )\r\n\t{\r\n\t\tListen( Stage.UpdateBones, 0, Tick, \u0022JumpGame.Tick\u0022 );\r\n\t}\r\n\r\n\tvoid Tick()\r\n\t{\r\n\t\tif ( !Game.IsPlaying ) return;\r\n\r\n\t\tEnsureWorld();\r\n\r\n\t\tvar down = Input.Down( \u0022Attack1\u0022 ) || Input.Down( \u0022Jump\u0022 );\r\n\r\n\t\t// Q\uFF1A\u4ECE\u6E38\u620F / \u7ED3\u7B97\u753B\u9762\u8FD4\u56DE\u5F00\u59CB\u83DC\u5355\r\n\t\tif ( !InMenu \u0026\u0026 Input.Pressed( \u0022Menu\u0022 ) )\r\n\t\t{\r\n\t\t\tEnterMenu();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif ( IsGameOver )\r\n\t\t{\r\n\t\t\tif ( down \u0026\u0026 !_wasDown ) ResetGame();\r\n\t\t\t_wasDown = down;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif ( _piece.IsValid() ) PiecePosition = _piece.BasePosition;\r\n\r\n\t\tTickTrailDots();\r\n\t\tUpdateAssistLine();\r\n\r\n\t\tif ( _flying )\r\n\t\t{\r\n\t\t\tTickFlight( Time.Delta );\r\n\t\t\t_wasDown = down;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// \u5F00\u59CB\u83DC\u5355\u80CC\u666F / \u65B0\u624B\u6F14\u793A\uFF1AAI \u81EA\u52A8\u8868\u6F14\u0022\u84C4\u529B\u2192\u8D77\u8DF3\u2192\u843D\u70B9\u0022\r\n\t\tif ( _menuMode || _tutorialActive )\r\n\t\t{\r\n\t\t\t// \u83DC\u5355\u91CC\u652F\u6301\u952E\u76D8\u9009\u96BE\u5EA6\uFF08\u6570\u5B57\u952E 1/2/3\uFF09\r\n\t\t\tif ( _menuMode )\r\n\t\t\t{\r\n\t\t\t\tif ( Input.Pressed( \u0022Slot1\u0022 ) ) { StartGame( JumpDifficulty.Easy ); return; }\r\n\t\t\t\tif ( Input.Pressed( \u0022Slot2\u0022 ) ) { StartGame( JumpDifficulty.Normal ); return; }\r\n\t\t\t\tif ( Input.Pressed( \u0022Slot3\u0022 ) ) { StartGame( JumpDifficulty.Hard ); return; }\r\n\t\t\t}\r\n\r\n\t\t\tTickAutoJump( Time.Delta );\r\n\t\t\t_wasDown = false;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif ( TutorialText != null \u0026\u0026 _sinceTutorialEnd \u003E 2.4f ) TutorialText = null;\r\n\r\n\t\t// \u5F00\u5C40 3 \u79D2\u89C2\u5BDF\u7A97\uFF1A\u73A9\u5BB6\u81EA\u5DF1\u6309\u4E0B \u2192 \u8DF3\u8FC7\u6F14\u793A\uFF1B\u4E00\u76F4\u95F2\u7F6E \u2192 \u64AD\u653E\u6F14\u793A\r\n\t\tif ( _waitingForInput )\r\n\t\t{\r\n\t\t\tif ( down \u0026\u0026 !_wasDown )\r\n\t\t\t{\r\n\t\t\t\t_waitingForInput = false;\r\n\t\t\t\t_tutorialDone = true;\r\n\t\t\t}\r\n\t\t\telse if ( _sinceSpawn \u003E 3f )\r\n\t\t\t{\r\n\t\t\t\t_waitingForInput = false;\r\n\t\t\t\tStartTutorial();\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// \u5730\u9762\u72B6\u6001\uFF1A\u6309\u4E0B\u84C4\u529B\uFF0C\u677E\u5F00\u8D77\u8DF3\r\n\t\tif ( down \u0026\u0026 !_wasDown )\r\n\t\t{\r\n\t\t\t_charging = true;\r\n\t\t\t_power = 0f;\r\n\t\t}\r\n\r\n\t\tif ( _charging )\r\n\t\t{\r\n\t\t\tif ( down )\r\n\t\t\t{\r\n\t\t\t\t_power = Math.Min( _power \u002B Time.Delta / ChargeTime, 1f );\r\n\t\t\t\t_piece?.SetCharge( _power );\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_charging = false;\r\n\t\t\t\tLaunch();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t_wasDown = down;\r\n\t}\r\n\r\n\t// ---- \u4E16\u754C\u642D\u5EFA ----\r\n\r\n\tvoid EnsureWorld()\r\n\t{\r\n\t\tif ( _worldReady ) return;\r\n\t\t_worldReady = true;\r\n\r\n\t\t// \u590D\u7528\u65E7\u7684\u4E16\u754C\u6839\uFF08\u70ED\u91CD\u8F7D\u7B49\u573A\u666F\u4E0B\u907F\u514D\u5806\u53E0\u591A\u5957\u5E73\u53F0/\u76F8\u673A\uFF09\uFF0C\u5E76\u6E05\u6389\u5176\u52A8\u6001\u5B50\u7269\u4F53\u3002\r\n\t\t// \u6CE8\u610F\uFF1APlay \u505C\u6B62\u540E static \u5F15\u7528\u53EF\u80FD\u6307\u5411\u5DF2\u5378\u8F7D\u7684\u65E7\u573A\u666F\u5BF9\u8C61\uFF08IsValid \u4ECD\u4E3A\u771F\uFF09\uFF0C\r\n\t\t// \u5FC5\u987B\u6821\u9A8C\u573A\u666F\u5F52\u5C5E\uFF0C\u5426\u5219\u5411\u65E7\u573A\u666F\u6839\u6302\u5B50\u7269\u4F53\u76F4\u63A5\u65AD\u8A00\u5931\u8D25\r\n\t\tif ( _sharedRoot.IsValid() \u0026\u0026 _sharedRoot.Scene == Scene )\r\n\t\t{\r\n\t\t\t_root = _sharedRoot;\r\n\t\t\tforeach ( var child in _root.Children.ToArray() )\r\n\t\t\t{\r\n\t\t\t\tif ( child.IsValid() ) child.Destroy();\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t_sharedRoot = null;\r\n\t\t\t_root = new GameObject( true, \u0022JumpGame\u0022 );\r\n\t\t\t_sharedRoot = _root;\r\n\r\n\t\t\t// \u5C4F\u853D\u573A\u666F\u91CC\u6A21\u677F\u7559\u4E0B\u7684\u6240\u6709\u5BF9\u8C61\uFF08\u793A\u4F8B\u76F8\u673A\u3001\u65B9\u5757\u7B49\uFF09\uFF0C\u53EA\u8DD1\u6211\u4EEC\u751F\u6210\u7684\u4E16\u754C\u3002\r\n\t\t\t// Scene \u672C\u8EAB\u5C31\u662F\u6839 GameObject\uFF0C\u9876\u5C42\u5BF9\u8C61\u90FD\u5728\u5B83\u7684 Children \u91CC\r\n\t\t\tforeach ( var go in Scene.Children )\r\n\t\t\t{\r\n\t\t\t\tif ( go.IsValid() \u0026\u0026 go != _root ) go.Enabled = false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tCreateLight();\r\n\t\tCreateCamera();\r\n\t\tCreateHud();\r\n\t\tBuildInitialWorld();\r\n\t}\r\n\r\n\tvoid CreateLight()\r\n\t{\r\n\t\tvar sun = new GameObject( true, \u0022Sun\u0022 );\r\n\t\tsun.Parent = _root;\r\n\t\tsun.WorldRotation = Rotation.LookAt( new Vector3( -0.5f, -0.6f, -1.1f ).Normal );\r\n\r\n\t\tvar light = sun.AddComponent\u003CDirectionalLight\u003E();\r\n\t\tlight.LightColor = new Color( 1f, 0.96f, 0.90f );\r\n\t\tlight.Shadows = true;\r\n\t\tlight.ShadowCascadeCount = 2;\r\n\r\n\t\tvar ambientGo = new GameObject( true, \u0022Ambient\u0022 );\r\n\t\tambientGo.Parent = _root;\r\n\t\tambientGo.AddComponent\u003CAmbientLight\u003E().Color = new Color( 0.48f, 0.52f, 0.60f );\r\n\t}\r\n\r\n\tvoid CreateCamera()\r\n\t{\r\n\t\tvar go = new GameObject( true, \u0022JumpCamera\u0022 );\r\n\t\tgo.Parent = _root;\r\n\r\n\t\tvar cam = go.AddComponent\u003CCameraComponent\u003E();\r\n\t\tcam.IsMainCamera = true;\r\n\t\tcam.Priority = -100;\r\n\t\tcam.FieldOfView = 55f;\r\n\t\tcam.BackgroundColor = new Color( 0.80f, 0.81f, 0.83f );\r\n\r\n\t\t_camera = go.AddComponent\u003CJumpCamera\u003E();\r\n\t}\r\n\r\n\tvoid CreateHud()\r\n\t{\r\n\t\tvar go = new GameObject( true, \u0022Hud\u0022 );\r\n\t\tgo.Parent = _root;\r\n\t\tgo.AddComponent\u003CScreenPanel\u003E();\r\n\t\tgo.AddComponent\u003CJumpHud\u003E();\r\n\t}\r\n\r\n\tvoid BuildInitialWorld()\r\n\t{\r\n\t\tScore = 0;\r\n\t\tStreak = 0;\r\n\t\tHasJumped = false;\r\n\t\tIsGameOver = false;\r\n\t\t_charging = false;\r\n\t\t_power = 0f;\r\n\t\t_flying = false;\r\n\t\t_falling = false;\r\n\t\t_dirAngle = 0f;\r\n\r\n\t\tforeach ( var p in _platforms ) p.DestroyAll();\r\n\t\t_platforms.Clear();\r\n\t\t_target = null;\r\n\t\t_previous = null;\r\n\r\n\t\tif ( _piece.IsValid() ) _piece.GameObject.Destroy();\r\n\r\n\t\tvar first = CreatePlatform( Vector2.Zero, 80f, 80f, 100f, PlatformColors[0] );\r\n\t\t_current = first;\r\n\r\n\t\tvar pieceGo = new GameObject( true, \u0022Piece\u0022 );\r\n\t\tpieceGo.Parent = _root;\r\n\t\t_piece = pieceGo.AddComponent\u003CJumpPiece\u003E();\r\n\t\t_piece.Init( first );\r\n\r\n\t\t// \u5F00\u5C40\uFF1A\u5F53\u524D\u5E73\u53F0\u5C31\u662F\u7B2C\u4E00\u5757\uFF0C\u8EAB\u540E\u6CA1\u6709\u5E73\u53F0\uFF0C\u6240\u4EE5\u6CA1\u6709\u5012\u8DF3\u76EE\u6807\r\n\t\t_previous = null;\r\n\t\tSpawnNextPlatform();\r\n\r\n\t\t_camera?.SnapTo( PiecePosition = _piece.BasePosition );\r\n\r\n\t\t// \u83DC\u5355\u80CC\u666F\u6F14\u793A / \u5F00\u5C40\u5F15\u5BFC\uFF08\u672C\u4F1A\u8BDD\u7B2C\u4E00\u6B21\u8FDB\u5165\u65F6\u5148\u7B49 3 \u79D2\uFF1A\r\n\t\t// \u73A9\u5BB6 3 \u79D2\u5185\u81EA\u5DF1\u6309\u4E0B \u2192 \u8DF3\u8FC7\u6F14\u793A\u76F4\u63A5\u73A9\uFF1B\u4E00\u76F4\u95F2\u7F6E \u2192 \u81EA\u52A8\u64AD\u653E\u6F14\u793A\uFF09\r\n\t\tif ( _menuMode )\r\n\t\t{\r\n\t\t\t_waitingForInput = false;\r\n\t\t\tTutorialText = null;\r\n\t\t\t_tutorialT = 0;\r\n\t\t\t_demoPower = ComputeDemoPower();\r\n\t\t}\r\n\t\telse if ( _tutorialDone )\r\n\t\t{\r\n\t\t\tTutorialText = null;\r\n\t\t\t_waitingForInput = false;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t_waitingForInput = true;\r\n\t\t\t_sinceSpawn = 0;\r\n\t\t\tTutorialText = null;\r\n\t\t}\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E \u8BA1\u7B97\u8BA9\u68CB\u5B50\u6B63\u597D\u843D\u5728\u76EE\u6807\u5E73\u53F0\u4E2D\u5FC3\u7684\u84C4\u529B\u503C \u003C/summary\u003E\r\n\tfloat ComputeDemoPower()\r\n\t{\r\n\t\tif ( !_target.IsValid() || !_piece.IsValid() ) return 0.35f;\r\n\r\n\t\tvar dx = _target.CenterX - _piece.BasePosition.x;\r\n\t\tvar dy = _target.CenterY - _piece.BasePosition.y;\r\n\t\tvar dist = MathF.Sqrt( dx * dx \u002B dy * dy );\r\n\r\n\t\treturn Math.Clamp( ( dist - MinJumpDistance ) / ( MaxJumpDistance - MinJumpDistance ), 0.05f, 1f );\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E \u8FDB\u5165\u5F00\u59CB\u83DC\u5355\uFF08\u80CC\u666F AI \u6F14\u793A\uFF09 \u003C/summary\u003E\r\n\tpublic void EnterMenu()\r\n\t{\r\n\t\t_menuMode = true;\r\n\t\t_tutorialActive = false;\r\n\t\tResetGame();\r\n\r\n\t\t// \u83DC\u5355\u906E\u7F69\u4F1A\u5728\u4E0B\u4E00\u5E27 HUD \u5237\u65B0\u65F6\u624D\u663E\u793A\uFF0C\u0022\u8FD4\u56DE\u83DC\u5355\u0022\u8FD9\u4E00\u4E0B\u70B9\u51FB\u4E0D\u8BE5\u987A\u624B\u628A\u84C4\u529B\u4E5F\u5F00\u59CB\u4E86\r\n\t\tif ( Input.Down( \u0022Attack1\u0022 ) ) _wasDown = true;\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E \u4ECE\u5F00\u59CB\u83DC\u5355\u9009\u62E9\u96BE\u5EA6\u8FDB\u5165\u6B63\u5F0F\u6E38\u620F \u003C/summary\u003E\r\n\tpublic void StartGame( JumpDifficulty difficulty )\r\n\t{\r\n\t\tDifficulty = difficulty;\r\n\t\t_menuMode = false;\r\n\t\t_tutorialActive = false;\r\n\t\tResetGame();\r\n\r\n\t\t// \u6309\u94AE\u662F\u5728\u9F20\u6807\u6309\u4E0B/\u677E\u5F00\u90A3\u4E00\u523B\u88AB\u70B9\u4E2D\u7684\uFF0C\u90A3\u4E00\u6B21\u70B9\u51FB\u7528\u6765\u9009\u96BE\u5EA6\uFF0C\r\n\t\t// \u4E0D\u7F6E\u4F4D\u7684\u8BDD\u677E\u624B\u4F1A\u88AB\u5F53\u6210\u0022\u84C4\u529B\u7ED3\u675F\u0022\u7ACB\u523B\u8D77\u8DF3\u4E00\u6B21\r\n\t\tif ( Input.Down( \u0022Attack1\u0022 ) ) _wasDown = true;\r\n\r\n\t\tSound.Play( \u0022sounds/kenney/ui/ui.navigate.forward.sound\u0022, PiecePosition, 0f );\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E \u5F00\u5C40\u6F14\u793A\uFF1AAI \u81EA\u52A8\u8868\u6F14\u4E00\u6B21\u84C4\u529B\u8DF3\u8DC3\uFF0C\u843D\u70B9\u5BF9\u51C6\u76EE\u6807\u4E2D\u5FC3 \u003C/summary\u003E\r\n\tvoid StartTutorial()\r\n\t{\r\n\t\t_tutorialDone = true;\r\n\t\t_tutorialActive = true;\r\n\t\t_tutorialT = 0;\r\n\t\tTutorialText = \u0022Watch! Hold to charge, release to jump\u0022;\r\n\t\t_demoPower = ComputeDemoPower();\r\n\t}\r\n\r\n\tvoid TickAutoJump( float dt )\r\n\t{\r\n\t\tvar t = (float)_tutorialT;\r\n\r\n\t\tif ( t \u003C 0.9f )\r\n\t\t{\r\n\t\t\t// \u5148\u505C\u987F\u4E00\u62CD\uFF0C\u8BA9\u73A9\u5BB6\u770B\u6E05\u5F00\u5C40\r\n\t\t}\r\n\t\telse if ( t \u003C 1.7f )\r\n\t\t{\r\n\t\t\t// \u6A21\u62DF\u84C4\u529B\uFF1A\u529B\u5EA6\u722C\u5347\u5230\u0022\u521A\u597D\u80FD\u843D\u5728\u4E2D\u5FC3\u0022\u7684\u503C\r\n\t\t\t_power = Math.Min( ( t - 0.9f ) / 0.8f, 1f ) * _demoPower;\r\n\t\t\t_piece?.SetCharge( _power );\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tLaunch();\r\n\t\t}\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E \u6F14\u793A\u8DF3\u8DC3\u7684\u8F68\u8FF9\u70B9\uFF1A\u68CB\u5B50\u98DE\u8FC7\u7684\u5730\u65B9\u7559\u4E0B\u5C0F\u5706\u70B9\u5E76\u6E10\u6E10\u6D88\u6563 \u003C/summary\u003E\r\n\tvoid SpawnTrailDot( Vector3 basePos )\r\n\t{\r\n\t\tvar go = new GameObject( true, \u0022TrailDot\u0022 );\r\n\t\tgo.Parent = _root;\r\n\r\n\t\tvar r = go.AddComponent\u003CModelRenderer\u003E();\r\n\t\tr.Model = SphereModel;\r\n\t\tr.Tint = new Color( 0.25f, 0.28f, 0.34f );\r\n\r\n\t\tvar size = SphereModel.Bounds.Size.x;\r\n\t\tvar scale = 14f / size;\r\n\t\tvar dotScale = new Vector3( scale, scale, scale );\r\n\t\tgo.LocalScale = dotScale;\r\n\t\tgo.WorldPosition = basePos \u002B Vector3.Up * ( SphereModel.Bounds.Size.z * scale * 0.5f \u002B 2f );\r\n\r\n\t\t_dots.Add( ( go, 0, dotScale ) );\r\n\t}\r\n\r\n\tvoid TickTrailDots()\r\n\t{\r\n\t\tfor ( int i = _dots.Count - 1; i \u003E= 0; i-- )\r\n\t\t{\r\n\t\t\tvar dot = _dots[i];\r\n\t\t\tvar age = (float)dot.Born;\r\n\r\n\t\t\tif ( age \u003E= 0.9f || !dot.Go.IsValid() )\r\n\t\t\t{\r\n\t\t\t\tif ( dot.Go.IsValid() ) dot.Go.Destroy();\r\n\t\t\t\t_dots.RemoveAt( i );\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tdot.Go.LocalScale = dot.Scale * ( 1f - age / 0.9f );\r\n\t\t}\r\n\t}\r\n\r\n\tvoid ResetGame()\r\n\t{\r\n\t\tBuildInitialWorld();\r\n\t}\r\n\r\n\tJumpPlatform CreatePlatform( Vector2 center, float halfX, float halfY, float topZ, Color color )\r\n\t{\r\n\t\tvar go = new GameObject( true, \u0022Platform\u0022 );\r\n\t\tgo.Parent = _root;\r\n\r\n\t\tvar platform = go.AddComponent\u003CJumpPlatform\u003E();\r\n\t\tplatform.Init( center, halfX, halfY, topZ, color );\r\n\t\t_platforms.Add( platform );\r\n\r\n\t\treturn platform;\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// \u5728\u5F53\u524D\u5E73\u53F0\u524D\u65B9\u751F\u6210\u4E0B\u4E00\u5757\u3002\u5DE6\u53F3\u4E25\u683C\u4EA4\u66FF\u3001\u76F8\u5BF9\u4E3B\u8F74(\u002BX)\u5B9A\u89D2\uFF08\u4E0D\u7D2F\u52A0\uFF09\uFF0C\r\n\t/// \u4FDD\u8BC1\u8DEF\u7EBF\u50CF\u539F\u7248\u4E00\u6837\u86C7\u5F62\u524D\u8FDB\u3001\u4E0B\u4E00\u5757\u5E73\u53F0\u59CB\u7EC8\u7559\u5728\u753B\u9762\u5185\u3002\r\n\t/// \u003C/summary\u003E\r\n\tvoid SpawnNextPlatform()\r\n\t{\r\n\t\tvar prev = _current;\r\n\r\n\t\t_turnSide = -_turnSide;\r\n\t\t_dirAngle = _turnSide * Game.Random.Float( TurnAngle.x, TurnAngle.y ) * MathF.PI / 180f;\r\n\r\n\t\tvar dist = Game.Random.Float( CenterDistance.x, CenterDistance.y );\r\n\t\tvar px = prev.CenterX \u002B MathF.Cos( _dirAngle ) * dist;\r\n\t\tvar py = prev.CenterY \u002B MathF.Sin( _dirAngle ) * dist;\r\n\r\n\t\tvar halfX = Game.Random.Float( PlatformHalfSize.x, PlatformHalfSize.y );\r\n\t\tvar halfY = Game.Random.Float( PlatformHalfSize.x, PlatformHalfSize.y );\r\n\t\tvar topZ = Math.Clamp( prev.TopZ \u002B Game.Random.Float( -50f, 60f ), 40f, 210f );\r\n\r\n\t\tif ( _target.IsValid() ) _target.IsTarget = false;\r\n\t\t_target = CreatePlatform( new Vector2( px, py ), halfX, halfY, topZ,\r\n\t\t\tGame.Random.FromArray( PlatformColors ) );\r\n\t\t_target.IsTarget = true;\r\n\t}\r\n\r\n\tvoid PruneOldPlatforms()\r\n\t{\r\n\t\t// \u4ECE\u6700\u65E7\u7684\u4E00\u5757\u5F00\u59CB\u626B\uFF0C\u5220\u6389\u7B2C\u4E00\u5757\u0022\u5DF2\u7ECF\u6CA1\u7528\u4E86\u0022\u7684\u5E73\u53F0\u3002\r\n\t\t// \u4ECD\u5728\u7528\u7684\uFF08\u5F53\u524D\u4F4D\u7F6E / \u76EE\u6807 / \u8EAB\u540E\u90A3\u4E00\u5757\uFF09\u53EA\u662F\u8DF3\u8FC7\uFF0C\u4E0D\u80FD\u632A\u5230\u961F\u5C3E\u2014\u2014\r\n\t\t// \u4E00\u65E6\u6253\u4E71\u0022\u6700\u65E7\u5728\u524D\u0022\u7684\u987A\u5E8F\uFF0C\u5783\u573E\u5C31\u6C38\u8FDC\u6392\u4E0D\u5230\u961F\u5934\uFF0C\u6C60\u5B50\u4F1A\u65E0\u4E0A\u9650\u5730\u957F\u3002\r\n\t\tfor ( int i = 0; i \u003C _platforms.Count \u0026\u0026 _platforms.Count \u003E 3; i\u002B\u002B )\r\n\t\t{\r\n\t\t\tvar p = _platforms[i];\r\n\r\n\t\t\tif ( p == _current || p == _target || p == _previous ) continue;\r\n\r\n\t\t\t_platforms.RemoveAt( i );\r\n\t\t\tp.DestroyAll();\r\n\t\t\ti--;\r\n\t\t}\r\n\t}\r\n\r\n\t// ---- \u8DF3\u8DC3\u4E0E\u5224\u5B9A ----\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// \u73A9\u5BB6\u662F\u5426\u60F3\u8981\u5012\u7740\u8DF3\uFF1A\u53EA\u6309\u540E\u9000\u952E\uFF08\u53EA\u6309\u524D\u8FDB\u3001\u4E24\u4E2A\u90FD\u6309\u3001\u90FD\u4E0D\u6309\uFF0C\u4E00\u5F8B\u89C6\u4E3A\u524D\u8FDB\uFF09\u3002\r\n\t/// \u83DC\u5355\u80CC\u666F\u6F14\u793A\u548C\u65B0\u624B\u6F14\u793A\u7531 AI \u9A71\u52A8\uFF0C\u5FC5\u987B\u59CB\u7EC8\u5F80\u524D\u8DF3\uFF0C\u6240\u4EE5\u8FD9\u91CC\u4E00\u5E76\u6392\u9664\u3002\r\n\t/// \u003C/summary\u003E\r\n\tbool WantsBackJump =\u003E !_menuMode \u0026\u0026 !_tutorialActive\r\n\t\t\u0026\u0026 Input.Down( \u0022Backward\u0022 ) \u0026\u0026 !Input.Down( \u0022Forward\u0022 );\r\n\r\n\t/// \u003Csummary\u003E \u4F9D\u636E\u84C4\u529B\u503C\u8BA1\u7B97\u4E00\u6B21\u8DF3\u8DC3\u7684\u843D\u70B9/\u65F6\u957F/\u9AD8\u5EA6\uFF08\u8D77\u8DF3\u4E0E\u8F85\u52A9\u7EBF\u5171\u7528\uFF09 \u003C/summary\u003E\r\n\t(Vector3 to, float duration, float height) PlanJump( float power )\r\n\t{\r\n\t\tvar from = _piece.BasePosition;\r\n\r\n\t\t// \u53EA\u6309\u540E\u9000 = \u5728\u0022\u6765\u8DEF\u65B9\u5411\u0022\u4E0A\u6309\u5F53\u524D\u529B\u5EA6\u51FA\u624B\uFF08\u529B\u5EA6\u5408\u9002\u5C31\u5F39\u56DE\u4E0A\u4E00\u5757\u5E73\u53F0\uFF09\r\n\t\tvar origin = WantsBackJump \u0026\u0026 _previous.IsValid() ? _previous : _target;\r\n\t\tvar dir = new Vector2( origin.CenterX - from.x, origin.CenterY - from.y ).Normal;\r\n\r\n\t\tvar dist = Math.Min( MinJumpDistance \u002B power * ( MaxJumpDistance - MinJumpDistance ), MaxJumpDistance );\r\n\r\n\t\treturn ( new Vector3( from.x \u002B dir.x * dist, from.y \u002B dir.y * dist, from.z ),\r\n\t\t\t0.42f \u002B 0.22f * power,\r\n\t\t\t150f \u002B 60f * power );\r\n\t}\r\n\r\n\tvoid Launch()\r\n\t{\r\n\t\tif ( _target is null || !_piece.IsValid() ) return;\r\n\r\n\t\tHasJumped = true;\r\n\r\n\t\tvar (to, duration, height) = PlanJump( _power );\r\n\r\n\t\t_jumpFrom = _piece.BasePosition;\r\n\t\t_jumpTo = to;\r\n\t\t_jumpDuration = duration;\r\n\t\t_jumpHeight = height;\r\n\t\t_jumpTime = 0f;\r\n\t\t_prevBaseZ = _jumpFrom.z;\r\n\t\t_killZ = Math.Min( _current.TopZ, _target.TopZ ) - KillDepth;\r\n\t\t_flying = true;\r\n\t\t_falling = false;\r\n\r\n\t\t_piece.SetCharge( 0f );\r\n\t\t_piece.Impulse( 3.4f );\r\n\t\tSound.Play( \u0022sounds/footsteps/footstep-concrete-jump.sound\u0022, _jumpFrom, 0f );\r\n\r\n\t\tif ( !_menuMode \u0026\u0026 !_tutorialActive ) JumpAchievements.OnJump();\r\n\t}\r\n\r\n\tvoid TickFlight( float dt )\r\n\t{\r\n\t\tif ( !_piece.IsValid() ) return;\r\n\r\n\t\tif ( !_falling )\r\n\t\t{\r\n\t\t\t_jumpTime \u002B= dt;\r\n\t\t\tvar u = Math.Min( _jumpTime / _jumpDuration, 1f );\r\n\r\n\t\t\tvar x = MathX.Lerp( _jumpFrom.x, _jumpTo.x, u );\r\n\t\t\tvar y = MathX.Lerp( _jumpFrom.y, _jumpTo.y, u );\r\n\t\t\t// \u629B\u7269\u7EBF\uFF1A\u8D77\u70B9 0\u3001\u7EC8\u70B9 0\u3001\u9876\u70B9 jumpHeight\r\n\t\t\tvar z = _jumpFrom.z \u002B 4f * _jumpHeight * u * ( 1f - u );\r\n\r\n\t\t\t_piece.SetBasePosition( new Vector3( x, y, z ) );\r\n\r\n\t\t\t// \u6F14\u793A\u8DF3\u8DC3\u65F6\u7559\u4E0B\u8F68\u8FF9\u70B9\uFF0C\u6807\u51FA\u629B\u7269\u7EBF\r\n\t\t\tif ( _tutorialActive \u0026\u0026 _dotTimer \u003E 0.055f )\r\n\t\t\t{\r\n\t\t\t\t_dotTimer = 0;\r\n\t\t\t\tSpawnTrailDot( new Vector3( x, y, z ) );\r\n\t\t\t}\r\n\r\n\t\t\t// \u4E0B\u964D\u9636\u6BB5\u505A\u843D\u70B9\u5224\u5B9A\uFF08\u5E27\u95F4\u8DE8\u8FC7\u5E73\u53F0\u9876\u9762\u624D\u7B97\u843D\u4E0A\uFF0C\u4EFB\u4F55\u5E27\u7387\u4E0B\u90FD\u51C6\u786E\uFF09\r\n\t\t\tif ( z \u003C= _prevBaseZ )\r\n\t\t\t{\r\n\t\t\t\tResolveLanding( x, y, z, _prevBaseZ );\r\n\t\t\t}\r\n\t\t\t_prevBaseZ = z;\r\n\r\n\t\t\t// \u629B\u7269\u7EBF\u8D70\u5B8C\u4ECD\u6CA1\u843D\u5230\u4EFB\u4F55\u5E73\u53F0 \u2192 \u8F6C\u81EA\u7531\u843D\u4F53\r\n\t\t\tif ( u \u003E= 1f \u0026\u0026 !_falling \u0026\u0026 _flying )\r\n\t\t\t{\r\n\t\t\t\t_falling = true;\r\n\t\t\t\t_fallVelocity = new Vector3(\r\n\t\t\t\t\t( _jumpTo.x - _jumpFrom.x ) / _jumpDuration,\r\n\t\t\t\t\t( _jumpTo.y - _jumpFrom.y ) / _jumpDuration,\r\n\t\t\t\t\t-4f * _jumpHeight / _jumpDuration );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif ( _falling )\r\n\t\t{\r\n\t\t\t_fallVelocity -= Vector3.Up * Gravity * dt;\r\n\t\t\tvar pos = _piece.BasePosition \u002B _fallVelocity * dt;\r\n\t\t\t_piece.SetBasePosition( pos );\r\n\r\n\t\t\tResolveLanding( pos.x, pos.y, pos.z, pos.z - _fallVelocity.z * dt );\r\n\r\n\t\t\tif ( pos.z \u003C _killZ )\r\n\t\t\t{\r\n\t\t\t\tif ( _menuMode )\r\n\t\t\t\t{\r\n\t\t\t\t\tResetGame();\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tOnGameOver();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tvoid ResolveLanding( float x, float y, float baseZ, float prevBaseZ )\r\n\t{\r\n\t\t// \u5148\u5224\u0022\u6B63\u597D\u8DE8\u8FC7\u67D0\u4E2A\u5E73\u53F0\u9876\u9762\u0022\u2192 \u6210\u529F\u843D\u4E0A\r\n\t\tforeach ( var p in LandablePlatforms() )\r\n\t\t{\r\n\t\t\tif ( !InsidePlatform( p, x, y ) ) continue;\r\n\t\t\tif ( prevBaseZ \u003E= p.TopZ \u0026\u0026 baseZ \u003C= p.TopZ )\r\n\t\t\t{\r\n\t\t\t\tLandOn( p, x, y );\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// \u5DF2\u7ECF\u4F4E\u4E8E\u5E73\u53F0\u9876\u9762\u8FD8\u5904\u5728\u5E73\u53F0\u6295\u5F71\u5185 \u2192 \u649E\u4FA7\u9762\uFF0C\u8F6C\u5760\u843D\uFF08\u6E38\u620F\u5931\u8D25\uFF09\r\n\t\tif ( _flying \u0026\u0026 !_falling )\r\n\t\t{\r\n\t\t\tforeach ( var p in LandablePlatforms() )\r\n\t\t\t{\r\n\t\t\t\tif ( !InsidePlatform( p, x, y ) ) continue;\r\n\t\t\t\tif ( baseZ \u003C p.TopZ )\r\n\t\t\t\t{\r\n\t\t\t\t\t_falling = true;\r\n\t\t\t\t\tif ( !_menuMode )\r\n\t\t\t\t\t\tSound.Play( \u0022sounds/player_use_fail.sound\u0022, new Vector3( x, y, baseZ ), 0f );\r\n\t\t\t\t\tvar horizontal = _jumpDuration \u003E 0f\r\n\t\t\t\t\t\t? new Vector3( ( _jumpTo.x - _jumpFrom.x ) / _jumpDuration, ( _jumpTo.y - _jumpFrom.y ) / _jumpDuration, 0 )\r\n\t\t\t\t\t\t: Vector3.Zero;\r\n\t\t\t\t\t_fallVelocity = horizontal \u002B Vector3.Up * ( -4f * _jumpHeight * ( 2f * Math.Min( _jumpTime / _jumpDuration, 1f ) - 1f ) / _jumpDuration );\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tIEnumerable\u003CJumpPlatform\u003E LandablePlatforms()\r\n\t{\r\n\t\tif ( _target.IsValid() ) yield return _target;\r\n\t\tif ( _current.IsValid() \u0026\u0026 _current != _target ) yield return _current;\r\n\t\tif ( _previous.IsValid() \u0026\u0026 _previous != _target \u0026\u0026 _previous != _current ) yield return _previous;\r\n\t}\r\n\r\n\tbool InsidePlatform( JumpPlatform p, float x, float y )\r\n\t{\r\n\t\treturn MathF.Abs( x - p.CenterX ) \u003C= p.HalfX \u002B LandingMargin()\r\n\t\t\t\u0026\u0026 MathF.Abs( y - p.CenterY ) \u003C= p.HalfY \u002B LandingMargin();\r\n\t}\r\n\r\n\tfloat LandingMargin() =\u003E _piece.IsValid() ? _piece.Radius * 0.55f : 14f;\r\n\r\n\tvoid LandOn( JumpPlatform p, float x, float y )\r\n\t{\r\n\t\t_flying = false;\r\n\t\t_falling = false;\r\n\r\n\t\t// \u56F0\u96BE\u6A21\u5F0F\uFF1A\u843D\u70B9\u4E0D\u5438\u9644\u5E73\u53F0\u4E2D\u5FC3\uFF0C\u68CB\u5B50\u505C\u5728\u771F\u5B9E\u843D\u70B9\r\n\t\tif ( Difficulty == JumpDifficulty.Hard )\r\n\t\t\t_piece.SetBasePosition( new Vector3( x, y, p.TopZ ) );\r\n\t\telse\r\n\t\t\t_piece.PlaceOn( p );\r\n\t\t_piece.Impulse( -3.4f );\r\n\r\n\t\tif ( p == _target )\r\n\t\t{\r\n\t\t\tif ( _menuMode )\r\n\t\t\t{\r\n\t\t\t\t// \u80CC\u666F\u6F14\u793A\u4E0D\u8BA1\u5206\uFF1B\u529B\u5EA6\u5728\u65B0\u76EE\u6807\u751F\u6210\u540E\u91CD\u7B97\uFF08\u89C1\u4E0B\u65B9 SpawnNextPlatform \u4E4B\u540E\uFF09\r\n\t\t\t\t_tutorialT = 0;\r\n\t\t\t}\r\n\t\t\telse if ( _tutorialActive )\r\n\t\t\t{\r\n\t\t\t\t// \u6F14\u793A\u843D\u70B9\u4E0D\u8BA1\u5206\uFF0C\u628A\u63A7\u5236\u6743\u4EA4\u8FD8\u73A9\u5BB6\r\n\t\t\t\t_tutorialActive = false;\r\n\t\t\t\t_sinceTutorialEnd = 0;\r\n\t\t\t\tTutorialText = \u0022Your turn! Hold \u0026 release\u0022;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tvar centerDist = MathF.Sqrt( ( x - p.CenterX ) * ( x - p.CenterX ) \u002B ( y - p.CenterY ) * ( y - p.CenterY ) );\r\n\r\n\t\t\t\tif ( centerDist \u003C= PerfectRadius )\r\n\t\t\t\t{\r\n\t\t\t\t\tStreak\u002B\u002B;\r\n\t\t\t\t\tScore \u002B= Math.Min( Streak * 2, 16 );\r\n\t\t\t\t\tvar handle = Sound.Play( \u0022sounds/kenney/ui/ui.upvote.sound\u0022, p.Center, 0f );\r\n\t\t\t\t\tif ( handle.IsValid() ) handle.Pitch = 1f \u002B 0.1f * Math.Min( Streak - 1, 8 );\r\n\r\n\t\t\t\t\tJumpAchievements.OnPerfect( Streak );\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tStreak = 0;\r\n\t\t\t\t\tScore \u002B= 1;\r\n\t\t\t\t\tSound.Play( \u0022sounds/kenney/ui/ui.downvote.sound\u0022, p.Center, 0f );\r\n\r\n\t\t\t\t\tJumpAchievements.OnLanding();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// \u5F80\u524D\u8DF3\uFF1A\u521A\u79BB\u5F00\u7684\u5E73\u53F0\u53D8\u6210\u0022\u8EAB\u540E\u90A3\u4E00\u5757\u0022\uFF08\u5012\u8DF3\u76EE\u6807\uFF09\uFF0C\u66F4\u65E9\u7684\u90A3\u5757\u5DF2\u65E0\u7528\u3002\r\n\t\t\t// \u987A\u5E8F\u8981\u7D27\uFF1ASpawnNextPlatform \u662F\u4ECE _current \u5F80\u524D\u751F\u6210\uFF0C\u6240\u4EE5\u5148\u8BB0\u4E0B\u65E7\u503C\u518D\u6539 _current\r\n\t\t\tvar origin = _current;\r\n\t\t\t_previous = origin;\r\n\t\t\t_current = p;\r\n\r\n\t\t\tp.IsTarget = false;\r\n\t\t\t_target = null;\r\n\t\t\tSpawnNextPlatform();\r\n\t\t\tPruneOldPlatforms();\r\n\r\n\t\t\t// \u65B0\u76EE\u6807\u751F\u6210\u540E\u518D\u8BA1\u7B97\u6F14\u793A\u529B\u5EA6\uFF0C\u4FDD\u8BC1\u80CC\u666F AI \u6BCF\u4E00\u8DF3\u90FD\u6210\u529F\u843D\u5728\u4E2D\u5FC3\r\n\t\t\tif ( _menuMode )\r\n\t\t\t{\r\n\t\t\t\t_tutorialT = 0;\r\n\t\t\t\t_demoPower = ComputeDemoPower();\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if ( p == _previous )\r\n\t\t{\r\n\t\t\t// \u5012\u7740\u5F39\u56DE\u4E0A\u4E00\u5757\u5E73\u53F0\uFF1A\u8BA1\u5206\u89C4\u5219\u4E00\u81F4\uFF0C\u7136\u540E\u628A\u0022\u6765\u8DEF\u0022\u63A5\u56DE\u6765\u5F53\u65B0\u7684\u524D\u8FDB\u65B9\u5411\r\n\t\t\tSound.Play( \u0022sounds/kenney/ui/ui.navigate.back.sound\u0022, p.Center, 0f );\r\n\r\n\t\t\tif ( !_menuMode \u0026\u0026 !_tutorialActive )\r\n\t\t\t{\r\n\t\t\t\tvar centerDist = MathF.Sqrt( ( x - p.CenterX ) * ( x - p.CenterX ) \u002B ( y - p.CenterY ) * ( y - p.CenterY ) );\r\n\r\n\t\t\t\tif ( centerDist \u003C= PerfectRadius )\r\n\t\t\t\t{\r\n\t\t\t\t\tStreak\u002B\u002B;\r\n\t\t\t\t\tScore \u002B= Math.Min( Streak * 2, 16 );\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tStreak = 0;\r\n\t\t\t\t\tScore \u002B= 1;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tJumpAchievements.OnBackJump();\r\n\t\t\t}\r\n\r\n\t\t\t// \u6B63\u5728 P2 \u7AD9\u7740\u3001\u5F80\u524D\u76EE\u6807 P3\uFF0C\u5012\u8DF3\u843D\u56DE P1\uFF1A\r\n\t\t\t// \u65B0\u7684\u524D\u8FDB\u65B9\u5411\u662F\u521A\u79BB\u5F00\u7684 P2\uFF08\u6B63\u597D\u8FD8\u80FD\u518D\u8DF3\u4E00\u6B21\uFF09\uFF0CP1 \u8EAB\u540E\u6CA1\u6709\u8BB0\u5F55\uFF0C\u5012\u8DF3\u5230\u6B64\u4E3A\u6B62\u3002\r\n\t\t\t// \u4E0D\u5FC5\u91CD\u65B0\u751F\u6210\u5E73\u53F0\u2014\u2014\u53EA\u662F\u628A\u5F53\u524D\u6240\u5904\u7684\u5E73\u53F0\u632A\u4E00\u4E0B\r\n\t\t\tvar forward = _current;\r\n\t\t\tif ( _target.IsValid() ) _target.IsTarget = false;\r\n\t\t\t_previous = null;\r\n\t\t\t_current = p;\r\n\t\t\t_target = forward;\r\n\t\t\tp.IsTarget = true;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// \u529B\u5EA6\u4E0D\u8DB3\u843D\u56DE\u5F53\u524D\u6240\u7AD9\u7684\u90A3\u4E00\u5757\uFF1A\u72B6\u6001\u5B8C\u5168\u4E0D\u53D8\uFF0C\u53EA\u7528\u56DE\u9000\u97F3\u6548\u533A\u5206\r\n\t\t\tSound.Play( \u0022sounds/kenney/ui/ui.navigate.back.sound\u0022, p.Center, 0f );\r\n\t\t}\r\n\t}\r\n\r\n\t// ---- \u7B80\u5355\u6A21\u5F0F\u8F85\u52A9\u7EBF ----\r\n\r\n\tvoid UpdateAssistLine()\r\n\t{\r\n\t\t// \u73A9\u5BB6\u84C4\u529B\uFF08\u7B80\u5355\u6A21\u5F0F\uFF09\u6216\u65B0\u624B\u6559\u5B66\u6F14\u793A\u84C4\u529B\u65F6\u663E\u793A\uFF1B\u5F00\u59CB\u83DC\u5355\u80CC\u666F\u4E0D\u663E\u793A\r\n\t\tvar autoCharging = _tutorialActive \u0026\u0026 _power \u003E 0.01f;\r\n\t\tvar want = ( _charging || autoCharging ) \u0026\u0026 !IsGameOver\r\n\t\t\t\u0026\u0026 ( Difficulty == JumpDifficulty.Easy || _tutorialActive )\r\n\t\t\t\u0026\u0026 _target.IsValid() \u0026\u0026 _piece.IsValid();\r\n\r\n\t\tif ( !want )\r\n\t\t{\r\n\t\t\tif ( _assistVisible ) HideAssist();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tEnsureAssistObjects();\r\n\r\n\t\tvar (to, _, height) = PlanJump( _power );\r\n\t\tvar from = _piece.BasePosition;\r\n\r\n\t\tfor ( int i = 0; i \u003C AssistDotCount; i\u002B\u002B )\r\n\t\t{\r\n\t\t\tvar u = ( i \u002B 1 ) / (float)AssistDotCount;\r\n\t\t\tvar x = MathX.Lerp( from.x, to.x, u );\r\n\t\t\tvar y = MathX.Lerp( from.y, to.y, u );\r\n\t\t\tvar z = from.z \u002B 4f * height * u * ( 1f - u );\r\n\t\t\t_assistDots[i].WorldPosition = new Vector3( x, y, z \u002B SphereModel.Bounds.Size.z * 0.06f );\r\n\t\t\t_assistDots[i].Enabled = true;\r\n\t\t}\r\n\r\n\t\t// \u843D\u70B9\u73AF\uFF1A\u4F1A\u843D\u5728\u5E73\u53F0\u4E0A\u5C31\u8D34\u5176\u9876\u9762\u5E76\u53D8\u7EFF\uFF0C\u5426\u5219\u60AC\u7A7A\u53D8\u7EA2\r\n\t\tvar surface = from.z;\r\n\t\tvar hit = false;\r\n\t\tforeach ( var p in LandablePlatforms() )\r\n\t\t{\r\n\t\t\tif ( InsidePlatform( p, to.x, to.y ) )\r\n\t\t\t{\r\n\t\t\t\tsurface = p.TopZ;\r\n\t\t\t\thit = true;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t_assistRing.WorldPosition = new Vector3( to.x, to.y, surface \u002B 2f );\r\n\t\t_assistRingRenderer.Tint = hit ? AssistHitColor : AssistMissColor;\r\n\t\t_assistRing.Enabled = true;\r\n\t\t_assistVisible = true;\r\n\t}\r\n\r\n\tvoid EnsureAssistObjects()\r\n\t{\r\n\t\tif ( _assistDots.Count == AssistDotCount \u0026\u0026 _assistDots.All( d =\u003E d.IsValid() ) \u0026\u0026 _assistRing.IsValid() ) return;\r\n\r\n\t\tHideAssist();\r\n\t\t_assistDots.Clear();\r\n\r\n\t\tvar dotScale = 9f / SphereModel.Bounds.Size.x;\r\n\t\tfor ( int i = 0; i \u003C AssistDotCount; i\u002B\u002B )\r\n\t\t{\r\n\t\t\tvar go = new GameObject( false, \u0022AssistDot\u0022 );\r\n\t\t\tgo.Parent = _root;\r\n\t\t\tgo.LocalScale = new Vector3( dotScale, dotScale, dotScale );\r\n\r\n\t\t\tvar r = go.AddComponent\u003CModelRenderer\u003E();\r\n\t\t\tr.Model = SphereModel;\r\n\t\t\tr.Tint = new Color( 0.30f, 0.34f, 0.42f );\r\n\r\n\t\t\t_assistDots.Add( go );\r\n\t\t}\r\n\r\n\t\t_assistRing = new GameObject( false, \u0022AssistRing\u0022 );\r\n\t\t_assistRing.Parent = _root;\r\n\t\tvar bs = BoxModel.Bounds.Size;\r\n\t\t_assistRing.LocalScale = new Vector3( 30f / bs.x, 30f / bs.y, 2.2f / bs.z );\r\n\t\t_assistRingRenderer = _assistRing.AddComponent\u003CModelRenderer\u003E();\r\n\t\t_assistRingRenderer.Model = BoxModel;\r\n\t}\r\n\r\n\tvoid HideAssist()\r\n\t{\r\n\t\tforeach ( var dot in _assistDots )\r\n\t\t{\r\n\t\t\tif ( dot.IsValid() ) dot.Enabled = false;\r\n\t\t}\r\n\r\n\t\tif ( _assistRing.IsValid() ) _assistRing.Enabled = false;\r\n\t\t_assistVisible = false;\r\n\t}\r\n\r\n\r\n\tvoid OnGameOver()\r\n\t{\r\n\t\tif ( IsGameOver ) return;\r\n\r\n\t\tIsGameOver = true;\r\n\t\tif ( !_menuMode )\r\n\t\t{\r\n\t\t\tSound.Play( \u0022sounds/kenney/ui/ui.button.deny.sound\u0022, PiecePosition, 0f );\r\n\t\t\tJumpScores.Record( Score, Difficulty );\r\n\t\t\tJumpAchievements.OnGameEnd( Score, Difficulty );\r\n\t\t}\r\n\t}\r\n}"}]}