Racing on SVG

    image

    Introduction


    Arcade js game. The prototype was the so-called "Racing in the cage" (in some children's magazine saw). The point is that on the notebook sheet a line is drawn and the players walk around the cells. In one move, you can increase the speed by one or decrease. If you "crash" into a wall, then continue from this place at a unit speed.

    image

    Game code The game was

    made using raphael.js and playground.js, with rendering graphics in SVG. On my average laptop (CPU 4 x 2.1 GHz) under chromium, the game produces 50 fps for small tracks and 40fps for large ones.

    Up to 5 riders (or racers) participate on one track. Racers of opponents, repeat the actions of previous players who have the best results on this track. In order for the riser tracer to register in the database, it is enough not to be the last to finish. In the menu, you can choose the color of the riser and the name under which this result can be written (to display the results when finishing, and for the best - in the main menu).

    Physics is implemented on a two-dimensional array. Riser rides (flies?) In moves. Each turn the player can change the speed (3 units more or 10 units less) and also, direction (left or right). Based on this data and the current position, the game looks at where the current move will end and they will check the array for the presence of walls or finish. Depending on what is at this endpoint, the implementation of the corresponding algorithms follows. Creating tracers is also implemented on this array (indexes of arrays are stored in the database, but not the actual sizes of offsets).

    The widths of the tracks vary from 500 units to 2000. There is no actual size limit, but the more, the lower the fps the browser gives out.

    For graphics, SVG is used with 11 children. 5 (path) - for drawing risers, 4 (path) - for drawing tracks, 1 (circle) - for animating collisions with walls and 1 (path) - for exhaust lights.

    image

    The database is represented by a single json file with tracers. Three php scripts serve it. Maps are stored in the js variable. It would be possible to create json for cards, but they are static and they have a small size.

    In the arcade there are sounds of collisions, sounds of risers (both that under the control of the player, and strangers, when approaching them), as well as there is the sound of whistling walls. I really liked this effect in the NFS series, and here I tried to repeat it. All sounds downloaded from opengameart.org. The background is played by Let the Games Begin (Section 31 - Tech), or alien swamp (unfortunately, the author is unknown).

    Trace drawing


    image

    First I read an array of objects.

    {i: size_i, j: size_j, size: indent_in_cell}
    

    and I get a chain of segments, each element of which is connected to the next, and the final to the first. And so I get a closed set of segments.

    From this set of segments, using trigonometry functions, using indent_in_cell I get a set of parallelepipeds (there should have been rectangles, but they didn’t work)

    image

    Next, using all the same trigonometry functions, I determine whether the array element is located (which I created using the radius parameter, and initially it is filled with units) in any of the rectangles. If yes, then this element of the array is set to 0.

    At the very end, I draw all the rectangles with a single path element.

    For the finish, I indicate the offset in width i. At this offset, the algorithm searches for the first segment of zero elements and takes the end and start points from this segment, and then it is drawn using the path element.

    Values ​​for "boxes" are taken from an array of elements of type

    {i: size_i, j: size_j, width: width_in_cels}
    

    i and j are indicated for the upper left corner, and width is the total size for the square. Then, based on these parameters, the main array is changed. The elements that fall into the "boxes" are assigned the values ​​3. Next, through the array, I outline all the boxes. They are drawn, of course, all with a single path element.

    Create and read a tracer.


    The tracer is an array of objects of the type:

    {
      s: racer.speed,
      r: racer.root,
      i: racer.coord.i,
      j: racer.coord.j,
      wait: 0,
    }
    

    where s is the riser speed, root is the direction, i, j are the coordinates at which the riser should be placed in the current turn, wait is the waiting time. In the last element of the array, I add tracer information:

        time: this.timer,
        human_time: racer.human_time,
        start_i: racer.start.i,
        start_j: racer.start.j,   
        road: settings.road,
        name: racer.name,
        color: racer.fill,
    

    where, time is the total number of frames for which this riser reached the finish line, human_time is the time in hh format: ss, start_i, start_j are the coordinates for the starting position, road is the index of the route on which this tracer was received, name is the name tracer, color - the color of the riser.

    At each player’s turn, an object is recorded into an array with current parameters. Coordinates, to start the course, direction and speed. If the speed is zero, then the wait parameter increments. Once per frame, 1 is added to this parameter, and so on, until the speed becomes
    greater than zero.

    At the start of the riser, a move object is created and the wait increment begins (since the speed is zero at first) Next, at each move, when the frame counter is zero, either the move object is written to the array or the wait is incremented.

    When reading this array, the wait value first looks. If this parameter is greater than zero, then the render of this riser is turned off and its own counter begins to incriminate. When your own counter is equal to the wait value, then the next move object looks further. Tracer objects, where wait is not equal to zero, are processed by the same functions as the movements of the riser under the control of the player. That is, the speed and direction and the so-called target of the move (the end point for this move) are set out of the object, and a standard set of functions is launched to calculate the render parameters and draw the course of the riser.

    Conclusion


    As a result, I got a pretty nimble arcade game with elements of network interaction between players. I did not check cross-browser compatibility ... more precisely, firefox gave out terrible fps when changing the scale, and I do not know what this is connected with. If someone does not work, then chromium or, in extreme cases, chrom.

    At first, I thought about something leisurely and puzzling, that is, it was necessary to count the cells and find points from which it would be possible not to crash into the wall the next move. And there were cells. But after I realized that at 50 fps you can outline the racers with great speed and smoothness, I redid everything and instead of playing a logical game along the turns, I got an arcade.

    The game has approximately reached 3000 lines of code and 4 megabytes.

    UP 1: Someone knows why chromium works fine under linux (50fps), then it only gives 20. And why does firefox dislike viewBox and especially scaling? All render runs on window.requestAnimationFrame ()

    Also popular now: