/// <summary> /// 壁コリジョンを生成するための部屋の縁を求める /// 縁は幅1の矩形の集合として返す /// </summary> /// <returns></returns> public IEnumerable<Rectangle> GetOutlines() { var result = new List<Rectangle>(); var leftX = LeftTopCoord.X - 1; var rightX = LeftTopCoord.X + Width.X; var topY = LeftTopCoord.Y - 1; var bottomY = LeftTopCoord.Y + Width.Y; // 部屋の各辺にポータルがあるか // left, right, top bottom の順 bool[] edgeHasPortal = { false, false, false, false }; foreach (var portal in Portals) { if (portal.ConnectedPaths.Count == 0) { continue; } var pathWidth = portal.ConnectedPaths[0].Width; if (portal.Position.X == leftX || portal.Position.X == rightX) { // ポータルが部屋の左右にある場合 if (portal.Position.X == leftX) { edgeHasPortal[0] = true; } else if (portal.Position.X == rightX) { edgeHasPortal[1] = true; } var outline0 = new Rectangle( portal.Position.X, LeftTopCoord.Y, 1, portal.Position.Y - LeftTopCoord.Y - pathWidth / 2); result.Add(outline0); result.Add(new Rectangle( portal.Position.X, portal.Position.Y + 1 + pathWidth / 2, 1, Width.Y - outline0.Height - pathWidth)); } else if (portal.Position.Y == topY || portal.Position.Y == bottomY) { // ポータルが部屋の上下にある場合 if (portal.Position.Y == topY) { edgeHasPortal[2] = true; } else if (portal.Position.Y == bottomY) { edgeHasPortal[3] = true; } var outline0 = new Rectangle( LeftTopCoord.X, portal.Position.Y, portal.Position.X - LeftTopCoord.X - pathWidth / 2, 1); result.Add(outline0); result.Add(new Rectangle( portal.Position.X + 1 + pathWidth / 2, portal.Position.Y, Width.X - outline0.Width - pathWidth, 1)); } } // ポータルがない辺の縁を生成 // 左辺 if (!edgeHasPortal[0]) { result.Add(new Rectangle(leftX, topY + 1, 1, Width.Y)); } // 右辺 if (!edgeHasPortal[1]) { result.Add(new Rectangle(leftX + Width.X + 1, topY + 1, 1, Width.Y)); } // 上辺 if (!edgeHasPortal[2]) { result.Add(new Rectangle(leftX + 1, topY, Width.X, 1)); } // 下辺 if (!edgeHasPortal[3]) { result.Add(new Rectangle(leftX + 1, topY + Width.Y + 1, Width.X, 1)); } return result; }
void CutPortalOutlines(ref Rectangle[] outlines, int cut, int direction) { for (var i = 0; i < 2; ++i) { var connectedPortal = Portals[i]; var realcut = cut; // 通路同士のポータルの場合、幅は広い方になるので余分に削る if (connectedPortal.ConnectedPaths.Count == 2) { var maxWidth = MathUtil.Max( connectedPortal.ConnectedPaths[0].Width, connectedPortal.ConnectedPaths[1].Width); // 向こうの通路がこちらより広ければ if (maxWidth > Width) { realcut += (maxWidth - Width) / 2; } } // 水平通路で if (direction == 0) { // 左がポータルなら if (outlines[0].X == connectedPortal.Position.X) { outlines[0].X += realcut; outlines[1].X += realcut; outlines[0].Width -= realcut; outlines[1].Width -= realcut; } // 右がポータルなら if (outlines[0].X + outlines[0].Width == connectedPortal.Position.X) { outlines[0].Width -= realcut - 1; outlines[1].Width -= realcut - 1; } } // 垂直通路で else if (direction == 1) { // 上がポータルなら if (outlines[0].Y == connectedPortal.Position.Y) { outlines[0].Y += realcut; outlines[1].Y += realcut; outlines[0].Height -= realcut; outlines[1].Height -= realcut; } // 下がポータルなら if (outlines[0].Y + outlines[0].Height == connectedPortal.Position.Y) { outlines[0].Height -= realcut - 1; outlines[1].Height -= realcut - 1; } } } }
/// <summary> /// 壁コリジョン生成のための通路の縁を計算する /// </summary> /// <returns></returns> public IEnumerable<Rectangle> GetOutlines() { var result = new List<Rectangle>(); // 現在地点 var position = new int[2] { Portals[0].Position.X, Portals[0].Position.Y }; // 進む方向(水平か垂直か) var direction = IsHorizontal ? 0 : 1; // 1回に進む歩数(正か負か) var stepValue = new int[2] { Math.Sign(Portals[1].Position.X - Portals[0].Position.X), Math.Sign(Portals[1].Position.Y - Portals[0].Position.Y) }; for (var i = 0; i < steps.Count; ++i) { var start = new Point(position[0], position[1]); position[direction] += stepValue[direction] * steps[i]; var end = new Point(position[0], position[1]); var outlines = new Rectangle[2]; // まずstepの長さの壁を作る if (direction == 0) { // 上側の壁 outlines[0].X = MathUtil.Min(start.X, end.X); outlines[0].Y = start.Y - (Width / 2) - 1; outlines[0].Width = steps[i]; outlines[0].Height = 1; // 下側の壁 outlines[1].X = MathUtil.Min(start.X, end.X); outlines[1].Y = start.Y + (Width / 2) + 1; outlines[1].Width = steps[i]; outlines[1].Height = 1; } else if (direction == 1) { // 左側の壁 outlines[0].X = start.X - (Width / 2) - 1; outlines[0].Y = MathUtil.Min(start.Y, end.Y); outlines[0].Width = 1; outlines[0].Height = steps[i]; // 右側の壁 outlines[1].X = start.X + (Width / 2) + 1; outlines[1].Y = MathUtil.Min(start.Y, end.Y); outlines[1].Width = 1; outlines[1].Height = steps[i]; } // 曲がり角の内側の壁を削って外側の壁を伸ばす // 曲がり角の向きと進行方向によって8パターンある // 削る量 var cut = (Width / 2) + 1; // 伸ばす量 var add = (Width / 2) + 1; // 最初の通路でなければ始点をいじる if (i > 0) { // 水平通路で if (direction == 0) { // 前の通路が下から上で if (stepValue[1 - direction] < 0) { // この通路が右から左なら if (stepValue[direction] < 0) { // 下側の壁の右を削る // 上側の壁の右を伸ばす } // この通路が左から右なら else { // 下側の壁の左を削る outlines[1].X += cut; // 上側の壁の左を伸ばす outlines[0].X -= add; } outlines[0].Width += add; outlines[1].Width -= cut; } // 前の通路が上から下で else { // この通路が右から左なら if (stepValue[direction] < 0) { // 上側の壁の右を削る // 下側の壁の右を伸ばす } // この通路が左から右なら else { // 上側の壁の左を削る outlines[0].X += cut; // 下側の壁の左を伸ばす outlines[1].X -= add; } outlines[0].Width -= cut; outlines[1].Width += add; } } // 垂直通路で else if (direction == 1) { // 前の通路が右から左で if (stepValue[1 - direction] < 0) { // この通路が下から上なら if (stepValue[direction] < 0) { // 右側の壁の下を削る // 左側の壁の下を伸ばす } // この通路が上から下なら else { // 右側の壁の上を削る outlines[1].Y += cut; // 左側の壁の上を伸ばす outlines[0].Y -= add; } outlines[0].Height += add; outlines[1].Height -= cut; } // 前の通路が左から右で else { // この通路が下から上なら if (stepValue[direction] < 0) { // 左側の壁の下を削る // 右側の壁の下を伸ばす } // この通路が上から下なら else { // 左側の壁の上を削る outlines[0].Y += cut; // 右側の壁の上を伸ばす outlines[1].Y -= add; } outlines[0].Height -= cut; outlines[1].Height += add; } } } // 最初のstepならポータル部分を削る else { CutPortalOutlines(ref outlines, cut, direction); } // 最後の通路でなければ終点をいじる if (i < steps.Count - 1) { // 水平通路で if (direction == 0) { // 次の通路が下から上で if (stepValue[1 - direction] < 0) { // この通路が右から左なら if (stepValue[direction] < 0) { // 上側の壁の左を削る outlines[0].X += cut; // 下側の壁の左を伸ばす outlines[1].X -= add; } // この通路が左から右なら else { // 上側の壁の右を削る // 下側の壁の右を伸ばす } outlines[0].Width -= cut; outlines[1].Width += add; } // 次の通路が上から下で else { // この通路が右から左なら if (stepValue[direction] < 0) { // 下側の壁の左を削る outlines[1].X += cut; // 上側の壁の左を伸ばす outlines[0].X -= add; } // この通路が左から右なら else { // 下側の壁の右を削る // 上側の壁の右を伸ばす } outlines[0].Width += add; outlines[1].Width -= cut; } } // 垂直通路で else if (direction == 1) { // 次の通路が右から左で if (stepValue[1 - direction] < 0) { // この通路が下から上なら if (stepValue[direction] < 0) { // 左側の壁の上を削る outlines[0].Y += cut; // 右側の壁の上を伸ばす outlines[1].Y -= add; } // この通路が上から下なら else { // 左側の壁の下を削る // 右側の壁の下を伸ばす } outlines[0].Height -= cut; outlines[1].Height += add; } // 次の通路が左から右で else { // この通路が下から上なら if (stepValue[direction] < 0) { // 右側の壁の上を削る outlines[1].Y += cut; // 左側の壁の上の伸ばす outlines[0].Y -= add; } // この通路が上から下なら else { // 右側の壁の下を削る // 左側の壁の下を伸ばす } outlines[0].Height += add; outlines[1].Height -= cut; } } } // 最後のstepならポータル部分を削る else { CutPortalOutlines(ref outlines, cut, direction); } result.AddRange(outlines); direction = 1 - direction; } return result; }
Rectangle CalcStepRect2(Point start, Point end, bool horizontal) { // 通路幅の半分だけ幅を広げる // 幅3の通路なら1マス広げる var additional = Width / 2; // 曲がり角部分は削る // 幅3の通路なら2マス削る var cut = additional + 1; // 水平通路ならx方向を削る // 垂直通路ならy方向を削る var x = Math.Min(start.X, end.X) + (horizontal ? cut : -additional); var y = Math.Min(start.Y, end.Y) + (horizontal ? -additional : cut); var w = Math.Abs(end.X - start.X) + 1; w = horizontal ? (w - cut * 2) : Width; var h = Math.Abs(end.Y - start.Y) + 1; h = horizontal ? Width : (h - cut * 2); var result = new Rectangle(x, y, w, h); return result; }
/// <summary> /// 通路の曲がり角間の線分一つの矩形(きゅーぶ単位)を返す /// </summary> Rectangle CalcStepRect(Point start, Point end) { // 通路幅の半分だけ前後左右を伸ばす var additional = Width / 2; var result = new Rectangle( Math.Min(start.X, end.X) - additional, Math.Min(start.Y, end.Y) - additional, Math.Abs(end.X - start.X) + 1 + additional * 2, Math.Abs(end.Y - start.Y) + 1 + additional * 2); return result; }
/// <summary> /// 壁コリジョン生成用の縁を計算する /// </summary> /// <returns></returns> public IEnumerable<Rectangle> GetOutlines() { var result = new List<Rectangle>(); // 部屋につながっている if (ConnectedRoom != null) { global::System.Diagnostics.Debug.Assert(ConnectedPaths.Count == 1); var pathWidthHalfPlusOne = ConnectedPaths[0].Width / 2 + 1; var connectedRoomSide = GetConnectedRoomSide(); // 部屋の左辺 if (connectedRoomSide == 0) { result.Add(new Rectangle( Position.X - pathWidthHalfPlusOne + 1, Position.Y - pathWidthHalfPlusOne, pathWidthHalfPlusOne, 1)); result.Add(new Rectangle( Position.X - pathWidthHalfPlusOne + 1, Position.Y + pathWidthHalfPlusOne, pathWidthHalfPlusOne, 1)); } // 部屋の右辺 else if (connectedRoomSide == 1) { result.Add(new Rectangle( Position.X, Position.Y - pathWidthHalfPlusOne, pathWidthHalfPlusOne, 1)); result.Add(new Rectangle( Position.X, Position.Y + pathWidthHalfPlusOne, pathWidthHalfPlusOne, 1)); } // 部屋の上辺 else if (connectedRoomSide == 2) { result.Add(new Rectangle( Position.X - pathWidthHalfPlusOne, Position.Y - pathWidthHalfPlusOne + 1, 1, pathWidthHalfPlusOne)); result.Add(new Rectangle( Position.X + pathWidthHalfPlusOne, Position.Y - pathWidthHalfPlusOne + 1, 1, pathWidthHalfPlusOne)); } // 部屋の下辺 else if (connectedRoomSide == 3) { result.Add(new Rectangle( Position.X - pathWidthHalfPlusOne, Position.Y, 1, pathWidthHalfPlusOne)); result.Add(new Rectangle( Position.X + pathWidthHalfPlusOne, Position.Y, 1, pathWidthHalfPlusOne)); } } // 2本の通路につながっている else if (ConnectedPaths.Count == 2) { int[] pathWidth = { ConnectedPaths[0].Width, ConnectedPaths[1].Width }; int[] pathWidthHalf = { pathWidth[0] / 2, pathWidth[1] / 2 }; var maxWidth = MathUtil.Max(pathWidth[0], pathWidth[1]); var minWidth = MathUtil.Min(pathWidth[0], pathWidth[1]); // とりあえず全方位の壁を作る CreateAllOutlines(result, maxWidth, MathUtil.Max(pathWidthHalf[0], pathWidthHalf[1])); var removeList = new List<Rectangle>(); for (var i = 0; i < 2; ++i) { var connectedPathSide = GetConnectedPathSide(ConnectedPaths[i]); removeList.Add(result[connectedPathSide]); // 広い通路の方は何も追加せず if (pathWidth[i] == maxWidth) { } // 狭い通路の方は穴を開けて二分割した壁を追加 else if (maxWidth > minWidth && pathWidth[i] == minWidth) { if (connectedPathSide == 0 || connectedPathSide == 1) { var outline0 = new Rectangle( result[connectedPathSide].X, result[connectedPathSide].Y, 1, (maxWidth - minWidth) / 2); result.Add(outline0); result.Add(new Rectangle( outline0.X, outline0.Y + outline0.Height + minWidth, 1, (maxWidth - minWidth) / 2)); } else if (connectedPathSide == 2 || connectedPathSide == 3) { var outline0 = new Rectangle( result[connectedPathSide].X, result[connectedPathSide].Y, (maxWidth - minWidth) / 2, 1); result.Add(outline0); result.Add(new Rectangle( outline0.X + outline0.Width + minWidth, outline0.Y, (maxWidth - minWidth) / 2, 1)); } } } // 元の壁を消す removeList.ForEach((rect) => { result.Remove(rect); }); } // 1本の通路につながっている(袋小路) else if (ConnectedPaths.Count == 1) { var pathWidth = ConnectedPaths[0].Width; var pathWidthHalf = pathWidth / 2; // とりあえず全方位の壁を作る CreateAllOutlines(result, pathWidth, pathWidthHalf); var connectedPathSide = GetConnectedPathSide(ConnectedPaths[0]); // 通路がある方の壁を消去 result.RemoveAt(connectedPathSide); } return result; }