public override void smartSetShape (JelloClosedShape shape, ShapeSettingOptions options = ShapeSettingOptions.None, SmartShapeSettingOptions smartOptions = SmartShapeSettingOptions.None) { base.smartSetShape (shape, options, smartOptions); mNormalList = new Vector2[mEdgePointMasses.Length]; mEdgeLengthList = new float[mEdgePointMasses.Length]; }
public void DrawPointMasses(JelloBody body, bool editable) { Handles.color = new Color(0.75f, 0.75f, 0.2f, 0.5f); for(int i = 0; i < body.Shape.EdgeVertexCount; i++) { Vector2 pos = body.transform.TransformPoint (body.Shape.EdgeVertices[i]); if(editable) { int hot = GUIUtility.hotControl; Handles.FreeMoveHandle(pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.075f, Vector3.zero, Handles.DotCap); if(GUIUtility.hotControl != hot) { if(currentSubEditor == 1)//point mass editor! { subEditors[currentSubEditor].SetEditIndex(i); Repaint(); } } } else { Handles.color = new Color(0.5f, 0.5f, 0.5f, 0.5f); Handles.DotCap(3, pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.075f); } } for(int i = 0; i < body.Shape.InternalVertexCount; i++) { Handles.color = new Color(0.75f, 0f, 0.75f, 0.5f); Vector2 pos = body.transform.TransformPoint (body.Shape.InternalVertices[i]); if(editable) { int hot = GUIUtility.hotControl; EditorGUI.BeginChangeCheck(); pos = Handles.FreeMoveHandle(pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.075f, Vector3.zero, Handles.DotCap); if(EditorGUI.EndChangeCheck()) { if(!JelloShapeTools.Contains(body.Shape.EdgeVertices, body.transform.InverseTransformPoint(pos)) || JelloShapeTools.PointOnPerimeter(body.Shape.EdgeVertices, body.transform.InverseTransformPoint(pos))) { JelloClosedShape newShape = new JelloClosedShape(body.Shape.EdgeVertices, null, false); for(int a = 0; a < body.Shape.InternalVertexCount; a++) { //dont add this point if(a == i) continue; newShape.addInternalVertex(body.Shape.InternalVertices[a]); } newShape.finish(false); body.smartSetShape(newShape, JelloBody.ShapeSettingOptions.MovePointMasses, smartShapeSettingOptions); EditorUtility.SetDirty(body); GUIUtility.hotControl = 0; subEditors[currentSubEditor].SetEditIndex(-1); Repaint(); break; } body.Shape.changeInternalVertexPosition(i, body.transform.InverseTransformPoint(pos)); body.Shape.finish(false); EditorUtility.SetDirty(body); } if(GUIUtility.hotControl != hot && GUIUtility.hotControl != 0) { if(currentSubEditor == 1)//point mass editor! { subEditors[currentSubEditor].SetEditIndex(i + body.EdgePointMassCount); Repaint(); } } } else { Handles.color = new Color(0.5f, 0.5f, 0.5f, 0.5f); Handles.DotCap(3, pos, Quaternion.identity, HandleUtility.GetHandleSize(pos) * 0.075f); } } }
//todo add onenable and ondisable events to body. public virtual void DrawEditorGUI() { //check polycollider vs pointmasscount if(!Application.isPlaying) //TODO have this be handled by a class that extends the polycollider and recognises changes? { for(int b = 0; b < targets.Length; b++) { JelloBody body = (JelloBody)targets[b]; body.setComponentReferences(); body.polyCollider.points = JelloShapeTools.RemoveDuplicatePoints(body.polyCollider.points); JelloClosedShape shape = new JelloClosedShape(body.polyCollider.points, null, false); if(body.Shape != null) { for(int i = 0; i < body.Shape.InternalVertexCount; i++) shape.addInternalVertex(body.Shape.InternalVertices[i]); shape.finish(false); } if(shape.EdgeVertexCount != body.Shape.EdgeVertexCount || shape.InternalVertexCount != body.Shape.InternalVertexCount) body.smartSetShape(shape, JelloBody.ShapeSettingOptions.MovePointMasses, smartShapeSettingOptions); else body.setShape(shape, JelloBody.ShapeSettingOptions.MovePointMasses); //will i need to do this for constraints as well? for(int i = 0; i < body.AttachPointCount; i++) { body.GetAttachPoint(i).UpdateEditorMode(); } } } serializedObject.Update(); EditorGUI.showMixedValue = eMass.hasMultipleDifferentValues; EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(eMass, massContent); if(EditorGUI.EndChangeCheck()) { for(int i = 0; i < serializedObject.targetObjects.Length; i++) { JelloBody b = (JelloBody)serializedObject.targetObjects[i]; b.Mass = eMass.floatValue; } serializedObject.UpdateIfDirtyOrScript(); } EditorGUI.showMixedValue = false; if(!tar.IsStatic) { EditorGUILayout.BeginHorizontal(); EditorGUI.showMixedValue = eAffectedByGravity.hasMultipleDifferentValues; EditorGUILayout.PropertyField(eAffectedByGravity, useGravContent); EditorGUI.showMixedValue = false; EditorGUILayout.EndHorizontal(); if(eAffectedByGravity.boolValue) { EditorGUI.indentLevel++; EditorGUI.showMixedValue = eOverrideGravity.hasMultipleDifferentValues; EditorGUILayout.PropertyField(eOverrideGravity, overrideGravContent); EditorGUI.showMixedValue = false; if(eOverrideGravity.boolValue) { EditorGUI.indentLevel++; EditorGUI.showMixedValue = eGravity.hasMultipleDifferentValues; EditorGUILayout.PropertyField(eGravity, customGravContent); EditorGUI.showMixedValue = false; EditorGUI.indentLevel--; } EditorGUI.indentLevel--; } EditorGUI.showMixedValue = eKinematic.hasMultipleDifferentValues; EditorGUILayout.PropertyField(eKinematic, kinematicContent); EditorGUI.showMixedValue = false; } EditorGUI.showMixedValue = eTrigger.hasMultipleDifferentValues; EditorGUILayout.PropertyField(eTrigger, triggerContent); EditorGUI.showMixedValue = false; EditorGUI.showMixedValue = eAwake.hasMultipleDifferentValues; EditorGUILayout.PropertyField(eAwake, awakeContent); EditorGUI.showMixedValue = false; if(tar.meshLink == null || tar.meshLink.canModifyPivotPoint) { EditorGUI.indentLevel++; EditorGUILayout.BeginHorizontal(); SerializedProperty ePivotOffset = serializedObject.FindProperty("pivotOffset"); GUIStyle pivotStyle = new GUIStyle(EditorStyles.foldout); if(ePivotOffset.prefabOverride) pivotStyle.fontStyle = FontStyle.Bold; showPivot = EditorGUILayout.Foldout(showPivot, pivotFoldoutContent, pivotStyle); if(GUILayout.Button(centerPivotContent, EditorStyles.miniButton)) { Undo.RecordObjects(serializedObject.targetObjects, "Center Pivot"); for(int i = 0; i < serializedObject.targetObjects.Length; i++) { JelloBody jb = (JelloBody)serializedObject.targetObjects[i]; Undo.RecordObject(jb.gameObject.transform, "Center Pivot transform"); if(jb.meshLink != null) { Undo.RecordObject(jb.meshLink, "Center Pivot mesh"); Undo.RecordObject(jb.polyCollider, "Center Pivot collider"); Undo.RecordObject(jb.GetComponent<Renderer>(), "Center Pivot renderer"); Undo.RecordObject(jb.meshLink.LinkedMeshFilter, "Center Pivot filter"); } } for(int i = 0; i < serializedObject.targetObjects.Length; i++) { JelloBody bod = (JelloBody)serializedObject.targetObjects[i]; CenterPivot(bod); EditorUtility.SetDirty(bod.gameObject); EditorUtility.SetDirty(bod.meshLink); //serializedObject.UpdateIfDirtyOrScript(); } SceneView.RepaintAll(); } EditorGUILayout.EndHorizontal(); if(showPivot) { if(!serializedObject.isEditingMultipleObjects) { pivot = EditorGUILayout.Vector2Field("Position", pivot); if(pivot != Vector2.zero) { EditorGUILayout.BeginHorizontal(); if(GUILayout.Button(applyPivotContent, EditorStyles.miniButton)) { JelloBody jb = (JelloBody)serializedObject.targetObject; Undo.RecordObject(jb, "Change Pivot"); Undo.RecordObject(jb.gameObject.transform, "Change Pivot transform"); if(jb.meshLink != null) { Undo.RecordObject(jb.meshLink, "Change Pivot mesh"); Undo.RecordObject(jb.polyCollider, "Change Pivot collider"); Undo.RecordObject(jb.GetComponent<Renderer>(), "Change Pivot renderer"); Undo.RecordObject(jb.meshLink.LinkedMeshFilter, "Change Pivot filter"); } ChangePivot(tar); EditorUtility.SetDirty(tar.gameObject); EditorUtility.SetDirty(tar.meshLink); // serializedObject.UpdateIfDirtyOrScript(); } if(GUILayout.Button(cancelPivotContent, EditorStyles.miniButton)) pivot = Vector2.zero; SceneView.RepaintAll(); EditorGUILayout.EndHorizontal(); } } else { EditorGUILayout.HelpBox("Pivot Points may only be centered when multiple Game Objects are selected", MessageType.Info); } } EditorGUI.indentLevel--; } serializedObject.ApplyModifiedProperties(); }
//TODO make sure any/all of this is precomputed in editor. void Awake() { myTransform = transform; polyCollider = (PolygonCollider2D)GetComponent<Collider2D>(); //HACK this is to handle how ragespline keeps recreating the polygon collider with duplicate points if(meshLink != null && meshLink.meshLinkType == MeshLink.MeshLinkType.RageSplineMeshLink) { Vector2[] points = JelloShapeTools.RemoveDuplicatePoints(polyCollider.points); if(polyCollider.points.Length != points.Length) polyCollider.points = points; } //set pointmass positions to polygoncollider positions if(polyCollider.points.Length != mEdgePointMasses.Length) { JelloClosedShape shape = new JelloClosedShape(polyCollider.points, mBaseShape != null ? mBaseShape.InternalVertices : null, false); setShape(shape, ShapeSettingOptions.MovePointMasses); } else { Shape.changeVertices(polyCollider.points, Shape.InternalVertices); setShape(mBaseShape, ShapeSettingOptions.MovePointMasses); } Scale = Scale; }
protected virtual void processSmartSetShape(List<int[]> indexPairs, JelloClosedShape shape, ShapeSettingOptions options = ShapeSettingOptions.None, SmartShapeSettingOptions smartOptions = SmartShapeSettingOptions.None) { bool rebuildJoints = (smartOptions & SmartShapeSettingOptions.RebuildInvalidatedJoints) == SmartShapeSettingOptions.RebuildInvalidatedJoints; bool rebuildAttachPoints = (smartOptions & SmartShapeSettingOptions.RebuildInvalidatedAttachPoints) == SmartShapeSettingOptions.RebuildInvalidatedAttachPoints; if(mJoints != null) { //work with joints first... for(int i = 0; i < mJoints.Length; i++) { JelloJoint joint = mJoints[i]; if(joint.bodyA == this) { for(int a = 0; a < joint.affectedIndicesA.Length; a++) { //Vector2 point = joint .GetAnchorPointA(true); bool found = false; for(int b = 0; b < indexPairs.Count; b++) { if(joint.affectedIndicesA[a] == indexPairs[b][1]) { joint.affectedIndicesA[a] = indexPairs[b][0]; found = true; break; } } if(!found) { if(rebuildJoints) { //rebuild the joint.. Vector2 pos = mBaseShape.getVertex(joint.affectedIndicesA[a]); Vector2[] fullShape = new Vector2[shape.VertexCount]; for(int c = 0; c < shape.VertexCount; c++) fullShape[c] = shape.getVertex(c); int[] closestIndices = JelloShapeTools.GetClosestIndices(pos, fullShape, joint.affectedIndicesA.Length); bool assignedClosest = false; //check if any of the indices are already in use for(int c = 0; c < closestIndices.Length; c++) { //joint.affectedindicesA[a] is the current one... //compare unchecked indices against their old position and compare checked indices against their new position. for(int d = 0; d < joint.affectedIndicesA.Length; d++) { //skip the index that we are currently working with. if(d == a) continue; if(d < a)//this index has been updated to the new shape so compare against indexPairs[index][0] { if(indexPairs[closestIndices[c]][0] == joint.affectedIndicesA[d])//in use continue; } else//d must be greater than a, so so this index is untouched and we should compare against indexPairs[index][1] { if(indexPairs[closestIndices[c]][1] == joint.affectedIndicesA[d])//in use continue; } //made it past the early continues, so the index must not be in use. joint.affectedIndicesA[a] = closestIndices[c]; assignedClosest = true; break; } if(assignedClosest) break; } //dont forget to rebuild the joint!!! Vector2[] affectedVertices = new Vector2[joint.affectedIndicesA.Length]; for(int q = 0; q < affectedVertices.Length; q++) affectedVertices[q] = shape.getVertex (joint.affectedIndicesA[q]); joint.RebuildAnchor(joint.localAnchorA, true, true, joint.affectedIndicesA, affectedVertices); } else { //this joint is invalid and will be removed in the clear invalid subcomponents method. mJoints[i] = null; break; } } } } else if(joint.bodyB == this) { for(int a = 0; a < joint.affectedIndicesB.Length; a++) { bool found = false; for(int b = 0; b < indexPairs.Count; b++) { if(joint.affectedIndicesB[a] == indexPairs[b][1]) { joint.affectedIndicesB[a] = indexPairs[b][0]; found = true; break; } } if(!found) { if(rebuildJoints) { //rebuild the joint.. Vector2 pos = mBaseShape.getVertex(joint.affectedIndicesB[a]); Vector2[] fullShape = new Vector2[shape.VertexCount]; for(int c = 0; c < shape.VertexCount; c++) fullShape[c] = shape.getVertex(c); int[] closestIndices = JelloShapeTools.GetClosestIndices(pos, fullShape, joint.affectedIndicesB.Length); bool assignedClosest = false; //check if any of the indices are already in use for(int c = 0; c < closestIndices.Length; c++) { //joint.affectedindicesA[a] is the current one... //compare unchecked indices against their old position and compare checked indices against their new position. for(int d = 0; d < joint.affectedIndicesB.Length; d++) { //skip the index that we are currently working with. if(d == a) continue; if(d < a)//this index has been updated to the new shape so compare against indexPairs[index][0] { if(indexPairs[closestIndices[c]][0] == joint.affectedIndicesB[d])//in use continue; } else//d must be greater than a, so so this index is untouched and we should compare against indexPairs[index][1] { if(indexPairs[closestIndices[c]][1] == joint.affectedIndicesB[d])//in use continue; } //made it past the early continues, so the index must not be in use. joint.affectedIndicesB[a] = closestIndices[c]; assignedClosest = true; break; } if(assignedClosest) break; } //dont forget to rebuild the joint!!! Vector2[] affectedVertices = new Vector2[joint.affectedIndicesB.Length]; for(int q = 0; q < affectedVertices.Length; q++) affectedVertices[q] = shape.getVertex(joint.affectedIndicesB[q]); joint.RebuildAnchor(joint.localAnchorB, false, true, joint.affectedIndicesB, affectedVertices); } else { //this joint is invalid and will be removed in the clear invalid subcomponents method. mJoints[i] = null; break; } } } } } } if(mAttachPoints != null) { //handle attach points now for(int i = 0; i < mAttachPoints.Length; i++) { JelloAttachPoint attachPoint = mAttachPoints[i]; for(int a = 0; a < attachPoint.affectedIndices.Length; a++) { bool found = false; for(int b = 0; b < indexPairs.Count; b++) { if(attachPoint.affectedIndices[a] == indexPairs[b][1]) { attachPoint.affectedIndices[a] = indexPairs[b][0]; found = true; break; } } if(!found) { if(rebuildAttachPoints) { //rebuild the attach point.. Vector2 pos = mBaseShape.getVertex(attachPoint.affectedIndices[a]); Vector2[] fullShape = new Vector2[shape.VertexCount]; for(int c = 0; c < shape.VertexCount; c++) fullShape[c] = shape.getVertex(c); int[] closestIndices = JelloShapeTools.GetClosestIndices(pos, fullShape, attachPoint.affectedIndices.Length); bool assignedClosest = false; //check if any of the indices are already in use for(int c = 0; c < closestIndices.Length; c++) { //joint.affectedindicesA[a] is the current one... //compare unchecked indices against their old position and compare checked indices against their new position. for(int d = 0; d < attachPoint.affectedIndices.Length; d++) { //skip the index that we are currently working with. if(d == a) continue; if(d < a)//this index has been updated to the new shape so compare against indexPairs[index][0] { if(indexPairs[closestIndices[c]][0] == attachPoint.affectedIndices[d])//in use continue; } else//d must be greater than a, so so this index is untouched and we should compare against indexPairs[index][1] { if(indexPairs[closestIndices[c]][1] == attachPoint.affectedIndices[d])//in use continue; } //made it past the early continues, so the index must not be in use. attachPoint.affectedIndices[a] = closestIndices[c]; assignedClosest = true; break; } if(assignedClosest) break; } //all legs have been updated, not rebuild the attach point. attachPoint.Rebuild(attachPoint.point, this, attachPoint.affectedIndices, true); } else { //this joint is invalid and will be removed in the clear invalid subcomponents method. mAttachPoints[i] = null; break; } } } } } }
/// <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> /// At the given index, smartly remove the internal JelloPointMass from this JelloBody and the internal vertex from 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="index">The index of internal JelloPointMass to be removed.</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 removed.</param> /// <param name="smartOptions">SmartShapeSettingOptions on how Subcomponents should be modified as the JelloPointMass and JelloClosedShape vertex is removed.</param> public virtual void smartRemoveInternalPointMass(int index, bool recenterBaseShape, ShapeSettingOptions options = ShapeSettingOptions.None, SmartShapeSettingOptions smartOptions = SmartShapeSettingOptions.None) { if(index < 0 && index >= mInternalPointMasses.Length) return; Vector2[] tempItnernalVertices = new Vector2[mBaseShape.InternalVertexCount - 1]; int a = 0; for(int i = 0; i < mBaseShape.InternalVertexCount; i++) { if(i != index) { tempItnernalVertices[a] = mBaseShape.InternalVertices[i]; a++; } } JelloClosedShape newShape = new JelloClosedShape(mBaseShape.EdgeVertices, tempItnernalVertices, recenterBaseShape); smartSetShape(newShape, options, smartOptions); }
/// <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> /// 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(); }
protected override void processSmartSetShape(List<int[]> indexPairs, JelloClosedShape shape, ShapeSettingOptions options, SmartShapeSettingOptions smartOptions = SmartShapeSettingOptions.None) { base.processSmartSetShape (indexPairs, shape, options, smartOptions); List<int[]> indexPairsQueue = new List<int[]>(); List<JelloSpring> tempSprings = new List<JelloSpring>(); if((options & ShapeSettingOptions.RebuildEdgeSprings) == ShapeSettingOptions.RebuildEdgeSprings) { clearEdgeSprings(); buildEdgeSprings(); } else if((options & ShapeSettingOptions.ClearEdgeSprings) == ShapeSettingOptions.ClearEdgeSprings) { clearEdgeSprings(); } else { //find the first common point to the two shapes int index = -1; for(int i = 0; i < shape.EdgeVertexCount; i++) { if(indexPairs[i][1] != -1) { index = i; break; } } if(index == -1)//in this case, there are no common points to this shape at all. we can just create new edge springs... { clearEdgeSprings(); buildEdgeSprings(); } else { while(indexPairsQueue.Count < shape.EdgeVertexCount) { if(index >= shape.EdgeVertexCount) index = 0; indexPairsQueue.Add (indexPairs[index]); index++; } //see if edge is intact... for(int i = 0; i < shape.EdgeVertexCount; i++)//using edge point mass length, because we only want the edge indices { int next = i + 1 < shape.EdgeVertexCount ? i + 1 : 0; //check if this edge is the same as the last. if(indexPairsQueue[i][1] != -1) //old doesnt equal -1 { //then the next should equal this plus 1 or 0 if full wrap around? if(indexPairsQueue[next][1] == (indexPairsQueue[i][1] + 1 < mBaseShape.EdgeVertexCount ? indexPairsQueue[i][1] + 1 : 0)) //our edge is preserved from the old shape, lets move our old spring into place... { bool found = false; JelloSpring spring = null; //first check the expected position. if(indexPairsQueue[i][1] < mEdgeSprings.Length) spring = mEdgeSprings[indexPairsQueue[i][1]]; if(spring != null && spring.pointMassA == indexPairsQueue[i][1] && spring.pointMassB == indexPairsQueue[next][1]) found = true; //if not in the expected position, check the rest of the positions... if(!found) { for(int a = 0; a < mEdgeSprings.Length; a++) { spring = mEdgeSprings[a]; if(spring.pointMassA == indexPairsQueue[i][1] && spring.pointMassB == indexPairsQueue[next][1]) { found = true; break; } } } if(!found)//the spring could not be found, create a new one. { //float dist = Vector2.Distance(shape.EdgeVertices[indexPairsQueue[i][1]], shape.EdgeVertices[indexPairsQueue[next][1]]); spring = new JelloSpring(indexPairsQueue[i][1], indexPairsQueue[next][1], 0f, DefaultEdgeSpringStiffness, DefaultEdgeSpringDamping); } spring.pointMassA = indexPairsQueue[i][0]; spring.pointMassB = indexPairsQueue[next][0]; tempSprings.Add(spring); } else { if(indexPairsQueue[next][1] == -1)//this is a new point. { //lets check if there is a spring here... bool found = false; //first check the expected position. JelloSpring spring = null; if(indexPairsQueue[i][1] < mEdgeSprings.Length) spring = mEdgeSprings[indexPairsQueue[i][1]]; if(spring != null && (spring.pointMassA == indexPairsQueue[i][1] && spring.pointMassB == (spring.pointMassA == mBaseShape.EdgeVertexCount - 1 ? spring.pointMassA + 1 : 0))) { found = true; } //we didnt find the spring in the expected position, lets look through the rest of the springs. if(!found) { for(int a = 0; a < mEdgeSprings.Length; a++) { spring = mEdgeSprings[a]; if(spring.pointMassA == indexPairsQueue[i][1] && spring.pointMassB == (spring.pointMassA == mBaseShape.EdgeVertexCount - 1 ? spring.pointMassA + 1 : 0)) { found = true; break; } } } //no old spring found, lets create one... if(!found) { spring = new JelloSpring(indexPairsQueue[i][0], indexPairsQueue[next][0], 0f, DefaultEdgeSpringStiffness, DefaultEdgeSpringDamping); } spring.pointMassA = indexPairsQueue[i][0]; spring.pointMassB = indexPairsQueue[next][0]; //spring.length = Vector2.Distance(shape.EdgeVertices[spring.pointMassA], shape.EdgeVertices[spring.pointMassB]); float multiplier = spring.lengthMultiplier; //first assing spring to this one... tempSprings.Add (spring); //int nextnext = next + 1 < shape.EdgeVertexCount ? next + 1 : 0; //now look through the rest of the points until i find a common point and create those springs in the image of this one... for(int a = next; a < shape.EdgeVertexCount; a++) { int nextnext = a + 1 < shape.EdgeVertexCount ? a + 1 : 0; spring = new JelloSpring(indexPairsQueue[a][0], indexPairsQueue[nextnext][0], 0f, spring.stiffness, spring.damping); //spring.length = Vector2.Distance(shape.EdgeVertices[spring.pointMassA], shape.EdgeVertices[spring.pointMassB]); spring.lengthMultiplier = multiplier; tempSprings.Add (spring); i++; if(indexPairsQueue[nextnext][1] != -1) break; } } else//this is a vertex preserved from the old shape... in otherwords, there was a point mass deleted here. { //lets check if there is a spring here... bool found = false; //first check the expected position. JelloSpring spring = null; if(indexPairsQueue[i][1] < mEdgeSprings.Length) spring = mEdgeSprings[indexPairsQueue[i][1]]; if(spring != null && (spring.pointMassA == indexPairsQueue[i][1] && spring.pointMassB == (spring.pointMassA == mBaseShape.EdgeVertexCount - 1 ? spring.pointMassA + 1 : 0))) { found = true; } //we didnt find the spring in the expected position, lets look through the rest of the springs. if(!found) { for(int a = 0; a < mEdgeSprings.Length; a++) { spring = mEdgeSprings[a]; if(spring.pointMassA == indexPairsQueue[i][1] && spring.pointMassB == (spring.pointMassA == mBaseShape.EdgeVertexCount - 1 ? spring.pointMassA + 1 : 0)) { found = true; break; } } } //no old spring found, lets create one... if(!found) { spring = new JelloSpring(indexPairsQueue[i][0], indexPairsQueue[next][0], 0f, DefaultEdgeSpringStiffness, DefaultEdgeSpringDamping); } spring.pointMassA = indexPairsQueue[i][0]; spring.pointMassB = indexPairsQueue[next][0]; //spring.length = Vector2.Distance(shape.EdgeVertices[spring.pointMassA], shape.EdgeVertices[spring.pointMassB]); tempSprings.Add (spring); } } } else { JelloSpring spring = new JelloSpring(indexPairsQueue[i][0], indexPairsQueue[next][0], 0f, DefaultEdgeSpringStiffness, DefaultEdgeSpringDamping); //spring.length = Vector2.Distance(shape.EdgeVertices[spring.pointMassA], shape.EdgeVertices[spring.pointMassB]); tempSprings.Add (spring); } } mEdgeSprings = new JelloSpring[tempSprings.Count]; int indexOffset = 0; for(int i = 0; i < tempSprings.Count; i++) { if(tempSprings[i].pointMassA == 0) { indexOffset = i; break; } } JelloSpring[] tempArray = new JelloSpring[tempSprings.Count]; for(int i = 0; i < tempArray.Length; i++) { int a = i + indexOffset; if(a >= tempArray.Length) a -= tempArray.Length; tempArray[i] = tempSprings[a]; } clearEdgeSprings(); addSprings(tempArray, ref mEdgeSprings); } } if((options & ShapeSettingOptions.RebuildInternalSprings) == ShapeSettingOptions.RebuildInternalSprings) { clearInternalSprings(); BuildInternalSprings(); } else if((options & ShapeSettingOptions.ClearInternalSprings) == ShapeSettingOptions.ClearInternalSprings) { clearInternalSprings(); } else if(mInternalSprings.Length > 0) { //now handle internal springs int[] tris = shape.Triangles; tempSprings.Clear(); for(int i = 0; i < tris.Length; i+=3) { for(int t = 0; t < 3; t++) { int r = t + 1 < 3 ? t + 1 : 0; if(tris[ i + t ] < shape.EdgeVertexCount && tris[ i + r] < shape.EdgeVertexCount) //dont build edge springs { if(tris[ i + t ] != 0 && tris[ i + r ] != 0) { if(Mathf.Abs( tris[ i + t ] - tris[ i + r ] ) == 1) { continue; } } else if(tris[ i + t ] == shape.EdgeVertexCount - 1 || tris[ i + r ] == shape.EdgeVertexCount - 1 || tris[ i + t ] == 1 || tris[ i + t ] == 1) { continue; } } bool exists = false; for(int a = 0; a < tempSprings.Count; a++) { if((tris[i + t] == tempSprings[a].pointMassA && tris[i + r] == tempSprings[a].pointMassB) || (tris[i + t] == tempSprings[a].pointMassB && tris[i + r] == tempSprings[a].pointMassA)) { exists = true; break; } } if(exists) continue; else tempSprings.Add ( new JelloSpring ( tris[i + t], tris[i + r], 0f, mDefaultInternalSpringK, mDefaultInternalSpringDamp ) ); } } //now compare our new internal springs to our old internal springs... for(int i = 0; i < tempSprings.Count; i++) { JelloSpring spring = tempSprings[i]; int pairA = -1; int pairB = -1; for(int a = 0; a < indexPairs.Count; a++) { if(indexPairs[a][0] == spring.pointMassA) pairA = a; if(indexPairs[a][0] == spring.pointMassB) pairB = a; if(pairA != -1 && pairB != -1) break; } if(pairA == -1 || pairB == -1) { //this shouldnt be possible continue; } //check if there is an old point assosiated with each spring end point if(indexPairs[pairA][1] != -1 && indexPairs[pairB][1] != -1) { JelloSpring oldSpring; for(int a = 0; a < mInternalSprings.Length; a++) { oldSpring = mInternalSprings[a]; if((oldSpring.pointMassA == indexPairs[pairA][1] && oldSpring.pointMassB == indexPairs[pairB][1]) || (oldSpring.pointMassB == indexPairs[pairA][1] && oldSpring.pointMassA == indexPairs[pairB][1])) { spring.damping = oldSpring.damping; spring.lengthMultiplier = oldSpring.lengthMultiplier; spring.stiffness = oldSpring.stiffness; } } } } //now set our new internal springs. clearInternalSprings(); if(tempSprings.Count > 0) addSprings(tempSprings.ToArray(), ref mInternalSprings); } if((options & ShapeSettingOptions.ClearCustomSprings) == ShapeSettingOptions.ClearCustomSprings) { clearCustomSprings(); } else { tempSprings.Clear(); bool rebuildCustomSprings = (smartOptions & SmartShapeSettingOptions.RebuildInvalidatedCustomSprings) == SmartShapeSettingOptions.RebuildInvalidatedCustomSprings; //now handle custom springs for(int i = 0; i < mCustomSprings.Length; i++) { JelloSpring spring = mCustomSprings[i]; int indexA = -1; int indexB = -1; for(int a = 0; a < indexPairs.Count; a++) { if(spring.pointMassA == indexPairs[a][1]) indexA = indexPairs[a][0]; if(spring.pointMassB == indexPairs[a][1]) indexB = indexPairs[a][0]; if(indexA != -1 && indexB != -1) { break; } } //here if(indexA == -1 || indexB == -1) { if(rebuildCustomSprings) { Vector2[] fullShape = new Vector2[shape.VertexCount]; for(int c = 0; c < shape.VertexCount; c++) fullShape[c] = shape.getVertex(c); if(indexA == -1) { //rebuild the spring Vector2 pos = mBaseShape.getVertex(spring.pointMassA); int[] closestIndices = JelloShapeTools.GetClosestIndices(pos, fullShape, 2); //check if any of the indices are already in use for(int c = 0; c < closestIndices.Length; c++) { //already in use by index b if(indexB == closestIndices[c]) continue; indexA = closestIndices[c]; break; } } if(indexB == -1) { //rebuild the spring Vector2 pos = mBaseShape.getVertex(spring.pointMassB); int[] closestIndices = JelloShapeTools.GetClosestIndices(pos, fullShape, 2); //check if any of the indices are already in use for(int c = 0; c < closestIndices.Length; c++) { //already in use by index b if(indexA == closestIndices[c]) continue; indexB = closestIndices[c]; break; } } } else { continue; } } spring.pointMassA = indexA; spring.pointMassB = indexB; tempSprings.Add(spring); } clearCustomSprings(); if(tempSprings.Count > 0) addSprings(tempSprings.ToArray(), ref mCustomSprings); } }
public override void smartSetShape(JelloClosedShape shape, ShapeSettingOptions options, SmartShapeSettingOptions smartOptions = SmartShapeSettingOptions.None) { base.smartSetShape (shape, options, smartOptions); }
/// <summary> /// Set the JelloBody.Shape of this JelloBody to a new JelloClosedShape object. This function /// will remove any existing JelloPointMass objects, and replace them with new ones if /// the new JelloClosedShape has a different vertex count than the previous one. In this case /// the JelloPointMass.Mass for each newly added JelloPointMass will be set to the JelloBody.Mass. Otherwise the JelloBody.Shape is just /// updated, not affecting any existing JelloPointMass other than by position. /// Any JelloJoint or AttachPoint made invalid by the new shape will be removed. /// If present, each internal JelloSpring will be removed and a new set will be built. /// If present, each custom spring will be removed. /// Will clear and replace every edge JelloSpring. /// Will update every JelloSpring rest length. /// </summary> /// <param name="shape">New JelloClosedShape to implement</param> /// <param name="options">ShapeSettingOptions for setting the JelloClosedShape.</param> public override void setShape(JelloClosedShape shape, ShapeSettingOptions options = ShapeSettingOptions.None) { base.setShape (shape, options); if((options & ShapeSettingOptions.RebuildEdgeSprings) == ShapeSettingOptions.RebuildEdgeSprings) { clearEdgeSprings(); buildEdgeSprings(); } else if((options & ShapeSettingOptions.ClearEdgeSprings) == ShapeSettingOptions.ClearEdgeSprings) { clearEdgeSprings(); } if((options & ShapeSettingOptions.RebuildInternalSprings) == ShapeSettingOptions.RebuildInternalSprings) { clearInternalSprings(); BuildInternalSprings(); } else if((options & ShapeSettingOptions.ClearInternalSprings) == ShapeSettingOptions.ClearInternalSprings) { clearInternalSprings(); } if((options & ShapeSettingOptions.ClearCustomSprings) == ShapeSettingOptions.ClearCustomSprings) { clearCustomSprings(); } updateSpringDistances(); }
public override void smartSetShape(JelloClosedShape shape, ShapeSettingOptions options = ShapeSettingOptions.None, SmartShapeSettingOptions smartOptions = SmartShapeSettingOptions.None) { base.smartSetShape (shape, options, smartOptions); mNormalList = new Vector2[mEdgePointMasses.Length]; mEdgeLengthList = new float[mEdgePointMasses.Length]; }