プログラミングとイラストレーション » ボールを投げて積み木を壊す| cocos2d-js
プログラミングとイラストレーション > cocos2d > ボールを投げて積み木を壊す| cocos2d-js

ボールを投げて積み木を壊す| cocos2d-js

ボールを投げて積み木を壊す| cocos2d-js

Box2Dを使った、ボールを投げて(ドラッグ&ドロップ)、積み木を壊すシミュレーション。

ボールを投げて積み木を壊す| cocos2d-js : デモ

ボールを投げて積み木を壊す| cocos2d-js : ZIPファイル(1.2MB)

1.HTML

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title></title>
	<meta name="apple-mobile-web-app-capable" content="yes"/>
	<meta name="full-screen" content="yes"/>
	<meta name="screen-orientation" content="portrait"/>
	<meta name="x5-fullscreen" content="true"/>
	<meta name="360-fullscreen" content="true"/>
	<style>
		body, canvas, div{
			-moz-user-select:none;
			-webkit-user-select:none;
			-ms-user-select:none;
			-khtml-user-select:none;
			-webkit-tap-highlight-color:rgba(0,0,0,0);
		}
	</style>
</head>
<body style="padding:0; margin:0; background:#000;">
<canvas id="gameCanvas"></canvas>
<script src="cocos2d-js-v3.12.js"></script>
<script cocos src="main.js"></script>
</body>
</html>

2.JavaScript

main.js

cc.game.onStart = function(){
	if(!cc.sys.isNative && document.getElementById("cocosLoading"))
		document.body.removeChild(document.getElementById("cocosLoading"));

	cc.view.enableRetina(false);
	cc.view.adjustViewPort(true);
	cc.view.setDesignResolutionSize(480, 320, cc.ResolutionPolicy.SHOW_ALL);
	cc.view.resizeWithBrowserSize(true);
	cc.LoaderScene.preload(g_resources, function(){
		cc.director.runScene(new gameScene());
	}, this);
	
};
cc.game.run();

app.js


var world,
//Box2Dではメートル単位
//「1メートル = 30ピクセル」
worldScale = 30,
gameLayer,
b2Vec2 = Box2D.Common.Math.b2Vec2, // 2Dベクトル
b2Body = Box2D.Dynamics.b2Body, // Body
b2PolyDef = Box2D.Dynamics.b2PolyDef,
b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef, //マウスジョイント
b2AABB = Box2D.Collision.b2AABB, //AABB(axis-aligned bounding box)
mouseX = null,
mouseY = null,
mousePVec = null,
isMouseDown = null,
selectedBody = null,
mouseJoint = null,
bodyCircle = null
;

var gameScene = cc.Scene.extend({
	onEnter:function(){
		this._super();
		gameLayer = new game();
		gameLayer.init();
		this.addChild(gameLayer);
		//cc.log("my awesome game starts here");
	}
});

