首页 文章

unity3d(2d!) - 相机以玩家为中心,但绝不超过“ Map ”范围

提问于
浏览
4

我正在制作一款游戏,它有多达4个正交摄影机(最多4个玩家),根据玩家的数量占据不同数量的屏幕 .

2 cameras (with 0.7 each)

4 Cameras

我有一个控制所有摄像机的脚本,设置相对于他们正在观看的玩家的位置 .

我想要实现的是一种简单的头顶 - 跑步运动风格,相机将跟随玩家,但不会超出 Map 的范围 .

required behaviour

当摄像机为“方形”时(如在4人游戏布局中),我已设法在左上角的相机中工作 . 但是,其他相机根本没有正确跟踪,而在矩形双人模式下,顶部相机仍然左右移动太远 . 我很确定我确切地知道导致问题的代码行...但我不知道我需要做些什么来修复它...

SpriteRenderer spriteBounds = GameObject.Find("Map").GetComponentInChildren<SpriteRenderer>();
float leftBound =0;
float rightBound =0;
float bottomBound =0;
float topBound = 0;

if((trackPlayer1 == null))
{
    camPlayer1.transform.position = this.transform.position;
}
else
{
    float vertExtent = camPlayer1.orthographicSize;
    float horzExtent = vertExtent * Screen.width / Screen.height; //I guess the problem is here... but how do I fix this??

    leftBound = (float)(horzExtent - spriteBounds.sprite.bounds.size.x / 2.0f);
    rightBound = (float)(spriteBounds.sprite.bounds.size.x / 2.0f - horzExtent);
    bottomBound = (float)(vertExtent - spriteBounds.sprite.bounds.size.y / 2.0f);
    topBound = (float)(spriteBounds.sprite.bounds.size.y / 2.0f - vertExtent);

    camPlayer1.transform.position = new Vector3(Mathf.Clamp(trackPlayer1.transform.position.x, leftBound, rightBound), Mathf.Clamp(trackPlayer1.transform.position.y, bottomBound, topBound), camPlayer1.transform.position.z);
}
if((trackPlayer2 == null))
{
    camPlayer2.transform.position = this.transform.position;
}
else
{
    float vertExtent = camPlayer2.orthographicSize ;
    float horzExtent = vertExtent * Screen.width / Screen.height; //I guess the problem is here... but how do I fix this??

    leftBound = (float)(horzExtent - spriteBounds.sprite.bounds.size.x / 2.0f);
    rightBound = (float)(spriteBounds.sprite.bounds.size.x / 2.0f - horzExtent);
    bottomBound = (float)(vertExtent - spriteBounds.sprite.bounds.size.y / 2.0f);
    topBound = (float)(spriteBounds.sprite.bounds.size.y / 2.0f - vertExtent);

    camPlayer2.transform.position = new Vector3(Mathf.Clamp(trackPlayer2.transform.position.x, leftBound, rightBound), Mathf.Clamp(trackPlayer2.transform.position.y, topBound, bottomBound), camPlayer2.transform.position.z);
}
if((trackPlayer3 == null))
{
    camPlayer3.transform.position = this.transform.position;
}
else
{
    float vertExtent = camPlayer3.orthographicSize;
    float horzExtent = vertExtent * Screen.width / Screen.height; //I guess the problem is here... but how do I fix this??

    leftBound = (float)(horzExtent - spriteBounds.sprite.bounds.size.x / 2.0f);
    rightBound = (float)(spriteBounds.sprite.bounds.size.x / 2.0f - horzExtent);
    bottomBound = (float)(vertExtent - spriteBounds.sprite.bounds.size.y / 2.0f);
    topBound = (float)(spriteBounds.sprite.bounds.size.y / 2.0f - vertExtent);

    camPlayer3.transform.position = new Vector3(Mathf.Clamp(trackPlayer3.transform.position.x, leftBound, rightBound), Mathf.Clamp(trackPlayer3.transform.position.y, topBound, bottomBound), camPlayer3.transform.position.z);         
}
if((trackPlayer4 == null))
{
    camPlayer4.transform.position = this.transform.position;
}
else
{
    float vertExtent = camPlayer4.orthographicSize;
    float horzExtent = vertExtent * Screen.width / Screen.height; //I guess the problem is here... but how do I fix this??

    leftBound = (float)(horzExtent - spriteBounds.sprite.bounds.size.x / 2.0f);
    rightBound = (float)(spriteBounds.sprite.bounds.size.x / 2.0f - horzExtent);
    bottomBound = (float)(vertExtent - spriteBounds.sprite.bounds.size.y / 2.0f);
    topBound = (float)(spriteBounds.sprite.bounds.size.y / 2.0f - vertExtent);

    camPlayer4.transform.position = new Vector3(Mathf.Clamp(trackPlayer4.transform.position.x, leftBound, rightBound), Mathf.Clamp(trackPlayer4.transform.position.y, topBound, bottomBound), camPlayer4.transform.position.z);
}

