3D games on Instagram Javascript, or sausage flight path



After the first article on programming games in masks on Instagram, a customer asked me to create a game on Instagram for his pizzeria. This game was planned to be used for business promotion purposes. Of course, I understand that, judging by the number of views of the first article, the Instagram theme is not particularly interesting to the Habr community. Apparently, this service is still considered some kind of frivolous entertainment for blondes, and an article about it is only suitable as a Friday reading for an evening cocktail. However, today, just Friday. Get a cocktail. And don't forget to invite blondes. However, it is likely that in the future technology will reach such heights that we will begin to play on Instagram in GTA-5. Or 10.

This article will discuss two games at once. I made one to order, and then the second for myself. With each new game, I was faced with new tasks, the search for solutions to which made me get acquainted with the new nuances of development. Perhaps this story will be useful to other developers of elements of augmented reality. The games are written in pure Javascript without the use of any additional libraries. To display 3D graphics, the built-in Instagram tools are used.

Game 1. Pizza


So, what is the essence of the first game. Spinning pizza. Components fly off from its surface - pieces of sausage, tomatoes and so on. Two players must catch their mouth. The winner is the one who, as you might guess, will catch more. Over time, the rotation speed increases. The funniest thing in this game for me was programming the flight paths of these edible elements. Imagine programming a sausage flight. No, definitely, I've never had so much fun programming. I laugh even now, writing this article.

When the customer saw the working prototype, he sent me this: “:))”. There was a problem while testing the final result. It consisted in the fact that the subjects could not play the game: they were simply bursting with laughter - it was so much fun.

Let me remind you, the development of masks and games is made in Spark AR Studio. And then from there you can upload your work on Instagram for everyone to see. It is noteworthy that web technologies blur the boundaries between operating systems for a developer. You write one code, which then works in the Instagram application for both iOS and Android, without any modifications or changes. Of course, the payment for this becomes a fundamentally low script speed.

The schedule for the game was provided by the customer. There were some difficulties with 3D models. In particular, when using the built-in animation engine Spark AR Studio, it turned out that if the moving object is scaled, then its coordinates are incorrectly determined in the game. But this annoying effect is not observed if you do not scale the entire object completely, but each of its mesh separately. I had to leave the scale of pizza 1: 1, and to prescribe a certain coefficient for each element making it. There were also problems with the FBX format into which you need to export models for an Instagram game. The graphic designer sent models with built-in textures, which, moreover, were placed along a relative path. The 3D editor saw them, but Spark AR didn’t. I had to repack the models so that the texture files lay separately from the models and along one path with them.

And another small problem that I encountered was that the api Saprk AR object responsible for displaying the text on the screen refused to accept the value as a value - a game score, for example. For a long time I could not understand why nothing is displayed. What am I doing wrong? It turned out that you first need to convert the numbers to strings (.toString ()). This goes without saying for other languages, but rather atypical for javascript, which has always done this itself.

Simple animation


One of the new things for me in this game was programming the animation of 3D objects. (In my previous Instagram game “Tic-Tac-Toe,” there was no animation at all.) The animation engine in Spark AR Studio is very specific. It takes some input parameters, and then reactively connects the variable with the object in which you want to change something. For example, this will change the y coordinate (startValue, endValue - its initial and final values) of some 3D object over time t:

var driverParameters = {
    durationMilliseconds: t,
    loopCount: Infinity,
    mirror: false
};
var driver = Animation.timeDriver(driverParameters);
var sampler = Animation.samplers.linear(startValue, endValue);
sceneObject.transform.y = Animation.animate(driver, sampler);
driver.start();

To move the pizza ingredients flying off in space, I decided to simply run three such animations in parallel for each coordinate. It was enough to indicate the initial coordinate (startValue) and the final coordinate calculated by the angle of rotation of the pizza (endValue), so that it was somewhere far away in case the player did not catch this “shell” with his mouth. If caught - then the movement stops. The mouth opening event I already described in a previous article. Only here is a game for two and, accordingly, there will already be two faces and two mouths:

