1. Introduction

This report presents and explains thoroughly the project we made for the “iOS Programming” course. The choice for our project was to develop a game compatible with iOS devices in order to apply the knowledge and the concepts of the course. The emerging trend of mobile devices (and especially tablets) guided us to investigate the gaming field from that scope. Our game is named “Viking Madness” due to the main character of the game being a Viking who goes through an adventure that includes English knights.

 

Having this simple concept in mind, we initially aimed for an adventure game with an almost 3d feel (2,5d) but soon enough, we headed to another direction due to constraints. These were mostly related to finding game graphics that could support our initial idea. Since we did not posses graphic design knowledge, we had to work with what we could find. Hence, we ended up using a Viking named Baelog from the well known sequel game “The Lost Vikings”.

 

Developing a game has always been an interesting and challenging task at the same time. It is quite challenging to engage the user and make him enjoy a game. For this reason our goal was to make a funny game with moderate difficulty and moderate-sized levels provided as checkpoints. With video games emerging back from more than 30 years ago, lots of genres have been developed. By reviewing online resources (and even by our own experience), we found out that platformers were and still are, one of the most recognizable game genres for the gaming community. We also considered arcade and puzzle games but we chose platformers because they usually involve more interactive animations and physics.

 

The main goal of the project is to provide entertainment and fun to the user by playing our game. In order to achieve this goal, our game has to intrigue the user to play it by a handful of facts. Some of these are: smooth animations with real world physics, creativity engagement in objective accomplishment, suspenseful combat, engaging game mechanics etc. While applying these, we have to make sure that the effort is not exceeding the threshold of becoming frustrating – not too complex mechanics or very difficult objectives. This mostly applies because we are aiming casual gameplay and our target group varies from kids, to adults and is irrelevant of occupation.

2. Methods and materials

 

2.1 Idea conceptualization

In order to formulate our game concept we initially reviewed other games available and tried to extract pros and cons between them. More specifically, we narrowed down our search over a good (iCycle) and a bad (Krazy Kong) example of games and distinguished what features and design characteristics really grew their good or bad fame. After reviewing these, we proceeded with brainstorming sessions in order to pick a game genre and form a conceptual model for the game.

 

2.2 Prototyping

By the time our conceptual model was ready, the next step was to develop paper prototypes to collect some initial feedback in order to evaluate our concept. To provide some more input with the prototypes, we also sketched a use-case diagram because it would make a lot of sense for the users we presented our prototypes since they were from the software engineering domain. Furthermore, identifying use cases helped us staying focused to our target direction of implementation.

 

2.3 Implementation

After having all the initial feedback we could gather, we had to start the implementation phase. The implementation phase included a lot of studying of the Objective-C programming language and Apple’s latest game development framework called SpriteKit. Aside from the programming resources, we also had to gather multimedia resources for graphics and sounds, both of them being essential for our game. Sources and credits of our resources are referenced in the credits section.

 

Since no specific architecture is predefined by SpriteKit, we proceeded with our implementation using a tutorial book as a guide. If we consider the necessary resources (sprites, images, sounds) as our application data, the SKScene class as a view median and SKSpritenode as a controller, one could assume that our architecture follows somehow the Model-View-Controller pattern – although it might not be that distinguishable.

 

For graphics and animations, as mentioned above, we used images and the so-called sprite sheets. A sprite is a two-dimensional image or animation that is integrated into a larger scene. Sprite sheets are collections of sprites placed in a single image file with certain order and predefined standards in terms of size and margins, so the developer can navigate into the sheet using a coordinate system (e.g. Baelog’s sprite sheet). Unfortunately, SpriteKit has no direct way of handling sprite sheets and our book guide also used a 1:1 approach for sprites – images. So, we had the sprite sheet split into single images. This applied for animated graphics – our main character and his opponents namely – since the rest of the sprites had a static texture.

 

The terrain generation for the level was a mandatory task for fulfillment. We used a predefined tile map sprite – in a nutshell a sprite sheet with tiles used as a palette that can form a custom terrain or map when combined. There is software available that can handle this type of sprites in order to generate maps; one of them, which is also freeware, is called Tiled and is the one we used. Tiled provides functionality for building maps using tile map sprites and stores the generated map in XML format (.tmx file extension). Apart from the map generation, you can also add object and image layers to a map as a way of including properties and static content. We also used an object layer to provide spawning points and patrol ranges for the English knights.

 

Physics was yet another challenge; fortunately SpriteKit provides support for physics by implicitly utilizing the open source C++ physics Box2D. The main drill for simulating physics using SpriteKit was to define a physics body for a given sprite and some properties, based on the behavior we wished to implement.

 

Each of these tasks did not take place simultaneously, in the beginning we set up some basic animations to get feedback for the sizes and the first impression on the models, then a main screen, the first level map and so on. Each small iteration that had some significant changes from the previous was tested as a non or semi-working prototype by potential users or us.

 

3. Results

 

3.1 Idea conceptualization

After reviewing various games available in the app store, we chose a good and a bad example to extract dos and don’ts for our project. We consulted various websites with already made reviews on games (e.g. toucharcade, slidetoplay). The following list presents the characteristics we marked as positives and negatives from these games.

  • Positives
    • Attractive visuals and animations
    • Realistic physics
    • Creativity engagement
    • Moderate sized levels
    • Humorous interaction (sounds, visuals)
  • Negatives
    • Buggy responsiveness
    • Bad physics
    • Lack of checkpoints / user progression
    • Limited features
    • Bad lore, possible copyright infringement

Another issue worth mentioning but did not really consider, was the frequent appearance of advertisements which was disturbing. We did not take this into account at all since our project did not include a goal for giving monetary value to our game. The named bad and good games we extracted this characteristics are Krazy Kong and iCycle respectively.

 

After brainstorming on game ideas and concepts, we initially chose to develop an adventure game with the main character being a Viking that would travel between levels and fight English knights to complete a storyline. The initial gameplay was from a 3rd person’s point of view from above, in a 2,5d experience (similar to other games e.g. the famous Diablo series). This derived mostly from our enthusiasm for this specific gameplay and genre but we also considered the market trends proving 2,5d being popular these days (most MOBA games implement this technique). Soon enough, we realized that this approach was not possible due to the resource constraints of character models, let alone all the work needed to rotate the map in a diagonal view. So, we switched our project goal and aimed for a similar genre, making a platformer instead but still supporting combat features. Other ideas that entered the competition were puzzle games (e.g. Puzzle Bobble variant) and arcade games like space-shooters (e.g. Galaxian).

 

3.2 Prototyping

Our first paper prototypes with the original adventure-game idea returned valuable input of game features we could and should include, some of them are: quest objectives, power-ups, abilities, particle effects, sounds etc. Unfortunately, these prototypes were lost somewhere in the process and it’s no longer possible to attach them in this report. For the real prototypes (in the actual device) we constantly received feedback for matters like sizing, movement, bugs, controlling etc.

 

From our final prototype, we received input for making the theme a bit more medieval to match the Viking age and maybe some more savage actions and scenes. We wish we could go into more details about this but it could be considered inappropriate.

 

3.3 Design and Architecture

Our architecture is fairly simple and follows the trends presented in the book-guide we used: a single view controller (ViewController) that launches with the application, a scene (Level) for each scene of our game and entities and sprite nodes (Entity, SKSpritenode) to fill our scene with, which perform SKActions. A simple class diagram, that describes the inheritance in our project, is shown below:

Viking Madness ClassDiagram

The green color represents the SpriteKit classes, the orange the Cocoa touch classes and the blue our custom classes.

 

Below is also presented the use case diagram we presented on our first prototypes.

Viking Madness UC

3.4 Implementation

3.4.1 Baelog & Knights

Both, Baelog and the knights use a similar approach on how their animations are loaded into the scene. They do not share the same class though because they perform different tasks in the game. For each of their animations, there are image files (.png format) numbered and a method is implemented that returns an array of SKTextures that are needed to be loaded into an SKAction. Then, when the respective animation needs to be performed, the SKSpritenode runs the respective SKAction. Both are subclasses of the abstract class Entity that holds a few basic properties and methods that the entities share like direction, health, texture methods etc.

 

3.4.2 Level

The level class is the responsible class for loading and maintaining the level scene. It is instantiated when the story mode is selected and it immediately starts loading the resources into the scene:

  • Background
  • Map
  • Entities & other (Baelog, Knights, power-ups)
  • User interface
  • Actions
  • Sounds
  • Quest

The background loading is one of the simplest processes of SpriteKit, sprite initialization from an image file and then it is added to the respective SKNode. SKNodes are abstract node classes that are used as containers in order to maintain the representation of the so-called node tree.

 

