• cocos2dx for wp 之Box2D游戏是男人就坚持60M(一)


    搞了几天。发现cocos2d-x对c#(xna)真的是抛弃很彻底了。以后还是转c++吧。

    废话结束,开始记录。

    首先,跟上一节一样,先创建好一个世界并让这个世界开始模拟物理世界。

    创建一个继承于CCLayer的一个层:PlayGame。并添加好下面三个引用、一个常量以及一些全局变量:

     1 using cocos2d;
     2 using Box2D.XNA;
     3 using Microsoft.Xna.Framework;
     4 
     5 
     6 public static double PTM_RATIO = 32.0;
     7 
     8 World world;
     9 CCSprite ball;
    10 Body groundBody;

    重写init函数、node函数

    View Code
     1 public override bool init()
     2          {
     3              if (!base.init())
     4                  return false;
     5              //获取窗口大小
     6              CCSize winSize = CCDirector.sharedDirector().getWinSize();
     7  
     8              
     9  
    10              CCLabelTTF title = CCLabelTTF.labelWithString("Boxing", "Arial", 24);
    11              title.position = new CCPoint(winSize.width / 2, winSize.height / 2 - 50);
    12              this.addChild(title, 1);
    13  
    14              Vector2 gravity = new Vector2(0.0f, 0.0f);
    15              bool doSleep = true;
    16              world = new World(gravity, doSleep);
    17  
    18  
    19              BodyDef groundBodyDef = new BodyDef();
    20              groundBodyDef.position = new Vector2(0, 0);
    21              groundBody = world.CreateBody(groundBodyDef);
    22              PolygonShape groundBox = new PolygonShape();//凸多边形
    23              FixtureDef boxShapeDef = new FixtureDef();
    24              boxShapeDef.shape = groundBox;
    25              groundBox.SetAsEdge(new Vector2(0, 0), new Vector2((float)(winSize.width / PTM_RATIO), 0));
    26              groundBody.CreateFixture(boxShapeDef);
    27              groundBox.SetAsEdge(new Vector2(0, 0), new Vector2(0, (float)(winSize.height / PTM_RATIO)));
    28              groundBody.CreateFixture(boxShapeDef);
    29              groundBox.SetAsEdge(new Vector2(0, (float)(winSize.height / PTM_RATIO)),
    30                  new Vector2((float)(winSize.width / PTM_RATIO), (float)(winSize.height / PTM_RATIO)));
    31              groundBody.CreateFixture(boxShapeDef);
    32              groundBox.SetAsEdge(new Vector2((float)(winSize.width / PTM_RATIO), (float)(winSize.height / PTM_RATIO)),
    33                  new Vector2((float)(winSize.width / PTM_RATIO), 0));
    34              groundBody.CreateFixture(boxShapeDef);
    35  
    36              Body body1 = createBall();
    37              Body body2 = createBall();
    38  
    39              Player play1 = new Player(this,world,groundBody);
    40  
    41              this.schedule(tick);
    42              return true;
    43          }
    View Code
     1 public static new CCLayer node()
     2          {
     3              PlayGame layer = new PlayGame();
     4              if (layer.init())
     5              {
     6                  return layer;
     7              }
     8              else
     9                  layer = null;
    10              return layer;
    11          }

    基本上和上一节的内容差不多,但是我们这里把重力gravity设置为0,0.因为我们这个游戏并不需要重力的存在。 至于createBall是一个自定义函数,而Player是一个自定义的类,等下会讲到。

    createBall函数:

    View Code
     1 public Body createBall()
     2          {
     3              Body body;
     4              Random rand = new Random();
     5              ball = CCSprite.spriteWithFile("images/ball");
     6              this.addChild(ball);
     7              int Vx = rand.Next(1, 7) * 100;
     8              int Vy = rand.Next(1, 4) * 100;
     9              BodyDef ballBodyDef = new BodyDef();
    10              ballBodyDef.type = BodyType.Dynamic;//动态类型
    11              ballBodyDef.position = new Vector2((float)(Vx / PTM_RATIO), (float)(Vy / PTM_RATIO));
    12              ballBodyDef.userData = ball;
    13  
    14              body = world.CreateBody(ballBodyDef);
    15              CircleShape circle = new CircleShape();
    16              circle._radius = (float)(26.0 / PTM_RATIO);
    17  
    18              FixtureDef ballShapeDef = new FixtureDef();
    19              ballShapeDef.shape = circle;
    20              ballShapeDef.density = 1.0f;//密度
    21              ballShapeDef.friction = 0.0f;//摩擦
    22              ballShapeDef.restitution = 1.0f;
    23              body.CreateFixture(ballShapeDef);
    24  
    25              int Fx = rand.Next(1, 7) * 10;
    26              int Fy = rand.Next(1, 4) * 10;
    27              Vector2 force = new Vector2(Fx, Fy);
    28              body.ApplyLinearImpulse(force, ballBodyDef.position);
    29  
    30              return body;
    31          }

    这个函数的内容也是老内容了,除了设置了一个冲力force。

    1 Vector2 force = new Vector2(Fx, Fy);

    2 body.ApplyLinearImpulse(force, ballBodyDef.position);

    有了这个冲力,我们的游戏中初始化的球就会自己动起来

    添加好一个tick函数

    View Code
     1 void tick(float dt)
     2          {
     3              world.Step(dt, 10, 10);
     4  
     5              for (Body b = world.GetBodyList(); b != null; b = b.GetNext())
     6              {
     7                  if (b.GetUserData() != null)
     8                  {
     9                      CCSprite ballData = (CCSprite)b.GetUserData();
    10                      ballData.position = new CCPoint((float)(b.GetPosition().X * PTM_RATIO),
    11                      (float)(b.GetPosition().Y * PTM_RATIO));
    12                      ballData.rotation = -1 * MathHelper.ToDegrees(b.GetAngle());
    13                  }
    14              }
    15          }

    以上都是一些老内容。不熟悉可以去看上一篇介绍。
    最后修改一下程序入口applicationDidFinishLaunching

    1 CCScene pScene = CCScene.node();
    2 pScene.addChild(Classes.PlayGame.node());
    3 pDirector.deviceOrientation = ccDeviceOrientation.CCDeviceOrientationLandscapeLeft;
    4 pDirector.runWithScene(pScene);
    5 return true;

    运行,程序就会出现两个小球不停的在屏幕里来回跳动。

    接下来,是该实现控制小球和各个小球之间的碰撞了。

    构造Player自定义类。继承于CCSprite、ICCTargetedTouchDelegate。因为我们要手动(触屏)控制这个玩家,所以也要同时继承于ICCTargetedTouchDelegate。

    这里也是我fengyun1989不同的地方。在他那边是直接在游戏中的游戏界面写touch事件(因为Layer里也有touch事件)。而我习惯于将各个对象分开。

    首先,添加一些变量。方便后面调用,具体用处在用到时细说。

    MouseJoint mouseJoint=null;
    Fixture ballFixture;
    Body spriteBody;
    World locWord;
    Body gBody;
    public static double PTM_RATIO = 32.0;

    重写构造函数Player()

    View Code
     1 public Player(CCLayer layer,World world,Body groundBody)
     2         {
     3             gBody = groundBody;
     4             locWord = world;
     5 
     6             CCSize s = CCDirector.sharedDirector().getWinSize();
     7             this.initWithFile("images/ball");
     8             this.position = new CCPoint(s.width / 2, s.height / 2);
     9             layer.addChild(this);
    10             addBoxBodyForSprite(this, world);
    11         }

    同时将PlayGame里创建好的世界world和groundBody传过来,因为我们的玩家必须在同一个世界里才行。而且后面构造一些fixture也需要用到。

    接下来,是该进行触摸事件的修改了。先是ccTouchBegan()

    View Code
     1 public virtual bool ccTouchBegan(CCTouch touch, CCEvent eventer)
     2          {
     3              //创建一个鼠标关节
     4              if (mouseJoint != null)
     5                  return false;
     6              CCPoint location = touch.locationInView(touch.view());  
     7              location = CCDirector.sharedDirector().convertToGL(location);  
     8              Vector2 locationWorld = new Vector2((float)(location.x / PTM_RATIO), (float)(location.y / PTM_RATIO));
     9  
    10              if (ballFixture.TestPoint(locationWorld))
    11              {
    12                  MouseJointDef md = new MouseJointDef();
    13                  md.bodyA = gBody;
    14                  md.bodyB = spriteBody;
    15                  md.collideConnected = true;
    16                  md.target = locationWorld;
    17                  md.maxForce = 1000.0f * spriteBody.GetMass();
    18                  mouseJoint = (MouseJoint)locWord.CreateJoint(md);
    19                  spriteBody.SetAwake(true);
    20                  return true;
    21              }
    22              else
    23                  return false;

    首先,我们把touch坐标转换成coocs2d坐标(convertToGL)然后,再转换成Box2d坐标(locationWorld)。

    如果是的话,我们就创建一个所谓的”鼠标关节“。在Box2d里面,一个鼠标关节用来让一个body朝着一个指定的点移动---在这里个例子中,就是用户点的方向。

    当你创建一个mousejoint后,你赋值给它两个body。第一个没有被使用,通常都是设置成groundbody(前面提到这里用到了)。第二个,就是你想让它移动的body(这个类里面的精灵的body),在这个例子中就是spriteBody。

    你指定移动的终点---这个例子中就是用户点击的位置locationWorld。

    然后,设置bodyA和bodyB碰撞的时候,把它当成是碰撞,而不是忽略它。这个很重要!

    如果没有设置,当我们用鼠标拖动这个你的小球的时候,它并不会与屏幕的边界相碰撞。

    指定移动body的最大的力是多少。如果你减少这个数值的话,spritebody响应鼠标移动时就会慢一些。但是,我们想让spritebody快速地响应鼠标的变化。

    最后,我们把这个关节加入到world中。同时,我们还要把body设置成苏醒的(awake)。之所以要这么做,是因为如果body在睡觉的话,那么它就不会响应鼠标的移动!

    上面用到了一个鼠标关节mouseJoint。这是一个自定义的类。内容如下:

    View Code
     1 class MyContact
     2     {
     3         public Fixture fixtureA;
     4         public Fixture fixtureB;
     5 
     6     }
     7     class MyContactListener : IContactListener
     8     {
     9         public List<MyContact> contacts = new List<MyContact>();
    10 
    11         public void BeginContact(Contact contact)
    12         {
    13             MyContact myContact = new MyContact()
    14             {
    15                 fixtureA = contact.GetFixtureA(),
    16                 fixtureB = contact.GetFixtureB()
    17             };
    18             contacts.Add(myContact);
    19 
    20         }
    21 
    22         public void EndContact(Contact contact)
    23         {
    24             contacts.Clear();
    25         }
    26 
    27         public void PostSolve(Contact contact, ref ContactImpulse impulse)
    28         {
    29         }
    30         public void PreSolve(Contact contact, ref Manifold oldManifold)
    31         {
    32         }
    33     }

    接下来,让我们添加ccTouchesMoved方法:

    View Code
     1 public virtual void ccTouchMoved(CCTouch touch, CCEvent eventer)
     2          {
     3              if (mouseJoint == null)
     4                  return;
     5              Vector2 point = new Vector2(this.convertTouchToNodeSpace(touch).x,
     6                this.convertTouchToNodeSpace(touch).y);
     7              CCPoint location = touch.locationInView(touch.view());
     8              location = CCDirector.sharedDirector().convertToGL(location);
     9              Vector2 locationWorld = new Vector2((float)(location.x / PTM_RATIO), (float)(location.y / PTM_RATIO));
    10  
    11              mouseJoint.SetTarget(locationWorld);
    12          }

    我们更新了鼠标关节的目标位置(也就是我们想让玩家移动的位置的)。

    我们添加ccTouchesCacelled和ccTouchesEnded方法:这里只实现一件事,就是在我们移动完paddle或者取消移动之后销毁mouse joint。

    View Code
     1 public virtual void ccTouchEnded(CCTouch touch, CCEvent eventer)
     2          {
     3              if (mouseJoint != null)
     4              {
     5                  mouseJoint = null;
     6                  locWord.DestroyJoint(mouseJoint);
     7              }
     8          }
     9  
    10          public void ccTouchCancelled(CCTouch touch, CCEvent eventer)
    11          {
    12              if (mouseJoint != null)
    13              {
    14          mouseJoint = null;
    15                  locWord.DestroyJoint(mouseJoint);
    16                  
    17              }
    18          }

    编译并运行,你现在可以用手指控制属于你的精灵了,同时可以让它与篮球相互碰撞!

  • 相关阅读:
    webpack安装、环境搭建和基本配置
    webpack知识点总结
    Vue之Vuex的使用
    vue之获取滚动条位置
    MongoDB ORM mongoose 配置和使用
    sequelize之通过options生成sql语句
    七牛上传之PutExtra的使用
    使用ssl-validator识别证书信息
    深入理解计算机系统(第三版)第八章重要内容摘要
    深入理解计算机系统(第三版)第七章重要内容摘要
  • 原文地址:https://www.cnblogs.com/dieaz5/p/2979436.html
Copyright © 2020-2023  润新知