var game = cc.Layer.extend({
	init:function(){
		this._super();
		//グラデーション設定
		var backgroundLayer = cc.LayerGradient.create(cc.color(0x00, 0xe8, 0xff, 255), cc.color(0xff, 0xff, 0xff, 255));
		this.addChild(backgroundLayer);
		//重力を設定 -10を設定
		var gravity = new Box2D.Common.Math.b2Vec2(0, -10);
		//第1引数:重力、 第2引数:何の力も働かない時スリープ状態(true)
		world = new Box2D.Dynamics.b2World(gravity, true);



		this.scheduleUpdate();

		//物理空間にオブジェクト「body」を追加
		// 1:横方向の中心、2:縦方向の中心、3:幅、4:高さ、5:動的オブジェクトかどうか、6:画像ファイル、7:種類
		this.addBody(-5, 160, 10, 320, false, res.dot, "ground");
		this.addBody(240, 325, 480, 10, false, res.dot, "ground");
		this.addBody(485, 160, 10, 320, false, res.dot, "ground");
		this.addBody(240, 0, 480, 0, false, res.dot, "ground");

		this.addBody(204, 12, 24, 24, true, res.brick1x1_png, "solid"); //destroyable
		this.addBody(276, 12, 24, 24, true, res.brick1x1_png, "solid"); //destroyable
		this.addBody(240, 36, 96, 24, true, res.brick4x1_png, "solid"); //destroyable
		this.addBody(240, 60, 48, 24, true, res.brick2x1_png, "solid");
		this.addBody(228, 84, 72, 24, true, res.brick3x1_png, "solid"); //destroyable
		this.addBody(240, 120, 96, 48, true, res.brick4x2_png, "solid");

		this.addBodyTriangle(240, 300, 48, true, res.triangle, "triangle");
		this.addBody(240, 144, 24, 48, true, res.totem_png, "totem");

		this.addBodyCircle(100, 50, 60, true, res.circle, "ball");

		cc.eventManager.addListener(touchListener, this);



	},
	update:function(dt){
		//一定時間に対して、シミュレーションをする
		world.Step(dt, 10, 10);
		//物理世界上の力をリセット
		world.ClearForces();

		

		//すべてのbodyに対してループ処理
		for(var b=world.GetBodyList(); b; b=b.GetNext())
		{
			//b変数は、現在のbodyを指す。b変数のUserDataのチェックをする
			if(b.GetUserData() != null){
				var mySprite = b.GetUserData().asset;
				//GetPosition関数はbodyの位置を返す
				mySprite.setPosition(b.GetPosition().x * worldScale, b.GetPosition().y * worldScale);
				//GetAngle関数はbodyの回転角度を返す
				mySprite.setRotation(-1*cc.radiansToDegress(b.GetAngle()));

				//物理オブジェクト間の衝突をチェックする
				if(b.GetUserData().type == "totem"){
					//すべての衝突に対して、otherプロパティを利用し、トーテムが何と衝突しているかをチェックする
					for(var c = b.GetContactList(); c; c = c.m_next){
						//衝突が ground であれば、メッセージ表示
						if(c.other.GetUserData() && c.other.GetUserData().type=="ground"){
							//console.log("Oh no!!!!!!!!!!!!!!");
						}
					}
				}
			}
		}



		//マウスが押されて、マウスジョイントがない場合
		if(isMouseDown && (!mouseJoint))
		{
			
			var body = getBodyAtMouse();
			//console.log("body: " + body);
			
			if(body && body.GetUserData().type==="ball")
			{
				//console.log("" + body.GetUserData().type);
				//マウスジョイント作成
				var md = new b2MouseJointDef();
				//物理世界
				md.bodyA = world.GetGroundBody();
				//ひも付け対象のbody
				md.bodyB = body;
				//マウスジョイントの位置
				md.target.Set(mouseX, mouseY);
				//2つのボディの衝突判定をする
				md.collideConnected = true;
				//マウスジョイントが引っ張る力
				md.maxForce = 314 * body.GetMass();
				mouseJoint = world.CreateJoint(md);
				//スリープ状態解除
				body.SetAwake(true);
			}
		}

		if(mouseJoint)
		{
			
			//マウスドラッグしたとき
			if(isMouseDown)
			{
				mouseJoint.SetTarget(new b2Vec2(mouseX, mouseY));
			}
			//ドロップした時
			else
			{
				world.DestroyJoint(mouseJoint);
				mouseJoint = null;
			}
		}





	},
	addBody:function(posX, posY, width, height, isDynamic, spriteImage, type){

		//物体の特性を設定
		var fixtureDef = new Box2D.Dynamics.b2FixtureDef;
		//物体の性質を設定
		//density: 質量
		//friction: 摩擦
		//restitution: 反発係数
		fixtureDef.density = 1.0;
		fixtureDef.friction = 0.5;
		fixtureDef.restitution = 0.2;

		//幅と高さをもつボックスを作る。Box2Dでは、実際の半分の値を設定(1/2)また、ピクセルに変換するので、worldScaleで割る
		fixtureDef.shape = new Box2D.Collision.Shapes.b2PolygonShape;
		fixtureDef.shape.SetAsBox(0.5 * width / worldScale, 0.5 * height / worldScale);

		//bodyは静的か、動的かのいずれかの特性を設定する
		var bodyDef = new Box2D.Dynamics.b2BodyDef;
		if(isDynamic){
			bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody;
		}
		else{
			bodyDef.type = Box2D.Dynamics.b2Body.b2_staticBody;
		}

		//bodyの位置を設定。ピクセルからメートルに変換
		bodyDef.position.Set(posX/worldScale, posY/worldScale);

		//bodyに画像を貼り付け
		var userSprite = cc.Sprite.create(spriteImage);
		this.addChild(userSprite, 0);
		userSprite.setPosition(posX, posY);

		//bodyとtype, 画像の関連付け
		bodyDef.userData = {
			type: type,
			asset: userSprite
		}
		//bodyを空間に配置
		var body = world.CreateBody(bodyDef);
		//物理特性を与える
		body.CreateFixture(fixtureDef);
	},
	addBodyCircle:function(posX, posY, radius, isDynamic, spriteImage, type){

		//物体の特性を設定
		var fixtureDef = new Box2D.Dynamics.b2FixtureDef;
		//物体の性質を設定
		//density: 質量
		//friction: 摩擦
		//restitution: 反発係数
		fixtureDef.density = 1.0;
		fixtureDef.friction = 0.9;
		fixtureDef.restitution = 0.5;

		//直径をもつ円を作る。Box2Dでは、実際の半分の値を設定(1/2)また、ピクセルに変換するので、worldScaleで割る
		var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
		fixtureDef.shape = new b2CircleShape(0.5 * radius / worldScale);

		//bodyは静的か、動的かのいずれかの特性を設定する
		var bodyDef = new Box2D.Dynamics.b2BodyDef;
		if(isDynamic){
			bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody;
		}
		else{
			bodyDef.type = Box2D.Dynamics.b2Body.b2_staticBody;
		}


		//bodyに画像を貼り付け
		var userSprite = cc.Sprite.create(spriteImage);
		this.addChild(userSprite, 0);
		userSprite.setPosition(posX, posY);

		//bodyとtype, 画像の関連付け
		bodyDef.userData = {
			type: type,
			asset: userSprite
		}
		//bodyを空間に配置
		bodyCircle = world.CreateBody(bodyDef);
		//物理特性を与える
		bodyCircle.CreateFixture(fixtureDef);

		bodyCircle.SetAngularVelocity(0); //Math.PI*2
		bodyCircle.SetPositionAndAngle(new b2Vec2(posX/worldScale, posY/worldScale), 0*(Math.PI/180));
	},
	addBodyTriangle:function(posX, posY, width, isDynamic, spriteImage, type){
		
		
		//物体の特性を設定
		var fixtureDef = new Box2D.Dynamics.b2FixtureDef;
		//物体の性質を設定
		//density: 質量
		//friction: 摩擦
		//restitution: 反発係数
		fixtureDef.density = 1.0;
		fixtureDef.friction = 0.5;
		fixtureDef.restitution = 0.2;

		fixtureDef.shape = new Box2D.Collision.Shapes.b2PolygonShape;

	    var list = [
	        new b2Vec2(                        0,   0.5*width / worldScale),
	        new b2Vec2(-0.5*width*2 / worldScale,  -0.5*width / worldScale),
	        new b2Vec2( 0.5*width*2 / worldScale,  -0.5*width / worldScale)
	        
	    ];
	    fixtureDef.shape.SetAsArray(list);

		//bodyは静的か、動的かのいずれかの特性を設定する
		var bodyDef = new Box2D.Dynamics.b2BodyDef;
		if(isDynamic){
			bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody;
		}
		else{
			bodyDef.type = Box2D.Dynamics.b2Body.b2_staticBody;
		}

		//bodyの位置を設定。ピクセルからメートルに変換
		bodyDef.position.Set(posX/worldScale, posY/worldScale);

		//bodyに画像を貼り付け
		var userSprite = cc.Sprite.create(spriteImage);
		this.addChild(userSprite, 0);
		userSprite.setPosition(posX, posY);

		//bodyとtype, 画像の関連付け
		bodyDef.userData = {
			type: type,
			asset: userSprite
		}
		//bodyを空間に配置
		var body = world.CreateBody(bodyDef);
		//物理特性を与える
		body.CreateFixture(fixtureDef);


	}
});

