A Game which challenges you to throw your iPhone as high as possible.


 

by:

Jungne Losang – julos14,   Ditlev Selz Stoltenberg Stjerne – distj14,   Thomas Lemqvist – thlem14.

Bitbucket link: https://bitbucket.org/thlem14/let_me_fly/src/master/


Demo

Introduction

The Idea

The idea is a simple game where you measure how high you are able to throw your phone. Using the accelerometer it is possible to detect when the phone is in the air and when it hits the ground or the person caught the phone. Using a free fall calculation it is possible get the height of throw. A score will also be calculated, to provide some gamification elements. The score should include elements such as device type or consecutive throws over some height.
As an example a user with an iPhone X will get a higher multiplier than a user with an iPhone 4s therefore the user with an iPhone X will have a higher score than users with an older iphone if they throw the phone with the same height.

Common Platform

In order to understand and replicate the application, one needs basic understanding of acceleration and free fall and the free fall formula. The application relies on the accelerometer for detecting when the phone is thrown into the air and caught again. The height of the throw is calculated based on the time the phone was in free fall.

The chart above shows the acceleration of an iPhone in a throw. The first rise in the acceleration is when the phone is flinged. As the acceleration rapidly decreases, it stabilizes and is roughly constant at 0, this is when the phone is in free fall, and is flying freely up and down. The acceleration then rapidly increases again, this is when the phone is caught, or hits the ground.

Let me fly measures the time the phone is in free fall, by looking at the acceleration of the phone. This time measurement is then use in this  free fall formula [link] 0.5 * g * (t/2) ^ 2, where g is the acceleration of gravity (9.81 here in denmark), and t is the time of the free fall.

There already exists an application similar to Let me fly. It is called send me to heaven or (S.M.T.H). It is created by Carrotpop [link] . The purpose of the application is to try and throw the phone as high as possible. When a throw is done the height is calculated and the displayed. It is possible to see the local score and a global score. Let me fly will generate a score which includes more parameters than just the height of the throw.

Problem Statement

Have you ever wondered who of your friends can throw an iPhone the highest? Ever craved the thrill of throwing expensive electronics in the air? Do you hate consumerism and want to make a statement to show how cool you are? Tired of your iPhone and want to commit insurance fraud while having fun?

Goals and Objectives

The aim of the application is to create a user friendly and intuitive application with the ability to challenge friends and family to a fun, daring and potentially costly game.

The product will be achieved by including users and improving the product by evaluating the feedback coming from the user tests. The guidelines for iOS development will also be taken into account when creating the design of the application. These guidelines will be the foundation of the design and the user tests will be used for improving the use of these guidelines.

Methods and Materials

Brainstorming was used to generate ideas and features for elements of the application. It was decided that the app should be a game, which included the phone sensors as input. A brainstorming session was then conducted to come up with ideas for how to incorporate sensors into a game. The group eventually settled on the idea of a game where you score points for throwing your iPhone into the air.

A conceptual design of the application, was created based on the brainstorm. The concept is to create an application where a user can throw the phone into the air and then use a formula to estimate the height of the throw and calculate a score based on the height and the type of device thrown. It should then also be possible to compare one’s throws to others and challenge them.

Some simple prototypes was developed, in order to get early user feedback on the simple things, such as the concept and navigation of the application. The first two prototypes was created on paper to get some fast mockups of the product. These paper designs was then used in the Marvel application to get a better feel of how the application works and be able to show it to potential users.
The evaluation of the user testing was done by giving the prototype to the potential users using the marvel app and see their interaction with the prototype. No information was giving to the users prior to the test as we want to evaluate the full first time experience of the prototype.

A use-case diagram was produced to capture and show functional requirements and how they interact with other actors. This was done by converting our conceptual design ideas into functional requirements and brainstorming on additional functional requirements. Non-functional requirements were also found by brainstorming and some of them were described in the project description.

The application was implemented using the MVC design pattern, in the Swift programming language. In order to get accelerometer data in the app, the CoreMotion SDK was used. In order to store data on the phone, the CoreData SDK was used. In order to play sound files in the app, the AVFoundation SDK was used.

Results

Prototypes

In order to ensure that the application would be usable, the group developed two different prototypes, which was focused on the UI and navigation flow of the application. These prototypes was mildly inspired by Apples design guidelines.

Prototype 1 (above) is a “main menu-style” navigation, where the navigation flows from a single view, and links to the others with buttons.

Prototype 2 (above), is a “tab-style” navigation, where the navigation is controlled by the tab buttons at the bottom of the screen.

These prototypes were then converted into a digital prototype using marvel app [link]. These digital prototypes were then shown to potential users, for them to provide feedback.
The feedback on the two prototypes indicated that both navigation styles worked well. With the one in prototype 2 being better for separating the different aspects of the application such as the game itself and the leaderboard.

