My story of creating a motivational application (iOS and Android) for a daughter with a daughter on Unity and C #
This is the first part of the story (mixed with a story about my mistakes and their solutions) about how I (about two years in my spare time) developed a mobile application for iOS and Android that would motivate my daughter to solve math examples so that reached automatism in the fundamentals of arithmetic (the warehouse of the number 10 or the multiplication table). In the end, it turned out the application that allows the child to make money with his mind.
I used the Unity engine and the C # language, as well as an additional set of software like the obligatory Photohsop or Audacity (to create sounds).
Story Plan (Part One)
- About monetization
- Why Unity
- About Scriptable Objects
- About plugin Anima2D
- About Lean Localization
- About iTween
- About Unity Analitics
- About Visual Studio
Prehistory and Colorado beetles
My previous experience is several years in 3D and, more recently, the development of several indie games for a couple with a programmer, where I acted mostly only as a designer and artist (very rarely writing some elementary C # scripts). Although, I am familiar with programming firsthand (Basic kolupal at school and dabbled in C / C ++ in the university).
This whole epic began with a problem. I saw that when solving school problems, the daughter “stumbles” and makes mistakes not on something complex, but on the basics. I decided that it would be fun if I wrote her a funny application, where she would solve examples in a game form (gaining experience and achieving automatism in this way). And in order to motivate her even more, I made sure that she received money for the correct solution of the examples (the amount of money was calculated by the application based on the number of correct answers, and then I deduced the required amount, paying my daughter cash).
Although ... this story began even earlier. First, I made my daughter an application that paid money for learning English words. But this application turned out to be much more difficult to implement so that it became convenient not only for me (the developer) and my daughter, but also for other parents. Therefore, the English application is still an internal development.
I chose money as a motivator because it was the easiest to implement. And also because of personal memories from childhood: I loved doing everything for money. Even if it was a very tedious job, such as collecting Colorado potato beetles from potatoes. I remember collecting a half-liter jar of them (my parents paid me 1 kopeck for each beetle). So I thought that since my daughter (I did not check it, but I am firmly convinced of this), then she should like it too. Well, in the end did not lose.
My wife is against encouraging children to do something for money. But my argument dissuaded her a little bit: in the case of this application, the child receives money not for what he should do (such as homework), but for the fact that he additionally practices mathematics in his spare time.
The second reason why I took up the development - I wanted to practice programming. “Becoming a programmer” was my cherished dream from the first classes at school (right after the dream of “becoming a scientist”, but before the dream of “making cartoons”).
Monetization and pleasure
Initially, I made this app (the working title was Math4Ami) exclusively under iPod touch 5's daughter. I didn’t even think to make the application available on all iOS devices or publish it for everyone and, moreover, didn’t think to release it for the Android platform (I’ve heard a lot of scary stories from iOS developers plus I don’t have anything to test the Android version).
Some time ago, I was eager to publish it in the AppStore (I was very attracted by the idea that I would have my own application in the Apple store and everyone would be able to see it).
I thought I would manage it in a month. After all, all the functionality was ready, the matter remained for the small one - to make it working for all aspect ratios of the screen and understandable not only for me, but also for other parents. And now, six months later, I published it in the AppStore and Google Play.
I initially decided to make Math4Ami completely free without a hint of monetization. There are several reasons for this decision.
The first. As you already understood, I initially made it free for my daughter and did not want to fasten anything at the “end” of the development.
The second. I decided that it would be a development for my own pleasure. I already have a similar experience - I am doing a blog for fun (which initially only ate money: money for hosting and a domain name, time for writing articles and their promotion). My logic was as follows: if I pay money for visiting the water park, buying a book or ice cream to enjoy, then why can't I afford to pay money for hosting, a domain name or membership in the Apple Developer Program, if that brings me pleasure too.
The third is for the sake of a wider audience, which would be significantly curtailed, if I made the app paid (as many developers of children's apps do). I dismissed other types of monetization for the reasons described below.
I don’t stand for in-game advertising — I don’t like it when the design of the application is disfigured by advertisements (except for advertisements in the form of video views at will , and not when the video pops up around the corner). In addition, for participation in the programs “Made for Kids” by Apple and “Designed for Families” by Google, you need to strictly filter the ads shown to children.
I myself, as a parent, block in-game purchases on all devices and children, when downloading applications on their own, simply physically cannot buy anything inside the application. Another thing is when the parent himself initially buys the application for the child (but I already mentioned this above).
Why Unity and how
I chose Unity because I worked in it before and I liked it. I also had a good friend a C # programmer and I hoped that he would help me with programming, if that. Unity also has a smart community and it’s very easy to find answers to almost all questions on how to implement something in C # + Unity in Google.
I also worked with Unreal (as a 3D artist), but I didn’t understand either C ++ or 2D functionality.
Initially, Math4Ami was “cloudy,” although it was said loudly. All data was stored on my eVPS (Elastic Virtual Private Server) and I used FTP to transfer TXT files with data and application settings (my hands did not reach the database, although I did not take the first steps in writing my server on node.js undertook). To work with ftp I screwed an easy-to-use Simple C # FTP Class to Unity .
Then, when I decided to make the app public, I refused the server part.
On the one hand, it would be too confused: do not like authentication (oh so they do not like users) or save the session identifier in iCloud using NSUbiquitousKeyValueStore (this would automatically identify the user between the removal of the applique and reinstallation), but I didn’t understand with this (perhaps the article Writing Plugin for Unity would help me correctly. Part 1: iOS , but then it was not there yet).
On the other hand, the data in this application is not so important that they need to be stored on the server.
On the third hand, there was no need for server synchronization. Here for my application for teaching English - there yes, synchronization was needed. As the parent adds new words in the parent application, and the child teaches them in the children's application (although, maybe I'm an amateur complicate things).
In the end, I made everything stored locally (on the device), but not in txt, but in JSON format.
ScriptableObject and correct answers
JSON format in conjunction with ScriptableObject turned out to be a great find. I used the native UnityEngine methods to serialize objects in json - JsonUtility (and then saved the json text files locally on the device to the Application.persistentDataPath folder ).
ScriptableObject (SO) is a separate topic of conversation, but I will still touch on it. I can't even imagine how I used to live without SO.
Everything that I use in my work, I learned from these two mega-useful videos about the principles of working with SO (and the accompanying code on GitHub and Bitbucket):
- Game Architecture with Scriptable Objects ( code );
- Overthrowing the MonoBehavior Tyranny in a Glorious Scriptable Object Revolution ( code ).
Personally, I used SO for these purposes:
- To store data (so that you do not have to crawl into the code each time to add a new functionality or data):
- kind of examples
- currency type
- button style (I have the same buttons in many places and I just create SO-based skins for them),
- reward value, etc.
- As global variables (which are visible in all scenes):
- the number of correct answers
- amount of money earned
- currently active settings
- current type of example
- timer, records, etc.
- To store logic (for example, a subscription to the event of receiving the correct answer).
The only minus of SO for working with data is that it cannot store data between sessions of an application: an SOset asset (after a cold launch of an application) will always contain the data that you wrote there in the editor. Therefore, the logic of my work is:
- After the application starts, I read the json-files from the disk and load the data from them into SO assets (FromJsonOverwrite method).
- While the application is running and I need maximum performance - I work only with Assemble Scriptable Objects. These asset s keep data all the time while the application is running or in the background.
- When I need to save data (for example, when the application is finished or on the fly), I serialize SO to json (ToJson method) and save to disk.
There is a (obvious) disadvantage of this approach - you cannot save only one changed parameter to disk (if there are several of them in SO), all the time you have to save the text json file completely.
But many data do not need to be saved to disk (for example, the current number of correct answers) and then SO is a powerful tool that allows me to significantly simplify my work.
In the video below, I show an example of my implementation of accounting for correct and incorrect answers using UnityEvent (event - whether the number of correct answers has changed) + Listener (listeners do some work if they heard that the correct answer was received, and the logic of subscription to the event listeners also implemented on SO) + SO (keeps records of the number of correct answers):
Thus, I can not only manually enter the correct and erroneous answers, but simply moving the slider, generate new examples and test the logic of the application.
Anima2D, characters and twitching smile
The video above shows that when a new penny falls, the other pennies begin to smile broadly, and when the turd falls, the penny is terrified.
For a long time I could not win a glitch, when, when switching from one type of smile to another, the change did not occur instantaneously, but blinked (from one state to another) for a while. Then I will tell you how I implemented it and how I won this glitch.
The change of facial expressions is implemented using the script Sprite Mesh Animation, which is part of the powerful Anima2D plugin (which Unity recently bought and made free). This script essentially just switches the sprites for the mouth (smile, open smile, scared mouth) using the Frame slider :
The whole ambush is that the value of the Frame slider cannot be changed directly from the scripts, but only through the animation system. Therefore, I created a new animation layer OpenSmile (arrow 1 in the figure below) in Additive blending mode with Weight = 1 and added a horror animation ( Coin_scared ) and a wide smile ( SmillingWide ).
By the way, did you notice what a bad example I am setting, with the names of the animation? I'm still in the process of casting the names to the same style. It would be correct to change Coin_scared to A_CoinScared (why this is the way to read in the section “What I regret”).
I created a new layer, and did not use the old one, because I didn’t want to rewrite the animation of the mouth. I only needed to change the sprite of the mouth (from a smile to a wide smile or from a smile to horror) and to keep the animation of the mouth from the base layer. That is why I chose the Additive blending mode - adding a new animation to an existing one (without overwriting it).
At their core, the SmillingWide and Coin_scared animations are just the animation of the Frame slider at positions 1 and 2, respectively.
The whole problem was that the transition from any state to a state of horror (when clicking on the transition (arrow 2 in the figure above), the properties of this transition (arrow 3 in the figure above) open in the inspector) did not occur instantly, but smoothly over the length of time Transition Duration (arrow 4 in the figure above), which was not zero by default. Thus, the value of the Frame slider could not change correctly, because there were only integers, and therefore there is no intermediate value between 0 and 1. Therefore, to get rid of the blinking glitch, it was necessary only to reset the Transition Duration value .
But the condition for the transition to a state of horror is the trigger isScared (arrow 5 in the figure above). I activate this trigger in code with the following reference to the object on which the Animator component hangs (with the controller, the layers of which I showed above):
How I translated the application into different languages
Somewhere here, on Habré, I read that you need to think about localization at the beginning of creating the application and I followed this advice ... immediately ... after a year and a half of development (as soon as I decided that Math4Ami would be public).
Why I chose Lean Localization (except for the reason that the plugin is free) I don’t remember, but I remember that I chose long and hard.
It was very easy to use it. You can either manually set the language or use automatic language detection. I stopped at the automatic language detection (for example, other children's applications).
The plugin translates everything (from text to sounds and pictures).
But I still made one mistake with localization (although I made it intentionally, because I wanted to try different approaches). The error is that I did not put all the phrases in a text file (on the left in the figure below). Some phrases remained inside the Lean Localization component (to the right in the figure below). So now, when I give this file to a Japanese translator, I will have to work manually (to transfer EVERYTHING to a text file).
Although some things cannot be translated by a text file (such as the space "", which I used as a separator between thousands), you will still have to use the component.
Once upon a time I watched a smart Juice it or lose it video about how all sorts of small micromovements and animation nuances help a boring game to make a breathtaking action. And even before that, another video sunk into my soul - The art of screenshake , which is actually not only and not so much about screen shake.
All the time while creating Math4Ami, I kept in mind the concepts from the above videos, as well as the idea that all this additional animation should be as short as possible and act more on the subconscious than on the consciousness. Sometimes, I spent more time adding “juiciness” than adding useful functionality.
Only one place confuses me very much - the final calculation of the money earned (you can see this moment at the end of my video demonstration above). I shortened it as much as I could, but it still takes a little more than 4 seconds (the keyboard disappears, victory appears, counting kopecks, leaving the record table, getting the “New Record” nameplate, displaying the “More” button).
The best “source of juice” for me is the free supplement iTween . I can not even imagine how without it you can do anything in Unity. I use it wherever at least some kind of animation is needed (whether it is the animation of a button or the appearance of a menu item or the animation of a cents count).
I tried to implement something like this on my own, based on Corutinos and Mathf.Lerp or Mathf.MoveTowards, but it was not flexible or universal (and sometimes it worked differently in the editor and on the device). So now I'm not trying to reinvent the wheel, but just enjoying iTween.
There are also pitfalls in this animation system, with which I incorrectly fought at first:
- If during the work of iTween you hide an object (via SetActive (false) , for example) and then show it again, then iTween will continue to run from the interrupted place.
- If during the work of one iTween to launch another (which affects the same values), then at the end of the execution of both, the object may not return to its original position.
- You need to keep track of exactly which GameObject launches iTween, and on which this animation works.
For example (on the last point), object A launches iTween so that it works on object B. To stop iTween animation, you cannot simply launch iTween.Stop () on object A. You need to run iTween.Stop (object B).
The strength of iTween is the possibility of using different types of easing (type of easing). Ising is a parameter that softens the movement (so that it does not start jerk and end stupidly).
The Izing types were a great surprise for me:
Apple and Google statistics are good, but Unity Analytics is better
Also, from the experience of past games, I knew that having my own statistics is very cool. At first I wanted to create some kind of my logging system, but then I remembered Unity Analitics . And what was my surprise when it turned out that the free version of the functionality for my case is not limited. Worse, I would have if I had some kind of monetization, then analytics tools are available only for Pro subscribers.
By simply embedding Analytics.CustomEvent in the right place in the code, I can track which examples are more popular, how many children solve examples in the first days or after a while, etc.
I can compare data from different platforms (iOS and Android) in one place.
And how many things there are interesting that I would like to try, but all hands do not reach. Type Remote Settings (changing the application's content without updating the update) or A / B Testing or Tutorial Manager .
Visual Studio like Sublime
In the past, when I needed to edit some code (be it python, html or node.js), I used Notepad ++ (completely free, but only under Windows) and Sublime Text (paid for all OS, but you can fully try for free ).
In Unity, I was sitting on MonoDevelop, but he zadolbal me so his glitches (such as the inability to switch between layouts or paste something copied outside of Mono) that I decided - it's time to throw the sinking ship and climbed to the Visual Studio Community 2017 (good It is free for singles, like me, developers).
For developers on Unity 2018, this is not relevant now, since the 2018 version includes a multiplatform Visual Studio Code . But I wanted my application to work under iOS 7 (since my daughter’s iPhone was running this iOS), so I had to use any version of Unity older than 2018th.
It helped me with the transition to VS video How to setup Visual Studio with Unity .
From the box, VS does not have all the cool pieces I’m used to in other editors, so I simplified my life for myself:
- turned on the minimap instead of a simple vertical scroll:
- added the SemanticColorizer extension , which allows more flexible customization of the colors of the code. Specifically, it took me to distinguish global variables from local variables by color.
- installed the Match Margin extension , which highlights the word under the carriage and all its copies in the text of the code, and also does it on the minimap. This is very convenient for quickly navigating through the code in order to find all the places where some method or variable is used:
- I use Strip'em to automatically fix line endings.
My scripts for this application are on GitHub. There are only my scripts , not the whole Unity project - sorry if it is impossible to understand them because of this. Until the last moment I did not plan to give a link to the sources, because I do not consider my code to be the one to be oriented. But then he changed his mind because of the chance that more experienced developers could point out my mistakes.
This is the end of the first part. Continued in the second part , where I will tell:
- About writing code
- About version control
- About voice acting
- About icon
- About the build for Android
- About the iOS build
- About the name and promotion
- What regret
- What did you understand
List of links from the body of the article in the order of their mention:
+ Simple C # FTP Class.
+ Session ID for iOS.
+ Write plugin for Unity correctly. Part 1: iOS .
+ Methods for serializing objects in JSON (official help).
+ ScriptableObject (official help).
+ Video Game Architecture with Scriptable Objects ( code ).
+ Master class Overthrowing the MonoBehavior Tyranny in a Glorious Scriptable Object Revolution ( code ).
+ Video demonstration of the work of my application in the editor .
+ Free Anima2D plugin for skeletal animation 2D characters.
+ Free library for application localization - Lean Localization.
+ Video about tricks that improve the perception of the game Juice it or lose it .
+ Video about acting on the subconscious animation techniques The art of screenshake .
+ Free, but powerful animation system iTween .
+ Visual demonstration of ising types (need flash).
+ iTween (official help).
+ Unity Analitics .
+ Text editors Notepad ++ and Sublime Text .
+ Visual Studio Community 2017 and Visual Studio Code.
+ Video Tutorial How to setup Visual Studio with Unity .
+ SemanticColorizer plugin (for code color settings).
+ Match Margin plugin (selects the word under the carriage and all its copies).
+ Strip'em plugin (auto-correcting line endings).