所以我很确定我需要检查屏幕上的摄像机大小和相对位置,但我完全失去了我需要做的事情 .

(编辑) Script explanation:

  • 脚本是附加到主摄像机对象的全局脚本,玩家从未看到过该脚本

  • 四个播放器凸轮(camPlayer1 - camPlayer4)是公共变量并分配给设计器中的脚本

  • trackPlayer1-trackPlayer4是公共游戏对象,在设计器中分配 - 它们被分配给玩家对象

  • 玩家跟踪适用于所有凸轮...例如,如果我更改camPlayer2.transform.position = new Vector3(Mathf.Clamp(trackPlayer2.transform.position.x,leftBound,rightBound),Mathf.Clamp(trackPlayer2.transform.position) .y,topBound,bottomBound),camPlayer2.transform.position.z);到camPlayer2.transform.position = trackPlayer2.transform.position;,代码具有预期的效果,摄像机跟随播放器 . 只是夹在 Map 的边界上我遇到了问题

  • 相机的正交尺寸设置为2

在启动时将摄像机定位在屏幕上的代码:

switch (playerCount)
    {
        case 1:
            camPlayer1.enabled = true;
            camPlayer2.enabled = false;
            camPlayer3.enabled = false;
            camPlayer4.enabled = false;

            camPlayer1.rect = new Rect(0, 0, 1, 1);
            camPlayer1.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer2.rect = new Rect(0, 0, 0, 0);
            camPlayer2.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer3.rect = new Rect(0, 0, 0, 0);
            camPlayer3.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer4.rect = new Rect(0, 0, 0, 0);
            camPlayer4.orthographicSize = CamManager.CAMERA_SIZE;

            break;
        case 2:
            camPlayer1.enabled = true;
            camPlayer2.enabled = true;
            camPlayer3.enabled = false;
            camPlayer4.enabled = false;


            camPlayer1.rect = new Rect(0, 0.5f, 0.7f, 0.5f);
            camPlayer1.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer2.rect = new Rect(0.3f, 0, 0.7f, 0.5f);
            camPlayer2.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer3.rect = new Rect(0, 0, 0, 0);
            camPlayer3.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer4.rect = new Rect(0, 0, 0, 0);
            camPlayer4.orthographicSize = CamManager.CAMERA_SIZE;


            Destroy(play3);
            Destroy(play4);

            break;
        case 3:
            camPlayer1.enabled = true;
            camPlayer2.enabled = true;
            camPlayer3.enabled = true;
            camPlayer4.enabled = false;

            camPlayer1.rect = new Rect(0, 0.5f, 0.5f, 0.5f);
            camPlayer1.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer2.rect = new Rect(0.5f, 0.5f, 0.5f, 0.5f);
            camPlayer2.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer3.rect = new Rect(0.25f, 0, 0.5f, 0.5f);
            camPlayer3.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer4.rect = new Rect(0, 0, 0, 0);
            camPlayer4.orthographicSize = CamManager.CAMERA_SIZE;

            Destroy(play4);


            break;
        case 4:
            camPlayer1.enabled = true;
            camPlayer2.enabled = true;
            camPlayer3.enabled = true;
            camPlayer4.enabled = true;

            camPlayer1.rect = new Rect(0, 0.5f, 0.5f, 0.5f);
            camPlayer1.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer2.rect = new Rect(0.5f, 0.5f, 0.5f, 0.5f);
            camPlayer2.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer3.rect = new Rect(0, 0, 0.5f, 0.5f);
            camPlayer3.orthographicSize = CamManager.CAMERA_SIZE;

            camPlayer4.rect = new Rect(0.5f, 0, 0.5f, 0.5f);
            camPlayer4.orthographicSize = CamManager.CAMERA_SIZE;

            break;
    }
}

