Algorithms for adjusting room placement.

调整房间放置的算法。
This commit is contained in:
Cold-Mint 2024-05-22 22:39:41 +08:00
parent 544c5303f3
commit 16a2a1d551
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
6 changed files with 191 additions and 144 deletions

View File

@ -72,3 +72,6 @@ script = ExtResource("5_dis4v")
[node name="DamageNumberContainer" type="Node2D" parent="."] [node name="DamageNumberContainer" type="Node2D" parent="."]
[node name="WeaponContainer" type="Node2D" parent="."] [node name="WeaponContainer" type="Node2D" parent="."]
[node name="Camera2D" type="Camera2D" parent="."]
zoom = Vector2(0.5, 0.5)

View File

@ -34,7 +34,10 @@ public partial class GameSceneLoader : SceneLoaderTemplate
MapGenerator.LayoutStrategy = new TestLayoutStrategy(); MapGenerator.LayoutStrategy = new TestLayoutStrategy();
MapGenerator.LayoutParsingStrategy = new SequenceLayoutParsingStrategy(); MapGenerator.LayoutParsingStrategy = new SequenceLayoutParsingStrategy();
MapGenerator.RoomPlacementStrategy = new PatchworkRoomPlacementStrategy(); MapGenerator.RoomPlacementStrategy = new PatchworkRoomPlacementStrategy();
MapGenerator.Seed = GuidUtils.GetGuid(); //Test the seeds used
//2531276826 Right-Down和Left-Down匹配成功
//4208831693 Left-Down和Right-Up匹配成功
MapGenerator.Seed = "2531276826";
await MapGenerator.GenerateMap(); await MapGenerator.GenerateMap();
} }
} }

View File

@ -1,34 +0,0 @@
using System.Threading.Tasks;
using ColdMint.scripts.map.dateBean;
using ColdMint.scripts.map.room;
namespace ColdMint.scripts.map.interfaces;
/// <summary>
/// <para>IRoomSlotsMatcher</para>
/// <para>房间插槽匹配器</para>
/// </summary>
public interface IRoomSlotsMatcher
{
/// <summary>
/// <para>Whether the slots of the two rooms can be matched</para>
/// <para>两个房间的插槽是否可匹配</para>
/// </summary>
/// <param name="mainRoom"></param>
/// <param name="newRoom"></param>
/// <returns></returns>
Task<bool> IsMatch(Room? mainRoom, Room newRoom);
/// <summary>
/// <para>LastMatchedMainSlot</para>
/// <para>最后匹配的主要插槽</para>
/// </summary>
RoomSlot? LastMatchedMainSlot { get; }
/// <summary>
/// <para>LastMatchedMinorSlot</para>
/// <para>最后匹配的次要插槽</para>
/// </summary>
RoomSlot? LastMatchedMinorSlot { get; }
}

View File

@ -85,7 +85,7 @@ public class Room
var midpoint = roomAreaCollisionShape2D.Position + roomAreaRect2.Position + roomAreaRect2.Size / 2; var midpoint = roomAreaCollisionShape2D.Position + roomAreaRect2.Position + roomAreaRect2.Size / 2;
//endregion //endregion
var roomSlots = new List<RoomSlot>(); var roomSlots = new List<RoomSlot>();
for (int i = 0; i < slotCount; i++) for (var i = 0; i < slotCount; i++)
{ {
//拿到了房间卡槽对象 //拿到了房间卡槽对象
var area2D = slotList.GetChild<Area2D>(i); var area2D = slotList.GetChild<Area2D>(i);
@ -107,11 +107,13 @@ public class Room
//转为瓦片地图的坐标(中点) //转为瓦片地图的坐标(中点)
var tileMapStartPosition = tileMap.LocalToMap(startPosition); var tileMapStartPosition = tileMap.LocalToMap(startPosition);
var tileMapEndPosition = tileMap.LocalToMap(endPosition); var tileMapEndPosition = tileMap.LocalToMap(endPosition);
var roomSlot = new RoomSlot(); var roomSlot = new RoomSlot
roomSlot.EndPosition = tileMapEndPosition; {
roomSlot.StartPosition = tileMapStartPosition; EndPosition = tileMapEndPosition,
//计算槽位的方向(房间中点为原点,指向槽位中点的向量) StartPosition = tileMapStartPosition,
roomSlot.DistanceToMidpointOfRoom = CoordinateUtils.VectorToOrientationArray(midpoint, midpointOfRoomSlots); //计算槽位的方向(房间中点为原点,指向槽位中点的向量)
DistanceToMidpointOfRoom = CoordinateUtils.VectorToOrientationArray(midpoint, midpointOfRoomSlots)
};
roomSlots.Add(roomSlot); roomSlots.Add(roomSlot);
} }

