コード例 #1
0
ファイル: TilemapCollider.cs プロジェクト: zhangf911/duality
        private static void GenerateCollisionShapes(TileEdgeMap edgeMap, Vector2 origin, Vector2 tileSize, bool roundedCorners, IList <ShapeInfo> shapeList)
        {
            // Traverse the edge map and gradually create chain / loop
            // shapes until all edges have been used.
            RawList <Point2>  currentChain = new RawList <Point2>();
            RawList <Vector2> vertexBuffer = new RawList <Vector2>();

            while (true)
            {
                // Begin a new continuous chain of nodes
                currentChain.Clear();

                // Find a starting node for our current chain.
                // If there is none, we found and handled all edges.
                Point2 start = edgeMap.FindNonEmpty();
                if (start == new Point2(-1, -1))
                {
                    break;
                }

                // Traverse the current chain node-by-node from the start we found
                Point2 current = start;
                while (true)
                {
                    // Add the current node to our continuous chain
                    currentChain.Add(current);

                    // Find the next node that connects to the current one.
                    // If there is none, our current chain is done.
                    Point2 next = edgeMap.GetClockwiseNextFrom(current);
                    if (next == new Point2(-1, -1))
                    {
                        break;
                    }

                    // Remove the edge we used to get to the next node
                    edgeMap.RemoveEdge(current, next);

                    // Use the next node as origin for traversing further
                    current = next;
                }

                // Generate a shape from the current chain
                bool isLoop = (start == currentChain[currentChain.Count - 1]);
                if (isLoop)
                {
                    currentChain.RemoveAt(currentChain.Count - 1);
                }
                vertexBuffer.Clear();

                // Rounded corners
                if (roundedCorners && currentChain.Count >= 3)
                {
                    vertexBuffer.Reserve(currentChain.Count * 2);
                    vertexBuffer.Count = 0;
                    for (int i = 0; i < currentChain.Count; i++)
                    {
                        int prevIndex = (i - 1 + currentChain.Count) % currentChain.Count;
                        int nextIndex = (i + 1) % currentChain.Count;

                        Vector2 currentVert = origin + tileSize * (Vector2)currentChain[i];
                        Vector2 prevVert    = origin + tileSize * (Vector2)currentChain[prevIndex];
                        Vector2 nextVert    = origin + tileSize * (Vector2)currentChain[nextIndex];

                        if (nextVert - currentVert != currentVert - prevVert)
                        {
                            if (!isLoop && (i == 0 || i == currentChain.Count - 1))
                            {
                                vertexBuffer.Add(currentVert);
                            }
                            else
                            {
                                vertexBuffer.Add(currentVert + (prevVert - currentVert).Normalized * tileSize * 0.2f);
                                vertexBuffer.Add(currentVert + (nextVert - currentVert).Normalized * tileSize * 0.2f);
                            }
                        }
                    }
                }
                // Sharp corners
                else
                {
                    vertexBuffer.Reserve(currentChain.Count);
                    vertexBuffer.Count = 0;
                    for (int i = 0; i < currentChain.Count; i++)
                    {
                        int prevIndex = (i - 1 + currentChain.Count) % currentChain.Count;
                        int nextIndex = (i + 1) % currentChain.Count;

                        Vector2 currentVert = origin + tileSize * (Vector2)currentChain[i];
                        Vector2 prevVert    = origin + tileSize * (Vector2)currentChain[prevIndex];
                        Vector2 nextVert    = origin + tileSize * (Vector2)currentChain[nextIndex];

                        if (nextVert - currentVert != currentVert - prevVert)
                        {
                            vertexBuffer.Add(currentVert);
                        }
                    }
                }

                Vector2[] vertices = new Vector2[vertexBuffer.Count];
                vertexBuffer.CopyTo(vertices, 0);
                shapeList.Add(isLoop ?
                              (ShapeInfo) new LoopShapeInfo(vertices) :
                              (ShapeInfo) new ChainShapeInfo(vertices));
            }
        }
