Herlockで始めるアプリの実装(3) [Edit]

こんにちは、最近アイスで生き延びているんじゃないかって思っているソニックムーブのみかちです。ありがとう!オフィスグ〇コさん!ありがとう!ジャイアント〇ーン!そんな私がアイスに夢中になっていても、Herlockは変わらず全力開発中です。

前回から、サンプルアプリでリリースしている「ねこ穴」のゲーム実装について下記の2回に分けてご説明させていただいております。

  1. ランダムでねこが飛び出る
  2. ねこをフリックすると得点が加算される

今回は「2.ねこをフリックすると得点が加算される」についてご紹介させていただきます。


前回と同様に細かい部分は割愛させていただきますので、詳細はコチラを参考にしてください。
「ねこ穴」は各ストアで公開しておりますので、まだダウンロードされてない方はぜひダウンロードしてみてください!
ねこ穴ダウンロードはコチラ⇒Google PlayApp Store

【ねこをフリックすると得点が加算される処理】

大まかな流れは、ねこをタップ=>ねこを飛ばす=>得点の算出となります。
それでは、下記の項目順に説明してきます。

  1. タッチイベントの検出
  2. 前回の座標からの差分を検出してフリックのベクトルを算出
  3. 当たり判定
  4. 得点計算
  5. 得点の保持(おまけ)

1.タッチイベントの検出

タッチイベントの検出はstageの"touchBegin"、"touchMove"、"touchEnd"等のイベントとして検出します。
タッチ座標はeventのプロパティとして取得することができます。

stage.addEventListener( "touchBegin", function ( event ) {
	console.log(event.stageX);
	console.log(event.localY);
} );

今回はstageのイベントを使用しますがInteractiveObjectを継承したものは全てタッチイベントを検知することができます。

[実行結果]
取得したタッチ座標を表示するようにしています。このようにタッチイベントを検出し、タッチ座標を取得することができます。

Herlock

2.前回の座標からの差分を検出してフリックのベクトルを算出

フリックされた方向を検出するために前回の座標からの差分を算出します。今回は単純にxy座標を保持して、毎回計算する方式をとっています。
まず、フリックのベクトルを保持する変数を定義します。
詳細は後述となりますが、ねこの捕獲処理は下記のtouchVectorを元に動作を算出することになります。

var touchVector = {
    x: 0,
    y: 0
};

次に各タッチイベントのリスナを設定します。

//前回のタッチ座標
var touchPrev = null;

//タッチが開始されると座標を保持します
stage.addEventListener( "touchBegin", function ( e ) {
    touchPrev = {
        x: e.stageX,
        y: e.stageY
    };
} );

//タッチの動きを監視してベクトル計算と座標の保持をします
stage.addEventListener( "touchMove", function ( e ) {

	if ( touchPrev === null ) return;

    var touchNow = {
        x: e.stageX,
        y: e.stageY
    };
    
    touchVector = {
        x: touchNow.x - touchPrev.x,
        y: touchNow.y - touchPrev.y
    };
    touchPrev = touchNow;
} );

//タッチの終了でベクトルをリセットします
stage.addEventListener( "touchEnd", function ( e ) {
    touchVector = {
        x: 0,
        y: 0
    };
} );

[実行結果]
前回の座標:touchPrevとタッチ終了時の座標:touchNowの差分でベクトル座標:touchVectorを算出しています。

Herlock

3.当たり判定

タッチ位置の当たり判定は各ねこオブジェクトのtouchRollOverイベントを検知して行います。

cat.addEventListener( "touchRollOver", onCatchHandler );

onCatchHandlerに当たり判定時の処理を記述します。

function onCatchHandler( event ) {
    
    var cat = event.target;
	
    //タップされたフラグを立てます
    cat.isCaught = true;

    var p = new Point( touchVector.x, touchVector.y );
    //touchVectorに値が無い場合(タッチ後移動していない)はランダムでスピードを算出
    if ( p.x === 0 && p.y === 0 || isNaN( p.x ) || isNaN( p.y ) ) {
        p.x = Math.random() - 0.5;
        p.y = Math.random() - 0.5;
    }
    
    //速度を丸める
    p.normalize( 20 );
    
    cat.speedX = p.x;
    cat.speedY = p.y;
    
    //飛ぶ角度に合わせてねこの向きを調整します
    var degree = 180 * Math.atan2( p.y, p.x ) / Math.PI;
    
    if ( cat.speedX < 0 ) {
        cat.scaleX = -1;
        cat.rotation = -30 + degree - 180;
    } else {
        cat.rotation = 30 + degree;
    }
	
    //2回目のタップは処理しないのでイベントリスナを解放します
    cat.removeEventListener( "touchRollOver", onCatchHandler );
}

※前回のコードの速度はY方向のみでしたが、今回はspeedX、speedYで分けて管理をしています。
※もう1点注意点はcatはBitmapではなく、Spriteである必要があります(タッチイベント検出のため)。

