- Home /
Can't delete random vertices from a PolygonCollider
This has me tearing my hair off.
This webm illustrates the problem. I've been trying to make melee combat hitboxes with placeholder sprites. This link started me out. As you can see in the example webm, I'm trying to make the arm the hitbox, and this same technique will be applied to other animations. The method itself works. I've successfully generated trigger collisions. BUT there is one problem here that leads to another:
I can't delete those extra vertices. Ever. I've tried deleting in a different order, but I always have the same vertices left, and you can see them in the webm as triangles in the legs and pelvis of the character. And no matter what I do, they can't be deleted.
Why does it matter? Because of this line:
localCollider.SetPath(0, colliders[(int)val].GetPath(0));
The path (polygon) I will get from GetPath will be 0. This would be perfectly fine... if I could delete those triangles. But since I can't, GetPath(0) has a high chance of returning one of those triangles, instead of the polygon I want (the box in arm in this case). I have no way to know which index is the correct one so I can use it, instead of hard coding GetPath(0), nor can I delete the extra paths (triangles) to just leave GetPath as it is now.
I don't know what to do with this problem. I've tried other methods for the hitboxes, but none were to my liking, so I'm not sure what to do anymore. Suggestions?
Answer by ArsDiaboli · Nov 22, 2014 at 07:02 PM
Another coder helped me with this problem. This is the solution that we ended up with.
Since the polygon collider behaved the way I mentioned above, the other coder made 2 script to let me add points to the current gameobject, using this as a replacement for the polygon collider.
This is the script for the custom inspector:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(PolygonHitboxShape))]
public class PolygonHitboxShapeEditor : Editor
{
public override void OnInspectorGUI()
{
var phs = target as PolygonHitboxShape;
this.DrawDefaultInspector();
if (GUILayout.Button("Add new point"))
{
Undo.RecordObject(target, "Added PolygonHitboxShape point");
// put new point in position that's an average of all of them
float ax = phs.points.Count > 0 ? phs.points.Average((px) => px.x) : 0f;
float ay = phs.points.Count > 0 ? phs.points.Average((py) => py.y) : 0f;
phs.points.Add(new Vector2(ax, ay));
EditorUtility.SetDirty(phs);
}
for (int i = 0; i < phs.points.Count; i++)
{
if (GUILayout.Button("Delete point #" + i))
{
Undo.RecordObject(target, "Deleted PolygonHitboxShape point " + i);
phs.points.RemoveAt(i);
EditorUtility.SetDirty(phs);
}
}
}
void OnSceneGUI()
{
var phs = target as PolygonHitboxShape;
if (!phs.enabled)
return;
// unity only adds to the undo list if GUI.changed happens during this func
Undo.RecordObject(target, "Changed PolygonHitboxShape points");
// fudge factor for moving numbers out of the way of handles
var fudgeFactor = HandleUtility.GetHandleSize(phs.transform.position) / 8f;
// list of points transformed into absolute scene space. this is what we draw!
var tps = phs.GetTransformedPoints().ToList();
// let user move any points we have
if (tps.Count > 0)
{
for (int i = 0; i < tps.Count; i++)
{
var p = tps[i];
var pAfter = Handles.PositionHandle(p, Quaternion.identity);
// put little numbers next to them
Handles.Label(new Vector3(p.x, p.y - fudgeFactor, 0f), i.ToString());
if (GUI.changed)
{
phs.points[i] = phs.UntransformPoint(new Vector2(pAfter.x, pAfter.y));
EditorUtility.SetDirty(phs);
}
}
}
// draw big polygon to show the bounds (in case drawDebugGizmo is off)
if (tps.Count > 0)
{
Handles.color = phs.gizmoColor ?? phs.defaultGizmoColor;
Handles.DrawPolyLine(tps.Union(new Vector2[] { new Vector2(tps[0].x, tps[0].y + 0.000001f) })
.Select((v2) => new Vector3(v2.x, v2.y, 0f)).ToArray());
}
}
}
The above code is dropped in the Editor folder, and you can see the PolygonHitboxShape class. That's the script used to create the polygon points, and is the next script:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
[ExecuteInEditMode]
public class PolygonHitboxShape : MonoBehaviour
{
// delete this field if you don't want it
// (can use this to have multiple PolygonHitboxShapes on the same gameobject)
public int number = 0;
// i like generic lists. so sue me.
// (List<T> is the one generic type Unity will serialize)
public List<Vector2> points = new List<Vector2>();
public Vector2 UntransformPoint(Vector2 v)
{
var v3 = this.transform.InverseTransformPoint(v);
return new Vector2(v3.x, v3.y);
}
public IEnumerable<Vector2> GetTransformedPoints()
{
// transform point (implicit cast Vector2->Vector3) then turn back into Vector2
return points.Select((v) => this.transform.TransformPoint(v)).Select((v3) => new Vector2(v3.x, v3.y));
}
//
// DEBUG VISUALIZATION STUFF FOLLOWS
//
// (Note that the PolygonHitboxShapeEditor also has code for drawing the shape!)
// DrawDebugGizmo could be set by the user, or you could set it at runtime so it's only visible
// when this hitbox is 'active'.
public bool drawDebugGizmo = false;
public Color defaultGizmoColor = Color.green;
/// <summary>
/// Overrides gizmo color. (Make me red on enter collision, for example, and set back to null on exit!)
/// </summary>
[HideInInspector]
public Color? gizmoColor = null;
void OnDrawGizmos()
{
if (drawDebugGizmo)
{
var tps = GetTransformedPoints().ToList();
if (tps.Count == 0)
return;
Gizmos.color = gizmoColor ?? defaultGizmoColor;
// 0->1, 1->2, .., n-1 -> n
for (int i = 1; i < tps.Count; i++)
Gizmos.DrawLine(tps[i - 1], tps[i]);
// n->0
Gizmos.DrawLine(tps[tps.Count - 1], tps[0]);
}
}
// so that you can tick/untick the PHS in the Inspector window
void Update() { }
}
The above script is your replacement for the PolygonCollider. Instead of having the PolyCollider like the reference I used, you use this script and add points to whatever GameObject has the script attached.
Finally, this required me to edit the initial code.
using UnityEngine;
using System.Collections;
using System.IO;
using UnityEditor;
//Based on http://answers.unity3d.com/questions/372252/how-can-i-do-frame-by-frame-hitbox-control-for-a-2.html
public class HitboxManagerScript : MonoBehaviour {
public GameObject hitbox;
public PolygonHitboxShape[] polyColliders;
// Collider on this game object
private PolygonCollider2D localCollider;
void Start()
{
// Create a polygon collider
localCollider = hitbox.gameObject.AddComponent<PolygonCollider2D>();
localCollider.isTrigger = true; // Set as a trigger so it doesn't collide with our environment
localCollider.pathCount = 0; // Clear auto-generated polygons
}
public void setHitBox(animationEnum.hitBoxes val)
{
if (val != animationEnum.hitBoxes.clear) {
Vector2[] points = new Vector2[polyColliders [(int)val].points.Count];
for (int i = 0; i < points.Length; i++) {
points [i] = polyColliders [(int)val].points [i];
}
localCollider.SetPath (0, points);
return;
}
localCollider.pathCount = 0;
}
void OnValidate(){
//It's ugly as sin, but it works. Generates the enum to use.
string copyPath = "Assets/Scripts/Autogenerated/AnimationEnumAutogenScript.cs";
using (StreamWriter outfile = new StreamWriter(copyPath)) {
outfile.WriteLine("using UnityEngine;");
outfile.WriteLine("using System.Collections;");
outfile.WriteLine("");
outfile.WriteLine("public class animationEnum : MonoBehaviour {");
outfile.WriteLine("public enum hitBoxes {");
for (int i = 0; i < polyColliders.Length; i++) {
outfile.Write(polyColliders[i].name);
outfile.WriteLine(",");
}
outfile.WriteLine("clear");
outfile.WriteLine("}");
outfile.WriteLine(" ");
outfile.WriteLine("}");
}
AssetDatabase.Refresh();
}
}
Since I wanted to use a dropdown in the Animator editor, I needed that enum. This creates an enum after you add a new hitbox to your manager. The hitbox manager itself is just added to the GameObject that you want to have the animations and hitboxes, like the link I referred to did.
It's rough around the edges and chances are that there are better ways to do this, but as it is, it's working just fine for us now.
Your answer
![](https://koobas.hobune.stream/wayback/20220613171622im_/https://answers.unity.com/themes/thub/images/avi.jpg)