The map node holds all the generated sprite nodes based on the tile map sprite and the xml (.tmx) file. The map generation is handled by a custom library (JSTileMap), which was developed by Jeremy Stone. The use of this library provides us a custom object of JSTileMap type, which is a subclass of SKNode. This object contains all the sprite nodes and the object properties for each layer as set on Tiled. In the map file we placed objects that hold coordinates that outline the terrain in order to create physics bodies for it, so our entities can have a solid ground to stand. Other objects were coordinates for power-ups (beers), knight spawns and a range for their patrol.

 

The user interface is a separate node that holds sprites for the directional buttons, the buttons for jumping and attacking and the avatar placeholder that shows information of the character status (health points, energy and lives remaining). In order to animate the drop or increase of health and energy bars we provided a node on top of them with a sprite used as a transparent mask of the same size. When the health and energy are full, the masks will be in the same position of the bars and as they drop, they are moved to the left progressively to unmask them (disappear).

 

Actions and sounds are initialized in the beginning for the sole purpose of having them available when they are needed. If we had them instantiated when they were first called, then the application would hang for a few milliseconds to allocate memory and process the request.

 

The quest initialization is another simple sprite loaded from an image with a fade-out action taking place after a few seconds in the beginning of the level.

 

3.4.3 Main Menu

The main menu scene is a small set of sprites providing a flashing logo and a few buttons with different actions when pressing them (fading in-out, tilt effect). Upon pressing story mode, the 1st level begins to load (the application hangs for a short duration due to the massive load of resources).

 

3.4.4 Frame loop

SpriteKit uses a traditional rendering loop for handling each frame. There are a handful of methods that are being called in succession plenty of times within milliseconds. The first of these methods in the sequence is the update method. In order to determine the time passed within each update call (or frame) we subtract the current time and the time since the last update

 

if (_lastUpdateTime) {

       _dt = currentTime - _lastUpdateTime;

   }

   else

   {

       _dt = 0;

   }

   _lastUpdateTime = currentTime;

 

 

The value _dt, or delta time, helps us determine the amount of distance our moving sprites have to cover with each frame among other things. This is essential because each frame is rendered in a different time that is based on processing time.

 

- (void)moveSprite:(SKNode *)sprite

         velocity:(CGPoint)velocity

{

   CGPoint amountToMove = CGPointMultiplyScalar(velocity, _dt);

   sprite.position = CGPointAdd(sprite.position, amountToMove);

 

}

 

3.4.5 Physics

The physics are handled by assigning an SKPhysicsBody to an existing sprite node. Physics bodies are defined by closed shapes and have specific restrictions. There are 3 types of bodies: volume-based which are distinguished between static and dynamic and edged-based. The physics simulator directly manipulates dynamic volume-based bodies; certain properties such as gravity, mass, forces etc. are taken into account. The simulator does not affect static volume-based bodies, yet they have volume which means that no collision-able object can appear inside it. Edge-based bodies are like static volume-based but they are volume-less which means that objects can appear inside their boundaries. In our implementation, the terrain is using edge-based bodies, Baelog and the knights have rectangular volume-based bodies and the power-ups (beers) have static volume-based bodies. To setup the collisions each body category is assigned a 32bit unsigned integer bitmask. Then, inside the frame loop, when the physics simulation methods are called, the contact detection is handled by making logic OR the bitmask couple that comes in contact. The collision is handled by assigning the collision bitmask property for each body with the logic OR of bitmasks we wished to collide with.

Also, for debugging purposes, we added a category class of SKSpritenode in order to attach borders to our physics bodies, so we can visualize them and understand collisions and contacts more efficiently.

 

3.4.6 Camera movement

The camera movement is done by moving the whole player node that holds the terrain, the character, the knights and the power-ups. The amount that the node has to cover between each frame is proportional to the delta time and its direction is the vector in the opposite direction of the vector specified by the center of the screen and Baelog’s current position in the screen.

 

-(void)moveCamera

