二重のfor文を使ってビンゴカードを作る

今回の授業は、for文を使って、ビンゴカードを自動作成しようというものでした。

二重のfor文

ポイントは、二重のfor文です。「5回の繰り返し」を5回繰り返すことで、5×5の25個の数字を並べます。

// 二重のfor文(c0,c1,c2,c3,c4を5回繰り返す)
for(var r=0; r<5; r++){
    console.log("r" + r);//r0~r4
    for(var c=0; c<5; c++){
        console.log(" c" + c);
        // 座標に応用
        var x = 32 * r;// 授業とはrとcが逆(列と行を逆にしたいから)
        var y = 32 * c;
        console.log(x + ", " + y);
        // ラベル
        var label = new Label();
        label.x = x;
        label.y = y;
        label.text = "[" + r +","+ c +"]";// こちらも逆にする(これで[列、行]の並びになる)
        scene.addChild(label);
         label.text = getRandom(1, 75);
    }
}

c0からc4をr0からr4まで5回繰り返すということです。(授業でやったものとはrとcを逆にしました。列でまとめたかったからです)

c0~c4が5列に並ぶという形になります。

ラベルのところでテキストを「“[” + r +”,”+ c +”]”」とすることで、

[00][10][20][30][40]

[01][11][21][31][41]

[02][12][22][32][42]

[03][13][23][33][43]

[04][14][24][34][44]

となりました。(rが列、cが行となっています)

あとは label.text = getRandom(1, 75); で、それぞれの表示を1〜75の数字をランダムに出るようにすれば、ビンゴカードの出来上がりです。

・・しかし、これだとそれぞれの数のランダムなので、数が重複する場合があります。(それはそれで二つ空けられるラッキーナンバーということにすればいいのかもしれませんが、一般的なビンゴゲームにも一応ルールがあって、その中には数は重複しないというのがあります)

数が重複しないようにシャッフルを使う

そこで、数が重複しないように作るにはどうしたらいいかということで、その方法も教えてくださいました。

ここでダステンフェルドのシャッフルを使います。

// 配列を使ってダブらないビンゴカードにする
// 配列を作る
var nums = [];
for (var i=0; i<25; i++){
    nums.push(i);//numsの中にiがプッシュされていく。今iは0~24の25個の数字
}
console.log(nums);
// ダステンフェルドの手法でシャッフルする
for(var i=nums.length-1; 0<i; i--){// iが24から1まで24回繰り返す
    var rdm = getRandom(0, i);// rdm:自分の数以下からランダムに選んだ数字一つ
    var tmp = nums[i];// 新しくtmpを用意してnum[i]を入れておく
    nums[i] = nums[rdm];// nums[i]に選ばれたランダムな数を入れる
    nums[rdm] = tmp;// nums[rdm]にtmp(muns[i])を入れる。これで交換完了
}
console.log("nums", nums);

0から24までの数をシャッフルしています。

どうなっているのかわかりにくいのでそれぞれconsole.logで見てみました。

// ダステンフェルドの手法でシャッフルする
for(var i=nums.length-1; 0<i; i--){// iが24から1まで24回繰り返す
    console.log("i:", i, "," , "muns[i]:", nums[i]);
    var rdm = getRandom(0, i);// rdm:自分の数以下からランダムに選んだ数字一つ
    console.log("rdm:" , rdm , "," , "nums[rdm]:" , nums[rdm]);
    var tmp = nums[i];// 新しくtmpを用意してnum[i]を入れておく
    console.log("tmp(=nums[i]):" , tmp);
    nums[i] = nums[rdm];// nums[i]に選ばれたランダムな数を入れる
    console.log("muns[i](=nums[rdm]):" , nums[i]);
    nums[rdm] = tmp;// nums[rdm]にtmp(muns[i])を入れる。これで交換完了
    console.log("nums[rdm](=tmp):" , nums[rdm]);
    console.log(nums);
    //全部consoleで見てみる
}
console.log("nums", nums);

すると、このような動きになっていました。

for文の繰り返しの中でその都度配列の順番が変わって上書きされるので、最初はnums[22]は22だったのが、入れ替わったことで途中でnums[22]は24に変わっています。

一度交換の相手になったランダムで選ばれた「数字」は、後ろから順番に入っていって固定され(iは減少していくから)二度と交換されることはありません。そうでないと精度的に正しいシャッフルとなりません。

ラベルで表示する

あとは、ランダムに並べ替えられた数字にindexを用意して、num[0]~num[24]までがラベルのテキストに入るように書き換えて、ランダムに並んだ数字を表示できました。