FaceTracking.face(0).mouth.openness.monitor().subscribe(function(event) {
    if (event.newValue > 0.2) {
        ...
    };
});

FaceTracking.face(1).mouth.openness.monitor().subscribe(function(event) {
    if (event.newValue > 0.2) {
        ...
    };
});

The key point in this game is catching flying ingredients with your mouth, in other words, finding a flying object in a given small area around the center of a person’s mouth. At first, this calculation did not want to be done correctly for me, but a solution to the problem was found after the introduction of a hidden object tied to the coordinates of the mouth, and it worked. For some reason, the direct coordinates of the mouth did not return. Here cameraTransform.applyTo is the reduction of the coordinates of face points to the coordinates in the 3D world (the method is taken from the documentation).

move: function() {
    //   (   )

    //    
    var object = Scene.root.find('pizzafly');
    var olast = {
        x: object.transform.x.pinLastValue(),
        y: object.transform.y.pinLastValue(),
        z: object.transform.z.pinLastValue()
    };

    //   ,       
    var objectHidden = Scene.root.find('nullObject');
    objectHidden.transform.x = FaceTracking.face(face).cameraTransform.applyTo(FaceTracking.face(face).mouth.center).x;
    objectHidden.transform.y = FaceTracking.face(face).cameraTransform.applyTo(FaceTracking.face(face).mouth.center).y;
    objectHidden.transform.z = FaceTracking.face(face).cameraTransform.applyTo(FaceTracking.face(face).mouth.center).z;

    //   
    var mouth = {
        x: objectHidden.transform.x.pinLastValue(),
        y: objectHidden.transform.y.pinLastValue(),
        z: objectHidden.transform.z.pinLastValue()
    };

    //    
    var d = {
        x: Math.abs(olast.x - mouth.x),
        y: Math.abs(olast.y - mouth.y),
        z: Math.abs(olast.z - mouth.z)
    };

    //  
    if ((d.x > 0.03) || (d.y > 0.03) || (d.z > 0.03)) {
        // 
        ...
    } else {
        // 
        ...
    };

},

Subsequently, I realized that you should remove the depth check (z coordinate), since in this particular game it is visually difficult to estimate the depth. That is, now it became possible to catch the ingredient by opening your mouth at any time during the flight. The main thing is the combination of x and y.


Finally, the last problem I encountered while writing this game was to limit the size of the final build to 4 MB. Moreover, according to the recommendation of Facebook, in order for the game to be displayed on as many mobile devices as possible, it is desirable to fit even 2 MB. And 3D-modelers are creative people and want to make heavy models with a dense grid and huge textures, completely not caring about us, programmers, or rather, about the final performance in games. I also somehow reduced the textures in size and compressed in jpg (instead of png), but the pizza model itself had to be sent for revision (retopology). As a result, nevertheless, it was possible to fit into the 2MB volume with all models, textures and a script. And the game went on moderation.

And after some time she returned with the wording that "the effect contains too much static text." I had to remove the countdown numbers and instead of them set the animation to the arrow on the stopwatch, which now began to count the time instead of them. After that, the game was approved.

Game 2. About the butterfly (ButterFlap)



Now I’ll talk about creating a second game. I can not help making games, this is my hobby. A few days before March 8, I decided to create an Instagram game for this holiday. Something about flowers, butterflies and sweets. But I couldn’t think of what the essence of the game could be. The thought revolved in my head. Maybe the butterfly will collect candies and put them in a basket? Or maybe butterflies will pick up sweets in the air and throw them, and the player will need to catch them with his mouth? In general, I scammed for a couple of days and, having not found a solution, I realized that by March 8, my game would still not have time to go through moderation, since the latter takes 3-4 days. I already wanted to leave this venture, when suddenly the idea came by itself, all of a sudden. I no longer tied the game to Women's Day, now it was just a game.

Scroller. From left to right, landscape and various obstacles move. The player must control a butterfly that can move freely within the screen and collect diamonds hanging in the air. Also, from right to left a little faster than the rest of the picture, clouds are moving, from which rain is pouring. You need to hide from the rain under the flowers. If a butterfly falls under water, then its wings get wet and it falls, this is the end of the game. I called the game simply: ButterFlap.