编辑,所以无论形状如何,我都能得到第一台相机(所以在双人模式中,使用矩形相机,玩家-1的凸轮将尊重 Map 的边界),使用下面的代码 . 我猜那时我需要对leftBound,rightBound,topBound和bottomBound应用一些偏移,这取决于其他凸轮的部分 . 如何 Build 和计算这些我不知道

if((trackPlayer1 == null))
{
 camPlayer1.transform.position = this.transform.position;
}
else
{
 float vertExtent = camPlayer1.orthographicSize;
 float horzExtent = vertExtent * (Screen.width * (camPlayer1.rect.width * 2)) / Screen.height; //I guess the problem is here... but how do I fix this??

4 回答

  • 10

    您的计算有几个问题 . 我想这只是运气,它适用于某些情况 . 虽然您的一般想法是正确的,但您使用错误的属性进行计算 . 基本上,您需要了解摄像机的纵横比, Map 的边界框以及摄像机在边界处的行为方式 .

    我们需要相机的纵横比来计算其宽度或范围 .
    aspect ratios and screen size

    正如你在图片中看到的那样, Screen.widthScreen.height 指的是游戏's window or the monitor resolution in case you'重新运行全屏游戏 . 您还可以看到,与游戏窗口相比,相机可能具有不同的宽高比 . 好消息是Unity提供了获取相机宽高比的属性 .

    float camVertExtent = cam.orthograpicSize;
    float camHorzExtent = cam.aspect * camVertExtent;
    

    现在我们有了相机's extents, let',看看你的 Map 及其边界框 .
    bounding box and camera position

    您尝试使用 bounds.size 进行计算,但正如您所见, bounds.size.x 是边界框的宽度 . 只有当 Map 的左下角从世界空间的(0,0)开始时,才能使用该大小 . 更好的方法是使用已经返回世界空间坐标的 bounds.minbounds.max .

    您的最终目标是摄像机应保持在 Map 的范围内,即其位置受以下四个条件的限制:

    (cam.transform.position.x - camHorzExtent) >= bounds.min.x // left
    (cam.transform.position.x + camHorzExtent) <= bounds.max.x // right
    (cam.transform.position.y - camVertExtent) >= bounds.min.y // bottom
    (cam.transform.position.y + camVertExtent) <= bounds.max.y // top
    

    现在您只需要取得玩家位置并将相机限制在以下条件:

    float leftBound   = bounds.min.x + camHorzExtent;
    float rightBound  = bounds.max.x - camHorzExtent;
    float bottomBound = bounds.min.y + camVertExtent;
    float topBound    = bounds.max.y - camVertExtent;
    
    float camX = Mathf.Clamp(player.transform.position.x, leftBound, rightBound);
    float camY = Mathf.Clamp(player.transform.position.y, bottomBound, topBound);
    
    cam.transform.position = new Vector3(camX, camY, cam.transform.position.z);
    

    如果所有相机具有相同的大小和宽高比,则可以对所有玩家相机使用相同的边界,并且仅为每个玩家计算 camXcamY .

  • 2

    我想建议你另一种方法来解决你的问题 .

    您是否可以在 Map 的边界添加高对撞机,以及相机的对撞机?然后你可以让相机跟随播放器(使用物理!),当玩家到达边界时,由于对撞机,相机不会翻过来 .

    然后,您可以轻松分割屏幕,了解您拥有多少玩家 . 一个球员? - >所有屏幕尺寸两名球员? - >摄像头1结束广告屏幕高度/ 2,摄像头2以屏幕高度/ 2 1开始,依此类推

  • -1

    大家好,对于一些需要完整代码的人来说,需要完成游戏对象的代码,并将许多儿童精灵带入Empty Gameobject,如:

    Background
    --mountains1(sprite)
    --mountains2(sprite)
    --mountains3(sprite)
    

    这段代码将循环到Background对象中并占据所有儿童精灵的界限:D,由于@Stefan Hoffmann的解释,我制作了一个Frankestein代码hahhaa . 我希望这会对别人有所帮助,对不起我的英文不好:(

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CameraFollow : MonoBehaviour
    {
    
        private float rightBound;
        private float leftBound;
        private float topBound;
        private float bottomBound;
        private Vector3 pos;
        private Transform target;
    
        private Camera cam;
        private Bounds bounds;
    
    
        // Use this for initialization
        void Start()
        {
            target = GameObject.FindWithTag("Player").transform;
    
    
            foreach (SpriteRenderer spriteBounds in GameObject.Find("Background").GetComponentsInChildren<SpriteRenderer>())
            {
                bounds.Encapsulate(spriteBounds.bounds);
            }
    
    
            cam = this.gameObject.GetComponent<Camera>();
            float camVertExtent = cam.orthographicSize;
            float camHorzExtent = cam.aspect * camVertExtent;
    
            Debug.Log(camVertExtent);
            Debug.Log(camHorzExtent);
            Debug.Log(cam.aspect);
            Debug.Log(cam.orthographicSize);
    
    
    
            leftBound = bounds.min.x + camHorzExtent;
            rightBound = bounds.max.x - camHorzExtent;
            bottomBound = bounds.min.y + camVertExtent;
            topBound = bounds.max.y - camVertExtent;
    
            Debug.Log("leftBound=" + leftBound);
            Debug.Log("rightBound=" + rightBound);
            Debug.Log("bottomBound=" + bottomBound);
            Debug.Log("topBound=" + topBound);
        }
    
        // Update is called once per frame
        void Update()
        {
    
    
            float camX = Mathf.Clamp(target.transform.position.x, leftBound, rightBound);
            float camY = Mathf.Clamp(target.transform.position.y, bottomBound, topBound);
    
            cam.transform.position = new Vector3(camX, camY, cam.transform.position.z);
        }
    
    }
    
  • 0

    你应该使用这个简单但有用的unity 2d camera follow script
    也可以在github上找到 . https://gist.github.com/unity3diy/5aa0b098cb06b3ccbe47


    using UnityEngine;
    using System.Collections;
    
    public class FollowCamera : MonoBehaviour {
    
    public float interpVelocity;
    public float minDistance;
    public float followDistance;
    public GameObject target;
    public Vector3 offset;
    Vector3 targetPos;
    // Use this for initialization
    void Start () {
        targetPos = transform.position;
    }
    
    // Update is called once per frame
    void FixedUpdate () {
        if (target)
        {
            Vector3 posNoZ = transform.position;
            posNoZ.z = target.transform.position.z;
    
            Vector3 targetDirection = (target.transform.position - posNoZ);
    
            interpVelocity = targetDirection.magnitude * 5f;
    
            targetPos = transform.position + (targetDirection.normalized * interpVelocity * Time.deltaTime); 
    
            transform.position = Vector3.Lerp( transform.position, targetPos + offset, 0.25f);
    
        }
      }
     }
    

相关问题