using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.InputSystem; [RequireComponent(typeof(Camera))] public class CameraController : MonoBehaviour { public float scrollSpeed; public RectTransform unitSelector; // TODO: this is not ideal. public List minerals; private Camera _camera; private Keyboard _keyboard; private Mouse _mouse; private Transform _transform; private Vector3 _positionChange; private bool _isSelecting; private Rect _selectorRect; private readonly List _selectedUnits = new List(); private Building _currentBuilding; private bool _wantsBuildBarracks; private void Start() { _mouse = Mouse.current; _keyboard = Keyboard.current; _transform = transform; _positionChange = Vector3.zero; _camera = GetComponent(); Cursor.lockState = CursorLockMode.Confined; var pivot = unitSelector.pivot; pivot.x = 0; pivot.y = 0; unitSelector.pivot = pivot; Team.Minerals = 10; _keyboard.onTextInput += OnTextInput; } private void OnTextInput(char ch) { var keyControl = _keyboard.FindKeyOnCurrentKeyboardLayout(ch.ToString()); if (_currentBuilding != null && keyControl != null) { _currentBuilding.ProcessKeyInput(keyControl.keyCode); } } private void Update() { #if UNITY_EDITOR if (_keyboard.escapeKey.wasPressedThisFrame) { Cursor.lockState = CursorLockMode.None; } #endif if (!Application.isFocused) { return; } var mousePos = _mouse.position.ReadValue(); _positionChange.x = 0; _positionChange.y = 0; if (mousePos.x <= 1) { _positionChange.x -= scrollSpeed * Time.deltaTime; } if (mousePos.x >= Screen.width - 1) { _positionChange.x += scrollSpeed * Time.deltaTime; } if (mousePos.y <= 1) { _positionChange.y -= scrollSpeed * Time.deltaTime; } if (mousePos.y >= Screen.height - 1) { _positionChange.y += scrollSpeed * Time.deltaTime; } _transform.position += _positionChange; if (_mouse.rightButton.wasPressedThisFrame) { // TODO: pick logic, if the mouse was not dragged then it should select whatever is in the tile. var worldPos = _camera.ScreenToWorldPoint(mousePos); worldPos.x = Mathf.RoundToInt(worldPos.x); worldPos.y = Mathf.RoundToInt(worldPos.y); worldPos.z = 0; var hasIssuedCommand = false; foreach (var building in Team.AllBuildings) { if (building.buildingCollider.OverlapPoint(worldPos) && building.isUnderConstruction) { //var worker = Team.AllUnits.FirstOrDefault() as Worker; var worker = _selectedUnits.FirstOrDefault(u => u is Worker) as Worker; if (worker) { hasIssuedCommand = true; worker.ConstructBuilding(building, worldPos); } break; } } foreach (var mineral in minerals) { if (mineral.boxCollider.OverlapPoint(worldPos)) { var cmd = Team.AllBuildings.FirstOrDefault(b => b.name == "CommandCenter"); // Not the best, but it will do. // TODO: basically will need to search all buildings for command centers (should be a type?) then find the closest to the patch. if (cmd) { var workers = _selectedUnits.FindAll(u => u is Worker); hasIssuedCommand = workers.Count > 0; Debug.Log($"has issued cmd: {hasIssuedCommand}"); workers.ForEach(w => ((Worker)w).StartMining(mineral, mineral.transform.position, cmd.transform.position)); } } } if (!hasIssuedCommand) { MoveAllSelectedUnits(worldPos); } } // TODO: for the selection box, we should select whatever thing there is most of in the box. // IE: if there is one worker but 3 combat units, then select the 3 combat units. // This is not ideal but since the prototype for iteration 0 is limited to 2 buildings, a worker, and a combat // unit for the types of selectables, I think it will be acceptable until I add more stuff. if (_mouse.leftButton.wasPressedThisFrame && !_wantsBuildBarracks) { _isSelecting = true; _selectorRect = unitSelector.rect; _selectorRect.min = _mouse.position.ReadValue(); } if (_isSelecting) { _selectorRect.max = _mouse.position.ReadValue(); if (_mouse.leftButton.wasReleasedThisFrame) { // Needs to be called before we clear the selector rect. FillSelection(); _isSelecting = false; _selectorRect.min = Vector2.zero; _selectorRect.max = Vector2.zero; unitSelector.anchoredPosition = _selectorRect.min; unitSelector.sizeDelta = _selectorRect.max; } Vector2 min; min.x = _selectorRect.min.x > _selectorRect.max.x ? _selectorRect.max.x : _selectorRect.min.x; min.y = _selectorRect.min.y > _selectorRect.max.y ? _selectorRect.max.y : _selectorRect.min.y; Vector2 max; max.x = _selectorRect.min.x > _selectorRect.max.x ? _selectorRect.min.x : _selectorRect.max.x; max.y = _selectorRect.min.y > _selectorRect.max.y ? _selectorRect.min.y : _selectorRect.max.y; unitSelector.anchoredPosition = min; unitSelector.sizeDelta = max - min; } // if (_keyboard.sKey.wasPressedThisFrame && _currentBuilding) // { // _currentBuilding.BuildWorker(); // } // TODO: this needs to cost money // B->B build barracks if (_selectedUnits.Count > 0) { if (_keyboard.bKey.wasPressedThisFrame) { _wantsBuildBarracks = true; } } if (_wantsBuildBarracks && _selectedUnits.Count > 0) { if (_mouse.leftButton.wasPressedThisFrame) { var worldPos = _camera.ScreenToWorldPoint(mousePos); worldPos.z = 0; worldPos.x = Mathf.RoundToInt(worldPos.x); worldPos.y = Mathf.RoundToInt(worldPos.y); var worker = _selectedUnits.FirstOrDefault() as Worker; if (worker) { worker.BuildBarracks(worldPos); _wantsBuildBarracks = false; } } } else { _wantsBuildBarracks = false; } // Barracks -> A marine // B->C command center. } private void FillSelection() { _selectedUnits.Clear(); var worldMin = _camera.ScreenToWorldPoint(_selectorRect.min); var worldMax = _camera.ScreenToWorldPoint(_selectorRect.max); var min = Vector3.zero; min.x = worldMin.x > worldMax.x ? worldMax.x : worldMin.x; min.y = worldMin.y > worldMax.y ? worldMax.y : worldMin.y; var max = Vector3.zero; max.x = worldMin.x > worldMax.x ? worldMin.x : worldMax.x; max.y = worldMin.y > worldMax.y ? worldMin.y : worldMax.y; var hasUnits = false; foreach (var unit in Team.AllUnits) { var pos = unit.transform.position; if (pos.x > min.x && pos.y > min.y && pos.x < max.x && pos.y < max.y) { _selectedUnits.Add(unit); hasUnits = true; } } // TODO: this is really not good, Ideally would just have a selectable type that provides abilities to the user. // IE a unit selectable type provides the ability to move. A building selectable type provides the ability to manufacture a unit. etc. if (!hasUnits) { // Try to select a building foreach (var building in Team.AllBuildings) { if (building.buildingCollider.OverlapPoint(min) || building.buildingCollider.OverlapPoint(max)) { Debug.Log("Building selected"); _currentBuilding = building; break; } } } } private void MoveAllSelectedUnits(Vector3 worldPos) { // TODO: not sure if pathfinding should happen here or on each unit. // Probably start with each unit and when swarming logic is needed re-think the solution. foreach (var unit in _selectedUnits) { unit.MoveTo(worldPos); } } }