コード例 #2
0
ファイル: TilemapCollider.cs プロジェクト: zhangf911/duality
        private static void AddBlockCollisionEdges(Grid <TileCollisionShape> collisionData, TileEdgeMap targetEdgeMap, Point2 edgeMapPos, Point2 totalSize)
        {
            int leftBorderPos   = 0 - edgeMapPos.X;
            int rightBorderPos  = totalSize.X - edgeMapPos.X;
            int topBorderPos    = 0 - edgeMapPos.Y;
            int bottomBorderPos = totalSize.Y - edgeMapPos.Y;

            // Add block geometry to the specified edge map
            for (int y = 0; y < SectorSize; y++)
            {
                for (int x = 0; x < SectorSize; x++)
                {
                    // Skip non-solid blocks
                    bool center = (collisionData[x, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    if (!center)
                    {
                        continue;
                    }

                    // A filled block will always overwrite its inner diagonal edges
                    targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y + 1));
                    targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y));

                    // Determine block collision neighbourhood
                    bool left   = (x == 0)              ? (x == leftBorderPos) : (collisionData[x - 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool right  = (x == SectorSize - 1) ? (x == rightBorderPos) : (collisionData[x + 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool top    = (y == 0)              ? (y == topBorderPos) : (collisionData[x, y - 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool bottom = (y == SectorSize - 1) ? (y == bottomBorderPos) : (collisionData[x, y + 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid;

                    // Adjust outer edge states
                    if (center != left)
                    {
                        targetEdgeMap.AddEdge(new Point2(x, y), new Point2(x, y + 1));
                    }
                    else
                    {
                        targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x, y + 1));
                    }
                    if (center != right)
                    {
                        targetEdgeMap.AddEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1));
                    }
                    else
                    {
                        targetEdgeMap.RemoveEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1));
                    }
                    if (center != top)
                    {
                        targetEdgeMap.AddEdge(new Point2(x, y), new Point2(x + 1, y));
                    }
                    else
                    {
                        targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y));
                    }
                    if (center != bottom)
                    {
                        targetEdgeMap.AddEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1));
                    }
                    else
                    {
                        targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1));
                    }
                }
            }

            // Detect diagonal fences next to solid blocks and remove the
            // edges that might have become redundant. This can't be done
            // in the above loop without complicating control flow, so it's
            // done here.
            for (int y = 0; y < SectorSize; y++)
            {
                for (int x = 0; x < SectorSize; x++)
                {
                    TileCollisionShape centerShape = collisionData[x, y];
                    bool diagonalDown = (centerShape & TileCollisionShape.DiagonalDown) == TileCollisionShape.DiagonalDown;
                    bool diagonalUp   = (centerShape & TileCollisionShape.DiagonalUp) == TileCollisionShape.DiagonalUp;

                    // Skip tiles that aren't diagonal fences
                    if (!diagonalDown && !diagonalUp)
                    {
                        continue;
                    }

                    // Determine block collision neighbourhood
                    bool left   = (x == 0)              ? (x == leftBorderPos) : (collisionData[x - 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool right  = (x == SectorSize - 1) ? (x == rightBorderPos) : (collisionData[x + 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool top    = (y == 0)              ? (y == topBorderPos) : (collisionData[x, y - 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool bottom = (y == SectorSize - 1) ? (y == bottomBorderPos) : (collisionData[x, y + 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid;

                    // Remove perpendicular edges that are redundant because of the diagonal fence
                    // connecting two adjacent solid blocks.
                    if (diagonalDown)
                    {
                        if (top && right)
                        {
                            targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y));
                            targetEdgeMap.RemoveEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1));
                        }
                        if (bottom && left)
                        {
                            targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1));
                            targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x, y + 1));
                        }
                    }
                    else
                    {
                        if (top && left)
                        {
                            targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y));
                            targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x, y + 1));
                        }
                        if (bottom && right)
                        {
                            targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1));
                            targetEdgeMap.RemoveEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1));
                        }
                    }
                }
            }
        }
