Looking through examples of different gaming applications and interesting solutions, I came across an example of the mechanics of a “typical” runner. Only the principle of motion of the background using the “parallax” effect was considered there, but this idea gave me some thoughts, which I would like to discuss below.
As a tool, I, as before, will use PointJS, because it is visual and simple.
Graphics preparation
For the background, I will use the same image from the example (which, apparently, is taken from another example of another engine):
For the "land", similarly:
As a “character” is a cute dog:
Original idea
As conceived by the author of the original example, the background is initially created as a long ribbon by copying the image onto the back layer for a certain number of times, which is limited by the length of the entire level. The same situation with the "land".
And it works, but it makes the level finite in its length.
In the classic examples of “runners” (typ. Note: “FlappyBird”), the levels are usually infinite and are lost until the player makes a fatal mistake that would lead to the completion of the level.
Principle of operation
My idea is to make the level infinite, but not to create an infinite length of tape for the background and the “ground”.
The idea as a whole is very, very simple: to create several objects that will fill the back space, and “climb out” a little outside the screen to simulate the effect of movement.
For the "earth" everything is exactly the same.
Programming
Since I chose PointJS to work, then the language will be JavaScript.
Prepare a ground for action:
// var pjs = new PointJS('2d', 800, 400); // - 800x400 pjs.system.initFullPage(); // // var game = pjs.game; // var point = pjs.vector.point; //
The dimensions that we set for the scene (800x400) are certainly well suited for the convenience of calculations, but in reality the screens are all very different sizes, and more and less.
After the initFullPage () command has been executed, the size of the scene will change, and we will work with them, but first we need to get them:
// var height = game.getWH().h; // var width = game.getWH().w; //
Great, we have a workspace in which we can work.
First of all, I thought to use an array, but for beginners, most likely, the example with arrays will be dim, so I will use the usual variables:
// var fon1 = game.newImageObject({ // x : 0, y : 0, // ( ) file : 'imgs/fon.jpg', // h : height, // onload : function () { // , fon2.x = fon1.x+fon1.w; // } });
Why do we need onload? Everything is generally clear for those who use JavaScript as the main language, or at least familiar with the asynchronous approach.
After creating the picture, we explicitly indicated its height, and, the new width of the picture after scaling will be available only after the picture is fully loaded. After loading, the variable “w” is written into the object, which we use in the formula: “fon2.x = fon1.x + fon1.w”, where fon2 is the second picture.
In this line, we set the position of the second picture immediately after the first one.
After that create the object itself:
var fon2 = game.newImageObject({ x : 0, y : 0, file : 'imgs/fon.jpg', h : height });
It's all the same here, but without “onload”.
Now create the earth object:
var gr1 = game.newImageObject({ x : 0, y : 0, file : 'imgs/ground.png', w : width, onload : function () { gr2.y = gr1.y = height — gr1.h; // Y gr2.x = gr1.x+gr1.w; // , } }); var gr2 = game.newImageObject({ x : 0, y : 0, file : 'imgs/ground.png', w : width });
Now we will create a dog object that will “run” on the moving earth:
var dog = game.newAnimationObject({ // x : width / 4, y : 0, // X h : 120, w : 150, // «» delay : 4, // ( FPS) animation : pjs.tiles.newAnimation('imgs/run_dog.png', 150, 120, 5) // «dog» });
So, we have created two objects of the background, two objects of the earth and one object of the “doggie”; we can start writing the motion algorithm.
We have several options:
I decided to implement the second one, and it all fits into one function:
var moveBackGround = function (s) { // s — // «» fon1.move(point(-s / 2, 0)); // fon2.move(point(-s / 2, 0)); // gr1.move(point(-s, 0)); // «» gr2.move(point(-s, 0)); // // , « » if (fon1.x + fon1.w < 0) { // fon1.x = fon2.x+fon2.w; // } // if (fon2.x + fon2.w < 0) { fon2.x = fon1.x+fon1.w; // } // if (gr1.x + gr1.w < 0) { gr1.x = gr2.x+gr2.w; } if (gr2.x + gr2.w < 0) { gr2.x = gr1.x+gr1.w; } };
That's the whole algorithm, it remains only to "run" the whole thing, for this we will announce the game cycle:
// game.newLoop('dog_game', function () { game.clear(); // , fon1.draw(); // fon2.draw(); // gr1.draw(); // gr2.draw(); // // «» : dog.y = -dog.h + gr1.y + gr1.h /2.7; // : () // , , 2.7 // dog.draw(); // ! moveBackGround(4); });
After the announcement of the game cycle, just call it to the execution of the plan:
// game.startLoop('dog_game');
Here we must understand that "dog_game" is an arbitrary name of the game cycle, which can be any.
The result was not forced to wait:
Well and, in order to see for yourself, launch in the browser of this example: Run and check
Well, according to tradition ...
var pjs = new PointJS('2d', 400, 400); pjs.system.initFullPage(); var game = pjs.game; var point = pjs.vector.point; var height = game.getWH().h; var width = game.getWH().w; var fon1 = game.newImageObject({ x : 0, y : 0, file : 'imgs/fon.jpg', h : height, onload : function () { fon2.x = fon1.x+fon1.w; } }); var fon2 = game.newImageObject({ x : 0, y : 0, file : 'imgs/fon.jpg', h : height }); var gr1 = game.newImageObject({ x : 0, y : 0, file : 'imgs/ground.png', w : width, onload : function () { gr2.y = gr1.y = height - gr1.h; gr2.x = gr1.x+gr1.w; } }); var gr2 = game.newImageObject({ x : 0, y : 0, file : 'imgs/ground.png', w : width }); var dog = game.newAnimationObject({ x : width / 4, y : 0, h : 120, w : 150, delay : 4, animation : pjs.tiles.newAnimation('imgs/run_dog.png', 150, 120, 5) }); var moveBackGround = function (s) { fon1.move(point(-s / 2, 0)); fon2.move(point(-s / 2, 0)); gr1.move(point(-s, 0)); gr2.move(point(-s, 0)); if (fon1.x + fon1.w < 0) { fon1.x = fon2.x+fon2.w; } if (fon2.x + fon2.w < 0) { fon2.x = fon1.x+fon1.w; } if (gr1.x + gr1.w < 0) { gr1.x = gr2.x+gr2.w; } if (gr2.x + gr2.w < 0) { gr2.x = gr1.x+gr1.w; } }; game.newLoop('game', function () { game.fill('#D9D9D9'); fon1.draw(); fon2.draw(); gr1.draw(); gr2.draw(); dog.y = -dog.h + gr1.y + gr1.h /2.7; dog.draw(); moveBackGround(4); }); game.startLoop('game');
Source: https://habr.com/ru/post/307650/
All Articles