Yes, all this sounds good. I, directly, wanted to play myself. But I remembered that I was not an artist at all. However, I am able to make 3D models, and this is easier than drawing. So, it was decided, let there be a 3D scroller.

Graphic arts


I found online royalty-free models that I had to refine. He pulled textures onto those that were without textures, having previously placed the latter in a texture atlas: artifacts in the form of “ladders” are visible on non-textured models, and smoothing can be set for textures in the game, which makes the appearance of objects more presentable and not so flat. Those models that contained textures also had their flaws that had to be fixed. Firstly, the size of the textures was not a multiple of two in power. That is, for example, it could be equal to 1200x1200. I had to compress to 1024x1024. Video processors can either scale or fill in inappropriate textures with empty space, that is, those that are not 1024x1024, 512x512, 256x256, etc. In any case, such actions are an extra load during the game’s work and meaningless memory consumption,therefore, it is better to prepare the correct images first manually. The situation was also saved by the fact that I distributed all the textures by texture atlases, so if, for example, the original texture was 400x200 pixels in size, then I could just put it in the atlas 1024x1024, as it is, next to other similar ones. After this, it is naturally necessary to scale the UV scan as well, but this is done in a couple of seconds. Still came across variants of models where for some reason the textures were tied along an absolute path such as “C: \ Work \ Vasya \ Map.jpg”. Well, there is no such folder on my computer! I had to specify the paths to the textures manually ... But why did you get the idea that I should keep all my projects on the “C:” drive !? Oh, these modelers, free artists ... By the way, this way, by the names of folders in randomly left paths, you can inadvertently learn more about the identity of the modeler,for example, his name. Nice to meet you!

The most difficult thing was to find a suitable butterfly model with animation. Spark AR uses animation that can be exported from any 3D editor to the FBX format. The wings of a pair of butterfly models I downloaded were somehow strangely mirrored - one wing was modeled, and the second was reflected in some way I did not understand, and, thus, two of them turned out. With this approach to modeling, in the end, in the game, one of the wings (copied) did not want to receive light from the source and always remained dim. I did not risk drastically changing the model, because then the animation would have flown. And in animation I’m an even bigger noob than in 3D modeling. Perhaps the problem was something else: I tried, for example, to expand the normals, but that did not help. In short, free 3D models are a pain. As a result, having washed all evening,by trial and error, I found a suitable model, which after some fine-tuning with a file began to look satisfactory. Something I didn’t like about it, but it was little things: I redid the texture, changed the geometry in some places, and adjusted the material parameters. Finally, the butterfly took off. Hooray. But now I am exhausted. So I decided to go to bed and continue the next day. Yes, I spent 7-8 days on creating the game. But it was such a rather leisurely work in the evenings, reading documentation, articles and finding answers to questions.So I decided to go to bed and continue the next day. Yes, I spent 7-8 days on creating the game. But it was such a rather leisurely work in the evenings, reading documentation, articles and finding answers to questions.So I decided to go to bed and continue the next day. Yes, I spent 7-8 days on creating the game. But it was such a rather leisurely work in the evenings, reading documentation, articles and finding answers to questions.


All evening the next day I worked on graphics. The specificity of game masks for Instagram, as I have already mentioned, is that it is advisable not to go beyond the 2MB volume for the entire game (maximum 4 MB). I was not worried about the script: its size is unlikely to exceed 50 Kb. But over 3D models of plants had to be pretty conjured. For example, that part of the sunflower where the seeds are located was made by geometry. Hundreds of polygons ... Replace it with a fragment of a sphere of a couple of dozen triangles and stretch the downloaded texture, in fact, of this part. The number of leaves can also be reduced. We make grass at the bottom of the scene with a plane of two triangles with an overlaid texture with an alpha channel. We reach the volume with shadows and copying fragments of the picture inside the texture itself.

