📜 ⬆️ ⬇️

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

Hello Habra community.

Relatively recently , I wrote an article about creating a new casual game on a Flash platform with physics a long time ago, promised a second article, please welcome.
In this article I will teach you how to draw the world and tell you about sensors. The rest is under the cut.

What can be done from these two lessons can be viewed here (music cannot be turned off, but you can remove the sound in the system) .
')
Once again, remember what was in the past " lesson ."

Graphics


Now you need to steal to draw future graphics in the game, but because This lesson is primarily about programming games, you can use what I did:

Texture 1:


Texture 2:


Here are two BMP textures, 474x474 pixels.
Serving they will be the basis for our world.

Texture 2 is a static texture, it will be represented as a Sprite and will always be drawn with the desired rotation angle.

Texture 1 is the texture for drawing the world, we will fill it with rectangles of our world with the help of beginBitmapFill and translate-matrix. In other words, what is not flooded is freedom of movement and empty space.

Slightly beautifully designed in accordance with the setting we need, we get something like:



We give the shell-picture (sprite) to our hero ball; here, too, I propose to take my graphics:


And so that it would be absolutely boring not to go through the most boring labyrinths, we will make obstacles: spikes, fire, lasers. But since the volume of the article should be adequate, we consider only spikes, but create a base for everything else.

Spike texture:


Create the necessary links and our library looks like this:


The graphics are ready.

Sensors



Sensors are normal objects (Static Body, Dynamic Body), except that they have the isSensor flag. What does this checkbox do? Everything is very simple, such an object “feels” a collision, however, for an object without the isSensor flag, nothing will happen. Solid object - just pass through the sensor, causing a collision event in the ContactListener.

ContactListener - “listener” collisions in Box2D. For example, the object A and B collide in the world, the Add (point: b2ContactPoint) function is called in the ContactListener, the point contains: shapes that participate in a collision; collision point in the global coordinate system; colliding body. In other words, these events can be handled.

- Sensors, listeners, nothing is clear, why is this?
Then, when our hero collides with a spike, it is logical to cause the death of the hero and the restart of the level.

From theory to business, start programming



Learning to draw in the world


Change the size of the project from 500x500 to 600x580.
Change the position of the debug sprite drawing to:

sprite.x = 300; //  600 sprite.y = 290; //  580 


All honest, all in the center.

Add new variables:

 public var game:Sprite; // -   public var level_sprite:Sprite = new Sprite(); // -c   public var viewport:Sprite; // -   (, ) 


In the constructor of the main class, initialize the game and viewport:

 game = new sprite_game(); game.x = 300; //  game.y = 290; //  game.cacheAsBitmap = true; addChild(game); viewport = new Sprite(); viewport.x = 300; viewport.y = 290; addChild(viewport); 


Flash has a special option “cache as bitmap”, which can be set for individual clips - this means that the clip is stored somewhere in the memory as a bitmap image and is no longer counted as a vector image. My experiments have shown that this gives a performance boost, but for complete happiness it is clearly not enough.

Add the "gear" rotation to the enterFrameListener, in the same place where we rotate sprite:

 game.rotation = sprite.rotation = rotator; 


We start, we see already something more or less pleasing to the eye, than just the picture rotating green garbage .

Give the ball a sprite. We are looking for the createHero function and add it somewhere at the end:

 var sprite:Sprite = new sprites_hero(); sprite.width = sprite.height = 16; viewport.addChild(sprite); hero.SetSprite(sprite); //  ,   SetSprite   . //     ,     ,   30 (. ) hero.GetSprite().x = hero.GetPosition().x * 30; hero.GetSprite().y = hero.GetPosition().y * 30; //  hero.GetSprite().rotation = hero.GetAngle() * 180 / Math.PI; 


body.SetSprite - not in the original box2D. In other words, it is an analogue of GetUserData. But GetUserData is used for the object type (“hero”, “box”), so I added SetSprite to the engine. If you write on the original Box2D - do not worry, use dynamic objects, for example: body.SetUserData ({type: "hero", sprite: sprite});

Synchronize the position of the display object with mathematical calculations every frame, go to the enterFrameListener function, and specifically at the moment where we update the hero's gravity, add:

 body.GetSprite().x = body.GetPosition().x * 30; body.GetSprite().y = body.GetPosition().y * 30; body.GetSprite().rotation = body.GetAngle() * 180 / Math.PI; 


We reduce Alpha from the sprite debug so that we can distinguish what is happening:

 dbgDraw.m_alpha = 0.5; dbgDraw.m_fillAlpha = 0.1; 


We start, we twist, we see that everything works:



It now remains to get rid of DebugDraw, in order to fully switch to our graphics, but it prevents us from doing this, that we do not draw walls, we will fix it.
Create variables for the translate matrix and texture:

 public var matrix_texture:Matrix; public var textureal:BitmapData; 


We initialize them in the constructor:

 textureal = new texture_gear(); matrix_texture = new Matrix(); matrix_texture.translate(237, 237); //    level_sprite viewport.addChild(level_sprite); 