Based on this feedback it was decided that a combination of the two prototypes will be used, where the navigation of game elements will use the prototype 1 style, and the prototype 2 style will be used for navigating between the game, leaderboards etc.

Use-cases and Requirements

 

The use case diagram (above) shows what features are available to the user, The diagram also shows one particular important thing, the extension of UC3.1 to UC1. This extension means the user has to read and accept a disclaimer, before being able to play the game. This is done so that the group is not held accountable for any damages made to property using the application.
Furthermore, the diagram shows what other systems that the application talks to, e.g. the Backend Server.

Based on the use cases, some requirements was established.

Functional requirements:

  1. Users should be able to start the game
  2. Users should be able to see high scores, e.g. global, local, phone-based, or personal.
  3. Users should be able to accept the disclaimer
  4. Users should be able to see achievements
  5. Users should be able to challenge each other
  6. Users should be able to share their results
  7. Users should be able to purchase power-ups

Non-functional requirements:

  1. The application should be able to measure height based on time in free fall.
  2. The application could calculate a style score based on phone attitude (pitch, roll and yaw).
  3. The application should be developed in the Swift language.
  4. The application should use GameKit[link] for the leaderboards, achievements, and challenges, etc.
  5. The application should notify the user about challenges.
  6. The application must make use of CoreData, CoreMotion, and AudioKit.

Implementation

Classes (Models, views, controllers)

The general implementation of application is split into 4 different class types models, views, controllers and data transfer objects (DTO).
There are currently four different models. The throw game, throw, achievement and achievements model. A controller tells the throw game model to start a new throw and to stop. The throw game model is then responsible for creating a throw model. A throw model consists of a start time and an end time. The model is then also responsible for calculation the height of the throw. This is done using the following formula
d = 0,5*9,80655*(timeInAir/2)^2
The reason the time is divided by two is that the time captured time is from the moment that the phone is thrown this means that it is both time it takes the device to travel upwards and down again.
Then there is the achievement model. This holds information like the title of the achievement, if a user has the achievement and the value of the height. The Achievements model then holds a group of achievements that are alike. This can be an achievement for the height. There can be different achievements for how high they can be thrown like 5 and 10 meters into the air.

The DTO’s are used for the request to and responses from the server. They implement the codable interface for easily converting the object into json object. These json objects are then send to and from the server.
There are three objects which are used to send and receive data. The score of the last throw is send to the server. A response from the server for the last throw is received and a DTO for the leaderboards.

The most important controller in the project is the throw as it has the responsibility of the game.
The throw controller uses two important methods from the life-cycle of the application viewWillAppear and viewWillDisappear. The viewWillAppear is used to start the accelerometer updates and a timer is used register the method that is to be called when an update from the accelerometer is registered.

override func viewWillAppear(_ animated: Bool) {
    motionManager.startAccelerometerUpdates()
    timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
}

The viewWillDisappear then stops the accelerometer from updating and invalidates the timer.

override func viewWillDisappear(_ animated: Bool) {
    motionManager.stopAccelerometerUpdates();
    timer?.invalidate()
    timer = nil
}

These methods are used to ensure that the application does not use any unnecessary processing power as the users navigates to different views.

CoreData

The CoreData SDK is used to store data relevant to achievements on the device. CoreData is accessed when the player opens achievement screen, to load previously earned achievements. The data in CoreData is only updated after the player has completed a throw and earned an achievement.

The following code snippet show how the data is accessed in CoreData.

let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: Achievement.entity().name!)

do {
    let result = try context.fetch(fetch)
    
    var dic = [String : AchievementsModel]()
    
    if var achievements = result as? [Achievement] {
        achievements = achievements.sorted(by: { $0.groupId < $1.groupId})
        for data in achievements {
            if let aa = dic[data.groupName!.name!] {
                aa.models.append(data)
            } else {
                let achi = AchievementsModel(title: data.groupName!.name!, description: "Height in meters")
                achi.models.append(data)
                dic[data.groupName!.name!] = achi
            }
        }
    }
}

CoreMotion

The CoreMotion SDK is used to access the accelerometer data, for estimating the time in free fall.

The following snippet of code shows how the CMMotionManager is used to get the acceleration data from the accelerometer. The data is then converted into a magnitude of the acceleration, using the formula a = sqrt(x^2 + y^2 + z^2)

The magnitude of the acceleration is then used for estimating the time in free fall, based on changes in the acceleration and thresholds.

