Xiper

Начальный шаблон для box2D

Автор: Евгений Рыжков Дата публикации:

Данный шаблон — это своего рода введение по работе с box2d.

Что качать

  • библиотека box2web — порт на Javascrpt известной библиотеки box2d написанной на C.

В Сети несколько разновидностей данного порта. Данный выделяется своей регулярной обновляемостью и большей схожестью синтаксиса с оригиналом.

Быстрый старт

<style>
canvas {
	position: absolute;
	top: 0;
	left: 0;
	border: 1px solid #000;
}
body {
	-webkit-transform: translateZ(0); // загадочное правило, которое разгоняет Safari
}
</style>
<script src="js/box2dweb-2.1.a.3.min.js"></script>
[...]
<canvas id="static" width="600" height="400"></canvas>
<canvas id="dynamic" width="600" height="400"></canvas>
window.onload = function()
{
	/* предзагрузка текстур */
	var	images = [];
	images.push(new Image());  
	images[0].src = "img/grass.jpg";
	images.push(new Image()); 
	images[1].src = "img/brick.jpg";
	loadImages(images, start); //функция для подгрузки изображений
	/* запускаем сцену после загрузки всех необходимых компонентов */  
  	function start(){
		var canvasStatic = document.getElementById("static"),	// канва для сттических тел
			ctxStatic = canvasStatic.getContext("2d"),
			canvasDynamic = document.getElementById("dynamic"),	// для динамических
			ctxDynamic = canvasDynamic.getContext("2d"),
			world,
			canvasW = canvasStatic.offsetWidth,
			canvasH = canvasStatic.offsetHeight,
			arrStaticBodies = [],			// массив статических объектов
			arrDynamicBodies = [];			// массив динамических объектов
		/* подключаем необходимые модули для нашей сцены */
		var b2Vec2 = Box2D.Common.Math.b2Vec2,
			b2BodyDef = Box2D.Dynamics.b2BodyDef,
			b2Body = Box2D.Dynamics.b2Body,
			b2FixtureDef = Box2D.Dynamics.b2FixtureDef,
			b2Fixture = Box2D.Dynamics.b2Fixture,
			b2World = Box2D.Dynamics.b2World,
			b2MassData = Box2D.Collision.Shapes.b2MassData,
			b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape,
			b2CircleShape = Box2D.Collision.Shapes.b2CircleShape,
			b2DebugDraw = Box2D.Dynamics.b2DebugDraw;
		/* 
			создание мира
			задаем общие силы
		*/
		function createWorld()
		{
			var gravity = new b2Vec2(0, 10);   /* 
													задаем гравитацию через вектор
													по оси x = 0
													по y = 10
												*/
			var doSleep = true; 				// указываем что "спящие" тела не учасвствуют в моделировании
			world = new b2World(gravity, doSleep); // создаем новый мир 
			return world;
		}
		/* объекты сцены ---------------------------------------- */
		/*
			объект Ground
			св-ва, форма и расположение земли
			получает:
				- world - мир, куда будет добавлена земля
				- width/height - размеры в метрах
			возвращает созданное тело
		*/
		var Ground = function(world, width, height)
		{
			this.bodyDef = new b2BodyDef(); // определение св-в тела: позиция, амортизация
			this.fixDef = new b2FixtureDef; // определяем физические свойства объекта + фигура
			this.fixDef.friction = 0.5; // трение
			this.fixDef.restitution = 0.5; // восстановление
			this.fixDef.shape = new b2PolygonShape;
			/* с помощью userData задаем объекту свои параметры */
			this.bodyDef.userData = new StylingStatic({texture: images[0], width: width, height: height});
			this.fixDef.shape.SetAsBox(width/2 , height/2); // задаем длину и ширину (половины, т.к. относительно центра фигуры)
			this.bodyDef.position.Set(width/2 , 3.9); // позиция центра объекта
			var ground = world.CreateBody(this.bodyDef);
			ground.CreateFixture(this.fixDef); // загружаем в мир созданный объект
			return ground;
		}
		/*
			объект Wall - стены
			получает:
				- world - мир, куда будет добавлена
				- width/height - размеры в метрах
				- posX/posY - позиция центра (в местрах)
			возвращает созданное тело
		*/
		var Wall = function(world, width, height, posX, posY)
		{
			this.bodyDef = new b2BodyDef();
			this.fixDef = new b2FixtureDef; // определяем физические свойства объекта + фигура
			this.fixDef.friction = 0.5; // трение
			this.fixDef.restitution = 0.5; // восстановление
			this.fixDef.shape = new b2PolygonShape;
			this.bodyDef.userData = new StylingStatic({texture: images[1], width: width, height: height});
			this.fixDef.shape.SetAsBox(width/2 , height/2); // задаем длину и ширину (половины, т.к. относительно центра фигуры)
			this.bodyDef.position.Set(posX , posY); // позиция центра объекта
			var wall = world.CreateBody(this.bodyDef);
			wall.CreateFixture(this.fixDef); // загружаем в мир созданный объект
			return wall;
		}
		/*
			объект Ball
			св-ва, форма, положение, силы объекта шар
			получает:
				- world - мир куда добавлем тело
				- posX/posY - начальные координаты
				- R - радиус (в метрах)
			возвращает созданное тело
		*/
		var Ball = function(world, posX, posY, R)
		{
			this.bodyDef = new b2BodyDef();
			this.fixDef = new b2FixtureDef; // определяем физические свойства объекта + фигура
			this.fixDef.density = 10; // плотность. на ее основании можно определить массу
			this.bodyDef.type = b2Body.b2_dynamicBody; // динамическое тело, на него дейсвтуют законы сцены
			this.fixDef.shape = new b2CircleShape(R); // форма - круг с радиусом 0.2м
			this.bodyDef.position.x = posX;				// начальные координаты
      		this.bodyDef.position.y = posY;
			this.bodyDef.userData = new StylingBall({fillStyle: "red", strokeStyle: "green"});
			var ball = world.CreateBody(this.bodyDef); 
			ball.CreateFixture(this.fixDef);
			return ball;
		}
		/*
			стилизирование объектов
			для примера один для заливки текстурой, другой просто цветом
		*/
		var StylingStatic = function(option)
		{
			this.width = option.width;
			this.height = option.height;
			this.fillStyle = option.fillStyle;
			this.strokeStyle = option.strokeStyle;
			this.texture = option.texture;
			return this;
		}
		var StylingBall = function(option)
		{
			this.fillStyle = option.fillStyle;
			this.strokeStyle = option.strokeStyle;
			return this;
		}
		/* функции отрисовки -------------------------------------------- */
		/* функция отрисовки статических тел */
		function staticDraw(arrBodies)
		{
			for (var i = arrBodies.length; i--;)
			{
				var body = arrBodies[i];
				ctxStatic.save();
				var bodyPos = body.m_xf.position; // св-во объекта, содержащее текущие координаты
				// накладываем текстуру
				var ptrn = ctxStatic.createPattern(body.m_userData.texture,"repeat");
				ctxStatic.fillStyle = ptrn;
				/*
					учитываем, что позиция объекта - это его центр, а fillRect заливает относительно верхнего левого угла,
					плюс масштаб
				*/
				ctxStatic.fillRect((bodyPos.x-body.m_userData.width/2)*scale, (bodyPos.y-body.m_userData.height/2)*scale, body.m_userData.width*scale, body.m_userData.height*scale); 
				ctxStatic.restore();
			}
			return;
		}
		/* функция отрисовки динамических тел */
		function dynamicDraw(arrBodies)
		{
			for (var i = arrBodies.length; i--;)
			{
				var body = arrBodies[i];
				ctxDynamic.save();
				ctxDynamic.lineWidth = 2;
				ctxDynamic.fillStyle = body.m_userData.fillStyle;
				ctxDynamic.strokeStyle = body.m_userData.strokeStyle;
				var bodyPos = body.m_xf.position; // св-во объекта, содержащее текущие координаты
				ctxDynamic.beginPath();
				ctxDynamic.arc(bodyPos.x*scale, bodyPos.y*scale, body.m_fixtureList.m_shape.m_radius*scale, 0, Math.PI*2, false); 
				ctxDynamic.closePath();
				ctxDynamic.stroke();
				ctxDynamic.fill();
				ctxDynamic.restore();
			}
			return;
		}
		/* отрисовка объектов сцены ---------------------------------------- */
		/* init scene -------------------------------------- */
		var scale = 100;				// масштаб 100px = 1м
		createWorld();	//  создаем мир
		/*	создаем и рисуем статику	*/
		var ground = new Ground(world, 4, 0.1);	// создаем землю
		arrStaticBodies.push(ground);
		var wall_1 = new Wall(world, 1, 1.5, 4.5, 3.2);
		arrStaticBodies.push(wall_1);
		var wall_2 = new Wall(world, .05, .5, 2, 3.6);
		arrStaticBodies.push(wall_2);
		staticDraw(arrStaticBodies);
		/* создаем динамику */
		ball_1 = new Ball(world,.02,.02,0.2);	// создаем первый мяч
		arrDynamicBodies.push(ball_1);
		ball_1.ApplyImpulse(new b2Vec2(4,1), ball_1.GetWorldCenter()); // начальный импульс шару
		/* для отладки физики анимации ----------------------------- */
		 var debugDraw = new b2DebugDraw();
			debugDraw.SetSprite(ctxStatic);
			debugDraw.SetDrawScale(scale);
			debugDraw.SetFillAlpha(0.5);
			debugDraw.SetLineThickness(1.0);
			debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
			world.SetDebugDraw(debugDraw);
		/* / для отладки физики анимации ----------------------------- */
		function update() {
	       world.Step(
    	         1 / 60   // 60 кадров в секу - менять не желатально
    	      ,  10       // предельные значения обработки изменения скорости объектов
    	      ,  10       // и их положений. Большее число - большая точность, но худшая производительность
    	   );
    	 //world.DrawDebugData(); // это
	     // world.ClearForces();  // и это нужно раскоментировать для отладки физики
		  /* перерисовываем только динамику */
		  ctxDynamic.clearRect(0, 0, 600, 400);
		  dynamicDraw(arrDynamicBodies);
    	   requestAnimFrame(update);
    	}; 
		requestAnimFrame(update);
	};
}

Посмотреть в живую.

Материалы

  • Проект box2d
  • Перевод документации на русский
  • Box2D & JavaScript tutorials by Seth Ladd
  • Box2D JaveScript Tutorial