Use cocos2d-x and box2d to create a breakout (hitting bricks) Game (1)

Source: Internet
Author: User
Tags addchild

This tutorial is based on the Child Dragon Mountain Man translation cocos2d iPhone tutorial, rewrite with cocos2d-x for xNa engine, plus some of my processing and production. In the tutorial, most of the text images are from the original author and the author of the translation, Long Shan, and many others are my own understandings and processing. I would like to thank the original author for his tutorial and the translation of Zilong shangren. This tutorial is only for learning and communication purposes. Do not conduct commercial communications.

IPhone tutorial address: http://www.cnblogs.com/zilongshanren/archive/2011/05/27/2059460.html

IPhone tutorial original address: http://www.raywenderlich.com/475/how-to-create-a-simple-breakout-game-with-box2d-and-cocos2d-tutorial-part-12

Program:


Box2d is a very powerful physical engine library, and it is well suited for game development on the Windows Phone with cocos2d-x. The famous Angry Birds and tiny wings are all written in box2d. You can use it to do many things well. Of course, the best learning method is to use it to create a simple game.

In this tutorial, we will create a simple breakout game step by step, complete the collision detection, basketball rebound physical effect, drag paddle through touch (that is, the White Rectangle ), and scenarios of victory/failure.

If you are not familiar with cocos2d-x and box2d, you may first have to read how to use cocos2d-x to make a simple window Phone 7 game and
For WP7> Use box2d in cocos2d-x.

Okay, it's time to make a breakout!

A ball that always rebounds

Open vs2010. Create a new cocos2d-x project named cocos2dbox2dbreakoutdemo and copy and add related DLL. The practice can refer to the "in the cocos2d-x for WP7> in the use of box2d in the cocos2d-x".

Add a new class to classes. And name it breakoutscene. CS.

Add the following space references.

using cocos2d;using Box2D.XNA;

Add a breakoutlayer class in this file and inherit it from cclayer.

        public static double PTM_RATIO = 32.0;        World world;        Body groundBody;           Fixture bottomFixture;        Fixture ballFixture;

In these statements, the first statement defines the ratio. We have discussed this ratio in the previous tutorial, and we will not be so embarrassed here.

