前回に引き続き、ブロック崩しの作成について、説明していきます。
ゲームオーバーや、残機設定等の細かな処理の追加となりますので
複数個所に渡って変更があります。
始めに最終のソースコードを記載いたします。
強調文字になっている箇所が今回の修正・追加対象となります。
- /*
- グローバル変数
- */
- //1Mを何pxで表現するか
- var PTM_RATIO = 32;
- //cocos2d-xのlayer
- var layer = null;
- //box2d の world
- var world = null;
- //box2d関連
- var b2BodyDef = Box2D.Dynamics.b2BodyDef;
- var b2Body = Box2D.Dynamics.b2Body;
- var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
- var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
- var b2Vec2 = Box2D.Common.Math.b2Vec2;
- var b2World = Box2D.Dynamics.b2World;
- //画面上部の境界線
- var spr_disp_upper = null;
- //画面下部の境界線
- var spr_disp_bottom = null;
- //画面左側の境界線
- var spr_disp_left = null;
- //画面右側の境界線
- var spr_disp_right = null;
- /*
- * 弾関連
- */
- var ball_moved = false;
- //弾の幅
- var BALL_WIDTH = 5;
- //弾の高さ
- var BALL_HEIGHT = 5;
- //弾のsprite
- var spr_ball = null;
- //弾の速度
- var ball_velocity = 8;
- //弾の角度
- //右方向を0度とし、下方向を90度、左方向を180度、上方向を270度とする
- //すると、x座標は cos、Y座標は -sin で算出することが出来る
- var ball_angle = 60.0 / 180;
- //弾の速度(時速 5M)
- var BALL_VELOCITY = 5;
- //弾のbody
- var body_ball = null;
- /*
- * バー関連
- */
- //バーのY座標位置
- var BAR_Y = 20;
- //バーの幅
- var BAR_WIDTH = 60;
- //バーの高さ
- var BAR_HEIGHT = 4;
- //バーのsprite
- var spr_bar = null;
- //バーのbody
- var body_bar = null;
- /*
- * ブロック関連
- */
- //ブロックの幅
- var BLOCK_WIDTH = 40;
- //ブロックの高さ
- var BLOCK_HEIGHT = 20;
- //ブロックのsprite
- var spr_block = new Array();
- //ブロックのbody
- var body_block = new Array();
- //ブロックが存在するかどうか
- var appear_block = new Array();
- /*
- *ゲーム用ステータス
- */
- var STAT_GAME_START = 1;
- var STAT_GAME_PLAY = 2;
- var STAT_GAME_END = 3;
- var STAT_GAME_OVER = 4;
- var block_status = STAT_GAME_START;
- //ブロック関連の残数
- var block_life = 3;
- var block_stage = 1;
- var block_count = 0;
- var overSprite;
- var clearSprite;
- var Helloworld = cc.Layer.extend({
- init:function () {
- this._super();
- var size = cc.Director.getInstance().getWinSize();
- //box2d world生成
- world = new b2World(new b2Vec2(0, 0), true);
- world.SetContinuousPhysics(true);
- var fixDef = new b2FixtureDef;
- fixDef.density = 1.0;
- //摩擦係数
- fixDef.friction = 0;
- //反発係数
- fixDef.restitution = 1;
- var bodyDef = new b2BodyDef;
- //create ground
- bodyDef.type = b2Body.b2_staticBody;
- fixDef.shape = new b2PolygonShape;
- //上下の境界線の幅と高さ設定
- fixDef.shape.SetAsBox(320.0 / PTM_RATIO, 1 / PTM_RATIO);
- //upper
- //ダミーでsprite設定
- //表示は行わない
- //CB関数用の変数となります
- spr_disp_upper = cc.Sprite.create( “res/boll.png” );
- bodyDef.position.Set(320.0 / PTM_RATIO / 2, 380.0 / PTM_RATIO);
- bodyDef.userData = spr_disp_upper;
- world.CreateBody(bodyDef).CreateFixture(fixDef);
- //bottom
- //ダミーでsprite設定
- //表示は行わない
- //CB関数用の変数となります
- spr_disp_bottom = cc.Sprite.create( “res/boll.png” );
- bodyDef.position.Set(320.0 / PTM_RATIO / 2, -1);
- bodyDef.userData = spr_disp_bottom;
- world.CreateBody(bodyDef).CreateFixture(fixDef);
- //左右の境界線の幅と高さ設定
- fixDef.shape.SetAsBox(1 / PTM_RATIO, 380.0 / PTM_RATIO);
- //left
- //ダミーでsprite設定
- //表示は行わない
- //CB関数用の変数となります
- spr_disp_left = cc.Sprite.create( “res/boll.png” );
- bodyDef.position.Set(0, 380.0 / PTM_RATIO / 2);
- bodyDef.userData = spr_disp_left;
- world.CreateBody(bodyDef).CreateFixture(fixDef);
- //right
- //ダミーでsprite設定
- //表示は行わない
- //CB関数用の変数となります
- spr_disp_right = cc.Sprite.create( “res/boll.png” );
- bodyDef.position.Set(320.0 / PTM_RATIO, 380.0 / PTM_RATIO / 2);
- bodyDef.userData = spr_disp_right;
- world.CreateBody(bodyDef).CreateFixture(fixDef);
- //ゲームオーバー
- overSprite = cc.Sprite.create(“res/gameover.png”);
- overSprite.setPosition(cc.p( size.width / 2, size.height / 2 ));
- overSprite.setAnchorPoint(cc.p(0, 0));
- //ゲームクリア
- clearSprite = cc.Sprite.create(“res/gameclear.png”);
- clearSprite.setPosition(cc.p( size.width / 2, size.height / 2 ));
- clearSprite.setAnchorPoint(cc.p(0, 0));
- //layerの生成
- var lazyLayer = new cc.Layer.create();
- this.addChild(lazyLayer);
- //背景spriteの生成
- var sprite = cc.Sprite.create( “res/bg.png” );
- sprite.setPosition(cc.p( size.width / 2, size.height / 2 ) );
- sprite.setScale( 1.0 );
- lazyLayer.addChild( sprite, 0 );
- //バーspriteの生成
- spr_bar = cc.Sprite.create( “res/bar.png” );
- spr_bar.setPosition( cc.p( size.width / 2 – 5, BAR_Y ) );
- this.addChild( spr_bar, 0 );
- body_bar = this.addNewSpriteWithCoords(spr_bar, cc.p( size.width / 2 – 5, BAR_Y ), BAR_WIDTH, BAR_HEIGHT, false);
- //ボールspriteの生成
- spr_ball = cc.Sprite.create( “res/boll.png” );
- spr_ball.setPosition( cc.p( size.width / 2 – 5, BAR_Y + BAR_HEIGHT + BALL_HEIGHT ) );
- this.addChild( spr_ball, 0 );
- //ブロックの生成
- var h_size = 0;
- for( var i = 0; i < 5; i++ ) {
- for( var j = 0; j < 8; j++ ) {
- spr_block[i * 8 + j] = cc.Sprite.create( “res/block_2.png” );
- spr_block[i * 8 + j].setPosition( cc.p( 20 + ( j * 40 ), 370 – h_size) );
- spr_block[i * 8 + j].setScale( 1.0 );
- this.addChild( spr_block[i * 8 + j], 0 );
- body_block[i * 8 + j] = this.addNewSpriteWithCoords(spr_block[i * 8 + j], cc.p( 20 + ( j * 40 ), 370 – h_size), BLOCK_WIDTH, BLOCK_HEIGHT, false);
- //ブロックが存在するかどうか
- appear_block[i * 8 + j] = true;
- block_count++;
- }
- h_size += 20;
- }
- //タッチを有効にする
- this.setTouchEnabled( true );
- //box2dを反映するため、スケジューリングする
- this.scheduleUpdate();
- //衝突イベントを検知する
- var listener = new Box2D.Dynamics.b2ContactListener;
- listener.BeginContact = function (contact) {
- var contactA = contact.GetFixtureA().GetBody().GetUserData();
- var contactB = contact.GetFixtureB().GetBody().GetUserData();
- if (contactA == spr_disp_upper || contactB == spr_disp_upper) {
- //上の壁
- }
- else if (contactA == spr_disp_bottom || contactB == spr_disp_bottom) {
- setTimeout(function () {
- //今のバーとボールを消す
- layer.removeChild(spr_bar);
- layer.removeChild(spr_ball);
- world.DestroyBody(body_bar);
- world.DestroyBody(body_ball);
- //再度、バーとボールを描画する
- //バー画像を設定する
- //sprite定義
- spr_bar = cc.Sprite.create( “res/bar.png” );
- spr_bar.setPosition( cc.p( size.width / 2 – 5, BAR_Y ) );
- layer.addChild( spr_bar, 0 );
- body_bar = layer.addNewSpriteWithCoords(spr_bar, cc.p( size.width / 2 – 5, BAR_Y ),BAR_WIDTH, BAR_HEIGHT, false);
- //ボール画像を設定するEIGHT, false);
- spr_ball = cc.Sprite.create( “res/boll.png” );
- spr_ball.setPosition( cc.p( size.width / 2 – 5, BAR_Y + BAR_HEIGHT + BALL_HEIGHT ) );
- layer.addChild( spr_ball, 0 );
- //ボールが動いていない状態にする
- ball_moved = false;
- //タッチも一旦破棄する
- layer.isMouseDown = false;
- //gamestart対処 Start
- //残機をマイナスする
- block_life–;
- if (block_life <= 0) {
- //ゲームオーバー
- block_status = STAT_GAME_OVER;
- layer.addChild(overSprite);
- var actionFadeIn = cc.FadeIn.create(0.5);
- var actionDelay = cc.DelayTime.create(1);
- var actionFadeOut = cc.FadeOut.create(0.5);
- var actionCb = cc.CallFunc.create(function () {
- layer.removeChild(overSprite);
- //残機を3に戻す
- block_life = 3;
- //ブロック数初期化
- block_count = 0;
- //ブロックを全消去して、再度配置し直す
- //ブロックを表示する
- var h_size = 0;
- for( var i = 0; i < 5; i++ )
- {
- for( var j = 0; j < 8; j++ )
- {
- //消去
- if (appear_block[i * 8 + j]) {
- layer.removeChild(spr_block[i * 8 + j]);
- world.DestroyBody(body_block[i * 8 + j]);;
- }
- //配置
- spr_block[i * 8 + j] = cc.Sprite.create( “res/block_2.png” );
- spr_block[i * 8 + j].setPosition( cc.p( 20 + ( j * 40 ), 370 – h_size) );
- spr_block[i * 8 + j].setScale( 0.5 );
- this.addChild( spr_block[i * 8 + j], 0 );
- body_block[i * 8 + j] = this.addNewSpriteWithCoords(spr_block[i * 8 + j], cc.p( 20 + ( j * 40 ), 370 – h_size), BLOCK_WIDTH, BLOCK_HEIGHT, false);
- appear_block[i * 8 + j] = true;
- block_count++;
- }
- h_size += 20;
- }
- }, layer);
- var actionSeq = cc.Sequence.create(actionFadeIn, actionDelay, actionFadeOut, actionCb);
- var action = cc.RepeatForever.create(actionSeq);
- }
- }, 1);
- }
- else if (contactA == spr_disp_left || contactB == spr_disp_left) {
- //左の壁
- }
- else if (contactA == spr_disp_right || contactB == spr_disp_right) {
- //右の壁
- }
- else {
- //その他はブロック
- for (var i = 0; i < spr_block.length; i++) {
- if (contactA == spr_block[i] || contactB == spr_block[i]) {
- layer.removeChild(spr_block[i]);
- //gamestart対処 Start
- //ブロックの数マイナス
- appear_block[i] = false;
- block_count–;
- //ブロックの数が0ならクリア
- if (block_count <= 39) {
- //今のバーとボールを消す
- layer.removeChild(spr_ball);
- world.DestroyBody(body_ball);
- //ボールが動いていない状態にする
- ball_moved = false;
- //タッチも一旦破棄する
- layer.isMouseDown = false;
- //クリア
- block_status = STAT_GAME_END;
- layer.addChild(clearSprite);
- var actionFadeIn = cc.FadeIn.create(0.5);
- var actionDelay = cc.DelayTime.create(1);
- var actionFadeOut = cc.FadeOut.create(0.5);
- var actionSeq = cc.Sequence.create(actionFadeIn, actionDelay, actionFadeOut);
- var action = cc.RepeatForever.create(actionSeq);
- clearSprite.runAction(action);
- }
- setTimeout(function () {
- world.DestroyBody(body_block[i]);
- }, 1);
- break;
- }
- }
- }
- //跳ね返った後の速度を取得し、遅くなっていたら補正する
- setTimeout(function () {
- var b2Vec2 = body_ball.GetLinearVelocity();
- var vect_x = b2Vec2.x;
- var vect_y = b2Vec2.y;
- if (vect_x * vect_x + vect_y * vect_y < (ball_velocity - 2) * (ball_velocity - 2)) {
- //かなり遅くなっている
- //補正する
- var b2Vec2 = Box2D.Common.Math.b2Vec2;
- var boost = false;
- for (var i = 1; i <= 20; i++) {
- if ((vect_x * (1 + 0.1 * i)) * (vect_x * (1 + 0.1 * i)) + (vect_y * (1 + 0.1 * i)) * (vect_y * (1 + 0.1 * i)) >= (ball_velocity – 2) * (ball_velocity – 2)) {
- body_ball.SetLinearVelocity(new b2Vec2(vect_x * (1 + 0.1 * i), vect_y * (1 + 0.1 * i)));
- boost = true;
- break;
- }
- }
- if (boost == false) {
- body_ball.SetLinearVelocity(new b2Vec2(vect_x * (1 + 0.1 * 20), vect_y * (1 + 0.1 * 20)));
- }
- }
- }, 100);
- };
- listener.EndContact = function (contact) {
- };
- world.SetContactListener(listener);
- //Spriteの生成
- var startSprite = cc.Sprite.create(“res/gamestart.png”);
- startSprite.setPosition(cc.p( size.width / 2, size.height / 2 ));
- layer.addChild(startSprite, 7);
- //フェードインの設定
- var actionFadeIn = cc.FadeIn.create(0.5);
- //Delayの設定
- var actionDelay = cc.DelayTime.create(1);
- //フェードアウトの設定
- var actionFadeOut = cc.FadeOut.create(0.5);
- //コールバックの設定
- var actionCb = cc.CallFunc.create(function () {
- layer.removeChild(startSprite);
- block_status = STAT_GAME_PLAY;
- },layer);
- //シーケンスの設定
- var actionSeq = cc.Sequence.create(actionFadeIn, actionDelay, actionFadeOut, actionCb);
- //アクション開始
- startSprite.runAction(actionSeq);
- return true;
- },
- addNewSpriteWithCoords:function (sprite, p, width, height, dynamic) {
- var bodyDef = new b2BodyDef();
- if (dynamic == true) {
- bodyDef.type = b2Body.b2_dynamicBody;
- bodyDef.position.Set(1.0 * p.x / PTM_RATIO, 1.0 * p.y / PTM_RATIO);
- bodyDef.userData = sprite;
- } else {
- bodyDef.type = b2Body.b2_staticBody;
- bodyDef.position.Set(1.0 * p.x / PTM_RATIO, 1.0 * p.y / PTM_RATIO);
- bodyDef.userData = sprite;
- }
- var body = world.CreateBody(bodyDef);
- var dynamicBox = new b2PolygonShape();
- dynamicBox.SetAsBox(1.0 * width / PTM_RATIO / 2.0, 1.0 * height / PTM_RATIO / 2.0);
- //Define the dynamic body fixture.
- var fixtureDef = new b2FixtureDef();
- fixtureDef.shape = dynamicBox;
- fixtureDef.density = 1.0;
- //摩擦係数
- fixtureDef.friction = 0;
- //反発係数
- fixtureDef.restitution = 1;
- //設定する
- body.CreateFixture(fixtureDef);
- return body;
- },
- update:function (dt) {
- var velocityIterations = 8;
- var positionIterations = 1;
- world.Step(dt, velocityIterations, positionIterations);
- for (var b = world.GetBodyList(); b; b = b.GetNext()) {
- if (b.GetUserData() != null) {
- var myActor = b.GetUserData();
- myActor.setPosition(cc.p(b.GetPosition().x * PTM_RATIO, b.GetPosition().y * PTM_RATIO));
- myActor.setRotation(-1 * cc.RADIANS_TO_DEGREES(b.GetAngle()));
- }
- }
- },
- onTouchesBegan:function (touches, event) {
- //ゲーム開始アニメーション中はタッチ無効
- if (block_status == STAT_GAME_START) {
- return;
- }
- if (block_status == STAT_GAME_END) {
- return;
- }
- if (block_status == STAT_GAME_OVER) {
- return;
- }
- },
- onTouchesMoved:function (touches, event) {
- //ゲーム開始アニメーション中はタッチ無効
- if (block_status == STAT_GAME_START) {
- return;
- }
- if (block_status == STAT_GAME_END) {
- return;
- }
- if (block_status == STAT_GAME_OVER) {
- return;
- }
- var size = cc.Director.getInstance().getWinSize();
- //タッチポイントの取得
- var touch_point = touches[0].getLocation();
- if(touch_point.x < BAR_WIDTH/2) {
- spr_bar.setPosition(cc.p( BAR_WIDTH/2, BAR_Y));
- }
- else if(touch_point.x > (size.width – BAR_WIDTH/2)) {
- spr_bar.setPosition(cc.p(size.width – BAR_WIDTH/2, BAR_Y));
- }
- else {
- spr_bar.setPosition(cc.p(touch_point.x, BAR_Y));
- }
- //box2d側も動かす
- body_bar.SetPosition(cc.p(spr_bar.getPositionX() / PTM_RATIO, spr_bar.getPositionY() / PTM_RATIO));
- },
- onTouchesEnded:function (touches, event) {
- //ゲーム開始アニメーション中はタッチ無効
- if (block_status == STAT_GAME_START) {
- return;
- }
- if (block_status == STAT_GAME_END) {
- return;
- }
- if (block_status == STAT_GAME_OVER) {
- return;
- }
- if (ball_moved == false) {
- var size = cc.Director.getInstance().getWinSize();
- body_ball = this.addNewSpriteWithCoords(spr_ball, cc.p( size.width / 2 – 5, BAR_Y + BAR_HEIGHT / 2 + BALL_HEIGHT / 2 ), BALL_WIDTH, BALL_HEIGHT, true);
- var b2Vec2 = Box2D.Common.Math.b2Vec2;
- //弾の発射角度を調整 Start
- var ball_angle_work = Math.random() * 40;
- if (ball_angle_work < 20) {
- ball_angle_work = -1 * ball_angle_work – 20;
- } else {
- ball_angle_work = ball_angle_work;
- }
- ball_angle = ball_angle_work / 180;
- body_ball.SetLinearVelocity(new b2Vec2(-1 * ball_velocity * Math.sin(ball_angle), ball_velocity * Math.cos(ball_angle)));
- ball_moved = true;
- }
- },
- });
- var HelloWorldScene = cc.Scene.extend({
- onEnter:function () {
- this._super();
- layer = new Helloworld();
- layer.init();
- this.addChild(layer);
- }
- });
おおまかな説明としましては
ゲームクリア・ゲームオーバーのスプライトを新たに定義し
衝突イベント検知部に処理を追加して、下の壁(下限)との衝突判定を検知した場合
残機を1ずつ減らし、if文によってゲーム続行か終了かを判定しています。
他には、ゲーム状態を保持する変数を定義して
ゲーム中以外はタッチを無効にしており
ゲームスタートのフェードアウトを行った後のスプライトを消去するタイミングにて
タッチを有効にしています。
一番大きな処理追加としましては以下になります。
- setTimeout(function () {
- var b2Vec2 = body_ball.GetLinearVelocity();
- var vect_x = b2Vec2.x;
- var vect_y = b2Vec2.y;
- if (vect_x * vect_x + vect_y * vect_y < (ball_velocity - 2) * (ball_velocity - 2)) {
- //かなり遅くなっている
- //補正する
- var b2Vec2 = Box2D.Common.Math.b2Vec2;
- var boost = false;
- for (var i = 1; i <= 20; i++) {
- if ((vect_x * (1 + 0.1 * i)) * (vect_x * (1 + 0.1 * i)) + (vect_y * (1 + 0.1 * i)) * (vect_y * (1 + 0.1 * i)) >= (ball_velocity – 2) * (ball_velocity – 2)) {
- body_ball.SetLinearVelocity(new b2Vec2(vect_x * (1 + 0.1 * i), vect_y * (1 + 0.1 * i)));
- boost = true;
- break;
- }
- }
- if (boost == false) {
- body_ball.SetLinearVelocity(new b2Vec2(vect_x * (1 + 0.1 * 20), vect_y * (1 + 0.1 * 20)));
- }
- }
- }, 100);
使用するボールが異形だった場合、当たる場所によっては
衝突後にスピードが落ちてしまう可能性があります。
今回は、ブロック崩しということで
ボールの速度はある程度一定を保つことが望ましいです。
その為、この処理を記載して
ボールのスピードをコントロールしております。
以上で、ブロック崩しの作成は終了となります。
<次回以降の更新について>
次回は、cocos2d-xにて、JNIを使用したネイティブ連携処理(Android側)をご紹介します。