In general, it is desirable to minimize the number of textures, as well as their size in bytes. It is the textures that create the bulk of the game. I'm used to textures where transparency is needed, placed in one texture atlas - in png 32 bit, and textures that do not use the alpha channel, pack in another atlas, saving it in jpg. Jpg can be shrunk stronger than png. Grass and UI elements that need transparency have gone to the png atlas, and everything else to jpg.

The total volume. In total, I got 4 types of plants, a rock, a butterfly, which the player will control, and a cloud. The textures of all these models in compressed form took 2 atlases 1024x1024 jpg and png with a total volume of 500 Kb. The models themselves took about 200 Kb. Plus a script. Sounds - 31 Kb. Total: about 1 Mb. In green, the size of the build is just displayed (this is what should fit in 2 MB).


Suddenly I ran into a completely unexpected problem. I deleted several unused models, but not through Spark AR Studio, but from the file system. Subsequently, when assembling the build, I found that they disappeared from the Spark AR Studio scene, but still got into the assembly. And to clear the project from unused resources is impossible. Links to them from the “Asset Summary” do not lead anywhere. Apparently, this is a defect in Spark AR Studio. I had to recreate the project again, adding all the necessary resources there from the very beginning.

Scene


I have been thinking for a long time about how to implement scrolling of all objects on the screen. Of the tools for this, there is only javascript and the simplest built-in animation engine in Spark AR Studio, which can simply change the coordinates of the object from the initial value to the final in a given time.

Yes, the dilemma still arose before this: whether to create the entire level of the game, a scene in a 3D editor, by duplicating the repeating plants the required number of times and placing them in the right positions, in order to scroll the entire scene in the game, or load only one copy of each 3D object and substitute them in the direction of the player just outside the screen. The answer was obvious. Our choice is the second option. Otherwise, it’s definitely not possible to fit into 2 MB: most likely, the scene according to the first option will be heavy. But then you need a layout of objects. And I, without hesitation, decided to use the 3D editor as a level editor. Yeah, it turns out that I did both. Plants for the game, I saved each in one copy. And from the editor I needed only the coordinates. After completing the work,I wrote out the coordinates of all the objects and made an array with the data for the game.

lv:[
    {n:'flower1', x:8.0, y:-6.5},
    {n:'cloud', x:10.0, y:0.1},
    {n:'rain', x:10.0, y:6.6},
    {n:'flower1', x:14, y:-2.5},
    {n:'diamond_red', x:20, y:2.0},
	...
],

And yet - a separate associative array, according to the types of objects where the sizes of colliders and their displacements relative to the centers of 3D objects are stored, as well as a flag (col) that determines whether the object is an obstacle (otherwise the player passes through it). For some types of objects, the (destroy) flag is set, which determines whether the object should be hidden after interaction, as well as the (v) parameter, which determines some degree of interaction, for example, the number of points a player gains or loses.

colld:{
    'flower1': {dx:0, dy:5, w:2.3, h:1.4, col:true},
    'diamond_green': {dx:0, dy:0, w:1, h:1, col:false, v:1, destroy:true},
    'diamond_red':{dx:0, dy:0, w:1, h:1, col:false, v:-2},

    ...
},

A little nuance. The grass at the bottom of the scene should scroll continuously. So, you have to use two copies of this object and substitute them one after another as you move. Flowers will appear on one screen no more than in one copy each.

Bearing in mind the scale problem that I encountered when developing the first game, I reset the scale of all models in the 3D editor. Thus, in Saprk AR models immediately loaded in normal sizes. True, in the script anyway, it could not do without the "magic number", the global coefficient, the universal code, which contains the essence of the universe. A virtual universe, of course. And I am ready to reveal this number to you. Use it, people! I do not mind! This number is 0.023423. In short, despite the reset of all scales, one meter in the 3D editor turned out to be equal to this very number in Spark AR Studio. Most likely, I, nevertheless, do not fully understand all the intricacies of working with 3D graphics, hence the coefficient. The coordinates (but not the size) of all objects that were exported from the editor are multiplied by it, you guessed it.Where to adjust the scale of the scene in Spark AR, I did not find.

