⬆️ ⬇️

Creation of another casual game on a Flash platform with physics. Part I

Hello my dear friend.



To begin with, I have been doing game-devil relatively recently.

Therefore, do not rely on the quality of the code and information%)



In this article, I will talk about how to use the Box2D physics engine for my games, using the example of a prototype.

')

If you are lucky and you enjoy the article, there will be a second part, and a third part. In the end, we get this game . (yes, the game is not terribly completed, the same as in the article, only with graphics and sensors)

The rest is under the cut.







We are going to hike





What do we need?

  1. The physics itself Box2D. Here it is important that the version was 2.0.2, since it is much more stable. Yes, and they have the same performance.

    You can download it here or here (but the second link also contains all this source code).
  2. You need experience creating something on as3.
  3. Actually, on what we will write, in other words IDE. You can use FlashDevelop (like me), which, by the way, is free; or use Adobe Flash CS5, for example.
  4. brain.dll, respectively, and hands.dll




Prehistory





And so Box2D is a free, open-source, two-dimensional physics engine (for modeling the physical world in games and not only) developed by one person, Erin Catto, for C ++.



But there was another good man who took it and ported it to Flash.



Someone says that he is very “fat”, like he is slowly working, someone is the opposite. I know one thing - they make real games on it, and therefore, most likely, it depends on the hands.



But first you need to say about the general provisions relating to Box2D. These provisions will be provided in the form of theses:



  1. as a unit of measure Box2D does not use pixels
  2. as a unit of measure Box2D uses the international system C (s) - meters, kilograms, seconds (ISS)
  3. Box2D was configured to work with dynamic objects ranging from 10 centimeters to 10 meters, that is, it is possible to create objects from the glass to the bus
  4. since the units that Box2D operates on do not coincide with the screen units (pixels), it is necessary to set a certain conversion factor of meters (units) into pixels. For example: you can specify that 10 centimeters = 30 pixels.
  5. Box2D operates with two types of objects: dynamic and static
  6. Dynamic objects are involved in the analysis of collisions between themselves (bricks, balls, molecules, helicopters, cars, people ...), static - no (earth, foundation, frame - everything that is absolutely indestructible)
  7. Box2D implements collisions with the following shapes: circle, square, convex polygons.
  8. The names of most structures in Box2D begin with the prefix “b2” in order to better identify the engine structures visually and make it less likely to conflict with user structures




Well, like, while all.



Getting started





We create a project, the window size is 500x500 and 30FPS, we are looking for our class Main.as.

Sui to the Main class our daddy with Box2D.

We import the necessary ones:



import Box2D.Dynamics.*; import Box2D.Dynamics.Joints.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; 




Create constants:

 public static const ITERATIONS:int = 20; public static const TIMESTEP:Number = 1.0 / 30.0; 




What are these constants - I will explain later.



We declare the variables world, sprite, rotator, hero, and boundsWorld:



 public var world:b2World; public var sprite:Sprite; //   " " public var boundsWorld:b2Body; public var hero:b2Body; public var rotator:Number; 




