おさるでポイポイ

クリックすると、猿がりんご、バナナ、さくらんぼのどれかをランダムに落とします。

落とした分だけ得点にカウントされますが、地面に落ちた分はマイナスされるので、かごに積んだ分が得点になります。

時間は50秒で、スコアと、りんご、バナナ、さくらんぼの数の分の絵が表示されます。

作り込みの授業のときに困ったところをいくつか先生に助言をいただいたおかげでそのときにすごく進めることができたみたいで喜んでいました。やっぱり一人で作るより助けを借りて作ったほうがいいですね。

最後に、結果画面のところに、「積んだ果物をそれぞれの個数分で画像で並べて出したい」と言い出して、(ノ∀`)アチャーまた難しそうなことを(ヤメテー)・・と思いましたが、偶然にも私の助言のおかげで(エッヘン)実現することができました。

その結果画面について、難しかったので覚えで書いておきます。息子に説明してもらいました。

何が難しいかというと、

①りんご、バナナ、さくらんぼの数はそれぞれその都度異なる。

②それぞれその都度異なる数のものを、等間隔に並べて表示する。(x座標の問題)

③端まできたら、次の行に移して並べていく。(x,y座標の問題)

どういうアルゴリズムでやればいいのでしょうか?

①りんご、バナナ、さくらんぼの数を配列にしてセーブします

var howmanyF = [0,0,0];//フルーツの数を入れておく配列をつくる(howmanyF[0]がりんごの数、
howmanyF[1]がバナナの数、howmany[2]がさくらんぼの数)

function dropBlock(){
    var fruits1 = new PhyBoxSprite(32,32, enchant.box2d.DYNAMIC_SPRITE,1,0.5,0);
    var rdm = getRandom(0,2);
    //ifと同じだが2つ以上だとこっちが楽
    switch(rdm){
        case 0: fruits1.image= core.assets["images/cf307/monkeyapple.png"];
            howmanyF[0] +=1;//りんごを落としたらりんごの数が+1される
            break;
        case 1: fruits1.image= core.assets["images/cf307/banana.png"];
            howmanyF[1] +=1;//バナナを落としたらバナナの数が+1される
            break;
        case 2: fruits1.image = core.assets["images/cf307/sakuranbo.png"];
            howmanyF[2] +=1;//さくらんぼを落としたらさくらんぼの数が+1される
            break;
    }

(略)

// (2)当たり判定(ground x group)//フルーツが地面に当たったら
ground.addCollision(group);
ground.on(Event.COLLISION, function(e){
    //console.log(e);
    var target = e.collision.target;
    target.remove();
    switch(target.image){
        case core.assets["images/cf307/monkeyapple.png"]:
            console.log("aplle");
            howmanyF[0] -=1;//りんごだったらりんごの数を-1する
            break;
        case core.assets["images/cf307/banana.png"]:
            console.log("banana");
            howmanyF[1] -=1;//(以下同様)
            break;
        case core.assets["images/cf307/sakuranbo.png"]:
            console.log("さくらんぼ");
            howmanyF[2] -=1;//(以下同様)
            break;
    }
    if(score != 0){
        score--;
    }
});

rope.tl.delay(16);
rope.tl.then(function(){
    timer--;
    console.log("あと"+ timer + "秒");
    timelabel.text = "TIME: " + timer;
    if(timer <= 10){
        timelabel.color = "red";
    }
    if(timer == 0){//0秒になったら
        BGM.stop();//音を止める
        console.log("おしまい");
        console.log(howmanyF);
        localStorage.setItem("howmanyF", howmanyF);//howmanyFをセーブする
        localStorage.setItem("monkey_score", score);
        endStart();
    }
});
rope.tl.loop();

②りんご、バナナ、さくらんぼをセーブした個数分画像で並べる

例えば、りんごを15個並べたいとき、10個目までは32ピクセルずつずらしてならべ、11個目からはまたx座標は0に戻さないといけないのです。

x座標で言うと、0のときは0、1のときは32×1、2のときは32×2、・・・9のときは32×9、そして10のときはまた0に戻る、これはどう書けばいいのか・・

息子は悩みはじめました。こんな数学の問題は学校ではでてきません。どんな数式でしょうか。

要は0〜9まではそれぞれ対応して0〜9で、10〜19までまた戻って0〜9、20から29までまた戻って0〜9(この繰り返し)になればいいのです。だから、「10で割ってその余りの数の・・」というところまで息子は考えたのですがわかりませんでした。

そこで私が思い出したのは、ブロック崩しもどきのゲームを作ったときに、先生のサンプルから真似して見よう見まねで書いたものの中に、ブロックをきっちりすきまなく順番に並べて置いていくというものを作ったことをうっすら思い出しました。意味もわからず見よう見まねで書いたものがあったよね、それがもしかしたら参考になるのでは?と息子に言って見せると

block.x = 0 + ((i % 40) * 8);//(iを40で割った数の余り)×8
block.y = 0 + (Math.floor(i / 40) * 8);//(iを40で割った商の小数点を切り捨てた数)×8

↑これだ!これがやりたかったの!これのことだったんだ!と感激していました。

これって・・言われればそのとおりだけど、思いついた人はすごいと思う。

%というのは、「割った余りの数値」です。Math.floorとは「小数点を切り捨て」です。

上の例だと、8ピクセルのブロックを40個ずつ並べたいので、iを40で割った余りの8倍をx座標にすれば40までは i (×8)ずつ増えて40で0に戻り、iを40で割った商の小数点を切り捨てた数の8倍をy座標にすれば、iが40増えるごとに 1(×8)ずつ増えるので40個ずつで行が下に行くことになります。

ということで、母のかすかな記憶のおかげで偶然にも解決できました。見よう見まねで作ってみたものがこんなところで役に立つとは。おかげで私も、初めてこれの意味を理解することができました。

さらに、ブロックは一種類でしたが今回はりんご、バナナ、さくらんぼを三種類を画像を変えて並べないといけないので、iの数を前の果物の数だけ加えてずらすという工夫が必要でした。

以下コメント部分はゆうじの解説です。

function endStart(){// 結果画面
    scene = new Scene();
    core.replaceScene(scene); core.resume();

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

    var sound = core.assets["sounds/cf307/trumpet1.mp3"].clone();
    sound.play();

    Fruits = Class.create(Sprite, { // フルーツというクラスを、Spriteを継承させてつくる
        initialize: function(image){ // クラスのものをつくるときに行う imageが引数
            Sprite.call(this,32,32); // 継承したものを呼び出す(これ、32,32)
            this.image = core.assets[image]; //そのクラスのプロパティを設定
        }
    });

    var howmany = localStorage.getItem("howmanyF"); // howmanyFで名付けた配列を取り出して
                                                     howmanyと名付ける
    var howmanyA = howmany.split(","); // howmanyを区切って取りしたものをhowmanyAとする
    var howmanyN =[];// 空っぽの配列を作る
    for (var n in howmanyA) { // howmanyAの中身を順番に数字にする(長さ分だけ繰り返す例えば、
                               2,3,5とかなら3)
    var N = Number(howmanyA[n]); // howmanyAのn番目を文字列から数値にする(文字列として保存
                                 されているから)
    howmanyN.push(N); // 数値にしたものを作っておいた空っぽの配列howmanyNに入れる 
    console.log(howmanyN); // howmanyNというのは(りんごの数、バナナの数、さくらんぼの数)
                            という配列なので、howmanyN[0]はりんごの数、
                            howmany[1]はバナナの数、howmany[2]はさくらんぼの数になる
    }

   //林檎をさっき皿の上にのったかずだけ出す
    for(var Fx = 0; Fx < howmanyN[0]; Fx++){ // りんごの数分だけ繰り返す(howmanyN[0]という
                           のが今りんごの個数になっているのでその分繰り返す)
        var apple = new Fruits("images/cf307/monkeyapple.png");
        apple.x = 0 + ((Fx % 10) * 32); // 10個まで32ずつずらして並べ、かつ10個ずつで折り返し
                        たいので、「りんごの個数を10で割った余り×32」をx座標と
                        する
        console.log(apple.x);
        apple.y = (Math.floor(Fx / 10) * 32); // 10個ならべたら次の行にするには「りんごの数を
                           10で割った数の小数点を切り捨てた数×32」をりんご
                           のy座標とする
        scene.addChild(apple);
    }

  //バナナをその続きからだす
    for(var Fx2 = 0; Fx2 < howmanyN[1]; Fx2++){
        var banana = new Fruits("images/cf307/banana.png");
        banana.x = 0 + (((Fx2 + Fx) % 10) * 32);// バナナもりんごと同様だが、りんごの後に並べるので
                             りんごの分をずらさないといけないので個数はりんごの
                             分も加えた(Fx2 + Fx)とする
        console.log(Fx);
        banana.y = (Math.floor((Fx2 + Fx) / 10) * 32);
        scene.addChild(banana);
    }

    //さくらんぼをバナナの続きからだす
    for(var Fx3 = 0; Fx3 < howmanyN[2]; Fx3++){
        var cherry = new Fruits("images/cf307/sakuranbo.png");
        cherry.x = 0 + (((Fx3 + Fx2 + Fx) % 10) * 32);// りんごとバナナの分ずらさないといけないの
                                でその分の個数を加えた(Fx3 + Fx2 + Fx)と
                                する
        console.log(Fx3);
        cherry.y = (Math.floor((Fx3 + Fx2 + Fx) / 10) * 32);
        scene.addChild(cherry);
    }

    scene.backgroundColor = "green";
    var score = localStorage.getItem("monkey_score");

    var scorelabel = new Label("結果: " + score);
    scorelabel.x = 320 / 2 -100;
    scorelabel.y = 340;
    scorelabel.color = 'yellow';
    scorelabel.font = "48px 'PixelMplus10'";
    scene.addChild(scorelabel);
    //console.log(scorelabel);

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

}

【感想】

今回の発表は、発表用の原稿の紙がなかったこともあって、アドリブの効かない息子は、デモプレイを一回やったっきり、だんまりで間が持たず、(デモプレイを繰り返ししていればよかったのにそれもなぜかせず ^♢^; )、苦労したんだからそのあたりもうちょっと説明したらいいのになと可笑しかったのですが、それも一つの個性なので、まあいいかと思います。(みんな違ってみんないい)

奇をてらわずシンプルで、息子の真面目さが出たようなゲームだと思いました。ゲームとして突き抜けたようなアイデアはないけれど、こういうふうに動かしたい、こういうものを出したいという構想を思い描いたとおりに実現させました(プログラミングの醍醐味だね)。そういうところを見ると、もしかすると息子はゲームよりも、実用的なアプリとかシステムをつくるプログラミングのほうが向いているかも知れないなと思いました。

ゲーム投稿サイト9leapに投稿してみました。こちらは得点の登録ができるプラグインがあるので、得点ランキングがでるところがいいです!

http://9leap.net/games/5633

グループの中の最上位置の判定とスクロール

下書きしたまま放置の記事が結構ありますが、あんまりきちんとと堅苦しくなると続けられなくなるので、思いつくまま軽い感じでいきたいと思います。

今日の授業は最近の中でも面白く感じたので、記録として書いておきます。

今チャレンジしているゲームですが、実は息子は、今回のテーマの初回の授業を受けた後から、偶然にも、先生がモデルにされたサリータワーと全く同じようなものを作りたいと作り始めていました。左右に移動するもの(授業ではヘリ)から物(授業ではブロック)を落として、積み上げるというものです。

前回の授業では、ブロックが積み上げられたら、ヘリにぶつかってしまうので、そうならないようにヘリを上昇させるにはどうしたらいいでしょうね、というところで終わりました。

息子は続きを作るべく、そこに挑んでみましたが、これは難しいのです。なぜなら、「ブロックが上のほうまで積まれてきたら」というのが問題なのですが、これを判定しようにも、ブロックは一番上の位置から落としているのです。落とす時点で既に一番上にあるのですから、「ブロックの位置から・・」とやろうとすると、落とす時点でもう積んでいる。だから「積まれたブロックの一番上のものから・・」とやろうとするなら、いつ判定するのか、どうやって判定するのかということになります。この時点で、私はもう面倒くさそうと思ってしまい、授業では擬似的な方法で簡単にそれっぽく見せる方法を採られるのではないかと思っていました。

そこで、今日の授業です。授業で示された方法は、「ブロックを落としてから2秒後(連続して落としていても最後に落としたものから2秒後)に、落としたブロックをすべて調べて一番上にあるものの高さを取り、それとヘリの高さを比較して一定以上近ければ離す」というものでした。

先生は「如何に誤魔化すか」と言われましたが、私は結構な正攻法じゃないかと思ってさすが先生と思いました。

    // タッチするとブロックが落ちる(と同時にreadyAlt()が発動)
    scene.on(Event.TOUCH_START, function(e){
        dropBlock();
        // (1) タッチしたタイミングで処理を開始しよう
        readyAlt();    
    });

    function dropBlock(){
        var block1 = new PhyBoxSprite(31, 31, enchant.box2d.DYNAMIC_SPRITE);
        block1.image = core.assets["images/do_daruma.png"];
        block1.x = heli.centerX;
        block1.y = heli.centerY;
        group.addChild(block1);
    }

    // (2) 処理を遅れさせよう(readyAlt()の中身)
    function readyAlt(){
       scene.tl.clear();// これを入れることで、落とす度にリセットされ、最後に落としたものからのカウントになる
       scene.tl.delay(32);// 32フレーム経ってからceckAlt();が発動
       scene.tl.then(function(){
          checkAlt(); 
       });
    }

    // (3) ブロックを調べる処理(checkAlt()の中身)
    function checkAlt(){
        console.log("高さを調べる");
        // (4-1) グループで最も高い位置にあるブロックを調べる
        //for(初期化; 条件; 後処理;){処理}
        var borderY = 480;// 画面の一番下。これよりブロックは下へ行くことがないので、これをブロックの最上位の位置の初期値とする。
(これとブロックの位置と比較して、それより上の位置なら最上位として更新するという仕組み)
        for(var i=0; i<group.childNodes.length; i++){ // ブロックの数だけ繰り返す(ブロック全部調べる)
            console.log(i);
            var y = group.childNodes[i].y;// ブロックのi番目のy座標が取得される。
            console.log(y);
            //高い位置にあれば更新
            if(y < borderY){
                borderY = y;// 繰り返すことで最終的には一番高いyがborderYとなる。
            }
        }
        console.log("最も高い位置にあるのは:" + borderY);
        // (4-2) 高い位置から一定の距離を保つ
        var padding = 130;// ヘリと一番上のブロックの距離
        if(borderY < heli.y + padding){
              heli.y = borderY - padding;
        }
    }
    
    // (5) スクロールさせる
    scene.setScrollRange(heli,80,0);// ヘリが上に上がっていくと見えなくなるのでスクロールさせる

どうやって一番上のブロックのy座標を取り出すのかというと、まずブロックはy座標480より下に行くことはないので、borderYの初期値を480としておきます。そして、1個めのブロックのy座標と比較して、これより上ならそのy座標の値をborderYに書き換え、さらに2個めのブロックも同様・・とブロックの個数分つまり全部で繰り返せば、最終的にborderYが一番高いブロックのy座標の値に書き換えられているという訳です。プログラミングではこうやってやるんだーと感心、納得。

こうして順序立てて教えていただくと、想像したより難しくなかったですし、考え方ややり方はほかにも色々応用できそうです。