305 lines
7.7 KiB
C#
305 lines
7.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using Edgegap.Editor;
|
|
using GameKit.Dependencies.Utilities;
|
|
using log4net;
|
|
using PrimeTween;
|
|
using Unity.Mathematics;
|
|
using Unity.VisualScripting;
|
|
using UnityEditor.Rendering;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering.Universal;
|
|
using static UnityEngine.Mathf;
|
|
|
|
public class CameraOperator : MonoBehaviour
|
|
{
|
|
|
|
private static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
private Dictionary<int, GameObject> players = new Dictionary<int, GameObject>();
|
|
|
|
[SerializeField]
|
|
private int MaxAdditionalDistance = 30;
|
|
[SerializeField]
|
|
private float FarOutBias = 0.3f;
|
|
[SerializeField]
|
|
private float MaxFollowDistance = 100f;
|
|
[SerializeField]
|
|
[Tooltip("Target Offset/ (Framerate * (1/FollowSpeed))")]
|
|
private float FollowSpeed = 2f;
|
|
[SerializeField]
|
|
[Tooltip("Lower values make the camera tilt harder to keep the mid point centered")]
|
|
private float TiltFactor = 1.5f;
|
|
[SerializeField]
|
|
[Range(0.0f, 1f)]
|
|
private float ZoomOutMargin = 0.5f;
|
|
[SerializeField]
|
|
[Range(0.0f, 1f)]
|
|
private float ZoomInMargin = 0.3f;
|
|
[SerializeField]
|
|
private float ZoomInSpeed = 0.4f;
|
|
[SerializeField]
|
|
private float ZoomOutSpeed = 0.5f;
|
|
[SerializeField]
|
|
private float TimeUntilZoomIn = 0.3f;
|
|
[SerializeField]
|
|
private float TimeUntilZoomOut = 0.3f;
|
|
|
|
|
|
private float _switchZoomInTime = 0f;
|
|
private float _switchZoomOutTime = 0f;
|
|
private Vector2 _zoomInPixels = new();
|
|
private Vector2 _zoomOutPixels = new();
|
|
|
|
private Vector3 _currentZoomSpeed = Vector3.zero;
|
|
|
|
private float InitialDistance = 0;
|
|
private float MaxDistance;
|
|
|
|
private float ZoomTriggerXDistance = 0;
|
|
private float ZoomTriggerYDistance = 0;
|
|
|
|
private Camera cam;
|
|
|
|
private void Awake()
|
|
{
|
|
cam = gameObject.GetComponent<Camera>();
|
|
// Distance here is in the negative direction on the z axis
|
|
InitialDistance = transform.localPosition.z;
|
|
MaxDistance = InitialDistance - MaxAdditionalDistance;
|
|
_zoomInPixels.x = Screen.width * 0.5f * ZoomInMargin;
|
|
_zoomInPixels.y = Screen.height * 0.5f * ZoomInMargin;
|
|
_zoomOutPixels.x = Screen.width * 0.5f * ZoomOutMargin;
|
|
_zoomOutPixels.y = Screen.height * 0.5f * ZoomOutMargin;
|
|
}
|
|
|
|
public void ShakeCam(float strength = 0.1f, float duration = 0.05f, float frequency = 50f)
|
|
{
|
|
Tween.ShakeLocalPosition(cam.transform, new Vector2(strength, strength), duration, frequency);
|
|
//Tween.ShakeLocalRotation(cam.transform, new Vector2(strength, strength), duration);
|
|
}
|
|
|
|
public void AddCharacter(GameObject ship)
|
|
{
|
|
players[ship.GetInstanceID()] = ship;
|
|
}
|
|
|
|
public void RemoveCharacter(GameObject ship)
|
|
{
|
|
players.Remove(ship.GetInstanceID());
|
|
ZoomTriggerXDistance = 0;
|
|
ZoomTriggerYDistance = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tilts the camera to point at the center between the players.
|
|
/// Zooms to keep players in frame.
|
|
/// Follows the players mid-point.
|
|
/// </summary>
|
|
private void LateUpdate()
|
|
{
|
|
|
|
Vector3 center = CalculatePlayersCenter();
|
|
if (center.IsNan())
|
|
return;
|
|
if (players.Count == 1)
|
|
{
|
|
FollowPosition(players.First().Value.transform.localPosition);
|
|
FacePlayersCenter(center - transform.localPosition);
|
|
}
|
|
else if (players.Count > 0)
|
|
{
|
|
FollowPosition(center);
|
|
FacePlayersCenter(center);
|
|
AdjustZoom();
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
private Vector3 CalculatePlayersCenter()
|
|
{
|
|
Vector3 center = new Vector3();
|
|
if (players.Count < 1)
|
|
{
|
|
return transform.localPosition;
|
|
}
|
|
Vector3 furthestPoint = new Vector3();
|
|
foreach (GameObject p in players.Values)
|
|
{
|
|
if (p.IsDestroyed())
|
|
continue;
|
|
Vector3 position;
|
|
position = p.transform.localPosition;
|
|
center += position;
|
|
if ((position - center).magnitude > furthestPoint.magnitude)
|
|
{
|
|
furthestPoint = position - center;
|
|
}
|
|
}
|
|
center /= players.Count();
|
|
|
|
if (FarOutBias > 0 && players.Count > 2)
|
|
{
|
|
center += (center - furthestPoint) * FarOutBias;
|
|
}
|
|
|
|
return center;
|
|
}
|
|
|
|
private void FacePlayersCenter(Vector3 center)
|
|
{
|
|
var x = center.x;
|
|
var y = center.y;
|
|
var z = center.z;
|
|
x /= players.Count();
|
|
y /= players.Count();
|
|
|
|
|
|
float a = z - transform.localPosition.z * TiltFactor;
|
|
float cXAxis = (float)Sqrt(Pow(a, 2) + Pow(x, 2));
|
|
float cYAxis = (float)Sqrt(Pow(a, 2) + Pow(y, 2));
|
|
Vector3 xyRotation = new Vector3(Rad2Deg * Acos(a / cYAxis) * -Math.Sign(y),
|
|
Rad2Deg * Acos(a / cXAxis) * Math.Sign(x), 0);
|
|
if (transform.localEulerAngles != xyRotation / 2)
|
|
{
|
|
transform.localEulerAngles = xyRotation / 2;
|
|
}
|
|
|
|
}
|
|
|
|
private void CalculateMaxInterPlayerDistance()
|
|
{
|
|
if (players.Count < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float maxXDistance = 0;
|
|
float maxYDistance = 0;
|
|
|
|
|
|
foreach (GameObject player in players.Values)
|
|
{
|
|
var screenPos1 = cam.WorldToScreenPoint(player.transform.position);
|
|
foreach (GameObject p in players.Values)
|
|
{
|
|
if (p == player)
|
|
continue;
|
|
var screenPos2 = cam.WorldToScreenPoint(p.transform.position);
|
|
var distance = screenPos2 - screenPos1;
|
|
float xDistance = Abs(distance.x);
|
|
if (maxXDistance < xDistance)
|
|
maxXDistance = xDistance;
|
|
float yDistance = Abs(distance.y);
|
|
if (maxYDistance < yDistance)
|
|
maxYDistance = Abs(distance.y);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AdjustZoom()
|
|
{
|
|
if (players.Count < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Vector2 maxDistance = new Vector2(0, 0);
|
|
Vector2 minDistance = new Vector2(-1, -1);
|
|
|
|
foreach (GameObject player in players.Values)
|
|
{
|
|
var screenPos = cam.WorldToScreenPoint(player.transform.position);
|
|
var screenMiddle = new Vector2(Screen.width / 2, Screen.height / 2);
|
|
var distance = new Vector2
|
|
{
|
|
x = Math.Abs(screenMiddle.x - screenPos.x),
|
|
y = Math.Abs(screenMiddle.y - screenPos.y)
|
|
};
|
|
|
|
if (minDistance.x == -1)
|
|
minDistance.x = distance.x;
|
|
if (minDistance.y == -1)
|
|
minDistance.y = distance.y;
|
|
|
|
if (distance.x < minDistance.x)
|
|
minDistance.x = distance.x;
|
|
if (distance.y < minDistance.y)
|
|
minDistance.y = distance.y;
|
|
|
|
if (distance.x > maxDistance.x)
|
|
maxDistance.x = distance.x;
|
|
if (distance.y > maxDistance.y)
|
|
maxDistance.y = distance.y;
|
|
}
|
|
|
|
//Log.Debug(maxDistance);
|
|
|
|
|
|
if (maxDistance.x < _zoomInPixels.x
|
|
&& maxDistance.y < _zoomInPixels.y)
|
|
{
|
|
_switchZoomOutTime = TimeUntilZoomOut;
|
|
if (_switchZoomInTime > 0)
|
|
{
|
|
_switchZoomInTime -= Time.deltaTime;
|
|
return;
|
|
}
|
|
|
|
if (transform.localPosition.z < InitialDistance)
|
|
{
|
|
transform.localPosition += new Vector3(0, 0, ZoomInSpeed);
|
|
//Vector3 target = transform.localPosition + new Vector3(0, 0, MinZoomSpeed);
|
|
// transform.localPosition =
|
|
// Vector3.SmoothDamp(transform.localPosition, target, ref _currentZoomSpeed, 0.0008f);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (maxDistance.x > _zoomOutPixels.x
|
|
|| maxDistance.y > _zoomOutPixels.y)
|
|
{
|
|
_switchZoomInTime = TimeUntilZoomIn;
|
|
// if (_switchZoomOutTime > 0)
|
|
// {
|
|
// _switchZoomOutTime -= Time.deltaTime;
|
|
// return;
|
|
// }
|
|
|
|
if (transform.localPosition.z > MaxDistance)
|
|
{
|
|
transform.localPosition -= new Vector3(0, 0, ZoomOutSpeed);
|
|
//Vector3.SmoothDamp(transform.localPosition, target, ref _currentZoomSpeed, 0.0003f);
|
|
// transform.localPosition =
|
|
// Vector3.SmoothDamp(transform.localPosition, target, ref _currentZoomSpeed, MaxZoomSpeed);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void FollowPosition(Vector3 position)
|
|
{
|
|
Vector3 offset = position - transform.localPosition;
|
|
offset.z = 0;
|
|
|
|
Vector3 ignoreZ = new Vector3(transform.localPosition.x, transform.localPosition.y, 0);
|
|
if (offset.magnitude < 1)
|
|
{
|
|
return;
|
|
}
|
|
if (MaxFollowDistance != -1
|
|
&& ignoreZ.magnitude > MaxFollowDistance
|
|
&& (ignoreZ + offset).magnitude > ignoreZ.magnitude)
|
|
{
|
|
return;
|
|
}
|
|
|
|
transform.localPosition += offset / ((1 / Time.deltaTime) * (1 / FollowSpeed));
|
|
}
|
|
} |