proto_rts/Assets/Scripts/CameraController.cs

276 lines
9.2 KiB
C#

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> 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<Unit> _selectedUnits = new List<Unit>();
private Building _currentBuilding;
private bool _wantsBuildBarracks;
private void Start()
{
_mouse = Mouse.current;
_keyboard = Keyboard.current;
_transform = transform;
_positionChange = Vector3.zero;
_camera = GetComponent<Camera>();
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);
}
}
}