using UnityEngine; using System.Collections.Generic; public class NewRoomBuilder : MonoBehaviour { [Header("Prefabs")] public GameObject wallPrefab; public GameObject wallPointPrefab; public GameObject doorOpeningPrefab; [Header("Settings")] public float wallHeight = 3f; public float wallThickness = 0.2f; public float doorWidth = 1.5f; public float doorHeight = 2.5f; public float snapDistance = 1f; // Distance for snapping to existing points private Camera playerCamera; private BuildingUIController.BuildingMode currentMode = BuildingUIController.BuildingMode.Normal; // Wall creation state private bool isCreatingWall = false; private Vector3 wallStartPoint; private GameObject previewWall; private LineRenderer previewLine; // Available angles (in degrees) private readonly float[] snapAngles = { 0f, 45f, 90f, 135f, 180f, 225f, 270f, 315f }; // Wall storage private List walls = new List(); // Door preview private GameObject doorPreview; private GameObject hoveredWall; // Snap indicators private GameObject snapIndicator; private GameObject currentSnapPoint; private void Start() { // Try multiple ways to find the camera playerCamera = Camera.main; if (playerCamera == null) { playerCamera = FindFirstObjectByType(); Debug.LogWarning($"Camera.main was null, found camera: {playerCamera?.name ?? "NONE"}"); } if (playerCamera == null) { Debug.LogError("No camera found! Wall detection will not work."); } else { Debug.Log($"Using camera: {playerCamera.name} at {playerCamera.transform.position}"); } CreatePreviewLine(); CreateDoorPreview(); CreateSnapIndicator(); // Ensure we have prefabs EnsurePrefabsExist(); // Fix any existing walls that might not be tagged properly FixExistingWallTags(); } private void FixExistingWallTags() { // First, look for walls that are already properly tagged (from InitialRoomSetup) GameObject[] existingWalls = GameObject.FindGameObjectsWithTag("Wall"); Debug.Log($"Found {existingWalls.Length} objects already tagged as 'Wall'"); foreach (GameObject wall in existingWalls) { if (!walls.Contains(wall)) { walls.Add(wall); Debug.Log($"Added existing tagged wall to tracking: {wall.name} - Scale: {wall.transform.localScale}"); } } // If we found properly tagged walls, we're done if (existingWalls.Length > 0) { Debug.Log($"Successfully found and added {existingWalls.Length} pre-tagged walls from InitialRoomSetup"); return; } // Fallback: Look for objects that should be walls but aren't tagged yet GameObject[] allObjects = FindObjectsByType(FindObjectsSortMode.None); int fixedWalls = 0; Debug.Log($"No pre-tagged walls found. Scanning {allObjects.Length} objects for potential walls..."); foreach (GameObject obj in allObjects) { // Skip door components and other non-wall objects if (obj.name.Contains("Post") || obj.name.Contains("Beam") || obj.name.Contains("Door") || obj.name.Contains("Mat") || obj.name.Contains("Entrance") || obj.name.Contains("Frame")) { continue; } // Look for specific wall naming patterns from InitialRoomSetup bool isInitialRoomWall = obj.name.Contains("North Wall") || obj.name.Contains("South Wall") || obj.name.Contains("East Wall") || obj.name.Contains("West Wall"); // Also check for other wall patterns bool isWallObject = obj.name.Contains("Wall") || obj.name.Contains("wall") || obj.name.StartsWith("Room") || obj.name.Contains("Interior"); if ((isInitialRoomWall || isWallObject) && !obj.CompareTag("Wall")) { // Additional check: make sure it has a collider (walls should have colliders) if (obj.GetComponent() != null || obj.GetComponentInChildren() != null) { Debug.Log($"Found untagged wall: {obj.name} - Scale: {obj.transform.localScale}, Position: {obj.transform.position}"); obj.tag = "Wall"; fixedWalls++; Debug.Log($"Fixed wall tag for: {obj.name}"); // Also add it to our walls list if (!walls.Contains(obj)) { walls.Add(obj); Debug.Log($"Added newly tagged wall to tracking: {obj.name}"); } } } } if (fixedWalls > 0) { Debug.Log($"Fixed {fixedWalls} existing walls that weren't properly tagged"); } else { Debug.Log("No walls found at all. This might indicate InitialRoomSetup hasn't run or there's a scene setup issue."); // Debug: List some objects to see what's in the scene Debug.Log("Sample objects in scene:"); for (int i = 0; i < Mathf.Min(20, allObjects.Length); i++) { if (allObjects[i].name.Contains("Wall") || allObjects[i].name.Contains("wall") || allObjects[i].transform.localScale.y > 2f) { Debug.Log($" Potential wall: {allObjects[i].name} - Scale: {allObjects[i].transform.localScale} - Tag: {allObjects[i].tag}"); } } } } private void EnsurePrefabsExist() { // Create basic prefabs if they don't exist if (wallPrefab == null) { wallPrefab = CreateWallPrefab(); Debug.Log("Created wall prefab dynamically"); } if (wallPointPrefab == null) { wallPointPrefab = CreateWallPointPrefab(); Debug.Log("Created wall point prefab dynamically"); } if (doorOpeningPrefab == null) { doorOpeningPrefab = CreateDoorOpeningPrefab(); Debug.Log("Created door opening prefab dynamically"); } } #region Prefab Creation private GameObject CreateWallPrefab() { // Create a parent GameObject for the wall GameObject wall = new GameObject("Wall_Prefab"); // Add a cube as child for the visual representation GameObject wallMesh = GameObject.CreatePrimitive(PrimitiveType.Cube); wallMesh.name = "WallMesh"; wallMesh.transform.SetParent(wall.transform); wallMesh.transform.localPosition = Vector3.zero; wallMesh.transform.localRotation = Quaternion.identity; wallMesh.transform.localScale = Vector3.one; // Set up the wall material Renderer renderer = wallMesh.GetComponent(); Material wallMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit")); // Set up proper wall color - light gray/beige wallMaterial.color = new Color(0.9f, 0.9f, 0.8f); // Light beige/cream color // Make sure it's not metallic and has proper smoothness wallMaterial.SetFloat("_Metallic", 0.0f); wallMaterial.SetFloat("_Smoothness", 0.2f); renderer.material = wallMaterial; // Ensure the wall has a collider for door placement detection BoxCollider collider = wallMesh.GetComponent(); if (collider == null) { collider = wallMesh.AddComponent(); } wall.tag = "Wall"; // Don't destroy this object, keep it for instantiation DontDestroyOnLoad(wall); wall.SetActive(false); // Hide the template return wall; } private GameObject CreateWallPointPrefab() { // Create a parent GameObject for the wall point GameObject point = new GameObject("WallPoint_Prefab"); // Add a sphere as child for the visual representation GameObject pointMesh = GameObject.CreatePrimitive(PrimitiveType.Sphere); pointMesh.name = "PointMesh"; pointMesh.transform.SetParent(point.transform); pointMesh.transform.localPosition = Vector3.zero; pointMesh.transform.localRotation = Quaternion.identity; pointMesh.transform.localScale = Vector3.one * 0.2f; Renderer renderer = pointMesh.GetComponent(); Material pointMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit")); // Set up bright red color for wall points pointMaterial.color = Color.red; pointMaterial.SetFloat("_Metallic", 0.0f); pointMaterial.SetFloat("_Smoothness", 0.8f); // Make it a bit shinier for visibility renderer.material = pointMaterial; // Remove collider to prevent interference Collider collider = pointMesh.GetComponent(); if (collider != null) DestroyImmediate(collider); DontDestroyOnLoad(point); point.SetActive(false); // Hide the template return point; } private GameObject CreateDoorOpeningPrefab() { // Create a parent GameObject for the door frame (visible part) GameObject door = new GameObject("DoorOpening_Prefab"); // Create door frame - just the frame, not a solid door GameObject doorFrame = new GameObject("DoorFrame"); doorFrame.transform.SetParent(door.transform); // Create frame components: left post, right post, top beam // Posts should be positioned at ground level with proper Y positioning CreateDoorFramePost(doorFrame, "LeftPost", new Vector3(-doorWidth / 2f, doorHeight / 2f, 0f)); CreateDoorFramePost(doorFrame, "RightPost", new Vector3(doorWidth / 2f, doorHeight / 2f, 0f)); CreateDoorFrameBeam(doorFrame, "TopBeam", new Vector3(0f, doorHeight - 0.1f, 0f)); door.tag = "Door"; DontDestroyOnLoad(door); return door; } private void CreateDoorFramePost(GameObject parent, string name, Vector3 position) { GameObject post = GameObject.CreatePrimitive(PrimitiveType.Cube); post.name = name; post.transform.SetParent(parent.transform); post.transform.localPosition = position; post.transform.localScale = new Vector3(0.1f, doorHeight, 0.2f); // Thin post Renderer renderer = post.GetComponent(); Material frameMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit")); frameMaterial.color = new Color(0.4f, 0.2f, 0.1f); // Brown wood frameMaterial.SetFloat("_Metallic", 0.0f); frameMaterial.SetFloat("_Smoothness", 0.3f); renderer.material = frameMaterial; } private void CreateDoorFrameBeam(GameObject parent, string name, Vector3 position) { GameObject beam = GameObject.CreatePrimitive(PrimitiveType.Cube); beam.name = name; beam.transform.SetParent(parent.transform); beam.transform.localPosition = position; beam.transform.localScale = new Vector3(doorWidth + 0.2f, 0.2f, 0.2f); // Wide beam across top Renderer renderer = beam.GetComponent(); Material frameMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit")); frameMaterial.color = new Color(0.4f, 0.2f, 0.1f); // Brown wood frameMaterial.SetFloat("_Metallic", 0.0f); frameMaterial.SetFloat("_Smoothness", 0.3f); renderer.material = frameMaterial; } #endregion #region Main Update Loop private void Update() { switch (currentMode) { case BuildingUIController.BuildingMode.CreatingWalls: HandleWallCreation(); break; case BuildingUIController.BuildingMode.CreatingDoors: HandleDoorPlacement(); break; case BuildingUIController.BuildingMode.CreatingEntrance: HandleEntranceCreation(); break; default: HandleNormalMode(); HideAllPreviews(); break; } } public void SetBuildingMode(BuildingUIController.BuildingMode mode) { // Cancel any current wall creation if (isCreatingWall) { CancelWallCreation(); } currentMode = mode; HideAllPreviews(); Debug.Log($"Building mode changed to: {mode}"); } #endregion #region Normal Mode private void HandleNormalMode() { // In normal mode, allow deletion of entrances with right-click if (Input.GetMouseButtonDown(1)) // Right click { Vector3 mousePos = GetMouseWorldPosition(); CheckForEntranceDeletion(mousePos); } } private void CheckForEntranceDeletion(Vector3 mousePos) { Ray ray = playerCamera.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit, 1000f)) { GameObject hitObj = hit.collider.gameObject; // Check if we hit an entrance or entrance component GameObject entranceToDelete = null; if (hitObj.CompareTag("Entrance")) { entranceToDelete = hitObj; } else if (hitObj.transform.parent != null && hitObj.transform.parent.CompareTag("Entrance")) { entranceToDelete = hitObj.transform.parent.gameObject; } if (entranceToDelete != null) { Debug.Log($"Deleting entrance: {entranceToDelete.name}"); DestroyImmediate(entranceToDelete); // Note: The wall segments around the entrance will remain // This creates a permanent opening where the entrance was } } } #endregion #region Wall Creation private void HandleWallCreation() { Vector3 mousePos = GetMouseWorldPosition(); if (Input.GetMouseButtonDown(0)) // Left click { if (!isCreatingWall) { // Start creating a wall StartWallCreation(mousePos); } else { // Finish creating the wall FinishWallCreation(mousePos); } } else if (Input.GetMouseButtonDown(1)) // Right click { // Cancel wall creation if (isCreatingWall) { CancelWallCreation(); } } // Update preview while creating wall if (isCreatingWall) { UpdateWallPreview(mousePos); } } private void StartWallCreation(Vector3 startPoint) { // Snap to nearby existing points Vector3 snappedStart = SnapToNearbyPoint(startPoint); wallStartPoint = snappedStart; isCreatingWall = true; // Show preview if (previewLine != null) { previewLine.enabled = true; } Debug.Log($"Started wall creation at: {wallStartPoint}"); } private void UpdateWallPreview(Vector3 currentMousePos) { if (!isCreatingWall || previewLine == null) return; // Calculate snapped end point Vector3 snappedEndPoint = SnapToAngle(wallStartPoint, currentMousePos); Vector3 finalSnappedPoint = SnapToNearbyPoint(snappedEndPoint); // Show snap indicator if we're snapping to something if (Vector3.Distance(finalSnappedPoint, snappedEndPoint) > 0.1f) { ShowSnapIndicator(finalSnappedPoint); } else { HideSnapIndicator(); } // Update preview line previewLine.SetPosition(0, wallStartPoint); previewLine.SetPosition(1, finalSnappedPoint); } private void ShowSnapIndicator(Vector3 position) { if (snapIndicator != null) { snapIndicator.transform.position = position; snapIndicator.SetActive(true); } } private void HideSnapIndicator() { if (snapIndicator != null) { snapIndicator.SetActive(false); } } private void FinishWallCreation(Vector3 endPoint) { if (!isCreatingWall) return; // Calculate snapped end point Vector3 snappedEndPoint = SnapToAngle(wallStartPoint, endPoint); snappedEndPoint = SnapToNearbyPoint(snappedEndPoint); // Create the actual wall CreateWall(wallStartPoint, snappedEndPoint); // Reset state isCreatingWall = false; if (previewLine != null) { previewLine.enabled = false; } Debug.Log($"Created wall from {wallStartPoint} to {snappedEndPoint}"); } private void CancelWallCreation() { isCreatingWall = false; if (previewLine != null) { previewLine.enabled = false; } Debug.Log("Wall creation cancelled"); } private Vector3 SnapToAngle(Vector3 startPoint, Vector3 endPoint) { Vector3 direction = (endPoint - startPoint).normalized; float distance = Vector3.Distance(startPoint, endPoint); // Calculate angle in degrees float currentAngle = Mathf.Atan2(direction.z, direction.x) * Mathf.Rad2Deg; if (currentAngle < 0) currentAngle += 360f; // Find closest snap angle float closestAngle = snapAngles[0]; float smallestDifference = Mathf.Abs(currentAngle - closestAngle); foreach (float angle in snapAngles) { float difference = Mathf.Abs(currentAngle - angle); if (difference > 180f) difference = 360f - difference; // Handle wrapping if (difference < smallestDifference) { smallestDifference = difference; closestAngle = angle; } } // Calculate snapped direction float rad = closestAngle * Mathf.Deg2Rad; Vector3 snappedDirection = new Vector3(Mathf.Cos(rad), 0f, Mathf.Sin(rad)); return startPoint + snappedDirection * distance; } private Vector3 SnapToNearbyPoint(Vector3 point) { float closestCornerDistance = float.MaxValue; float closestWallDistance = float.MaxValue; Vector3 closestCornerPoint = point; Vector3 closestWallPoint = point; bool foundCorner = false; foreach (GameObject wall in walls) { if (wall == null) continue; // First priority: Check both endpoints of the wall (corners) Vector3[] endpoints = GetWallEndpoints(wall); foreach (Vector3 endpoint in endpoints) { float distance = Vector3.Distance(point, endpoint); if (distance < snapDistance && distance < closestCornerDistance) { closestCornerDistance = distance; closestCornerPoint = endpoint; foundCorner = true; Debug.Log($"Found corner snap point at {endpoint}, distance: {distance}"); } } // Second priority: Check snapping to wall surface/center (only if no corner found nearby) if (!foundCorner || closestCornerDistance > snapDistance * 0.7f) // Allow wall snapping if no close corner { Vector3 wallCenter = wall.transform.position; wallCenter.y = 0f; // Project to ground level // Project point onto wall line to find closest point on wall Vector3 wallDirection = wall.transform.forward; Vector3 toPoint = point - wallCenter; float projection = Vector3.Dot(toPoint, wallDirection); // Clamp projection to wall bounds float wallLength = wall.transform.localScale.z; projection = Mathf.Clamp(projection, -wallLength / 2f, wallLength / 2f); Vector3 closestPointOnWall = wallCenter + wallDirection * projection; float wallDistance = Vector3.Distance(point, closestPointOnWall); // Snap to wall surface if clicking near the wall if (wallDistance < snapDistance && wallDistance < closestWallDistance) { closestWallDistance = wallDistance; closestWallPoint = closestPointOnWall; Debug.Log($"Found wall surface snap point at {closestPointOnWall}, distance: {wallDistance}"); } } } // Return the best snap point: prioritize corners over wall surfaces if (foundCorner && closestCornerDistance < snapDistance) { Debug.Log($"Using corner snap point: {closestCornerPoint}"); UpdateSnapIndicatorColor(true); // Corner = cyan return closestCornerPoint; } else if (closestWallDistance < snapDistance) { Debug.Log($"Using wall surface snap point: {closestWallPoint}"); UpdateSnapIndicatorColor(false); // Wall surface = yellow return closestWallPoint; } Debug.Log($"No snap point found, using original point: {point}"); return point; } private void UpdateSnapIndicatorColor(bool isCorner) { if (snapIndicator != null) { Renderer renderer = snapIndicator.GetComponent(); if (renderer != null) { Color indicatorColor = isCorner ? Color.cyan : Color.yellow; Color emissionColor = indicatorColor * 2f; renderer.material.color = indicatorColor; renderer.material.SetColor("_EmissionColor", emissionColor); } } } private void CreateWall(Vector3 startPoint, Vector3 endPoint) { if (Vector3.Distance(startPoint, endPoint) < 0.1f) return; // Too short // Calculate wall properties Vector3 center = (startPoint + endPoint) / 2f; center.y = wallHeight / 2f; // Set proper height Vector3 direction = (endPoint - startPoint).normalized; float length = Vector3.Distance(startPoint, endPoint); // Create wall object from prefab GameObject wall = Instantiate(wallPrefab, center, Quaternion.LookRotation(direction, Vector3.up)); wall.SetActive(true); // Make sure it's visible // Scale the entire wall object to proper dimensions: // X = thickness (perpendicular to wall direction) // Y = height (up/down) // Z = length (along the wall direction) wall.transform.localScale = new Vector3(wallThickness, wallHeight, length); // Set wall name and add to list wall.name = $"Wall_{walls.Count}"; walls.Add(wall); Debug.Log($"Created wall: {wall.name} from {startPoint} to {endPoint}, length: {length}"); } #endregion #region Door Placement private void HandleDoorPlacement() { Vector3 mousePos = GetMouseWorldPosition(); // Find wall under mouse GameObject wallUnderMouse = FindWallUnderMouse(mousePos); // Update hover preview UpdateDoorPreview(wallUnderMouse, mousePos); if (Input.GetMouseButtonDown(0)) // Left click { Debug.Log($"Door placement click detected at {mousePos}"); if (wallUnderMouse != null) { Debug.Log($"Wall found under mouse: {wallUnderMouse.name}"); PlaceDoorOnWall(wallUnderMouse, mousePos); } else { Debug.LogWarning("No wall found under mouse cursor for door placement"); // Let's try a different approach - check nearby walls GameObject nearestWall = FindNearestWall(mousePos); if (nearestWall != null) { Debug.Log($"Found nearest wall: {nearestWall.name}, placing door there"); PlaceDoorOnWall(nearestWall, mousePos); } else { Debug.LogWarning("No walls found nearby for door placement"); } } } } private void UpdateDoorPreview(GameObject wall, Vector3 mousePos) { if (wall != hoveredWall) { hoveredWall = wall; if (wall != null) { ShowDoorPreview(wall, mousePos); } else { HideDoorPreview(); } } else if (wall != null) { // Update preview position ShowDoorPreview(wall, mousePos); } } private void ShowDoorPreview(GameObject wall, Vector3 mousePos) { if (doorPreview == null) return; // Calculate door position on the wall Vector3 doorPosition = CalculateDoorPositionOnWall(wall, mousePos); // Position and orient the preview - door should face perpendicular to wall doorPreview.transform.position = doorPosition; doorPreview.transform.rotation = Quaternion.LookRotation(wall.transform.right); doorPreview.SetActive(true); } private void HideDoorPreview() { if (doorPreview != null) { doorPreview.SetActive(false); } } private void PlaceDoorOnWall(GameObject wall, Vector3 mousePos) { Debug.Log($"Attempting to place door on {wall.name} at mouse position {mousePos}"); // Calculate door position Vector3 doorPosition = CalculateDoorPositionOnWall(wall, mousePos); Debug.Log($"Calculated door position: {doorPosition}"); // Check if door would intersect with existing doors if (WouldDoorIntersectExistingDoors(wall, doorPosition)) { Debug.LogWarning("Cannot place door - would intersect with existing door"); return; } // Get wall endpoints and direction for splitting Vector3[] wallEndpoints = GetWallEndpoints(wall); Vector3 wallStart = wallEndpoints[0]; Vector3 wallEnd = wallEndpoints[1]; Vector3 wallDirection = wall.transform.forward; // Calculate door boundaries along the wall Vector3 doorStart = doorPosition - wallDirection * (doorWidth / 2f); Vector3 doorEnd = doorPosition + wallDirection * (doorWidth / 2f); // Create door at wall position with proper rotation // Door should face perpendicular to the wall direction (reuse existing wallDirection) Quaternion doorRotation = Quaternion.LookRotation(wall.transform.right); // Face perpendicular to wall GameObject door = Instantiate(doorOpeningPrefab, doorPosition, doorRotation); door.SetActive(true); door.name = $"Door_on_{wall.name}"; // Split the original wall into two segments around the door SplitWallAroundDoor(wall, wallStart, wallEnd, doorStart, doorEnd); Debug.Log($"Successfully placed door: {door.name} and split wall into segments"); } private bool WouldDoorIntersectExistingDoors(GameObject wall, Vector3 doorPosition) { // Find all existing doors GameObject[] existingDoors = GameObject.FindGameObjectsWithTag("Door"); foreach (GameObject existingDoor in existingDoors) { // Check if doors are on the same wall or parallel walls float distance = Vector3.Distance(doorPosition, existingDoor.transform.position); if (distance < doorWidth * 1.5f) // Doors too close { Debug.Log($"Door too close to existing door {existingDoor.name}: distance = {distance}"); return true; } } return false; } private void SplitWallAroundDoor(GameObject originalWall, Vector3 wallStart, Vector3 wallEnd, Vector3 doorStart, Vector3 doorEnd) { // Calculate the two new wall segments float originalWallLength = Vector3.Distance(wallStart, wallEnd); float leftSegmentLength = Vector3.Distance(wallStart, doorStart); float rightSegmentLength = Vector3.Distance(doorEnd, wallEnd); // Only create segments if they're long enough bool createLeftSegment = leftSegmentLength > 0.1f; bool createRightSegment = rightSegmentLength > 0.1f; Debug.Log($"Splitting wall: original length = {originalWallLength}, left = {leftSegmentLength}, right = {rightSegmentLength}"); if (createLeftSegment) { // Create left wall segment - full height Vector3 leftCenter = (wallStart + doorStart) / 2f; leftCenter.y = wallHeight / 2f; GameObject leftWall = Instantiate(wallPrefab, leftCenter, originalWall.transform.rotation); leftWall.SetActive(true); leftWall.name = $"{originalWall.name}_Left"; leftWall.transform.localScale = new Vector3(wallThickness, wallHeight, leftSegmentLength); walls.Add(leftWall); Debug.Log($"Created left wall segment: {leftWall.name}"); } if (createRightSegment) { // Create right wall segment - full height Vector3 rightCenter = (doorEnd + wallEnd) / 2f; rightCenter.y = wallHeight / 2f; GameObject rightWall = Instantiate(wallPrefab, rightCenter, originalWall.transform.rotation); rightWall.SetActive(true); rightWall.name = $"{originalWall.name}_Right"; rightWall.transform.localScale = new Vector3(wallThickness, wallHeight, rightSegmentLength); walls.Add(rightWall); Debug.Log($"Created right wall segment: {rightWall.name}"); } // Create header beam above the door (if there are segments on both sides) if (createLeftSegment && createRightSegment) { CreateDoorHeaderBeam(originalWall, doorStart, doorEnd); } // Remove original wall from list and destroy it walls.Remove(originalWall); DestroyImmediate(originalWall); Debug.Log("Removed original wall after splitting"); } private void CreateDoorHeaderBeam(GameObject originalWall, Vector3 doorStart, Vector3 doorEnd) { // Create beam above door opening Vector3 beamCenter = (doorStart + doorEnd) / 2f; beamCenter.y = doorHeight + 0.1f; // Just above the door GameObject headerBeam = Instantiate(wallPrefab, beamCenter, originalWall.transform.rotation); headerBeam.SetActive(true); headerBeam.name = $"{originalWall.name}_Header"; float beamLength = Vector3.Distance(doorStart, doorEnd); float beamHeight = wallHeight - doorHeight - 0.1f; // Remaining height above door if (beamHeight > 0.1f) // Only create beam if there's significant height remaining { headerBeam.transform.localScale = new Vector3(wallThickness, beamHeight, beamLength); walls.Add(headerBeam); Debug.Log($"Created door header beam: {headerBeam.name}"); } else { // If no room for header beam, destroy it DestroyImmediate(headerBeam); Debug.Log("Door is full height - no header beam needed"); } } #endregion #region Entrance Creation private void HandleEntranceCreation() { Vector3 mousePos = GetMouseWorldPosition(); // Find wall under mouse GameObject wallUnderMouse = FindWallUnderMouse(mousePos); // Update entrance preview (similar to door but different style) UpdateEntrancePreview(wallUnderMouse, mousePos); if (Input.GetMouseButtonDown(0)) // Left click { Debug.Log($"Entrance placement click detected at {mousePos}"); if (wallUnderMouse != null) { Debug.Log($"Wall found under mouse: {wallUnderMouse.name}"); PlaceEntranceOnWall(wallUnderMouse, mousePos); } else { Debug.LogWarning("No wall found under mouse cursor for entrance placement"); // Try alternative detection method first GameObject alternativeWall = FindWallUnderMouseAlternative(mousePos); if (alternativeWall != null) { Debug.Log($"Alternative method found wall: {alternativeWall.name}, placing entrance there"); PlaceEntranceOnWall(alternativeWall, mousePos); } else { // Let's try a different approach - check nearby walls GameObject nearestWall = FindNearestWall(mousePos); if (nearestWall != null) { Debug.Log($"Found nearest wall: {nearestWall.name}, placing entrance there"); PlaceEntranceOnWall(nearestWall, mousePos); } else { Debug.LogWarning("No walls found nearby for entrance placement"); } } } } } private void UpdateEntrancePreview(GameObject wall, Vector3 mousePos) { if (wall != null) { ShowEntrancePreview(wall, mousePos); } else { HideEntrancePreview(); } } private void ShowEntrancePreview(GameObject wall, Vector3 mousePos) { if (doorPreview == null) return; // Reuse door preview for now // Calculate entrance position on the wall Vector3 entrancePosition = CalculateDoorPositionOnWall(wall, mousePos); // Position and orient the preview - entrance should face perpendicular to wall doorPreview.transform.position = entrancePosition; doorPreview.transform.rotation = Quaternion.LookRotation(wall.transform.right); // Make entrance preview slightly different (maybe different scale or color) doorPreview.transform.localScale = new Vector3(0.2f, doorHeight, doorWidth * 1.2f); // Slightly wider doorPreview.SetActive(true); } private void HideEntrancePreview() { if (doorPreview != null) { doorPreview.SetActive(false); } } private void PlaceEntranceOnWall(GameObject wall, Vector3 mousePos) { Debug.Log($"Attempting to place entrance on {wall.name} at mouse position {mousePos}"); // Calculate entrance position on the wall surface Vector3 entrancePosition = CalculateDoorPositionOnWall(wall, mousePos); Debug.Log($"Calculated entrance position: {entrancePosition}"); // Check if there's already an entrance GameObject[] existingEntrances = GameObject.FindGameObjectsWithTag("Entrance"); if (existingEntrances.Length > 0) { Debug.LogWarning("Building already has an entrance. Replacing existing entrance."); foreach (GameObject existingEntrance in existingEntrances) { DestroyImmediate(existingEntrance); } } // Determine wall orientation based on its scale Vector3 wallScale = wall.transform.localScale; bool isNorthSouthWall = wallScale.x > wallScale.z; // Wide in X means North/South wall Debug.Log($"Wall scale: {wallScale}, isNorthSouthWall: {isNorthSouthWall}"); // Calculate entrance boundaries and determine how to split the wall float entranceWidth = doorWidth * 1.5f; // Create entrance with proper rotation GameObject entrance = CreateEntrancePrefab(); entrance.transform.position = entrancePosition; // Set rotation based on wall orientation if (isNorthSouthWall) { // For North/South walls (extending in X), entrance should face along Z-axis entrance.transform.rotation = Quaternion.identity; // Facing forward (positive Z) } else { // For East/West walls (extending in Z), entrance should face along X-axis entrance.transform.rotation = Quaternion.Euler(0, 90, 0); // Facing right (positive X) } entrance.SetActive(true); entrance.name = "BuildingEntrance"; entrance.tag = "Entrance"; Debug.Log($"Placed entrance at {entrance.transform.position} with rotation {entrance.transform.rotation.eulerAngles}"); // Split the wall properly based on its orientation SplitWallForEntrance(wall, entrancePosition, entranceWidth, isNorthSouthWall); Debug.Log($"Successfully placed entrance and split wall"); } private void SplitWallForEntrance(GameObject originalWall, Vector3 entrancePosition, float entranceWidth, bool isNorthSouthWall) { Vector3 wallPos = originalWall.transform.position; Vector3 wallScale = originalWall.transform.localScale; Quaternion wallRotation = originalWall.transform.rotation; // Get the original wall's material to maintain consistency Material originalMaterial = originalWall.GetComponent()?.material; if (wallPrefab == null) { Debug.LogError("WallPrefab is null! Cannot create wall segments. Aborting split operation."); return; } if (isNorthSouthWall) { // Wall extends in X direction, split along X-axis float wallLength = wallScale.x; // Corrected: X is the length for North/South walls float wallLeft = wallPos.x - wallLength / 2f; float wallRight = wallPos.x + wallLength / 2f; float entranceLeft = entrancePosition.x - entranceWidth / 2f; float entranceRight = entrancePosition.x + entranceWidth / 2f; Debug.Log($"North/South wall split: wallLeft={wallLeft}, wallRight={wallRight}, entranceLeft={entranceLeft}, entranceRight={entranceRight}"); float leftSegmentLength = entranceLeft - wallLeft; float rightSegmentLength = wallRight - entranceRight; Debug.Log($"Segment lengths: left={leftSegmentLength}, right={rightSegmentLength}"); // Create left segment if there's enough space if (leftSegmentLength > 0.5f) { Vector3 leftCenter = new Vector3(wallLeft + leftSegmentLength / 2f, wallPos.y, wallPos.z); GameObject leftWall = Instantiate(wallPrefab, leftCenter, wallRotation); leftWall.SetActive(true); leftWall.name = $"{originalWall.name}_Left_Entrance"; leftWall.transform.localScale = new Vector3(leftSegmentLength, wallScale.y, wallScale.z); leftWall.tag = "Wall"; // Apply original material if available, with error checking if (originalMaterial != null) { Renderer leftRenderer = leftWall.GetComponent(); if (leftRenderer != null) { leftRenderer.material = originalMaterial; Debug.Log($"Applied original material to left wall segment"); } else { Debug.LogWarning($"Left wall segment {leftWall.name} has no Renderer component - cannot apply material"); } } walls.Add(leftWall); Debug.Log($"SUCCESS: Created left wall segment: {leftWall.name}"); } else { Debug.Log($"LEFT SEGMENT TOO SMALL: {leftSegmentLength} <= 0.5f, not creating"); } // Create right segment if there's enough space if (rightSegmentLength > 0.5f) { Vector3 rightCenter = new Vector3(entranceRight + rightSegmentLength / 2f, wallPos.y, wallPos.z); GameObject rightWall = Instantiate(wallPrefab, rightCenter, wallRotation); rightWall.SetActive(true); rightWall.name = $"{originalWall.name}_Right_Entrance"; rightWall.transform.localScale = new Vector3(rightSegmentLength, wallScale.y, wallScale.z); rightWall.tag = "Wall"; // Apply original material if available, with error checking if (originalMaterial != null) { Renderer rightRenderer = rightWall.GetComponent(); if (rightRenderer != null) { rightRenderer.material = originalMaterial; Debug.Log($"Applied original material to right wall segment"); } else { Debug.LogWarning($"Right wall segment {rightWall.name} has no Renderer component - cannot apply material"); } } walls.Add(rightWall); Debug.Log($"SUCCESS: Created right wall segment: {rightWall.name}"); } else { Debug.Log($"RIGHT SEGMENT TOO SMALL: {rightSegmentLength} <= 0.5f, not creating"); } } else { // Wall extends in Z direction, split along Z-axis float wallLength = wallScale.z; float wallFront = wallPos.z - wallLength / 2f; float wallBack = wallPos.z + wallLength / 2f; float entranceFront = entrancePosition.z - entranceWidth / 2f; float entranceBack = entrancePosition.z + entranceWidth / 2f; Debug.Log($"East/West wall split: wallFront={wallFront}, wallBack={wallBack}, entranceFront={entranceFront}, entranceBack={entranceBack}"); float frontSegmentLength = entranceFront - wallFront; float backSegmentLength = wallBack - entranceBack; Debug.Log($"Segment lengths: front={frontSegmentLength}, back={backSegmentLength}"); // Create front segment if there's enough space if (frontSegmentLength > 0.5f) { Vector3 frontCenter = new Vector3(wallPos.x, wallPos.y, wallFront + frontSegmentLength / 2f); Debug.Log($"Creating front segment at {frontCenter} with scale ({wallScale.x}, {wallScale.y}, {frontSegmentLength})"); GameObject frontWall = Instantiate(wallPrefab, frontCenter, wallRotation); frontWall.SetActive(true); frontWall.name = $"{originalWall.name}_Front_Entrance"; frontWall.transform.localScale = new Vector3(wallScale.x, wallScale.y, frontSegmentLength); frontWall.tag = "Wall"; // Apply original material if available, with error checking if (originalMaterial != null) { Renderer frontRenderer = frontWall.GetComponent(); if (frontRenderer != null) { frontRenderer.material = originalMaterial; Debug.Log($"Applied original material to front wall segment"); } else { Debug.LogWarning($"Front wall segment {frontWall.name} has no Renderer component - cannot apply material"); } } walls.Add(frontWall); Debug.Log($"SUCCESS: Created front wall segment: {frontWall.name}"); } else { Debug.Log($"FRONT SEGMENT TOO SMALL: {frontSegmentLength} <= 0.5f, not creating"); } // Create back segment if there's enough space if (backSegmentLength > 0.5f) { Vector3 backCenter = new Vector3(wallPos.x, wallPos.y, entranceBack + backSegmentLength / 2f); Debug.Log($"Creating back segment at {backCenter} with scale ({wallScale.x}, {wallScale.y}, {backSegmentLength})"); GameObject backWall = Instantiate(wallPrefab, backCenter, wallRotation); backWall.SetActive(true); backWall.name = $"{originalWall.name}_Back_Entrance"; backWall.transform.localScale = new Vector3(wallScale.x, wallScale.y, backSegmentLength); backWall.tag = "Wall"; // Apply original material if available, with error checking if (originalMaterial != null) { Renderer backRenderer = backWall.GetComponent(); if (backRenderer != null) { backRenderer.material = originalMaterial; Debug.Log($"Applied original material to back wall segment"); } else { Debug.LogWarning($"Back wall segment {backWall.name} has no Renderer component - cannot apply material"); } } walls.Add(backWall); Debug.Log($"SUCCESS: Created back wall segment: {backWall.name}"); } else { Debug.Log($"BACK SEGMENT TOO SMALL: {backSegmentLength} <= 0.5f, not creating"); } } // Remove original wall Debug.Log($"REMOVING ORIGINAL WALL: {originalWall.name} (active: {originalWall.activeInHierarchy})"); walls.Remove(originalWall); // Try multiple removal methods to ensure it's gone originalWall.SetActive(false); DestroyImmediate(originalWall); Debug.Log("Original wall removal completed"); } private GameObject CreateEntrancePrefab() { // Create a more elaborate entrance than a regular door GameObject entrance = new GameObject("BuildingEntrance"); // Create entrance frame - wider and more elaborate GameObject entranceFrame = new GameObject("EntranceFrame"); entranceFrame.transform.SetParent(entrance.transform); float entranceWidth = doorWidth * 1.5f; // Create frame components: left post, right post, top beam, and entrance mat CreateEntrancePost(entranceFrame, "LeftPost", new Vector3(-entranceWidth / 2f, doorHeight / 2f, 0f), entranceWidth); CreateEntrancePost(entranceFrame, "RightPost", new Vector3(entranceWidth / 2f, doorHeight / 2f, 0f), entranceWidth); CreateEntranceBeam(entranceFrame, "TopBeam", new Vector3(0f, doorHeight - 0.1f, 0f), entranceWidth); CreateEntranceMat(entranceFrame, "WelcomeMat", new Vector3(0f, 0.05f, 1f), entranceWidth); entrance.tag = "Entrance"; DontDestroyOnLoad(entrance); return entrance; } private void CreateEntrancePost(GameObject parent, string name, Vector3 position, float width) { GameObject post = GameObject.CreatePrimitive(PrimitiveType.Cube); post.name = name; post.transform.SetParent(parent.transform); post.transform.localPosition = position; post.transform.localScale = new Vector3(0.15f, doorHeight, 0.3f); // Slightly thicker than door posts Renderer renderer = post.GetComponent(); Material frameMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit")); frameMaterial.color = new Color(0.3f, 0.15f, 0.05f); // Darker brown for entrance frameMaterial.SetFloat("_Metallic", 0.0f); frameMaterial.SetFloat("_Smoothness", 0.4f); renderer.material = frameMaterial; } private void CreateEntranceBeam(GameObject parent, string name, Vector3 position, float width) { GameObject beam = GameObject.CreatePrimitive(PrimitiveType.Cube); beam.name = name; beam.transform.SetParent(parent.transform); beam.transform.localPosition = position; beam.transform.localScale = new Vector3(width + 0.3f, 0.3f, 0.3f); // Wider and thicker beam Renderer renderer = beam.GetComponent(); Material frameMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit")); frameMaterial.color = new Color(0.3f, 0.15f, 0.05f); // Darker brown for entrance frameMaterial.SetFloat("_Metallic", 0.0f); frameMaterial.SetFloat("_Smoothness", 0.4f); renderer.material = frameMaterial; } private void CreateEntranceMat(GameObject parent, string name, Vector3 position, float width) { GameObject mat = GameObject.CreatePrimitive(PrimitiveType.Cube); mat.name = name; mat.transform.SetParent(parent.transform); mat.transform.localPosition = position; mat.transform.localScale = new Vector3(width, 0.1f, 1.5f); // Welcome mat dimensions Renderer renderer = mat.GetComponent(); Material matMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit")); matMaterial.color = new Color(0.8f, 0.2f, 0.2f); // Red welcome mat matMaterial.SetFloat("_Metallic", 0.0f); matMaterial.SetFloat("_Smoothness", 0.1f); // Rough mat texture renderer.material = matMaterial; } #endregion #region Helper Methods private void CreatePreviewLine() { GameObject previewObj = new GameObject("WallPreview"); previewLine = previewObj.AddComponent(); // Use URP Unlit shader for line renderer (better for simple colored lines) previewLine.material = new Material(Shader.Find("Universal Render Pipeline/Unlit")); previewLine.material.color = Color.yellow; previewLine.startColor = Color.yellow; previewLine.endColor = Color.yellow; previewLine.startWidth = 0.1f; previewLine.endWidth = 0.1f; previewLine.positionCount = 2; previewLine.enabled = false; } private void CreateDoorPreview() { // Create a simple preview object doorPreview = GameObject.CreatePrimitive(PrimitiveType.Cube); doorPreview.name = "DoorPreview"; // Make it transparent green Renderer renderer = doorPreview.GetComponent(); Material previewMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit")); // Set up bright green transparent color previewMaterial.color = new Color(0f, 1f, 0f, 0.6f); // Bright transparent green // Set up transparency properly for URP previewMaterial.SetFloat("_Surface", 1); // Set to Transparent previewMaterial.SetFloat("_Blend", 0); // Alpha blend previewMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); previewMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); previewMaterial.SetInt("_ZWrite", 0); previewMaterial.renderQueue = 3000; // Make it non-metallic and smooth for better visibility previewMaterial.SetFloat("_Metallic", 0.0f); previewMaterial.SetFloat("_Smoothness", 0.9f); renderer.material = previewMaterial; // Scale to door size - match the actual door dimensions doorPreview.transform.localScale = new Vector3(0.2f, doorHeight, doorWidth); doorPreview.SetActive(false); // Remove collider to prevent interference Collider collider = doorPreview.GetComponent(); if (collider != null) Destroy(collider); } private void CreateSnapIndicator() { snapIndicator = GameObject.CreatePrimitive(PrimitiveType.Sphere); snapIndicator.name = "SnapIndicator"; // Make it a bright, pulsing indicator Renderer renderer = snapIndicator.GetComponent(); Material indicatorMaterial = new Material(Shader.Find("Universal Render Pipeline/Lit")); indicatorMaterial.color = Color.cyan; indicatorMaterial.SetFloat("_Metallic", 0.0f); indicatorMaterial.SetFloat("_Smoothness", 0.9f); // Make it emissive so it glows indicatorMaterial.EnableKeyword("_EMISSION"); indicatorMaterial.SetColor("_EmissionColor", Color.cyan * 2f); renderer.material = indicatorMaterial; // Scale it to be visible but not too large snapIndicator.transform.localScale = Vector3.one * 0.3f; snapIndicator.SetActive(false); // Remove collider to prevent interference Collider collider = snapIndicator.GetComponent(); if (collider != null) Destroy(collider); } private void HideAllPreviews() { if (previewLine != null) { previewLine.enabled = false; } HideDoorPreview(); HideSnapIndicator(); } private Vector3 GetMouseWorldPosition() { Ray ray = playerCamera.ScreenPointToRay(Input.mousePosition); // Cast ray onto ground plane (Y = 0) if (Physics.Raycast(ray, out RaycastHit hit)) { return hit.point; } // Fallback: project onto Y = 0 plane float distance = -ray.origin.y / ray.direction.y; return ray.origin + ray.direction * distance; } private GameObject FindWallUnderMouse(Vector3 mousePos) { Ray ray = playerCamera.ScreenPointToRay(Input.mousePosition); Debug.Log($"Raycasting for wall detection at mouse position: {Input.mousePosition}"); Debug.Log($"Ray origin: {ray.origin}, Ray direction: {ray.direction}"); Debug.Log($"Camera: {playerCamera?.name ?? "NULL"}, Camera position: {playerCamera?.transform.position ?? Vector3.zero}"); Debug.Log($"Total walls in list: {walls.Count}"); // Check if we have any walls with colliders int wallsWithColliders = 0; foreach (GameObject wall in walls) { if (wall != null) { Collider[] colliders = wall.GetComponentsInChildren(); if (colliders.Length > 0) { wallsWithColliders++; Debug.Log($"Wall {wall.name} has {colliders.Length} colliders"); } } } Debug.Log($"Walls with colliders: {wallsWithColliders}"); // Try raycasting with all layers first if (Physics.Raycast(ray, out RaycastHit hit, 1000f)) { GameObject hitObj = hit.collider.gameObject; Debug.Log($"Raycast hit object: {hitObj.name} on layer {hitObj.layer}"); Debug.Log($"Hit point: {hit.point}, Hit distance: {hit.distance}"); // Check if it's one of our walls (could be parent or child) GameObject wallToCheck = hitObj; // If we hit a child mesh, get the parent if (hitObj.name == "WallMesh" && hitObj.transform.parent != null) { wallToCheck = hitObj.transform.parent.gameObject; Debug.Log($"Hit child mesh, checking parent: {wallToCheck.name}"); } if (walls.Contains(wallToCheck)) { Debug.Log($"Hit object is a valid wall: {wallToCheck.name}"); return wallToCheck; } else { Debug.Log($"Hit object is not a wall we created: {wallToCheck.name}"); // Also check if the hit object has a Wall tag as fallback if (hitObj.CompareTag("Wall") || (hitObj.transform.parent != null && hitObj.transform.parent.CompareTag("Wall"))) { GameObject taggedWall = hitObj.CompareTag("Wall") ? hitObj : hitObj.transform.parent.gameObject; Debug.Log($"Found wall by tag: {taggedWall.name}"); return taggedWall; } } } else { Debug.Log("Raycast didn't hit anything - checking for potential issues:"); Debug.Log($"PlayerCamera null? {playerCamera == null}"); Debug.Log($"Physics.DefaultRaycastLayers: {Physics.DefaultRaycastLayers}"); // Try a wider raycast with all layers RaycastHit[] hits = Physics.RaycastAll(ray, 1000f); Debug.Log($"RaycastAll found {hits.Length} hits"); for (int i = 0; i < hits.Length; i++) { Debug.Log($"Hit {i}: {hits[i].collider.gameObject.name} on layer {hits[i].collider.gameObject.layer}"); } } return null; } private GameObject FindWallUnderMouseAlternative(Vector3 worldMousePos) { // Alternative approach: find the closest wall to the mouse world position GameObject closestWall = null; float closestDistance = float.MaxValue; float maxDistance = 2f; // Maximum distance to consider a wall "under" the mouse Debug.Log($"Alternative wall detection at world pos: {worldMousePos}"); foreach (GameObject wall in walls) { if (wall == null) continue; // Calculate distance from mouse world position to wall center float distance = Vector3.Distance(worldMousePos, wall.transform.position); // Also check distance to wall bounds Bounds wallBounds = GetWallBounds(wall); float boundDistance = wallBounds.SqrDistance(worldMousePos); Debug.Log($"Wall {wall.name}: center distance = {distance}, bound distance = {Mathf.Sqrt(boundDistance)}"); if (boundDistance < maxDistance * maxDistance && distance < closestDistance) { closestDistance = distance; closestWall = wall; } } if (closestWall != null) { Debug.Log($"Alternative method found wall: {closestWall.name} at distance {closestDistance}"); } else { Debug.Log("Alternative method found no walls nearby"); } return closestWall; } private Bounds GetWallBounds(GameObject wall) { Renderer renderer = wall.GetComponentInChildren(); if (renderer != null) { return renderer.bounds; } // Fallback: calculate bounds from transform Vector3 scale = wall.transform.localScale; return new Bounds(wall.transform.position, scale); } private GameObject FindNearestWall(Vector3 mousePos) { GameObject nearestWall = null; float nearestDistance = float.MaxValue; foreach (GameObject wall in walls) { if (wall == null) continue; // Calculate distance from mouse position to wall float distance = Vector3.Distance(mousePos, wall.transform.position); if (distance < nearestDistance && distance < 5f) // Within 5 units { nearestDistance = distance; nearestWall = wall; } } if (nearestWall != null) { Debug.Log($"Found nearest wall: {nearestWall.name} at distance {nearestDistance}"); } return nearestWall; } private Vector3[] GetWallEndpoints(GameObject wall) { // Calculate endpoints from wall transform Vector3 center = wall.transform.position; // With our new rotation, the wall extends along the forward (Z) axis Vector3 wallDirection = wall.transform.forward; float length = wall.transform.localScale.z; // Z is now the length Vector3 start = center - wallDirection * (length / 2f); Vector3 end = center + wallDirection * (length / 2f); // Ensure Y = 0 for ground-level snapping start.y = 0f; end.y = 0f; return new Vector3[] { start, end }; } private Vector3 CalculateDoorPositionOnWall(GameObject wall, Vector3 mousePos) { // Project mouse position onto the wall Vector3 wallCenter = wall.transform.position; Vector3 wallScale = wall.transform.localScale; // Use the wall's actual transform directions instead of world axes Vector3 wallDirection; float wallLength; // Determine wall orientation based on scale bool isNorthSouthWall = wallScale.x > wallScale.z; if (isNorthSouthWall) { // Wall extends in X direction - use the wall's right vector (local X axis) wallDirection = wall.transform.right; wallLength = wallScale.x; } else { // Wall extends in Z direction - use the wall's forward vector (local Z axis) wallDirection = wall.transform.forward; wallLength = wallScale.z; } // Project mouse position onto the wall's direction vector Vector3 toMouse = mousePos - wallCenter; float projection = Vector3.Dot(toMouse, wallDirection); // Clamp to wall bounds float clampedProjection = Mathf.Clamp(projection, -wallLength / 2f + doorWidth / 2f, wallLength / 2f - doorWidth / 2f); Vector3 doorPos = wallCenter + wallDirection * clampedProjection; doorPos.y = 0f; // Set door at ground level return doorPos; } #endregion }