Unity實現一個基本的2D相機控制器-01

先上效果圖dom

Unity2D相機
該相機控制器目前有如下功能:
ide

  1. 跟隨主角移動
  2. 平滑移動,可設置x軸y軸的跟進速度
  3. 主角在一段距離內自由活動可不影響相機跟進
  4. 相機在場景四周的活動限制,以及x軸和y軸的移動限制
  5. 震屏

1、總體思路

首先要使相機跟隨主角,只須要獲取到主角位置,而後挪動相機到該位置便可。
以後咱們但願相機在移動的過程當中有一個平滑的移動,像是相機在一直追趕主角的效果,因而使用Vector2.Lerp去作一個插值移動。
測試

目前相機已經能夠跟隨主角移動,可是隻要主角一動,相機便會跟着動,咱們但願主角可以有一個自由活動的空間,在這個範圍內,相機不會跟隨主角,以下圖所示:
Unity2D相機
這咱們只須要設置一個範圍,只要主角跑出去,便執行相機跟隨,直到相機的X座標與角色的X座標之差小於一個較小的值爲止,便認爲角色已位於相機中心。

動畫

通常來講場景都有一個邊界,角色抵達邊界的時候,相機不該該拍到邊界以外的物體。
2D相機
如上圖,咱們只但願相機在紅框範圍內移動,而且只但願相機在X軸上移動。
因而在咱們但願的極限位置上設置四個標記物體,而且爲相機添加BoxCollider2D組件求出相機的邊界座標,只要相機抵達邊界,便不在讓其移動。


ui

最後使用DOTween添加一個經常使用到的震屏效果。spa


2、代碼實現

using DG.Tweening;
using UnityEngine;

[RequireComponent(typeof(BoxCollider2D))]
public class MainCameraController : MonoBehaviour
{ 
    private Camera mainCamera; //獲取主相機
    private Transform playerPos; //獲取主角位置
    private BoxCollider2D boxCol; //Trigger, 用來計算相機邊界位置

    private float cameraHalfWidth; //相機半長
    private float cameraHalfHeight; //相機半高

    public Vector2 centerOffset; //中心位置的偏移
    [Range(0, 4)] public float centerArea; //主角自由活動位置,此範圍相機不跟隨

    public float moveSpeedX; //X跟進速度
    public float moveSpeedY; //Y跟進速度

    [Header("Raw Froze")]
    public bool frozeX = false; //鎖住X軸移動
    public bool frozeY = false; //鎖住Y軸移動

    [Header("Camera Limit")]
    //場景四方向限制,Transfrom用來獲取極限位置座標,隨後Destroy掉
    public Transform leftLimitTrans; 
    public Transform rightLimitTrans;
    public Transform topLimitTrans;
    public Transform bottomLimitTrans;
    private float leftLimit = 0;
    private float rightLimit = 0;
    private float topLimit = 0;
    private float bottomLimit = 0;

	//DOTween震動相關參數
    [Header("Camera Shake")]
    public float shakeDuration = 0.5f; //震動週期
    public float shakeStrength = 0.2f; //震動強度
    public int shakeVibrato = 15; //震動次數
    [Range(0, 180)] public float shakeRandomness = 180; //震動隨機性 0-180
    public bool shakeFadeOut = true; //是否淡出
    public Ease shakeEase = Ease.InExpo; //動畫曲線

	//爲全部腳本提供的靜態震動入口
    private static bool shake = false;

    private void Awake()
    { 
    	//各組件的獲取
        mainCamera = Camera.main;
        playerPos = GameObject.FindGameObjectWithTag("Player").transform;
#if UNITY_EDITOR
        if (mainCamera == null)
        { 
            Debug.LogWarning("Main camera is null!");
            enabled = false;
            return;
        }
        if (playerPos == null)
        { 
            Debug.LogWarning("Player can`t be find by tag 'Player'!");
            enabled = false;
            return;
        }
        else
        { 
            Debug.Log("MainCamera follow player object named " + playerPos.gameObject.name);
        }
#endif
		//獲取Trigger, 並計算相機半長、半寬
        boxCol = GetComponent<BoxCollider2D>();
        cameraHalfWidth = boxCol.size.x * 0.5f;
        cameraHalfHeight = boxCol.size.y * 0.5f;
        boxCol.enabled = false;

        InitLimit(); //↓↓↓
    }

InitLimit()code

//設置四周極限距離,而後銷燬
	//無則設置爲 最大/最小
    private void InitLimit()
    { 
        if (leftLimitTrans != null)
        { 
            leftLimit = leftLimitTrans.position.x;
            Destroy(leftLimitTrans.gameObject);
        }
        else
            leftLimit = int.MinValue;
            
        if (topLimitTrans != null)
        { 
            topLimit = topLimitTrans.position.y;
            Destroy(topLimitTrans.gameObject);
        }
        else
            topLimit = int.MaxValue;

		//bottom和right 略
    }
private void LateUpdate()
    { 
    	//相機跟隨角色
        CameraFollow((Vector2)playerPos.position + centerOffset);
        //相機活動限制
        CameraLimit();
		//若是shake爲true則執行一次震屏
        if (shake) ShakeCameraHandler();
    }