The next problem I encountered was the loss of the sort order of objects when exporting from a 3D editor. Complex objects consisting of several meshes could unpredictably appear on the scene in the game in such a way that, for example, a mesh that was behind another suddenly jumped forward. And, if you look at the order of objects after exporting to Spark AR Studio, then it really shows that this mesh is for some reason higher in the list, although it was lower in the editor. I solved this problem by dividing the scene into layers and saving them to different files.

Complex animation


For another three evenings, I fiddled with programming. If I used the standard Spark AR Studio engine to animate the wings of a butterfly, the task of moving the background was not so simple. I still did not understand how to attach not just one variable parameter to iterations of the motion cycle, but a full-fledged callback function. In any case, I could not do it, the current animation parameters did not want to be transferred there. And such a function is simply necessary, because if in the first game (with pizza) I checked the collision by the event of opening the mouth by subscribing to it, then there was no such event here. And it was just necessary to check collisions with environmental objects as the character moves, according to its current coordinates. And for this, the coordinates must be compared at each iteration. And then I thought. After all, I already wrote games in javascript.And why not use your animation engine that I wrote earlier for those games? Its principle of operation is approximately the same: in a given time, the parameter (or parameters) changes between the given initial and final values. And the current value is passed as a parameter - you won’t believe it - in the given callback function, in which, for example, you can set a 3D object on the scene at coordinates equal to these current values, or check for collisions on them. Thanks, Cap. I had to slightly adapt my engine to the local “ecosystem”: remove references to the window object from there, since it is not here, and other little things.And the current value is passed as a parameter - you won’t believe it - in the given callback function, in which, for example, you can set a 3D object on the scene at coordinates equal to these current values, or check for collisions on them. Thanks, Cap. I had to slightly adapt my engine to the local “ecosystem”: remove references to the window object from there, since it is not here, and other little things.And the current value is passed as a parameter - you won’t believe it - in the given callback function, in which, for example, you can set a 3D object on the scene at coordinates equal to these current values, or check for collisions on them. Thanks, Cap. I had to slightly adapt my engine to the local “ecosystem”: remove references to the window object from there, since it is not here, and other little things.

Yes, yet - about scrolling the landscape and objects of the environment. I decided to put the whole world in one NullObject, that is, in an empty 3D object, a container, and move it using only one parameter for animation - its x coordinate. Inside the container, all models have the same coordinates as if they were outside, only now for them the reference system is tied to this empty object. Rocks and flowers will be repeated (moreover, at different heights from the ground, in accordance with the level diagram), so you can reuse these objects as you move, setting them the desired horizontal and vertical position inside the “container”. I wrote a search system for objects falling into the frame (at the current container offset), which sets the object that leaves the frame to a new position further if it should appear there. You can see,how it works on the example of three objects. (There will be more of them in the game, so there you will no longer see such an effect of "rearrangement" of environment objects.)



The function for updating the coordinates of objects looks like this:

oCoordSet: function(v) {
    //  :    
    //   
    var camx = -ap.oWorld.transform.x.pinLastValue();
    //  
    var x1 = camx - ap.game.scro.w2;
    var x2 = camx + ap.game.scro.w2;
    //   
    for (var i = 0; i < ap.d.lv.length; i++) {
        //    
        if ((ap.d.lv[i].x >= x1) & (ap.d.lv[i].x <= x2)) {
            //   
            ap.d.lv[i].o = Scene.root.find(ap.d.lv[i].n);
            //  -  
            ap.d.lv[i].o.transform.y = ap.d.lv[i].y;
            if ((ap.d.lv[i].n == 'cloud') || (ap.d.lv[i].n == 'rain')) {
                //    ,
                //  
                //   2.3,
                //     
                ap.d.lv[i].o.transform.x = ap.d.lv[i].x - (x2 - ap.d.lv[i].x) * 2.3 + 0.2;
            } else {
                //    ,
                //      
                ap.d.lv[i].o.transform.x = ap.d.lv[i].x;
            };
        };
    };
    //        
    //     
    if (camx > ap.game.grassPosLast) {
        ap.game.grassPosLast += ap.game.grassd;
        ap.game.grassi = 1 - ap.game.grassi;
        ap[ap.game.grassNm[ap.game.grassi]].transform.x = ap.game.grassPosLast;
    };
},