// インデックス
var index = 0;
//label.text = "[" + r +","+ c +"]";// こちらも逆にする(これで[列、行]の並びになる)
label.text = nums[index];// 初期値は0
index++;// ラベルのテキストがnums[0]からnums[24]になる

一般的なルールのビンゴカードを作る

つぎに、これを応用して、日本でのビンゴの一般的なルールにしたがったものを作ってみたいと思ってやってみました。

wikipediaのビンゴより

ルールには無数にバリエーションがあるが、ここでは日本で一般的に行われている代表的なものを示す。一般的にビンゴカードはタテ・ヨコ5マスずつ、計25個のマス目が書かれている。その内、中央を除く24マスには1から75までの番号のうち24個の番号が書かれており、中央はフリースポットとして最初から有効な番号として扱われる。1枚のカードの中で同じ番号が重複することはない。

カードに書かれる番号は、通常はまったくのランダムで配置されているわけではなく、一番左の列は1-15の中から5個選ばれている。同様に、左から2列目は16-30、中央列は31-45、右から2列目は46-60、一番右の列は61-75から5個ずつ(中央列のみフリースポットがあるので4個)選ばれている

全くのランダムではなく、15づつの数の固まりから5個ずつ選ぶようになっているようですね。

ちょっとややこしそうですが、for文をたくさん使ってやってみました。

1〜15まで、16から30まで、31から45まで、46から60まで、61から75までの数からそれぞれ5つずつランダムに選ぶ必要があるので、それぞれシャッフルした15個の中から5つを選んでそれを左の列から並べるというやり方で作りました。

まず、nを始めの数とする変数として15個の数のシャッフルを作る関数 Nums15(n) を作ります。ここでやっていることは、要素15個の配列をシャッフルして、その最初から5つの目までの数を取り出してnumsselという配列に入れていくという処理です。既にシャッフルされた数なので、そこから5つの数を取り出せば、ランダムで重複しない数となります。

    // 目的の配列を用意
    var numssel = [];

    //nをはじめの数とする15個の数字のシャッフルを作る関数
    function Nums15(n){
        var nums15 = [];// 15個の配列の入れ物を用意
        for (var i=n; i<n+15; i++){
            nums15.push(i);// n~n+15までの数を[]に入れる
        }
        // シャッフル
        for (var i=nums15.length-1; 0<i; i--){
            var rdm = getRandom(0, i);
            var tmp = nums15[i];
            nums15[i] = nums15[rdm];
            nums15[rdm] = tmp;
        }
        // 出来たランダムな配列nums15から5つの数字を取り出してnumsselという配列に入れていく。
        for(var i=0; i<5; i++){
            numssel.push(nums15[i]);// 最初から5つをnumsselに入れていく
        }
    }

あとは、そのかたまりの始めの数は1,16,31,46,61なので、その関数をつぎのようなfor文で実行します。

// 実行する。ここでnumsselが最終的に5*5で25個となる
for(var i=1; i<76; i+=15){ 
    Nums15(i);
}

すると、ランダムに選ばれた5つの数を5回入れていくので、numsselには、1~15から5つ、16~30から5つ、31~45から5つ、46~60から5つ、61~75から5つ、合計25個の数が選ばれて入ります。

ただし、真ん中はFreeにしたいので、真ん中はmussel[12]なので

numssel[12] = "free";

とします。

あとは、授業でやったときとおなじように、この配列musselにindexを使ってラベルに対応させて表示させれば、左の列からルールのようにならんだビンゴカードができました。

forをたくさん使ったので、すごくforの練習になりました。functionも使ったので、途中、頭がこんがらがって死にそうになりました。関数は作ってもその実行は別の場所でやる感じなので、コードでは後ろに書いてあっても処理としては戻るような感じになって、あっちこっちで訳がわからなくなりそうでしたが、これも慣れなのでしょうか・・頭の中でforループの処理を同時にシミュレーションできないと書けないと思うので、これがサラッとできちゃうプログラマさんてすごいね。でもfunctionmとforを使うと本当に短く書くことが出来て、達成感がありました。あと今見て思ったんですが「配列の配列」ってあるんかな、当然あるでしょうね。(私は汚いコードでも動けばいいという^^;だめよねそれじゃ)

文字の基準が中心で揃っていなっていないのが気になります・・そのうちこれを使って、何かゲームが作れたら作ってみたいと思います・・(授業で話しが出た独りビンゴなんかいいと思うけどなー)

完成したもの

センサー 回転軸

センサーを使って、表示された矢印を三方向の傾きによって動かします。

今回の課題は以下です。

// 端末の回転軸を取得しよう
    // 1.デバイスの向きを変えても起動時の方角を指すよにしよう
    // 2.デバイスを縦に傾けた時、矢印が上下に移動するようにしよう
    // 3.デバイスを横に傾けた時、矢印が左右に移動するようにしよう

