ProjectCreep/Assets/TBTK/Scripts/CameraControl.cs

466 lines
15 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
namespace TBTK {
public class CameraControl : MonoBehaviour {
public static Camera mainCam;
public static Camera GetMainCam(){ return InitMainCamera(); }
public static Camera InitMainCamera(){
if(mainCam!=null) return mainCam;
mainCam=Camera.main;
if(mainCam==null) Debug.LogWarning("Main camera not found");
return mainCam;
}
//inputID=-1 - mouse cursor, inputID>=0 - touch finger index
public static bool IsCursorOnUI(int inputID=-1){
EventSystem eventSystem = EventSystem.current;
return ( eventSystem!=null && eventSystem.IsPointerOverGameObject( inputID ) );
}
public bool autoFocusOnActiveUnit;
private Unit focusUnit;
[Space(5)]
public bool useTouchInput=false;
public bool enableZoom=true;
public bool enableRotate=true;
public bool scrollKey=true; //move pivot using keyboard
public bool scrollCursorDrag=true; //move pivot using on screen cursor drag (works for touch input)
public bool scrollCursorOnEdge=false; //move pivot by placing cursor on edge of the screen
public float scrollCursorOnEdgeTH=10;
[Space(5)] public bool avoidClipping=false;
private bool hasObstacle=false;
[Header("Sensitivity")]
public float scrollSpeed=5;
public float zoomSpeed=5;
public float rotateSpeed=1;
[Header("Limit")]
public bool enablePositionLimit=true;
public float minPosX=-10; //limit when zoom all the way in (if linkPanLimitToZoom are enabled)
public float maxPosX=10;
public float minPosZ=-10;
public float maxPosZ=10;
public float minZoomDistance=8;
public float maxZoomDistance=30;
public float minRotateAngle=10;
public float maxRotateAngle=89;
private float currentZoom=0;
private Transform camT;
private Transform thisT;
public static CameraControl instance;
void Awake(){
thisT=transform;
instance=this;
if(enableActionCam){
Unit.actionCamCheck=ActionCamCheck;
Unit.actionCamStart=ActionCamFadeIn;
Unit.actionCamEnd=ActionCamFadeOut;
}
}
void Start(){
camT=thisT.GetChild(0);//Camera.main.transform;
currentZoom=camT.localPosition.z;
}
private float initialMousePosX; private float initialRotX;
private float initialMousePosY; private float initialRotY;
private Vector3 lastMousePos;
private Vector3 mouseDeltaPos;
private Vector2 initialCursorP;
private Vector2 initialRot;
private bool dragRotating=false;
private bool dragScrolling=false;
public Transform target;
public float smoothTime = 0.3F;
private Vector3 velocity = Vector3.zero;
public void OnSelectUnit(Unit unit){ if(autoFocusOnActiveUnit && unit!=null && unit.IsVisible()) focusUnit=unit; }
public void ClearFocusUnit(){ focusUnit=null; }
void OnEnable(){
TBTK.onSelectUnitE += OnSelectUnit ;
}
void OnDisable(){
TBTK.onSelectUnitE += OnSelectUnit ;
}
void Update(){
if(useTouchInput) TouchInput();
else MouseInput();
if(focusUnit!=null){
thisT.position = Vector3.SmoothDamp(thisT.position, focusUnit.GetPos(), ref velocity, smoothTime);
}
}
void TouchInput(){
if(scrollCursorDrag && scrollSpeed!=0){
float moveH=0; float moveV=0;
if(Input.GetMouseButtonDown(0) && !IsCursorOnUI()){
lastMousePos=Input.mousePosition;
dragScrolling=true;
}
else if(Input.GetMouseButton(0) && dragScrolling){
mouseDeltaPos=Input.mousePosition-lastMousePos;
lastMousePos=Input.mousePosition;
moveH=-mouseDeltaPos.x*0.1f;
moveV=-mouseDeltaPos.y*0.1f;
}
else dragScrolling=false;
if(moveH!=0){
Vector3 dirH=transform.InverseTransformDirection(Quaternion.Euler(0, thisT.eulerAngles.y, 0)*Vector3.right);
thisT.Translate(dirH * scrollSpeed * GetdeltaT() * moveH); ClearFocusUnit();
}
if(moveV!=0){
Vector3 dirV=transform.InverseTransformDirection(Quaternion.Euler(0, thisT.eulerAngles.y, 0)*Vector3.forward);
thisT.Translate(dirV * scrollSpeed * GetdeltaT() * moveV); ClearFocusUnit();
}
//~ if(enablePositionLimit && (moveH!=0 || moveV!=0)){
//~ float x=Mathf.Clamp(thisT.position.x, minPosX, maxPosX);
//~ float z=Mathf.Clamp(thisT.position.z, minPosZ, maxPosZ);
//~ thisT.position=new Vector3(x, thisT.position.y, z);
//~ }
ApplyHorizontalLimit(moveH, moveV);
}
if(enableZoom){
if(Input.touchCount==2){
Touch touch1 = Input.GetTouch(0);
Touch touch2 = Input.GetTouch(1);
// Find the position in the previous frame of each touch.
Vector2 touch1PrevPos = touch1.position - touch1.deltaPosition;
Vector2 touch2PrevPos = touch2.position - touch2.deltaPosition;
if(Vector2.Angle(touch1PrevPos, touch2PrevPos)<15){
// Find the magnitude of the vector (the distance) between the touches in each frame.
float prevTouchDeltaMag = (touch1PrevPos - touch2PrevPos).magnitude;
float touchDeltaMag = (touch1.position - touch2.position).magnitude;
// Find the difference in the distances between each frame.
float zoomInput = prevTouchDeltaMag - touchDeltaMag;
currentZoom=Mathf.Clamp(currentZoom+zoomSpeed*zoomInput*.1f, -maxZoomDistance, -minZoomDistance);
}
}
Zoom();
}
if(enableRotate){
if(Input.touchCount==2){
Touch touch1 = Input.touches[0];
Touch touch2 = Input.touches[1];
Vector2 delta1=touch1.deltaPosition.normalized;
Vector2 delta2=touch2.deltaPosition.normalized;
Vector2 delta=(delta1+delta2)/2;
float rotX=thisT.rotation.eulerAngles.x-delta.y*rotateSpeed;
float rotY=thisT.rotation.eulerAngles.y+delta.x*rotateSpeed;
rotX=Mathf.Clamp(rotX, minRotateAngle, maxRotateAngle);
thisT.rotation=Quaternion.Euler(rotX, rotY, 0);
}
}
}
void MouseInput(){
if(enableRotate){
if(Input.GetMouseButtonDown(1) && !IsCursorOnUI()){
lastMousePos=Input.mousePosition;
dragRotating=true;
}
else if(Input.GetMouseButton(1) && dragRotating){
mouseDeltaPos=Input.mousePosition-lastMousePos;
lastMousePos=Input.mousePosition;
if(mouseDeltaPos.magnitude!=0){
float rotY=thisT.eulerAngles.y+rotateSpeed*mouseDeltaPos.x;//*Time.unscaledDeltaTime;
float rotX=thisT.eulerAngles.x-rotateSpeed*mouseDeltaPos.y;//*Time.unscaledDeltaTime;
rotX=Mathf.Clamp(rotX, minRotateAngle, maxRotateAngle);
thisT.rotation=Quaternion.Euler(rotX, rotY, 0);
}
}
else dragRotating=false;
}
if((scrollKey || scrollCursorDrag || scrollCursorOnEdge) && scrollSpeed!=0){
float moveH=0; float moveV=0;
if(scrollKey){
if(Input.GetButton("Horizontal")) moveH=Input.GetAxisRaw("Horizontal");
if(Input.GetButton("Vertical")) moveV=Input.GetAxisRaw("Vertical");
}
if(scrollCursorDrag){
if(Input.GetMouseButtonDown(0) && !IsCursorOnUI()){
lastMousePos=Input.mousePosition;
dragScrolling=true;
}
else if(Input.GetMouseButton(0) && dragScrolling){
mouseDeltaPos=Input.mousePosition-lastMousePos;
lastMousePos=Input.mousePosition;
moveH=-mouseDeltaPos.x*0.1f;
moveV=-mouseDeltaPos.y*0.1f;
}
else dragScrolling=false;
}
if(scrollCursorOnEdge){
Vector3 mPos=Input.mousePosition;
if(mPos.x>=Screen.width-scrollCursorOnEdgeTH) moveH=1;
else if(mPos.x<=scrollCursorOnEdgeTH) moveH=-1;
if(mPos.y>=Screen.height-scrollCursorOnEdgeTH) moveV=1;
else if(mPos.y<=scrollCursorOnEdgeTH) moveV=-1;
}
if(moveH!=0){
Vector3 dirH=transform.InverseTransformDirection(Quaternion.Euler(0, thisT.eulerAngles.y, 0)*Vector3.right);
thisT.Translate(dirH * scrollSpeed * GetdeltaT() * moveH); ClearFocusUnit();
}
if(moveV!=0){
Vector3 dirV=transform.InverseTransformDirection(Quaternion.Euler(0, thisT.eulerAngles.y, 0)*Vector3.forward);
thisT.Translate(dirV * scrollSpeed * GetdeltaT() * moveV); ClearFocusUnit();
}
//~ if(enablePositionLimit && (moveH!=0 || moveV!=0)){
//~ float x=Mathf.Clamp(thisT.position.x, minPosX, maxPosX);
//~ float z=Mathf.Clamp(thisT.position.z, minPosZ, maxPosZ);
//~ thisT.position=new Vector3(x, thisT.position.y, z);
//~ }
ApplyHorizontalLimit(moveH, moveV);
}
if(enableZoom){
float zoomInput=Input.GetAxis("Mouse ScrollWheel");
if(zoomInput!=0) currentZoom=Mathf.Clamp(currentZoom+zoomSpeed*zoomInput, -maxZoomDistance, -minZoomDistance);
Zoom();
}
}
private void ApplyHorizontalLimit(float moveH, float moveV){
if(enablePositionLimit && (moveH!=0 || moveV!=0 || linkPanLimitToZoom)){
Vector2 limitX=GetLimitX();
Vector2 limitZ=GetLimitZ();
float x=Mathf.Clamp(thisT.position.x, limitX.x, limitX.y);
float z=Mathf.Clamp(thisT.position.z, limitZ.x, limitZ.y);
thisT.position=new Vector3(x, thisT.position.y, z);
}
}
private void Zoom(){
if(inActionCam) return;
if(avoidClipping){
Vector3 aPos=thisT.TransformPoint(new Vector3(0, 0, currentZoom));
Vector3 dirC=aPos-thisT.position;
float dist=Vector3.Distance(aPos, thisT.position); RaycastHit hit;
hasObstacle=Physics.Raycast (thisT.position, dirC, out hit, dist);
if(hasObstacle){
dist=Vector3.Distance(hit.point, thisT.position)*0.85f;
float camZ=Mathf.Lerp(camT.localPosition.z, -dist, Time.deltaTime*50);
camT.localPosition=new Vector3(camT.localPosition.x, camT.localPosition.y, camZ);
}
}
if(!avoidClipping || !hasObstacle){
currentZoom=Mathf.Clamp(currentZoom, -maxZoomDistance, -minZoomDistance);
float camZ=Mathf.Lerp(camT.localPosition.z, currentZoom, Time.deltaTime*4);
camT.localPosition=new Vector3(camT.localPosition.x, camT.localPosition.y, camZ);
}
}
private float GetdeltaT(){ return Time.unscaledDeltaTime; }
#region ActionCam
[Header("Action-Cam")]
public bool enableActionCam=true;
public float actionCamFreqAttack=0.25f;
public float actionCamFreqAbility=0.25f;
public static bool ActionCamCheck(bool isNormalAttack){
if(isNormalAttack && Rand.value()<instance.actionCamFreqAttack) return true;
else if(!isNormalAttack && Rand.value()<instance.actionCamFreqAbility) return true;
return false;
}
private Vector3 cachedPos;
private Quaternion cachedRot;
private float cachedZoom;
private bool inActionCam=false;
public static IEnumerator ActionCamFadeIn(Vector3 srcPos, Vector3 tgtPos){
if(instance==null) yield break;
instance.inActionCam=true;
yield return instance.StartCoroutine(instance._ActionCamFadeIn(srcPos, tgtPos));
}
public IEnumerator _ActionCamFadeIn(Vector3 srcPos, Vector3 tgtPos, float duration=0){
inActionCam=true;
cachedPos=thisT.position;
cachedRot=thisT.rotation;
cachedZoom=camT.localPosition.z;
Vector3 centerPos=srcPos+(tgtPos-srcPos)*0.35f;
centerPos.y=thisT.position.y;
Quaternion wantedRot=Quaternion.LookRotation(centerPos-srcPos);
wantedRot*=Quaternion.Euler(15, (Rand.value()<0.5f ? 1 : -1)*20, 0);
wantedRot=Quaternion.Euler(wantedRot.eulerAngles.x, wantedRot.eulerAngles.y, 0);
float dist=Vector3.Distance(centerPos, srcPos);
while(duration<1){
thisT.rotation=Quaternion.Euler(thisT.rotation.eulerAngles.x, thisT.rotation.eulerAngles.y, 0);
camT.localPosition=Vector3.Lerp(new Vector3(0, 0, cachedZoom), new Vector3(0, 0, -dist-GridManager.GetNodeSize()*2.25f), duration);
thisT.position=Vector3.Lerp(cachedPos, centerPos, duration);
thisT.rotation=Quaternion.Lerp(cachedRot, wantedRot, duration);
duration+=Time.deltaTime*.75f; yield return null;
}
thisT.rotation=Quaternion.Euler(thisT.rotation.eulerAngles.x, thisT.rotation.eulerAngles.y, 0);
yield return new WaitForSeconds(0.5f);
}
public static IEnumerator ActionCamFadeOut(){
if(instance==null || !instance.inActionCam) yield break;
yield return instance.StartCoroutine(instance._ActionCamFadeOut());
}
public IEnumerator _ActionCamFadeOut(float duration=0){
yield return new WaitForSeconds(0.5f);
Vector3 startingPos=thisT.position;
Quaternion startingRot=thisT.rotation;
float currentZoom=camT.localPosition.z;
while(duration<1){
thisT.rotation=Quaternion.Euler(thisT.rotation.eulerAngles.x, thisT.rotation.eulerAngles.y, 0);
camT.localPosition=Vector3.Lerp(new Vector3(0, 0, currentZoom), new Vector3(0, 0, cachedZoom), duration);
thisT.position=Vector3.Lerp(startingPos, cachedPos, duration);
thisT.rotation=Quaternion.Lerp(startingRot, cachedRot, duration);
duration+=Time.deltaTime*.75f; yield return null;
}
thisT.rotation=Quaternion.Euler(thisT.rotation.eulerAngles.x, thisT.rotation.eulerAngles.y, 0);
yield return new WaitForSeconds(0.25f);
inActionCam=false;
}
#endregion
[Header("DynamicLimit")]
public bool linkPanLimitToZoom;
public float limitExtX_ZOut=10; //limit when zoom all the way out
public float limitExtZ_ZOut=10;
public Vector2 GetLimitX(){
if(!linkPanLimitToZoom) return new Vector2(minPosX, maxPosX);
float zoomRatio=1-(Mathf.Abs(camT.localPosition.z)-minZoomDistance)/(maxZoomDistance-minZoomDistance);
return new Vector2(minPosX-zoomRatio*limitExtX_ZOut, maxPosX+zoomRatio*limitExtX_ZOut);
}
public Vector2 GetLimitZ(){
if(!linkPanLimitToZoom) return new Vector2(minPosZ, maxPosZ);
float zoomRatio=1-(Mathf.Abs(camT.localPosition.z)-minZoomDistance)/(maxZoomDistance-minZoomDistance);
return new Vector2(minPosZ-zoomRatio*limitExtZ_ZOut, maxPosZ+zoomRatio*limitExtZ_ZOut);
}
[Space(10)] public bool showGizmo=true;
void OnDrawGizmos(){
if(showGizmo && enablePositionLimit){
Vector3 p1=new Vector3(minPosX, transform.position.y, maxPosZ);
Vector3 p2=new Vector3(maxPosX, transform.position.y, maxPosZ);
Vector3 p3=new Vector3(maxPosX, transform.position.y, minPosZ);
Vector3 p4=new Vector3(minPosX, transform.position.y, minPosZ);
Gizmos.color=Color.green;
GizmosDrawSquare(p1, p2, p3, p4);
if(linkPanLimitToZoom){
if(camT==null) camT=transform.GetChild(0);
p1=new Vector3(minPosX-limitExtX_ZOut, transform.position.y, maxPosZ+limitExtZ_ZOut);
p2=new Vector3(maxPosX+limitExtX_ZOut, transform.position.y, maxPosZ+limitExtZ_ZOut);
p3=new Vector3(maxPosX+limitExtX_ZOut, transform.position.y, minPosZ-limitExtZ_ZOut);
p4=new Vector3(minPosX-limitExtX_ZOut, transform.position.y, minPosZ-limitExtZ_ZOut);
GizmosDrawSquare(p1, p2, p3, p4);
Vector2 limitX=GetLimitX();
Vector2 limitZ=GetLimitZ();
Gizmos.color=new Color(.2f, 1f, 1f, 1f);
p1=new Vector3(limitX.x, transform.position.y, limitZ.y);
p2=new Vector3(limitX.y, transform.position.y, limitZ.y);
p3=new Vector3(limitX.y, transform.position.y, limitZ.x);
p4=new Vector3(limitX.x, transform.position.y, limitZ.x);
GizmosDrawSquare(p1, p2, p3, p4);
}
}
}
void GizmosDrawSquare(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4){
Gizmos.DrawLine(p1, p2);
Gizmos.DrawLine(p2, p3);
Gizmos.DrawLine(p3, p4);
Gizmos.DrawLine(p4, p1);
}
}
}