qjchen 发表于 2013-1-28 20:19:47

[飞马系列] AUTOCAD中的"愤怒的小鸟"游戏,需BOX2DX引擎支持

本帖最后由 qjchen 于 2013-10-20 19:28 编辑

AutoCAD版的愤怒的小鸟 代码
http://bbs.mjtd.com/data/attachment/album/201301/28/1010367afmmovffoida7vf.gif
其实两三年前就有这个想法了,当时学flex编程的时候,找到了BOX2D库,觉得很有趣
那时只是简单测试了一下flash的box2d库,没有仔细学习。愤怒的小鸟也还没有出来。后来有了,知道它也是用了这个库,但是和box2d库的作者弄的不可开交。
由于个人比较喜欢autocad,所以比较想在cad中实现。但当时的BOX2D库还只有c++版,不大懂。这几天稍有空,随手找了几个物理引擎库,发现已经有人port到了.net版,于是重燃了此番昔日思路。花了2天时间,利用box2d引擎,撰写了如下这个多线程版本的 Autocad 版的 愤怒的小鸟。可以一边玩,一边画图。纯属自娱自乐。此.NET引擎似乎还有一些小问题,比如圆形似乎就没有rotation特性(或者是我还没有发现),时间间隔亦宜小些,否则会出现一些侵蚀现象(当然,后期会调整过来)。
本文只提供cad部分的代码,BOX2DX的由于不是我编的,自然不能放上来如有兴趣的,请到http://code.google.com/p/box2dx/ 处,根据其协议,相应下载和使用,此处不做讨论。由于只是一个娱乐自玩小程序,故没有太多的容错内容,也没考虑多少种情况,大概能用而已。
测试的dwg可见附件。使用方法:1. 打开dwg2. 编译代码到DLL,另外编译下载的BOX2DX得到BOX2DX.DLL,Netload这两个ball.dll   需要注意的是,在编译用于高版本ACAD的BOX2DX.DLL的时候,需要将其编译平台设为any cpu3. 用mybox2d命令启动,选择不可移动物体,选择可移动物体,选择可发射物体(力在此图中建议为120000)4. 要结束这个动态过程,请键入mys结束之,也一定在关闭cad前运行此命令// ========================================================================
// The following codes are writen by QJCHEN                               ;
// Http://qjchen.mjtd.com                                                 ;
// Important: Box2d Library is necessary to support this code             ;
// http://code.google.com/p/box2dx/                                       ;
// You should download it to generate Box2DX.dll first                  ;
//   then you could use this code                                       ;
// Purpose: Test angry bird in Autocad                                    ;
// How to use:                                                            ;
// 1) open the dwg in the attachment                                    ;
// 2) netload Box2DX and this dll (compile this code to DLL)            ;
// 3) command mybox2d, then select the object for fix object(circle or    ;
//    lwpolyline), moving object, and ejecting object (these two is block ;
//    reference, I have make several one, such as 1*1 box, R=0.5 circle,;
//    then use different scalefactor to insert them). Then input the force;
//    because the unit of these world is (N, m), so it need a big force   ;
//    in this jpg, the force is 120000                                    ;
// 4) Use mys command to stop the game                                    ;
// The platform: Acad2008 and after                                       ;
// .Net platform is considered to be a better solution for this function;
// Version: 1.0                                                         ;
// 2013.01.28                                                             ;
// Thanks to Gile's function for judging the direction of lwpolyline      ;
// ========================================================================
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using System;
using System.Collections;
using Box2DX.Dynamics;
using Box2DX.Collision;
using Box2DX.Common;
//gile's function
using Body = Box2DX.Dynamics.Body;
using aapp = Autodesk.AutoCAD.ApplicationServices.Application;
using sm = System.Math;
namespace ball
{
    public class Class1
    {
      public static ObjectIdCollection mBodyId;//能移动的所有物体的id    Moving objects IDs
      public static ObjectIdCollection eBodyId;//能有初速度的所有物体的idEjecting objects IDs
      public static World world;
      public static float timeStep = 1.0f / 60.0f;
      public static int velocityIterations = 8;
      public static int positionIterations = 1;
      public static Body body;
      public static Body[] mbody;
      public static Body[] ebody;
      static System.Windows.Forms.Timer timer2 = new System.Windows.Forms.Timer();
      // 停止的命令
      
      public void drawpics()
      {
            timer2.Stop();
      }
      