View File

@ -1,8 +1,10 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using ColdMint.scripts.debug;
using ColdMint.scripts.levelGraphEditor; using ColdMint.scripts.levelGraphEditor;
using ColdMint.scripts.map.dateBean; using ColdMint.scripts.map.dateBean;
using ColdMint.scripts.map.interfaces; using ColdMint.scripts.map.interfaces;
using ColdMint.scripts.map.room; using ColdMint.scripts.map.room;
using ColdMint.scripts.utils;
using Godot; using Godot;
namespace ColdMint.scripts.map.RoomPlacer; namespace ColdMint.scripts.map.RoomPlacer;
@ -50,8 +52,48 @@ public class PatchworkRoomPlacementStrategy : IRoomPlacementStrategy
{ {
return Task.FromResult<RoomPlacementData?>(null); return Task.FromResult<RoomPlacementData?>(null);
} }
// var roomResArray = RoomFactory.RoomTemplateSetToRoomRes(newRoomNodeData.RoomTemplateSet);
//TODO:在这里实现房间的放置策略。 var roomResArray = RoomFactory.RoomTemplateSetToRoomRes(newRoomNodeData.RoomTemplateSet);
if (roomResArray.Length == 0)
{
return Task.FromResult<RoomPlacementData?>(null);
}
var roomSlots = parentRoomNode.RoomSlots;
if (roomSlots == null || roomSlots.Length == 0)
{
return Task.FromResult<RoomPlacementData?>(null);
}
//Matches unmatched slots.
//对未匹配的插槽进行匹配。
foreach (var roomRes in roomResArray)
{
var newRoom = RoomFactory.CreateRoom(roomRes);
if (newRoom == null)
{
continue;
}
//Create a room, try to use the room slot to match the existing room slot.
//创建了一个房间,尝试使用房间的槽与现有的房间槽匹配。
if (!IsMatch(parentRoomNode, newRoom, out var mainRoomSlot, out var newRoomSlot).Result) continue;
if (mainRoomSlot == null || newRoomSlot == null)
{
continue;
}
var position = CalculatedPosition(parentRoomNode, newRoom, mainRoomSlot, newRoomSlot, true)
.Result;
if (position == null) continue;
var roomPlacementData = new RoomPlacementData
{
Room = newRoom,
Position = position
};
return Task.FromResult<RoomPlacementData?>(roomPlacementData);
}
return Task.FromResult<RoomPlacementData?>(null); return Task.FromResult<RoomPlacementData?>(null);
} }
@ -79,32 +121,152 @@ public class PatchworkRoomPlacementStrategy : IRoomPlacementStrategy
} }
/// <summary>
/// <para>if it matches</para>
/// <para>是否匹配</para>
/// </summary>
/// <param name="mainRoom"></param>
/// <param name="newRoom"></param>
/// <returns></returns>
public Task<bool> IsMatch(Room? mainRoom, Room newRoom, out RoomSlot? outMainRoomSlot, out RoomSlot? outNewRoomSlot)
{
if (mainRoom == null)
{
outNewRoomSlot = null;
outMainRoomSlot = null;
return Task.FromResult(false);
}
var roomSlots = mainRoom.RoomSlots;
if (roomSlots == null)
{
outNewRoomSlot = null;
outMainRoomSlot = null;
return Task.FromResult(false);
}
var newRoomSlots = newRoom.RoomSlots;
if (newRoomSlots == null)
{
outNewRoomSlot = null;
outMainRoomSlot = null;
return Task.FromResult(false);
}
foreach (var mainRoomSlot in roomSlots)
{
if (mainRoomSlot == null || mainRoomSlot.Matched)
{
//如果已经匹配过了,就不再匹配
continue;
}
foreach (var newRoomSlot in newRoomSlots)
{
if (newRoomSlot == null)
{
continue;
}
if (newRoomSlot.Matched)
{
//如果已经匹配过了,就不再匹配
continue;
}
if (mainRoomSlot.IsHorizontal != newRoomSlot.IsHorizontal)
{
continue;
}
if (mainRoomSlot.Length != newRoomSlot.Length)
{
continue;
}
var distanceToMidpointOfRoom = mainRoomSlot.DistanceToMidpointOfRoom;
var newDistanceToMidpointOfRoom = newRoomSlot.DistanceToMidpointOfRoom;
if (distanceToMidpointOfRoom == null || newDistanceToMidpointOfRoom == null)
{
continue;
}
if (distanceToMidpointOfRoom[0] == newDistanceToMidpointOfRoom[0] &&
distanceToMidpointOfRoom[1] == newDistanceToMidpointOfRoom[1])
{
continue;
}
LogCat.Log(distanceToMidpointOfRoom[0] + "-" + distanceToMidpointOfRoom[1] + "和" +
newDistanceToMidpointOfRoom[0] + "-" + newDistanceToMidpointOfRoom[1] + "匹配成功");
mainRoomSlot.Matched = true;
newRoomSlot.Matched = true;
outMainRoomSlot = mainRoomSlot;
outNewRoomSlot = newRoomSlot;
return Task.FromResult(true);
}
}
outNewRoomSlot = null;
outMainRoomSlot = null;
return Task.FromResult(false);
}
private Task<Vector2?> CalculatedPosition(Room mainRoom, Room newRoom, RoomSlot? mainRoomSlot, private Task<Vector2?> CalculatedPosition(Room mainRoom, Room newRoom, RoomSlot? mainRoomSlot,
RoomSlot? newRoomSlot, bool roomSlotOverlap) RoomSlot? newRoomSlot, bool roomSlotOverlap)
{ {
if (mainRoom.RootNode == null || mainRoom.TileMap == null || newRoom.TileMap == null || mainRoomSlot == null || if (mainRoom.RootNode == null || newRoom.RootNode == null || newRoom.TileMap == null ||
mainRoom.TileMap == null ||
newRoom.TileMap == null || mainRoomSlot == null ||
newRoomSlot == null) newRoomSlot == null)
{ {
return Task.FromResult<Vector2?>(null); return Task.FromResult<Vector2?>(null);
} }
//计算主插槽中点在世界中的位置。 //Main room slot location description
//mainRoom.RootNode.Position意为房间所在的世界位置 //主房间槽位置描述
//mainRoom.TileMap.MapToLocal(mainRoomSlot.StartPosition)意为主插槽在房间中的位置 var mainOrientationDescribe = mainRoomSlot.DistanceToMidpointOfRoom;
var result = mainRoom.RootNode.Position + mainRoom.TileMap.MapToLocal(mainRoomSlot.StartPosition); //New room slot location description
if (roomSlotOverlap) //新房间槽位置描述
var newOrientationDescribe = newRoomSlot.DistanceToMidpointOfRoom;
if (mainOrientationDescribe == null || newOrientationDescribe == null)
{ {
//执行减法,从槽中点偏移到左上角 //If the room slot is described as null, null is returned
result -= _halfCell; //若房间槽描述为null那么返回null
return Task.FromResult<Vector2?>(null);
}
Vector2 result;
if (mainOrientationDescribe[0] == CoordinateUtils.OrientationDescribe.Left &&
newOrientationDescribe[0] == CoordinateUtils.OrientationDescribe.Right)
{
//Move left to new room.
//左移新房间。
var mainSlotPosition = mainRoom.RootNode.Position + mainRoom.TileMap.MapToLocal(mainRoomSlot.StartPosition);
var newSlotPosition = newRoom.RootNode.Position + newRoom.TileMap.MapToLocal(newRoomSlot.StartPosition);
result = mainSlotPosition +
newRoom.TileMap.Position - newRoom.TileMap.MapToLocal(newRoomSlot.StartPosition);
//Modified y height
//修正y高度
result.Y -= newSlotPosition.Y - mainSlotPosition.Y;
//If the room slots don't overlap
//如果房间槽不能重叠
if (!roomSlotOverlap)
{
result.X -= Config.CellSize;
}
} }
else else
{ {
//执行减法,从槽中点偏移到右下角 var mainSlotPosition = mainRoom.RootNode.Position + mainRoom.TileMap.MapToLocal(mainRoomSlot.StartPosition);
result += _halfCell; var newSlotPosition = newRoom.RootNode.Position + newRoom.TileMap.MapToLocal(newRoomSlot.StartPosition);
result = mainSlotPosition;
// result.Y += newSlotPosition.Y - mainSlotPosition.Y;
} }
//我们不能将新房间的原点设置在主房间槽的左上角或右下角,这会导致插槽不对应。
//竖直槽,我们需要在同一水平上。
//We need to be on the same level.
//我们需要在同一水平上。
if (mainRoomSlot.IsHorizontal) if (mainRoomSlot.IsHorizontal)
{ {
result += newRoom.TileMap.MapToLocal(new Vector2I(newRoomSlot.EndPosition.X, 0)) - _halfCell; result += newRoom.TileMap.MapToLocal(new Vector2I(newRoomSlot.EndPosition.X, 0)) - _halfCell;

View File

@ -1,89 +0,0 @@
using System.Threading.Tasks;
using ColdMint.scripts.map.dateBean;
using ColdMint.scripts.map.interfaces;
using ColdMint.scripts.map.room;
namespace ColdMint.scripts.map.slotsMatcher;
public class RoomSlotsMatcher : IRoomSlotsMatcher
{
private RoomSlot? _lastMatchedMainSlot;
private RoomSlot? _lastMatchedMinorSlot;
public Task<bool> IsMatch(Room? mainRoom, Room newRoom)
{
if (mainRoom == null)
{
return Task.FromResult(false);
}
var roomSlots = mainRoom.RoomSlots;
if (roomSlots == null)
{
return Task.FromResult(false);
}
var newRoomSlots = newRoom.RoomSlots;
if (newRoomSlots == null)
{
return Task.FromResult(false);
}
foreach (var mainRoomSlot in roomSlots)
{
if (mainRoomSlot == null || mainRoomSlot.Matched)
{
//如果已经匹配过了,就不再匹配
continue;
}
foreach (var newRoomSlot in newRoomSlots)
{
if (newRoomSlot == null)
{
continue;
}
if (newRoomSlot.Matched)
{
//如果已经匹配过了,就不再匹配
continue;
}
if (mainRoomSlot.IsHorizontal != newRoomSlot.IsHorizontal)
{
continue;
}
if (mainRoomSlot.Length != newRoomSlot.Length)
{
continue;
}
var distanceToMidpointOfRoom = mainRoomSlot.DistanceToMidpointOfRoom;
var newDistanceToMidpointOfRoom = newRoomSlot.DistanceToMidpointOfRoom;
if (distanceToMidpointOfRoom == null || newDistanceToMidpointOfRoom == null)
{
continue;
}
if (distanceToMidpointOfRoom[0] == newDistanceToMidpointOfRoom[0] &&
distanceToMidpointOfRoom[1] == newDistanceToMidpointOfRoom[1])
{
continue;
}
mainRoomSlot.Matched = true;
newRoomSlot.Matched = true;
_lastMatchedMainSlot = mainRoomSlot;
_lastMatchedMinorSlot = newRoomSlot;
return Task.FromResult(true);
}
}
return Task.FromResult(false);
}
public RoomSlot? LastMatchedMainSlot => _lastMatchedMainSlot;
public RoomSlot? LastMatchedMinorSlot => _lastMatchedMinorSlot;
}