Then, reload the init method. The modification is as follows:

        public override bool init()        {            if (!base.init())                return false;            CCSize winSize = CCDirector.sharedDirector().getWinSize();            CCLabelTTF title = CCLabelTTF.labelWithString("Boxing", "Arial", 24);            title.position = new CCPoint(winSize.width / 2, winSize.height - 50);            this.addChild(title);            //Create the world            Vector2 gravity = new Vector2(0.0f, 0.0f);            bool doSleep = true;            world = new World(gravity, doSleep);            //Create edges around the entire screen            BodyDef groundBodyDef = new BodyDef();            groundBodyDef.position = new Vector2(0, 0);            groundBody = world.CreateBody(groundBodyDef);            PolygonShape groundBox = new PolygonShape();            FixtureDef boxShapeDef = new FixtureDef();            boxShapeDef.shape = groundBox;            groundBox.SetAsEdge(new Vector2(0, 0), new Vector2((float)(winSize.width / PTM_RATIO), 0));            bottomFixture = groundBody.CreateFixture(boxShapeDef);            groundBox.SetAsEdge(new Vector2(0, 0), new Vector2(0, (float)(winSize.height / PTM_RATIO)));            groundBody.CreateFixture(boxShapeDef);            groundBox.SetAsEdge(new Vector2(0, (float)(winSize.height / PTM_RATIO)),                new Vector2((float)(winSize.width / PTM_RATIO), (float)(winSize.height / PTM_RATIO)));            groundBody.CreateFixture(boxShapeDef);            groundBox.SetAsEdge(new Vector2((float)(winSize.width / PTM_RATIO), (float)(winSize.height / PTM_RATIO)),                new Vector2((float)(winSize.width / PTM_RATIO), 0));            groundBody.CreateFixture(boxShapeDef);            return true;        }

Well, this code is similar to creating a boundary for the entire Screen in the previous tutorial. Then, this time, we set the gravity to 0, because in our breakout game, we don't need gravity! Note that we store a pointer to the fixture at the bottom for later use (in the following tutorial, We will track when the basketball matches the top ).

Now, download my basketball image and add it to the images folder of the content project. Let's add an genie to the scenario. Then add the following code snippet to the above Code:

            //Create sprite and add it to the layer            CCSprite ball = CCSprite.spriteWithFile(@"images/Ball");            ball.position = new CCPoint(100, 100);            ball.tag = 1;            this.addChild(ball);

There is no doubt that we have done similar things many times. Note: We have set a tag ID for basketball, and you will see what this tag is.

Next, create a body for the shape:

           //Create ball body             BodyDef ballBodyDef = new BodyDef();            ballBodyDef.type = BodyType.Dynamic;            ballBodyDef.position = new Vector2((float)(100 / PTM_RATIO), (float)(100 / PTM_RATIO));            ballBodyDef.userData = ball;            Body ballBody = world.CreateBody(ballBodyDef);            //Create circle shape            CircleShape circle = new CircleShape();            circle._radius = (float)(26.0 / PTM_RATIO);            //Create shape definition and add to body            FixtureDef ballShapeDef = new FixtureDef();            ballShapeDef.shape = circle;            ballShapeDef.density = 1.0f;            ballShapeDef.friction = 0.0f;            ballShapeDef.restitution = 1.0f;            ballFixture = ballBody.CreateFixture(ballShapeDef);

This looks similar to the one in the previous tutorial. To create a body object, we must first create a body definition structure, then create the body, then shape, specify the fixture structure, and finally create a fixture object.

Note that we set these parameters to a little different: we set restitution to 1.0, which means that our ball will be completely elastic during collision.

At the same time, we also saved the fixture of the ball. The reason is the same as why we saved the fixture at the bottom of the screen. You will see it later.

Update: note that we also set the friction value of the ball to 0. This will prevent a slight deviation during the round-trip Collision due to friction loss of energy.

Well, it's time to do something totally different! The code above is as follows:

            Vector2 force = new Vector2(10, 10);            ballBody.ApplyLinearImpulse(force, ballBodyDef.position);

An impulse is applied to the ball so that it can be moved in a specific direction during initialization.

The last thing is to add a tick Scheduling Method in the init method:

this.schedule(tick);

The implementation of the tick method is as follows:

        void tick(float dt)        {            world.Step(dt, 10, 10);            for (Body b = world.GetBodyList(); b != null;b = b.GetNext() )            {                if (b.GetUserData() != null)                {                    CCSprite sprite = (CCSprite)b.GetUserData();                    sprite.position = new CCPoint((float)(b.GetPosition().X * PTM_RATIO),                        (float)(b.GetPosition().Y * PTM_RATIO));                    sprite.rotation = -1 * MathHelper.ToDegrees(b.GetAngle());                }            }        }

Of course, there is nothing special here, just like in the previous tutorial.

Now, before running the test. You have to do some work.

Add a static method to the breakoutlayer to work as the initialization layer.

        public static new BreakOutLayer node()        {            BreakOutLayer layer = new BreakOutLayer();            if (layer.init())            {                return layer;            }            return null;        }

Then let the previously unused breakoutccscene class inherit from ccscene. Add a breakoutlayer to the constructor.

Add the following code to the constructor:

            BreakOutLayer layer = BreakOutLayer.node();            this.addChild(layer);

Next, modify the director class appdelegate. Find the applicationdidfinishlaunching method. The end part is modified as follows:

            // create a scene. it's an autorelease object            //CCScene pScene = cocos2dBox2DBreakOutDemoScene.scene();            Classes.BreakoutScene pScene = new Classes.BreakoutScene();            //run            pDirector.runWithScene(pScene);            return true;

Okay. Let's have a try. Compile and run the project, and you will see a ball playing back and forth infinitely in the screen! ---- Very cool!



Add paddle:

If no paddle exists, it cannot be called a breakout game. Download http://dl.dbank.com/c0at986elastic and upload it to the imagesfolder. Then, add the following member variables to the breakoutlayer class:

        Body paddleBody;        Fixture paddleFixture;

Then, construct the paddle body in the init method:


           //Create paddle and add it to the layer            CCSprite paddle = CCSprite.spriteWithFile(@"images/Paddle");            paddle.position = new CCPoint(winSize.width / 2, 50);            this.addChild(paddle);            //Create paddle body            BodyDef paddleBodyDef = new BodyDef();            paddleBodyDef.type = BodyType.Dynamic;            paddleBodyDef.position = new Vector2((float)(winSize.width / 2 / PTM_RATIO), (float)(50 / PTM_RATIO));            paddleBodyDef.userData = paddle;            paddleBody = world.CreateBody(paddleBodyDef);            //Create paddle shape            PolygonShape paddleShape = new PolygonShape();            paddleShape.SetAsBox((float)(paddle.contentSize.width / PTM_RATIO / 2), (float)(paddle.contentSize.height / PTM_RATIO / 2));            //Create shape definition and add to body            FixtureDef paddleShapeDef = new FixtureDef();            paddleShapeDef.shape = paddleShape;            paddleShapeDef.density = 10.0f;            paddleShapeDef.friction = 0.4f;            paddleShapeDef.restitution = 0.1f;            paddleFixture = paddleBody.CreateFixture(paddleShapeDef);

I don't want to spend too much time explaining the above content. Because it is not much different from the process of creating a basketball body. Here, we only show the differences:

  • When you create ccsprite, you do not need to specify the sprite size. If you pass a file name to it, it automatically calculates the size.
  • Note that circle shape is not used here. This time, we use Polygon Shape. We use an auxiliary method to create a shape. Of course, its shape is a box.
  • We use the setasbox method to specify the position of the Shape relative to the body. This method is useful when building complex objects. Here, we just place the shape in the middle of the body.
  • I set the paddle density to much greater than the ball and adjusted other parameters. (These parameters need to be tested and calculated based on the real knowledge of high school physics, which may not be available)
  • At the same time, we store the reference of paddlebody and paddlefixture for later use.

If you compile and run it, you will see a paddle in the middle of the screen, and it will rebound when the ball hits it.

Then, this is not very interesting, because we cannot move paddle!

Move paddle

The Touch event is required to move the paddle, so the touch event is allowed in the init method first:

            this.isTouchEnabled = true;

Then, add the following member variables to the breakoutlayer class:

        MouseJoint mouseJoint = null;

Now, let's implement the touch method! First, cctouchesbegan:

        public override void ccTouchesBegan(List<CCTouch> touches, CCEvent event_)        {            if (mouseJoint != null)                return;            CCTouch myTouch = touches.FirstOrDefault();            CCPoint location = myTouch.locationInView(myTouch.view());            location = CCDirector.sharedDirector().convertToGL(location);            Vector2 locationWorld = new Vector2((float)(location.x / PTM_RATIO), (float)(location.y / PTM_RATIO));            if (paddleFixture.TestPoint(locationWorld))            {                MouseJointDef md = new MouseJointDef();                md.bodyA = groundBody;                md.bodyB = paddleBody;                md.target = locationWorld;                md.collideConnected = true;                md.maxForce = 1000.0f * paddleBody.GetMass();                mouseJoint = (MouseJoint)world.CreateJoint(md);                paddleBody.SetAwake(true);            }        }

Er, a lot of new knowledge! Let's discuss it at 1.1.

First, convert the touch coordinate to the coocs2d coordinate (converttogl), and then convert it to the box2d coordinate (locationworld ).

Then, we use the paddle Fixture Method to test whether the touch point is inside the fixture.

If yes, we will create a so-called "Mouse joint". In box2d, a mouse joint is used to move a body toward a specified point. In this example, it is the direction of the user point.

After you create a mouse joint, you assign two bodies to it. The first is not used. It is usually set to the ground body. The second is the body you want to move. In this example, It is paddle.

Next, you specify the moving end point. In this example, the user clicks the position.

Then, you tell box2d, but when bodya and bodyb collide, think of it as a collision, rather than ignoring it. This is important! Because I didn't set it to true before, and the result won't work! Therefore, when we drag the paddle with the mouse, it will not conflict with the boundary of the screen, and sometimes, my paddle will fly out of the screen directly. This is very strange, but now I know why. Because bodya and bodyb are not set to be collision-able.

You then specify the maximum force of moving the body. If you reduce this value, the paddle body slows down when responding to the mouse movement. However, we want paddle to quickly respond to mouse changes.

Finally, we add the joint to the world and save the pointer because it is useful later. At the same time, we also need to set the body to awake ). This is because if the body is sleeping, it will not respond to the mouse movement!

