å
容
ç§ãã¡ã®äººçã¯ã²ãŒã ã§ãã
Iyen Banksããã¬ã€ã€ãŒåããŠãã³ã³ãã¥ãŒã¿ãŒã²ãŒã ãéããŠãã»ãšãã©ã®åäŸãã¡ãšåæ§ã«ã³ã³ãã¥ãŒã¿ãŒã«èå³ãæã€ããã«ãªããŸããã å¶åŸ¡å¯èœãªã·ãã¥ã¬ãŒã·ã§ã³äžçã®äžçã«åŒã蟌ãŸããç©èªãèªãããŸãã-圌ããæäŸããæ¬åœã®å¯èœæ§ããããæ³ååã®ç¯å²ãäžããããããã§ãã
ã²ãŒã ããã°ã©ããŒãšããŠã®ãã£ãªã¢ãåžæããŸããã é³æ¥œæ¥çã®ããã«ãããã«è¡ãããè¥è
ã®æ°ãšåœŒãã«å¯Ÿããæ¬åœã®éèŠãšã®äžäžèŽãäžå¥åº·ãªç°å¢ãäœãåºããŠããŸãã ãããã楜ãã¿ã®ããã«ã²ãŒã ãæžãã®ã¯çŽ æŽãããããšã§ãã
ãã®ç« ã§ã¯ãåçŽãªãã©ãããã©ãŒããŒã®å®è£
ã«ã€ããŠèª¬æããŸãã ãã©ãããã©ãŒããŒïŒãŸãã¯ããžã£ã³ãããŠå®è¡ãïŒã§ã¯ããã¬ã€ã€ãŒã¯ãåŽé¢ããèŠããïŒéåžžïŒ2次å
ã®äžçã暪åã£ãŠãã£ã®ã¥ã¢ã移åããå€ãã®å Žåç°ãªããã®ãé£ã³è¶ããå¿
èŠããããŸãã
ã²ãŒã
ç§ãã¡ã®ã²ãŒã ã¯ãããŒãã¹ã»ãã¬ãã«ããããŒã¯ãã«ãŒã®ã²ãŒã ã«ã»ãŒåºã¥ããŠããŸãã é¢çœããŠãããã«ã§ãããæå°éã®ã³ãŒãã§äœæã§ãããããç§ã¯ãããéžãã ã 次ã®ããã«ãªããŸãã
é»ãé·æ¹åœ¢ã¯ãèµ€ãã»ã¯ã·ã§ã³ïŒæº¶å²©ïŒïŒãé¿ããŠãé»è²ãæ£æ¹åœ¢ïŒã³ã€ã³ïŒãåéããã¿ã¹ã¯ãæã€ãã¬ãŒã€ãŒãè¡šããŸãã ãã¬ã€ã€ãŒããã¹ãŠã®ã³ã€ã³ãåéãããšãã¬ãã«ã¯çµäºããŸãã
ãã¬ãŒã€ãŒã¯ããŒãå·Šå³ã«åãããŠããžã£ã³ãããããšãã§ããŸãã ãžã£ã³ãã¯ãã£ã©ã¯ã¿ãŒã®åŸæåéã§ãã 圌ã¯èªåã®èº«é·ãããæ°åé«ããžã£ã³ããã空äžã§ã®ç§»åæ¹åãå€ããããšãã§ããŸãã ããã¯ããŸãçŸå®çã§ã¯ãããŸãããããã¬ãŒã€ãŒãç»é¢äžã®ã¢ãã¿ãŒãå®å
šã«ã³ã³ãããŒã«ã§ããããã«ãªããŸãã
ã²ãŒã ã«ã¯ã°ãªããã®åœ¢ã§åºå®ãããèæ¯ãããã移åããèŠçŽ ãèæ¯ã«éããããŸãã åã°ãªããã»ã«ã¯ç©ºãå£ããŸãã¯æº¶å²©ã§ãã åãèŠçŽ ã¯ããã¬ã€ã€ãŒãã³ã€ã³ã溶岩ã®äžéšã§ãã 第7ç« ã®çåœã®ã·ãã¥ã¬ãŒã·ã§ã³ãšã¯ç°ãªãããããã®èŠçŽ ã®äœçœ®ã¯ã°ãªããã«é¢é£ä»ããããŠããŸããã ãããã®åº§æšã¯åæ°ã§ãããã¹ã ãŒãºãªåããæäŸããŸãã
æè¡
ã°ã©ãã£ãã¯ã«ã¯ãã©ãŠã¶ãŒDOMã䜿çšãããŠãŒã¶ãŒå
¥åãèªã¿åããããŒããŒãã€ãã³ããåŠçããŸãã
ç»é¢ãšããŒããŒãã«é¢é£ããã³ãŒãã¯ãã²ãŒã ãäœæããããã«å¿
èŠãªäœæ¥ã®ã»ãã®äžéšã§ãã ãã¹ãŠãè²ä»ãã®æ£æ¹åœ¢ã§æ§æãããŠãããããæç»ã¯ç°¡åã§ããDOMèŠçŽ ãäœæããã¹ã¿ã€ã«ã䜿çšããŠèæ¯è²ããµã€ãºãããã³å Žæãèšå®ããŸãã
èæ¯ã¯äžå®ã®æ£æ¹åœ¢ã®ã°ãªããã§ãããããèæ¯ãè¡šãšããŠæ瀺ããŸãã èªç±ã«ç§»åããèŠçŽ ã¯ã絶察é
眮ã䜿çšããŠäžéšã«éãåãããããšãã§ããŸãã
ãŠãŒã¶ãŒã®ã¢ã¯ã·ã§ã³ã«é
æ»ãªãå¿çããã°ã©ãã£ãã¯ã¢ãã¡ãŒã·ã§ã³ãåããã²ãŒã ããã®ä»ã®ã€ã³ã¿ã©ã¯ãã£ãããã°ã©ã ã§ã¯ãå¹çãéåžžã«éèŠã§ãã DOMã¯é«éã°ã©ãã£ãã¯ã¹ã衚瀺ããããã«ã¯èšèšãããŠããŸããããããã¯äºæ³ä»¥äžã«ããŸãåŠçãããŸãã 第13ç« ã§ã¯ãå°ãã¢ãã¡ãŒã·ã§ã³ãèŠãŸããã çŸä»£ã®ã³ã³ãã¥ãŒã¿ãŒã§ã¯ãæé©åã«ã€ããŠããã»ã©å¿é
ããå¿
èŠããªãå Žåã§ãããã®ãããªã·ã³ãã«ãªã²ãŒã ã¯ããŸããããŸãã
次ã®ç« ã§ã¯ãå¥ã®ãã©ãŠã¶ãã¯ãããžãŒãã€ãŸããããäŒçµ±çãªæç»æ¹æ³ãæäŸããDOMèŠçŽ ã®ä»£ããã«ã·ã§ã€ããšãã¯ã»ã«ãæäœããã¿ã°ãæ€èšããŸãã
ã¬ãã«
第7ç« ã§ã¯ãæååé
åã䜿çšããŠ2次å
æ Œåãèšè¿°ããŸããã ããã§ãåãããšãã§ããŸãã ããã«ãããæåã«ã¬ãã«ãšãã£ã¿ãäœæããã«ã¬ãã«ãéçºã§ããŸãã
åçŽãªã¬ãã«ã¯æ¬¡ã®ããã«ãªããŸãã
var simpleLevelPlan = [ " ", " ", " x = x ", " xoox ", " x @ xxxxx x ", " xxxxx x ", " x!!!!!!!!!!!!x ", " xxxxxxxxxxxxxx ", " " ];
åºå®ã°ãªãããšç§»åèŠçŽ ãå«ãŸããŠããŸãã xã¯å£ãè¡šããã¹ããŒã¹ã¯ç©ºãã¹ããŒã¹ãè¡šããæå笊ã¯åºå®æº¶å²©ãè¡šããŸãã
@ã¯ããã¬ãŒã€ãŒãéå§ããå Žæã瀺ããŸãã o-ã³ã€ã³ãçå·=移åãã溶岩ã®ãããã¯ãæå³ããååŸã«æ°Žå¹³ã«ç§»åããŸãã ãããã®äœçœ®ã®ã©ãã£ã¹ã«ã¯ç©ºã®ã¹ããŒã¹ãå«ãŸããå¥ã®ããŒã¿æ§é ããããã®ç§»åèŠçŽ ã®äœçœ®ã远跡ããããã«äœ¿çšãããããšã«æ³šæããŠãã ããã
ããã«2çš®é¡ã®æº¶å²©ããµããŒãããŸããåçŽç·| -åçŽã«ç§»åããç Žçã®å Žåãããã³v溶岩ã滎äžããå Žåã 圌女ã¯äžã«ç§»åããŸãããè·³ãè¿ãã®ã§ã¯ãªããåºã«çãããéå§äœçœ®ã«ãžã£ã³ãããŸãã
ã²ãŒã ã¯ãå®äºããå¿
èŠãããããã€ãã®ã¬ãã«ã§æ§æãããŠããŸãã ãã¹ãŠã®ã³ã€ã³ãåéããããšãã¬ãã«ãå®äºããŸãã ãã¬ã€ã€ãŒã溶岩ã«è§ŠãããšãçŸåšã®ã¬ãã«ã¯å
ã®ç¶æ
ã«æ»ãããã¬ã€ã€ãŒã¯åã³èµ·åããŸãã
èªè§£ã¬ãã«
次ã®ã³ã³ã¹ãã©ã¯ã¿ãŒã¯ãã¬ãã«ãªããžã§ã¯ããäœæããŸãã åŒæ°ã¯ãã¬ãã«ãæå®ããæååã®é
åã§ãªããã°ãªããŸããã
function Level(plan) { this.width = plan[0].length; this.height = plan.length; this.grid = []; this.actors = []; for (var y = 0; y < this.height; y++) { var line = plan[y], gridLine = []; for (var x = 0; x < this.width; x++) { var ch = line[x], fieldType = null; var Actor = actorChars[ch]; if (Actor) this.actors.push(new Actor(new Vector(x, y), ch)); else if (ch == "x") fieldType = "wall"; else if (ch == "!") fieldType = "lava"; gridLine.push(fieldType); } this.grid.push(gridLine); } this.player = this.actors.filter(function(actor) { return actor.type == "player"; })[0]; this.status = this.finishDelay = null; }
ç°¡æœã«ããããã«ãã³ãŒãã¯åä¿¡ããŒã¿ããã§ãã¯ããŸããã 圌ã¯ãã¬ãã«èšç»ãåãå
¥ãå¯èœã§ããããšããã¬ãŒã€ãŒã®éå§äœçœ®ãšä»ã®å¿
èŠãªãã®ãããããšãææ¡ããŸãã
ã¬ãã«ã¯ãå¹
ãšé«ããããã«2ã€ã®ã¢ã¬ã€ãä¿æããŸã-1ã€ã¯ã°ãªã«çšããã1ã€ã¯å¯åéšçšã§ãã ã©ãã£ã¹ã¯é
åã®é
åã§ãããã¹ããããåé
åã¯æ°Žå¹³ç·ãè¡šããåæ£æ¹åœ¢ã«ã¯ç©ºã®æ£æ¹åœ¢ã®å Žåã¯ãã«ããŸãã¯æ£æ¹åœ¢ã®ã¿ã€ããåæ ããæååïŒãå£ããŸãã¯ã溶岩ãïŒãå«ãŸããŸãã
ã¢ã¯ã¿ãŒé
åã«ã¯ãåçèŠçŽ ã®äœçœ®ãšç¶æ
ã远跡ãããªããžã§ã¯ããå«ãŸããŸãã ããããã«ã¯ãäœçœ®ïŒå·Šäžé
ã®åº§æšïŒãå«ãposããããã£ããµã€ãºãå«ããµã€ãºããããã£ãããã³ã¿ã€ãïŒãlavaãããcoinãããŸãã¯ãplayerãïŒã説æããè¡ãå«ãtypeããããã£ãå¿
èŠã§ãã
ã©ãã£ã¹ãæ§ç¯ããåŸããã£ã«ã¿ãŒã¡ãœããã䜿çšããŠãlevelããããã£ã«æ ŒçŽãããŠãããã¬ãŒã€ãŒãªããžã§ã¯ããèŠã€ããŸãã statusããããã£ã¯ããã¬ãŒã€ãŒãåã£ããè² ãããã远跡ããŸãã ãããçºçãããšãfinishDelayã䜿çšãããŸããããã«ãããã¬ãã«ããã°ããã¢ã¯ãã£ãã«ãªããåçŽãªã¢ãã¡ãŒã·ã§ã³ã衚瀺ãããŸãã ïŒãã ã¡ã«ã¬ãã«ã®ç¶æ
ã埩å
ãããã次ã®ã¬ãã«ãéå§ããŸã-èŠèŠããã§ãïŒ ãã®ã¡ãœããã¯ãã¬ãã«ãå®å
šã§ãããã©ããã調ã¹ãããã«äœ¿çšã§ããŸãã
Level.prototype.isFinished = function() { return this.status != null && this.finishDelay < 0; };
俳åªïŒä¿³åªïŒ
ã¢ã¯ã¿ãŒã®äœçœ®ãšãµã€ãºãä¿åããããã«ãx座æšãšy座æšããªããžã§ã¯ãã«ã°ã«ãŒãåããçã®Vectorã¿ã€ãã«æ»ããŸãã
function Vector(x, y) { this.x = x; this.y = y; } Vector.prototype.plus = function(other) { return new Vector(this.x + other.x, this.y + other.y); }; Vector.prototype.times = function(factor) { return new Vector(this.x * factor, this.y * factor); };
timesã¡ãœããã¯ãæå®ãããéã§ãã¯ãã«ãä¹ç®ããŸãã ãã®æéå
ã«ç§»åããè·é¢ãèŠã€ããããã«ãé床ãã¯ãã«ã«æéééãæããå¿
èŠãããå Žåã«äŸ¿å©ã§ãã
åã®ã»ã¯ã·ã§ã³ã§ã¯ãLevelã³ã³ã¹ãã©ã¯ã¿ãŒã¯actorCharsãªããžã§ã¯ãã䜿çšããŠãæåãã³ã³ã¹ãã©ã¯ã¿ãŒé¢æ°ã«é¢é£ä»ããŸããã ãªããžã§ã¯ãã¯æ¬¡ã®ããã«ãªããŸãã
var actorChars = { "@": Player, "o": Coin, "=": Lava, "|": Lava, "v": Lava };
3ã€ã®æåã¯æº¶å²©ãæããŸãã Levelã³ã³ã¹ãã©ã¯ã¿ãŒã¯ãã¢ã¯ã¿ãŒã®å
ã®æåã2çªç®ã®åŒæ°ãšããŠã³ã³ã¹ãã©ã¯ã¿ãŒã«æž¡ããLavaã³ã³ã¹ãã©ã¯ã¿ãŒã¯ããã䜿çšããŠåäœã調æŽããŸãïŒæ°Žå¹³æ¹åã«ãžã£ã³ããåçŽæ¹åã«ãžã£ã³ããç¹æ»ŽïŒã
ãã¬ãŒã€ãŒã¿ã€ãã¯ã次ã®ã³ã³ã¹ãã©ã¯ã¿ãŒã«ãã£ãŠæ§ç¯ãããŸãã çŸåšã®é床ãä¿åããé床ããããã£ããããéåéãšéåãã·ãã¥ã¬ãŒãããã®ã«åœ¹ç«ã¡ãŸãã
function Player(pos) { this.pos = pos.plus(new Vector(0, -0.5)); this.size = new Vector(0.8, 1.5); this.speed = new Vector(0, 0); } Player.prototype.type = "player";
ãã¬ãŒã€ãŒã®èº«é·ã¯1.5å¹³æ¹ãªã®ã§ã圌ã®åæäœçœ®ã¯ãã@ãèšå·ãé
眮ãããŠããäœçœ®ã®ååäžã«èšå®ãããŸãã ãããã£ãŠããã®åºéšã¯ããããçŸããæ£æ¹åœ¢ã®åºéšãšäžèŽããŸãã
åçãªLavaãªããžã§ã¯ããäœæãããšãã¯ããã£ã©ã¯ã¿ãŒã«å¿ããŠãªããžã§ã¯ããåæåããå¿
èŠããããŸãã åç溶岩ã¯ãé害ç©ã«ééãããŸã§æå®ã®é床ã§ç§»åããŸãã 次ã«ã圌女ã«repeatPosããããã£ãããå Žåãéå§äœçœ®ã«æ»ããŸãïŒæ»ŽäžïŒã ããã§ãªãå Žåã¯ãé床ãå転ãããå察æ¹åã«ç§»åãç¶ããŸãïŒããŠã³ã¹ïŒã ã³ã³ã¹ãã©ã¯ã¿ãŒã¯ãå¿
èŠãªããããã£ã®ã¿ãèšå®ããŸãã åŸã§ãã ãŒãã¡ã³ãèªäœãæ±ãã¡ãœãããäœæããŸãã
function Lava(pos, ch) { this.pos = pos; this.size = new Vector(1, 1); if (ch == "=") { this.speed = new Vector(2, 0); } else if (ch == "|") { this.speed = new Vector(0, 2); } else if (ch == "v") { this.speed = new Vector(0, 3); this.repeatPos = pos; } } Lava.prototype.type = "lava";
ã³ã€ã³ã¯ç°¡åã«å®è£
ã§ããŸãã 圌ãã¯ãã ãã£ãšåº§ã£ãŠããŸãã ããããã²ãŒã ã埩掻ãããããã«ã圌ãã¯éããŠããããã«åçŽã«ååŸã«åããŸãã ããã远跡ããããã«ãã³ã€ã³ãªããžã§ã¯ãã¯ã¡ã€ã³ããžã·ã§ã³ãšãŠã©ãã«ããããã£ãä¿åããåãã®ãã§ãŒãºã远跡ããŸãã äžç·ã«ãã³ã€ã³ã®äœçœ®ã決å®ããŸãïŒposããããã£ã«æ ŒçŽãããŸãïŒã
function Coin(pos) { this.basePos = this.pos = pos.plus(new Vector(0.2, 0.1)); this.size = new Vector(0.6, 0.6); this.wobble = Math.random() * Math.PI * 2; } Coin.prototype.type = "coin";
第13ç« ã§ã¯ãMath.sinãåäžã®ç¹ã®y座æšãäžããããšãèŠãŸããã åã®äžã移åããªããæ»ãããªæ³¢ã®åœ¢ã§ååŸã«ç§»åãããããæ£åŒŠé¢æ°ã¯æ³¢ã®åãã®ã¢ããªã³ã°ã«é©ããŠããŸãã
ãã¹ãŠã®ã³ã€ã³ãåæããŠç§»åããå Žåãåé¿ãããããããããã®åæ段éã¯ã©ã³ãã ã«ãªããŸãã æ³¢ã®äœçžã¯Math.sinã§ãæ³¢é·ã¯2Ïã§ãã Math.randomã«ãã£ãŠè¿ãããå€ã«ãã®æ°ãæããŠãã³ã€ã³ã«æ³¢ã®äžã§ã©ã³ãã ãªåæäœçœ®ãäžããŸãã
ããã§ãã¬ãã«ã®ç¶æ
ãè¡šãããã«å¿
èŠãªãã¹ãŠãèšè¿°ããŸããã
var simpleLevel = new Level(simpleLevelPlan); console.log(simpleLevel.width, "by", simpleLevel.height);
ãããã®ã¬ãã«ãç»é¢ã«è¡šç€ºãããããã®äžã®æéãšåããã·ãã¥ã¬ãŒãããå¿
èŠããããŸãã
ã«ãã»ã«åã®è² æ
ã»ãšãã©ã®å Žåããã®ç« ã®ã³ãŒãã¯ã«ãã»ã«åãæ°ã«ããŸããã ãŸããã«ãã»ã«åã«ã¯äœåãªåŽåãå¿
èŠã§ãã ããã°ã©ã ã¯å€§ãããªããããå€ãã®æŠå¿µãšã€ã³ã¿ãŒãã§ãŒã¹ãå¿
èŠã«ãªããŸãã ãããŠãèªè
ã®ç®ãããŸãã«ãå€ãã®ã³ãŒãããç®ãåããã®ã§ãç§ã¯ããã°ã©ã ãå°ããä¿ã€ããã«ããŸããã
第äºã«ãã²ãŒã ã®ããŸããŸãªèŠçŽ ãéåžžã«çµã³ã€ããŠããããããããã®1ã€ã®åäœãå€æŽãããå Žåãæ®ãã®ã³ãŒããå€æŽãããªãå¯èœæ§ã¯ã»ãšãã©ãããŸããã èŠçŽ éã«ã€ã³ã¿ãŒãã§ã€ã¹ãäœæãããšãã²ãŒã ã®ä»çµã¿ã«ã€ããŠå€ãã®ä»®å®ã䜿çšããããšã«ãªããŸãã ãããŠããããã¯å¹æçã§ã¯ãããŸãã-ã·ã¹ãã ã®äžéšãå€æŽãããšãä»ã®éšåã«ã©ã®ããã«åœ±é¿ããããèããå¿
èŠããããŸãããããã®ã€ã³ã¿ãŒãã§ãŒã¹ã¯æ°ããç¶æ³ãã«ããŒããªãããã§ãã
ã·ã¹ãã ã®äžéšã®éšåã¯ãå³å¯ã«å®çŸ©ãããã€ã³ã¿ãŒãã§ã€ã¹ã§çŽ°ããåå²ããã®ã«é©ããŠããŸãããããã§ãªãéšåããããŸãã æ確ãªå¢çãæããªããã®ãã«ãã»ã«åããããšãããšãå€ãã®ãšãã«ã®ãŒãæ¶è²»ããããšãä¿èšŒãããŸãã ãã®ãããªééããç¯ããšãã€ã³ã¿ãŒãã§ã€ã¹ã倧ãããªããããŠè©³çŽ°ã«ãªããããã°ã©ã ã®é²åäžã«é »ç¹ã«å€æŽããªããã°ãªããªãããšãããããŸãã
ç§ãã¡ã¯ãŸã äžã€ã®ããšãã«ãã»ã«åããŠããŸã-æç»ãµãã·ã¹ãã ã ããã¯ç¹ã«ã次ã®ç« ã§åãã²ãŒã ãç°ãªãæ¹æ³ã§è¡šç€ºã§ããããã«ããããã«è¡ãããŸãã ã€ã³ã¿ãŒãã§ãŒã¹ã®èåŸã«å³é¢ãé ããã®ã§ãåãããã°ã©ã ãããŠã³ããŒãããŠãæ°ããç»é¢åºåã¢ãžã¥ãŒã«ãæ¥ç¶ããã ãã§ãã
æç»
ç»é¢ã«ã¬ãã«ã衚瀺ãã衚瀺ãªããžã§ã¯ããå°å
¥ããŠãæç»çšã®ã³ãŒããã«ãã»ã«åããŸãã å®çŸ©ããç»é¢ã®ã¿ã€ãã¯ãDOMèŠçŽ ã䜿çšããŠã¬ãã«ã衚瀺ãããããDOMDisplayãšåŒã°ããŸãã
ã¹ã¿ã€ã«ã·ãŒãã䜿çšããŠãã²ãŒã ãæ§æããèŠçŽ ã®è²ããã®ä»ã®åºå®ããããã£ãèšå®ããŸãã äœææã«ã¹ã¿ã€ã«ããããã£ãä»ããŠèŠçŽ ã«ã¹ã¿ã€ã«ãçŽæ¥å²ãåœãŠãããšã¯å¯èœã§ããããã®å Žåã®ããã°ã©ã ã¯äžå¿
èŠã«åé·ã«ãªããŸãã
次ã®ãã«ããŒé¢æ°ã䜿çšãããšãã¯ã©ã¹ãå²ãåœãŠãŠèŠçŽ ãç°¡åã«äœæã§ããŸãã
function elt(name, className) { var elt = document.createElement(name); if (className) elt.className = className; return elt; }
æ¥ç¶ãã芪èŠçŽ ãšã¬ãã«ãªããžã§ã¯ããæž¡ãããšã§ç»é¢ãäœæããŸãã
function DOMDisplay(parent, level) { this.wrap = parent.appendChild(elt("div", "game")); this.level = level; this.wrap.appendChild(this.drawBackground()); this.actorLayer = null; this.drawFrame(); }
appendChildãè¿œå ãããèŠçŽ ãè¿ããšããäºå®ã䜿çšããŠãåšå²ã®ã©ãããŒèŠçŽ ãäœæãããããwrapããããã£ã«ä¿åããŸãã
äžå®ã¬ãã«ã®èæ¯ã1åæç»ãããŸãã ã¢ã¯ã¿ãŒã¯ãç»é¢ãæŽæ°ããããã³ã«åæç»ãããŸãã actorLayerããããã£ã¯drawFrameã§äœ¿çšãããã¢ã¯ã¿ãŒãå«ãèŠçŽ ã远跡ããŸããããã«ãããã¢ã¯ã¿ãŒãç°¡åã«åé€ããã³çœ®æã§ããŸãã
座æšãšå¯žæ³ã¯ãã°ãªããã®ãµã€ãºãåºæºã«ããåäœã§æž¬å®ãããããã1ã€ã®è·é¢ã¯ã°ãªããã®1ã€ã®èŠçŽ ãæå³ããŸãã ãã¯ã»ã«åäœã§å¯žæ³ãèšå®ããå Žåã座æšãã¹ã±ãŒãªã³ã°ããå¿
èŠããããŸãã1ã€ã®ãã¯ã»ã«ã§1ã€ã®æ£æ¹åœ¢ãæå®ãããšãã²ãŒã ã¯éåžžã«æµ
ããªããŸãã ã¹ã±ãŒã«å€æ°ã¯ã1ã€ã®ã°ãªããèŠçŽ ãå ãããã¯ã»ã«æ°ã瀺ããŸãã
var scale = 20; DOMDisplay.prototype.drawBackground = function() { var table = elt("table", "background"); table.style.width = this.level.width * scale + "px"; this.level.grid.forEach(function(row) { var rowElt = table.appendChild(elt("tr")); rowElt.style.height = scale + "px"; row.forEach(function(type) { rowElt.appendChild(elt("td", type)); }); }); return table; };
ãã§ã«è¿°ã¹ãããã«ãèæ¯ã¯èŠçŽ ãéããŠæãããŸã
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , â ( ). (). CSS :
.background { background: rgb(52, 166, 251); table-layout: fixed; border-spacing: 0; } .background td { padding: 0; } .lava { background: rgb(255, 100, 100); } .wall { background: white; }
(table-layout, border-spacing padding) . , , .
background . CSS (white) rgb(R, G, B), , 0 255. , rgb(52, 166, 251) 52, 166 251. , . , .lava â .
DOM , . scale, .
DOMDisplay.prototype.drawActors = function() { var wrap = elt("div"); this.level.actors.forEach(function(actor) { var rect = wrap.appendChild(elt("div", "actor " + actor.type)); rect.style.width = actor.size.x * scale + "px"; rect.style.height = actor.size.y * scale + "px"; rect.style.left = actor.pos.x * scale + "px"; rect.style.top = actor.pos.y * scale + "px"; }); return wrap; };
, . CSS actor absolute. . lava, , .
.actor { position: absolute; } .coin { background: rgb(241, 229, 89); } .player { background: rgb(64, 64, 64); }
drawFrame , , . DOM , . DOM, . , .
DOMDisplay.prototype.drawFrame = function() { if (this.actorLayer) this.wrap.removeChild(this.actorLayer); this.actorLayer = this.wrap.appendChild(this.drawActors()); this.wrap.className = "game " + (this.level.status || ""); this.scrollPlayerIntoView(); };
wrapper , - , . CSS, , .
.lost .player { background: rgb(160, 64, 64); } .won .player { box-shadow: -4px -7px 8px white, 4px -7px 8px white; }
-, . , .
, . scrollPlayerIntoView â , , , . CSS , , . relative, .
.game { overflow: hidden; max-width: 600px; max-height: 450px; position: relative; }
scrollPlayerIntoView . , scrollLeft scrollTop, .
DOMDisplay.prototype.scrollPlayerIntoView = function() { var width = this.wrap.clientWidth; var height = this.wrap.clientHeight; var margin = width / 3; // The viewport var left = this.wrap.scrollLeft, right = left + width; var top = this.wrap.scrollTop, bottom = top + height; var player = this.level.player; var center = player.pos.plus(player.size.times(0.5)) .times(scale); if (center.x < left + margin) this.wrap.scrollLeft = center.x - margin; else if (center.x > right - margin) this.wrap.scrollLeft = center.x + margin - width; if (center.y < top + margin) this.wrap.scrollTop = center.y - margin; else if (center.y > bottom - margin) this.wrap.scrollTop = center.y + margin - height; };
, Vector , , . , ( ) . , , .
, . , , . â DOM . scrollLeft -10, 0.
â . . «» , , .
, .
DOMDisplay.prototype.clear = function() { this.wrap.parentNode.removeChild(this.wrap); };
.
<link rel="stylesheet" href="css/game.css"> <script> var simpleLevel = new Level(simpleLevelPlan); var display = new DOMDisplay(document.body, simpleLevel); </script>
rel="stylesheet"
CSS. game.css .
â . , â , , ( ), ( ).
. , . , . , . , , .
. , « », . , .
, , , . â . â , .
, . , . â . , , .
, ( ) - .
Level.prototype.obstacleAt = function(pos, size) { var xStart = Math.floor(pos.x); var xEnd = Math.ceil(pos.x + size.x); var yStart = Math.floor(pos.y); var yEnd = Math.ceil(pos.y + size.y); if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall"; if (yEnd > this.height) return "lava"; for (var y = yStart; y < yEnd; y++) { for (var x = xStart; x < xEnd; x++) { var fieldType = this.grid[y][x]; if (fieldType) return fieldType; } } };
, Math.floor Math.ceil . , â 11 . , , .
, âwallâ âlavaâ . . , , , .
(, ) . , ( ).
, , :
Level.prototype.actorAt = function(actor) { for (var i = 0; i < this.actors.length; i++) { var other = this.actors[i]; if (other != actor && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) return other; } };
animate Level . step . keys , .
var maxStep = 0.05; Level.prototype.animate = function(step, keys) { if (this.status != null) this.finishDelay -= step; while (step > 0) { var thisStep = Math.min(step, maxStep); this.actors.forEach(function(actor) { actor.act(thisStep, this, keys); }, this); step -= thisStep; } };
status , null ( , ), finishDelay, , , .
while . , maxStep. , 0.12 0.05 0.02
act, , level keys. Lava, key:
Lava.prototype.act = function(step, level) { var newPos = this.pos.plus(this.speed.times(step)); if (!level.obstacleAt(newPos, this.size)) this.pos = newPos; else if (this.repeatPos) this.pos = this.repeatPos; else this.speed = this.speed.times(-1); };
, . , . , . repeatPos, . ( -1), .
act, . , , act .
var wobbleSpeed = 8, wobbleDist = 0.07; Coin.prototype.act = function(step) { this.wobble += step * wobbleSpeed; var wobblePos = Math.sin(this.wobble) * wobbleDist; this.pos = this.basePos.plus(new Vector(0, wobblePos)); };
wobble , , Math.sin , .
. , , â . .
var playerXSpeed = 7; Player.prototype.moveX = function(step, level, keys) { this.speed.x = 0; if (keys.left) this.speed.x -= playerXSpeed; if (keys.right) this.speed.x += playerXSpeed; var motion = new Vector(this.speed.x * step, 0); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) level.playerTouched(obstacle); else this.pos = newPos; };
«» «». , playerTouched, . .
, .
var gravity = 30; var jumpSpeed = 17; Player.prototype.moveY = function(step, level, keys) { this.speed.y += step * gravity; var motion = new Vector(0, this.speed.y * step); var newPos = this.pos.plus(motion); var obstacle = level.obstacleAt(newPos, this.size); if (obstacle) { level.playerTouched(obstacle); if (keys.up && this.speed.y > 0) this.speed.y = -jumpSpeed; else this.speed.y = 0; } else { this.pos = newPos; } };
, . , . , .
. , . «», ( , -, ), . . , - .
act :
Player.prototype.act = function(step, level, keys) { this.moveX(step, level, keys); this.moveY(step, level, keys); var otherActor = level.actorAt(this); if (otherActor) level.playerTouched(otherActor.type, otherActor); // Losing animation if (level.status == "lost") { this.pos.y += step; this.size.y -= step; } };
, , playerTouched, . actor, , playerTouched , .
, ( ), , - ( ), player.
, :
Level.prototype.playerTouched = function(type, actor) { if (type == "lava" && this.status == null) { this.status = "lost"; this.finishDelay = 1; } else if (type == "coin") { this.actors = this.actors.filter(function(other) { return other != actor; }); if (!this.actors.some(function(actor) { return actor.type == "coin"; })) { this.status = "won"; this.finishDelay = 1; } } };
, âlostâ. , , â âwonâ. , . , .
, keypress. , , ( )
, , . preventDefault, .
, , , . "keydown" "keyup", , .
var arrowCodes = {37: "left", 38: "up", 39: "right"}; function trackKeys(codes) { var pressed = Object.create(null); function handler(event) { if (codes.hasOwnProperty(event.keyCode)) { var down = event.type == "keydown"; pressed[codes[event.keyCode]] = down; event.preventDefault(); } } addEventListener("keydown", handler); addEventListener("keyup", handler); return pressed; }
, . type , , true ("keydown") false ("keyup").
requestAnimationFrame, 13, . â , , requestAnimationFrame .
, , runAnimation, , . frame false, .
function runAnimation(frameFunc) { var lastTime = null; function frame(time) { var stop = false; if (lastTime != null) { var timeStep = Math.min(time - lastTime, 100) / 1000; stop = frameFunc(timeStep) === false; } lastTime = time; if (!stop) requestAnimationFrame(frame); } requestAnimationFrame(frame); }
100 (1/10 ). , requestAnimationFrame , . , lastTime , . ( animate).
, , .
runLevel Level, display, , â . document.body . ( ), runLevel , , andThen, .
var arrows = trackKeys(arrowCodes); function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); }
â . , . , . , ( ) display
function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); }
. runAnimation runLevel â , , 5. , , - , . â . , , .
. â , , , â , 17, 20.
GAME_LEVELS . runGame, .
<link rel="stylesheet" href="css/game.css"> <body> <script> runGame(GAME_LEVELS, DOMDisplay); </script> </body>
. , .
, , . , .
runGame, . .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runGame â ... function runGame(plans, Display) { function startLevel(n) { runLevel(new Level(plans[n]), Display, function(status) { if (status == "lost") startLevel(n); else if (n < plans.length - 1) startLevel(n + 1); else console.log("You win!"); }); } startLevel(0); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>
Esc.
, runLevel, , Esc.
, runAnimation â runLevel, .
, -. . arrows â , , . , . trackKeys, runLevel, , .
<link rel="stylesheet" href="css/game.css"> <body> <script> // runLevel â ... function runLevel(level, Display, andThen) { var display = new Display(document.body, level); runAnimation(function(step) { level.animate(step, arrows); display.drawFrame(step); if (level.isFinished()) { display.clear(); if (andThen) andThen(level.status); return false; } }); } runGame(GAME_LEVELS, DOMDisplay); </script> </body>