{

   if (!CGPointEqualToPoint(_camera, CGPointZero)) {

       [_bgLayer removeAllActions];

       [self runAction:[SKAction sequence:@[[SKAction waitForDuration:0.2],

                                             [SKAction customActionWithDuration:0

                                                                   actionBlock:^(SKNode *node, CGFloat elapsedTime) {

            CGPoint velocityVector = CGPointMultiplyScalar(_camera, -1);

           if (_bgLayer.position.y > 0 && velocityVector.y > 0)

           {

               velocityVector = CGPointMake(velocityVector.x, 0);

           }

           if (_bgLayer.position.y < -768 &&

               velocityVector.y < 0)

           {

               velocityVector = CGPointMake(velocityVector.x, 0);

           }

           if (_bgLayer.position.x > 0 && velocityVector.x > 0) {

               velocityVector = CGPointMake(0, velocityVector.y);

           }

           if (_bgLayer.position.x < -11776

               && velocityVector.x < 0)

           {

               velocityVector = CGPointMake(0, velocityVector.y);

           }

           [self moveSprite:_bgLayer velocity:velocityVector];

           CGPoint unit = CGPointMake(_terrainMap.mapSize.width/ (6 * _background.size.width),

                                       _terrainMap.mapSize.height/ (6 * _background.size.height));

           CGPoint bgVel = CGPointMultiply(velocityVector, unit);

           [self moveSprite:_background velocity:bgVel];

       }]]]];

   }

}

 

The method moveCamera is called with each frame (inside the update method). The “if” cases make a range check so the camera won’t go outside the level’s boundaries. The same camera-movement logic applies for the background image but in a different (smaller) proportional of the same vector because the background image is much smaller than the terrain node. It also gives a better feeling as if the background is in a deeper layer (in the long distance).

 

3.4.7 Enemy behavior

Since we could not invest much of our available time into intelligent behavior of our non-playing-characters, we implemented a rather simple (and buggy) behavior for our knights. As stated above, when the level is initialized, the knights begin a dummy patrol within a certain range from their spawn position. This behavior continues forever until the direct distance between them and Baelog becomes smaller than a certain threshold. This distance is calculated by each knight individually and it’s done by informing every knight of Baelog’s position in every frame. When the distance becomes smaller than the specified threshold, the knight starts to hunt down Baelog and as soon as he reaches him, he attacks him until he or Baelog drops dead.

 

3.4.8 Level completion/Game over

For the sake of having a deliverable prototype, the level completion and the game over status provide almost the same result. Upon killing all knights – with the last one being at the right edge of the map – a “Level Cleared” label node with green color appears with a rotating-scaling animation. On the other hand, if he loses all of his 3 lives, the whole player node is removed from the parent node and a “Game Over” label node with red color appears and after 5 seconds the main menu screen is presented again.

The full code repository is available at

Bitbucket

4. Discussion

4.1 Evaluation

After comparing the project goals and the user feedback from testing our game, we can confidently say that our project succeeded in fulfilling them – at least most of them. While testing with potential users we realized that we should invest more time designing the user controls as it was not clear how to control the character. We were able to observe users engaging with our game in a positive way that seemed fun and entertaining for them. There are plenty of bugs though, that disturb the user experience; they are discussed in the next section. Unfortunately, due to time constraints, we were not able to deliver the second game mode named “Brawler Mode” and the options menu. Also, one level is not enough for a whole game but our design and implementation is not far away from making the level generation fully dynamic.

 

4.2 Possible Improvements

Animations suffer from a lot of bugs to start with. In the video, there are notable interruptions of animations that should not be interrupted or the other way around (Baelog walking in middle-air). Another issue that was identified but we didn’t manage to deal with in time, was the knights attacking too frequently, resulting to game-breaking mechanic (the knight can kill Baelog if he corners him without being able to attack). In regards to level generation, with some more work, we would only need to provide map files (.tmx) and make the code to dynamically load each level upon completion. Another mandatory feature, that our game lacks, is saving level progression (checkpoints). Yet another one would be, enemies having health bars or some kind of interface for the player to know their remaining health.

It is also worth mentioning that, despite we had no intention of copyright infringements, we had to use copyrighted material in order to have a starting point and to save time for more difficult tasks. We have no intention of launching our game and the use of copyrighted material was purely for educational purposes.

 

5. Conclusion

As a summary, we engaged the challenge of developing a video game. The challenge took place as part of the course “iOS Programming” and was developed for iPad devices. We explained our design, implementation and software process included in this project. We believe we achieved the learning goals of the course and covered the course’s scopes. Comparing to our good and bad examples, we definitely do not believe that we achieved the level of the good example but at least we managed to surpass the bad one. If we were to continue this project from this point, we would firstly implement the possible improvements mentioned above and continue prototyping until the desired goal was reached (“fake it till you make it”).

 

References

Apple SpriteKit guide

SpriteKit guide book by Ray Wenderlich

iOS Programming: The Big Nerd Ranch Guide 4th edition

Countless threads from stackoverflow.com

Credits

JSTileMap by Jeremy Stone

Knights by wulax

Map tile palette by socapex

All sound effects from Partners In Rhyme

Font by docxy

Leave a Reply