canvasèŠçŽ ã¯HTML5ã§å°å ¥ããããããæäœããããã®APIãæäŸããŸãã APIã¯ã·ã³ãã«ã§ãããã°ã©ãã£ãã¯ã¹ãäžåºŠã䜿çšããããšããªãå Žåã¯ãæ £ãããŸã§æéãããããŸãã Canvasã¯å€æ°ã®ãã©ãŠã¶ã§ãµããŒããããŠãããããWebã¯ã²ãŒã ãäœæããããã®åªãããã©ãããã©ãŒã ã«ãªããŸãã
ãã£ã³ãã¹ã®äœ¿çšã¯ç°¡åã§ããã¿ã°ãäœæããJavaScriptã§è¡šç€ºã³ã³ããã¹ããäœæãããã®ã³ã³ããã¹ãã§fillRectãdrawImageãªã©ã®ã¡ãœããã䜿çšããŠãã©ãŒã ãç»åã衚瀺ããŸãã APIã«ã¯ãããŸããŸãªãã¹ã®äœæãç»åå€æãªã©ã®ããã®å€ãã®ã¡ãœãããå«ãŸããŠããŸãã
ãã®èšäºã§ã¯ããã£ã³ãã¹ã䜿çšããŠã²ãŒã ãäœæããŸãã ã¹ãã©ã€ããè¡çªã®è¿œè·¡ããããŠãã¡ããççºã䌎ãå®éã®ã²ãŒã ã ççºã®ãªããªããŠã²ãŒã ïŒ
ãããŠããããç§ãã¡ãäœæããããšããŠããã²ãŒã ã§ãã
æºåãã
ã²ãŒã ã¯è€éã«èŠãããããããŸããããå®éã«ã¯ããã€ãã®ã³ã³ããŒãã³ãã䜿çšããããšã«ãªããŸãã ãã£ã³ãã¹ãè€æ°ã®ã¹ãã©ã€ããã³ãªãžã§ã³ãã©ããã³ã°ãã²ãŒã ã«ãŒããã©ãã ã䜿çšã§ãããããã€ãé©ããŸããã
ã²ãŒã ã®ã³ã³ããŒãã³ãã«å®å šã«çŠç¹ãåœãŠãããã«ãã³ãŒããšAPIã®ãã¹ãŠã®è¡ãåã¿ãŸããã ãã®èšäºã¯äžçŽã¬ãã«ã§æžãããŠããŸããããã¹ãŠã®ã¬ãã«ã®äººã ãç解ã§ããããšãé¡ã£ãŠããŸãã ãã®èšäºã¯ãèªè ããã§ã«JavaScriptãšHTMLã®åºæ¬ã«ç²ŸéããŠããããšãåæãšããŠããŸãã ãŸãããã£ã³ãã¹APIãšãã²ãŒã ã«ãŒããªã©ã®åºæ¬çãªã²ãŒã ã®ååã«ã€ããŠã觊ããŸãã
ãã£ã³ãã¹ãäœæ
ã³ãŒãã®åŠç¿ãå§ããŸãããã ã²ãŒã ã®ã»ãšãã©ã¯app.jsã«ãããŸãã
æåã«è¡ãããšã¯ãã¿ã°ãäœæãããã®å¹ ãšé«ããèšå®ããããšã§ãã ãã¹ãŠãJSã«ä¿æããããã«ãããåçã«è¡ããŸãããHTMLããã¥ã¡ã³ãã§ãã£ã³ãã¹ãäœæãã getElementByIdã䜿çšããŠååŸã§ããŸãã 2ã€ã®éã«éãã¯ãããŸãããããã¯åã«å¥œã¿ã®åé¡ã§ãã
// Create the canvas var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); canvas.width = 512; canvas.height = 480; document.body.appendChild(canvas);
Canvasã«ã¯ã衚瀺ã³ã³ããã¹ããååŸããããã«äœ¿çšãããgetContextã¡ãœããããããŸãã ã³ã³ããã¹ãã¯ããã£ã³ãã¹APIãšå¯Ÿè©±ããã¡ãœãããåŒã³åºãããšã«ãããªããžã§ã¯ãã§ãã 3Dã·ãŒã³ã«WebGLã䜿çšããå Žåã¯ããã©ã¡ãŒã¿ãŒ 'webgl'ãæž¡ãããšãã§ããŸãã
次ã«ã ctxå€æ°ã䜿çšããŠãã¹ãŠã®èŠçŽ ã衚瀺ããŸãã
ã²ãŒã ãµã€ã¯ã«
ã²ãŒã ãåžžã«æŽæ°ããŠè¡šç€ºããã²ãŒã ãµã€ã¯ã«ãå¿ èŠã§ãã ããã¯æ¬¡ã®ãããªãã®ã§ãã
// The main game loop var lastTime; function main() { var now = Date.now(); var dt = (now - lastTime) / 1000.0; update(dt); render(); lastTime = now; requestAnimFrame(main); };
ã·ãŒã³ãæŽæ°ããŠè¡šç€ºãã requestAnimationFrameã䜿çšããŠæ¬¡ã®ã«ãŒãããã¥ãŒã«å ¥ããŸãã setTimeoutïŒã¡ã€ã³ã1000/60ïŒã䜿çšããŠã60ãã¬ãŒã /ç§ã衚瀺ããããšãããšãæ¬åœã«ç°¡åã«ãªããŸãã app.jsã®æäžéšã§requestAnimationFrameã®ã©ãããŒãäœæããŸããããã¹ãŠã®ãã©ãŠã¶ãŒããã®ã¡ãœããããµããŒãããŠããããã§ã¯ãªãããã§ãã
SetTimeoutïŒã¡ã€ã³ã1000/60ïŒã¯äœ¿çšãããŸãããããã¯ã粟床ãäœããäžèŠãªå Žåã«è¡šç€ºã«å€ãã®ãµã€ã¯ã«ãè²»ããããã§ãã
æŽæ°é¢æ°ã®dtã®ãã©ã¡ãŒã¿ãŒã¯ãçŸåšã®æå»ãšæåŸã®æŽæ°ã®æå»ã®å·®ã§ãã ãã¬ãŒã ã«äžå®ã®å€ã䜿çšããŠã·ãŒã³ããªãã¬ãã·ã¥ããªãã§ãã ããïŒã¹ããªããã§ã¯ãx + = 5ïŒã ã²ãŒã ã¯ç°ãªãã³ã³ãã¥ãŒã¿ãŒ/ãã©ãããã©ãŒã ã§ç°ãªãåäœãããããããã¬ãŒã ã¬ãŒãã«é¢ä¿ãªãã·ãŒã³ãæŽæ°ããå¿ èŠããããŸãã
ããã¯ãæåŸã®æŽæ°ããã®æéãèšç®ãããã¹ãŠã®åãã1ç§ãããã®ãã¯ã»ã«ã§è¡šçŸããããšã«ããå®çŸãããŸãã ãããŠãåãã¯æ¬¡ã®x + = 50 * dtãã€ãŸã50ãã¯ã»ã«/ç§ã«ãªããŸãã
ãªãœãŒã¹ã®èªã¿èŸŒã¿ãšã²ãŒã ã®èµ·å
ã³ãŒãã®æ¬¡ã®éšåã§ã¯ãã²ãŒã ãåæåããå¿ èŠãªãã¹ãŠã®ãªãœãŒã¹ãããŒãããŸãã ãããè¡ãã«ã¯ãåå¥ã«èšè¿°ããããã«ããŒã¯ã©ã¹ã®1ã€ã§ããresources.jsã䜿çšããŸãã ããã¯ããã¹ãŠã®ç»åãããŠã³ããŒããããã¹ãŠã®ç»åãèªã¿èŸŒãŸãããšã€ãã³ããçºçãããéåžžã«ã·ã³ãã«ãªã©ã€ãã©ãªã§ãã
ã²ãŒã ã«ã¯ãç»åãã·ãŒã³ããŒã¿ãªã©ã®ãªãœãŒã¹ãå«ãŸããŠããŸãã 2Dã²ãŒã ã®å Žåãäž»ãªãªãœãŒã¹ã¯ç»åã§ãã ããã«äœ¿çšã§ããããã«ããã«ã¯ãã¢ããªã±ãŒã·ã§ã³ãèµ·åããåã«ãã¹ãŠã®ãªãœãŒã¹ãããŠã³ããŒãããå¿ èŠããããŸãã
JavaScriptã䜿çšãããšãç»åãç°¡åã«ããŒãããŠãå¿ èŠãªãšãã«äœ¿çšã§ããŸãã
var img = new Image(); img.onload = function() { startGame(); }; img.src = url;
ãã¡ãããããããã®ç»åããªãéããããã¯éåžžã«é¢åã§ãã ããããã®ã°ããŒãã«å€æ°ãäœæããããŒããããã©ããããã§ãã¯ããå¿ èŠããããŸãã ããããã¹ãŠèªåçã«è¡ãããã®åºæ¬çãªãªãœãŒã¹ããŒããŒãäœæããŸããã
(function() { var resourceCache = {}; var loading = []; var readyCallbacks = []; // Load an image url or an array of image urls function load(urlOrArr) { if(urlOrArr instanceof Array) { urlOrArr.forEach(function(url) { _load(url); }); } else { _load(urlOrArr); } } function _load(url) { if(resourceCache[url]) { return resourceCache[url]; } else { var img = new Image(); img.onload = function() { resourceCache[url] = img; if(isReady()) { readyCallbacks.forEach(function(func) { func(); }); } }; resourceCache[url] = false; img.src = url; } } function get(url) { return resourceCache[url]; } function isReady() { var ready = true; for(var k in resourceCache) { if(resourceCache.hasOwnProperty(k) && !resourceCache[k]) { ready = false; } } return ready; } function onReady(func) { readyCallbacks.push(func); } window.resources = { load: load, get: get, onReady: onReady, isReady: isReady }; })();
ä»çµã¿ïŒãã¹ãŠã®ã€ã¡ãŒãžãèªã¿èŸŒãã§resources.loadãåŒã³åºãã次ã«resources.onReadyãåŒã³åºããŠããã¹ãŠã®ããŒã¿ã®loadã€ãã³ãã§ã³ãŒã«ããã¯ãäœæããŸãã resources.loadã¯ã²ãŒã ã®åŸåã§äœ¿çšããããèµ·åæã«ã®ã¿äœ¿çšãããŸã
ã¢ããããŒããããç»åã¯resourcesCacheã«ãã£ãã·ã¥ããããã¹ãŠã®ç»åãã¢ããããŒãããããšããã¹ãŠã®ã³ãŒã«ããã¯ãåŒã³åºãããŸãã ãããè¡ãããšãã§ããŸãïŒ
resources.load([ 'img/sprites.png', 'img/terrain.png' ]); resources.onReady(init);
Resources.getïŒ 'img / sprites.pngïŒã䜿çšããŠç»åãååŸããŸãã ç°¡åïŒ
ãã¹ãŠã®ç»åãæåã§ããŠã³ããŒãããŠã²ãŒã ãå®è¡ããããããã»ã¹ãç°¡çŽ åããããã«resources.jsã®ç²Ÿç¥ã§äœãã䜿çšã§ããŸãã
äžèšã®ã³ãŒãã§ã¯ããã¹ãŠã®ç»åãèªã¿èŸŒãŸãããšinitãåŒã³åºãããŸãã Initã¯èæ¯ç»åãäœæãããPlay againããã¿ã³ã«ã€ãã³ããæããã²ãŒã ããªã»ããããŠéå§ããã²ãŒã ãéå§ããŸãã
function init() { terrainPattern = ctx.createPattern(resources.get('img/terrain.png'), 'repeat'); document.getElementById('play-again').addEventListener('click', function() { reset(); }); reset(); lastTime = Date.now(); main(); }
ã²ãŒã ã®ç¶æ
ããå§ããŸãããïŒ ã²ãŒã ããžãã¯ãå§ããŸãããã ãã¹ãŠã®ã²ãŒã ã®äžæ žã¯ãã²ãŒã ã®ç¶æ ãã§ãã ãããã¯ãã²ãŒã ã®çŸåšã®ç¶æ ãè¡šãããŒã¿ã§ãããããäžã®ãªããžã§ã¯ãã®ãªã¹ãã座æšããã®ä»ã®ããŒã¿ã çŸåšã®ãã€ã³ããªã©ã
以äžãã²ãŒã ã®ç¶æ ã§ãã
// Game state var player = { pos: [0, 0], sprite: new Sprite('img/sprites.png', [0, 0], [39, 39], 16, [0, 1]) }; var bullets = []; var enemies = []; var explosions = []; var lastFire = Date.now(); var gameTime = 0; var isGameOver; var terrainPattern; // The score var score = 0; var scoreEl = document.getElementById('score');
å€ãã®ããšã®ããã«æããŸãããå®éã«ã¯ããã¹ãŠãããã»ã©è€éã§ã¯ãããŸããã ã»ãšãã©ã®å€æ°ã¯è¿œè·¡ãããå€ã§ãïŒãã¬ãŒã€ãŒã®æåŸã®ã·ã§ããïŒlastFiredïŒãã²ãŒã ã®å®è¡æéïŒgameTimeïŒãã²ãŒã ã®çµäºïŒisGameOverïŒãå°åœ¢ç»åïŒterrainPatternïŒããã³ãã€ã³ãã ãããäžã®ãªããžã§ã¯ãã説æãããŠããŸãïŒåŒŸäžžãæµãççºã
ãã¬ãŒã€ãŒã®æ¬è³ªãããããã¬ãŒã€ãŒã®äœçœ®ãšã¹ãã©ã€ãã®ç¶æ ã远跡ãããŸãã ã³ãŒãã«å ¥ãåã«ããšã³ãã£ãã£ãšã¹ãã©ã€ãã«ã€ããŠè©±ããŸãããã
ãšã³ãã£ãã£ãšã¹ãã©ã€ã
ãšã³ãã£ãã£
ãšã³ãã£ãã£ã¯ããããäžã®ãªããžã§ã¯ãã§ãã è¹ã匟䞞ãççºããã¹ãŠå®äœã§ãããã©ããã¯é¢ä¿ãããŸããã
ã·ã¹ãã å ã®ãšã³ãã£ãã£ã¯ããªããžã§ã¯ãã®äœçœ®ãªã©ã«é¢ããæ å ±ãæ ŒçŽããJavaScriptãªããžã§ã¯ãã§ãã ããã¯ãåã¿ã€ãã®ãšã³ãã£ãã£ãæåã§ç£èŠããéåžžã«ã·ã³ãã«ãªã·ã¹ãã ã§ãã åãšã³ãã£ãã£ã«ã¯ãposããã³spriteãããã³å Žåã«ãã£ãŠã¯ãã®ä»ã®ããããã£ããããŸãã ããšãã°ãæµããããã«è¿œå ããå Žåã次ã®ããã«ããŸãã
enemies.push({ pos: [100, 50], sprite: new Sprite(/* sprite parameters */) });
ãã®ã³ãŒãã¯ãç¹å®ã®ã¹ãã©ã€ãã䜿çšããŠãäœçœ®x = 100ãy = 50ã§æµããããã«è¿œå ããŸãã
ã¹ãã©ã€ããšã¢ãã¡ãŒã·ã§ã³
ã¹ãã©ã€ãã¯ããšã³ãã£ãã£ã®è¡šçŸã衚瀺ããç»åã§ãã ã¢ãã¡ãŒã·ã§ã³ããªãå Žåãã¹ãã©ã€ãã¯ctx.drawImageã䜿çšããŠè¡šãããéåžžã®ç»åã§ãã
ããã€ãã®ç»åãèªã¿èŸŒãã§ãæéã®çµéãšãšãã«ããããå€æŽããããšã§ãã¢ãã¡ãŒã·ã§ã³ãå®è£ ã§ããŸãã ããã¯ãã¬ãŒã ã¢ãã¡ãŒã·ã§ã³ãšåŒã°ããŸãã

ãããã®ç»åãæåããæåŸãŸã§äº€äºã«äžŠã¹ããšã次ã®ããã«ãªããŸãã

ç»åã®ç·šéãšèªã¿èŸŒã¿ãç°¡åã«ããããã«ãéåžžã¯ãã¹ãŠã1ã€ã«ãŸãšããããŸããããã¯ã¹ãã©ã€ãã«ãŒããšåŒã°ããŸãã ãã®CSSãã¯ããã¯ã¯æ¢ã«ãåããããããŸããã
ããã¯ãã²ãŒã ã®ã¹ãã©ã€ããããã§ãïŒèæ¯ã¯éæã§ãïŒã

ããŒãããã¥ãŒã ã€ã¡ãŒãžã»ããã䜿çšããŸãã ãã®ã»ããã¯bmpãã¡ã€ã«ã®ã»ãããªã®ã§ãå¿ èŠãªç»åãã³ããŒããŠ1ã€ã®ã¹ãã©ã€ãã·ãŒããã貌ãä»ããŸããã ãã®ããã«ã¯ãã·ã³ãã«ãªã°ã©ãã£ãã¯ãšãã£ã¿ãŒãå¿ èŠã§ãã
ãã¹ãŠã®ã¢ãã¡ãŒã·ã§ã³ãæåã§ç®¡çããããšã¯å°é£ã§ãã ãããè¡ãã«ã¯ã2çªç®ã®ãã«ããŒã¯ã©ã¹sprite.jsã䜿çšããŸãã ããã¯ãã¢ãã¡ãŒã·ã§ã³ããžãã¯ãå«ãå°ããªãã¡ã€ã«ã§ãã èŠãŠã¿ãŸãããïŒ
function Sprite(url, pos, size, speed, frames, dir, once) { this.pos = pos; this.size = size; this.speed = typeof speed === 'number' ? speed : 0; this.frames = frames; this._index = 0; this.url = url; this.dir = dir || 'horizontal'; this.once = once; };
ããã¯ãSpriteã¯ã©ã¹ã®ã³ã³ã¹ãã©ã¯ã¿ãŒã§ãã å€ãã®è°è«ãå¿ èŠã§ããããã¹ãŠãå¿ é ãšããããã§ã¯ãããŸããã ãããã®ãããããèæ ®ããŠãã ããïŒ
- url ïŒç»åãã¹
- pos ïŒã¹ãã©ã€ããããäžã®ç»åã®xããã³y座æš
- ãµã€ãº ïŒãµã€ãºïŒ1ãã¬ãŒã ã®ã¿ïŒ
- é床 ïŒãã¬ãŒã /ç§ã®ã¢ãã¡ãŒã·ã§ã³é床
- ãã¬ãŒã ïŒã¢ãã¡ãŒã·ã§ã³é ã®ãã¬ãŒã ã€ã³ããã¯ã¹ã®é å
- dir ïŒã¹ãã©ã€ããããäžã§ç§»åããæ¹åïŒ 'horizoâântalïŒããã©ã«ãïŒãŸãã¯' vertical '
- once ïŒã¢ãã¡ãŒã·ã§ã³ã«ãŒãã1ã€ã ã衚瀺ããå Žåã¯trueãããã©ã«ã-false
ãã¬ãŒã åŒæ°ã«ã€ããŠã¯ãããããããã«èª¬æãå¿ èŠã§ãã ã¢ãã¡ãŒã·ã§ã³ã®ãã¹ãŠã®ãã¬ãŒã ã¯åããµã€ãºã§ãããšç解ãããŠããŸãïŒããã¯äžèšã§æž¡ããããµã€ãºã§ãïŒã ã¢ãã¡ãŒã·ã§ã³äžãã·ã¹ãã ã¯ãäœçœ®pos cããéå§ããŠãxãŸãã¯y軞ã«æ²¿ã£ãŠãµã€ãºããšã«å¢åããŠãã¹ãã©ã€ããããã«æ²¿ã£ãŠïŒdirå€ã«å¿ããŠïŒåã«ã移åãããŸãã ã¢ãã¡ãŒã·ã§ã³ã®ãã¬ãŒã ãã©ã®ããã«ééããå¿ èŠãããããèšè¿°ããããã«ããã¬ãŒã ãå®çŸ©ããå¿ èŠããããŸãã [0ã1ã2ã3ã2ã1]ãã¬ãŒã ã®å€ã¯ãæåããæåŸãŸã§ããããŠæåããæåŸãŸã§æ»ããŸãã
ã¢ãã¡ãŒã·ã§ã³ã¯äžèŠãªãããurlãposãsizeã®ã¿ãå¿ èŠã§ãã
åSpriteãªããžã§ã¯ãã«ã¯ãã¢ãã¡ãŒã·ã§ã³ãæŽæ°ããããã®updateã¡ãœãããããããã®åŒæ°ã¯ã°ããŒãã«æŽæ°ãšåæ§ã«ãã«ã¿æéã§ãã åã¹ãã©ã€ãã¯ããã¬ãŒã ããšã«æŽæ°ããå¿ èŠããããŸãã
Sprite.prototype.update = function(dt) { this._index += this.speed*dt; }
åSpriteãªããžã§ã¯ãã«ã¯ãããèªäœãã¬ã³ããªã³ã°ããããã®renderã¡ãœããããããŸãã ã¢ãã¡ãŒã·ã§ã³ã®åºæ¬çãªããžãã¯ãå«ãŸããŠããŸãã ã©ã®ãã¬ãŒã ãæç»ããããç£èŠããã¹ãã©ã€ããããäžã®åº§æšãèšç®ããctx.drawImageãåŒã³åºããŠãã¬ãŒã ãæç»ããŸãã
Sprite.prototype.render = function(ctx) { var frame; if(this.speed > 0) { var max = this.frames.length; var idx = Math.floor(this._index); frame = this.frames[idx % max]; if(this.once && idx >= max) { this.done = true; return; } } else { frame = 0; } var x = this.pos[0]; var y = this.pos[1]; if(this.dir == 'vertical') { y += frame * this.size[1]; } else { x += frame * this.size[0]; } ctx.drawImage(resources.get(this.url), x, y, this.size[0], this.size[1], 0, 0, this.size[0], this.size[1]); }
drawImageã®3ã€ã®åœ¢åŒã䜿çšããŸããããã«ãããã¹ãã©ã€ãã®ãµã€ãºããªãã»ãããããã³æ¹åãåå¥ã«æå®ã§ããŸãã
ã·ãŒã³æŽæ°
ã²ãŒã ã«ãŒãã§ããã¬ãŒã ããšã«æŽæ°ïŒdtïŒãåŒã³åºããæ¹æ³ãèŠããŠããŸããïŒ ããã§ãã®é¢æ°ãå®çŸ©ããå¿ èŠããããŸãããã®é¢æ°ã¯ããã¹ãŠã®ã¹ãã©ã€ãã®æŽæ°ããšã³ãã£ãã£ã®äœçœ®ãšè¡çªã®æŽæ°ãåŠçããå¿ èŠããããŸãã
unction update(dt) { gameTime += dt; handleInput(dt); updateEntities(dt); // It gets harder over time by adding enemies using this // equation: 1-.993^gameTime if(Math.random() < 1 - Math.pow(.993, gameTime)) { enemies.push({ pos: [canvas.width, Math.random() * (canvas.height - 39)], sprite: new Sprite('img/sprites.png', [0, 78], [80, 39], 6, [0, 1, 2, 3, 2, 1]) }); } checkCollisions(); scoreEl.innerHTML = score; };
æµããããã«è¿œå ããæ¹æ³ã«æ³šæããŠãã ããã ã©ã³ãã ãªå€ãèšå®ããããããå€ãããå°ããå Žåã¯æµãè¿œå ããèŠããªããšããã®å³åŽã«è¿œå ããŸãã 瞊座æšã¯ãä¹±æ°ã«ãããã®é«ããšæµã®é«ãã®å·®ãæããããšã«ãã£ãŠç¢ºç«ãããŸãã æµã®ç»åã®é«ãã¯ãããŒãã³ãŒãåããããŠããããããã®é«ãã¯ããã£ãŠããŸãããã®ã³ãŒãã¯äŸãšããŠäœ¿çšãããŸãã
ãããå€ã¯ãé¢æ°
1 - Math.pow(.993, gameTime)
ã«ãã£ãŠæ¯åäžæããŸãã
ããŒã¹ãããŒã¯
ããŒã¹ãããŒã¯ãåŠçããããã«ãå¥ã®å°ããªã©ã€ãã©ãªãŒinput.jsãäœæããŸãã ã ããã¯ãkeyupããã³keydownã€ãã³ããã³ãã©ãŒãè¿œå ããããšã«ãããæŒãããããŒã®ç¶æ ãåçŽã«ä¿åããéåžžã«å°ããªã©ã€ãã©ãªã§ãã
ãã®ã©ã€ãã©ãªã¯ãinput.isDownãšãã1ã€ã®é¢æ°ãæäŸããŸãã 'a'ãªã©ã®åŒæ°ãšããŠåŒæ°ãåãããã®ããŒãæŒãããå Žåã«trueãè¿ããŸãã 次ã®å€ãæž¡ãããšãã§ããŸãã
- ã¹ããŒã¹
- å·Š
- å³
- ã¢ãã
- ããŠã³
ããã§ãããŒã¹ãããŒã¯ãåŠçã§ããŸãã
function handleInput(dt) { if(input.isDown('DOWN') || input.isDown('s')) { player.pos[1] += playerSpeed * dt; } if(input.isDown('UP') || input.isDown('w')) { player.pos[1] -= playerSpeed * dt; } if(input.isDown('LEFT') || input.isDown('a')) { player.pos[0] -= playerSpeed * dt; } if(input.isDown('RIGHT') || input.isDown('d')) { player.pos[0] += playerSpeed * dt; } if(input.isDown('SPACE') && !isGameOver && Date.now() - lastFire > 100) { var x = player.pos[0] + player.sprite.size[0] / 2; var y = player.pos[1] + player.sprite.size[1] / 2; bullets.push({ pos: [x, y], dir: 'forward', sprite: new Sprite('img/sprites.png', [0, 39], [18, 8]) }); bullets.push({ pos: [x, y], dir: 'up', sprite: new Sprite('img/sprites.png', [0, 50], [9, 5]) }); bullets.push({ pos: [x, y], dir: 'down', sprite: new Sprite('img/sprites.png', [0, 60], [9, 5]) }); lastFire = Date.now(); } }
ãã¬ãŒã€ãŒãsãŸãã¯äžç¢å°ãæŒããšããã¬ãŒã€ãŒã瞊座æšã«æ²¿ã£ãŠäžã«ç§»åããŸãã ãã£ã³ãã¹åº§æšç³»ã®å·Šäžé ã«ã¯åº§æšïŒ0,0ïŒãããããããã¬ãŒã€ãŒã®äœçœ®ã倧ãããããšãç»é¢äžã®ãã¬ãŒã€ãŒã®äœçœ®ãå°ãããªããŸãã ä»ã®ãã¹ãŠã®ããŒã«ã€ããŠãåãããšãè¡ããŸããã
app.jsã®å é ã§playerSpeedãå®çŸ©ããããšã«æ³šæããŠãã ããã èšå®ããé床ã¯æ¬¡ã®ãšããã§ãã
// Speed in pixels per second var playerSpeed = 200; var bulletSpeed = 500; var enemySpeed = 100;
playerSpeedã«dtãã©ã¡ãŒã¿ãä¹ç®ããŠããã¬ãŒã å ã移åãããã¯ã»ã«ã®åèšãèšç®ããŸãã æåŸã®æŽæ°ãã1ç§ãçµéããå Žåããã¬ãŒã€ãŒã¯200ãã¯ã»ã«ã0.5ã®å Žåã¯100ãã¯ã»ã«é²ã¿ãŸããããã¯ããã¬ãŒã ã¬ãŒãã«å¿ããäžå®ã®ç§»åé床ãšããŠè¡šç€ºãããŸãã
æåŸã«è¡ãã®ã¯ãããæ¡ä»¶äžã§ã®åŒŸäžžã·ã§ããã§ããã¹ããŒã¹ãæŒãããããã¯æåŸã®ã·ã§ãããã100ããªç§ä»¥äžçºçããŸããã lastFireã¯ã°ããŒãã«å€æ°ã§ãããã²ãŒã ã®ç¶æ ã®äžéšã§ãã ã·ã§ããã®é »åºŠãå¶åŸ¡ããã®ã«åœ¹ç«ã¡ãŸããããããªããšããã¬ãŒã€ãŒã¯ãã¹ãŠã®ãã¬ãŒã ãæ®åœ±ã§ããŸãã ãšãŠãç°¡åã§ãããïŒ
var x = player.pos[0] + player.sprite.size[0] / 2; var y = player.pos[1] + player.sprite.size[1] / 2; bullets.push({ pos: [x, y], dir: 'forward', sprite: new Sprite('img/sprites.png', [0, 39], [18, 8]) }); bullets.push({ pos: [x, y], dir: 'up', sprite: new Sprite('img/sprites.png', [0, 50], [9, 5]) }); bullets.push({ pos: [x, y], dir: 'down', sprite: new Sprite('img/sprites.png', [0, 60], [9, 5]) }); lastFire = Date.now();
æ°ãã匟䞞ã®äœçœ®ãxããã³y座æšã§èšç®ããŸãã ãã¬ã€ã€ãŒã®äœçœ®ã«å ããŠããã¬ã€ã€ãŒã®é«ããšå¹ ã®ååãå ããŠããã¬ã€ã€ãŒãè¹ã®äžå¿ããæã€ããã«ããŸãã

匟䞞ã®æ¹åãç°ãªãããã3ã€ã®åŒŸäžžãè¿œå ããŸãã ããã«ããããã¬ãŒã€ãŒããã©ããã§ããªããããã²ãŒã ãç°¡åã«ãªããŸãã ç®æ¡æžããšã³ãã£ãã£ãåºå¥ããããã«ããdirãããããã£ã«å€ãforwardãããupãããdownããè¿œå ããŸããã
ãšã³ãã£ãã£
ãã¹ãŠã®ãšã³ãã£ãã£ãæŽæ°ããå¿ èŠããããŸãã ãã¬ã€ã€ãŒã®æ¬è³ªãšã匟䞞ãæµãççºã®æ¬è³ªãæã€3ã€ã®ã¢ã¬ã€ããããŸãã
function updateEntities(dt) { // Update the player sprite animation player.sprite.update(dt); // Update all the bullets for(var i=0; i<bullets.length; i++) { var bullet = bullets[i]; switch(bullet.dir) { case 'up': bullet.pos[1] -= bulletSpeed * dt; break; case 'down': bullet.pos[1] += bulletSpeed * dt; break; default: bullet.pos[0] += bulletSpeed * dt; } // Remove the bullet if it goes offscreen if(bullet.pos[1] < 0 || bullet.pos[1] > canvas.height || bullet.pos[0] > canvas.width) { bullets.splice(i, 1); i--; } } // Update all the enemies for(var i=0; i<enemies.length; i++) { enemies[i].pos[0] -= enemySpeed * dt; enemies[i].sprite.update(dt); // Remove if offscreen if(enemies[i].pos[0] + enemies[i].sprite.size[0] < 0) { enemies.splice(i, 1); i--; } } // Update all the explosions for(var i=0; i<explosions.length; i++) { explosions[i].sprite.update(dt); // Remove if animation is done if(explosions[i].sprite.done) { explosions.splice(i, 1); i--; } } }
ããäžåºŠå§ããŸãããããã¬ãŒã€ãŒã®ã¹ãã©ã€ãã¯ãã¹ãã©ã€ãæŽæ°æ©èœãåŒã³åºãã ãã§æŽæ°ãããŸãã ããã«ãããã¢ãã¡ãŒã·ã§ã³ãåæ¹ã«ç§»åããŸãã
匟䞞ãæµãççºã®æ¬¡ã®3ã€ã®ãµã€ã¯ã«ã ããã»ã¹ã¯èª°ã§ãåãã§ããã¹ãã©ã€ããæŽæ°ããåããæŽæ°ãããšã³ãã£ãã£ãã·ãŒã³ã®å¢çãè¶ããå Žåã¯åé€ããŸãã ãã¹ãŠã®ãšã³ãã£ãã£ã移åã®æ¹åãå€æŽããããšã¯ã§ããªããããå¯èŠé åãé¢ããåŸã«ãšã³ãã£ãã£ãä¿åããå¿ èŠã¯ãããŸããã
匟䞞ã®åããæãé£ããã§ãïŒ
switch(bullet.dir) { case 'up': bullet.pos[1] -= bulletSpeed * dt; break; case 'down': bullet.pos[1] += bulletSpeed * dt; break; default: bullet.pos[0] += bulletSpeed * dt; }
bullet.dir = 'up'ã®å Žåã匟䞞ãy軞ã®äžã«ç§»åããŸãã éã«ãdir = 'down'ã®å Žåãããã©ã«ãå€ã§ã¯æšªè»žã«æ²¿ã£ãŠç§»åããŸãã
// Remove the bullet if it goes offscreen if(bullet.pos[1] < 0 || bullet.pos[1] > canvas.height || bullet.pos[0] > canvas.width) { bullets.splice(i, 1); i--; }
次ã«ãç®æ¡æžããšã³ãã£ãã£ãåé€ã§ãããã©ããã確èªããŸãã 匟䞞ã¯ãããã®æ¹åã«ã®ã¿ç§»åãããããäœçœ®ã¯äžç«¯ãäžç«¯ãããã³å³ç«¯ã«å¯ŸããŠãã§ãã¯ãããŸãã
è¡é æåãåé€ããã«ã¯ããã®ãªããžã§ã¯ããé åããåé€ããŠiãæžãããŸãã
è¡çªè¿œè·¡
誰ããæããŠãããã®ã®ããã«ïŒè¡çªè¿œè·¡ïŒ å®éãå°ãªããšãç§ãã¡ã®ã²ãŒã ã§ã¯ãèŠãç®ã»ã©é£ãããããŸããã
远跡ããå¿ èŠãããè¡çªã«ã¯3ã€ã®ã¿ã€ãããããŸãã
- æµãšåŒŸäžž
- æµãšãã¬ã€ã€ãŒ
- ãã¬ãŒã€ãŒãšç»é¢ã®ç«¯
2Dè¡çªã®æ€åºã¯ç°¡åã§ãïŒ
function collides(x, y, r, b, x2, y2, r2, b2) { return !(r <= x2 || x > r2 || b <= y2 || y > b2); } function boxCollides(pos, size, pos2, size2) { return collides(pos[0], pos[1], pos[0] + size[0], pos[1] + size[1], pos2[0], pos2[1], pos2[0] + size2[0], pos2[1] + size2[1]); }
ãããã®2ã€ã®æ©èœã1ã€ã«ãŸãšããããšãã§ããŸãããç§ã«ã¯ãšãŠãèªã¿ãããããã§ãã collidesã¯ãäž¡æ¹ã®ãªããžã§ã¯ãã®äž/å·Šããã³äž/å³é ã®åº§æšãååŸãã亀差ããããã©ããã確èªããŸãã
boxCollidesé¢æ°ã¯ãåèŠçŽ ã®äœçœ®ãšãµã€ãºãæã€é åãåãå ¥ããè¡çªã®ã©ãããŒã§ãã é¢æ°ã§ã¯ã次å ã䜿çšããŠçµ¶å¯Ÿäœçœ®åº§æšãèšç®ããŸãã
ãããŠãå®éã«è¡çªãæ€åºããã³ãŒãã¯æ¬¡ã®ãšããã§ãã
function checkCollisions() { checkPlayerBounds(); // Run collision detection for all enemies and bullets for(var i=0; i<enemies.length; i++) { var pos = enemies[i].pos; var size = enemies[i].sprite.size; for(var j=0; j<bullets.length; j++) { var pos2 = bullets[j].pos; var size2 = bullets[j].sprite.size; if(boxCollides(pos, size, pos2, size2)) { // Remove the enemy enemies.splice(i, 1); i--; // Add score score += 100; // Add an explosion explosions.push({ pos: pos, sprite: new Sprite('img/sprites.png', [0, 117], [39, 39], 16, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], null, true) }); // Remove the bullet and stop this iteration bullets.splice(j, 1); break; } } if(boxCollides(pos, size, player.pos, player.sprite.size)) { gameOver(); } } }
è¡çªæ€åºã¯ææ°é¢æ°çã§ããã·ãŒã³äžã®åãšã³ãã£ãã£éã®è¡çªããã§ãã¯ããå¿ èŠãããããã§ãã ã²ãŒã ã§ã¯ããã¹ãŠã®æµããã¹ãŠã®åŒŸäžžã«å¯ŸããŠãã¹ãããå¿ èŠããããŸãã æµã®é åãç¹°ãè¿ããå匟䞞ã®ãµã€ã¯ã«ã§è¡çªããã§ãã¯ããŸãã
BoxCollidesãåŒã³åºãããæµãšåŒŸäžžã®äœçœ®ãšãµã€ãºãé¢æ°ã«æž¡ããŸããé¢æ°ãtrueãè¿ããšã次ã®ããšãèµ·ãããŸãã
- æµã®é€å»
- ãã€ã³ããè¿œå ãã
- ãšã¯ã¹ãããŒãžã§ã³ãšãã»ã³ã¹ãè¿œå
- 匟䞞ã®é€å»
- æµãååšããªããããå éšã®åŒŸäžžãµã€ã¯ã«ãåæ¢ãã
ççºã®çºçæ¹æ³ã«æ³šæããŠãã ããã ããããã£posããã³ã¹ãã©ã€ãã䜿çšããŠãªããžã§ã¯ããäœæããã¹ãã©ã€ããããã§ã¢ãã¡ãŒã·ã§ã³çšã«13ãã¬ãŒã ãæå®ããŸãã ãŸããã¢ãã¡ãŒã·ã§ã³ã1åã ãåçãããããã«ãonceãã©ã¡ãŒã¿ãŒãtrueã§ããããšã瀺ããŸãã
次ã®3è¡ãèŠãŠãã ããã
if(boxCollides(pos, size, player.pos, player.sprite.size)) { gameOver(); }
ããã§ã¯ããã¬ã€ã€ãŒãšæµã®è¡çªããã§ãã¯ããè¡çªãããã°ã²ãŒã ãªãŒããŒã§ãã
æåŸã«ãcheckPlayerBoundsã«ã€ããŠèª¬æããŸãããã
function checkPlayerBounds() { // Check bounds if(player.pos[0] < 0) { player.pos[0] = 0; } else if(player.pos[0] > canvas.width - player.sprite.size[0]) { player.pos[0] = canvas.width - player.sprite.size[0]; } if(player.pos[1] < 0) { player.pos[1] = 0; } else if(player.pos[1] > canvas.height - player.sprite.size[1]) { player.pos[1] = canvas.height - player.sprite.size[1]; } }
ãã¬ãŒã€ãŒãããããè¶ ããªãããã«ãã座æšã0ããã³canvas.width / canvas.heightå ã«ç¶æããŸãã
ã¬ã³ããªã³ã°
ã»ãŒå®äºã§ãïŒ ããã§ã¯ãåãã¬ãŒã ã®ã·ãŒã³ã衚瀺ããããã«ã²ãŒã ã«ãŒãã«ãã£ãŠåŒã³åºãããã¬ã³ããŒé¢æ°ãå®çŸ©ããå¿ èŠããããŸãã ããã¯æ¬¡ã®ãããªãã®ã§ãã
// Draw everything function render() { ctx.fillStyle = terrainPattern; ctx.fillRect(0, 0, canvas.width, canvas.height); // Render the player if the game isn't over if(!isGameOver) { renderEntity(player); } renderEntities(bullets); renderEntities(enemies); renderEntities(explosions); }; function renderEntities(list) { for(var i=0; i<list.length; i++) { renderEntity(list[i]); } } function renderEntity(entity) { ctx.save(); ctx.translate(entity.pos[0], entity.pos[1]); entity.sprite.render(ctx); ctx.restore(); }
æåã«è¡ãããšã¯ãèæ¯ã®æç»ã§ãã ctx.createPatternã䜿çšããŠinité¢æ°ã§å°åœ¢ã®èæ¯ãäœæããfillStyleãèšå®ããŠfillRecté¢æ°ãåŒã³åºããŠèæ¯ãã¬ã³ããªã³ã°ããŸãã
次ã«ããã¬ã€ã€ãŒããã¹ãŠã®åŒŸäžžããã¹ãŠã®æµãšççºãæããŸãã renderEntitesã¯ããšã³ãã£ãã£ã®é åãã«ãŒãããŠæç»ããŸãã renderEntityã¯ããã£ã³ãã¹å€æã䜿çšããŠãªããžã§ã¯ããç»é¢ã«é 眮ããŸãã ctx.saveã¯çŸåšã®å€æãä¿åããctx.restoreã¯ããã埩å ããŸãã
ã¹ãã©ã€ãã¬ã³ããŒé¢æ°ãèŠããšãã¹ãã©ã€ããäœçœ®ïŒ0,0ïŒã«ããããšãããããŸãããctx.translateãåŒã³åºããšããªããžã§ã¯ããæ£ããå Žæã«ç§»åããŸãã
ã²ãŒã ãªãŒããŒ
æåŸã«è¡ãå¿ èŠãããã®ã¯ãã²ãŒã ã®çµäºãåŠçããããšã§ããã²ãŒã ãçµäºããããã®ç»é¢ã衚瀺ããé¢æ°gameOverãšãã²ãŒã ãåã³éå§ããå¥ã®ãªã»ãããå®çŸ©ããå¿ èŠããããŸãã
// Game over function gameOver() { document.getElementById('game-over').style.display = 'block'; document.getElementById('game-over-overlay').style.display = 'block'; isGameOver = true; } // Reset game to original state function reset() { document.getElementById('game-over').style.display = 'none'; document.getElementById('game-over-overlay').style.display = 'none'; isGameOver = false; gameTime = 0; score = 0; enemies = []; bullets = []; player.pos = [50, canvas.height / 2]; };
gameOverã¯ãindex.htmlã§å®çŸ©ãããç»é¢ã«ãGane Overããšè¡šç€ºããããrestartããã¿ã³ã衚瀺ãããŸãã
ãªã»ããã¯ããã¹ãŠã®ã²ãŒã ç¶æ å€ãåæã«èšå®ããã²ãŒã çµäºç»é¢ãé衚瀺ã«ããŠãã²ãŒã ãåèµ·åããŸãã
æçµçãªèã
ãã®èšäºã§ã¯å€ãã®ããšãåŠã¶å¿ èŠããããŸãããã²ãŒã ã®äœæãããã»ã©é£ãããªãããšã瀺ãããã«ãããªãåçŽãªéšåã«åå²ããããšãé¡ã£ãŠããŸãã
äœã¬ãã«ã®Canvas APIã䜿çšããŠãæè¿ã®2Dã²ãŒã ã®äœæãããã«ç°¡åããæããã«ããããšã«çŠç¹ãåœãŠãŸããããã¡ãããæ¬åœã«è€éãªãã®ãäœæããããã«äœ¿çšã§ããããã€ãã®ã²ãŒã ãšã³ãžã³ããããŸããå€ãã®ã²ãŒã ãšã³ãžã³ã¯ããšã³ãã£ãã£ã®ã€ã³ã¿ãŒãã§ã€ã¹ãæšæºåããŠãããåã¿ã€ãã®ã¬ã³ããªã³ã°ããã³æŽæ°é¢æ°ãå®çŸ©ããã ãã§ãã·ãŒã³ãããŒãžã£ãŒã¯ãã¹ãŠã®ãã¬ãŒã ã®ãã¹ãŠã®ãšã³ãã£ãã£ã«å¯ŸããŠããããèªåçã«åŒã³åºããŸãã