Catformer

Catformer is an infinite scrolling 2D platformer. With it’s one-finger mechanic the challenge is to climb as high up as possible while dodging hazards. This was a one semester, six-team project.


Level Building

A test run of the background prefab

The team decided the progression of the level would involve different environmental stages that loop a certain number of times before transitioning to the next one. So my first responsibility was to make a prefab with adjustable properties:
  • The speed for each layer
  • The total number of stages
  • The sprites for each layer
  • How many times each stage would loop before the next one
The background code also handled the task of scaling the sprites so their width matched the screen.

Platforming

There are three types of platforms the player jumps on depending on the stage they’re in and the system for this spawns them randomly with a check that prevents players from relying on one pattern to guarantee a safe upward path.

Demonstration of recycling platforms

The platforms themselves are managed by a pooling system. A fixed list of platforms are instantiated at the beginning (based on the highest observed number of active platforms), and then disappearing platforms are queued to reuse when spawning new ones. To make platforms reusable, I saved the common data between them into a scriptable object, which the parent spawner passes into one platform prefab. One difficult part of this was how every platform has a particle effect attached to it, but particle system components can’t be referenced individually like sprites or numbers. To address this, we saved a prefab with the particle system attached to it, then the platform’s script would mirror the properties over using C#’s reflection system.


Reflection Loops
	
// The property info for the ParticleSystem class type and the saved instance
PropertyInfo[] particleProps = particles.GetType().GetProperties();
ParticleSystem savedSystem = data.ParticlePrefab.GetComponent<ParticleSystem>();

// For each property in this class,
foreach (var property in particleProps)
{
	// Grab each particle module (except subemitters which aren't used)
	if(property.PropertyType.Name.Contains("Module") && !property.PropertyType.Name.Contains("SubEmitters"))
	{
		// Grab the corresponding module object on this GameObject, grab its properties,
		object module = property.GetValue(particles);
		PropertyInfo[] properties = module.GetType().GetProperties();
		object newModule = savedSystem.GetType().GetProperty(property.Name).GetValue(savedSystem);

		// Then loop through these properties and set them to the value from the prefab object.
		foreach (var prop in properties)
			if(prop.GetIndexParameters().Length == 0 && prop.CanWrite)
				prop.SetValue(module, prop.GetValue(newModule));
	}
}

// Repeat the process for the ParticleSystemRenderer.
ParticleSystemRenderer renderer = GetComponentInChildren<ParticleSystemRenderer>(),
	savedRenderer = data.ParticlePrefab.GetComponent<ParticleSystemRenderer>();
particleProps = renderer.GetType().GetProperties();

foreach(var property in particleProps)
{
	if(property.GetIndexParameters().Length == 0 && property.CanRead && property.CanWrite && !property.Name.Contains("materials", true))
	{
		if (property.Name.Contains("material", true) && !property.Name.Equals("sharedMaterial"))
			continue;
		property.SetValue(renderer, property.GetValue(savedRenderer));
	}
}