4.得点計算

得点計算です。固定の値をタップ毎に加算します。
下準備として、新規レイヤーを用意し、スコア表示用のテキストフィールドを表示させます。

var scoreStage = new Stage( 640, 640 );
	var scoreText = new TextField();
	scoreText.x = 10;
	scoreText.y = 10;
	scoreText.defaultTextFormat = new TextFormat( null, 50 );
	var scoreLayer = new Layer( scoreStage );
	scoreLayer.verticalAlign = "top";
	scoreLayer.horizontalAlign = "left";
	window.addLayer( scoreLayer );
	scoreStage.addChild( scoreText );
	scoreText.text = "score: 0";
	scoreText.autoSize = "left";

スコアの加算処理です。スコア加算用の変数と、加算値を用意します。今回は加算値は「20」とします。

var totalPoint = 0;
var POINT_TAP = 20;

得点の加算処理を先ほどのonCatchHandlerに追記します。

totalPoint += POINT_TAP;
scoreText.text = "score: " + totalPoint;

[実行結果]
表示はシンプルですが、得点が加算されて表示されました。

Herlock

5.得点の保持

今回のコードにゲーム終了時の処理はないので、おまけとなりますが、HerlockではHTML5のlocalStorage同様の機能がありますので、過去の最高得点を保持する事にします。

var STORAGE_ITEM = "bestScore";
function getBestScore(currentScore) {
    var storage = window.localStorage;
    var bestScore = storage.getItem( STORAGE_ITEM );
    if ( currentScore > bestScore || bestScore === null ) {
        storage.setItem( STORAGE_ITEM, currentScore );
        bestScore = currentScore;
    }
    return bestScore;
}

getBestScore(totalPoint)などとして最高得点を得る事ができます。

前回と今回で「ねこ穴」のゲーム実装でメインとなる部分は説明は終了となります。
この他にタップした時のイベントに音声を入れたり、はじかれるアニメーションを入れるなどの演出を入れることによってストアで公開している「ねこ穴」のアプリは完成です。それでは最後に前回と今回のすべてのコードをつなげてみます。

/***********************************
  初期化及び設定値の定義
***********************************/

// Stageを初期化します
var stage = new Stage( 640, 960 );
var layer = new Layer( stage );

//今回ははみ出す事前提でscaleModeをnoBorderにします
layer.scaleMode = "noBorder";
window.addLayer( layer );

// Score表示
var scoreStage = new Stage( 640, 640 );
var scoreText = new TextField();
scoreText.x = 10;
scoreText.y = 10;
scoreText.defaultTextFormat = new TextFormat( null, 50 );
var scoreLayer = new Layer( scoreStage );
scoreLayer.verticalAlign = "top";
scoreLayer.horizontalAlign = "left";
window.addLayer( scoreLayer );
scoreStage.addChild( scoreText );
scoreText.text = "score: 0";
scoreText.autoSize = "left";

//得点
var totalPoint = 0;
//加算値
var POINT_TAP = 20;


//素材定義
var materials = {
    "bg": {
        type: "image",
        url: "assets/images/background.png"
    },
    "cat1": {
        type: "image",
        url: "assets/images/cat1.png"
    },
    "cat2": {
        type: "image",
        url: "assets/images/cat2.png"
    },
    "cat3": {
        type: "image",
        url: "assets/images/cat3.png"
    }
};


var CatType = {
    Cat1: null,
    Cat2: null,
    Cat3: null
};

// TOUCH TRACKING
var touchVector = {
    x: 0,
    y: 0
};

var touchPrev = null;
stage.addEventListener( "touchMove", function ( e ) {
    var touchNow = {
        x: e.stageX,
        y: e.stageY
    };
    if ( touchPrev === null )
    return null;
    touchVector = {
        x: touchNow.x - touchPrev.x,
        y: touchNow.y - touchPrev.y
    };
    touchPrev = touchNow;
} );

stage.addEventListener( "touchEnd", function ( e ) {
    touchVector = {
        x: 0,
        y: 0
    };
    
} );

stage.addEventListener( "touchBegin", function ( e ) {
    touchPrev = {
        x: e.stageX,
        y: e.stageY
    };
} );

/***********************************
  読み込みと初期配置
***********************************/
var loader = new BulkLoader( materials );
loader.onload = startHandler;


function startHandler() {

    //背景を設置します
    stage.addChild( new Bitmap( new BitmapData( loader.get( "bg" ) ) ) );

    //CatTypeにBitmapDataを設定します
    CatType.Cat1 = new BitmapData( loader.get( "cat1" ) );
    CatType.Cat2 = new BitmapData( loader.get( "cat2" ) );
    CatType.Cat3 = new BitmapData( loader.get( "cat3" ) );

    //enterFrame
    stage.addEventListener( "enterFrame", updateHandler );

}

/***********************************
  更新及びアニメーション処理
***********************************/
var updateCount = 0;
var cats = [];
var removes = [];