Create a function:



 public function InitializePhysicsWorld():void { var worldAABB:b2AABB = new b2AABB(); //   ,     ,     . worldAABB.lowerBound.Set(-300, -300.0); worldAABB.upperBound.Set(300.0, 300.0); world = new b2World(worldAABB, new b2Vec2(0, 0), false); //  , worldAABB -  ; new bVec2(0, 0) -  ,      ; false -     . var dbgDraw:b2DebugDraw = new b2DebugDraw(); //    ,    ,   ,  . dbgDraw.m_sprite= sprite; dbgDraw.m_drawScale= 30; dbgDraw.m_alpha = 1; dbgDraw.m_fillAlpha = 0.5; dbgDraw.m_lineThickness = 1; dbgDraw.AppendFlags(b2DebugDraw.e_shapeBit); //    world.SetDebugDraw(dbgDraw); } 




And in main () we write:



 public function Main():void { sprite = new Sprite(); addChild(sprite); sprite.x = sprite.y = 250; rotator = 0; InitializePhysicsWorld(); } 




Our world is created, we will try now to fill it with butterflies, grass and water with something.

Create the boundaries of the world, create a function:



 public function CreateBoundsWorld():b2Body { var body:b2Body; //   var bodyDef:b2BodyDef; //   var polygon:b2PolygonDef; //  bodyDef = new b2BodyDef(); polygon = new b2PolygonDef(); bodyDef.position.Set(0, 0); //    x:0, y:0 polygon.density = 0; //   0, ..  density  ,    . polygon.friction = 1.0; polygon.restitution = 0.2; body = world.CreateBody(bodyDef); //     //    polygon.SetAsOrientedBox(300/2/30, 1/2/30, new b2Vec2(0, -300/2/30), 0); body.CreateShape(polygon); polygon.SetAsOrientedBox(300/2/30, 1/2/30, new b2Vec2(0, 300/2/30), 0); body.CreateShape(polygon); polygon.SetAsOrientedBox(1/2/30, 300/2/30, new b2Vec2(300/2/30, 0), 0); body.CreateShape(polygon); polygon.SetAsOrientedBox(1/2/30, 300/2/30, new b2Vec2(-300/2/30, 0), 0); body.CreateShape(polygon); body.SetMassFromShapes(); //  ,      . return body; //  . } 




We write at the end of main ():



 boundsWorld = CreateBoundsWorld(); 




Now our world is not empty, but wait, why is nothing visible when compiling?

Do not worry, dear% username%, everything is fine. Let's make our engine draw and calculate physics.

To do this, create a function:



 public function enterFrameListener(event:Event):void { world.Step(timestep, ITERATIONS); //     Box2D    (   ).   :  Box2D   Step(float timeStep,int iterations),   : ,             . } 




We hang the listener in main ():



 this.addEventListener(Event.ENTER_FRAME, enterFrameListener); 




Launch and see:

image



Everything would be fine, but now it's time to add dynamic objects, in our case, this is a ball hero. Again we create the function:



 public function CreateHero(x:Number, y:Number):void { var bodyDef:b2BodyDef; var circleDef:b2CircleDef; var x:Number; var y:Number; var r:Number; /* HERO */ x = x / 30; //        y = y / 30; // r = 8 / 30; //  bodyDef = new b2BodyDef(); bodyDef.position.Set(x, y); circleDef = new b2CircleDef(); circleDef.radius = r; circleDef.density = 1; circleDef.friction = 1; circleDef.restitution = 0.2; hero = world.CreateBody(bodyDef); hero.SetUserData("hero"); //    " " hero.CreateShape(circleDef); hero.SetMassFromShapes(); } 




Add the function again to main ():



 CreateHero(0, 0); 




We compile, we are already seeing something new, a white ball appeared in the center.

But he does not move, if you are an attentive person, then remember that we have turned off gravity. What for? To make the pseudo revolution of the world. Since gravity in our world, simply must be dynamic.



Therefore, we will make the rotation of our world on the keys "Left" and "Right".

Rotate will need to sprite itself with the world and change gravity.

I hope you understand why you can’t just rotate the static “room” for our hero%)



Add two public variables:



 public var keyPressedRight:Boolean; public var keyPressedLeft:Boolean; 




Listeners in main ():



 parent.addEventListener(KeyboardEvent.KEY_DOWN, keyDownListener); parent.addEventListener(KeyboardEvent.KEY_UP, keyUpListener); 




And accordingly the functions themselves:



 public function keyDownListener(event:KeyboardEvent):void { switch(event.keyCode) { case 37: keyPressedRight = true; break; case 39: keyPressedLeft = true; break; } } public function keyUpListener(event:KeyboardEvent):void { switch(event.keyCode) { case 37: keyPressedRight=false; break; case 39: keyPressedLeft = false; break; } } 




Now go to our enterFrameListener, add:



 if (keyPressedRight) rotator -= 4; else if(keyPressedLeft) rotator += 4; 




Rotate the sprite:



 sprite.rotation = rotator; 




Management is ready. Now we tie the control to the physics engine, create gravity, which depends on the rotator:



 var angle:Number = (270 + rotator_fix) / 180 * Math.PI; var pseudo_gravity:b2Vec2 = b2Vec2.Make(Math.cos(angle), -Math.sin(angle)); 




We calculated the gravity vector, now we need to apply it to our hero.



 for (var body:b2Body = world.GetBodyList(); body; body = body.GetNext()) //      . { if (body.GetUserData() == "hero") //  userdata  - hero,     . { pseudo_gravity.x *= 9.8 * body.GetMass(); pseudo_gravity.y *= 9.8 * body.GetMass(); body.ApplyForce(pseudo_gravity, body.GetWorldCenter()); //   } } 




We compile and aha! Add some crap in the middle. Create a function:



 public function CreateStaticRect(x:Number, y:Number, w:Number, h:Number):void { var total_x:Number = ((x + w / 2) - 150) / 30; var total_y:Number = ((y + h / 2) - 150) / 30; var total_w:Number = w / 30; var total_h:Number = h / 30; var body:b2Body; var bodyDef:b2BodyDef; var polygon:b2PolygonDef; bodyDef = new b2BodyDef(); polygon = new b2PolygonDef(); bodyDef.position.Set(0, 0); bodyDef.position.Set(total_x, total_y); polygon.SetAsBox(total_w / 2, total_h / 2); polygon.density = 0.0; polygon.friction = 0.5; polygon.restitution = 0.2; body = world.CreateBody(bodyDef); body.CreateShape(polygon); body.SetMassFromShapes(); body.SetUserData("box"); } 




And at the end of main ():



 CreateStaticRect(0, 150, 150, 10); 




Ready "prototype" on the face.



Results





Here is such a step-by-step. Dear friends, if you like, I will definitely write a number of articles, in the end we will get something like this .



PS: Once again I will give all the links:

Sources | SWF'shka file | Embed SWF | Box2dlib



PSS: Sorry for links to all frivolny hosting. Your server is not.



UPD: Part II

Source: https://habr.com/ru/post/121193/



All Articles