/// <summary> /// Add an internal JelloPointMass to this JelloBody. /// This will also add an internal vertex to JelloBody.Shape.InternalVertices. /// </summary> /// <param name="pointMass">The new JelloPointMass to add.</param> /// <param name="recenterBaseShape">Whether to recenter JelloBody.Shape.</param> public void addInternalPointMass(JelloPointMass pointMass, bool recenterBaseShape) { if(mBaseShape == null) return; if(mBaseShape.addInternalVertex(pointMass.LocalPosition)) { JelloPointMass[] oldMasses = mInternalPointMasses; mInternalPointMasses = new JelloPointMass[oldMasses.Length + 1]; for(int i = 0; i < oldMasses.Length; i++) mInternalPointMasses[i] = oldMasses[i]; mInternalPointMasses[mInternalPointMasses.Length - 1] = pointMass; mBaseShape.finish(recenterBaseShape); } }
/// <summary> /// Grabs the point mass. /// </summary> public void GrabPointMass() { //i am now pulling the body. pulling = true; //clear old values from last pull adjacentPointMasses.Clear(); oldMultipliers.Clear(); //set the position to lock it in place while pulling the body position = body.Position; //get the closest point mass to the mouse positions and cache some information about it. mousePosInWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition); pmIndex = body.getClosestPointMass(mousePosInWorld, false); //only grab edge point masses pointmass = body.getEdgePointMass(pmIndex); oldMultipliers.Add (pointmass.shapeMatchingMultiplier); pointmass.shapeMatchingMultiplier = 0f; adjacentPointMasses.Add (pointmass); //Set the body to kinematic to keep it in place while pulling. body.IsKinematic = true; int adjIndex = 0; int numFromStart = 0; //grab adjacent point masses further in the array. for(int i = pmIndex; i < pmIndex + numAdjacentPoints; i++) { adjIndex = i > body.EdgePointMassCount - 1 ? i - body.EdgePointMassCount : i; if(adjIndex > body.EdgePointMassCount - 1 || adjacentPointMasses.Contains(body.getEdgePointMass(adjIndex))) continue; //cache the point mass info and modify the shape matching values by how close it is to the grabbed point mass adjacentPointMasses.Add (body.getEdgePointMass(adjIndex)); oldMultipliers.Add(body.getEdgePointMass(adjIndex).shapeMatchingMultiplier); numFromStart++; body.getEdgePointMass(adjIndex).shapeMatchingMultiplier = numFromStart / (numAdjacentPoints + 1 + adjacentDropoff); } numFromStart = 0; //grab adjacent point masses before the current index in the array. for(int i = pmIndex; i > pmIndex - numAdjacentPoints; i--) { adjIndex = i < 0 ? i + body.EdgePointMassCount: i; if(adjIndex < 0 || adjacentPointMasses.Contains(body.getEdgePointMass(adjIndex))) continue; //cache the point mass info and modify the shape matching values by how close it is to the grabbed point mass adjacentPointMasses.Add (body.getEdgePointMass(adjIndex)); oldMultipliers.Add(body.getEdgePointMass(adjIndex).shapeMatchingMultiplier); numFromStart++; body.getEdgePointMass(adjIndex).shapeMatchingMultiplier = numFromStart / (numAdjacentPoints + 1 + adjacentDropoff); } //find any internal point masses connected to the selected point mass (via spring) and cache/modify its shape matching multiplier. for(int i = 0; i < body.SpringCount; i++) { if(body.getSpring(i).pointMassA == pmIndex || body.getSpring(i).pointMassB == pmIndex) { if(body.getSpring(i).pointMassA >= body.EdgePointMassCount) { adjacentPointMasses.Add (body.getPointMass(body.getSpring(i).pointMassA)); oldMultipliers.Add (body.getPointMass(body.getSpring(i).pointMassA).shapeMatchingMultiplier); body.getPointMass(body.getSpring(i).pointMassA).shapeMatchingMultiplier = 1 / (2 + adjacentDropoff); } else if(body.getSpring(i).pointMassB >= body.EdgePointMassCount) { adjacentPointMasses.Add (body.getPointMass(body.getSpring(i).pointMassB)); oldMultipliers.Add (body.getPointMass(body.getSpring(i).pointMassB).shapeMatchingMultiplier); body.getPointMass(body.getSpring(i).pointMassB).shapeMatchingMultiplier = 1 / (2 + adjacentDropoff); } } } }
/// <summary> /// Add an internal JelloPointMass to this JelloBody. /// This will also add an internal vertex to JelloBody.Shape.InternalVertices. /// Smartly means that most edits made to the JelloBody will be retained and is done via the JelloBody.smartSetShape() method. /// </summary> /// <param name="pointMass">The new JelloPointMass to add.</param> /// <param name="recenterBaseShape">Whether to recenter JelloBody.Shape.</param> /// <param name="options">ShapeSettingOptions on how Subcomponents should be modified as the JelloPointMass and JelloClosedShape vertex is added .</param> /// <param name="smartOptions">SmartShapeSettingOptions options on how Subcomponents should be modified as the JelloPointMass and JelloClosedShape vertex is added.</param> public void smartAddInternalPointMass(JelloPointMass pointMass, bool recenterBaseShape, ShapeSettingOptions options = ShapeSettingOptions.None, SmartShapeSettingOptions smartOptions = SmartShapeSettingOptions.None) { if(mBaseShape == null) return; JelloClosedShape newShape = new JelloClosedShape(mBaseShape.EdgeVertices, mBaseShape.InternalVertices, false); if(!newShape.addInternalVertex(pointMass.LocalPosition)) return; newShape.finish(recenterBaseShape); smartSetShape(newShape, options, smartOptions); mInternalPointMasses[mInternalPointMasses.Length - 1] = pointMass; }
/// <summary> /// Smartly sets JelloBody.Shape to a new JelloClosedShape object. /// Make sure your JelloShape does not contain any duplicate points ( Use JelloShapeTools.RemoveDuplicates() ). /// This method will attemp to retain as much information as possible from the previous shape. /// The subcomponents that will be processed in order to try and retained are JelloPointMass, JelloJoint, JelloAttachPoint, and JelloSpring. /// </summary> /// <param name="shape">New JelloClosedShape.</param> /// <param name="options">ShapeSettingOptions for setting the JelloClosedShape.</param> /// <param name="smartOptions">SmartShapeSettingOptions for setting the JelloClosedShape.</param> public virtual void smartSetShape(JelloClosedShape shape, ShapeSettingOptions options = ShapeSettingOptions.None, SmartShapeSettingOptions smartOptions = SmartShapeSettingOptions.None) { //no need to run smart method if no shape yet assigned. if(mBaseShape == null) { setShape(shape, options); return; } setComponentReferences(); //find common points between the old shape and the new one. List<int[]> indexPairs = new List<int[]>(); //start with the edges...??? for(int a = 0; a < shape.EdgeVertexCount; a++) { bool found = false; for(int i = 0; i < mBaseShape.EdgeVertexCount; i++) { if(mBaseShape.EdgeVertices[i] == shape.EdgeVertices[a]) { indexPairs.Add (new int[2]{a, i}); found = true; break; } } if(!found) indexPairs.Add (new int[2]{a, -1}); } for(int a = 0; a < shape.InternalVertexCount; a++) { bool found = false; for(int i = 0; i < mBaseShape.InternalVertexCount; i++) { if(mBaseShape.InternalVertices[i] == shape.InternalVertices[a]) { indexPairs.Add (new int[2]{a, i}); found = true; break; } } if(!found) indexPairs.Add (new int[2]{a, -1}); } bool movePointMasses = (options & ShapeSettingOptions.MovePointMasses) == ShapeSettingOptions.MovePointMasses; //reconfigure point masses JelloPointMass[] tempPointMasses = new JelloPointMass[shape.EdgeVertexCount]; for(int i = 0; i < shape.EdgeVertexCount; i++) { //this is a new point, create a point mass here if(indexPairs[i][1] == -1) { Vector2 pos; if(movePointMasses) pos = myTransform.TransformPoint(shape.EdgeVertices[indexPairs[i][0]]); else pos = Position; tempPointMasses[i] = new JelloPointMass(Mass, pos, this, false); } else//this point exists from the old shape, move that point mass into this index. { tempPointMasses[i] = mEdgePointMasses[indexPairs[i][1]]; } } mEdgePointMasses = tempPointMasses; tempPointMasses = new JelloPointMass[shape.InternalVertexCount]; for(int i = 0; i < shape.InternalVertexCount; i++) { //this is a new point, create a point mass here if(indexPairs[i + shape.EdgeVertexCount][1] == -1) { Vector2 pos; if(movePointMasses) pos = myTransform.TransformPoint(shape.InternalVertices[indexPairs[i + shape.EdgeVertexCount][0]]); else pos = Position; tempPointMasses[i] = new JelloPointMass(Mass, pos, this, false); } else//this point exists from the old shape, move that point mass into this index. { tempPointMasses[i] = mInternalPointMasses[indexPairs[i + shape.EdgeVertexCount][1]]; } } mInternalPointMasses = tempPointMasses; pivotOffset = JelloShapeTools.FindCenter(shape.EdgeVertices); //offset index pairs for internal points for(int i = 0; i < shape.InternalVertexCount; i++) { indexPairs[shape.EdgeVertexCount + i][0] += shape.EdgeVertexCount; indexPairs[shape.EdgeVertexCount + i][1] += mBaseShape.EdgeVertexCount; } // //TODO remove this? it seems a bit redundant... // for(int i = 0; i < mEdgePointMasses.Length; i++) // mEdgePointMasses[i].body = this; // for(int i = 0; i < mInternalPointMasses.Length; i++) // mInternalPointMasses[i].body = this; processSmartSetShape(indexPairs, shape, options, smartOptions); ClearInvalidSubComponents(); //this is joints and attach points.... mBaseShape = shape; }
/// <summary> /// Sets JelloBody.Shape to a new JelloClosedShape object. /// Make sure your JelloShape does not contain any duplicate points ( Use JelloShapeTools.RemoveDuplicates() ). /// This method will remove any existing JelloPointMass objects, and replace them with new ones if /// the new shape has a different JelloClosedShape.VertexCount than the previous one. In this case /// the JelloPointMass.Mass for each newly added JelloPointMass will be set to JelloBody.Mass. /// Otherwise the JelloBody.Shape is just updated, not affecting the existing JelloPointMass objects other than thier positions. /// Any JelloJoint, JelloSpring, or AttachPoint made invalid by the new shape will be removed. /// </summary> /// <param name="shape">New JelloClosedShape.</param> /// <param name="options">ShapeSettingOptions for setting the JelloClosedShape.</param> /// /// <dl class="example"><dt>Example</dt></dl> /// ~~~{.c} /// //Change the shape of the body to match the shape of the collider collided against /// JelloSpringBody springBody; /// /// void OnTriggerEnter2D(Collider2D coll) /// { /// if(coll.GetType() == typeof(PolygonCollider2D)) /// { /// PolygonCollider2D polyColl = (PolygonCollider2D)coll; /// /// JelloClosedShape shape = new JelloClosedShape(polyColl.points, true); /// /// springBody.setShape(shape, true); /// } /// } /// ~~~ public virtual void setShape(JelloClosedShape shape, ShapeSettingOptions options = ShapeSettingOptions.None) { //TODO make this all work better... mBaseShape = shape; bool movePointMasses = (options & ShapeSettingOptions.MovePointMasses) == ShapeSettingOptions.MovePointMasses; setComponentReferences(); if (mBaseShape.EdgeVertices.Length != mEdgePointMasses.Length) { Vector2 pos; // JelloPointMass[] oldPointMasses = mPointMasses; mEdgePointMasses = new JelloPointMass[mBaseShape.EdgeVertices.Length]; //how to make this work so that it doesnt move the point masses... what about mesh link options? for (int i = 0; i < mBaseShape.EdgeVertices.Length; i++) { // if(i < oldPointMasses.Length) // { // mPointMasses[i] = oldPointMasses[i]; //retain as many of the original point masses? // // if(movePointMasses) // mPointMasses[i].Position = myTransform.TransformPoint(mBaseShape.EdgeVertices[i]); // } // else // { if(movePointMasses) pos = myTransform.TransformPoint(mBaseShape.EdgeVertices[i]); else pos = Position; mEdgePointMasses[i] = new JelloPointMass(Mass, pos, this, false); // } } } else if(movePointMasses) { for(int i = 0; i < EdgePointMassCount; i++) mEdgePointMasses[i].Position = myTransform.TransformPoint(mBaseShape.EdgeVertices[i]); } if (mBaseShape.InternalVertices.Length != mInternalPointMasses.Length) { Vector2 pos; // JelloPointMass[] oldPointMasses = mInternalPointMasses; mInternalPointMasses = new JelloPointMass[mBaseShape.InternalVertices.Length]; //how to make this work so that it doesnt move the point masses... what about mesh link options? for (int i = 0; i < mBaseShape.InternalVertices.Length; i++) { // if(i < oldPointMasses.Length) // { // mInternalPointMasses[i] = oldPointMasses[i]; //retain as many of the original point masses? // // if(movePointMasses) // mInternalPointMasses[i].Position = myTransform.TransformPoint(mBaseShape.InternalVertices[i]); // } // else // { if(movePointMasses) pos = myTransform.TransformPoint(mBaseShape.InternalVertices[i]); else pos = Position; mInternalPointMasses[i] = new JelloPointMass(Mass, pos, this, false); // } } } else if(movePointMasses) { for(int i = 0; i < mInternalPointMasses.Length; i++) mInternalPointMasses[i].Position = myTransform.TransformPoint(mBaseShape.InternalVertices[i]); } pivotOffset = JelloShapeTools.FindCenter(mBaseShape.EdgeVertices); for(int i = 0; i < mEdgePointMasses.Length; i++) mEdgePointMasses[i].body = this; for(int i = 0; i < mInternalPointMasses.Length; i++) mInternalPointMasses[i].body = this; ClearInvalidSubComponents(); }
/// <summary> /// Set the internal JelloPointMass at the given index. /// </summary> /// <param name="pointMass">The new JelloPointMass.</param> /// <param name="index">The index that the new JelloPointMass will occupy. Operation will fail if index is out of range.</param> public void setInternalPointMass(JelloPointMass pointMass, int index) { if(index >= 0 && index < mInternalPointMasses.Length) mInternalPointMasses[index] = pointMass; }
/// <summary> /// Set the edge JelloPointMass at the given index. /// </summary> /// <param name="pointMass">The new JelloPointMass.</param> /// <param name="index">The index that the new JelloPointMass will occupy. Operation will fail if index is out of range.</param> public void setEdgePointMass(JelloPointMass pointMass, int index) { if(index >= 0 && index < mEdgePointMasses.Length) mEdgePointMasses[index] = pointMass; }
/// <summary> /// Flip the body about its local x-axis. /// </summary> public virtual void FlipY() { JelloPointMass[] tempMasses = new JelloPointMass[mEdgePointMasses.Length]; Vector2[] tempVertices = new Vector2[polyCollider.points.Length]; for(int i = 0; i < mEdgePointMasses.Length; i++) { mEdgePointMasses[i].Position = new Vector2(mEdgePointMasses[i].Position.x, mEdgePointMasses[i].Position.y + 2 * (Position.y - mEdgePointMasses[i].Position.y)); tempMasses[mEdgePointMasses.Length - i - 1] = mEdgePointMasses[i]; polyCollider.points[i] = new Vector2(polyCollider.points[i].x, polyCollider.points[i].y + 2 * (Position.y - polyCollider.points[i].y)); tempVertices[polyCollider.points.Length - i - 1] = polyCollider.points[i]; } mEdgePointMasses = tempMasses; polyCollider.points = tempVertices; pivotOffset = new Vector2(pivotOffset.x, -pivotOffset.y); tempMasses = new JelloPointMass[mInternalPointMasses.Length]; for(int i = 0; i < mInternalPointMasses.Length; i++) { mInternalPointMasses[i].Position = new Vector2(mInternalPointMasses[i].Position.x, mInternalPointMasses[i].Position.y + 2 * (Position.y - mInternalPointMasses[i].Position.y)); tempMasses[mInternalPointMasses.Length - i - 1] = mEdgePointMasses[i]; } mInternalPointMasses = tempMasses; mBaseShape.flipY(); }
/// <summary> /// Grabs the point mass. /// </summary> public void GrabPointMass() { //i am now pulling the body. pulling = true; //clear old values from last pull adjacentPointMasses.Clear(); oldMultipliers.Clear(); //set the position to lock it in place while pulling the body position = body.Position; //get the closest point mass to the mouse positions and cache some information about it. mousePosInWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition); pmIndex = body.getClosestPointMass(mousePosInWorld, false); //only grab edge point masses pointmass = body.getEdgePointMass(pmIndex); oldMultipliers.Add (pointmass.shapeMatchingMultiplier); pointmass.shapeMatchingMultiplier = 0f; adjacentPointMasses.Add (pointmass); //Set the body to kinematic to keep it in place while pulling. body.IsKinematic = true; int adjIndex = 0; int numFromStart = 0; //grab adjacent point masses further in the array. for(int i = pmIndex; i < pmIndex + numAdjacentPoints; i++) { adjIndex = i > body.EdgePointMassCount - 1 ? i - body.EdgePointMassCount : i; if(adjIndex > body.EdgePointMassCount - 1 || adjacentPointMasses.Contains(body.getEdgePointMass(adjIndex))) continue; //cache the point mass info and modify the shape matching values by how close it is to the grabbed point mass adjacentPointMasses.Add (body.getEdgePointMass(adjIndex)); oldMultipliers.Add(body.getEdgePointMass(adjIndex).shapeMatchingMultiplier); numFromStart++; body.getEdgePointMass(adjIndex).shapeMatchingMultiplier = numFromStart / (numAdjacentPoints + 1 + adjacentDropoff); } numFromStart = 0; //grab adjacent point masses before the current index in the array. for(int i = pmIndex; i > pmIndex - numAdjacentPoints; i--) { adjIndex = i < 0 ? i + body.EdgePointMassCount: i; if(adjIndex < 0 || adjacentPointMasses.Contains(body.getEdgePointMass(adjIndex))) continue; //cache the point mass info and modify the shape matching values by how close it is to the grabbed point mass adjacentPointMasses.Add (body.getEdgePointMass(adjIndex)); oldMultipliers.Add(body.getEdgePointMass(adjIndex).shapeMatchingMultiplier); numFromStart++; body.getEdgePointMass(adjIndex).shapeMatchingMultiplier = numFromStart / (numAdjacentPoints + 1 + adjacentDropoff); } //find any internal point masses connected to the selected point mass (via spring) and cache/modify its shape matching multiplier. for(int i = 0; i < body.SpringCount; i++) { if(body.getSpring(i).pointMassA == pmIndex || body.getSpring(i).pointMassB == pmIndex) { if(body.getSpring(i).pointMassA >= body.EdgePointMassCount) { adjacentPointMasses.Add (body.getPointMass(body.getSpring(i).pointMassA)); oldMultipliers.Add (body.getPointMass(body.getSpring(i).pointMassA).shapeMatchingMultiplier); body.getPointMass(body.getSpring(i).pointMassA).shapeMatchingMultiplier = 1 / (2 + adjacentDropoff); } else if(body.getSpring(i).pointMassB >= body.EdgePointMassCount) { adjacentPointMasses.Add (body.getPointMass(body.getSpring(i).pointMassB)); oldMultipliers.Add (body.getPointMass(body.getSpring(i).pointMassB).shapeMatchingMultiplier); body.getPointMass(body.getSpring(i).pointMassB).shapeMatchingMultiplier = 1 / (2 + adjacentDropoff); } } } }