There is also overhauled camera zoom behavior, an update to the match logic, tweaks to the main menu scene.
263 lines
6.4 KiB
C#
263 lines
6.4 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
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]
|
|
private float ZoomOutMargin = Screen.height / 4;
|
|
[SerializeField]
|
|
private float ZoomInMargin = Screen.height / 3;
|
|
[SerializeField]
|
|
private float MinZoomSpeed = 0.4f;
|
|
[SerializeField]
|
|
private float MaxZoomSpeed = 0.5f;
|
|
|
|
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;
|
|
}
|
|
|
|
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 FixedUpdate()
|
|
{
|
|
Vector3 center = CalculatePlayersCenter();
|
|
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();
|
|
|
|
// TODO: Competing furthest points cause jitter
|
|
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 Margin = Screen.height / 2;
|
|
|
|
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;
|
|
}
|
|
|
|
float maxXPos = 0;
|
|
float maxYPos = 0;
|
|
float minXPos = -1;
|
|
float minYPos = -1;
|
|
|
|
foreach (GameObject player in players.Values)
|
|
{
|
|
var screenPos = cam.WorldToScreenPoint(player.transform.position);
|
|
if (minXPos == -1)
|
|
minXPos = screenPos.x;
|
|
if (minYPos == -1)
|
|
minYPos = screenPos.y;
|
|
if (screenPos.x < minXPos)
|
|
minXPos = screenPos.x;
|
|
if (screenPos.y < minYPos)
|
|
minYPos = screenPos.y;
|
|
if (screenPos.x > maxXPos)
|
|
maxXPos = screenPos.x;
|
|
if (screenPos.y > maxYPos)
|
|
maxYPos = screenPos.y;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (maxXPos < Screen.width - ZoomInMargin
|
|
&& maxYPos < Screen.height - ZoomInMargin
|
|
&& minXPos > ZoomInMargin
|
|
&& minYPos > ZoomInMargin)
|
|
// Anti-Shake
|
|
// && (ZoomTriggerXDistance < maxXDistance - 3f
|
|
// || ZoomTriggerYDistance < maxYDistance - 3f))
|
|
{
|
|
if (transform.localPosition.z < InitialDistance)
|
|
{
|
|
Vector3 target = transform.localPosition + new Vector3(0, 0, MinZoomSpeed);
|
|
transform.localPosition =
|
|
Vector3.SmoothDamp(transform.localPosition, target, ref _currentZoomSpeed, 0.0003f);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (maxXPos > Screen.width - ZoomOutMargin
|
|
|| maxYPos > Screen.height - ZoomOutMargin
|
|
|| minXPos < ZoomOutMargin
|
|
|| minYPos < ZoomOutMargin)
|
|
{
|
|
if (transform.localPosition.z > MaxDistance)
|
|
{
|
|
Vector3 target = transform.localPosition - new Vector3(0, 0, MaxZoomSpeed);
|
|
transform.localPosition =
|
|
Vector3.SmoothDamp(transform.localPosition, target, ref _currentZoomSpeed, 0.0007f);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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));
|
|
}
|
|
} |