Unity third-person camera

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/wangjiangrong/article/details/93202185

Today chicken is to achieve simple simulation game under third-person camera.

Observed

We played chicken hand travel all know, chicken characters followed camcorder has two states

1. Slide the screen when the camera move around, people will follow and move around, up and down looked down (corresponding animation playback, but does not move up and down). Rear view camera has maintained a character. Can rotate 360 ​​degrees around, the upper and lower limit will have a maximum and a minimum, can not reach 0 degrees look up or look down 90 degrees.

2. Hold down the button and drag the top right small eyes, the camera move around, people will not follow turning left and right, up and down around the head will swing. Almost 360-degree view figure. Up and down about the same in the case of a limit.

Without considering the turning of the head, the two cases may be summarized camera rotates around the rules are the same person, that is a moving sphere of fixed radius is formed, is removed and the uppermost part of the lowermost sphere, Perspective always look to the center of the sphere. When the case of sliding screen, the characters also will spin around, keep the front and back cameras observe the characters.

analysis

According to the phenomenon of performance, we can analyze the logic we need to implement. We can imagine a line directly behind the character Line, direction (0,0, -1), the camera distance Distance from the figures, the initial value of the position of the camera (0,0, -distance)

When the 360-degree camera move, we can move operation is split into two steps, the first camera is moved vertically, i.e. characters as the center, the radius of the distance moved on a circle. Provided vertical angle α (0 <min <α < max <90), the height of the camera y-axis is the distance * sin α, the characters from the z-axis distance of distance * cos α. Position of the camera at this time is (0, distance * sin α, -distance * cos α)

Before then, based on our horizontal movement of the camera, i.e. the height of the head character distance * sin α is the center point, the radius of movement on a circle of distance * cos α. Horizontal set angle β, the camera x-axis is the COS * [alpha] * SiN Distance β, Z-axis distance is updated to the distance * cos α * cos β.

Therefore, we can use the two angles α, β, controls the rotation of the camera, the vertical movement value [alpha] updated, update move around beta] value, the position of the camera (distance * cos α * sin β, distance * sin α, -distance * cos α * cos β), always look to the camera (LookAt) People

Then we have to distinguish between the state of the characters in both cases, when the sliding screen, people will follow along in the opposite direction of rotation, so as to ensure the camera is always watching the back of the figure. So when the camera is turned β degree level, people should turn to (0, -β, 0). While holding down the small eyes, the characters do not need to move with it, but when the release small eyes, the camera needs to return to the original position, so in the press, we need to record the offset angle of the camera is pressed to when released for recovery.

achieve

Demo We have a total of four cs file, a small eye control buttons, a sliding screen control, a control character movement, and the last is to control the camera.

There is need to build a simple test scenarios, which simulate the role of a box, do a ground plane, and then add a Button UGUI the eyes of a small button.

First, the characters move controller PlayerController, is to do a simple displacement

using UnityEngine;

namespace Test
{
    public class PlayerController : MonoBehaviour
    {
        //速度转换
        const float SPEED_CONVERTER = 0.3f;

        void Update()
        {
            float inputV = Input.GetAxis("Vertical");
            float inputH = Input.GetAxis("Horizontal");

            this.transform.position += this.transform.forward * inputV * SPEED_CONVERTER;
            this.transform.position += this.transform.right * inputH * SPEED_CONVERTER;
        }
    }
}

We followed the camera controller ThridPlayerCameraController, the rotation operation of the camera

using UnityEngine;

namespace CameraTool
{
    [RequireComponent(typeof(Camera))]
    public class ThridPlayerCameraController : MonoBehaviour
    {
        [SerializeField] Transform m_target;
        //相机与人物距离
        [SerializeField] float m_distance = 5;
        //初始化的偏移角度,以人物的(0,0,-1)为基准
        [SerializeField] float m_offsetAngleX = 0;
        [SerializeField] float m_offsetAngleY = 45;

        //相机与人物的坐标的偏移量
        Vector3 m_offsetVector;
        //纪录偏移角度用于复原
        float m_recordAngleX;
        float m_recordAngleY;
        //相机是否在旋转,旋转中需要一直重新计算 m_offsetVector
        bool m_isRotateing = false;

        //弧度,用于Mathf.Sin,Mathf.Cos的计算
        const float ANGLE_CONVERTER = Mathf.PI / 180;

        //相机上下的最大最小角度
        const float MAX_ANGLE_Y = 80;
        const float MIN_ANGLE_Y = 10;

        Transform m_trans;
        public Transform mineTransform
        {
            get
            {
                if (m_trans == null)
                {
                    m_trans = this.transform;
                }
                return m_trans;
            }
        }

        GameObject m_go;
        public GameObject mineGameObject
        {
            get
            {
                if (m_go == null)
                {
                    m_go = this.gameObject;
                }
                return m_go;
            }
        }

        void Start()
        {
            CalculateOffset();
        }