      public void test()
      {
            //世界的定义,只在其中是有用的,定义在-100到100的高度范围内
            //World of Box2d, -100 to 100
            AABB worldAABB = new AABB();
            worldAABB.LowerBound.Set(-100.0f);
            worldAABB.UpperBound.Set(100.0f);
            Vec2 gravity = new Vec2(0.0f, -10.0f);// Define the gravity vector.
            bool doSleep = true;// Do we want to let bodies sleep?
            world = new World(worldAABB, gravity, doSleep);// Construct a world object, which will hold and simulate the rigid bodies.
            //--选择物体分别作为刚体和可移动物体
            //--Select Object for Each group
            SelectForFixBody();
            SelectForMovingBody();
            SelectForEjectingBody();
            //--设置时间 Time setting
            timer2.Interval = 20;//设置时间为20秒-这个和动画间隔相关
            timer2.Tick += new EventHandler(timer2_Tick);
            timer2.Start();
      }
      private static void timer2_Tick(System.Object myObject, System.EventArgs myEventArgs)
      {
            DocumentLock docLock = aapp.DocumentManager.MdiActiveDocument.LockDocument(); // Code
            world.Step(timeStep, velocityIterations, positionIterations);
            //对每一个可移动的物体进行更新 Update for moving objects
            int i = 0;
            foreach (ObjectId id in mBodyId)
            {
                Vec2 position = mbody.GetPosition();
                float angle = mbody.GetAngle();
                ChangeBlockRefInsPointAndAngle(id, new Point3d(position.X, position.Y, 0), angle);
                i = i + 1;
            }
            //Update for ejecting objects
            i = 0;
            foreach (ObjectId id in eBodyId)
            {
                Vec2 position = ebody.GetPosition();
                float angle = ebody.GetAngle();
                ChangeBlockRefInsPointAndAngle(id, new Point3d(position.X, position.Y, 0), angle);
                i = i + 1;
            }
            docLock.Dispose();
      }
      //选择刚性不动物体,(必须是封闭的多义线-即用c进行闭合的多义线),否则会出错
      //Select Fixed Object(Circle or closed lwpolyline)
      public static ArrayList SelectForFixBody()
      {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            ArrayList res = new ArrayList();
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                PromptSelectionOptions acOptSel = new PromptSelectionOptions();
                acOptSel.MessageForAdding = "\n请选择不可移动物体(Please select the fixed object):";
                // Create a TypedValue array to define the filter criteria
                TypedValue[] acTypValAr = new TypedValue;
                acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "LWPOLYLINE,CIRCLE"), 0);
                // Assign the filter criteria to a SelectionFilter object
                SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);
                PromptSelectionResult acSSPrompt = ed.GetSelection(acOptSel, acSelFtr);
                if (acSSPrompt.Status == PromptStatus.OK)
                {
                  SelectionSet acSSet = acSSPrompt.Value;
                  foreach (SelectedObject acSSObj in acSSet)
                  {
                        if (acSSObj != null)
                        {
                            Entity acEnt = trans.GetObject(acSSObj.ObjectId, OpenMode.ForWrite) as Entity;
                            if (acEnt != null)
                            {
                              ObjectId ObjID = acEnt.ObjectId;
                              BodyDef groundBodyDef = new BodyDef();
                              Entity ent0 = (Entity)trans.GetObject(ObjID, OpenMode.ForRead, false);
                              if (ent0.GetType().Name == "Circle")
                              {
                                    Circle ent = (Circle)trans.GetObject(ObjID, OpenMode.ForRead, false);
                                    Point3d cen = ent.Center;
                                    //----定义不可移动物体,默认属性
                                    groundBodyDef.Position.Set((float)cen.X, (float)cen.Y);
                                    Body groundBody = world.CreateBody(groundBodyDef);
                                    CircleDef groundShapeDef = new CircleDef();
                                    groundShapeDef.Radius = (float)ent.Radius;
                                    groundBody.CreateFixture(groundShapeDef);
                                    res.Add(groundBody);
                              }
                              else if (ent0.GetType().Name == "Polyline")
                              {
                                    Polyline ent = (Polyline)trans.GetObject(ObjID, OpenMode.ForRead, false);
                                    Point2d v0 = ent.GetPoint2dAt(0);
                                    Vec2[] plvex = GetPolylineForBox2d(ent);
                                    //----定义不可移动物体,默认属性
                                    groundBodyDef.Position.Set((float)v0.X, (float)v0.Y);
                                    Body groundBody = world.CreateBody(groundBodyDef);
                                    PolygonDef groundShapeDef = new PolygonDef();
                                    groundShapeDef.VertexCount = plvex.Length;
                                    groundShapeDef.Vertices = plvex;
                                    groundBody.CreateFixture(groundShapeDef);
                                    res.Add(groundBody);
                              }
                            }
                        }
                  }
                  trans.Commit();
                }
                return res;
            }
      }
      //选择可发射物体,必须是矩形或圆形的块,否则会出错
      //Select Ejecting Object, Should be rectangle of circle Block
      public static ArrayList SelectForEjectingBody()
      {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            ArrayList res = new ArrayList();
            eBodyId = new ObjectIdCollection();
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                // Create a TypedValue array to define the filter criteria
                TypedValue[] acTypValAr = new TypedValue;
                acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "INSERT"), 0);
                SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);
                PromptSelectionOptions acOptSel = new PromptSelectionOptions();
                acOptSel.MessageForAdding = "\n----\n请选择可发射物体(Please select ejecting object):";
                PromptSelectionResult acSSPrompt = ed.GetSelection(acOptSel, acSelFtr);
                PromptStringOptions pStrOpts = new PromptStringOptions("\n输入X水平力(Input the X force):(100000)N:");
                pStrOpts.AllowSpaces = true;
                PromptResult pStrRes = ed.GetString(pStrOpts);
                float forcex = System.Convert.ToSingle(pStrRes.StringResult);
                pStrOpts = new PromptStringOptions("\n输入Y水平力(Input the Y force):(100000)N:");
                pStrOpts.AllowSpaces = true;
                pStrRes = ed.GetString(pStrOpts);
                float forcey = System.Convert.ToSingle(pStrRes.StringResult);
                Vec2 force = new Vec2(forcex, forcey);
                if (acSSPrompt.Status == PromptStatus.OK)
                {
                  SelectionSet acSSet = acSSPrompt.Value;
                  ebody = new Body;
                  int i = 0;
                  foreach (SelectedObject acSSObj in acSSet)
                  {
                        if (acSSObj != null)
                        {
                            Entity acEnt = trans.GetObject(acSSObj.ObjectId, OpenMode.ForWrite) as Entity;
                            if (acEnt != null)
                            {
                              var blkRef = trans.GetObject(acSSObj.ObjectId, OpenMode.ForWrite, false) as BlockReference;
                              float dx = (float)blkRef.ScaleFactors.X;
                              float dy = (float)blkRef.ScaleFactors.Y;
                              float rotation = (float)blkRef.Rotation;
                              Point3d cen = blkRef.Position;
                              //只考虑方形和圆形两种物体
                              //-----定义可移动物体,随便定义的属性
                              BodyDef BodyDef = new BodyDef();
                              BodyDef.Position.Set((float)cen.X, (float)cen.Y);
                              Body eBodyi = world.CreateBody(BodyDef);
                              Point3d respt = GetCircleInBlockReference(acSSObj.ObjectId);
                              if (respt != Point3d.Origin)
                              {
                                    CircleDef eShapeDef = new CircleDef();
                                    eShapeDef.Radius = dx / 2.0f;
                                    eShapeDef.Density = 1.0f;// Set the box density to be non-zero, so it will be dynamic.
                                    eShapeDef.Restitution = 0.35f;//这个是反弹的控制(0-1)
                                    eShapeDef.Friction = 0.25f;// Override the default friction.
                                    eBodyi.CreateFixture(eShapeDef);// Add the shape to the body.
                                    eBodyi.SetMassFromShapes();
                                    eBodyi.SetAngle(rotation);
                                    Vec2 forceP = new Vec2((float)cen.X, (float)cen.Y);
                                    eBodyi.ApplyForce(force, eBodyi.GetWorldCenter());
                                    ebody = eBodyi;
                                    eBodyId.Add(acSSObj.ObjectId);
                              }
                              else
                              {
                                    PolygonDef eShapeDef = new PolygonDef();
                                    eShapeDef.SetAsBox(dx / 2.0f, dy / 2.0f);
                                    eShapeDef.VertexCount = 4;
                                    eShapeDef.Density = 1.0f;// Set the box density to be non-zero, so it will be dynamic.
                                    eShapeDef.Restitution = 0.35f;//这个是反弹的控制(0-1),不知道只设置一个有没有效果
                                    eShapeDef.Friction = 0.25f;// Override the default friction.
                                    eBodyi.CreateFixture(eShapeDef);// Add the shape to the body.
                                    eBodyi.SetMassFromShapes();
                                    eBodyi.SetAngle(rotation);
                                    eBodyi.ApplyForce(force, eBodyi.GetWorldCenter());
                                    ebody = eBodyi;
                                    eBodyId.Add(acSSObj.ObjectId);
                              }
                            }
                        }
                        i++;
                  }
                }
                trans.Commit();
            }
            return res;
      }
      //选择可掉下物体,必须是矩形或圆形的块,否则会出错
      //Select Moving Object, Should be rectangle of circle Block
      public static ArrayList SelectForMovingBody()
      {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            ArrayList res = new ArrayList();
            mBodyId = new ObjectIdCollection();
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                // Create a TypedValue array to define the filter criteria
                TypedValue[] acTypValAr = new TypedValue;
                acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "INSERT"), 0);
                SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);
                PromptSelectionOptions acOptSel = new PromptSelectionOptions();
                acOptSel.MessageForAdding = "\n----\n请选择可移动物体(Please select Moving object):";
                PromptSelectionResult acSSPrompt = ed.GetSelection(acOptSel, acSelFtr);
                if (acSSPrompt.Status == PromptStatus.OK)
                {
                  SelectionSet acSSet = acSSPrompt.Value;
                  mbody = new Body;
                  int i = 0;
                  foreach (SelectedObject acSSObj in acSSet)
                  {
                        if (acSSObj != null)
                        {
                            Entity acEnt = trans.GetObject(acSSObj.ObjectId, OpenMode.ForWrite) as Entity;
                            if (acEnt != null)
                            {
                              var blkRef = trans.GetObject(acSSObj.ObjectId, OpenMode.ForWrite, false) as BlockReference;
                              float dx = (float)blkRef.ScaleFactors.X;
                              float dy = (float)blkRef.ScaleFactors.Y;
                              float rotation = (float)blkRef.Rotation;
                              Point3d cen = blkRef.Position;
                              //只考虑方形和圆形两种物体
                              //-----定义可移动物体,随便定义的属性
                              BodyDef BodyDef = new BodyDef();
                              BodyDef.Position.Set((float)cen.X, (float)cen.Y);
                              Body mBodyi = world.CreateBody(BodyDef);
                              Point3d respt = GetCircleInBlockReference(acSSObj.ObjectId);
                              if (respt != Point3d.Origin)
                              {
                                    CircleDef mShapeDef = new CircleDef();
                                    mShapeDef.Radius = dx / 2.0f;
                                    mShapeDef.Density = 1.0f;// Set the box density to be non-zero, so it will be dynamic.
                                    mShapeDef.Restitution = 0.35f;//这个是反弹的控制(0-1),不知道只设置一个有没有效果
                                    mShapeDef.Friction = 0.25f;// Override the default friction.
                                    mBodyi.CreateFixture(mShapeDef);// Add the shape to the body.
                                    mBodyi.SetMassFromShapes();
                                    mBodyi.SetAngle(rotation);
                                    mbody = mBodyi;
                                    mBodyId.Add(acSSObj.ObjectId);
                              }
                              else
                              {
                                    PolygonDef mShapeDef = new PolygonDef();
                                    //Vec2[] plvex = ChangeBox2Poly(dx, dy, rotation);
                                    mShapeDef.SetAsBox(dx / 2.0f, dy / 2.0f);
                                    mShapeDef.VertexCount = 4;
                                    //mShapeDef.Vertices = plvex;
                                    mShapeDef.Density = 1.0f;// Set the box density to be non-zero, so it will be dynamic.
                                    mShapeDef.Restitution = 0.35f;//这个是反弹的控制(0-1),不知道只设置一个有没有效果
                                    mShapeDef.Friction = 0.25f;// Override the default friction.
                                    mBodyi.CreateFixture(mShapeDef);// Add the shape to the body.
                                    mBodyi.SetMassFromShapes();
                                    mBodyi.SetAngle(rotation);
                                    mbody = mBodyi;
                                    mBodyId.Add(acSSObj.ObjectId);
                              }
                            }
                        }
                        i++;
                  }
                }
                trans.Commit();
            }
            return res;
      }
      //改变块的插入点和方向
      //Change the insertion point and direction of block reference
      public static void ChangeBlockRefInsPointAndAngle(ObjectId BlockRefId, Point3d newposition, float angle)
      {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                Entity ent = (Entity)tr.GetObject(BlockRefId, OpenMode.ForWrite);
                var blkRef = tr.GetObject(BlockRefId, OpenMode.ForWrite, false) as BlockReference;
                blkRef.Position = newposition;
                blkRef.Rotation = angle;
                tr.Commit();
                Autodesk.AutoCAD.ApplicationServices.Application.UpdateScreen();
            }
      }
      // Get some circle information in a block reference
      public static Point3d GetCircleInBlockReference(ObjectId oId)
      {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Point3d pt = new Point3d();
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockReference bref = tr.GetObject(oId, OpenMode.ForRead) as BlockReference;
                BlockTableRecord btr = tr.GetObject(bref.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
                foreach (ObjectId id in btr)
                {
                  Circle cir = tr.GetObject(id, OpenMode.ForRead) as Circle;
                  if (cir != null)
                  {
                        pt = cir.Center.TransformBy(bref.BlockTransform);
                  }
                }
            }
            return pt;
      }
      //Get lwPolyline information for drawing fixed object
      public static Vec2[] GetPolylineForBox2d(Polyline pl)
      {
            Point2d P0 = pl.GetPoint2dAt(0);
            Vec2[] res = new Vec2;
            Point3d P03d = new Point3d(pl.GetPoint2dAt(0).X, pl.GetPoint2dAt(0).Y, 0);
            Point3d P13d = new Point3d(pl.GetPoint2dAt(1).X, pl.GetPoint2dAt(1).Y, 0);
            Point3d P23d = new Point3d(pl.GetPoint2dAt(2).X, pl.GetPoint2dAt(2).Y, 0);
            Vector3d normal = new Vector3d(0, 0, 1);
            int judge = Clockwise(P03d, P13d, P23d, normal);
            Point2d Pti = new Point2d();
            for (int i = 0; i < pl.NumberOfVertices; i++)
            {
                if (judge == -1)
                { Pti = pl.GetPoint2dAt(i); }
                else
                { Pti = pl.GetPoint2dAt(pl.NumberOfVertices - 1 - i); }
                Vec2 resi = new Vec2((float)(Pti.X - P0.X), (float)(Pti.Y - P0.Y));
                res = resi;
            }
            return res;
      }
      //gile's function http://www.theswamp.org/index.php?topic=30428.msg360469#msg360469
      // Clockwise method, returns 1 if p1, p2, p3 are clockwise,
      // 0 if they're aligned, -1 if they're counterclockwise
      public static int Clockwise(Point3d p1, Point3d p2, Point3d p3, Vector3d normal)
      {
            const double pi = 3.141592653589793;
            Vector3d v1 = p1.GetVectorTo(p2);
            Vector3d v2 = p1.GetVectorTo(p3);
            double angle = v1.GetAngleTo(v2, normal);
            if (angle == 0.0 || angle == pi)
                return 0;
            else
            {
                if (v1.GetAngleTo(v2, normal) < pi)
                  return -1;
                else
                  return 1;
            }
      }
    }
}

仲文玉 发表于 2013-1-28 20:45:04

支持qjchen

landsat99 发表于 2022-5-20 10:51:35

相当棒的试验受启发

hhh454 发表于 2022-9-3 21:22:23

感谢分享,佩服,太有才了

chpmould 发表于 2013-1-28 22:59:29

本帖最后由 chpmould 于 2013-1-28 23:00 编辑

支持一下,写的很好

1716768452 发表于 2013-1-29 13:04:14

陈老师~!赞啊~

guohq 发表于 2013-1-29 13:46:40

~~佩服~~

fhxu 发表于 2013-1-29 15:04:11

这个厉害,不过玩起来应该很费劲!

Casa小宝 发表于 2013-1-30 17:23:47

~老师相当厉害!~

chleiwu 发表于 2013-2-1 08:45:32

神技术

mokson 发表于 2013-2-4 18:03:43

太有才了。

logoin 发表于 2013-2-8 23:34:05

强人。。。。。
页: [1] 2 3
查看完整版本: [飞马系列] AUTOCAD中的"愤怒的小鸟"游戏,需BOX2DX引擎支持