e.alpha: 机に置いて、水平のまま回転させたときの角度(0〜360)

e.beta:縦方向(首を上に向ける、天井を見る)方向に傾けたときの角度(-180〜180)

e.gamma:横方向(左右に傾ける方向)に傾けたときの角度(-90〜90)

(先生の記述とはbetaとgammaが逆のようです。端末によって異なるそうです)

それぞれ、角度が表示されるように、ラベルを用意します。

 //ラベルを用意する
    var labelA = new Label();
    labelA.x = 10;
    labelA.y = 10;
    labelA.text = "*";
    scene.addChild(labelA);
 
    var labelB = new Label();
    labelB.x = 10;
    labelB.y = 40;
    labelB.text = "*";
    scene.addChild(labelB);
 
    var labelC = new Label();
    labelC.x = 10;
    labelC.y = 80;
    labelC.text = "*";
    scene.addChild(labelC);

回転軸を取得するイベントをつけて、それぞれの方向の値(horizontal, vartical, rotation)をe.beta, egamma, e.alphaとすれば、矢印がそれぞれの傾きに反応して、課題のとおりに動きました。

// 端末の回転軸を取得しよう
    window.addEventListener("deviceorientation", function(e){
        labelA.text = e.alpha;// 向き(角度)
        labelB.text = e.beta; // 縦の傾き(ipadでは横移動)
        labelC.text = e.gamma;// 横の傾き(ipadでは縦移動)

        // 水平方向の位置
        var horizontal = e.beta;// 横移動
        arrow.centerY = scene.height / 2 + horizontal;
                        // horizontal(e.beta)の分だけ横方向に移動する
        // 垂直方向の位置
        var vertical = e.gamma;// 縦移動
        arrow.centerX = scene.width / 2 + vertical;
                        // vartical(e.gammma)の分だけ縦方向に移動する
        // 角度(矢印の向き)
        var rotation = e.alpha;// 向きだからここにe.alpha
        arrow.rotation = rotation;// rotation(e.alpha)の分だけ回転する
    });

ソースコード

/*
    // 端末の回転軸を取得しよう
    // 1.デバイスの向きを変えても起動時の方角を指すよにしよう
    // 2.デバイスを縦に傾けた時、矢印が上下に移動するようにしよう
    // 3.デバイスを横に傾けた時、矢印が左右に移動するようにしよう

    イベントターゲット: window
    イベント: deviceorientation
    イベント情報:
        e.alpha : デバイスの向き 角度
        e.beta  : デバイスの縦の傾き (水平方向)
        e.gamma : デバイスの横の傾き (垂直方向)

    
    ※端末によっては向きが異なる場合があります。

*/

var arrowImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgCAYAAAAbifjMAAAAfklEQVRIS+2TwQ7AIAhD8f8/ekY3DHYtO3gcuy0tL1CwWf5dj9yUTQpm5sVeS70KgMUSwgCqmEIQEIuHFjNAbQIjgBkwxJfHAZQOHfgImze2iR2Nf7XGBcE5cd3ZHUzNAV/rlHp2SNkIq9MC7JfIXuzRc64t3JFWiJXBX+6gA+0eLB5r+K8XAAAAAElFTkSuQmCC";

var assets = [
    arrowImage
];

function gameStart(){// ゲーム画面
    scene = gameManager.createGameScene();
    core.replaceScene(scene); core.resume();

    //==========
    // ここから
    //==========

    var arrow = new Sprite(16, 32);
    arrow.image = core.assets[arrowImage];
    arrow.centerX = scene.width / 2;
    arrow.centerY = scene.height / 2;
    scene.addChild(arrow);
    
    //ラベルを用意する
    var labelA = new Label();
    labelA.x = 10;
    labelA.y = 10;
    labelA.text = "*";
    scene.addChild(labelA);

    var labelB = new Label();
    labelB.x = 10;
    labelB.y = 40;
    labelB.text = "*";
    scene.addChild(labelB);

    var labelC = new Label();
    labelC.x = 10;
    labelC.y = 80;
    labelC.text = "*";
    scene.addChild(labelC);
     

    // 端末の回転軸を取得しよう
    window.addEventListener("deviceorientation", function(e){
        //alpha
        labelA.text = e.alpha;// 向き(角度)
        labelB.text = e.beta; // 縦の傾き(ipadでは横移動)
        labelC.text = e.gamma;// 横の傾き(ipadでは縦移動)

        // 水平方向の位置
        var horizontal = e.beta;// 横移動
        arrow.centerY = scene.height / 2 + horizontal;
        // 垂直方向の位置
        var vertical = e.gamma;// 縦移動
        arrow.centerX = scene.width / 2 + vertical;
        // 角度(矢印の向き)
        var rotation = e.alpha;// 向きだからここにe.alpha
        arrow.rotation = rotation;
    });

    //==========
    // ここまで
    //==========
}