        void LateUpdate()
        {
            //相机坐标 = 人物坐标 + 偏移坐标
            mineTransform.position = m_target.position + m_offsetVector;
            mineTransform.LookAt(m_target);
        }

        void Update()
        {
            if (m_isRotateing)
            {
                CalculateOffset();
            }
        }

        //计算偏移,可以想象成在一个球面转,m_distance为半径,m_offsetAngleY决定了相机的高度y
        //高度确定后,就是在一个圆面上转,根据m_offsetAngleX计算出x与z
        void CalculateOffset()
        {
            m_offsetVector.y = m_distance * Mathf.Sin(m_offsetAngleY * ANGLE_CONVERTER);
            float newRadius = m_distance * Mathf.Cos(m_offsetAngleY * ANGLE_CONVERTER);
            m_offsetVector.x = newRadius * Mathf.Sin(m_offsetAngleX * ANGLE_CONVERTER);
            m_offsetVector.z = -newRadius * Mathf.Cos(m_offsetAngleX * ANGLE_CONVERTER);
        }

        //开始旋转,纪录当前偏移角度,用于复原
        public void StartRotate()
        {
            m_isRotateing = true;

            m_recordAngleX = m_offsetAngleX;
            m_recordAngleY = m_offsetAngleY;
        }

        //旋转,修改偏移角度的值,屏幕左右滑动即修改m_offsetAngleX,上下滑动修改m_offsetAngleY
        public void Rotate(float x, float y)
        {
            if (x != 0)
            {
                m_offsetAngleX += x;
            }
            if (y != 0)
            {
                m_offsetAngleY += y;
                m_offsetAngleY = m_offsetAngleY > MAX_ANGLE_Y ? MAX_ANGLE_Y : m_offsetAngleY;
                m_offsetAngleY = m_offsetAngleY < MIN_ANGLE_Y ? MIN_ANGLE_Y : m_offsetAngleY;
            }
        }

        //旋转结束,如需要复原镜头则,偏移角度还原并计算偏移坐标
        public void EndRotate(bool isNeedReset = false)
        {
            m_isRotateing = false;

            if (isNeedReset)
            {
                m_offsetAngleY = m_recordAngleY;
                m_offsetAngleX = m_recordAngleX;
                CalculateOffset();
            }
        }
    }
}

Followed by two and UI controls UIController SmallEyeController, achieve IDragHandler, IBeginDragHandler, IEndDragHandler Interface

using UnityEngine;
using UnityEngine.EventSystems;

namespace Test
{
    public class UIController : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
    {
        [SerializeField] Transform m_target;
        [SerializeField] CameraTool.ThridPlayerCameraController m_thridCamera;

        //滑动的偏移转换为角度偏移的系数
        const float DRAG_TO_ANGLE = 0.5f;
        Vector2 m_previousPressPosition;
        float m_angleX, m_angleY;

        public void OnBeginDrag(PointerEventData eventData)
        {
            m_previousPressPosition = eventData.position;
            m_thridCamera.StartRotate();
        }

        public void OnDrag(PointerEventData eventData)
        {
            //滑动屏幕,人物和摄像机一起旋转(人物只转Y轴,即左右旋转)
            m_angleX = (eventData.position.x - m_previousPressPosition.x) * DRAG_TO_ANGLE;
            m_angleY = (eventData.position.y - m_previousPressPosition.y) * DRAG_TO_ANGLE;
            m_thridCamera.Rotate(-m_angleX, -m_angleY);
            m_target.Rotate(new Vector3(0, m_angleX, 0));
            m_previousPressPosition = eventData.position;
        }

        public void OnEndDrag(PointerEventData eventData)
        {
            m_thridCamera.EndRotate();
        }
    }
}
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

namespace Test{
	public class SmallEyeController : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
    {
        [SerializeField] CameraTool.ThridPlayerCameraController m_thridCamera;

        const float DRAG_TO_ANGLE = 0.5f;
        Vector2 m_previousPressPosition;
        float m_angleX, m_angleY;
        Image m_image;

        void Start()
        {
            m_image = GetComponent<Image>();
        }

        public void OnBeginDrag(PointerEventData eventData)
        {
            m_previousPressPosition = eventData.position;
            m_thridCamera.StartRotate();
            m_image.color = Color.gray;
        }

        public void OnDrag(PointerEventData eventData)
        {
            //滑动小眼睛按钮,只旋转摄像机
            m_angleX = (eventData.position.x - m_previousPressPosition.x) * DRAG_TO_ANGLE;
            m_angleY = (eventData.position.y - m_previousPressPosition.y) * DRAG_TO_ANGLE;
            m_thridCamera.Rotate(-m_angleX, -m_angleY);
            m_previousPressPosition = eventData.position;
        }

        public void OnEndDrag(PointerEventData eventData)
        {
            m_image.color = Color.white;
            m_thridCamera.EndRotate(true);
        }
	}
}

Renderings

Guess you like

Origin blog.csdn.net/wangjiangrong/article/details/93202185