センサー 回転軸

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

今回の課題は以下です。

// 端末の回転軸を取得しよう
    // 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と説明されていた)ここでは正の数で、これでもできるのですね。地面に下に押し付けて、その反発で上に上がる様子になっているようです。

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

PhyBoxSprite・PhyCircleSprite(Box2d)

四角いものはPhyBoxSprite クラスを使う。

 var ground = new PhyBoxSprite(320 * 100, 64, enchant.box2d.STATIC_SPRITE);

STATIC_SPRITEは、動かないもの。重力の影響を受けて動かしたいものには、DYNAMIC_SPRITEを使う。

 var daruma = new PhyBoxSprite(
     31,
     31,
     enchant.box2d.DYNAMIC_SPRITE,
     1, // 第四引数:density(密度)・・・省略時:1.0
     0, // 第五引数:friction(摩擦)・・・省略時:0.5
     0.3 //第六引数:restitution(反発)・・・省略時:0.3
);

 

丸いものには、PhyCircleSpriteクラスを使う。

var ball = new PhyCircleSprite(32 / 2, enchant.box2d.DYNAMIC_SPRITE);

第一引数には、半径を用いる。

四角いものでも可能。丸いものとしての動きになるみたい。

重力のある世界をつくる(Box2d)

 var world = new PhysicsWorld( 0, 9.8);
 scene.addEventListener(Event.ENTER_FRAME, function() {
     world.step(core.fps);
 });

二つの引数は、縦方向の重力と縦方向の重力。ここで9.8とは、9.8m/sということ。

この新しくつくったworldを、フレームごとに動かす(step)というのが、2行目。

fpsとは、フレームレート(Frames Per Second)のことで、ここでは16フレーム/秒。一秒の間に16回計算して動かしていますよという意味。(一秒の間に16回のstep)

ループキャンセル

ループで繰り返し動かしているものに別の動きを加えると、その加えた動きが新たにループに組み込まれてしまう。そこで一旦ループをキャンセルする。

特に、ループして動いているものに新たに別のループの動きを加えたい場合は、先のループをキャンセルしないと、二重でループがはたらき続けることになり、奇怪な動きになってしまうので注意。

例:左右に動いているたぬき。画面をタッチすると、近づいて、また左右に動く。

 // 狸を表示
 var tanuki = new ExSprite(32, 32);
 tanuki.image = core.assets["images/e_tanuki.png"];
 tanuki.x = 160 - 16;
 tanuki.y = 0;
 scene.addChild(tanuki);

 // 最初のループの動き
 tanuki.tl.moveBy(100, 0, 16);
 tanuki.tl.moveTo(160 - 16, 0, 16);
 tanuki.tl.moveBy(-100, 0, 16);
 tanuki.tl.moveTo(160 - 16, 0, 16);
 tanuki.tl.loop();
 console.log("1個目のループ");

 // タッチすると
 scene.addEventListener(Event.TOUCH_START, function(){
      // 近づく
     tanuki.tl.unloop();// 先にはたらいていた1個目のループをキャンセル
     tanuki.tl.clear(); //これを入れないと、途中で割り込めない
     tanuki.tl.moveTo(160 - 16, 240 - 16, 16);
     tanuki.tl.moveTo(160 - 16, 100, 16);
     // その後また左右にループする動き
     tanuki.tl.then(function(){
         tanuki.tl.moveBy(100, 0, 16);
         tanuki.tl.moveTo(160 - 16, 100, 16);
         tanuki.tl.moveBy(-100, 0, 16);
         tanuki.tl.moveTo(160 - 16, 100, 16);
         tanuki.tl.loop();
         console.log("2個目のループ");
     });
 });

サンプル 画面タッチでたぬきが移動して別のループに切り替わる

 

パワーメーターの作り方

パワーメーター

スプライトの幅を、HPと連動させることによって作ります。

まず、パワーメーターを設置します。

 // (1)パワーメーター
 var pMeter = new ExSprite(310, 10);
 pMeter.x = 5;
 pMeter.y = 5;
 pMeter.backgroundColor = "lightgreen";
 scene.addChild(pMeter);
 pMeter.originX = 0; 

ボスのHPの初期値は10とします。

 boss.hp = 10;

ボスのHPが1減るごとに、パワーメーターの幅が10分の1ずつ減るようにするには、パワーメータの幅を、boss.hpの値の10分の1の値の倍率にします。

(考え方:HPが10→9になったら、パワーメーターの幅を10分の9(9割)の幅、8になったら8割の幅、7なら7割の幅‥(以下同様)なので、パワーメーターの幅を、HPの10分の1のスケールの倍率にすればよい)

 // 判定
 function judge(){
 ・・・
    // (2)パワーメーターの幅を計算
    pMeter.scaleX = boss.hp / 10;
 }

さらに、このままではHPに制限がなく、パワーメーターも無限に伸びてしまうので、boss.hpに制限をかけるには、当たり判定のところで、以下のようにします。(0以上10以下の範囲で制限)

 // 当たり判定
 boss.addEventListener(Event.COLLISION, function(e){  
     // ボスにヒット
     boss.hp += e.collision.target.power;
     if(boss.hp < 0){
         boss.hp = 0;
     }
     if(boss.hp > 10){
         boss.hp = 10;
     }
     // 弾を消す
     e.collision.target.remove();
 });

パラメータ(衝突対象)

衝突対象にパラメータをつけて、衝突したときのダメージやプラスの値を対象毎に簡単に変えることができる方法。

bulletGroup.addChild(bullet1);
bulletGroup.addChild(bullet2);
boss.addCollision(bulletGroup);

としたとき、

bullet1.damage = -1;
bullet2.damege = 1;

とパラメータをつくって、衝突判定のところで

boss.hp += e.collision.target.damage;

としておけば、bullet1に衝突したときはboss.hpが-1、bullet2に衝突したときは+1の変化とすることができる。(e.collision.targetがbullet1だったりbullet2だったりするというわけ)

複数の条件分岐 else if(){}

条件分岐を複数組み合わせることができる。

上から順番に条件に当てはまるか判定をして処理をしていくので、上から優先的になるように書くことに注意する。(ふるいにかけていくようなイメージ)

if (条件①){
    条件①に当てはまるときにやる処理;
}else if (条件②) {
    条件①には当てはまらなくて、条件②に当てはまるときにやる処理;
}else if (条件③) {
    条件①にも条件②にも当てはまらなくて、条件③に当てはまるときにやる処理;
}else{
    上からのすべての条件にすべて当てはまらない時にやる処理;
}

例:10より大きい(11〜)→最強
  7より大きい(8〜10)→強い
  4より小さい(〜3)→弱い
  それ以外(4〜7)→普通 
  (少数以下がない場合)

if (boss.hp > 10){ 
    console.log("最強"); 
}else if (boss.hp > 7) { 
    console.log("強い"); 
}else if (boss.hp < 4) { 
    console.log("弱い"); 
}else{ 
    console.log("普通"); 
}