	//若角色超出自由活動範圍,則followX設置爲true,使相機跟隨角色
	//若角色到達相機視野中央,則followX設置爲false
    private bool followX = false;
    private void CameraFollow(Vector2 targetPos)
    { 
        float currentX = mainCamera.transform.position.x;
        float currentY = mainCamera.transform.position.y;
		//未鎖住X移動
        if (!frozeX)
        { 
        	//判斷角色是否超出自由活動範圍,是則設置follow爲true開始x軸的跟隨
        	//可簡化代碼
            if (targetPos.x > currentX + centerArea ||
                targetPos.x < currentX - centerArea)
            { 
                followX = true;
            }
            //執行x軸的跟隨
            if (followX)
            { 
            	//求相機這一幀的位置
                currentX = Mathf.Lerp(currentX, targetPos.x, moveSpeedX);
                //做差小於0.1f認爲角色到達視野中央,則中止跟隨
                if (Mathf.Abs(targetPos.x - currentX) < 0.1f)
                { 
                    followX = false;
                }
            }
        }
        //未鎖住Y軸移動
        if (!frozeY)
        { 
            currentY = Mathf.Lerp(currentY, targetPos.y, moveSpeedY);
        }
		//設置相機位置,Z軸不變
        mainCamera.transform.position = new Vector3(
            currentX, currentY, mainCamera.transform.position.z);
    }

    //邊界限制
    private void CameraLimit()
    { 
        float posX = mainCamera.transform.position.x;
        float posY = mainCamera.transform.position.y;
		
		/* 以posX爲例, LeftBoard()求出相機左邊界,判斷是否超出左極限 是 則posX等於左極限 + 相機半寬 否 則判斷右邊界是否超出右極限 是 則posX等於右極限 - 相機半寬 否 則posX不變,等於自身 */
        posX = LeftBoard() < leftLimit ? leftLimit + cameraHalfWidth :
                RightBoard() > rightLimit ? rightLimit - cameraHalfWidth : posX;
        posY = BottomBoard() < bottomLimit ? bottomLimit + cameraHalfHeight :
                TopBoard() > topLimit ? topLimit - cameraHalfHeight : posY;

        mainCamera.transform.position = new Vector3(
                posX, posY,
                mainCamera.transform.position.z
            );
    }

#if UNITY_EDITOR
	//在Inspector窗口測試震動
    [ContextMenu("RunMode-ShakeCamera")]
    public void ShakeCameraInEditor()
    { 
        ShakeCamera();
    }
#endif
	//對外暴露的接口,設置shake爲true,在LateUpdate中執行震動
    public static void ShakeCamera()
    { 
        shake = true;
    }
	//shake爲true時在LateUpdate中調用
    private void ShakeCameraHandler()
    { 
        shake = false;
        /* (震動週期 震動力度 震動次數 震動隨機性 是否淡出). (動畫曲線) */
        Camera.main.DOShakePosition(
                shakeDuration,
                shakeStrength,
                shakeVibrato,
                shakeRandomness,
                shakeFadeOut
            ).SetEase(shakeEase);
    }

    //獲取相機邊界位置
    private float LeftBoard()
    { 
        return mainCamera.transform.position.x - cameraHalfWidth;
    }
    private float RightBoard()
    { 
        return mainCamera.transform.position.x + cameraHalfWidth;
    }
    private float TopBoard()
    { 
        return mainCamera.transform.position.y + cameraHalfHeight;
    }
    private float BottomBoard()
    { 
        return mainCamera.transform.position.y - cameraHalfHeight;
    }
}

若有錯誤,懇請糾正
下接:
orm

相關文章
相關標籤/搜索