Go to the CreateStaticRect function and add something like:

 level_sprite.graphics.beginBitmapFill(textureal, matrix_texture); level_sprite.graphics.drawRect(x - 150, y - 150, w, h); level_sprite.graphics.endFill(); 


And for clarity, let's add another StaticRect (in the constructor):

 CreateStaticRect(150, 140, 30, 30); 


For the time being, disable DebugDraw by commenting on the line:

 // world.SetDebugDraw(dbgDraw); 


Compile, admire the traced walls:



We learned how to draw our world, create obstacles.
Create an abstract class, our obstacles will be inherited from it, listing the Obstacle :

 package { import Box2D.Dynamics.b2World; import flash.display.Sprite; /** * ... * @author forhaxed */ public class Obstacle extends Sprite { public var active:Boolean = true; //      public var angle:int = 0; //   public var viewport:Sprite; //   viewport public var world:b2World; //    // 0 - LEFT, 1 - UP, 2 - RIGHT - 3 - DOWN public function Obstacle(_viewport:Sprite, _world:b2World, x:Number, y:Number, angle:int = 0) { viewport = _viewport; world = _world; } } } 


And create a child of this class, Spike (actually our spike) listing:

 package { import Box2D.Collision.Shapes.b2CircleDef; import Box2D.Dynamics.b2Body; import Box2D.Dynamics.b2BodyDef; import Box2D.Dynamics.b2World; import flash.display.Sprite; /** * ... * @author forhaxed */ public class Spike extends Obstacle { public function Spike(_viewport:Sprite, _world:b2World, x:Number, y:Number, angle:int = 0) { super(_viewport, _world, x, y, angle); //   switch(angle) //     { case 0: this.rotation = 90; break; case 1: this.rotation = 180; break; case 2: this.rotation = 270; break; case 3: this.rotation = 0; break; } var body:b2Body; var bodyDef:b2BodyDef; var circleDef:b2CircleDef; var sprite:Sprite; this.x = x; this.y = y; viewport.addChild(this); /* OBSTACLE */ x = x / 30; y = y / 30; var r:Number = 6 / 30; bodyDef = new b2BodyDef(); bodyDef.position.Set(x, y); circleDef = new b2CircleDef(); circleDef.radius = r; circleDef.density = 1; circleDef.friction = 1; circleDef.isSensor = true; //  isSensor  true,        circleDef.restitution = 0.2; body = world.CreateBody(bodyDef); body.SetUserData("spike"); //    spike body.SetSprite(this); //   body.CreateShape(circleDef); body.SetMassFromShapes(); addChild(new saw()); } } } 


Add three thorns to our world, go to the constructor of the main class and write:

 new Spike(viewport, world, 37, 5, 0); new Spike(viewport, world, 15, 27, 1); new Spike(viewport, world, 15, -17, 3); 


We compile, admire the spikes, but they are not dangerous yet, so let's make them dangerous.



We create a new class GearMazeContact , which is inherited from b2ContactListener , we will redefine its methods (yes, in AS3 override is very relevant). Listing:

 package { import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Dynamics.Joints.*; import Box2D.Dynamics.Contacts.*; import Box2D.Common.*; import Box2D.Common.Math.*; import flash.display.MovieClip; public class GearMazeContact extends b2ContactListener { public override function Add(point:b2ContactPoint):void { var p1:b2Body = point.shape1.GetBody(); var p2:b2Body = point.shape2.GetBody(); if(p1.GetUserData()=="hero" && p2.GetUserData()=="spike") { if ((p2.GetSprite() as Obstacle).active) { trace("Ha-ha!"); p1.SetUserData("hero_dead"); //     hero_dead,    /* ,         ,    ,     ,    */ } } } } } 


We connect our listener to the world, in the same place where we create the world and set DebugDraw:

 var GearListener:GearMazeContact = new GearMazeContact(); world.SetContactListener(GearListener); 


And we add the functionality that will remove our ball in the case of death, in the array search with the bodies (enterFrameListener):

 if (body.GetUserData() == "hero_dead") { if (body.GetSprite() is Sprite) viewport.removeChild(body.GetSprite()); world.DestroyBody(body); } 


Here you can continue to cut the gameplay and make all sorts of buns: lasers, portals, saws, girls .

If you want to make a toy out of this, you can make a simple algorithm for procedural generation of labyrinths with an Obstacle.

By tradition, I attach all the code for the article, a link to the demo and the finished toy made by these steps.

By the way, the finished game will not be, alas :-(
While this is some kind of alpha with 18 levels. Refactoring the code kills me, I'm a creative person: create, create and create again. Money does not interest me :-)

Links: source | demo | ready game

PS Thanks a lot to a good datacompboy man when I wrote the first part of the article - I donated hosting with a domain to me so that demos, games and source codes you wouldn’t download from frivolous sites.
PSS you can still write to me about gamedev in as3, I’m happy to answer.

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


All Articles