/** * If there is any true bits in a 3x3 grid make the centre bit false * @param {BinaryImage} ref BinaryImage b [description] * @return {BinaryImage} The erosion image */ private BinaryImage erosion(BinaryImage b) { int[,] dirs = { { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { -1, 0 }, { -1, 1 } }; BinaryImage t = new BinaryImage(b.x, b.y, true); int w = b.x; // width int h = b.y; // height for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { // t.Set(x + y*w, true); for (int z = 0; z < dirs.GetLength(0); z++) { int i = x + dirs[z, 0], j = y + dirs[z, 1]; if (i < w && i >= 0 && j < h && j >= 0) { if (!b.Get(i + j * w)) { t.Set(x + y * w, false); } } else { t.Set(x + y * w, false); } } } } return(t); }
private List <Vector2> getPath(ref BinaryImage b, int startPos) { List <Vector2> path = new List <Vector2>(); // Add start point to path path.Add(new Vector2(startPos % b.x, startPos / b.x)); int pos = 0, prevPos = startPos, currPos = startPos; bool open = true; if (!nextPos(ref b, ref pos, prevPos)) { // No other points found from the starting point means this is a single pixel island so we can remove it b.Set(currPos, false); return(null); } // While there is a next pos while (open) { if (nextPos(ref b, ref pos, ref currPos, ref prevPos)) { // b.Set(pos, false); if (currPos == startPos) { open = false; // We found a closed path } path.Add(new Vector2(currPos % b.x, currPos / b.x)); b.Set(currPos, false); } else { // If no next position, backtrack till we find a closed path var index = backTrack(ref b, ref path, path.Count - 1); if (index != -1) { // find next new point! path.RemoveRange(index + 1, path.Count - 1 - index); pos = (int)path[index].x + (int)path[index].y * b.x; prevPos = (int)path[index - 1].x + (int)path[index - 1].y * b.x; currPos = pos; } else { open = false; // If we cannot, close the path (this is the worst case and will give us a buggy collider) } } } return(path); }
IEnumerator GenerateTerrain() { float t = Time.time; yield return(new WaitForEndOfFrame()); SimplexNoiseGenerator sng = new SimplexNoiseGenerator(seed); BinaryImage bi = new BinaryImage(tex.width, tex.height, false); for (int x = 0; x < tex.width; x++) { float height = sng.coherentNoise(x / terrainStretch + (transform.position.x * terrainStretch), 0, 0, 1, 200, 1.75f, 2f, 0.9f); height += sng.coherentNoise(x / terrainStretch + (transform.position.x * terrainStretch), 0, 0, 2, 25, 0.05f, 2f, 0.9f); height += sng.coherentNoise(x / terrainStretch + (transform.position.x * terrainStretch), 0, 0, 1, 25, 0.1f, 2f, 0.9f); for (int y = 0; y < tex.height; y++) { if (y < height * tex.height + 500) { bi.Set(x, y, true); } else { break; } } } binaryImage = bi; binaryImage = tidyBinaryImage(binaryImage); updateCollider(); Debug.Log((Time.time - t) * 1000f); }
void floodFill(ref BinaryImage b, ref List <int> i, int pos) { int w = b.x; if (b.Get(pos)) { i.Add(pos); b.Set(pos, false); } else { return; } if ((pos % w) + 1 < w) { floodFill(ref b, ref i, pos + 1); // Right } if ((pos % w) - 1 >= 0) { floodFill(ref b, ref i, pos - 1); // Left } if (pos + w < b.Length) { floodFill(ref b, ref i, pos + w); // Top } if (pos - w >= 0) { floodFill(ref b, ref i, pos - w); // Bottom } }
/** * Remove a part of the texture * @param {Vector2} point World point * @param {int} radius Radius of destroyed area */ public void ApplyDamage(Vector2 point, int radius) { // edit binaryImage int w = binaryImage.x, h = binaryImage.y; // get relative position of the circle Vector2 rPos = new Vector2(point.x - transform.position.x, point.y - transform.position.y); // get rotation matrix float theta = transform.rotation.eulerAngles.z * Mathf.PI / 180f; float sin = Mathf.Sin(theta); float cos = Mathf.Cos(theta); // apply rotation to the circle centre Vector2 c = new Vector2(rPos.x * cos + rPos.y * sin, -rPos.x * sin + rPos.y * cos); c.x = (xBounds + (c.x / transform.localScale.x)) / (xBounds * 2) * w; c.y = (yBounds + (c.y / transform.localScale.y)) / (yBounds * 2) * h; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { float dx = x - c.x; float dy = y - c.y; float dist = Mathf.Sqrt(dx * dx + dy * dy); if (dist <= radius) { binaryImage.Set(x + y * w, false); } } } updateCollider(); }
/** * Generates a BitArray from a Texture2D * @param {Texture2D} t [] * @return {BitArray} [description] */ private BinaryImage BinaryImageFromTex(ref Texture2D t) { BinaryImage b = new BinaryImage(t.width, t.height); Color[] data = t.GetPixels(); for (int x = 0; x < b.Length; x++) { b.Set(x, data[x].a > 0); } return(b); }
/** * Subtracts one binaryImage from another * @param {BinaryImage} b1 [description] * @param {BinaryImage} b2 [description] * @return {BinaryImage} [description] */ private BinaryImage subtraction(BinaryImage b1, BinaryImage b2) { BinaryImage t = new BinaryImage(b1.x, b1.y); int w = b1.x; // width int h = b1.y; // height for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { t.Set(x + y * w, (b1.Get(x + y * w) != b2.Get(x + y * w))); } } return(t); }
/** * Remove a part of the texture * @param {Vector2} point World point * @param {int} radius Radius of destroyed area */ public void ApplyDamage(Vector2 point, int radius) { // edit binaryImage int w = binaryImage.x, h = binaryImage.y; // get relative position of the circle Vector2 rPos = new Vector2(point.x - transform.position.x, point.y - transform.position.y); // get rotation matrix float theta = transform.rotation.eulerAngles.z * Mathf.PI / 180f; float sin = Mathf.Sin(theta); float cos = Mathf.Cos(theta); // apply rotation to the circle centre Vector2 c = new Vector2(rPos.x * cos + rPos.y * sin, -rPos.x * sin + rPos.y * cos); c.x = (xBounds + (c.x / transform.localScale.x)) / (xBounds * 2) * w; c.y = (yBounds + (c.y / transform.localScale.y)) / (yBounds * 2) * h; int loops = 0; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { float dx = x - c.x; float dy = y - c.y; float dist = Mathf.Sqrt(dx * dx + dy * dy); if (dist <= radius) { binaryImage.Set(x + y * w, false); loops++; } } } Debug.Log("Finished destruction"); tex = ApplyBinaryImage2Texture(ref tex, ref binaryImage); gameObject.GetComponent <SpriteRenderer>().sprite = Sprite.Create(tex, gameObject.GetComponent <SpriteRenderer>().sprite.rect, new Vector2(0.5f, 0.5f)); Debug.Log("Finished collider"); }
/** * Subtracts one binaryImage from another * @param {BinaryImage} b1 [description] * @param {BinaryImage} b2 [description] * @return {BinaryImage} [description] */ private BinaryImage subtraction(BinaryImage b1, BinaryImage b2) { BinaryImage t = new BinaryImage(b1.x, b1.y); int w = b1.x; // width int h = b1.y; // height for(int x=0; x<w; x++){ for(int y=0; y<h; y++){ t.Set(x+y*w, (b1.Get(x+y*w)!=b2.Get(x+y*w)) ); } } return t; }
void split(BinaryImage b) { int startPos; // TODO: copy the binaryImage instead of setting it BinaryImage t = new BinaryImage(b.x, b.y); for(int x=0; x<b.Length; x++) t.Set(x, b.Get(x)); List<List<int> > islands = new List<List<int> >(); // Find islands while(findStartPos(ref t, out startPos)) { List<int> island = new List<int>(); floodFill(ref t, ref island, startPos); islands.Add(island); } // If there is only 1 island we wont split anything if(islands.Count <= 1) return; // Get bounding boxes for each island for(int i=0; i<islands.Count; i++) { int x1, y1, x2, y2; x1 = x2 = islands[i][0]%b.x; y1 = y2 = Mathf.FloorToInt((float)islands[i][0]/b.x); // Find the smallest and biggest points for(int j=0; j<islands[i].Count; j++) { int x = islands[i][j]%b.x, y = Mathf.FloorToInt((float)islands[i][j]/b.x); if(x < x1) x1 = x; else if(x > x2) x2 = x; if(y < y1) y1 = y; else if(y > y2) y2 = y; } int w = x2-x1, h = y2-y1; // bounds int cx = (x2+x1)/2, cy = (y2+y1)/2; // new center for island // Create new gameobject GameObject go = new GameObject("DestructibleSpritePiece"); go.AddComponent<SpriteRenderer>(); go.AddComponent<Rigidbody2D>(); go.AddComponent<DestructibleSprite>(); go.GetComponent<DestructibleSprite>().doSplit = true; // Copy part of the original texture to our new texture Color32[] d = tex.GetPixels32(); Color32[] e = new Color32[w*h]; for(int x=0, y=0; x<d.Length; x++) { if(x%tex.width>=x1 && x%tex.width<x2 && Mathf.FloorToInt((float)x/tex.width)<y2 && Mathf.FloorToInt((float)x/tex.width)>=y1) { e[y] = d[x]; y++; } } // Apply to our new texture Texture2D texture = new Texture2D(w,h); texture.SetPixels32(e); texture.Apply(); // Add the spriteRenderer and apply the texture and inherit parent options SpriteRenderer s = go.GetComponent<SpriteRenderer>(); s.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); s.color = gameObject.GetComponent<SpriteRenderer>().color; s.sortingOrder = gameObject.GetComponent<SpriteRenderer>().sortingOrder; // Set the position to the new center go.transform.position = new Vector3(transform.position.x + (cx + pixelOffset)/pixelsToUnits - xBounds, transform.position.y + (cy + pixelOffset)/pixelsToUnits - yBounds, transform.position.z); // Put it in the same layer as the parent go.layer = gameObject.layer; } // We can destroy the orignal object Destroy(gameObject); }
private List<Vector2> getPath(ref BinaryImage b, int startPos) { List<Vector2> path = new List<Vector2>(); // Add start point to path path.Add(new Vector2(startPos%b.x, startPos/b.x)); int pos = 0, prevPos = startPos, currPos = startPos; bool open = true; if(!nextPos(ref b, ref pos, prevPos)) { // No other points found from the starting point means this is a single pixel island so we can remove it b.Set(currPos, false); return null; } // While there is a next pos while(open) { if(nextPos(ref b, ref pos, ref currPos, ref prevPos)) { // b.Set(pos, false); if(currPos == startPos) open = false; // We found a closed path path.Add(new Vector2(currPos%b.x, currPos/b.x)); b.Set(currPos, false); } else { // If no next position, backtrack till we find a closed path var index = backTrack(ref b, ref path, path.Count-1); if(index!=-1) { // find next new point! path.RemoveRange(index+1, path.Count-1-index); pos = (int)path[index].x + (int)path[index].y*b.x; prevPos = (int)path[index-1].x + (int)path[index-1].y*b.x; currPos = pos; } else open = false; // If we cannot, close the path (this is the worst case and will give us a buggy collider) } } return path; }
void floodFill(ref BinaryImage b, ref List<int> i, int pos) { int w = b.x; if(b.Get(pos)) { i.Add(pos); b.Set(pos, false); } else return; if((pos%w)+1 < w) floodFill(ref b, ref i, pos+1); // Right if((pos%w)-1 >= 0) floodFill(ref b, ref i, pos-1); // Left if(pos+w < b.Length) floodFill(ref b, ref i, pos+w); // Top if(pos-w >= 0) floodFill(ref b, ref i, pos-w); // Bottom }
/** * If there is any true bits in a 3x3 grid make the centre bit false * @param {BinaryImage} ref BinaryImage b [description] * @return {BinaryImage} The erosion image */ private BinaryImage erosion(BinaryImage b) { int[,] dirs = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; BinaryImage t = new BinaryImage(b.x, b.y, true); int w = b.x; // width int h = b.y; // height for(int x=0; x<w; x++) { for(int y=0; y<h; y++) { // t.Set(x + y*w, true); for(int z=0; z<dirs.GetLength(0); z++) { int i = x+dirs[z,0], j = y+dirs[z,1]; if(i<w && i>=0 && j<h && j>=0) { if(!b.Get(i + j*w)) t.Set(x + y*w, false); } else t.Set(x + y*w, false); } } } return t; }
/** * Generates a BitArray from a Texture2D * @param {Texture2D} t [] * @return {BitArray} [description] */ private BinaryImage BinaryImageFromTex(ref Texture2D t) { BinaryImage b = new BinaryImage(t.width, t.height); Color[] data = t.GetPixels(); for(int x=0; x<b.Length; x++) b.Set(x, data[x].a > 0 ); return b; }
void split(BinaryImage b) { int startPos; // TODO: copy the binaryImage instead of setting it BinaryImage t = new BinaryImage(b.x, b.y); for (int x = 0; x < b.Length; x++) { t.Set(x, b.Get(x)); } List <List <int> > islands = new List <List <int> >(); // Find islands while (findStartPos(ref t, out startPos)) { List <int> island = new List <int>(); floodFill(ref t, ref island, startPos); islands.Add(island); } // If there is only 1 island we wont split anything if (islands.Count <= 1) { return; } // Get bounding boxes for each island for (int i = 0; i < islands.Count; i++) { int x1, y1, x2, y2; x1 = x2 = islands[i][0] % b.x; y1 = y2 = Mathf.FloorToInt((float)islands[i][0] / b.x); // Find the smallest and biggest points for (int j = 0; j < islands[i].Count; j++) { int x = islands[i][j] % b.x, y = Mathf.FloorToInt((float)islands[i][j] / b.x); if (x < x1) { x1 = x; } else if (x > x2) { x2 = x; } if (y < y1) { y1 = y; } else if (y > y2) { y2 = y; } } int w = x2 - x1, h = y2 - y1; // bounds int cx = (x2 + x1) / 2, cy = (y2 + y1) / 2; // new center for island // Create new gameobject GameObject go = new GameObject("DestructibleSpritePiece"); go.AddComponent <SpriteRenderer>(); go.AddComponent <Rigidbody2D>(); go.AddComponent <DestructibleSprite>(); go.GetComponent <DestructibleSprite>().doSplit = true; // Copy part of the original texture to our new texture Color32[] d = tex.GetPixels32(); Color32[] e = new Color32[w * h]; for (int x = 0, y = 0; x < d.Length; x++) { if (x % tex.width >= x1 && x % tex.width < x2 && Mathf.FloorToInt((float)x / tex.width) < y2 && Mathf.FloorToInt((float)x / tex.width) >= y1) { e[y] = d[x]; y++; } } // Apply to our new texture Texture2D texture = new Texture2D(w, h); texture.SetPixels32(e); texture.Apply(); // Add the spriteRenderer and apply the texture and inherit parent options SpriteRenderer s = go.GetComponent <SpriteRenderer>(); s.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); s.color = gameObject.GetComponent <SpriteRenderer>().color; s.sortingOrder = gameObject.GetComponent <SpriteRenderer>().sortingOrder; // Set the position to the new center go.transform.position = new Vector3(transform.position.x + (cx + pixelOffset) / pixelsToUnits - xBounds, transform.position.y + (cy + pixelOffset) / pixelsToUnits - yBounds, transform.position.z); // Put it in the same layer as the parent go.layer = gameObject.layer; } // We can destroy the orignal object Destroy(gameObject); }