/*
物理空間にあるbodyを選択、または壊す
*/
var touchListener = cc.EventListener.create({
	event: cc.EventListener.TOUCH_ONE_BY_ONE,
	swallowTouches: true,
	onTouchBegan: function(touch, event){
		//cc.log("onTouchBegan");
		//console.log('down x: '+touch.getLocationX(), 'y: ' + touch.getLocationY());
		//クリック、タップした位置を取得し、空間の位置に変換(ピクセルをメートルに変える)
		var worldPoint = new Box2D.Common.Math.b2Vec2(touch.getLocation().x / worldScale, touch.getLocation().y / worldScale);
		for(var b = world.GetBodyList(); b; b = b.GetNext()){
			//壊すことができるブロックは destroyable だけ
			if(b.GetUserData() != null && b.GetUserData().type=="destroyable"){
				//タッチした位置に壊せるブロックがあるかどうかをチェックする
				for(var f = b.GetFixtureList(); f; f=f.GetNext()){
					//引数の位置があればtrueを返す
					if(f.TestPoint(worldPoint)){
						//画像とbodyを削除
						gameLayer.removeChild(b.GetUserData().asset);
						world.DestroyBody(b);
					}
				}
			}
		}
		return true;
	},
 
    onTouchMoved: function (touch, event) {
        //cc.log("onTouchMoved");
        //console.log('move x: '+touch.getLocationX(), 'y: ' + touch.getLocationY());
        isMouseDown = true;
        mouseX = touch.getLocation().x / worldScale;
        mouseY = touch.getLocation().y / worldScale;
    },
 
    onTouchEnded: function (touch, event) {
        //cc.log("onTouchEnded");
        isMouseDown = false;
        mouseX = null;
        mouseY = null;
    },
 
    onTouchCancelled: function (touch, event) {
        //cc.log("onTouchCancelled");
    }
});

