To improve my skills with instantiating objects and using script references, I developed a recreation of Crossy Road mechanics through scenarios of specific patterns that appear in the game.
Characters in Crossy Road can move one tile at a time in any of the four directions. To make this I added input checks for the direction keys. When one of these is pressed, the chicken moves 1 tile in that direction and rotates to face the direction pressed. Crossy Road’s play area is 9 tiles wide, and the player starts in the central tile. I used a variable to track which lane the player was in, and only allowed sideways movement if the player was within the lane boundaries. There is a move cooldown to stop the player from being able to move too fast. Every time they jump, the cooldown is set to a value and the player can not move again until that number of frames have passed.
Once the player has started moving, the camera follows them and speeds up if they are near the top of the screen to keep the player in view. At the start of the game, the player and camera positions are saved. When the player dies, both the player and camera are reset to the starting positions. In Crossy Road, the player dies if they are too far behind the screen scroll. To implement this I compared the player and camera positions, and ran the player’s death function using its reference in the camera script if the player was behind.
To choose which scenario to play, there is a dropdown menu to select the 3 types. Depending on the type selected, there is a secondary dropdown to choose the variation of that type. When the player presses the start button, the drop down options selected determine the scenario number, which sets the parameters for creating the scenario.
In Crossy Road, there are set lane sequences that repeat. I made a set of prefabs with different layouts to simulate this, making the layouts to target the specific scenarios. When the player selects the scenario, the first chunk is randomly spawned out of the options that contain that specific scenario. Each time the player reaches the start of a new chunk, the next chunk is instantiated ahead out of the camera view so the player never runs out of lanes.
I created this using chunk prefabs so that I could accurately recreate the lane patterns for each scenario, instead of another way such as spawning individual lanes which would require additional code for scenario for lane pattern logic.
In police scenarios, a road is randomly selected in each chunk to be the police lane. When the chunk is spawned, every object with the road script is added to an array. This also applies to roads from previous chunks, so the roads are filtered into a new array by comparing their parent to the most recent chunk. A road is then selected from this array to be the police lane by getting the script reference of the road and setting the police lane variable to true.
To make the double police scenario, the lane directly after the first police lane is also set to a police lane. I limited the random road selection to be unable to choose the last road, so that there was always one after it to use as the second road.
The last police scenario works the same as the first, but also has railways in the chunks. Every set of road lanes has railways positioned just before, so that the scenario works no matter where the police lane is, as the train pressure always forces the player into the road section with the police car.
Making the cycle scenarios used the same code to get the array of roads. I then set the cycle, speed and direction of specific roads using the road script references to create the patterns. However, the variables set by the road script were overriding this, so I added a new boolean variable to the road script to track if a road used random data. I set the roads that were not part of the cycle patterns to random, so that the cycle could be successfully generated without being overridden.
While playtesting, I noticed that cars from new chunks would sometimes spawn in the location of the old chunks. I found that this happened when the same chunk spawned again, as my current code compared the name, which was identical for duplicate chunks. To fix this, I created a list of the spawned chunks, and added new chunks when they were spawned. I checked if the list contained the new chunk and if it did not, it would be detected as new so the roads were added to the array.
In the AB cycle scenario, the cycle, speed and direction of the two lanes are identical, but the cycles are offset from each other. To create this offset, I changed the starting value of the count of one of the lanes so that it was already partway through its cycle. I decided to use this method as it required minimal changes to the existing code as opposed to changing the cycle mechanics for this one scenario.
Two of the lane priority scenarios focus on trains instead of cars. To make these scenarios I added the railways to an array in the same way I had done the roads previously. Trains in Crossy Road always have the same speed and interval time, and they are so fast that the direction rarely affects gameplay. This means that only the timing of train spawns needs to be changed to create the scenarios. To make the train stall scenario, I set the counter of each train script higher than the previous so that the trains spawned in order, whereas the multi train scenario sets the counter off every train script to the same value, so they all spawn at the same time.
The railway script uses a counter that spawns a train when it reaches 2000. To vary when the train spawns, the counter starts at a random number. In Crossy Road there is a red light warning just before the train spawns. To implement this I added a red light that turns on when the counter is 1700 and off when the counter reaches 1900.
Each road in Crossy Road has between 1-4 cars followed by a big gap before repeating. The roads have a spawner script which randomly chooses the cycle and car direction of that road. For 1 cycles, a car is spawned routinely at a set interval. For 2, 3 and 4 cycles, there is an interval multiplier to create a larger gap between cycles.
When the car is spawned it gets the spawner script component from its road parent object and sets its speed using the defined value in the spawner script. As the speed of the road is set in the start function, every car on a road has the same speed which replicates how they move in Crossy Road and prevents the cars from colliding.
In Crossy Road, there are two main skills in police car situations: recognising the police lane and playing around it. Police car lanes can be recognised as they are completely empty before the police car appears.
Originally I used a main cycle in between police cars. The first version I made used a boolean to track when the main cycle was over to start the police cycle, but switching between road modes made the police spawn too fast after the main cycle resulting in collisions. I tried to fix this with a second boolean to track the gap between cycles, but this became very complicated and confusing to implement. I changed this to an integer tracker, using 1 for the main cycle, 2 for the police and 3 for the gap after police before the main cycle restarts.
The police cars travel much faster than regular cars, so I changed the car script to get the police speed from the spawner script if it wasn’t a regular car. After playtesting I found that it was quite rare to encounter a police car, so I removed the main cycle so that police cars spawned on the police lanes more frequently.
As all the roads had the same interval between car spawns, slower lanes would have cars bunching up with gaps that were too small to get through. To fix this, I changed the interval depending on the road speed. Once the road has randomly chosen the speed, it calculates the speed percentage of the max speed and uses that to generate the interval percentage which is proportional to the speed. The interval is then set to the maximum interval length minus the interval percentage, making the interval inversely proportional to the speed. However, this created a further issue. Cars with high speeds generated high interval percentages, which then resulted in very low intervals, creating an endless stream of cars. To fix this, I set a minimum bound for the interval, so that the interval doesn’t go too low.
An issue I found was that in the first few seconds the roads were completely empty as no cars had spawned yet, whereas in Crossy Road the roads are populated instantly. To fix this I changed the counter in the road script to start just before the interval, causing the first car to spawn almost immediately. I chose to implement this change over spawning cars at different stages along the road as this kept the cycles more consistent. I also added a fake loading screen when a scenario is selected to hide the empty roads at the start.
When making the scenarios, I had used multiple functions for the same purpose in different scenarios so that it was easy to understand how each section worked. After I finished creating the scenarios I cleaned up the code by using a single function for creating the road array used by 7 scenarios, and a function for the train array called by the other 2. I also moved the code for the random chunk selection of each scenario into the same function.
By developing this project I have improved my skills with instantiating and using scripts references of objects, such as setting variables of an instantiated object using its script reference.