ゲーム開発は、ソフトウェア開発業界に絶えず挑戦している、より興味深い高度なプログラミング手法の1つです。
ゲームの開発に使用されるプログラミングプラットフォームは数多くあり、それらをプレイするためのデバイスは多数ありますが、Webブラウザーでゲームをプレイする場合は、Flashベースの開発が依然として先導しています。
Flashベースのゲームを HTML5 Canvasテクノロジーにより、モバイルブラウザーでも再生できるようになります。そして、Apache Cordovaを使用すると、 熟練したWeb開発者 それらをクロスプラットフォームのモバイルゲームアプリに簡単にラップできます。
の人々 CreateJS それ以上のことを始めました。
EaselJS 、CreateJSのスイートの一部であるため、HTML5Canvasでの描画が簡単になります。高性能と数千の要素を備えたカスタムデータ視覚化を構築することを想像してみてください。スケーラブルベクターグラフィック(SVG)は、DOM要素を使用するため、適切な選択ではありません。約600のDOM要素で、初期レンダリング、再描画、およびアニメーションが高価な操作になると、ブラウザーは圧倒されます。 HTML5 Canvasを使用すると、これらの問題を簡単に回避できます。キャンバスの描画は紙の上のインクのようなものであり、DOM要素とそれに関連するコストはありません。
つまり、Canvasベースの開発では、要素を分離し、それらにイベントと動作を付加する際に、より注意を払う必要があります。 EaselJSが助けになります。個々の要素を処理しているかのようにコーディングして、EaselJSライブラリにマウスオーバー、クリック、および衝突を処理させることができます。
SVGベースのコーディングには1つの大きな利点があります。SVGには古い仕様があり、開発で使用するためにSVGアセットをエクスポートする設計ツールがたくさんあるため、設計者と開発者の協力がうまく機能します。などの人気のあるライブラリ D3.JS 、およびのような新しい、より強力なライブラリ SnapSVG 、テーブルにたくさん持ってきてください。
デザイナーから開発者へのワークフローがSVGを使用する唯一の理由である場合は、次の拡張機能を検討してください。 アドビイラストレーター (AI)AIで作成された形状からコードを生成します。私たちのコンテキストでは、このような拡張機能はEaselJSコードまたはProcessingJSコードを生成します。どちらもHTML5Canvasベースのライブラリです。
結論として、新しいプロジェクトを開始する場合、SVGを使用する理由はもうありません!
SoundJS CreateJSスイートの一部です。 HTML5オーディオ仕様のシンプルなAPIを提供します。
Ruby onRailsのしくみ
PreloadJS ビットマップ、サウンドファイルなどのアセットをプリロードするために使用されます。他のCreateJSライブラリと組み合わせて使用するとうまく機能します。
EaselJS、SoundJS、およびPreloadJSを使用すると、JavaScriptの忍者にとってゲーム開発が非常に簡単になります。そのAPIメソッドは、Flashベースのゲーム開発を使用したことのある人なら誰でも知っています。
「これはすべて素晴らしいです。しかし、一連のゲームをFlashからHTML5に変換する開発者のチームがある場合はどうなるでしょうか。このスイートでそれを行うことは可能ですか?」
答え: 「はい。ただし、すべての開発者がJediレベルである場合に限ります!」
さまざまなスキルセット開発者のチームがいる場合(よくあることですが)、CreateJSを使用して、スケーラブルでモジュール式のコードを期待するのは少し怖いかもしれません。 CreateJSスイートを AngularJS ?最良で最も採用されているフロントエンドJSフレームワークを導入することで、このリスクを軽減できますか?
はい 、およびこのHTML5 Canvasゲームのチュートリアルでは、CreateJSとAngularJSを使用して基本的なゲームを作成する方法を説明します。
AngularJSは、開発チームが次のことを行えるようにすることで、複雑さを大幅に軽減します。
剛体のダイナミクスチュートリアル
私のように、あなたが「いじくり回し」または触覚学習者である場合は、次のコードを取得する必要があります。 GitHub 学習を開始します。私の提案は、チェックインを確認し、CreateJSコードにAngularJSの優れた機能を追加することのメリットを得るために行った手順を理解することです。
まだ行っていない場合は、インストールする必要があります nodeJS このデモを実行する前に。
AngularJSシードプロジェクトを作成した後、またはからダウンロードした後 GitHub 、実行npm install
すべての依存関係をアプリフォルダーにダウンロードします。
アプリケーションを実行するには、npm start
を実行します同じフォルダからhttp://localhost:8000/app/#/view1
に移動しますお使いのブラウザで。ページは下の画像のようになります。
CreateJSライブラリ参照をAngularJSシードプロジェクトに追加します。 CreateJSスクリプトがAngularJSの後に含まれていることを確認してください。
次に、アプリケーションをクリーンアップします。
… Angular seed app: v … view2
を削除します次の行を削除して、app.js
のモジュール
myApp.view2,
これまでAngularJSを使用したことがなく、AngularJSディレクティブに精通していない場合は、 このチュートリアルを確認してください 。 AngularJSのディレクティブは、HTMLにいくつかの新しいトリックを教える方法です。これらはフレームワークで最もよく考えられた機能であり、AngularJSを強力で拡張可能にします。
特殊なDOM機能またはコンポーネントが必要な場合は、オンラインで検索してください。のような場所ですでに利用可能である可能性が高いです Angularモジュール 。
次に行う必要があるのは、EaselJSの例を実装する新しいAngularJSディレクティブを作成することです。 /app/view1/directives/spriteSheetRunner.js
にある新しいファイルにspriteSheetRunnerという新しいディレクティブを作成します。
angular.module('myApp.directives', []) .directive('spriteSheetRunner', function () { 'use strict'; return { restrict : 'EAC', replace : true, scope :{ }, template: '', link: function (scope, element, attribute) { var w, h, loader, manifest, sky, grant, ground, hill, hill2; drawGame(); function drawGame() { //drawing the game canvas from scratch here //In future we can pass stages as param and load indexes from arrays of background elements etc if (scope.stage) { scope.stage.autoClear = true; scope.stage.removeAllChildren(); scope.stage.update(); } else { scope.stage = new createjs.Stage(element[0]); } w = scope.stage.canvas.width; h = scope.stage.canvas.height; manifest = [ {src: 'spritesheet_grant.png', id: 'grant'}, {src: 'sky.png', id: 'sky'}, {src: 'ground.png', id: 'ground'}, {src: 'hill1.png', id: 'hill'}, {src: 'hill2.png', id: 'hill2'} ]; loader = new createjs.LoadQueue(false); loader.addEventListener('complete', handleComplete); loader.loadManifest(manifest, true, '/app/assets/'); } function handleComplete() { sky = new createjs.Shape(); sky.graphics.beginBitmapFill(loader.getResult('sky')).drawRect(0, 0, w, h); var groundImg = loader.getResult('ground'); ground = new createjs.Shape(); ground.graphics.beginBitmapFill(groundImg).drawRect(0, 0, w groundImg.width, groundImg.height); ground.tileW = groundImg.width; ground.y = h - groundImg.height; hill = new createjs.Bitmap(loader.getResult('hill')); hill.setTransform(Math.random() * w, h - hill.image.height * 4 - groundImg.height, 4, 4); hill.alpha = 0.5; hill2 = new createjs.Bitmap(loader.getResult('hill2')); hill2.setTransform(Math.random() * w, h - hill2.image.height * 3 - groundImg.height, 3, 3); var spriteSheet = new createjs.SpriteSheet({ framerate: 30, 'images': [loader.getResult('grant')], 'frames': {'regX': 82, 'height': 292, 'count': 64, 'regY': 0, 'width': 165}, // define two animations, run (loops, 1.5x speed) and jump (returns to run): 'animations': { 'run': [0, 25, 'run', 1.5], 'jump': [26, 63, 'run'] } }); grant = new createjs.Sprite(spriteSheet, 'run'); grant.y = 35; scope.stage.addChild(sky, hill, hill2, ground, grant); scope.stage.addEventListener('stagemousedown', handleJumpStart); createjs.Ticker.timingMode = createjs.Ticker.RAF; createjs.Ticker.addEventListener('tick', tick); } function handleJumpStart() { grant.gotoAndPlay('jump'); } function tick(event) { var deltaS = event.delta / 1000; var position = grant.x 150 * deltaS; var grantW = grant.getBounds().width * grant.scaleX; grant.x = (position >= w grantW) ? -grantW : position; ground.x = (ground.x - deltaS * 150) % ground.tileW; hill.x = (hill.x - deltaS * 30); if (hill.x hill.image.width * hill.scaleX <= 0) { hill.x = w; } hill2.x = (hill2.x - deltaS * 45); if (hill2.x hill2.image.width * hill2.scaleX <= 0) { hill2.x = w; } scope.stage.update(event); } } } });
ディレクティブが作成されたら、/app/app.js
を更新してアプリに依存関係を追加します以下のように:
'use strict'; // Declare app level module which depends on views, and components angular.module('myApp',[ 'ngRoute', 'myApp.view1', 'myApp.version', 'myApp.services', 'myApp.uiClasses', 'myApp.directives']) .config(['$routeProvider', function($routeProvider) { $routeProvider.otherwise({redirectTo: '/view1'}); }]);
index.html
にディレクティブコードを含めますspriteSheetRunner.js
への参照を追加します。
app/view/view1.html
準備がほぼ整いました!ゲームアセットをアプリフォルダーにコピーします。画像を用意しましたので、お気軽にダウンロードしてアプリ/アセットフォルダに保存してください。
最後のステップとして、新しく作成したディレクティブをページに追加します。これを行うには、loaderSvc.js
を変更しますファイルを作成し、それをワンライナーにします。
/app/view1/services
アプリケーションを起動すると、ランナーが動き始めます:)
scala(プログラミング言語)
これが最初のAngularJSまたは最初のCreateJSアプリケーションである場合は、お祝いしてください。本当にクールなものを作成しました。
AngularJSのサービスは、主にコードとデータを共有するために使用されるシングルトンです。サービスを使用して、アプリケーション全体で「ゲームアセット」を共有します。 AngularJSサービスの詳細については、 AngularJSドキュメント 。
AngularJS開発サービス すべてのアセットを1か所でロードおよび管理するための効果的なメカニズムを提供します。アセットの変更はサービスの個々のインスタンスに伝播されるため、コードの保守がはるかに簡単になります。
//app/view1/services/loaderSvc.js myServices.service('loaderSvc', function () { var manifest = [ {src: 'spritesheet_grant.png', id: 'grant'}, {src: 'sky.png', id: 'sky'}, {src: 'ground.png', id: 'ground'}, {src: 'hill1.png', id: 'hill'}, {src: 'hill2.png', id: 'hill2'} ], loader = new createjs.LoadQueue(true); this.getResult = function (asset) { return loader.getResult(asset); }; this.getLoader = function () { return loader; }; this.loadAssets = function () { loader.loadManifest(manifest, true, '/app/assets/'); }; });
という名前の新しいJSファイルを作成しますあなたのapp.js
フォルダ。
myApp.services
AngularJSでは、使用しているサービスを登録する必要があります。これを行うには、'use strict'; // Declare app level module which depends on views, and components angular.module('myApp',[ 'ngRoute', 'myApp.view1', 'myApp.version', 'myApp.services', 'myApp.directives']) .config(['$routeProvider', function($routeProvider) { $routeProvider.otherwise({redirectTo: '/view1'}); }]); var myServices = angular.module('myApp.services', []);
を更新しますapp/view1/directives/spriteSheetRunner.js
への参照を含めるファイル。
angular.module('myApp.directives', []) .directive('spriteSheetRunner', ['loaderSvc', function (loaderSvc) { 'use strict'; return { restrict : 'EAC', replace : true, scope :{ }, template: '', link: function (scope, element, attribute) { var w, h, manifest, sky, grant, ground, hill, hill2; drawGame(); function drawGame() { //drawing the game canvas from scratch here //In future we can pass stages as param and load indexes from arrays of background elements etc if (scope.stage) { scope.stage.autoClear = true; scope.stage.removeAllChildren(); scope.stage.update(); } else { scope.stage = new createjs.Stage(element[0]); } w = scope.stage.canvas.width; h = scope.stage.canvas.height; loaderSvc.getLoader().addEventListener('complete', handleComplete); loaderSvc.loadAssets(); } function handleComplete() { sky = new createjs.Shape(); sky.graphics.beginBitmapFill(loaderSvc.getResult('sky')).drawRect(0, 0, w, h); var groundImg = loaderSvc.getResult('ground'); ground = new createjs.Shape(); ground.graphics.beginBitmapFill(groundImg).drawRect(0, 0, w + groundImg.width, groundImg.height); ground.tileW = groundImg.width; ground.y = h - groundImg.height; hill = new createjs.Bitmap(loaderSvc.getResult('hill')); hill.setTransform(Math.random() * w, h - hill.image.height * 4 - groundImg.height, 4, 4); hill.alpha = 0.5; hill2 = new createjs.Bitmap(loaderSvc.getResult('hill2')); hill2.setTransform(Math.random() * w, h - hill2.image.height * 3 - groundImg.height, 3, 3); var spriteSheet = new createjs.SpriteSheet({ framerate: 30, 'images': [loaderSvc.getResult('grant')], 'frames': {'regX': 82, 'height': 292, 'count': 64, 'regY': 0, 'width': 165}, // define two animations, run (loops, 1.5x speed) and jump (returns to run): 'animations': { 'run': [0, 25, 'run', 1.5], 'jump': [26, 63, 'run'] } }); grant = new createjs.Sprite(spriteSheet, 'run'); grant.y = 35; scope.stage.addChild(sky, hill, hill2, ground, grant); scope.stage.addEventListener('stagemousedown', handleJumpStart); createjs.Ticker.timingMode = createjs.Ticker.RAF; createjs.Ticker.addEventListener('tick', tick); } function handleJumpStart() { grant.gotoAndPlay('jump'); } function tick(event) { var deltaS = event.delta / 1000; var position = grant.x + 150 * deltaS; var grantW = grant.getBounds().width * grant.scaleX; grant.x = (position >= w + grantW) ? -grantW : position; ground.x = (ground.x - deltaS * 150) % ground.tileW; hill.x = (hill.x - deltaS * 30); if (hill.x + hill.image.width * hill.scaleX <= 0) { hill.x = w; } hill2.x = (hill2.x - deltaS * 45); if (hill2.x + hill2.image.width * hill2.scaleX <= 0) { hill2.x = w; } scope.stage.update(event); } } } }]);
'use strict'; // Declare app level module which depends on views, and components angular.module('myApp',[ 'ngRoute', 'myApp.view1', 'myApp.version', 'myApp.services', 'myApp.uiClasses', 'myApp.directives']) .config(['$routeProvider', function($routeProvider) { $routeProvider.otherwise({redirectTo: '/view1'}); }]); var uiClasses = angular.module('myApp.uiClasses', []); var myServices = angular.module('myApp.services', []);
でディレクティブコードを更新しますファイル、プリロードコードを削除し、代わりにサービスを使用します。
uiClasses.factory('Sky', [ 'loaderSvc', function (loaderSvc) { function Sky(obj) { this.sky = new createjs.Shape(); this.sky.graphics.beginBitmapFill(loaderSvc.getResult('sky')).drawRect(0, 0, obj.width, obj.height); } Sky.prototype = { addToStage: function (stage) { stage.addChild(this.sky); }, removeFromStage: function (stage) { stage.removeChild(this.sky); } }; return (Sky); }]);
ゲーム開発でスプライトを再利用して繰り返すことは非常に重要です。 UIクラス(この場合はスプライト)のインスタンス化を有効にするために、AngularJSファクトリを使用します。
Factoryは、他のAngularJSモジュールと同じようにアプリケーションに登録されます。 uiClassesファクトリを作成するには、app.jsファイルを次のように変更します。
uiClasses.factory('Hill', [ 'loaderSvc', function (loaderSvc) { function Hill(obj) { this.hill = new createjs.Bitmap(loaderSvc.getResult(obj.assetName)); this.hill.setTransform(Math.random() * obj.width, obj.height - this.hill.image.height * obj.scaleFactor - obj.groundHeight, obj.scaleFactor, obj.scaleFactor); } Hill.prototype = { addToStage: function (stage) { stage.addChild(this.hill); }, removeFromStage: function (stage) { stage.removeChild(this.hill); }, setAlpha: function (val) { this.hill.alpha = val; }, getImageWidth: function () { return this.hill.image.width; }, getScaleX: function () { return this.hill.scaleX; }, getX: function () { return this.hill.x; }, getY: function () { return this.hill.y; }, setX: function (val) { this.hill.x = val; }, move: function (x, y) { this.hill.x = this.hill.x + x; this.hill.y = this.hill.y + y; } }; return (Hill); }]);
新しいファクトリを使用して、空、丘、地面、およびランナーを作成しましょう。これを行うには、以下にリストされているJavaScriptファイルを作成します。
uiClasses.factory('Ground', [ 'loaderSvc', function (loaderSvc) { function Ground(obj) { var groundImg = loaderSvc.getResult('ground'); this.ground = new createjs.Shape(); this.ground.graphics.beginBitmapFill(groundImg).drawRect(0, 0, obj.width + groundImg.width, groundImg.height); this.ground.tileW = groundImg.width; this.ground.y = obj.height - groundImg.height; this.height = groundImg.height; } Ground.prototype = { addToStage: function (stage) { stage.addChild(this.ground); }, removeFromStage: function (stage) { stage.removeChild(this.ground); }, getHeight: function () { return this.height; }, getX: function () { return this.ground.x; }, setX: function (val) { this.ground.x = val; }, getTileWidth: function () { return this.ground.tileW; }, move: function (x, y) { this.ground.x = this.ground.x + x; this.ground.y = this.ground.y + y; } }; return (Ground); }]);
uiClasses.factory('Character', [ 'loaderSvc', function (loaderSvc) { function Character(obj) { var spriteSheet = new createjs.SpriteSheet({ framerate: 30, 'images': [loaderSvc.getResult(obj.characterAssetName)], 'frames': {'regX': 82, 'height': 292, 'count': 64, 'regY': 0, 'width': 165}, // define two animations, run (loops, 1.5x speed) and jump (returns to run): 'animations': { 'run': [0, 25, 'run', 1.5], 'jump': [26, 63, 'run'] } }); this.grant = new createjs.Sprite(spriteSheet, 'run'); this.grant.y = obj.y; } Character.prototype = { addToStage: function (stage) { stage.addChild(this.grant); }, removeFromStage: function (stage) { stage.removeChild(this.grant); }, getWidth: function () { return this.grant.getBounds().width * this.grant.scaleX; }, getX: function () { return this.grant.x; }, setX: function (val) { this.grant.x = val; }, playAnimation: function (animation) { this.grant.gotoAndPlay(animation); } }; return (Character); }]);
index.html
myDirectives.directive('spriteSheetRunner', ['loaderSvc','Sky', 'Ground', 'Hill', 'Character', function (loaderSvc, Sky, Ground, Hill, Character) { 'use strict'; return { restrict : 'EAC', replace : true, scope :{ }, template: '', link: function (scope, element, attribute) { var w, h, sky, grant, ground, hill, hill2; drawGame(); function drawGame() { //drawing the game canvas from scratch here if (scope.stage) { scope.stage.autoClear = true; scope.stage.removeAllChildren(); scope.stage.update(); } else { scope.stage = new createjs.Stage(element[0]); } w = scope.stage.canvas.width; h = scope.stage.canvas.height; loaderSvc.getLoader().addEventListener('complete', handleComplete); loaderSvc.loadAssets(); } function handleComplete() { sky = new Sky({width:w, height:h}); sky.addToStage(scope.stage); ground = new Ground({width:w, height:h}); hill = new Hill({width:w, height:h, scaleFactor: 4, assetName: 'hill', groundHeight: ground.getHeight()}); hill.setAlpha(0.5); hill.addToStage(scope.stage); hill2 = new Hill({width:w, height:h, scaleFactor: 3, assetName: 'hill2', groundHeight: ground.getHeight()}); hill2.addToStage(scope.stage); ground.addToStage(scope.stage); grant = new Character({characterAssetName: 'grant', y: 34}) grant.addToStage(scope.stage); scope.stage.addEventListener('stagemousedown', handleJumpStart); createjs.Ticker.timingMode = createjs.Ticker.RAF; createjs.Ticker.addEventListener('tick', tick); } function handleJumpStart() { grant.playAnimation('jump'); } function tick(event) { var deltaS = event.delta / 1000; var position = grant.getX() + 150 * deltaS; grant.setX((position >= w + grant.getWidth()) ? -grant.getWidth() : position); ground.setX((ground.getX() - deltaS * 150) % ground.getTileWidth()); hill.move(deltaS * -30, 0); if (hill.getX() + hill.getImageWidth() * hill.getScaleX() <= 0) { hill.setX(w); } hill2.move(deltaS * -45, 0); if (hill2.getX() + hill2.getImageWidth() * hill2.getScaleX() <= 0) { hill2.setX(w); } scope.stage.update(event); } } } }]);
これらすべての新しいJSファイルをuiClasses
に追加することを忘れないでください。
次に、ゲームディレクティブを更新する必要があります。
keyDown
handleComplete()
を移動することに注意してくださいディレクティブから、ディレクティブのサイズが91行から65行に20%減少しました。
さらに、ファクトリクラスごとに個別にテストを記述して、メンテナンスを簡素化できます。
注:テストはこの投稿では取り上げられていないトピックですが、 ここに 始めるのに良い場所です。
化粧品業界の規模
HTML5 Canvasゲームチュートリアルのこの時点で、携帯電話をマウスでクリックまたはタップすると、男がジャンプし、彼を止めることはできません。矢印キーコントロールを追加しましょう:
これを行うには、function keydown(event) { if (event.keyCode === 38) {//if keyCode is 'Up' handleJumpStart(); } if (event.keyCode === 39) {//if keyCode is 'Right' if (scope.status === 'paused') { createjs.Ticker.addEventListener('tick', tick); scope.status = 'running'; } } if (event.keyCode === 37) {//if keyCode is 'Left' createjs.Ticker.removeEventListener('tick', tick); scope.status = 'paused'; } } window.onkeydown = keydown;
を作成します関数を作成し、loadQueue
の最後の行としてイベントリスナーを追加します関数。
PreloaderJS
ゲームをもう一度実行して、キーボードコントロールを確認してください。
ゲームは音楽なしでは楽しくないので、音楽をかけてみましょう。
まず、MP3ファイルをapp / assetsフォルダーに追加する必要があります。以下のURLからダウンロードできます。
次に、ローダーサービスを使用してこれらのサウンドファイルをプリロードする必要があります。 app/view1/services/loaderSvc.js
を使用しますのmyServices.service('loaderSvc', function () { var manifest = [ {src: 'spritesheet_grant.png', id: 'grant'}, {src: 'sky.png', id: 'sky'}, {src: 'ground.png', id: 'ground'}, {src: 'hill1.png', id: 'hill'}, {src: 'hill2.png', id: 'hill2'}, {src: 'runningTrack.mp3', id: 'runningSound'}, {src: 'jump.mp3', id: 'jumpingSound'} ], loader = new createjs.LoadQueue(true); // need this so it doesn't default to Web Audio createjs.Sound.registerPlugins([createjs.HTMLAudioPlugin]); loader.installPlugin(createjs.Sound); this.getResult = function (asset) { return loader.getResult(asset); }; this.getLoader = function () { return loader; }; this.loadAssets = function () { loader.loadManifest(manifest, true, '/app/assets/'); }; });
図書館。 myDirectives.directive('spriteSheetRunner', [ 'loaderSvc', 'Sky', 'Ground', 'Hill', 'Character', function (loaderSvc, Sky, Ground, Hill, Character) { 'use strict'; return { restrict : 'EAC', replace : true, scope :{ }, template: '', link: function (scope, element, attribute) { var w, h, sky, grant, ground, hill, hill2, runningSoundInstance, status; drawGame(); function drawGame() { //drawing the game canvas from scratch here if (scope.stage) { scope.stage.autoClear = true; scope.stage.removeAllChildren(); scope.stage.update(); } else { scope.stage = new createjs.Stage(element[0]); } w = scope.stage.canvas.width; h = scope.stage.canvas.height; loaderSvc.getLoader().addEventListener('complete', handleComplete); loaderSvc.loadAssets(); } function handleComplete() { sky = new Sky({width:w, height:h}); sky.addToStage(scope.stage); ground = new Ground({width:w, height:h}); hill = new Hill({width:w, height:h, scaleFactor: 4, assetName: 'hill', groundHeight: ground.getHeight()}); hill.setAlpha(0.5); hill.addToStage(scope.stage); hill2 = new Hill({width:w, height:h, scaleFactor: 3, assetName: 'hill2', groundHeight: ground.getHeight()}); hill2.addToStage(scope.stage); ground.addToStage(scope.stage); grant = new Character({characterAssetName: 'grant', y: 34}); grant.addToStage(scope.stage); scope.stage.addEventListener('stagemousedown', handleJumpStart); createjs.Ticker.timingMode = createjs.Ticker.RAF; createjs.Ticker.addEventListener('tick', tick); // start playing the running sound looping indefinitely runningSoundInstance = createjs.Sound.play('runningSound', {loop: -1}); scope.status = 'running'; window.onkeydown = keydown; } function keydown(event) { if (event.keyCode === 38) {//if keyCode is 'Up' handleJumpStart(); } if (event.keyCode === 39) {//if keyCode is 'Right' if (scope.status === 'paused') { createjs.Ticker.addEventListener('tick', tick); runningSoundInstance = createjs.Sound.play('runningSound', {loop: -1}); scope.status = 'running'; } } if (event.keyCode === 37) {//if keyCode is 'Left' createjs.Ticker.removeEventListener('tick', tick); createjs.Sound.stop(); scope.status = 'paused'; } } function handleJumpStart() { if (scope.status === 'running') { createjs.Sound.play('jumpingSound'); grant.playAnimation('jump'); } } function tick(event) { var deltaS = event.delta / 1000; var position = grant.getX() + 150 * deltaS; grant.setX((position >= w + grant.getWidth()) ? -grant.getWidth() : position); ground.setX((ground.getX() - deltaS * 150) % ground.getTileWidth()); hill.move(deltaS * -30, 0); if (hill.getX() + hill.getImageWidth() * hill.getScaleX() <= 0) { hill.setX(w); } hill2.move(deltaS * -45, 0); if (hill2.getX() + hill2.getImageWidth() * hill2.getScaleX() <= 0) { hill2.setX(w); } scope.stage.update(event); } } } }]);
を更新しますこれらのファイルをプリロードします。
index.html
ゲームディレクティブを変更して、ゲームイベントでサウンドを再生します。
app/view1/view1.html
関連: ApeeScape開発者によるAngularJSのベストプラクティスとヒント ゲームのスコアとライフ(ハート)インジケーターをHTML5Canvasゲームに追加しましょう。スコアは左上隅に数字で表示され、右上隅にあるハート記号はライフカウントを示します。
ハートのレンダリングには外部フォントライブラリを使用するため、
に追加しますファイル:Score: {{score}}
Life: 1' class='fa fa-heart'> 2' class='fa fa-heart'> .top-left { position: absolute; left: 30px; top: 10px; } .top-right { position: absolute; right: 100px; top: 10px; float: right; }
lifesCount
インジケーターを適切に配置するには、app/view1/view1.js
に左上と右上のCSSクラスを追加する必要があります。ファイル。
'use strict'; angular.module('myApp.view1', ['ngRoute']) .config(['$routeProvider', function($routeProvider) { $routeProvider.when('/view1', { templateUrl: 'view1/view1.html', controller: 'View1Ctrl' }); }]) .controller('View1Ctrl', ['$scope', function($scope) { $scope.score = 0; $scope.lifesCount = 3; }]);
スコアを初期化し、... replace : true, scope :{ score: '=score', lifesCount: '=lifesCount' }, template: ...
handleComplete()
の変数コントローラ。
scope.score = 10; scope.lifesCount = 2; scope.$apply();
インジケーターが適切に更新されるようにするには、スコープ変数を使用するようにメインのゲームディレクティブを変更します。
app/view1/view1.js
スコープバインディングをテストするには、$window
の最後にこれらの3行を追加します。方法。
'use strict'; angular.module('myApp.view1', ['ngRoute']) .config(['$routeProvider', function($routeProvider) { $routeProvider.when('/view1', { templateUrl: 'view1/view1.html', controller: 'View1Ctrl' }); }]) .controller('View1Ctrl', ['$scope', '$window', function($scope, $window) { $scope.windowWidth = $window.innerWidth; $scope.gameHeight = 400; $scope.score = 0; $scope.lifesCount = 3; }]);
アプリケーションを実行すると、スコアと寿命のインジケーターが表示されます。
HTML5ゲームプログラミングチュートリアルのこの時点ではまだゲームの幅と高さをハードコーディングしているため、ページの右側に追加の空白が引き続き表示されます。
AngularJSには便利なメソッドとサービスが満載です。それらの1つは$ windowで、これは要素の位置を計算するために使用するinnerWidthプロパティを提供します。
... scope :{ width: '=width', height: '=height', score: '=score', lifesCount: '=lifesCount' }, ... drawGame(); element[0].width = scope.width; element[0].height = scope.height; w = scope.width; h = scope.height; function drawGame() { ...
を変更します注入する
|_+_|サービス。
|_+_|
メインのゲームディレクティブをwidthプロパティとheightプロパティで拡張すれば、それだけです。
|_+_|
これで、ゲームがブラウザウィンドウの幅に合わせて調整されました。
これをモバイルアプリに移植したい場合は、他のモバイルアプリ開発チュートリアルを読むことをお勧めします。 Ionicフレームワークを使用してモバイルアプリを作成する 。イオンシードアプリを作成し、このプロジェクトからすべてのコードをコピーして、1時間以内にモバイルデバイスでゲームを開始できるはずです。
ここで取り上げていないのは、衝突検出だけです。それについてもっと学ぶために、私は読んだ この記事 。
このゲーム開発チュートリアルの過程で、あなたは次のことに気づいたと思います。 AngularJS とCreateJSは、HTML5ベースのゲーム開発で勝利を収めたデュオです。あなたはすべての基本を持っており、これら2つのプラットフォームを組み合わせることの利点を認識していると確信しています。
この記事のコードはからダウンロードできます GitHub 、自由に使用、共有、そしてあなた自身のものにしてください。
Photoshopでフォントを作成する方法関連: 開発者が犯す最も一般的なAngularJSの間違いトップ18