调用
摄像机开始跟随角色
CameraMMO.CameraFollow(gameObject, GameObject.FindGameObjectWithTag("Player").transform, Camera.main.transform).enabled = true;
//不跟随则相反
CameraMMO.CameraFollow(gameObject, GameObject.FindGameObjectWithTag("Player").transform, Camera.main.transform).enabled = false;
CameraMMO.cs
// We developed a simple but useful MMORPG style camera. The player can zoom in
// and out with the mouse wheel and rotate the camera around the hero by holding
// down the right mouse button.
//
// Note: we turned off the linecast obstacle detection because it would require
// colliders on all environment models, which means additional physics
// complexity (which is not needed due to navmesh movement) and additional
// components on many gameobjects. Even if performance is not a problem, there
// is still the weird case where if a tent would have a collider, the inside
// would still be part of the navmesh, but it's not clickable because of the
// collider. Clicking on top of that collider would move the agent into the tent
// though, which is not very good. Not worrying about all those things and
// having a fast server is a better tradeoff.
using UnityEngine;
/// <summary>
/// 一款简单但实用的MMORPG风格相机。
/// 1.跟随玩家
/// 2.缩小拉镜与玩家的距离
/// 3.按下鼠标右键 摄像机绕角色旋转
/// </summary>
public class CameraMMO : MonoBehaviour
{
Transform targetT;
Transform cameraT;
public int mouseButton = 1; // right button by default
public float distance = 20;
public float minDistance = 3;
public float maxDistance = 25;
public float zoomSpeedMouse = 1;
public float zoomSpeedTouch = 0.2f;
public float rotationSpeed = 2;
public float xMinAngle = -40;
public float xMaxAngle = 80;
float rightDownDuration = 0.75f;
private float timer = 0f;
bool startRotate = false;
// the target position can be adjusted by an offset in order to foucs on a
// target's head for example
public Vector3 offset = Vector3.zero;
// the layer mask to use when trying to detect view blocking
// (this way we dont zoom in all the way when standing in another entity)
// (-> create a entity layer for them if needed)
//public LayerMask layerMask;
// store rotation so that unity never modifies it, otherwise unity will put
// it back to 360 as soon as it's <0, which makes a negative min angle
// impossible
Vector3 rot;
/// <summary>
/// 跟随角色
/// </summary>
/// <param name="go">CameraMMOcs挂在哪个物体上执行</param>
/// <param name="targetT">角色</param>
/// <param name="cameraT">摄像机</param>
/// <returns></returns>
public static CameraMMO CameraFollow(GameObject go,Transform targetT, Transform cameraT)
{
CameraMMO _cameraMMO = go.GetComponent<CameraMMO>();
if (_cameraMMO == null)
{
_cameraMMO = go.AddComponent<CameraMMO>();
_cameraMMO.Init(targetT, cameraT);
}
return _cameraMMO;
}
#region Init() 初始化
bool isInit { get; set; }
void Init(Transform targetT, Transform cameraT)
{
if (false == isInit)
{
this.targetT = targetT;
this.cameraT = cameraT;
cameraT.position = targetT.position - (cameraT.rotation * Vector3.forward * distance);
rot = cameraT.eulerAngles;
isInit = true;
}
}
#endregion
void LateUpdate()
{
if (isInit==false || targetT ==null) return;
var targetPos = targetT.position + offset;
// rotation and zoom should only happen if not in a UI right now
if (!IsCursorOverUserInterface())
{
// right mouse rotation if we have a mouse
if (Input.mousePresent)
{
if (Input.GetMouseButtonDown(1))
{
//Debug.Log ( "鼠标右键瞬间按下=" + Time.realtimeSinceStartup );
timer = Time.realtimeSinceStartup;
startRotate = false;
}
if (Input.GetMouseButton(1))
{
// Debug.Log ( "鼠标右键按下中=" + Time.realtimeSinceStartup + "))result=" +( timer + time <= Time.realtimeSinceStartup?"true":"false ") );
if (timer + rightDownDuration <= Time.realtimeSinceStartup)
{
startRotate = true;
}
}
if (Input.GetMouseButton(mouseButton) && startRotate)
{
// note: mouse x is for y rotation and vice versa
rot.y += Input.GetAxis("Mouse X") * rotationSpeed;
rot.x -= Input.GetAxis("Mouse Y") * rotationSpeed;
rot.x = Mathf.Clamp(rot.x, xMinAngle, xMaxAngle);
cameraT.rotation = Quaternion.Euler(rot.x, rot.y, 0);
}
}
else
{
// forced 45 degree if there is no mouse to rotate (for mobile)
cameraT.rotation = Quaternion.Euler(new Vector3(45, 0, 0));
}
// zoom
float speed = Input.mousePresent ? zoomSpeedMouse : zoomSpeedTouch;
float step = GetZoomUniversal() * speed;
distance = Mathf.Clamp(distance - step, minDistance, maxDistance);
}
// target follow
cameraT.position = targetPos - (cameraT.rotation * Vector3.forward * distance);
}
static float GetZoomUniversal()
{
if (Input.mousePresent)
return GetAxisRawScrollUniversal();
else if (Input.touchSupported)
return GetPinch();
return 0;
}
// two finger pinch detection
// source: https://docs.unity3d.com/Manual/PlatformDependentCompilation.html
static float GetPinch()
{
if (Input.touchCount == 2)
{
// Store both touches.
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
// Find the position in the previous frame of each touch.
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
// Find the magnitude of the vector (the distance) between the touches in each frame.
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
// Find the difference in the distances between each frame.
return touchDeltaMag - prevTouchDeltaMag;
}
return 0;
}
static bool IsCursorOverUserInterface()
{
IsPointerOverGameObject check for left mouse (default)
//if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject())
// return true;
IsPointerOverGameObject check for touches
//for (int i = 0; i < Input.touchCount; ++i)
// if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject(Input.GetTouch(i).fingerId))
// return true;
OnGUI check
//return GUIUtility.hotControl != 0;
return false;
}
// hard mouse scrolling that is consistent between all platforms
// Input.GetAxis("Mouse ScrollWheel") and
// Input.GetAxisRaw("Mouse ScrollWheel")
// both return values like 0.01 on standalone and 0.5 on WebGL, which
// causes too fast zooming on WebGL etc.
// normally GetAxisRaw should return -1,0,1, but it doesn't for scrolling
static float GetAxisRawScrollUniversal()
{
float scroll = Input.GetAxisRaw("Mouse ScrollWheel");
if (scroll < 0) return -1;
if (scroll > 0) return 1;
return 0;
}
}