{"author_link":"\/users\/tater-hater","author_name":"tater-hater","author_uid":"tater-hater","comments":[],"epoch":1713414824,"event":"LD55","format":"md","ldjam_node_id":393944,"likes":6,"metadata":{"p_key":"206273","p_author":"tater-hater","p_authorkey":"1002021","p_urlkey":"442801","p_title":"Making Tracks: How I made a track ","p_cat":"LDJam ","p_event":"LD55","p_time":"1713414824","p_likes":"6","p_comments":"0","p_status":"WAYBACK","us_key":"1002021","us_name":"tater-hater","us_username":"tater-hater","event_start":"1712966400","event_key":"120","event_name":"Ludum Dare 55"},"node":{"_collation":{"body_sanitizer":"TextUtils::SanitizeHTML via existing importer","event":"LD55","removed_author":false},"_superparent":383062,"_trust":3,"author":2021,"body":"For my [game](https:\/\/ldjam.com\/events\/ludum-dare\/55\/the-game-with-forced-financial-competitions-featuring-wizards-and-demons), I wanted to make something with racing. I was a little fixated on that, since I made an arcade racer with the theme sort of shoehorned in. The hardest part for me was figuring out how to make tracks, manage progress on the track, record laptimes, etc. Fortunately, I had a really good place to start. \n\nI watched this [video](https:\/\/www.youtube.com\/watch?v=saAQNRSYU9k) from Sebastian Lague years ago and was enamored.  The ability to dynamically generate road like curves on the fly in unity was exciting, because at the time, I really had no idea how to make roads for a racing game in unity other than hand make it. This showed that it was possible to do all sorts of fun designs and dynamic tracks. I wrote code that used his Unity plugin several years ago and made a little demo that allowed me to generate tracks from a JSON file. \n\nIt's great and all to make a mesh that looks like a track, but there are no rules for how that road should work. How do you know if you're cutting corners? how do you know if you completed a lap? \n\n### Checkpoints\nI updated the code from Lague's generator to add a game object along the path created. I created a prefab with a trigger box collider and fence pieces and named them iteratively. \n![Pasted image 20240417220911.png](\/\/\/raw\/5e7\/z\/64449.png)\n\nThis created a sequential list of trigger box colliders along the route that I could then use to check the user's progress along the track. I track the player's progress using an OnTriggerEnter function.\n``` csharp\nvoid Generate()\n\u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (pathCreator != null && prefab != null && holder != null)\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 DestroyObjects();\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 VertexPath path = pathCreator.path;\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 spacing = Mathf.Max(minSpacing, spacing);\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 float dst = 0;\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 int cout = 0;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 while (dst < path.length)\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Vector3 point = path.GetPointAtDistance(dst);\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Quaternion rot = path.GetRotationAtDistance(dst);\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 var temp = Instantiate(prefab, point, rot, holder.transform);\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (isFromFile)\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 temp.transform.localScale = new Vector3(1, trackFromFile.roadWidth \/ 2, 1);\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 temp.gameObject.name = cout.ToString();\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (cout == 0)\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 var finish = Instantiate(finishLine, temp.transform.position, temp.transform.rotation, holder.transform);\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 finish.transform.localScale = new Vector3(0.02f, trackFromFile.roadWidth * 2, 0.2f);\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 dst += spacing;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 cout++;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 TotalCheckpoints = cout;\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 }\n```\n\nWith the following script, the player can't progress unless they hit each trigger sequentially. In the future, it might be more fair to give a time penalty instead, but with only 48 hours, making the player hit every single one was all I could really do. \n``` csharp\n\u00a0 \u00a0 private void OnTriggerEnter(Collider other)\n\u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 int checkpointName = int.Parse(other.gameObject.name);\n\n\u00a0 \u00a0 \u00a0 \u00a0 if (currentCheckpoint == placer.getTotal())\n\u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/lap completed\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Debug.Log(\"Lap Completed\");\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 nextCheckpoint = 1;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 currentCheckpoint = 0;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 RecordLapTime();\n\u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 if (checkpointName + 1 == nextCheckpoint)\n\u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 nextCheckpoint++;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 currentCheckpoint++;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Debug.Log(\"next: \" + nextCheckpoint + \" current: \" + currentCheckpoint);\n\u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 if (checkpointName == 0 && !startTimer)\n\u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 startTimer = true;\n\u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 }\n```\nThis was how I created the tracks for the game. I inputted a JSON file with the coordinates for the bezier curve, drew the mesh, installed checkpoints, and viola! A racetrack.\n\nCheck it out for yourself! Let me know if you have any other improvements I could implement.  https:\/\/ldjam.com\/events\/ludum-dare\/55\/the-game-with-forced-financial-competitions-featuring-wizards-and-demons","comments":0,"created":"2024-04-18T04:27:10Z","files":[],"files-timestamp":0,"id":393944,"love":6,"love-timestamp":"2024-04-18T11:40:33Z","meta":[],"modified":"2024-04-18T11:40:33Z","name":"Making Tracks: How I made a track ","node-timestamp":"2024-04-18T04:40:48Z","parent":391195,"parents":[1,5,9,383062,391195],"path":"\/events\/ludum-dare\/55\/the-game-with-forced-financial-competitions-featuring-wizards-and-demons\/making-tracks-how-i-made-a-track","published":"2024-04-18T04:33:44Z","scope":"public","slug":"making-tracks-how-i-made-a-track","subsubtype":"","subtype":"","type":"post","version":1232777},"node_metadata":{"n_key":"393944","n_urlkey":"442801","n_parent":"391195","n_path":"\/events\/ludum-dare\/55\/the-game-with-forced-financial-competitions-featuring-wizards-and-demons\/making-tracks-how-i-made-a-track","n_slug":"making-tracks-how-i-made-a-track","n_type":"post","n_subtype":"","n_subsubtype":"","n_author":"2021","n_created":"1713414430","n_modified":"1713440433","n_version":"1232777","n_status":"WAYBACK"},"source_url":"https:\/\/ldjam.com\/events\/ludum-dare\/55\/the-game-with-forced-financial-competitions-featuring-wizards-and-demons\/making-tracks-how-i-made-a-track","text":"For my [game](https:\/\/ldjam.com\/events\/ludum-dare\/55\/the-game-with-forced-financial-competitions-featuring-wizards-and-demons), I wanted to make something with racing. I was a little fixated on that, since I made an arcade racer with the theme sort of shoehorned in. The hardest part for me was figuring out how to make tracks, manage progress on the track, record laptimes, etc. Fortunately, I had a really good place to start. \n\nI watched this [video](https:\/\/www.youtube.com\/watch?v=saAQNRSYU9k) from Sebastian Lague years ago and was enamored.  The ability to dynamically generate road like curves on the fly in unity was exciting, because at the time, I really had no idea how to make roads for a racing game in unity other than hand make it. This showed that it was possible to do all sorts of fun designs and dynamic tracks. I wrote code that used his Unity plugin several years ago and made a little demo that allowed me to generate tracks from a JSON file. \n\nIt's great and all to make a mesh that looks like a track, but there are no rules for how that road should work. How do you know if you're cutting corners? how do you know if you completed a lap? \n\n### Checkpoints\nI updated the code from Lague's generator to add a game object along the path created. I created a prefab with a trigger box collider and fence pieces and named them iteratively. \n![Pasted image 20240417220911.png](\/\/\/raw\/5e7\/z\/64449.png)\n\nThis created a sequential list of trigger box colliders along the route that I could then use to check the user's progress along the track. I track the player's progress using an OnTriggerEnter function.\n``` csharp\nvoid Generate()\n\u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (pathCreator != null && prefab != null && holder != null)\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 DestroyObjects();\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 VertexPath path = pathCreator.path;\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 spacing = Mathf.Max(minSpacing, spacing);\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 float dst = 0;\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 int cout = 0;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 while (dst < path.length)\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Vector3 point = path.GetPointAtDistance(dst);\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Quaternion rot = path.GetRotationAtDistance(dst);\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 var temp = Instantiate(prefab, point, rot, holder.transform);\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (isFromFile)\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 temp.transform.localScale = new Vector3(1, trackFromFile.roadWidth \/ 2, 1);\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 temp.gameObject.name = cout.ToString();\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 if (cout == 0)\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 var finish = Instantiate(finishLine, temp.transform.position, temp.transform.rotation, holder.transform);\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 finish.transform.localScale = new Vector3(0.02f, trackFromFile.roadWidth * 2, 0.2f);\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 dst += spacing;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 cout++;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 TotalCheckpoints = cout;\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 }\n```\n\nWith the following script, the player can't progress unless they hit each trigger sequentially. In the future, it might be more fair to give a time penalty instead, but with only 48 hours, making the player hit every single one was all I could really do. \n``` csharp\n\u00a0 \u00a0 private void OnTriggerEnter(Collider other)\n\u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 int checkpointName = int.Parse(other.gameObject.name);\n\n\u00a0 \u00a0 \u00a0 \u00a0 if (currentCheckpoint == placer.getTotal())\n\u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/lap completed\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Debug.Log(\"Lap Completed\");\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 nextCheckpoint = 1;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 currentCheckpoint = 0;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 RecordLapTime();\n\u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 if (checkpointName + 1 == nextCheckpoint)\n\u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 nextCheckpoint++;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 currentCheckpoint++;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Debug.Log(\"next: \" + nextCheckpoint + \" current: \" + currentCheckpoint);\n\u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 \u00a0 \u00a0 if (checkpointName == 0 && !startTimer)\n\u00a0 \u00a0 \u00a0 \u00a0 {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 startTimer = true;\n\u00a0 \u00a0 \u00a0 \u00a0 }\n\u00a0 \u00a0 }\n```\nThis was how I created the tracks for the game. I inputted a JSON file with the coordinates for the bezier curve, drew the mesh, installed checkpoints, and viola! A racetrack.\n\nCheck it out for yourself! Let me know if you have any other improvements I could implement.  https:\/\/ldjam.com\/events\/ludum-dare\/55\/the-game-with-forced-financial-competitions-featuring-wizards-and-demons","title":"Making Tracks: How I made a track ","wayback_source":[]}