The main character


The main character in the game is an unsinkable butterfly, which boldly flies forward, overcoming obstacles. I decided to do the management by setting a marker, or cursor (light point), by turning and tilting my head. And in the direction of this point a butterfly will fly slowly (in fact, it is not a fighter, nor can it teleport). For example, if you subscribe to the head tilt event, then you can implement vertical control like this:

FaceTracking.face(0).cameraTransform.rotationX.monitor().subscribe(function(event) {
    var v = event.newValue;
    //  
    var scrH2 = ap.game.scr.h2;
    //
    var p = -v * 0.5;
    if (p < -scrH2) {
        p = -scrH2;
    } else if (p > scrH2) {
        p = scrH2;
    };
    //  
    var d = 0.006;
    //  
    var cur = ap.oPers.transform.y.pinLastValue();
    if (p < cur) {
        cur -= d;
        if (cur < p) {cur = p;};
    } else {
        cur += d;
        if (cur > p) {cur = p;};
    };
    //    ()
    ap.oPointer1.transform.y = p;
    //   ,
    // ,    
    ap.game.pers.dy + = cur - ap.game.pers.y;
});

Similarly for the horizontal. Only there it is necessary to subscribe to the event not of a tilt, but of a head rotation (rotationY), and instead of the height of the screen, consider its width.

Colliders


All this is wonderful, the world is moving, and the game character can freely move around the screen. But now you need a collision handler, otherwise no game will work. There are three events in the game, according to which the character's position can change. This is the rotation and tilt of the head, as well as the movement of the world, in which the horizontal coordinate of the player (x) automatically increases.

Since I don’t know how iterations of the face handler work in Spark AR - whether they are called with a certain frequency or are set to the maximum possible clock frequency, and in my animation engine I can control this parameter, I decided that I would define collisions in my function the movement of the world, which is called at a frequency set by me (60 frames per second). In face processing events, we will only “accumulate” movement.

The principle will be like that. In head tilt and head turning events, an offset along the x and y axes is accumulated. Further, in the function of scrolling the world, a horizontal displacement is also added to the “piggy bank”. And then it is checked, if the accumulated displacement is added to the original coordinates, then there will be a collision with any of the objects in the world. If not, then the player’s original coordinates plus the offset are made by the current coordinates. And then we update the source ones to the current ones (reset) and reset the offsets. If so, then roll back the coordinates to the original. Moreover, we need to determine which axis the collision would be, since both coordinates cannot be rolled back. Otherwise, the player simply “sticks” to one point in space and can no longer move anywhere. It is necessary to give him freedom of movement along the axis along which the collision does not occur.So you can give him a chance to fly around an obstacle.

setPersPos: function(camx, camdx) {
    //   
    //       (camx,camdx)

    //     
    var persx = ap.game.pers.x;
    var persy = ap.game.pers.y;
    var dx = ap.game.pers.dx;
    var dy = ap.game.pers.dy;

    // ,     
    var col = ap.collisionDetect(
        {x: persx, y: persy, dx: dx, dy: dy},
        {x: camx, dx: camdx, y: 0}
    );

    if (col.f == true) {
        // 

        if (col.colx == true) {
            //   
            //    ,
            //   
            //(    ,    )
            ap.game.pers.x = col.x;
        } else {
            //    ,
            //   
            ap.game.pers.x = persx + dx;
        };

        if (col.coly == true) {
            // -  
            ap.game.pers.y = col.y;
        } else {
            ap.game.pers.y = persy + dy;
        };

    } else {
        //  ,   
        ap.game.pers.x = persx + dx;
        ap.game.pers.y = persy + dy;
    };

    // 
    ap.game.pers.dx = 0;
    ap.game.pers.dy = 0;

    //     
    ap.oPers.transform.x = ap.game.pers.x;
    ap.oPers.transform.y = ap.game.pers.y;
},