コード例 #3
0
ファイル: TilemapCollider.cs プロジェクト: SirePi/duality
        private static void GenerateCollisionShapes(TileEdgeMap edgeMap, Vector2 origin, Vector2 tileSize, bool roundedCorners, IList<ShapeInfo> shapeList)
        {
            // Traverse the edge map and gradually create chain / loop
            // shapes until all edges have been used.
            RawList<Point2> currentChain = new RawList<Point2>();
            RawList<Vector2> vertexBuffer = new RawList<Vector2>();
            while (true)
            {
                // Begin a new continuous chain of nodes
                currentChain.Clear();

                // Find a starting node for our current chain.
                // If there is none, we found and handled all edges.
                Point2 start = edgeMap.FindNonEmpty();
                if (start == new Point2(-1, -1))
                    break;

                // Traverse the current chain node-by-node from the start we found
                Point2 current = start;
                while (true)
                {
                    // Add the current node to our continuous chain
                    currentChain.Add(current);

                    // Find the next node that connects to the current one.
                    // If there is none, our current chain is done.
                    Point2 next = edgeMap.GetClockwiseNextFrom(current);
                    if (next == new Point2(-1, -1))
                        break;

                    // Remove the edge we used to get to the next node
                    edgeMap.RemoveEdge(current, next);

                    // Use the next node as origin for traversing further
                    current = next;
                }

                // Generate a shape from the current chain
                bool isLoop = (start == currentChain[currentChain.Count - 1]);
                if (isLoop) currentChain.RemoveAt(currentChain.Count - 1);
                vertexBuffer.Clear();

                // Rounded corners
                if (roundedCorners && currentChain.Count >= 3)
                {
                    vertexBuffer.Reserve(currentChain.Count * 2);
                    vertexBuffer.Count = 0;
                    for (int i = 0; i < currentChain.Count; i++)
                    {
                        int prevIndex = (i - 1 + currentChain.Count) % currentChain.Count;
                        int nextIndex = (i + 1) % currentChain.Count;

                        Vector2 currentVert = origin + tileSize * (Vector2)currentChain[i];
                        Vector2 prevVert = origin + tileSize * (Vector2)currentChain[prevIndex];
                        Vector2 nextVert = origin + tileSize * (Vector2)currentChain[nextIndex];

                        if (nextVert - currentVert != currentVert - prevVert)
                        {
                            if (!isLoop && (i == 0 || i == currentChain.Count - 1))
                            {
                                vertexBuffer.Add(currentVert);
                            }
                            else
                            {
                                vertexBuffer.Add(currentVert + (prevVert - currentVert).Normalized * tileSize * 0.2f);
                                vertexBuffer.Add(currentVert + (nextVert - currentVert).Normalized * tileSize * 0.2f);
                            }
                        }
                    }
                }
                // Sharp corners
                else
                {
                    vertexBuffer.Reserve(currentChain.Count);
                    vertexBuffer.Count = 0;
                    for (int i = 0; i < currentChain.Count; i++)
                    {
                        int prevIndex = (i - 1 + currentChain.Count) % currentChain.Count;
                        int nextIndex = (i + 1) % currentChain.Count;

                        Vector2 currentVert = origin + tileSize * (Vector2)currentChain[i];
                        Vector2 prevVert = origin + tileSize * (Vector2)currentChain[prevIndex];
                        Vector2 nextVert = origin + tileSize * (Vector2)currentChain[nextIndex];

                        if (nextVert - currentVert != currentVert - prevVert)
                            vertexBuffer.Add(currentVert);
                    }
                }
                shapeList.Add(isLoop ?
                    (ShapeInfo)new LoopShapeInfo(vertexBuffer) :
                    (ShapeInfo)new ChainShapeInfo(vertexBuffer));
            }
        }
コード例 #4
0
        private static void GetTileAreaOutlines(IReadOnlyGrid<bool> tileArea, Vector2 tileSize, ref List<Vector2[]> outlines)
        {
            // Initialize the container we'll put our outlines into
            if (outlines == null)
                outlines = new List<Vector2[]>();
            else
                outlines.Clear();

            // Generate a data structure containing all visible edges
            TileEdgeMap edgeMap = new TileEdgeMap(tileArea.Width + 1, tileArea.Height + 1);
            for (int y = 0; y < edgeMap.Height; y++)
            {
                for (int x = 0; x < edgeMap.Width; x++)
                {
                    // Determine highlight state of the four tiles around this node
                    bool topLeft     = x > 0              && y > 0               && tileArea[x - 1, y - 1];
                    bool topRight    = x < tileArea.Width && y > 0               && tileArea[x    , y - 1];
                    bool bottomLeft  = x > 0              && y < tileArea.Height && tileArea[x - 1, y    ];
                    bool bottomRight = x < tileArea.Width && y < tileArea.Height && tileArea[x    , y    ];

                    // Determine which edges are visible
                    if (topLeft     != topRight   ) edgeMap.AddEdge(new Point2(x, y), new Point2(x    , y - 1));
                    if (topRight    != bottomRight) edgeMap.AddEdge(new Point2(x, y), new Point2(x + 1, y    ));
                    if (bottomRight != bottomLeft ) edgeMap.AddEdge(new Point2(x, y), new Point2(x    , y + 1));
                    if (bottomLeft  != topLeft    ) edgeMap.AddEdge(new Point2(x, y), new Point2(x - 1, y    ));
                }
            }

            // Traverse edges to form outlines until no more edges are left
            RawList<Vector2> outlineBuilder = new RawList<Vector2>();
            while (true)
            {
                // Find the beginning of an outline
                Point2 current = edgeMap.FindNonEmpty();
                if (current.X == -1 || current.Y == -1) break;

                // Traverse it until no more edges are left
                while (true)
                {
                    Point2 next = edgeMap.GetClockwiseNextFrom(current);
                    if (next.X == -1 || next.Y == -1) break;

                    outlineBuilder.Add(next * tileSize);
                    edgeMap.RemoveEdge(current, next);
                    current = next;
                }

                // Close the loop by adding the first element again
                if (outlineBuilder.Count > 0)
                    outlineBuilder.Add(outlineBuilder[0]);

                // If we have enough vertices, keep the outline for drawing
                Vector2[] outline = new Vector2[outlineBuilder.Count];
                outlineBuilder.CopyTo(outline, 0);
                outlines.Add(outline);

                // Reset the outline builder to an empty state
                outlineBuilder.Clear();
            }
        }
