OndrejNepozitek / Edgar-Unity

Unity Procedural Level Generator
https://ondrejnepozitek.github.io/Edgar-Unity/docs/introduction
MIT License
817 stars 70 forks source link

How to refer to all the doors in the room template? #96

Closed longtran2904 closed 3 years ago

longtran2904 commented 3 years ago

Is the Doors property of RoomInstance refer to all possible doors (all doors in the template prefab) or the doors that have been chosen (instance doors)? For example, if I have a room that has 4 doors, but when got generated only used 1 door then the Doors list's length is 4 or 1?

OndrejNepozitek commented 3 years ago

The Doors property contains only the doors that were chosen for the room. So in your example, the length of the list would be 1.

By the way, if you're using a smart IDE like Visual Studio, you can easily add a breakpoint to your code, attach the provided debugger to Unity editor and you'll be able to inspect the values of all the variables/properties in your code.

longtran2904 commented 3 years ago

Are there any ways to get all the doors in the room template? I'm making a system that will add/remove some tiles if a specific door is opened or closed, so I need to know if a door is being chosen by the generator or not.

OndrejNepozitek commented 3 years ago

Sure but it's not as straightforward as getting only the used doors. I'll try to get back to you tomorrow with a solution.

OndrejNepozitek commented 3 years ago

Please try the following script:

using System;
using System.Collections.Generic;
using System.Linq;
using Edgar.Geometry;
using Edgar.GraphBasedGenerator.Grid2D;
using Edgar.Unity;
using UnityEngine;

namespace Assets
{
    [CreateAssetMenu(menuName = "Edgar - Unused doors", fileName = "UnusedDoors")]
    public class UnusedDoors : DungeonGeneratorPostProcessBase
    {
        public override void Run(GeneratedLevel level, LevelDescription levelDescription)
        {
            foreach (var roomInstance in level.GetRoomInstances())
            {
                // Here are the doors that are not used
                var unusedDoors = GetUnusedDoors(roomInstance);
            }
        }

        public List<DoorInstance> GetUnusedDoors(RoomInstance roomInstance)
        {
            var doorMode = roomInstance.RoomTemplateInstance.GetComponent<Doors>().GetDoorMode();

            var polygon = RoomTemplatesLoader.GetPolygonFromRoomTemplate(roomInstance.RoomTemplateInstance);
            var doors = doorMode
                .GetDoors(polygon)
                .SelectMany(x => 
                    x.Line
                        .GetPoints()
                        .Select(point => 
                            new OrthogonalLineGrid2D(point, point + x.Length * x.Line.GetDirectionVector(), x.Line.GetDirection()
                ))).ToList();
            var unusedDoors = doors.ToList();

            foreach (var door in roomInstance.Doors)
            {
                var from = door.DoorLine.From;
                var to = door.DoorLine.To;

                foreach (var otherDoor in unusedDoors.ToList())
                {
                    var otherFrom = otherDoor.From.ToUnityIntVector3();
                    var otherTo = otherDoor.To.ToUnityIntVector3();

                    if ((from == otherFrom && to == otherTo) || (from == otherTo && to == otherFrom))
                    {
                        unusedDoors.Remove(otherDoor);
                    }
                }
            }

            var result = new List<DoorInstance>();

            foreach (var door in unusedDoors)
            {
                result.Add(CreateDoor(door));
            }

            return result;
        }

        private static DoorInstance CreateDoor(OrthogonalLineGrid2D doorLine)
        {
            switch (doorLine.GetDirection())
            {
                case OrthogonalLineGrid2D.Direction.Right:
                    return new DoorInstance(new OrthogonalLine(doorLine.From.ToUnityIntVector3(), doorLine.To.ToUnityIntVector3()), Vector2Int.up,
                        null, null);

                case OrthogonalLineGrid2D.Direction.Left:
                    return new DoorInstance(new OrthogonalLine(doorLine.To.ToUnityIntVector3(), doorLine.From.ToUnityIntVector3()), Vector2Int.down,
                        null, null);

                case OrthogonalLineGrid2D.Direction.Top:
                    return new DoorInstance(new OrthogonalLine(doorLine.From.ToUnityIntVector3(), doorLine.To.ToUnityIntVector3()), Vector2Int.left,
                        null, null);

                case OrthogonalLineGrid2D.Direction.Bottom:
                    return new DoorInstance(new OrthogonalLine(doorLine.To.ToUnityIntVector3(), doorLine.From.ToUnityIntVector3()), Vector2Int.right,
                        null, null);

                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
}

The GetUnusedDoors takes a room instance and returns a list of unused doors. I haven't tested it very much so please let me know if something doesn't work.

longtran2904 commented 3 years ago

Thanks for replying. When reading through your code, I didn't understand why you called the ToList method on some properties that already a list (like doors or unusedDoors local variable). Can you explain it a little bit?

OndrejNepozitek commented 3 years ago

That was mostly for debugging purposes. If you call ToList() on a list, it actually creates a copy of that list. If I do that, I can add a breakpoint to the end of that method and check the difference between the variable doors (having all doors of the room) and the variable unusedDoors (having only the doors that are not used). If I didn't do that, both variables would reference the same list and therefore the contents would always be the same.

longtran2904 commented 3 years ago

Ok, I understand now.

longtran2904 commented 3 years ago

When you check from == otherFrom && to == otherFrom in the foreach loop of unusedDoors, it should be to == otherTo right?

OndrejNepozitek commented 3 years ago

You mean this part of code, right?

                    if ((from == otherFrom && to == otherFrom) || (from == otherTo && to == otherFrom))
                    {
                        unusedDoors.Remove(otherDoor);
                    }

There are both possibilities because the direction of a door can be switched by the generator so it's easier to just check for both options.

longtran2904 commented 3 years ago

Yes, but in the first part it should be to == otherTo not to == otherFrom. So the whole code should be if ((from == otherFrom && to == otherTo) || (from == otherTo && to == otherFrom))

OndrejNepozitek commented 3 years ago

Yes, you're right, I didn't see that.