Space-Smash-Out/Assets/Scripts/CameraOperator.cs

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));
}
}