//マウスの位置にあるbodyを取得する
function getBodyAtMouse()
{
	//console.log(mouseX, mouseY);
	mousePVec = new b2Vec2(mouseX, mouseY);
	//当たり判定用のAABB作成
	var aabb = new b2AABB();
	aabb.lowerBound.Set(mouseX-0.001, mouseY-0.001);
	aabb.upperBound.Set(mouseX+0.001, mouseY+0.001);
	//aabb.lowerBound.Set(mouseX, mouseY);
	//aabb.upperBound.Set(mouseX, mouseY);
	//aabb.lowerBound.Set(mouseX+10, mouseY+10);
	//aabb.upperBound.Set(mouseX-10, mouseY-10);
	selectedBody = null;
	//指定のAABB領域内のBodyを探す
	world.QueryAABB(getBodyCB, aabb);
	return selectedBody;
}

//AABB領域内のBodyを探す
function getBodyCB(fixture)
{
	//動的Bodyの場合
	if(fixture.GetBody().GetType() != b2Body.b2_staticBody)
	{
		//マウスとBodyの当たり判定
		if(fixture.GetShape().TestPoint(fixture.GetBody().GetTransform(), mousePVec))
		{
			//マウスの位置にあるBodyを取得
			selectedBody = fixture.GetBody();
			//探索の中止
			return false;
		}
		//AABB領域内の次のfixtureを探す
		return true;
	}
}

resource.js

var res = {
	brick1x1_png:"res/brick1x1.png",
	brick2x1_png:"res/brick2x1.png",
	brick3x1_png:"res/brick3x1.png",
	brick4x1_png:"res/brick4x1.png",
	brick4x2_png:"res/brick4x2.png",
	ground_png: "res/ground.png",
	totem_png: "res/totem.png",
	circle: "res/circle.png",
	dot: "res/dot.png",
	triangle: "res/triangle.png"
};

var g_resources = [];
for(var i in res){
	g_resources.push(res[i]);
}

project.json

{
    "debugMode"     : 1,
    "showFPS"		:true,
    "frameRate"     : 60,
    "id"            : "gameCanvas",
    "renderMode"    : 2,
    "modules"		:["cocos2d", "external"],
    "jsList"        : ["resource.js","app.js"]
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

コメントフィード

トラックバック URL : http://www.htmlcode.jp/%e3%83%9c%e3%83%bc%e3%83%ab%e3%82%92%e6%8a%95%e3%81%92%e3%81%a6%e7%a9%8d%e3%81%bf%e6%9c%a8%e3%82%92%e5%a3%8a%e3%81%99%ef%bd%9c-cocos2d-js/trackback/