function updateHandler() {
    
    updateCount++;
    
    //カウンターが10で割り切れるフレームのときに==10フレームに一回処理される
    if ( updateCount % 10 === 0 ) {
        //偏りを作る
        if ( Math.random() < 0.6 ) {
            //ねこを生成して配置する
            var cat = createCat();
            cats.push( cat );
            stage.addChild( cat );
        }
    }
    

    //catsを処理する
    for ( var i = 0; i < cats.length; i++ ) {
        
        cats[i].y -= cats[i].speedY;
        cats[i].x += cats[i].speedX;
        
        //Y位置判定
        if ( cats[i].y < 100 && !cats[i].isCaught) {
            cats[i].speedY *= -1;
        } 
        else if ( cats[i].y < -100 ) {
            //画面上端より上に出た時
            removes.push( cats[i] );
        } 
        else if ( cats[i].y > 1100 ) {
            //管理配列から削除
            //loop中にindexのサイズを変えたくないので事後処理要の配列に入れる
            removes.push( cats[i] );
        }
        
        //X座標判定
        if ( cats[i].x < -100 || cats[i].x > 640) {
            removes.push( cats[i] );
        }
    }

    //事後処理要(管理配列から削除)
    if ( removes.length > 0 ) {

        for ( var j = 0; j < removes.length; j++ ) {

            var removeIndex = cats.indexOf( removes[j] );

            if ( removeIndex >= 0 ) {
                //ステージから削除
                stage.removeChild( removes[j] );
                //管理配列から削除
                cats.splice( removeIndex, 1 );
            }
        }

        removes = [];
    }

}

    
function onCatchHandler( event ) {
    
    console.log("catch");
    
    var cat = event.target;
    cat.isCaught = true;

    var p = new Point( touchVector.x, touchVector.y );
    //touchVectorに値が無い場合(タッチ後移動していない)はランダムでスピードを算出
    if ( p.x === 0 && p.y === 0 || isNaN( p.x ) || isNaN( p.y ) ) {
        p.x = Math.random() - 0.5;
        p.y = Math.random() - 0.5;
    }
    
    //速度を丸める
    p.normalize( 20 );
    
    cat.speedX = p.x;
    cat.speedY = p.y;
    
    //飛ぶ角度に合わせてねこの向きを調整します
    var degree = 180 * Math.atan2( p.y, p.x ) / Math.PI;
    
    if ( cat.speedX < 0 ) {
        cat.scaleX = -1;
        cat.rotation = -30 + degree - 180;
    } else {
        cat.rotation = 30 + degree;
    }
    
    //得点の加算処理
    totalPoint += POINT_TAP;
    scoreText.text = "score: " + totalPoint;
    
    //2回目のタップは処理しないのでイベントリスナを解放します
	cat.removeEventListener( "touchRollOver", onCatchHandler );
}

/***********************************
  ねこ生成関連の関数
***********************************/
function getCatType() {
    //0-2の整数をランダムで取得します
    var num = Math.floor( Math.random() * 3 );
    //CatTypeに設定した"Cat1"~"Cat3"が取得したいので下記のようにします
    return "Cat" + String( num + 1 );
}

function createCat() {
    
    var cat = new Sprite();
    var catBitmap = new Bitmap( CatType[getCatType()] );
    
    cat.addChild(catBitmap);
    
    //中心位置からの差分をMath.randomで決めx座標に代入
    cat.x = stage.stageWidth / 2 + ( Math.random() - 0.5 ) * 500;

    //y座標はひとまず固定にします
    cat.y = 800;

    //速度は20〜40の範囲でランダムとします
    cat.speedY = (20 + Math.random() * 20) * 0.3;
    
    cat.speedX = 0;
    
    //タップされたフラグ
    cat.isCaught = false;
    
    cat.addEventListener( "touchRollOver", onCatchHandler );

    return cat;
}

少しわかりにくいですが、実行結果が下図となります。
斜めに飛んでいるねこはタッチしたねこですね。

Herlock

2回にわたって「ねこ穴」を使って実用的なアプリ実装についてお話しさせていただきました。Herockでアプリを作るイメージをつかんでもらえていれば幸いです!
そろそろこの記事連載も終盤です!次回はライブラリの扱い方についてご説明できればと思います!


もう7月も半ば!開発も佳境に入り始めました。Herlockも毎日機能が充実していっています!そんな「Herlock」、クローズドベータ版の利用事前登録も引き続きよろしくお願いします!無料でご利用が可能ですので、ねこ穴みたいなゲームアプリを作ってみたい方は「Herlock」をお試しいただけると幸いです!

また、FacebookやTwitterでも開発状況や新しい情報を配信しておりますのでこちらもチェックしてみてください!
FB:https://www.facebook.com/herlock.do
Twitter:@herlock_do

コメント

この記事にコメントを書く

記事に対するテクニカルな質問はご遠慮ください(利用規約)。

その他の記事