コード例 #5
0
ファイル: TilemapCollider.cs プロジェクト: SirePi/duality
        private static void AddBlockCollisionEdges(Grid<TileCollisionShape> collisionData, TileEdgeMap targetEdgeMap, Point2 edgeMapPos, Point2 totalSize)
        {
            int leftBorderPos = 0 - edgeMapPos.X;
            int rightBorderPos = totalSize.X - edgeMapPos.X;
            int topBorderPos = 0 - edgeMapPos.Y;
            int bottomBorderPos = totalSize.Y - edgeMapPos.Y;

            // Add block geometry to the specified edge map
            for (int y = 0; y < SectorSize; y++)
            {
                for (int x = 0; x < SectorSize; x++)
                {
                    // Skip non-solid blocks
                    bool center = (collisionData[x, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    if (!center) continue;

                    // A filled block will always overwrite its inner diagonal edges
                    targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y + 1));
                    targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y));

                    // Determine block collision neighbourhood
                    bool left   = (x == 0)              ? (x == leftBorderPos  ) : (collisionData[x - 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool right  = (x == SectorSize - 1) ? (x == rightBorderPos ) : (collisionData[x + 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool top    = (y == 0)              ? (y == topBorderPos   ) : (collisionData[x, y - 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool bottom = (y == SectorSize - 1) ? (y == bottomBorderPos) : (collisionData[x, y + 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid;

                    // Adjust outer edge states
                    if (center != left )  targetEdgeMap.AddEdge   (new Point2(x, y), new Point2(x, y + 1));
                    else                  targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x, y + 1));
                    if (center != right)  targetEdgeMap.AddEdge   (new Point2(x + 1, y), new Point2(x + 1, y + 1));
                    else                  targetEdgeMap.RemoveEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1));
                    if (center != top)    targetEdgeMap.AddEdge   (new Point2(x, y), new Point2(x + 1, y));
                    else                  targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y));
                    if (center != bottom) targetEdgeMap.AddEdge   (new Point2(x, y + 1), new Point2(x + 1, y + 1));
                    else                  targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1));
                }
            }

            // Detect diagonal fences next to solid blocks and remove the
            // edges that might have become redundant. This can't be done
            // in the above loop without complicating control flow, so it's
            // done here.
            for (int y = 0; y < SectorSize; y++)
            {
                for (int x = 0; x < SectorSize; x++)
                {
                    TileCollisionShape centerShape = collisionData[x, y];
                    bool diagonalDown = (centerShape & TileCollisionShape.DiagonalDown) == TileCollisionShape.DiagonalDown;
                    bool diagonalUp = (centerShape & TileCollisionShape.DiagonalUp) == TileCollisionShape.DiagonalUp;

                    // Skip tiles that aren't diagonal fences
                    if (!diagonalDown && !diagonalUp) continue;

                    // Determine block collision neighbourhood
                    bool left   = (x == 0)              ? (x == leftBorderPos  ) : (collisionData[x - 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool right  = (x == SectorSize - 1) ? (x == rightBorderPos ) : (collisionData[x + 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool top    = (y == 0)              ? (y == topBorderPos   ) : (collisionData[x, y - 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid;
                    bool bottom = (y == SectorSize - 1) ? (y == bottomBorderPos) : (collisionData[x, y + 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid;

                    // Remove perpendicular edges that are redundant because of the diagonal fence
                    // connecting two adjacent solid blocks.
                    if (diagonalDown)
                    {
                        if (top && right)
                        {
                            targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y));
                            targetEdgeMap.RemoveEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1));
                        }
                        if (bottom && left)
                        {
                            targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1));
                            targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x, y + 1));
                        }
                    }
                    else
                    {
                        if (top && left)
                        {
                            targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y));
                            targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x, y + 1));
                        }
                        if (bottom && right)
                        {
                            targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1));
                            targetEdgeMap.RemoveEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1));
                        }
                    }
                }
            }
        }