Successfully reported this slideshow.
Your SlideShare is downloading. ×

Html canvas shooting_and_performanceup

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
Arctic.js
Arctic.js
Loading in …3
×

Check these out next

1 of 83 Ad
Advertisement

More Related Content

Slideshows for you (20)

Viewers also liked (20)

Advertisement

Html canvas shooting_and_performanceup

  1. 1. HTML5 Canvasで シューティングゲーム ~ 作り方、パフォーマンス、ツール ~
  2. 2. 自己紹介 名前:  宗定 洋平(@yoheiMune)  YoheiM.NET(http://www.yoheim.net/)ブロガー 趣味:  テニス、プログラミング(最近は、HTML5, Objective-Cが中心)
  3. 3. 今日お話するネタはこちらです http://www.yoheim.net/labo/html5/canvasShooting.html
  4. 4. 今日お話するネタはこちらです "Canvas de Shooting" このゲームの作り方の 解説を通して、 HTML5でのゲーム制作する際の技術ポイントを 少しでもお伝え出来ればと思います!
  5. 5. Sencha Touch
  6. 6. NO Sencha Touch
  7. 7. enchant.js
  8. 8. NO enchant.js
  9. 9. ライブラリ無しで    いきます!!
  10. 10. スクラッチでゲームを作る上で、 どんな仕組みで、 どんな技術を使って、 どんな事を考えて、 作って行くのかをお伝えできればと思います。
  11. 11. ライブラリを使えば、 仕組みや技術要素は隠蔽され、 簡単に出来ます。 でも今回は、仕組みを学ぶ機会を提供したいと 考えています。
  12. 12. では目次へ
  13. 13. Agenda 1. HTML5 Canvasで今回利用する技術紹介 2. 実装内容のご紹介 3. Canvasのパフォーマンス向上 4. ツール紹介 5. 最後に
  14. 14. Agenda 1. HTML5 Canvasで今回利用する技術紹介 Canvasの技術を4つ紹介します
  15. 15. 1. Canvasの準備 // Canvasエレメントを取得 var canvas = document.getElementById("myCanvas"); // 描画命令を行う為のコンテキストを取得 var ctx = canvas.getContext("2d");
  16. 16. 2. 色の指定 // 塗りつぶしの色を指定 ctx.fillStyle = "rgb(0,255,0)"; // 緑 ctx.fillStyle = "rgb(255,255,0)"; // 黄 ctx.fillStyle = "rgb(255,0,0)"; // 青 利用用途:壁色の指定、ショットの色の指定
  17. 17. 3. 図形の描画と、画像の描画 // 四角形の描画 ctx.fillRect(posX, posY, width, height); // 画像の描画 ctx.drawImage(imageObject, posX, posY); 利用用途:ショットの描画、自機や敵機の描画
  18. 18. 4. 描画内容のクリア // Canvas上の描画内容をクリア ctx.clearRect(posX, posY , width, height); 利用用途:MainLoopで再描画する時に使う
  19. 19. えっ!? これだけ?
  20. 20. はい! これだけです。
  21. 21. 続いて、 実装内容の 紹介です
  22. 22. Agenda 1. HTML5 Canvasで今回利用する技術紹介 2. 実装内容のご紹介 3. Canvasのパフォーマンス向上 4. ツール紹介 5. 最後に
  23. 23. まずは 初期処理です
  24. 24. 1. 初期化処理 // Canvasを取得 var canvas = document.getElementById("myCanvas"); // Canvasの大きさを画面に合わせる canvas.width = window.innerWidth || 800; canvas.height = window.innerHeight * 0.95 || 400; // contextを生成する var ctx = canvas.getContext("2d");
  25. 25. 1. 初期化処理 // 使う画像を、事前に読み込みます img_plane = new Image(); img_plane.src = "img/plane.png"; img_plane.onload = function () { // 読み込み終了した状態を保存 imageLoadDone = true; }
  26. 26. 次に メインループ です
  27. 27. 2. MainLoopを開始する // 描画や当たり判定を行うMainLoopをつくる var gameMainLoop = function () { // 今回は、mainLoop内で再描画します ctx.clearRect(0, 0, w, h); } // setIntervalでゲームを開始! var timer = setInterval(gameMainLoop, 50);
  28. 28. ここまでで、 以下のような画面が出来ます 真っ暗です。でも動いてます。
  29. 29. 自機を動かします
  30. 30. 3. 自機を動かす ① // まずは自機を表すオブジェクトを作ります var plane = { img : img_plane, posX : 10, posY : (h - img_plane.height) / 2, life : 3 }
  31. 31. 3. 自機を動かす ② // 自機を動かす為に、keypressイベントを監視しま す document.onkeypress = function (e) { // 例えば自機を上に動かす場合 if (e.keyCode == 101/*E*/) { plane.posY -= 5; } }
  32. 32. 3. 自機を動かす ③ // そしてメインループ内で、自機を描画します var gameMainLoop = function () { // 自機を描画する ctx.drawImage(plane.img, plane.posX, plane.posY); }
  33. 33. 自機で 攻撃してみます
  34. 34. 4. 自機がショットを撃つ ① // 自機ショットを保持する配列を定義します var plane_balls = []; // 自機ショットは、以下のオブジェクト型と // します var ball = { radius : 5, posX : 10, posY : 10, speed : 10 }
  35. 35. 4. 自機がショットを撃つ ② // "K"ボタンが押されたら、ショットを生成 document.onkeypress = function () { if (e.keyCode == 107/*K*/) { var newBall = { radius : 5, posX : plane.posX, posY : plane. posY, speed : 10 }; plane_balls.push(newBall); } }
  36. 36. 4. 自機がショットを撃つ ③ // mainLoop内で、speed分だけ移動させて描画する var gameMainLoop = function () { for (var i = 0; i < plane_balls.length; i++) { var ball = plane_balls[i]; ball.posX += ball.speed; ctx.fillStyle = "rgba(200,0,0,1)"; ctx.fillRect(ball.posX, ball.posY, ball.radius, ball.radius); } }
  37. 37. これで自機を動かして、 攻撃できるようになりました
  38. 38. 次に、敵機を 出現させます
  39. 39. あっでも、 敵機の実装は、 自機やショットの実装説 明と同じなので、 パスします!!
  40. 40. 5. 敵機を出現させる ① // まずは敵機を保持する配列を定義します var enemies = []; // 敵機は、以下のオブジェクト定義とします var enemy = { img : img, /* 今回は2種類の画像を使う */ posX : x, posY : y, speed : s };
  41. 41. 5. 敵機を出現させる ② // 敵機はランダムに出現させます function createEnemy () { var num =(Math.floor((Math.random()*100)%100); if (num < 5) { var s = num; var i = (num%2==0 ? imgE01 : imgE02); var y = canvasHeght * Math.random(); var x = canvasWidth; var enemy = {img:i, posX:x, posY:y, speed:s}; enemies.push(enemy); } }
  42. 42. 5. 敵機を出現させる ③ // MainLoop内でspeed分、左に移動させて描画 var gameMainLoop = function () { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var enemy.posX -= enemy.speed; ctx.drawImage(enemy.img, enemy.posX, enemy.posY); } }
  43. 43. これで敵機が左に向かって、 動くようになりました
  44. 44. 当たり判定です
  45. 45. 6.当たり判定を行う ① 当たり判定は、物体と物体が重なったか、否かを 判断します。 重なっている場合には、2つの物体同士が当たって いると考えます。 Hit!!
  46. 46. 6.当たり判定を行う ② 例えば自機ショットと敵機の当たり判定の場合を 説明します。 x座標 = PosX+radius y座標 = PosY x座標 = PosX y座標 = PosY x座標 = PosX y座標 = PosY radius x座標 = PosX+radius y座標 = PosY+radius x座標 = PosX y座標 = PosY + img.height
  47. 47. 6.当たり判定を行う ③ // ソースコードではこんな感じです var b = plane_balls[i]; var e = enemies[j]; if ((b.posX + b.radius) >= e.posX /* x 座標 */ && b.posY <= e.posY+e.img.width /* y座標下 */ && b.posY >= e.posY) { /* y座標上 */ delete plane_balls[i]; delete enemies[j]; }
  48. 48. 6. 当たり判定を行う ④ // 最後にdeleteした要素をお掃除する // 例えば自機ショットの場合 var new_plane_balls = []; for (var i = 0; i < plane_balls.length; i++) { if (plane_balls[i]) new_plane_balls.push(plane_balls[i]); } plane_balls = new_plane_balls;
  49. 49. 7.GameOver or GameClear ① // ボスを倒すか、自機が倒されたら終わり if (deadBoss() || deadSelf()) { // mainLoopを終了させる clearTimeout(timer); // メッセージを表示する if (deadBoss()) alert("CONGURATURATION!!"); else alert("GAME OVER"); }
  50. 50. これでシューティングゲームが できました(*゚▽゚)ノ
  51. 51. 作ったら速く動かしたい!!
  52. 52. 続いて、 Canvas描画処理手法を 紹介をします。 3. Canvasのパフォーマンス向上
  53. 53. Cavasの高速化 1、requestAnimationFrameの利用 2、プレレンダリング 3、複数のCanvasを使う 4、描画範囲は最小に 5、ステート変更は最小限に 6、浮動点小数より整数値
  54. 54. 1、requestAnimationFrameの利用 今までの説明では、setIntervalを用いていました が、requestAnimationFrameを用いると、ブラウザ が最適なFPSで描画してくれます。 function gameMainLoop() { clearCnavas(); drawEnemies(); drawSelf(); ・・・ handle = requestAnimationFrame(gameMainLoop); }
  55. 55. 1、requestAnimationFrameの利用 requestAnimationFrameを利用する上で、 以下の注意点があります。 ・ベンダープレフィックスが必要な場合も ・サポート対象外のブラウザ(スマホは全滅に近い) 参考URL: ・http://www.w3.org/TR/animation-timing/ ・http://caniuse.com/#search=requestanimation
  56. 56. 2、プレレンダリング 画像描画や複雑な図形の描画を、事前にフレーム 外のCanvasに描画しておき、実際に描画する際に Canvasにプレ描画したCanvasに流し込む事で、高 速化します。 // 通常の描画 function gameMainLoop() { drawEnemy(xxx); requestAnimationFrame(gameMainLoop); }
  57. 57. 2、プレレンダリング // プレレンダリング var m_canvas = document.createElement('canvas'); m_canvas.width = 32; m_canvas.height = 32; var m_context = m_canvas.getContext(‘2d’); drawEnemy(xxx); // メインループでの描画 function gameMainLoop() { context.drawImage(m_canvas, 0, 0); requestAnimationFrame(render); }
  58. 58. 2、プレレンダリング
  59. 59. 3、複数のCanvasを使う 描画の変更タイミングが異なるオブジェクトは、異 なるCanvasに描画することで、各Canvasの描画速 度を向上させることが出来ます。 Main Contents Canvas Backgound Canvas
  60. 60. 3、複数のCanvasを使う パフォーマンスの違いは以下の感じです。
  61. 61. 4、描画範囲は最小に Canvasでフレーム毎に全体を描画するより、変更 個所のみ描画し直す方が、処理が速く終わりま す。 これは当たり前な感じなので、詳細説明はパ ス!!
  62. 62. 5、ステートの変更は最小に Canvasの描画色や線の太さなどのCanvasのス テートの変更回数を減らす事で、処理速度を向上 させることが出来ます。 // Bad for (var i = 0; i < STRIPES; i++) { context.fillStyle = (i % 2 ? COLOR1 : COLOR2); context.fillRect(i * GAP, 0, GAP, 480); }
  63. 63. 5、ステートの変更は最小に // Good context.fillStyle = COLOR1; for (var i = 0; i < STRIPES/2; i++) { context.fillRect((i*2) * GAP, 0, GAP, 480); } context.fillStyle = COLOR2; for (var i = 0; i < STRIPES/2; i++) { context.fillRect((i*2+1) * GAP, 0, GAP, 480); }
  64. 64. 5、ステートの変更は最小に パフォーマンスの違いは以下のイメージです。
  65. 65. 6、浮動点小数より整数値を HTML5 Canvasではサブピクセルレンダリングが サポートされています。 整数以外で座標で描画すると、線が滑らかになる ように自動的にアンチエイリアスがかかります。
  66. 66. 6、浮動点小数より整数値を Whole pixel bunny vs sub-pixel bunny 参考URL:http://seb.ly/2011/02/html5-canvas-sprite-optimisation/
  67. 67. 6、浮動点小数より整数値を 滑らかな描画を意図しない場合には、整数に整形 する処理を行うべきです。 // 例 var x = Math.floor(baseX * 0.9); var y = Math.round(baseY * 1.02); また似た例として、ShadowやBlurをCanvas上で多 用すると、処理が非常に重たくなります。
  68. 68. 6、浮動点小数より整数値を
  69. 69. パフォーマンスのお話は、 以上です。
  70. 70. と、ここまでライブラリなしで実 装することを説明してきました。
  71. 71. 大変だなって感じましたか? または、色々と考えることが あって面白いなって感じました か? (私は後者ですw。)
  72. 72. ライブラリ(例えばenchant.js)を つかうと、こんなに楽!!とい う点をお伝えします。
  73. 73. enchant.jsを用いた場合
  74. 74. ゲームの前処理 window.onload = function() { enchant(); // サイズを決めるのはこれだけ。HTML要素も要らない。 var game = new Game(320,320); // 画像読み込み完了か否かはライブラリが判断してくれる。 game.preload('fig1.png','fig2.png'); game.onload = function(){/*ゲーム開始時の処理*/} game.start(); } // ゲームループの実装は必要ない // ループ毎のCanvasのお掃除もない(Canvasではない為)
  75. 75. ゲーム中 part1 要素の追加、フレーム毎の処理 // 登場キャラクターの追加(オブジェクト指向で便利) // アーキテクチャ設計は既にされていて、考える必要なし var enemy = new Sprite(32,32); enemy.image = game.assets['enemy01.png']; enemy.x = enemy.y = 50; game.rootScene.addChild(enemy); // フレーム毎の処理は、追加した要素毎に記載 enemy.addEventListener('enterframe',function(){ this.x -= 2; });
  76. 76. ゲーム中 part2 // Sprite毎のタップイベントも簡単に扱える。 // 位置特定を自動でしてくれるなんていい感じ。 plane.addEventListener("touchend", function(param){ plane.x = param.x; plane.y = param.y; }); // 衝突判定はたったこれだけ。 // 面倒な条件判定はライブラリがやってくれる。 if(ball1.intersect(enemy1)){ game.rootScene.removeChild(enemy1); }
  77. 77. さて続いては、開発効率を上げ るために使っているツールを紹 介させて頂きます。 4. ツール紹介
  78. 78. 開発効率を上げるツールたち ・Scss、Sass ・qUnit ・grunt.js ・jsLint, jsHint ・HTML5 Boiler Plate ・Chrome Developer Tool ・jsperf ・Where can I use.
  79. 79. 最後に。
  80. 80. 最後までご清聴頂きありがとうございました。 最後に、何故ライブラリなしで実装する事をここで話そ うと思ったのか話できれば思います。 私はライブラリなしのスクラッチで開発する事が好きで す。 (仕事では開発スピード、サポートブラウザを考慮してライブラリを使いますけど)
  81. 81. スクラッチで開発する理由は以下です。 ・仕組みを知りたい ・仕組みを作れる人になりたい ・ないところから何かを作る(0から1を生み出す)ことを 生き甲斐にしたい 何か新しいことが世の中にでた場合に、その最前線で 戦うのに、ライブラリは無いかもしれない。
  82. 82. 人の力を借りずとも、自分が今まで経験/学習したこと で新しいことに挑戦していく。 そのためには、一見無駄かと思えるスクラッチでの開 発でたくさんの困難に出会い、それに立ち向かう。 その経験をたくさん積む事ができるのがスクラッチ開 発であり、将来に繋がる活動だと思っています。
  83. 83. まだまだやりたい事は一杯あるけど、力がないと感じる 今日この頃。 明日の自分は今日よりも1歩進んでいて、 1週間後の自分はだいぶ先に進んでいて、 1ヶ月後の自分は全くの別人となる。 そんな目標をもとに、活動しています。 これからもたくさんの方々に接したいと思います。あと の懇親会でもヨロシクおねがいしますw。 今日はお時間頂きありがとうございました。

×