Now, let's add the cctouchesmoved method:

        public override void ccTouchesMoved(List<CCTouch> touches, CCEvent event_)        {            if (mouseJoint == null)                return;            CCTouch myTouch = touches.FirstOrDefault();            CCPoint location = myTouch.locationInView(myTouch.view());            location = CCDirector.sharedDirector().convertToGL(location);            Vector2 locationWorld = new Vector2((float)(location.x / PTM_RATIO), (float)(location.y / PTM_RATIO));            mouseJoint.SetTarget(locationWorld);        }

The starting part of this method is similar to that of cctouchesbegan-we convert the touch coordinate to the box2d coordinate. The only difference is that we updated the target position of the mouse joint (that is, the position where we want to move the paddle ).

Next, we will add the cctouchescacelled and cctouchesended methods:

        public override void ccTouchesCancelled(List<CCTouch> touches, CCEvent event_)        {            if (mouseJoint != null)            {                world.DestroyJoint(mouseJoint);                mouseJoint = null;            }            }        public override void ccTouchesEnded(List<CCTouch> touches, CCEvent event_)        {            if (mouseJoint != null)            {                world.DestroyJoint(mouseJoint);                mouseJoint = null;            }        }

Only one thing we do in these methods is to destroy the mouse joint after we move the paddle or cancel the move.