The collision detection function itself:

collisionDetect: function(opers, ow) {
    // , opers -   , ow -  

    var res = {f: false, colx: false, coly: false, x: 0, y: 0};

    var ocoll, x, y, w, h, persx, persy, persx0, persy0, od, colx1, coly1, colx2, coly2;
    var collw = false, collh = false;

    //  
    //(  ,  "" )
    persx0 = opers.x + ow.x - ow.dx;
    persy0 = opers.y + ow.y;

    //       
    persx = persx0 + opers.dx;
    persy = persy0 + opers.dy;

    //  
    for (var i = 0; i < ap.d.lv.length; i++) {
        od = ap.d.lv[i]; //obj data

        //    (   ),
        //     
        //        
        if (typeof ap.d.colld[od.n] !== "undefined") {

            //       
            ocoll = ap.d.colld[od.n];
            colx1 = od.x + ocoll.x1;
            colx2 = od.x + ocoll.x2;
            coly1 = od.y + ocoll.y1;
            coly2 = od.y + ocoll.y2;

            if ((persx < colx1) || (persx > colx2) || (persy < coly1) || (persy > coly2)) {} else {
                //   
                res.f = true;

                //        ,
                //,    
                if ((persx0 < colx1) || (persx0 > colx2)) {
                    collw = true;
                };
                //        ,
                //,    
                if ((persy0 < coly1) || (persy0 > coly2)) {
                    collh = true;
                };

            };
        };

    };

    //   

    //  ,     ,
    //  
    if (collw == true) {
        res.colx = true;
        res.x = persx0 - ow.x;
    } else {
        res.x = opers.x;
    };

    // -  
    if (collh == true) {
        res.coly = true;
        res.y = persy0 + ow.y;
    } else {
        res.y = opers.y;
    };

    return res;
},

Rain


I used the standard particle engine Spark AR Studio to animate rain. There is nothing special here. We add an Emtter object to the scene, not forgetting to ask it Emitter -> Space -> Local (instead of World). This is to ensure that the drops do not dynamically lag behind the clouds during movement, but always fall directly. This method is more convenient for easier determination of the moment the butterfly falls in the rain - no need to make a correction for height. For the drops themselves, I prepared the appropriate texture. Well and, of course, the rain object will move together with the cloud object. Then I added a cloud hit condition to the collision handling code. And, if there is no obstacle above the player at this moment, then the butterfly falls and the game ends.

Moderation


For successful moderation, the mandatory video from the game should not contain static text that is not tied to objects. Otherwise, the robot instantly stops moderation. However, after that, a person checks the game for several days. And he did not like the abundance of text before the game, as well as at the end. I had to reduce the amount of text. After that, the game was approved.

Summary


Not that my game was particularly exciting. She probably lacks dynamics and additional mechanics. I will release an update. But I realized that making games on Instagram is interesting. This is a kind of challenge. I really enjoyed the process of programming and solving all kinds of tasks under minimalism in terms of tools and the amount of allocated memory. I remember someone said that 640 Kb is enough for everyone. Now try to fit a 3D game of 2 MB. I, perhaps, will not argue that they are enough for everyone ... But try it!


In conclusion, I would like to put together in one list all the unobvious moments that I encountered. Maybe this is useful to someone as a cheat sheet when creating games for Instagram.

  • Scale. Adjust the scale of all 3D models in the 3D editor so that he is immediately 1: 1 on the stage in the game.
  • . . , .
  • . FBX, . -. , .
  • . JPG , , , -, JPG, PNG.
  • . , , . Spark AR , . , , , 3D-.
  • 3D , : , . . .
  • , , . javascript .
  • In the context of Spark AR, there is no window object and all kinds of browser-specific objects like webkitRequestAnimationFrame, mozRequestAnimationFrame, and so on. This is a fate when programming in javascript animation.

All Articles