private void AddPressureBody(ClosedShape shape) { DraggablePressureBody body = new DraggablePressureBody(); body.Setup(JellyWorldManager.World, shape, 1.0f, 40.0f, 10.0f, 1.0f, 300.0f, 20.0f, transform.position, transform.rotation.eulerAngles.z, Vector2.one); body.Gravity = GravityModifier; body.addInternalSpring(0, 2, 400f, 12f); body.addInternalSpring(1, 3, 400f, 12f); _body = body; }
private void CreateFloor() { // static ground object. GameObject ground = new GameObject (); ClosedShape groundShape = new ClosedShape (); groundShape.begin(); groundShape.addVertex(new Vector2(-10f, -2f)); groundShape.addVertex(new Vector2(-10f, 2f)); groundShape.addVertex(new Vector2(10f, 2f)); groundShape.addVertex(new Vector2(10f, -2f)); groundShape.finish(); // make the body. Body groundBody = new Body(); groundBody.Setup(_world, groundShape, float.PositiveInfinity, new Vector2(0f, -5f), 0f, Vector2.one, false); }
private void AddSpringBody(ClosedShape shape) { DraggableSpringBody body = new DraggableSpringBody(); body.Setup(JellyWorldManager.World, shape, 1f, SpringK, Damping, SpringK, Damping, transform.position, transform.rotation.eulerAngles.z, Vector2.one); body.Gravity = GravityModifier; int i = springs.Count-1, l = -1; for(;i>l;i-=detail) { Vector2 v = springs[i]; try{ //TODO: check if triangle is already added, if so, flip triangle. body.addInternalSpring((int)v.x, (int)v.y,SpringK,Damping); } catch{ // Debug.Log("couldnt add spring at " +i); } } _body = body; }
/// <summary> /// Collision Stay. For override. /// </summary> /// <param name="otherBody">Other body.</param> public virtual void CollisionStay(Body otherBody) { if (OnCollisionStay != null) { OnCollisionStay(otherBody); } }
/// <summary> /// Collision Exit. For override. /// </summary> /// <param name="otherBody">Other body.</param> public virtual void CollisionExit(Body otherBody) { if (OnCollisionExit != null) { OnCollisionExit(otherBody); } }
/// <summary> /// Collision Entered. For override. /// </summary> /// <param name="otherBody">Other body.</param> public virtual void CollisionEnter(Body otherBody) { if (OnCollisionEnter != null) { OnCollisionEnter(otherBody); } }
public void Clear() { bodyA = bodyB = null; bodyApm = bodyBpmA = bodyBpmB = -1; hitPt = Vector2.Zero; edgeD = 0f; normal = Vector2.Zero; penetration = 0f; }
private void updateBodyBitmask(Body body) { AABB box = body.getAABB(); int minX = (int)Math.Floor((box.Min.X - mWorldLimits.Min.X) / mWorldGridStep.X); int maxX = (int)Math.Floor((box.Max.X - mWorldLimits.Min.X) / mWorldGridStep.X); if (minX < 0) { minX = 0; } else if (minX > 32) { minX = 32; } if (maxX < 0) { maxX = 0; } else if (maxX > 32) { maxX = 32; } int minY = (int)Math.Floor((box.Min.Y - mWorldLimits.Min.Y) / mWorldGridStep.Y); int maxY = (int)Math.Floor((box.Max.Y - mWorldLimits.Min.Y) / mWorldGridStep.Y); if (minY < 0) { minY = 0; } else if (minY > 32) { minY = 32; } if (maxY < 0) { maxY = 0; } else if (maxY > 32) { maxY = 32; } body.mBitMaskX.clear(); for (int i = minX; i <= maxX; i++) body.mBitMaskX.setOn(i); body.mBitMaskY.clear(); for (int i = minY; i <= maxY; i++) body.mBitMaskY.setOn(i); //Console.WriteLine("Body bitmask: minX{0} maxX{1} minY{2} maxY{3}", minX, maxX, minY, minY, maxY); }
private bool defaultCollisionFilter(Body A, int Apm, Body B, int Bpm1, int Bpm2, Vector2 hitPt, float normSpeed) { return true; }
private void bodyCollide(Body bA, Body bB, List<BodyCollisionInfo> infoList) { int bApmCount = bA.PointMassCount; int bBpmCount = bB.PointMassCount; AABB boxB = bB.getAABB(); // check all PointMasses on bodyA for collision against bodyB. if there is a collision, return detailed info. BodyCollisionInfo infoAway = new BodyCollisionInfo(); BodyCollisionInfo infoSame = new BodyCollisionInfo(); for (int i = 0; i < bApmCount; i++) { Vector2 pt = bA.getPointMass(i).Position; // early out - if this point is outside the bounding box for bodyB, skip it! if (!boxB.contains(ref pt)) continue; // early out - if this point is not inside bodyB, skip it! if (!bB.contains(ref pt)) continue; int prevPt = (i>0) ? i-1 : bApmCount-1; int nextPt = (i < bApmCount - 1) ? i + 1 : 0; Vector2 prev = bA.getPointMass(prevPt).Position; Vector2 next = bA.getPointMass(nextPt).Position; // now get the normal for this point. (NOT A UNIT VECTOR) Vector2 fromPrev = new Vector2(); fromPrev.X = pt.X - prev.X; fromPrev.Y = pt.Y - prev.Y; Vector2 toNext = new Vector2(); toNext.X = next.X - pt.X; toNext.Y = next.Y - pt.Y; Vector2 ptNorm = new Vector2(); ptNorm.X = fromPrev.X + toNext.X; ptNorm.Y = fromPrev.Y + toNext.Y; VectorTools.makePerpendicular(ref ptNorm); // this point is inside the other body. now check if the edges on either side intersect with and edges on bodyB. float closestAway = 100000.0f; float closestSame = 100000.0f; infoAway.Clear(); infoAway.bodyA = bA; infoAway.bodyApm = i; infoAway.bodyB = bB; infoSame.Clear(); infoSame.bodyA = bA; infoSame.bodyApm = i; infoSame.bodyB = bB; bool found = false; int b1 = 0; int b2 = 1; for (int j = 0; j < bBpmCount; j++) { Vector2 hitPt; Vector2 norm; float edgeD; b1 = j; if (j < bBpmCount - 1) b2 = j + 1; else b2 = 0; Vector2 pt1 = bB.getPointMass(b1).Position; Vector2 pt2 = bB.getPointMass(b2).Position; // quick test of distance to each point on the edge, if both are greater than current mins, we can skip! float distToA = ((pt1.X - pt.X) * (pt1.X - pt.X)) + ((pt1.Y - pt.Y) * (pt1.Y - pt.Y)); float distToB = ((pt2.X - pt.X) * (pt2.X - pt.X)) + ((pt2.Y - pt.Y) * (pt2.Y - pt.Y)); if ((distToA > closestAway) && (distToA > closestSame) && (distToB > closestAway) && (distToB > closestSame)) continue; // test against this edge. float dist = bB.getClosestPointOnEdgeSquared(pt, j, out hitPt, out norm, out edgeD); // only perform the check if the normal for this edge is facing AWAY from the point normal. float dot; //Vector2.Dot(ref ptNorm, ref edgeNorm, out dot); Vector2.Dot(ref ptNorm, ref norm, out dot); if (dot <= 0f) { if (dist < closestAway) { closestAway = dist; infoAway.bodyBpmA = b1; infoAway.bodyBpmB = b2; infoAway.edgeD = edgeD; infoAway.hitPt = hitPt; infoAway.normal = norm; infoAway.penetration = dist; found = true; } } else { if (dist < closestSame) { closestSame = dist; infoSame.bodyBpmA = b1; infoSame.bodyBpmB = b2; infoSame.edgeD = edgeD; infoSame.hitPt = hitPt; infoSame.normal = norm; infoSame.penetration = dist; } } } // we've checked all edges on BodyB. add the collision info to the stack. if ((found) && (closestAway > mPenetrationThreshold) && (closestSame < closestAway)) { infoSame.penetration = (float)Math.Sqrt(infoSame.penetration); infoList.Add(infoSame); } else { infoAway.penetration = (float)Math.Sqrt(infoAway.penetration); infoList.Add(infoAway); } } }
/// <summary> /// Remove a body from the world. call this outside of an update to remove the body. /// </summary> /// <param name="b">the body to remove</param> public void removeBody(Body b) { if (mBodies.Contains(b)) { mBodies.Remove(b); } }
/// <summary> /// Add a Body to the world. Bodies do this automatically, you should NOT need to call this. /// </summary> /// <param name="b">the body to add to the world</param> public void addBody(Body b) { if (!mBodies.Contains(b)) { mBodies.Add(b); } }
/// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input and playing audio. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> void Update() { // UPDATE the physics! for (int i = 0; i < 3; i++) mWorld.update(1 / 120f); // cursor movement. cursorPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); // if the user presses the A button, create a new body at the cursor position. if (Input.GetKeyDown(KeyCode.A)) { GameObject s = new GameObject (); JelloPhysics.ClosedShape shape = new JelloPhysics.ClosedShape (); shape.begin(); shape.addVertex(new Vector2(-1.0f, 0f)); shape.addVertex(new Vector2(0f, 1.0f)); shape.addVertex(new Vector2(1.0f, 0f)); shape.addVertex(new Vector2(0f, -1.0f)); shape.finish(); DraggableSpringBody body = new DraggableSpringBody(); body.Setup(mWorld, shape, 1f, 150, 5,300,15, new Vector2(cursorPos.x, cursorPos.y),((float)UnityEngine.Random.Range(0,360)), Vector2.one); body.addInternalSpring(0, 2, 400f, 12f); body.addInternalSpring(1, 3, 400f, 12f); } if (Input.GetKeyDown(KeyCode.B)) { GameObject s = new GameObject (); JelloPhysics.ClosedShape shape = new JelloPhysics.ClosedShape (); shape.begin(); for (int i = 0; i < 360; i += 20) { shape.addVertex(new Vector2((float)Mathf.Cos(((float)-i) * Mathf.Deg2Rad), (float)Mathf.Sin(((float)-i) * Mathf.Deg2Rad))); } shape.finish(); DraggablePressureBody pb = new DraggablePressureBody(); pb.Setup(mWorld, shape, 1.0f, 40.0f, 10.0f, 1.0f, 300.0f, 20.0f, cursorPos, 0, Vector2.one); pb.addTriangle(0, 10, 9); pb.addTriangle(0, 9, 1); pb.addTriangle(1, 9, 8); pb.addTriangle(1, 8, 2); pb.addTriangle(2, 8, 7); pb.addTriangle(2, 7, 3); pb.addTriangle(3, 7, 6); pb.addTriangle(3, 6, 4); pb.addTriangle(4, 6, 5); pb.addTriangle(17, 10, 0); pb.addTriangle(17, 11, 10); pb.addTriangle(16, 11, 17); pb.addTriangle(16, 12, 11); pb.addTriangle(15, 12, 16); pb.addTriangle(15, 13, 12); pb.addTriangle(14, 12, 15); pb.addTriangle(14, 13, 12); pb.finalizeTriangles(Color.white); pb.addInternalSpring(0, 2, 400f, 12f); pb.addInternalSpring(1, 3, 400f, 12f); } // dragging! if (Input.GetMouseButton(0)) { if (dragBody != null) { PointMass pm = dragBody.getPointMass(dragPoint); if (dragBody.GetType().Name == "DraggableSpringBody") ((DraggableSpringBody)dragBody).setDragForce(JelloPhysics.VectorTools.calculateSpringForce(pm.Position, pm.Velocity, cursorPos, Vector2.zero, 0.0f, 100.0f, 10.0f), dragPoint); else if (dragBody.GetType().Name == "DraggablePressureBody") ((DraggablePressureBody)dragBody).setDragForce(JelloPhysics.VectorTools.calculateSpringForce(pm.Position, pm.Velocity, cursorPos, Vector2.zero, 0.0f, 100.0f, 10.0f), dragPoint); } } else { dragBody = null; dragPoint = -1; } if (Input.GetMouseButtonDown(0)) { if (dragBody == null) { int body; mWorld.getClosestPointMass(cursorPos, out body, out dragPoint); dragBody = mWorld.getBody(body); } } }
void Update() { // UPDATE the physics! _cursorPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); // dragging! if (Input.GetMouseButton(0)) { if (_dragBody != null) { PointMass pm = _dragBody.getPointMass(_dragPoint); if (_dragBody.GetType().Name == "DraggableSpringBody") ((DraggableSpringBody)_dragBody).setDragForce(JelloPhysics.VectorTools.calculateSpringForce(pm.Position, pm.Velocity, _cursorPos, Vector2.zero, 0.0f, 100.0f, 10.0f), _dragPoint); else if (_dragBody.GetType().Name == "DraggablePressureBody") ((DraggablePressureBody)_dragBody).setDragForce(JelloPhysics.VectorTools.calculateSpringForce(pm.Position, pm.Velocity, _cursorPos, Vector2.zero, 0.0f, 100.0f, 10.0f), _dragPoint); } } else { _dragBody = null; _dragPoint = -1; } if (Input.GetMouseButtonDown(0)) { if (_dragBody == (null)) { int body; _world.getClosestPointMass(_cursorPos, out body, out _dragPoint); _dragBody = _world.getBody(body); } } }
/// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input and playing audio. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Update(GameTime gameTime) { ////////////////////////////////////////////////////////////////////// GamePadState gs = GamePad.GetState(PlayerIndex.One); // perform several updates per frame, with a smal timestep. for (int i = 0; i < 5; i++) //if (gs.Buttons.Y == ButtonState.Pressed) { mWorld.update(1f / 200f); // dragging! if (gs.Buttons.A == ButtonState.Pressed) { if (dragBody != null) { PointMass pm = dragBody.getPointMass(dragPoint); if (dragBody.GetType().Name == "DraggableSpringBody") ((DraggableSpringBody)dragBody).setDragForce(JelloPhysics.VectorTools.calculateSpringForce(pm.Position, pm.Velocity, mCursorPos, Vector2.Zero, 0.0f, 100.0f, 10.0f), dragPoint); else if (dragBody.GetType().Name == "DraggablePressureBody") ((DraggablePressureBody)dragBody).setDragForce(JelloPhysics.VectorTools.calculateSpringForce(pm.Position, pm.Velocity, mCursorPos, Vector2.Zero, 0.0f, 100.0f, 10.0f), dragPoint); } } else { dragBody = null; dragPoint = -1; } } // QUITTING if (gs.Buttons.Back == ButtonState.Pressed) this.Exit(); // CURSOR MOVEMENT - 360 PAD mCursorPos.X += (gs.ThumbSticks.Left.X * gs.ThumbSticks.Left.X) * 0.5f * ((gs.ThumbSticks.Left.X > 0) ? 1 : -1); mCursorPos.Y += (gs.ThumbSticks.Left.Y * gs.ThumbSticks.Left.Y) * 0.5f * ((gs.ThumbSticks.Left.Y > 0) ? 1 : -1); // CAMERA UPDATE Vector2 stray = (mCursorPos - new Vector2(cameraPos.X, cameraPos.Y)); float maxStrayX = cameraPos.Z * 0.35f; float maxStrayY = (screenHeight * maxStrayX) / screenWidth; if (Math.Abs(stray.X) > maxStrayX) cameraPos.X += (stray.X > 0) ? -(maxStrayX - stray.X) : (maxStrayX + stray.X); if (Math.Abs(stray.Y) > maxStrayY) cameraPos.Y += (stray.Y > 0) ? -(maxStrayY - stray.Y) : (maxStrayY + stray.Y); cameraPos.Z += (gs.Triggers.Left - gs.Triggers.Right); if (cameraPos.Z < 5.0f) { cameraPos.Z = 5.0f; } // DRAGGING! if ((gs.Buttons.A == ButtonState.Pressed) && (oldPadState.Buttons.A == ButtonState.Released)) { if (dragBody == null) { int body; mWorld.getClosestPointMass(mCursorPos, out body, out dragPoint); dragBody = mWorld.getBody(body); } } // PRESSURE CHANGE! mPressureBodies[0].GasPressure += (gs.ThumbSticks.Right.Y) * 3.0f; // DEBUG VISUALIZATION if ((gs.Buttons.LeftShoulder == ButtonState.Pressed) && (oldPadState.Buttons.LeftShoulder == ButtonState.Released)) showDebug = !showDebug; if ((gs.Buttons.RightShoulder == ButtonState.Pressed) && (oldPadState.Buttons.RightShoulder == ButtonState.Released)) showVelocities = !showVelocities; oldPadState = gs; base.Update(gameTime); }
/// <summary> /// Allows the game to perform any initialization it needs to before starting to run. /// This is where it can query for any required services and load any non-graphic /// related content. Calling base.Initialize will enumerate through any components /// and initialize them as well. /// </summary> protected override void Initialize() { #if XBOX360 graphics.PreferredBackBufferWidth = this.Window.ClientBounds.Width; graphics.PreferredBackBufferHeight = this.Window.ClientBounds.Height; graphics.PreferMultiSampling = true; #else graphics.PreferredBackBufferWidth = 800; graphics.PreferredBackBufferHeight = 600; #endif graphics.ApplyChanges(); screenWidth = graphics.GraphicsDevice.Viewport.Width; screenHeight = graphics.GraphicsDevice.Viewport.Height; // setup camera cameraPos = new Vector3(-2f, -15f, 25f); cameraLookVector = Vector3.Forward; // basic effect used for drawing all objects, based on vertexcolor. lineEffect = new BasicEffect(graphics.GraphicsDevice, null); lineEffect.LightingEnabled = false; lineEffect.VertexColorEnabled = true; lineEffect.Alpha = 1.0f; // initialize physics world mWorld = new World(); // lists to keep track of bodies. mSpringBodies = new List<DraggableSpringBody>(); mPressureBodies = new List<DraggablePressureBody>(); mStaticBodies = new List<Body>(); // STATIC COLLISION SHAPE // all Closed Shape objects are assumed to be a list of lines going from point to point, with the last point // connecting back to the first point to close the shape. This is a simple rectangle to represent the ground for this demo. // ClosedShape objects are automatically "centered" when you call finish(), and that center becomes the center when // setting the position of the object. ClosedShape stat = new ClosedShape(); stat.begin(); stat.addVertex(new Vector2(-20f,-1f)); stat.addVertex(new Vector2(-20f, 1f)); stat.addVertex(new Vector2(20f, 1f)); stat.addVertex(new Vector2(20f, -1f)); stat.finish(); // creating the body. Since this is a static body, we can use the base class Body. // setting the mass per point to PositiveInfinity makes it immobile / static. Body b = new Body(mWorld, stat, float.PositiveInfinity, new Vector2(0f, -19f), 0, Vector2.One, false); mStaticBodies.Add(b); // this is a more complex body, in the shape of a capital "I", and connected with many internal springs. ClosedShape shape = new ClosedShape(); shape.begin(); shape.addVertex(new Vector2(-1.5f, 2.0f)); shape.addVertex(new Vector2(-0.5f, 2.0f)); shape.addVertex(new Vector2(0.5f, 2.0f)); shape.addVertex(new Vector2(1.5f, 2.0f)); shape.addVertex(new Vector2(1.5f, 1.0f)); shape.addVertex(new Vector2(0.5f, 1.0f)); shape.addVertex(new Vector2(0.5f, -1.0f)); shape.addVertex(new Vector2(1.5f, -1.0f)); shape.addVertex(new Vector2(1.5f, -2.0f)); shape.addVertex(new Vector2(0.5f, -2.0f)); shape.addVertex(new Vector2(-0.5f, -2.0f)); shape.addVertex(new Vector2(-1.5f, -2.0f)); shape.addVertex(new Vector2(-1.5f, -1.0f)); shape.addVertex(new Vector2(-0.5f, -1.0f)); shape.addVertex(new Vector2(-0.5f, 1.0f)); shape.addVertex(new Vector2(-1.5f, 1.0f)); shape.finish(); // draggablespringbody is an inherited version of SpringBody that includes polygons for visualization, and the // ability to drag the body around the screen with the cursor. for (int x = -8; x <= 8; x += 4) { DraggableSpringBody body = new DraggableSpringBody(mWorld, shape, 1f, 150.0f, 5.0f, 300.0f, 15.0f, new Vector2(x, 0), 0.0f, Vector2.One); body.addInternalSpring(0, 14, 300.0f, 10.0f); body.addInternalSpring(1, 14, 300.0f, 10.0f); body.addInternalSpring(1, 15, 300.0f, 10.0f); body.addInternalSpring(1, 5, 300.0f, 10.0f); body.addInternalSpring(2, 14, 300.0f, 10.0f); body.addInternalSpring(2, 5, 300.0f, 10.0f); body.addInternalSpring(1, 5, 300.0f, 10.0f); body.addInternalSpring(14, 5, 300.0f, 10.0f); body.addInternalSpring(2, 4, 300.0f, 10.0f); body.addInternalSpring(3, 5, 300.0f, 10.0f); body.addInternalSpring(14, 6, 300.0f, 10.0f); body.addInternalSpring(5, 13, 300.0f, 10.0f); body.addInternalSpring(13, 6, 300.0f, 10.0f); body.addInternalSpring(12, 10, 300.0f, 10.0f); body.addInternalSpring(13, 11, 300.0f, 10.0f); body.addInternalSpring(13, 10, 300.0f, 10.0f); body.addInternalSpring(13, 9, 300.0f, 10.0f); body.addInternalSpring(6, 10, 300.0f, 10.0f); body.addInternalSpring(6, 9, 300.0f, 10.0f); body.addInternalSpring(6, 8, 300.0f, 10.0f); body.addInternalSpring(7, 9, 300.0f, 10.0f); // polygons! body.addTriangle(0, 15, 1); body.addTriangle(1, 15, 14); body.addTriangle(1, 14, 5); body.addTriangle(1, 5, 2); body.addTriangle(2, 5, 4); body.addTriangle(2, 4, 3); body.addTriangle(14, 13, 6); body.addTriangle(14, 6, 5); body.addTriangle(12, 11, 10); body.addTriangle(12, 10, 13); body.addTriangle(13, 10, 9); body.addTriangle(13, 9, 6); body.addTriangle(6, 9, 8); body.addTriangle(6, 8, 7); body.finalizeTriangles(Color.SpringGreen, Color.Navy); mSpringBodies.Add(body); } // pressure body. similar to a SpringBody, but with internal pressurized gas to help maintain shape. JelloPhysics.ClosedShape ball = new ClosedShape(); ball.begin(); for (int i = 0; i < 360; i += 20) { ball.addVertex(new Vector2((float)Math.Cos(MathHelper.ToRadians((float)-i)), (float)Math.Sin(MathHelper.ToRadians((float)-i)))); } ball.finish(); // make many of these. for (int x = -10; x <= 10; x+=5) { DraggablePressureBody pb = new DraggablePressureBody(mWorld, ball, 1.0f, 40.0f, 10.0f, 1.0f, 300.0f, 20.0f, new Vector2(x, -12), 0, Vector2.One); pb.addTriangle(0, 10, 9); pb.addTriangle(0, 9, 1); pb.addTriangle(1, 9, 8); pb.addTriangle(1, 8, 2); pb.addTriangle(2, 8, 7); pb.addTriangle(2, 7, 3); pb.addTriangle(3, 7, 6); pb.addTriangle(3, 6, 4); pb.addTriangle(4, 6, 5); pb.addTriangle(17, 10, 0); pb.addTriangle(17, 11, 10); pb.addTriangle(16, 11, 17); pb.addTriangle(16, 12, 11); pb.addTriangle(15, 12, 16); pb.addTriangle(15, 13, 12); pb.addTriangle(14, 12, 15); pb.addTriangle(14, 13, 12); pb.finalizeTriangles((x==-10) ? Color.Teal : Color.Maroon); mPressureBodies.Add(pb); } // default cursor position, etc. mCursorPos = new Vector2(cameraPos.X, cameraPos.Y); base.Initialize(); }