@objc func update() {
        
    if let accelerometerData = motionManager.accelerometerData?.acceleration {
    let a = sqrt(pow(accelerometerData.x, 2.0) + pow(accelerometerData.y, 2) + pow(accelerometerData.z, 2))
        
        var lastAcc = throwGame.lastAccel ?? 0.4
        
        if lastAcc > a + 1 && a < 0.4{
            if throwGame.isThrowing == false {
                // start ....
            }
        }
            
        if lastAcc < a-1 {
            if throwGame.isThrowing {
                // stop ....
            }
       }
            
       throwGame.lastAccel = a
   }
}

Leaderboards (TableView)

The leaderboards were implemented using TableView. The TableView gets populated dynamically with the results fetched from the node server. The results are retrieved as an array which is iterated through to create an array of objects of the LeaderBoardScore struct. The data from each LeaderBoardScore in the array is then used when dequeuing prototype cells to populate the cells with data. The leaderboard is refreshed when the leaderboard tab is opened or when the TableView is pulled down.

The following snippet of code shows the implementation of the function responsible for dequeuing prototype cells and populating them with data. The function is a required function of the UITableViewDataSource protocol.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "LeaderBoardTableViewCell", for: indexPath) as? LeaderBoardTableViewCell else {
        fatalError("The dequeued cell is not an instance of LeaderBoardTableViewCell.")
    }
    let rank = indexPath.row + 1
    let data = tableData[indexPath.row]
    cell.leaderboardLabel.text = "Rank " + rank.description + ": " + data.name + "\nScore: " + String(format:"%.1f", data.score) + "\t Height: " + String(format:"%.1f", data.height) + "m"
    return cell
}

AudioKit

The application plays sound when in the air, to indicate a successful throw, also as a little gag, the sound is “I believe I can fly” by R. Kelly. Then when the free fall is over, it will stop the song, and play a loud crash sound.
The sound is played using the AVAudioPlayer, from the AVFoundation.

The following snippet of code shows how the sound is played, it starts by checking if the file exists, by using the guard function. It then sets up the AVAudioPlayer, and plays the sound-file.

func playSound(fileName: String) {
   guard let url = Bundle.main.url(forResource: fileName, withExtension: "mp3") else {print("sound not found"); return }
      
   do {
       try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
       try AVAudioSession.sharedInstance().setActive(true)
      
       player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
          
       guard let player = player else { return }
          
       player.play()
   } catch let error {
       print(error.localizedDescription)
   }
}

func stopSound() {
   guard let player = player else { return }
   player.stop()
}

Problems

Achievements bug

There is a very reproducible bug in the achievements view, where the individual achievements switches positions every other time, meaning that the height achievements switches to the position of the height consecutive achievements.
To reproduce the bug, simply switch between the views using the navigation tab at the bottom.

Throw Trigger

There are currently some problems with the throw trigger being imprecise. It is possible to illegitimately activate the throw trigger by tapping hard on the phone on a table, other times it wont activate in legitimate throws.

GameKit

It was problematic to integrate with the built in Apple GameKit SDK, as it needed a developer account in order to work properly. As an alternative, a node.js server was developed, the server uses a SQL database to store the data about the throws, and is able to provide various leaderboards.

The node.js server will not be described further, as it is out of scope for this project. The server is available on the following github link https://github.com/DrBumlehund/ios_programming_e18.

HTTP vs HTTPS

As standard iOS does not allow one to make HTTP calls as apple wants it developers to use HTTPS. As our server does not have a certificate it became problem that throws an error when trying to fetch a HTTP request. A workaround for this was to change an application setting to allows for HTTP requests.

Discussion

It was possible to create an application with the ability to estimate the height of which the phone was thrown. As this is current just an estimation using a known formular there are certainly ways to improve the estimation of the height calculation.

The scoring element of the throws was implemented quite elegantly. It takes into account the height, device mode, device type and how many conservative throws over different heights the user has thrown.
Some parts of the application can be improved on the user experience but as far as our test suggests it app is quite intuitive.

The application was quite well received by users when presenting them with a test device. They did not however think that this would be an application they would install on their own devices.

The sound-effects when throwing the phone are very satisfying, but can be ruined by a bad throw trigger, where it either ends too soon or starts too late, or not at all.

The challenge and share functionality still needs to be implemented, as well as the rest of the achievements.

Our app is similar to the android app S.M.T.H. Our app has more gamification elements, like score multipliers and achievements. Our app also has music that plays, to indicate that the phone is thrown. S.M.T.H. has a better throwing screen, as it is not possible to accidentally leave the throwing screen when catching a thrown phone.

We are generally happy with how the application turned out, Were also quite satisfied with the process of both design and development as there weren’t many difficulties.

In the future, we could publish the application, but since we can’t get on the apple app store, as we are not using HTTPS, we could look into getting on the App Valley [link] store.
Since the throw trigger are a bit naive, it could be interesting to look into machine learning using CoreML [link].

Leave a Reply