Last active
December 16, 2015 13:48
-
-
Save wtrebella/5444072 to your computer and use it in GitHub Desktop.
This is a collection of classes that will allow you to simply make an array of vertices and have them drawn on screen as a polygon (with a solid color). You will also be able to collide a circle with that polygon, or just get the vertex points based on its rotation, scale, and position.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
// This is just an example of how to use the polygon sprite | |
public class WTDrawingPolygonsScene : MonoBehaviour { | |
void Start () { | |
FutileParams fp = new FutileParams(true, true, false, false); | |
fp.AddResolutionLevel(480f, 1.0f, 1.0f, "-res1"); | |
fp.backgroundColor = Color.black; | |
fp.origin = Vector2.zero; | |
Futile.instance.Init(fp); | |
Vector2[] vertices = new Vector2[] { | |
new Vector2(0, 0), | |
new Vector2(25, 5), | |
new Vector2(35, 45), | |
new Vector2(20, 23), | |
new Vector2(5, 57) | |
}; | |
WTPolygonSprite s = new WTPolygonSprite(new WTPolygonData(vertices)); | |
s.color = Color.green; | |
s.x = Futile.screen.halfWidth - 130; | |
s.y = Futile.screen.halfHeight; | |
Futile.stage.AddChild(s); | |
s = new WTPolygonSprite(new WTPolygonData(vertices)); | |
s.color = Color.green; | |
s.x = Futile.screen.halfWidth - 20; | |
s.y = Futile.screen.halfHeight; | |
s.scaleX = 2.0f; | |
s.scaleY = 0.5f; | |
s.rotation = 143; | |
Futile.stage.AddChild(s); | |
// This will log the original values of the vertices | |
Vector2[] originalVertices = s.polygonData.polygonVertices; | |
for (int i = 0; i < originalVertices.Length; i++) { | |
Vector2 v = originalVertices[i]; | |
Debug.Log("Original vertex " + i + ": " + v); | |
} | |
Debug.Log("==================================================================================================="); | |
// This will log the values of the vertices after taking rotation, scale, and position into account | |
Vector2[] adjustedVertices = s.GetAdjustedPolygonData().polygonVertices; | |
for (int i = 0; i < adjustedVertices.Length; i++) { | |
Vector2 v = adjustedVertices[i]; | |
Debug.Log("Adjusted vertex " + i + ": " + v); | |
} | |
int circleResolution = 50; | |
float radius = 50; | |
Vector2[] circleVertices = new Vector2[circleResolution]; | |
for (int i = 0; i < circleResolution; i++) { | |
float x = Mathf.Cos(2 * Mathf.PI / circleResolution * (i + 1)) * radius; | |
float y = Mathf.Sin(2 * Mathf.PI / circleResolution * (i + 1)) * radius; | |
circleVertices[i] = new Vector2(x, y); | |
} | |
WTPolygonSprite sCircle = new WTPolygonSprite(new WTPolygonData(circleVertices)); | |
sCircle.color = Color.cyan; | |
sCircle.x = Futile.screen.halfWidth + 100; | |
sCircle.y = Futile.screen.halfHeight; | |
Futile.stage.AddChild(sCircle); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System; | |
using System.Collections.Generic; | |
public class WTPolygonData | |
{ | |
public bool shouldUseSmoothSphereCollisions = false; //set to true manually if needed | |
private Vector2[] polygonVertices_; | |
public int[] polygonTriangles {get; private set;} //a list of triangle polygons (each one is an array of int triangle indices) | |
public WTPolygonData (Vector2[] vertices) | |
{ | |
this.polygonVertices = vertices; | |
} | |
public Vector2[] polygonVertices { //VERTICES MUST BE PROVIDED IN CLOCKWISE ORDER! | |
get {return polygonVertices_;} | |
set { | |
polygonVertices_ = value; | |
polygonTriangles = FPUtils.Triangulate(polygonVertices_); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
public class WTPolygonSprite : FSprite | |
{ | |
public WTPolygonData polygonData {get; private set;} | |
private int _triangleCount; | |
private Vector2 _uvTopLeft; | |
private Vector2 _uvBottomLeft; | |
private Vector2 _uvBottomRight; | |
public WTPolygonSprite (WTPolygonData polygonData) { | |
_element = Futile.atlasManager.GetElementWithName("Futile_White"); | |
_isAlphaDirty = true; | |
UpdateWithData(polygonData); | |
} | |
// Call this method if you want to change the points of the polygon after it already exists. | |
public void UpdateWithData(WTPolygonData newData) { | |
this.polygonData = newData; | |
if (polygonData != null) { | |
RefreshVertices(); | |
_triangleCount = this.polygonData.polygonTriangles.Length / 3; | |
Init(FFacetType.Triangle, _element, _triangleCount); | |
_isMatrixDirty = true; | |
_isAlphaDirty = true; | |
_areLocalVerticesDirty = true; | |
Redraw(true, false); | |
} | |
} | |
override public void Redraw(bool shouldForceDirty, bool shouldUpdateDepth) | |
{ | |
bool wasMatrixDirty = _isMatrixDirty; | |
bool wasAlphaDirty = _isAlphaDirty; | |
UpdateDepthMatrixAlpha(shouldForceDirty, shouldUpdateDepth); | |
if(shouldUpdateDepth) | |
{ | |
UpdateFacets(); | |
} | |
if(wasMatrixDirty || shouldForceDirty || shouldUpdateDepth) | |
{ | |
_isMeshDirty = true; | |
} | |
if(wasAlphaDirty || shouldForceDirty) | |
{ | |
_isMeshDirty = true; | |
_color.ApplyMultipliedAlpha(ref _alphaColor, _concatenatedAlpha); | |
} | |
if(_areLocalVerticesDirty) | |
{ | |
UpdateLocalVertices(); | |
} | |
if(_isMeshDirty) | |
{ | |
PopulateRenderLayer(); | |
} | |
} | |
override public void HandleElementChanged() | |
{ | |
_areLocalVerticesDirty = true; | |
} | |
// This is being overridden because if the vertice data gets changed for | |
// some reason, it needs to update some things. | |
override public void UpdateLocalVertices() | |
{ | |
_areLocalVerticesDirty = false; | |
_uvTopLeft = _element.uvTopLeft; | |
_uvBottomLeft = _element.uvBottomLeft; | |
_uvBottomRight = _element.uvBottomRight; | |
int[] trianglePolygons = polygonData.polygonTriangles; | |
_triangleCount = trianglePolygons.Length / 3; | |
if(_numberOfFacetsNeeded != _triangleCount) | |
{ | |
_numberOfFacetsNeeded = _triangleCount; | |
if(_isOnStage) _stage.HandleFacetsChanged(); | |
} | |
_isMeshDirty = true; | |
} | |
// This is where the polygon is actually "drawn" | |
override public void PopulateRenderLayer() | |
{ | |
if(_isOnStage && _firstFacetIndex != -1) | |
{ | |
_isMeshDirty = false; | |
Vector3[] vertices = _renderLayer.vertices; | |
Vector2[] uvs = _renderLayer.uvs; | |
Color[] colors = _renderLayer.colors; | |
Vector2[] polygonVertices = polygonData.polygonVertices; | |
int[] trianglePolygons = polygonData.polygonTriangles; | |
int nextTriangleIndex = _firstFacetIndex; | |
int polyTriangleCount = trianglePolygons.Length / 3; | |
for(int t = 0; t < polyTriangleCount; t++) | |
{ | |
int vertexIndex0 = nextTriangleIndex*3; | |
int vertexIndex1 = vertexIndex0 + 1; | |
int vertexIndex2 = vertexIndex0 + 2; | |
int threeT = t*3; | |
_concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex0], polygonVertices[trianglePolygons[threeT]],0); | |
_concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex1], polygonVertices[trianglePolygons[threeT+1]],0); | |
_concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex2], polygonVertices[trianglePolygons[threeT+2]],0); | |
uvs[vertexIndex0] = _uvBottomLeft; | |
uvs[vertexIndex1] = _uvTopLeft; | |
uvs[vertexIndex2] = _uvBottomRight; | |
colors[vertexIndex0] = _alphaColor; | |
colors[vertexIndex1] = _alphaColor; | |
colors[vertexIndex2] = _alphaColor; | |
nextTriangleIndex++; | |
} | |
_renderLayer.HandleVertsChange(); | |
} | |
} | |
// If for some reason you change the WTPolygonData for this polygon sprite, make sure to call this so | |
// it refreshes them and knows what to draw. There may be ways to make this more optimized for performance | |
// that I need to look into. | |
public void RefreshVertices() { | |
if(_isOnStage && _firstFacetIndex != -1) { | |
_renderLayer.Open(); | |
// Adding more facets than necessary because for some reason sometimes there aren't enough. | |
// I need to look into this to figure out a better way to handle this. | |
_renderLayer.GetNextFacetIndex(_triangleCount * 2); | |
Vector3[] vertices = _renderLayer.vertices; | |
for (int i = 0; i < vertices.Length; i++) { | |
_concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[i], Vector2.zero, 0); | |
} | |
} | |
} | |
// This method returns the polygon data for the sprite after taking into account the sprite's | |
// rotation, scale, and position. This is useful to get the actual vertice points that are | |
// being shown on screen rather than the unaltered ones. CAUTION: this does not take any | |
// parent container rotation, scale, or position into account. It just gets the data based | |
// on the sprite's own properties. This method has NOT been optimized for performance yet. | |
// There are ways to make it faster that I will update it with in the future. | |
public WTPolygonData GetAdjustedPolygonData() { | |
Vector2[] newPolygonVertices = new Vector2[polygonData.polygonVertices.Length]; | |
for (int i = 0; i < newPolygonVertices.Length; i++) { | |
newPolygonVertices[i] = new Vector2(polygonData.polygonVertices[i].x, polygonData.polygonVertices[i].y); | |
} | |
for (int i = 0; i < newPolygonVertices.Length; i++) { | |
Vector2 v = newPolygonVertices[i]; | |
v.x *= _scaleX; | |
v.y *= _scaleY; | |
Vector2 origV = v; | |
// rotation needs to be negative because of the way rotation works in futile (clockwise rather than counter clockwise) | |
float cosAngle = Mathf.Cos(-_rotation); | |
float sinAngle = Mathf.Sin(-_rotation); | |
v.x = origV.x * cosAngle - origV.y * sinAngle; | |
v.y = origV.x * sinAngle + origV.y * cosAngle; | |
v.x += this.x; | |
v.y += this.y; | |
newPolygonVertices[i] = v; | |
} | |
return new WTPolygonData(newPolygonVertices); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
public static class WTUtils { | |
// Line segment intersection based on this: http://www.pdas.com/lineint.html | |
public static bool IntersectLineSegments(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) { | |
Vector2 s1 = p1 - p0; | |
Vector2 s2 = p3 - p2; | |
float s, t, denom; | |
denom = -s2.x * s1.y + s1.x * s2.y; | |
if (denom == 0) { | |
// divide by zero | |
return false; | |
} | |
s = (-s1.y * (p0.x - p2.x) + s1.x * (p0.y - p2.y)) / denom; | |
t = ( s2.x * (p0.y - p2.y) - s2.y * (p0.x - p2.x)) / denom; | |
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) return true; | |
return false; | |
} | |
// Intersect circle/polygon algorithm based on this: http://gamedev.stackexchange.com/questions/7735/how-to-find-if-circle-and-concave-polygon-intersect | |
public static bool IntersectCirclePolygon(Vector2 circlePos, float circleRadius, Vector2[] polygonVertices) { | |
float xPolygonMin = float.MaxValue; | |
float xPolygonMax = float.MinValue; | |
float yPolygonMin = float.MaxValue; | |
float yPolygonMax = float.MinValue; | |
for (int i = 0; i < polygonVertices.Length; i++) { | |
if (IntersectCircleLineSegment(circlePos, circleRadius, polygonVertices[i], polygonVertices[(i+1)%polygonVertices.Length])) return true; | |
Vector2 v = polygonVertices[i]; | |
xPolygonMin = Mathf.Min(xPolygonMin, v.x); | |
xPolygonMax = Mathf.Max(xPolygonMax, v.x); | |
yPolygonMin = Mathf.Min(yPolygonMin, v.y); | |
yPolygonMax = Mathf.Max(yPolygonMax, v.y); | |
} | |
// Expand the polygon just a bit to make sure everything is encompassed. | |
float extra = 5; | |
xPolygonMin += extra; | |
xPolygonMax += extra; | |
yPolygonMin += extra; | |
yPolygonMax += extra; | |
Vector2 circRayRight_p0 = circlePos; | |
Vector2 circRayRight_p1 = new Vector2(10000, circlePos.y); // Extend the ray super far to the right | |
float numIntersections = 0; | |
for (int i = 0; i < polygonVertices.Length; i++) { | |
Vector2 poly_p0 = polygonVertices[i]; | |
Vector2 poly_p1 = polygonVertices[(i+1)%polygonVertices.Length]; | |
if (poly_p0.y == poly_p1.y) continue; | |
if (IntersectLineSegments(circRayRight_p0, circRayRight_p1, polygonVertices[i], polygonVertices[(i+1)%polygonVertices.Length])) numIntersections++; | |
} | |
if (numIntersections % 2 == 1) return true; | |
else return false; | |
} | |
public static bool IntersectCircleLineSegment(Vector2 circlePos, float circleRadius, Vector2 segPointA, Vector2 segPointB) { | |
Vector2 upperPoint; | |
if (segPointA.y > segPointB.y) upperPoint = segPointA; | |
else upperPoint = segPointB; | |
Vector2 segVector = segPointB - segPointA; | |
Vector2 aToCircVector = circlePos - segPointA; | |
float closestSegPointMagnitude = Vector2.Dot(aToCircVector, segVector.normalized); | |
Vector2 closestSegPoint; | |
if (closestSegPointMagnitude < 0) closestSegPoint = segPointA; | |
else if (closestSegPointMagnitude > segVector.magnitude) closestSegPoint = segPointB; | |
else closestSegPoint = segPointA + closestSegPointMagnitude * segVector.normalized; | |
Vector2 closestToCircVector = circlePos - closestSegPoint; | |
// Don't intersect with top point of line segment (keep it "open") | |
if (upperPoint.Equals(closestSegPoint)) { | |
if (closestToCircVector.magnitude < circleRadius) return true; | |
} | |
else { | |
if (closestToCircVector.magnitude <= circleRadius) return true; | |
} | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment