Heres a video showing the new opponents system! You can see some debugging bits that I added - lines show paths and boxes show nodes (see below for what that even means…)
The opponent data baked into each track is made up of nodes (points) and paths between nodes. After parsing that data, I end up with a list of nodes, and for each node, several possible paths.
When the race starts, each opponent finds the node closest to themselves. They then work out which path to take from that node, and also what they will do at the end of that next path. (So the opponents are looking 2 steps ahead). This means they know how tight the next corner is going to be. As they accelerate along the path, they constantly work out how long it will take to brake to get around the corner. This means they can keep their speed up, but slow down enough to get around tight corners (usually!). The opponent data also has a max speed setting per path, like if the path ends at the edge of a cliff, but its not used consistently. I’m not even sure if the original game uses it.
I’ve made each node quite big, so opponents don’t need to drive exactly over it - this means they will start turning before reaching the middle of the corner, and can get around it a bit faster. There are three different types of paths - Race, General and Cheat.
Race paths direct the AI around the race track. General paths let them drive around random streets. Cheat, as far as I can see, lets them get to places that normally they wouldn’t normally be able to get to. (Like hard to reach rooftops). A good implementation will be to choose a mix of race and general paths, generally biasing towards race paths, and use cheat paths occasionally when the player isn’t looking! Currently, the opponents always choose the race path.
Also, when the player gets far away from an opponent, the original game teleports the opponent near the player. I still have to implement that and also opponents attacking the player. Fun stuff :)
I’m tired after a night of coding, so I’ll just post some screenshots now and maybe add some details later…
The first one shows off the almost finished special volume support in the Magnachem industrial zone. The windscreen is using the specvol reflection, and the water doesn’t have much gravity (which is why I’m doing a wheelie)
These next ones are even more fun! Yep, opponents are here :) They don’t do anything yet, just sit there in a nice grid. This weekend I want to implement the opponent paths and get them following the race track around. I love this screenshot, because when I was browsing around my screenshots to upload it to the blog, I missed it at first thinking it was a screenshot I took with the original game :) Awesome! Sleep time now…
I’ve fixed a couple of problems that I hadn’t noticed before. The main one was I wasn’t always placing the volumes correctly, which was giving weird results (like water gravity being applied while driving down a road). Using the SpecVol Edit Mode in C1 and comparing it to my engine helped to fix that problem. I love the Stainless Software left edit mode available in the original game :) Heres a random screenshot showing correctly rotated and scaled special volumes (from the CoastB track). These particular special volumes define the engine sound to be used in the tunnels. Also shows the latest ripped font for rendering messages to the screen. Each font takes an hour or two because I have to manually cut each glyph from the original bitmap and massage it into the XNA bitmap font format. Its the only resource I have to modify instead of reading directly from the data files at runtime, but I’m planning to code up a proper C1 bitmap font renderer.
The other problem is implementing the ‘default water’ special volume. The race.txt file doesn’t contain where the water is, so the C1 engine has to look at all the water materials, get a list of water vertices, then build up a list of locations. I’ve done all this, and for the most part it works, but theres still some weird edge cases to figure out.