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

相关文章
相关标签/搜索