⬆️ ⬇️

Tiny Tron on JS (30 lines of code)

image



Actually, continuing the trend of the week.



The first time I read a tiny excel, I wanted to write something like that - small and cool. Seeing a snake - I realized that it was worth writing a game. After reading the comments - “I want pakman with brand sounds,” I decided that I would write “siteman” on canvas, with web audio api (and waka-waka-waka) and devouring pages.

')

But this was not destined to come true, more - under the cut.



For those who are not interested to read it all: control - arrows, you can run on any page:

jsfiddle , source.

source
siteman = { 'settings':{'size' : 10, 'speed' : 5, 'time':new Date().getTime(), 'width':window.innerWidth, 'height':window.innerHeight}, 'coord':{'x' : 0 + 20, 'y' : 0 + 20}, // plus siteman size 'direction':{ 'current':{'x': 0.75 * Math.PI, 'y' : 1.75 * Math.PI, 'clock' : false, 'calc_x' : function(){ return siteman.coord.x + siteman.settings.speed}, 'calc_y' : function(){return siteman.coord.y}}, 37:{'x': 0.75 * Math.PI, 'y' : 1.75 * Math.PI, 'clock' : true, 'calc_x' : function(){ return siteman.coord.x - siteman.settings.speed}, 'calc_y' : function(){return siteman.coord.y}}, 38:{'x': 1.75 * Math.PI, 'y' : 0.75 * Math.PI, 'clock' : false, 'calc_x' : function(){ return siteman.coord.x}, 'calc_y' : function(){return siteman.coord.y - siteman.settings.speed}}, 39:{'x': 0.75 * Math.PI, 'y' : 1.75 * Math.PI, 'clock' : false, 'calc_x' : function(){ return siteman.coord.x + siteman.settings.speed}, 'calc_y' : function(){return siteman.coord.y}}, 40:{'x': 1.75 * Math.PI, 'y' : 0.75 * Math.PI, 'clock' : true, 'calc_x' : function(){ return siteman.coord.x}, 'calc_y' : function(){return siteman.coord.y + siteman.settings.speed}} }, 'ctx':document.body.appendChild((function(element){element.width=window.innerWidth; element.height=window.innerHeight; element.style.cssText='background:rbga(60, 40, 20, 1); position:absolute; top:0; z-index: 100000000;'; return element;})(document.createElement('canvas'))).getContext("2d"), 'h_ctx':new function(){this.chain = function(method, args){siteman.ctx[method].apply(siteman.ctx, args); return this;}}, 'checkCollision':function(data){return data[0] == 128 && data[1] == 128 && data[2] == 128;} }; siteman.ctx.fillStyle = "rgb(128, 128, 128)"; siteman.h_ctx.chain('beginPath').chain('fillRect', [0, 0, window.innerWidth, window.innerHeight]); (function game_loop(){ siteman.coord = {'x':siteman.direction.current.calc_x(), 'y':siteman.direction.current.calc_y()}; siteman.ctx.fillStyle = "rgb(255, 255, 0)"; siteman.h_ctx.chain('beginPath').chain('arc', [siteman.coord.x, siteman.coord.y, siteman.settings.size, 0.25 * Math.PI, 1.25 * Math.PI, siteman.direction.current.clock]).chain('fill') siteman.h_ctx.chain('beginPath').chain('arc', [siteman.coord.x, siteman.coord.y, siteman.settings.size, siteman.direction.current.x, siteman.direction.current.y, siteman.direction.current.clock]).chain('fill'); var offset_x = siteman.settings.size * (siteman.coord.x != siteman.direction.current.calc_x() ? (siteman.direction.current.clock ? -1 : 1) : 0), offset_y = siteman.settings.size * (siteman.coord.y != siteman.direction.current.calc_y() ? (siteman.direction.current.clock ? 1 : -1) : 0); if(!siteman.checkCollision(siteman.ctx.getImageData(siteman.coord.x + offset_x, siteman.coord.y + offset_y,1,1).data)){ alert('Game over. Your score is: '+(new Date().getTime() - siteman.settings.time)+'. Siteman size: '+siteman.settings.size+' and speed:'+siteman.settings.speed+', map width: '+siteman.settings.width+' and height: '+siteman.settings.height); }else{ setTimeout(function(){game_loop();}, 1000 / 60); //requestAnimationFrame(game_loop); } })(); document.addEventListener('keydown', function (e) {siteman.direction.current=siteman.direction[e.keyCode] || siteman.direction.current}, false); 






How did it all start?



Long before the “marathon of 30 line applications on js” I wanted to write my own mini-game - master new technologies and “join the game-girl.” But, having understood that the resulting code, complete shit smells bad and you need to rewrite everything - like you lost the enthusiasm to continue, although the skills from working with canvass remained. Yes, and I do not leave hopes to continue.



But now here is the occasion to do something small and interesting. The choice fell on pakman who devours the site leaving behind blackness, but at the end it turned out to be a tron ​​training room for one player.



What is it all about?



I spent about 3 days on a small pile of mathematics (for the first time over the construction of a segment of the desired length on the canvas), calculating collisions, drawing shapes of the desired shape, and other techniques of normal and abnormal programming. In short, because most of the code is trivial.



It agree, some lines __ long, for example a line with creation canvas (11). But, consider that this is an abstraction, such as “draw a siteman”, “build a canvas”, “output the result”. It would also be possible to reduce the length of some variables, but we are not playing JS1K, right?



What is this code interesting?



0) Logic:

it is simple - we initialize the siteman, define its properties, draw the canvas on top of the page, press all the keys and “listen” to only certain ones, also display the siteman, compute collisions in the main loop.



1) Chaining implementation:



 //    source 'h_ctx':new function(){this.chain = function(method, args){siteman.ctx[method].apply(siteman.ctx, args); return this;}} /*     this    -   --,  this     window,       .  ,  this      ,   h_ctx     ,        - siteman.ctx[method].apply(siteman.ctx, args)     "  ", .apply -  ,    this   .  ""  */ siteman.h_ctx.chain('beginPath').chain('arc'... /*    */ siteman.ctx.beginPath(); siteman.ctx.arc(... /*   30         */ 




2) Siteman drawing. It consists of two filled perpendicular semi-circles (thanks to this article for the tip, and one of them is “static” (only the order of filling changes clockwise or counterclockwise), the other changes coordinates when the direction of movement changes (up + down - left + to the right), in fact, in this way it turned out to be quite compact to accommodate a drawing of a pacman that looks in all directions.



At first, I wanted to describe the state of movement as normal, then I realized that it takes up too much space, so I wrote down all the necessary formulas directly into the object containing the direction of movement. As a result, we have what we have



 /*  -   -*/ siteman.h_ctx.chain('beginPath') .chain('arc', [siteman.coord.x, siteman.coord.y, siteman.settings.size, 0.25 * Math.PI, 1.25 * Math.PI, siteman.direction.current.clock]) .chain('fill') /*        */ siteman.h_ctx.chain('beginPath') .chain('arc', [siteman.coord.x, siteman.coord.y, siteman.settings.size, siteman.direction.current.x, siteman.direction.current.y, siteman.direction.current.clock]) .chain('fill'); 




3) the calculation of collisions:



 /* .  ""   -   ""    ,  ,  siteman              ,     "" ,                  */ var offset_x = siteman.settings.size * (siteman.coord.x != siteman.direction.current.calc_x() ? (siteman.direction.current.clock ? -1 : 1) : 0), offset_y = siteman.settings.size * (siteman.coord.y != siteman.direction.current.calc_y() ? (siteman.direction.current.clock ? 1 : -1) : 0); /*   ,          128( ,   ).       ,    -.      ,     */ if(!siteman.checkCollision(siteman.ctx.getImageData(siteman.coord.x + offset_x, siteman.coord.y + offset_y,1,1).data)) 


How does it work and why exactly such formulas? If the x or y coordinate changes, it means that you need to take into account the offset along this coordinate (by the way, you can also add composite movements), otherwise you should not take it into account. By filling the circle (clockwise or counterclockwise) we understand the direction of motion, and either subtract or add an offset by size.

In the end, we add what we got to the coordinates, because we are interested in what will be in front of us (we take away - after).

The formulas for calculating the coordinates are very similar to those that I originally used to construct the loop.



4) Well, a little by creating canvas, but this is already a perversion, so I will not explain it, never a good practice.



The code works from both requestAnimationFrame and setTimeout. Why is setTimeout defaulted? Because on linux I couldn’t run the “polyfill” in one line, so it’s more stable.



What is this bad code?



1) You can not clone siteman'a. If it were possible - 100% could be stuffed with a small siteman factory and there would be at least some opponents. Now I see it, but I do not want to rewrite everything for the third time.

2) It's a bit boring to just chase a blank page. It turned out such a quickly fattening snake than tron.

3) Some lines are too large (in all senses) abstraction.

4) Nevertheless, it would be more correct to count through the delta from the past time and only then move the siteman when he “can”.

5) Well, the picture on the turns looks a bit strange, but here you’ll have to draw not a pacman but a whole circle, or even more distort with the coordinates.

6) Yes, you can eat yourself. if you press the opposite key. Because the direction of movement is nowhere explicitly indicated - it would have been a hard line with bindings of keys, but this is quite realistic.

7) Not enough reset'a



Yes, and for a long time I tried to force the normal loop to be displayed, but if you take into account the direction (and not to make a “simple” loop that will always be over-the-pack), you get too much code and formulas. The same with audio, at least you need to spend 3 lines on it:

 siteman.audio = new Audio(data:wav/base64); siteman.audio.loop=true; siteman.audio.play(); 


but not the fact that they will immediately earn money in most cases.



So, what is next?



And nothing. The goal was set to make the game on 30 lines, on js, with the help of canvas - it is completed. The code did not turn out to be the UG, but not the best either. But I got a lot of exp, and still made some kind of game.



True, I would still continue to do better, especially I would like to add a simple AI that will bounce off the walls and move randomly, but for this you would need to seriously rewrite the code, and not the fact that it would fit in 30 lines more or less readable code. Well, or still would have finished the idea with a site eater. But it is a pity time, only 5 hours were spent on this project and did not want to spend another couple of evenings on it.



PS I read the sarcastic article "js-code is just one line," and IMHO - the point is not to do exactly N lines (most languages ​​allow themselves to be compressed into just 1 line), but the point is to write something funny and small.

Source: https://habr.com/ru/post/202874/



All Articles