忘れないうちに、今までのをすっ飛ばして、新しいものから記録していきます。
息子はもうすぐ実力テストがあり、それに向けて毎日頑張って勉強しつつ、タイピングやドットインストールでの自習もやっておりますので、代わって息子に聞きながら記録しておきます。
2017.8~9にかけての内容です。
サンプルは「収穫せよ」というゲームで、コレクションゲーム?収集ゲームというのでしょうか?やったことがないのでよくわかりませんが、とにかく、ランダムに出てくるアイテムを集めるというものです。
それには、一度出たものを「獲った」と記憶させておく仕組みが必要です。今まで習ってきたものはすべてその画面での記録でしかなく、画面遷移をしてしまうと得点ですら残らないものでした。
それが、この仕組みを使えば、内部に記憶させておいて、出したい時に出す、消したければリセットすることができるという、いわゆる「セーブ・ロード・リセット」ということができるという訳です。
ゲームをやっていると当たり前のようにある機能も、このように一つ一つ学んでいくと、こういうことを使って作っているんだね〜、なるほど確かにこれは必要だね〜、と感慨深いものがありますね。
クラスでは、テーマは「武器」ということになりました。皆がそれぞれに作った素敵な武器を持ち寄って、作りました。
先日、できた分までの発表&報告があり、改めて見返してみましたが、個性があってほんとうに素晴らしい。私は、シュールなものも大好きなので、金床の後ろから武器が出てきてしまっていたり、武器が大根に変わったり、異様に大きな地球が出てきたり、武器がループして出現していたり、作る側の苦労も垣間見えるゲームづくりあるあるが微笑ましく、見ていると楽しいのでした。
以下作ったものについて説明します。
1.金床の配置
先生がつくられたサンプルのゲームに習って、一度に四つの武器がランダムに出るようにしました。
そのために、まず金床(鍛冶屋さんが金物を加工するための台座です。この言葉も初めて知りました。でも、この画像を出したときに、クラスのお友達はすぐに「かなとこー!」と反応してくれました。よく知ってるな〜と感心)を4つ配置します。
金床1〜金床4としました。
//金床1 var kanatoko = new ExSprite(120, 120); kanatoko.image = core.assets["images/cf307/kanatoko_suihei1.png"]; kanatoko.x = 30; kanatoko.y = 170; kanatoko.touchEnabled = false; scene.addChild(kanatoko); kanatoko.frame = 1;
2.ランダムの変数
その金床一つ一つに対して、ランダムに武器をだすので、ランダムの変数も4つ用意します。
4種類の武器に対して、全部で100%として確率を変えることにしました。
// 変数 var random = getRandom(1, 100); var random2 = getRandom(1, 100); var random3 = getRandom(1, 100); var random4 = getRandom(1, 100);
武器は、短剣、ハンマー、ソード、スコップの4種類です。
出現する確率として、短剣50%、ハンマー30%、ソード19%、スコップ1%にして、条件分岐にします。
3.問題発生(画像の大きさが異なる問題)
そこで、はじめ、先生のサンプルのようにそれぞれの確率に対して、
if (1 <= random && random <= 50) { arms1.image = (略:短剣のイメージ); } if (51 <= random && random <= 80) { arms1.image = (略:ハンマーのイメージ); } if (81 <= random &&random <= 99) { arms1.image = (略:ソードのイメージ); } if (random == 100) { arms1.image = (略:スコップのイメージ); }
のように書きました。すると、問題が発生しました。
武器の画像の大きさが異なるために、そのまま画像のイメージを入れ替えただけでは、はじめの画像の指定より小さな画像の武器はループされて表示されてしまい、大きな画像は切れてしまいます。
ですので、その都度画像の大きさを指定し直すという作業が必要になってしまい、それに付随して位置の微妙な調整も必要になってきてしまうということに気づきました。(要するに、たくさん書き足さなくてはならない)
4.問題の解決(関数を使って位置だけを変えて指定する)
調整すべきは、画像の大きさと、画像の位置、それぞれx方向とy方向の4つの値です。それが、4つの金床に対して4つの武器なので、4×4=16箇所になります。
そこで、一つの武器につき、出す位置を変数にした関数を一つづつ作って、金床に合わせて出す位置だけを変えて入力すればよい形にしました。こうすれば、武器それぞれに対しては大きさは固定されるので、変な出方をすることがありません。
短剣を出すための関数をArms1(x, y)としてつくります。
//武器1を出す関数 function Arms1(x,y){ var arms1 = new ExSprite(60, 100); arms1.image = core.assets["images/cf307/tannken.png"]; arms1.x = x; arms1.y = y; scene.addChild(arms1); arms1.rotate(-80); }
これで、金床の位置に合わせてx、yだけを指定すればいいことになります。武器2〜武器4も同様に
ハンマーを作る関数をArms2(x, y)、タガーを作る関数をArms3(x, y)、スコップを作る関数をArms4(x, y)として設定しました。
すると、今度は武器の表示をfanctionに入れたがために、そのままでは武器のタッチイベント(セーブ、武器をタッチすると消える、カードが画像入りのものに変わる)が効かなくなってしまいました。
そこで、それも一緒にfanctionの中に入れることで解決しました。(以下青字の部分)
//武器1を出す関数 function Arms1(x,y){ var arms1 = new ExSprite(60, 100); arms1.image = core.assets["images/cf307/tannken.png"]; arms1.x = x; arms1.y = y; scene.addChild(arms1); arms1.rotate(-80); arms1.addEventListener(Event.TOUCH_START, function(e) { setLocalStorage("cardA", "get"); arms1.remove(); cardA.frame = 1; }); }
5.カード
カードは、シルエットのカード(freme0)を出しておいて、武器を獲ったら(LocalStorageにgetとセーブされていたら)カードの絵(frame1)が出るようにします。
var cardA = new ExSprite(128 / 2, 83); cardA.image = core.assets["images/cf307/tannkencard2.png"]; if (getLocalStorage("cardA") == "get") { cardA.frame = 1; } else { cardA.frame = 0; }; cardA.x = 15; cardA.y = 390; scene.addChild(cardA);
6.サウンド
鍛冶屋なので、童謡「村の鍛冶屋」をBGMにしました。
7.完成したソースコード
以上を組み合わせて、以下のように完成しました。
var assets = [ // 背景 "images/cf307/kaziba.png", // カード "images/cf307/tannken.png", "images/cf308/so-do (5).png", "images/cf307/kanatoko_suihei1.png", "images/cf308/ka-do (1).png", "images/cf308/ka-do (2).png", "images/cf307/tannkencard2.png", "images/cf307/hannmer90_64.png", "images/cf308/karahurunasukoppu (1).png", "images/cf308/ka-do (4).png", "images/cf308/ka-do (5).png", "images/cf307/hannmercard2.png", // 削除ボタン "images/b_red.png", "images/title.png",// タイトル "sounds/cf307/kajiya.mp3", ]; function gameStart(){// ゲーム画面 scene = gameManager.createGameScene(); core.replaceScene(scene); core.resume(); // BGM var sound = core.assets["sounds/cf307/kajiya.mp3"].clone(); sound.play(); // 背景 var back = new ExSprite(320, 480); back.image = core.assets["images/cf307/kaziba.png"]; scene.addChild(back); //変数 var random = getRandom(1, 100); var random2 = getRandom(1, 100); var random3 = getRandom(1, 100); var random4 = getRandom(1, 100); console.log(random); console.log(random2); console.log(random3); console.log(random4); //金床1 var kanatoko = new ExSprite(120, 120); kanatoko.image = core.assets["images/cf307/kanatoko_suihei1.png"]; kanatoko.x = 30; kanatoko.y = 170; kanatoko.touchEnabled = false; scene.addChild(kanatoko); kanatoko.frame = 1; if (1 <= random && random <= 50) { Arms1(30, 160); } if (51 <= random && random <= 80) { Arms2(30, 170); } if (81 <= random &&random <= 99) { Arms3(50, 170); } if (random == 100) { Arms4(60, 170); } //金床2 var kanatoko2 = new ExSprite(120, 120); kanatoko2.image = core.assets["images/cf307/kanatoko_suihei1.png"]; kanatoko2.x = 180; kanatoko2.y = 170; kanatoko2.touchEnabled = false; scene.addChild(kanatoko2); kanatoko2.frame = 1; if (1 <= random2 && random2 <= 50) { Arms1(180, 160); } if (51 <= random2 && random2 <= 80) { Arms2(180, 170); } if (81 <= random2 && random2 <= 99) { Arms3(200, 170); } if (random2 == 100) { Arms4(210, 170); } //金床3 var kanatoko3 = new ExSprite(120, 120); kanatoko3.image = core.assets["images/cf307/kanatoko_suihei1.png"]; kanatoko3.x = 30; kanatoko3.y = 250; kanatoko3.touchEnabled = false; scene.addChild(kanatoko3); kanatoko3.frame = 1; if (1 <= random3 && random3 <= 50) { Arms1(30, 240); } if (51 <= random3 && random3 <= 80) { Arms2(30, 250); } if (81 <= random3 && random3 <= 99) { Arms3(50, 250); } if (random3 == 100) { Arms4(60, 250); } //金床4 var kanatoko4 = new ExSprite(120, 120); kanatoko4.image = core.assets["images/cf307/kanatoko_suihei1.png"]; kanatoko4.x = 180; kanatoko4.y = 250; kanatoko4.touchEnabled = false; scene.addChild(kanatoko4); kanatoko4.frame = 1; if (1 <= random4 && random4 <= 50) { Arms1(180, 240); } if (51 <= random4 && random4 <= 80) { Arms2(180, 250); } if (81 <= random4 && random4 <= 99) { Arms3(200, 250); } if (random4 == 100) { Arms4(210, 250); } //短剣を表示する関数 function Arms1(x,y){ var arms1 = new ExSprite(60, 100); arms1.image = core.assets["images/cf307/tannken.png"]; arms1.x = x; arms1.y = y; scene.addChild(arms1); arms1.rotate(-80); arms1.addEventListener(Event.TOUCH_START, function(e) { setLocalStorage("cardA", "get"); arms1.remove(); cardA.frame = 1; }); } //ハンマーを表示する関数 function Arms2(x,y){ var arms2 = new ExSprite(90, 64); arms2.image = core.assets["images/cf307/hannmer90_64.png"]; arms2.x = x; arms2.y = y; scene.addChild(arms2); //arms2.rotate(-80); arms2.addEventListener(Event.TOUCH_START, function(e) { setLocalStorage("cardB", "get"); arms2.remove(); cardB.frame = 1; }); } // 短剣を表示する関数 //もとは50、250 function Arms3(x,y){ var arms3 = new ExSprite(64, 80); arms3.image = core.assets["images/cf308/so-do (5).png"]; arms3.x = x; arms3.y = y; scene.addChild(arms3); arms3.rotate(-90); arms3.addEventListener(Event.TOUCH_START, function(e) { setLocalStorage("cardC", "get"); arms3.remove(); cardC.image = core.assets["images/cf308/ka-do (1).png"]; }); } //スコップを表示する関数 function Arms4(x,y){ var arms4 = new ExSprite(64, 80); arms4.image = core.assets["images/cf308/karahurunasukoppu (1).png"]; arms4.x = x;//200 arms4.y = y;//260 scene.addChild(arms4); arms4.rotate(-90); arms4.addEventListener(Event.TOUCH_START, function(e) { setLocalStorage("cardD", "get"); arms4.remove(); cardD.image = core.assets["images/cf308/ka-do (4).png"]; }); } // カード var cardA = new ExSprite(128 / 2, 83); cardA.image = core.assets["images/cf307/tannkencard2.png"]; if (getLocalStorage("cardA") == "get") { cardA.frame = 1; } else { cardA.frame = 0; }; cardA.x = 15; cardA.y = 390; scene.addChild(cardA); var cardB = new ExSprite(64, 80); cardB.image = core.assets["images/cf307/hannmercard2.png"]; if (getLocalStorage("cardB") == "get") { cardB.frame = 1; } else { cardB.frame = 0; }; cardB.x = 90; cardB.y = 390; scene.addChild(cardB); var cardC = new ExSprite(64, 80); if (getLocalStorage("cardC") == "get") { cardC.image = core.assets["images/cf308/ka-do (1).png"]; } else { cardC.image = core.assets["images/cf308/ka-do (2).png"]; }; cardC.x = 165; cardC.y = 390; scene.addChild(cardC); var cardD = new ExSprite(64, 80); if (getLocalStorage("cardD") == "get") { cardD.image = core.assets["images/cf308/ka-do (4).png"]; } else { cardD.image = core.assets["images/cf308/ka-do (5).png"]; }; cardD.x = 240; cardD.y = 390; scene.addChild(cardD); // 削除ボタン var button = new ExSprite(62, 55); button.image = core.assets["images/b_red.png"]; button.x = 250; button.y = 0; scene.addChild(button); button.addEventListener(Event.TOUCH_START, function() { removeLocalStorage("cardA"); removeLocalStorage("cardB"); removeLocalStorage("cardC"); removeLocalStorage("cardD"); }); } function titleStart(){// タイトル画面 var scene = gameManager.createTitleScene(); core.replaceScene(scene); core.pause(); scene.on(enchant.Event.TOUCH_START, function(){gameStart();}); } //========== // EnchantJS enchant(); var gameManager; var core; var scene; window.onload = function(){ gameManager = new common.GameManager(); core = gameManager.createCore(320, 480); core.preload(assets); core.onload = function(){titleStart();}; core.start(); }
8.別の書き方(先生の書き方)
「3.問題発生(画像の大きさが異なる問題)」のところの、画像の大きさが異なることについて、授業で先生が解説してくださいました。先生のやり方は、まず画像について大きさを指定しないで定義だけしておいて、あとからそれぞれサイズを指定するという方法でした。
var vegetable; var random = getRandom(1, 4); if(random == 1){ vegetable = new ExSprite(80, 120); vegetable.image = core.assets[(略)]; vegetable.x = ; vegetable.y = ; } if(random == 2){ vegetable = new ExSprite(90, 100); vegetable.image = core.assets[(略)]; vegetable.x = ; vegetable.y = ; } (略)〜〜 〜〜 scene.addChild(vegetable); vegetable.addEventLintener(~略
根本的にはやっていることは変わらないように思いますが、試しにそちらの書き方でも書いてみようと思います。
・・・・・・・
・・・と、やってみましたら、先生が書き足された部分について、ゆうじの場合は4箇所あるので、何度も書くのが大変なので関数にしただけで、やはりやっていたことはほぼ同じだったということに気づいたので、書き直すのはやめにしました。
要するに、先生が書き足されていた部分を、関数にして短くして書き足していたのでした。
その他、一度に4つの武器を出すことにしたために、色々書く順番が違っていましたが、こんなに書き方が違っても同じように動くのだなと大変勉強になり、興味深いものがありました。
ゆうじも授業を受けながら先生のを追って打っていたはずなのに結構違っていて、ゆうじなりに色々工夫したんだなと感心もしました。
書く順番が違っていても、ソースコードとしての順番さえ間違っていなければ、ちゃんと動くところがまたプログラミングの面白いところです。数学の解法に色々あるのに似ています。こういう、書き方にも人によって好みがあって、癖とか個性が出てくるのでしょうね。