// 根据长和宽找到房间大小比较大的 , // todo 需要限定宽高比,否则将会选择长条状 // todo random 使用seed; private static List <RoomForCircle> GetMainRoom(List <RoomForCircle> roomList, float threshold) { List <RoomForCircle> mainRoomList = new List <RoomForCircle>(); for (int i = 0; i < roomList.Count; i++) { RoomForCircle item = roomList[i]; if (item.rigidBody.Simualeted != false) { roomList.Remove(item); Destroy(item.gameObject); i--; continue; } // Debug.Log(item.size.Sum()); if (item.size.Sum() >= threshold) { if (item.sprite != null) { item.sprite.color = Color.yellow; } mainRoomList.Add(item); item.roomType = RoomType.MainRoom; } } return(mainRoomList); }
private static RoomForCircle GenerationRoom(Transform parent, Vector2 position, Vector2 size, Sprite testSprtie = null) { GameObject newGo = new GameObject("room"); newGo.transform.position = position; newGo.transform.rotation = Quaternion.identity; newGo.transform.parent = parent; RoomForCircle temp = newGo.AddComponent <RoomForCircle>(); temp.size = size; temp.boxCollider = newGo.AddComponent <BoxCollider2D>(); temp.rigidBody = newGo.AddComponent <RoomRigidBody>(); temp.sprite = newGo.AddComponent <SpriteRenderer>(); if (testSprtie != null) { temp.sprite.size = size; temp.sprite.sprite = testSprtie; temp.sprite.drawMode = SpriteDrawMode.Sliced; } return(temp); }
// todo 尝试使用异步完成 public IEnumerator GenerationRooms() { UnityEngine.Random.InitState(seed); int currentFinishCount = 0; float roomAverage = 0; //设置当前状态 isGenerating = true; for (int i = 0; i < RoomCount; i++) { // todo 将tilesize设定成参数。 // 生成房间初始位置 Vector2 pos = getRandomPointInEllipse(GenerateRadius, GenerateRadius, 1); // Debug.Log("pos : " + pos); // 房间大小可以使用随机也可以使用预制的房间 Vector2 size = getRandomSizeInCircle(RoomRadius, 2); // Debug.Log("size : " + size); roomAverage += size.Sum(); // 根据大小和初始位置生成房间 RoomForCircle temp = GenerationRoom(this.transform, pos, size, testSprtie); roomList.Add(temp); temp.transform.localScale = new Vector3(1, 1, 1); // 设置物体碰撞完成时的callback回调 temp.rigidBody.onSimualtedFinishCallback += () => { currentFinishCount++; }; //延迟 yield return(new WaitForFixedUpdate()); } // todo 可以自行调用来完成碰撞,减少随机性。可以通过随机来控制 float timer = 0; while (timer < maxWaitTime && currentFinishCount < RoomCount) { yield return(new WaitForFixedUpdate()); timer += Time.fixedDeltaTime; } // 获取主要房间 roomAverage /= roomList.Count; mainRoomList = GetMainRoom(roomList, roomAverage * mainRoomThreshold); foreach (var room in mainRoomList) { // todo 可定制的主房间,使用代理 DrawRoomInTilemap(floorTilemap, wallTilemap, floorTile, wallTile, room.transform.position.ToVector2(), room.size); } // 通过三角剖分获取主要房间之间的联通路 List <Vector2> mainRoomPos = mainRoomList.ToPosition(); List <Triangle> triangles = Polygon2D.DelaunayTriangulation(mainRoomPos); roadList = triangles.GetEdges(); foreach (var r in roadList) { Debug.DrawLine(mainRoomPos[r.indexA], mainRoomPos[r.indexB], Color.green, 1f); } // 使用最小生成树算法生成真实路径, List <Edge> mainRoadList = GetMainRoad(roadList, mainRoomPos); foreach (var r in mainRoadList) { Debug.DrawLine(mainRoomPos[r.indexA], mainRoomPos[r.indexB], Color.red, 1f); } // 算法思路: // 将两个房间确定一个矩形范围,在范围中确定一个点,用作折现点。 foreach (var road in mainRoadList) { RoomForCircle roomA = mainRoomList[road.indexA]; RoomForCircle roomB = mainRoomList[road.indexB]; // todo 可定制的path 使用代理 // 获取房间之间的路径点, 获取的路径点头和尾分别是房间A和房间B的门口 Vector2[] PathPoints = GetPathsBetweenRoom(roomA, roomB); GameObject pA = new GameObject("point0" + roomA.GetHashCode()); pA.transform.parent = this.gameObject.transform; pA.transform.position = PathPoints[0]; for (int i = 0; i < PathPoints.Length - 1; i++) { Debug.DrawLine(PathPoints[i] + new Vector2(0.5f, 0.5f), PathPoints[i + 1] + new Vector2(0.5f, 0.5f), Color.white, 1f); GameObject temp = new GameObject("point" + i); temp.transform.parent = pA.transform; temp.transform.position = PathPoints[i + 1] + new Vector2(0.5f, 0.5f); } // 对连线之间的房间进行选取,映射到tilemap中 // todo 可选的判断方法,间隔一个距离使用raycast 进行判断,减少性能消耗。 // BUG 房间内有概率出现走廊。 // BUG side房间的判断有时候有问题 for (int i = 0; i < PathPoints.Length - 1; i++) { RaycastHit2D[] result = new RaycastHit2D[mainRoadList.Count];// todo 这里的数量可以动态调整 int n = Physics2D.RaycastNonAlloc( PathPoints[i] + new Vector2(0.5f, 0.5f), (PathPoints[i + 1] - PathPoints[i]).normalized, result, Vector2.Distance(PathPoints[i + 1], PathPoints[i])); for (int j = 0; j < n; j++) { RaycastHit2D temp = result[j]; RoomForCircle room = temp.collider.gameObject.GetComponent <RoomForCircle>(); if (room.roomType == RoomType.NoneRoom) { room.roomType = RoomType.SideRoom; room.sprite.color = Color.red; // todo 可定制的side房间,使用代理 DrawRoomInTilemap(floorTilemap, wallTilemap, floorTile, wallTile, room.transform.position, room.size); } } } DrawHallwayInTilemap(floorTilemap, wallTilemap, floorTile, wallTile, PathPoints); } isGenerating = false; this.gameObject.SetActive(false); }
// 返回的是一个ve2的数组,头和尾是房间的门口 private static Vector2[] GetPathsBetweenRoom(RoomForCircle roomA, RoomForCircle roomB) { float AxorBx = UnityEngine.Random.value; List <Vector2> list = new List <Vector2>(); Vector2 TurnPoint; Vector2 pointA; Vector2 pointB; // todo 这一段看上去冗余 if (AxorBx >= 0.5f)//AXBY { Vector2 RedCenter = new Vector2(roomA.transform.position.x, roomB.transform.position.y); // -2 是防止处于墙壁边缘。 Vector2 RedSize = new Vector2(roomA.size.x - 2, roomB.size.y - 2); // 转折点 TurnPoint = getRandomPointInBox(RedSize, RedCenter); // 判断消减方向,确定门的位置 float dir = (TurnPoint.y - roomA.transform.position.y);// 缩小方向已经确定 正 + 负 - pointA = new Vector2( TurnPoint.x, roomA.transform.position.y + (((dir > 0) ? (roomA.size.y / 2) - 1 : -(roomA.size.y / 2))) ); dir = (TurnPoint.x - roomB.transform.position.x); // 正 + 负数 - pointB = new Vector2( roomB.transform.position.x + (((dir > 0) ? (roomB.size.x / 2) - 1 : -(roomB.size.x / 2))), TurnPoint.y ); } else//BXAY { Vector2 RedCenter = new Vector2(roomB.transform.position.x, roomA.transform.position.y); Vector2 RedSize = new Vector2(roomB.size.x - 2, roomA.size.y - 2); TurnPoint = getRandomPointInBox(RedSize, RedCenter); float dir = (TurnPoint.x - roomA.transform.position.x); pointA = new Vector2(roomA.transform.position.x + (((dir > 0) ? (roomA.size.x / 2) - 1 : -(roomA.size.x / 2))), TurnPoint.y); dir = (TurnPoint.y - roomB.transform.position.y); // 正 + 负数 - pointB = new Vector2(TurnPoint.x, roomB.transform.position.y + (((dir > 0) ? (roomB.size.y / 2) - 1 : -(roomB.size.y / 2)))); } // 如果转折点在房间中 if (CheckInBox(TurnPoint, roomA.transform.position, roomA.size - new Vector2(2, 2))) { Vector2 dir = (pointB - TurnPoint); if (dir.x != 0) { float offset = roomA.size.x / 2 - ((dir.x > 0) ?1:-1) * (TurnPoint.x - roomA.transform.position.x); TurnPoint += ((dir.x > 0) ? offset - 1 : -offset) * Vector2.right; } else { float offset = roomA.size.y / 2 - ((dir.y > 0) ?1:-1) * (TurnPoint.y - roomA.transform.position.y); TurnPoint += ((dir.y > 0) ? offset - 1 : -offset) * Vector2.up; } list.Add(TurnPoint); list.Add(pointB); } else if (CheckInBox(TurnPoint, roomB.transform.position, roomB.size - new Vector2(2, 2))) { Vector2 dir = (pointA - TurnPoint); if (dir.x != 0) { float offset = roomB.size.x / 2 - ((dir.x > 0) ?1:-1) * (TurnPoint.x - roomB.transform.position.x); TurnPoint += ((dir.x > 0) ? offset - 1 : -offset) * Vector2.right; } else { float offset = roomB.size.y / 2 - ((dir.y > 0) ?1:-1) * (TurnPoint.y - roomB.transform.position.y); TurnPoint += ((dir.y > 0) ? offset - 1 : -offset) * Vector2.up; } list.Add(pointA); list.Add(TurnPoint); } else// 转折点在房间外 { list.Add(pointA); list.Add(TurnPoint); list.Add(pointB); } return(list.ToArray()); }