top of page

Development

Development is an iterative process of expanding the design concepts based on the foundations. A robust foundation to the system is important to make the most of the resources provided to the developer by the platform. As the game is intended for a wider audience with a design plot, Android devices vary with hardware limitations and a concurrent feedback through playtest improve the compatibility on relevant devices. Development is divided into system design, gameplay, technical art and UI.

I. System Design:

Level design of Space Ball Jump is linked with player's intuition to hone their skills by practicing multiple times. This means that platforms must be placed at safe feasible distance for the player to jump and make decisions in that moment to feel accomplished. To implement the level design concepts, some of the references are taken from games like Minecraft, Terraria, Pac-Man etc to create a randomly generated level. On the other side, a well designed use of procedurally generated model is made in games like Subways Surfer, Temple Run, Ball Jump, etc., where the previously generated content is constantly discarding with the resources that are traversed by the player and creating space for the new content. A procedurally generated system decides in a moment to choose the outcome which would be best in that situation, often a hybrid of randomly generated sub-models as in the games like DeadCells and Rogue (1980). A common pattern among games that use a level based system is that they have a start and an end to them. Considering the design to represent the details of the nine planets as levels, a randomly generated system was chosen over a procedural generator because of the simplicity of start and end based on level design of Space Ball Jump and imagining the possibility of adding more rules or elements (objectives, gems etc) in an easy to understand manner. Once the idea to use a random generator was in place, the system code was made to call other components like platform (the block on which the ball lands) class and pickups (objectives, gems) class. The challenge that was faced in older prototype version is that although the random generator functioned as intended there was a game breaking bug during gameplay that had to be addressed and better organization of the level design elements was needed by separating the components individually as actors in the random generator. It was initially thought to use an actor as a whole entity that would contain cube mesh as the root component and a box collider surrounding it would be attached to the root component (Fig1). On the gameplay side, the character would be allowed to jump if the ball is in contact with the top side of platform collider (Fig 2). The criteria to move the character in one single direction is by calling the character collider event function (called when the sphere collider i.e. attached to the character mesh is collided with another object like platform collider, pickups etc) when it is in contact with the top portion of the platform. This reason led to the game breaking bug later when the character collides with the platform's rear side. The character always presumed to move forward (the ball used to be stuck with the rear side of the platform collider as the character collider function was constantly triggered to move forward), while it was supposed to do the opposite i.e. not move and fall. So, there are two points to note with a simple random generator - one the platforms and colliders need to be independent of each other and second is the need for a robust system to support the generator as the requirement to add more resources increases with more levels. Although, the shortcomings helped improve the model, the prototypes of different level design models (see previous section) helped understand the behavior of harder levels (patterns: 3-7, see previous section for level design), which also means that it would be easier to create initial level i.e. Mercury, and all would be needed is testing the compatibility of assets(models and materials) on the device later. 

Block+Collider.png
Fig. 1. A box collider is attached to the mesh component as part of 1 actor. While the ball moves in 1 direction using the top side of the collider, rear side caused the ball to get stuck, this is because the ball could not distinguish sides.
Honeycam 2020-02-07 01-52-45.gif
Fig. 2. Older prototype version tests advanced level design methods. The ball moves forward based on contact with the top side.

At this stage, prototyping different methods as part of the platform's level design helped analyze the pace of gameplay, however, the algorithm needed support of organizing resources for optimization and fixing gameplay issue with colliders. The assets were called on-the-fly in the earlier versions without having an organization when they were not required. A type of design pattern called object pool was used to solve the above challenge by using a fixed amount of objects stored in a pool. The pool objects are instantiated when the level is initially loaded, used when needed and returned back to the pool when their use is done. The technique is helpful for caching as the object is returned back to the pool and not completely destroyed as in the case of earlier version of calling new platform objects during gameplay when needed and calling 'Destroy()' (Unreal C++ built-in garbage collector) when not needed. Another instance of using an object pool is during checkpoints in level design. Checkpoints act as a safe region where a player does not have to worry about the platforms that they have traversed before and if the player dies later from this point, then they could start from the checkpoint as part of the retry. This would also mean that the assets (platforms, colliders, powerups etc) would have to be reused again which were spawned past the checkpoint. In this scenario, an object pool helped boost performance as the fixed size of objects (assets) were instantiated at the beginning of the level (instantiate in UE4 C++ 'BeginPlay()'), using the objects from the pool to load one part of the level and return the objects back to the pool when their use is done, while using them again from the pool when needed during the level. Objects in the pool consists of mesh (platform) and collider assets. Looking back at the reason of using a single actor consisting of a mesh and an attached collider, one alternate approach could be to use these components separately as part of the object pool. The object pool instantiates a set number of meshes and colliders that are used appropriately when needed. These colliders are adjusted based on the number of platforms that are spawned at a single given time. Currently, the set consists of two box collider types- one is used as a face or a rear side to the first platform in the contiguous series, allowing the player to fall when the ball hits the collider, while the second one is top side causing the player movement. To calculate the position of the collider facing top of the platform, one approach could be to half the distance of the platform(s) in x-direction as i.e. the direction of the ball movement. As the algorithm is based on random generator, the difference of the platform(s) is added to the position in x-direction of the last element chosen from the object pool. 

Collider_code.PNG

In the above code, 'SetColliderForRandomTiles()' function takes two parameters - first is the object of the collider pool (AColliderGeneratorActor class contains Top and Face Collider components) and second is the number of blocks that are requested by the random generator from the pool. 'ObjectPooler->GetPooledColliderObject()' passes the created object from the pool and stores (variable - 'poolableColliderActorRandom()') in the random generator class of 'AGridPattern'. 'middleLocation1' stores the value of the difference from x location(s) of the platform(s). The result is added to the last platform location in variable 'localPosition1'. As the value of the movement changes over x and z directions and y remains constant. Value of '-430.0f' was chosen at random and could be any other value but it was chosen as a constant y-direction value throughout. This would also mean that if the value of y-direction changes then the player would not be able to land on a safe platform feasibly. 'tempObjCube[]' is an array to store the pointers of the object pools. Imagine that the player is at the last platform and there is a series of platforms that they have to land on, the collider must be generalized for any number of platforms in that instance. It is worth knowing that the colliders cannot be imagined to be placed on top of the platforms if the platforms itself do not exist. Hence, the two steps were to spawn the mesh first without any of its own collider properties and place different collider components around it with their own purposes. 'SetFixedGridTiles()' takes parameter in following order - number of blocks, collider pool object and precise vector position of the platform. Once the position is known, 'CommonPatternProperties()' overloaded function takes the collider pool object as the parameter and activates the collision of the collider (code below): 

Collider_code2.PNG

Collider of each mesh object helps search with the interaction to begin as the next step when the character falls on the the collider. For example when the collider falls on either face (rear or front) of the collider, the next collider (if attained) would be able to achieve as a platform by the character. In conclusion, it must be feasible for the character to land on a platform. If the possibility is not attained, then the player would fall and die to continue with the next retry and it is given below:

bottom of page