function getRandom(start, end) {
    return start + Math.floor( Math.random() * (end - start + 1));
}

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);
    assets.push("images/title.png");
    core.preload(assets);
    core.onload = function(){titleStart();};
    core.start();
}

パワーメーター(長押しタイプ)

今回の授業は、パワーメーターの作り方でした。

以前のAIのところでは、クリックすると増えたり減ったりするパワーメーターでしたが、今度はタッチしている間増えるタイプです。

タッチしている間と右に長くなっていくメーターがあって、離すとその長さに比例したパワーでだるまが上に上がるという動きです。

   

 

 

 

 

 

 

今回、息子はパワーメーターという項目ができる前に、角度メーターが付けたいと言ってつくりはじめていたので、次回の発表の為にそれを完成させたいということで、授業のときはそちらをやっていたので、こちらは後から動画で確認しました。(有難いですね〜)

説明はコードの中に書いてみました。(以下青字の部分)

    // パワーメーター
    var meter = new Sprite(320, 24);
    meter.x = 0;
    meter.y = 240;
    meter.backgroundColor = "blue";
    scene.addChild(meter);

    // メーターのサイズ
    meter.originX = 0; // 拡大縮小の基準を左端にする(しなければ中心が基準)
    meter.scaleX  = 0; // 最初のサイズ320に対する割合。0.5なら半分の160になる。
ここでは320が初期値なので、power / powerMax(320) に変化させればよい。↓10行後
    var powerMax  = 320; // パワーの最大値を決めておく
    var power     = 0; // 初期値は0とする

    scene.addEventListener(Event.TOUCH_START, function() { // タッチしたら
        meter.scaleX = 0;// タッチすると320*0倍つまり0になる   
        power = 0;// powerの値は0に戻す
        meter.tl.delay(2); // タッチしてから2カウント待つ
        meter.tl.then(function(){ // 2カウント数え終わったら
            if(power < powerMax){ // パワーの値が320より少ないなら(320より
大きくしない為)
                power += 10; // power10増やす(タッチしている間)
                meter.scaleX = power / powerMax; // メーターの長さ初期値320
に対する割合なので、増えた分 / 320 の長さにすればOK          
            }           
        });
        meter.tl.loop();// meter.tlの部分をくりかえす(タッチしている間、2カウ
ント毎に+10ずつ増えていくのを繰り返すことになる)   
    });           
                                        
    scene.addEventListener(Event.TOUCH_END, function() { // タッチ離すと
        meter.tl.clear();// 離したところでメーターが止まる
        var impulseY = 60 * meter.scaleX;// インパルスの値を設定 powerで書く
と、60*power/320なので、power*0.1875になる
        daruma.addImpulse(0, impulseY);// 上で設定した値でだるまを飛ばす
    });

講義では、max値を320ではなくて30にして、1ずつ増えていくようにして説明されていました。

そうすると、メーターの30分の1ずつ変化することになります。上の場合は320で10なので、32分の1ずつ変化なのでほぼ同じです。(小さくするほど変化が遅くなる)

あと、気づいたのですが、最後の行の赤字のimpulseYのところは、上に飛ばしたいのだから素直にやればマイナスの数値だと思うのですが(講義でもここはpower *-1.0と説明されていた)ここでは正の数で、これでもできるのですね。地面に下に押し付けて、その反発で上に上がる様子になっているようです。

でもよく考えたらこちらのほうがリアルなのかもしれません。実際にジャンプするときは、地面を下方向に蹴ってその反発で上に上がっているので。ただしこうすると空中でタッチしたときには上には上がらず下にいくので、超魔界村のような二段ジャンプはできません

itch.io ゲームの投稿の仕方

ゲーム投稿サイト itch.io

英語のサイトなので、投稿の際は結構苦労しました。覚えとして残しておきます。

1 ユーザー登録

まずはユーザー登録をします。トップページの右上にある「Register」から登録画面に入ります。

 

 

 

 

 

 

 

2 ユーザー登録画面

 

 

3 ゲームの投稿

アカウントを作成すると、右上にアカウントが表示されるので、その下の矢印をクリックすると下にメニューが表示されます。

その中のUpload new projectを選択すると、ゲームのアップロード画面になります。

 

 

 

 

 

 

 

 

 

4 ゲームのアップロード画面

それぞれフォームに記入していきます。後から変更できるものがほとんどなので、よほど間違えなければ大丈夫だと思います。