Compile and run the program. Now you can move the paddle with the mouse and make it collide with basketball!

Very good... But wait, it's not a breakout! We cannot move the paddle to any position. We can only move it back and forth at the bottom of the screen!

Restrict the movement of paddle

We can easily restrict the movement of paddle by adding another joint called Prismatic Joint. This joint limits the movement of a body along a specified axis.

Therefore, we can use this method to limit the movement of paddle relative to the ground, that is, it can only move along the X axis.

Let's look at the relevant code. Add the following code to the init method:

            //Restrict paddle along the x axis            PrismaticJointDef jointDef = new PrismaticJointDef();            Vector2 worldAxis = new Vector2(1.0f, 0.0f);            jointDef.collideConnected = true;            jointDef.Initialize(paddleBody, groundBody, paddleBody.GetWorldCenter(), worldAxis);            world.CreateJoint(jointDef);

The first thing is to specify a vector along the X axis. Then, we need to specify collideconnected to true, so that our ball can bounce correctly instead of flying out of the screen.

Then, initialize the joint, specify the paddle and ground bodies, and then use the World object to create the joint!

Compile and run. Now you can only move the paddle along the X axis. This is exactly what we want, isn't it?

Complete touch event

Now, you may find that sometimes the ball rebound is very fast and sometimes slow. It depends on how you control the collision between the paddle and the ball.

Original Author's theory: When I first tried to fix this bug, I used the setlinearvelocity method by directly adjusting the ball speed. Then, Steve oldmeadow also pointed out that this is very bad! It will damage physical simulation. The best way is to call the setlineardamping method to indirectly affect the speed. Therefore, this tutorial is used now. (Damping means damping)

Next, add the following code to the tick method. The specific location is after ccsprite sprite = (ccsprite) B. getuserdata:

if (sprite.tag == 1)                    {                        int maxSpeed = 10;                        Vector2 velocity = b.GetLinearVelocity();                        float speed = velocity.Length();                        if (speed > maxSpeed)                        {                            b.SetLinearDamping(0.5f);                        }                        else if (speed < maxSpeed)                        {                            b.SetLinearDamping(0.0f);                        }                    }

Here, we determine the sprite tag to see if it is a ball tag. If yes, we will check its speed. If it is too large, we will set its damping to 0.5 so that it can slow down. If you are not satisfied with the speed, you can adjust the maxspeed and damping values.

If you compile and run the program, you will see a ball bounce back and forth around the screen at a very moderate speed.

Download this project: Workshop.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.