unity3d (2d!) - 相机以玩家为中心,但永远不会超过“地图”边界

Posted

技术标签:

【中文标题】unity3d (2d!) - 相机以玩家为中心,但永远不会超过“地图”边界【英文标题】:unity3d (2d!) - Camera to centre on player, but never exceed "map" bounds 【发布时间】:2015-01-03 07:48:58 【问题描述】:

我正在创建一个游戏,它最多有 4 个正交摄像机(最多可容纳 4 名玩家),它们会根据玩家的数量占用不同数量的屏幕。

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

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

当摄像机为“方形”时(如在 4 人布局中),我已设法在左上角的摄像机中设置边界。但是,其他摄像头根本无法正常跟踪,并且在矩形 2 人模式下,顶部摄像头仍然左右移动太远。我很确定我确切地知道是哪一行代码导致了问题......但我不知道我需要做些什么来解决它......

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);

所以我很确定我需要检查相机的大小和屏幕上的相对位置,但我不知道我需要做什么。

(编辑) 脚本说明:

脚本是附加到主摄像机对象的全局脚本,玩家永远看不到它 四个玩家凸轮 (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;,代码有预期的效果,相机跟随玩家。我遇到的问题只是限制在地图的边界上 相机的正交尺寸设置为 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;
    

编辑,所以无论形状如何,我都可以使用下面的代码让第一个摄像机进行牵引(所以在 2 人模式下,使用矩形摄像机,player-1 cam 将尊重地图的边界)。我猜我需要根据其他凸轮的矩形对 leftBound、rightBound、topBound 和 bottomBound 应用一些偏移量。如何建立和计算这些我不知道

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??

【问题讨论】:

你的代码很难理解。如果您添加一些 cmets 来解释您在某些部分尝试计算的内容,将会很有帮助。您是否考虑到 orthographicSize 属性是屏幕垂直长度的一半而不是全长? 所以我对 Unity 不是很熟悉,但我想你计算纵横比的方式至少是问题的一部分。您将每个相机的纵横比计算为Screen.width / Screen.height,但显然您不会渲染到整个屏幕区域。您需要使用相机的纵横比,这可能与屏幕的纵横比不同。 看Unity的Camera类,大概可以使用相机的rect属性来计算每个相机的宽高比。 @Homar。我不认为这是问题所在。 ex 在 4-plyr 模式下,使用当前脚本,cam1 跟踪玩家并尊重地图/背景的边界。它与其他相机的纵横比相同,它们不尊重边界(实际上,当应用钳位时,跟踪完全关闭,甚至没有聚焦在玩家身上)。我想你是对的,它与凸轮的矩形有 2 点关系,但不幸的是,我不知道是什么。我尝试使用 cam1.cameraToWorldPoint() 转换顶部、底部、左侧和右侧边界,然后使用 cam2.worldToCameraPoint,但这只会让事情变得更加奇怪。 我认为您可能有很多问题。例如,SpriteRenderer::bounds::size 是 AABB 的尺寸,不考虑位置。您可能应该使用Boundsminmax 属性来获取坐标扩展。 【参考方案1】:

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

我们需要相机的纵横比来计算它的宽度或范围。

正如您在图片中看到的,Screen.widthScreen.height 指的是游戏的窗口或显示器分辨率,以防您全屏运行游戏。您还可以看到,与游戏窗口相比,摄像机可能具有不同的纵横比。好消息是 Unity 提供了一个属性来获取相机的纵横比。

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

现在我们有了相机的范围,让我们看看您的地图及其边界框。

您尝试使用bounds.size 进行计算,但您可以看到bounds.size.x 是边界框的宽度。仅当地图的左下角从世界空间中的 (0,0) 开始时,使用该大小才有效。更好的方法是使用已经返回世界空间坐标的bounds.minbounds.max

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

(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

【讨论】:

您好。我已按照建议修改了代码。在 4 人模式下,玩家 2 和 3(分别位于屏幕右上角和左下角)正确跟踪,但 1 和 4 关闭。在 2 播放器模式下,播放器 2(右下角!)正确跟踪,但播放器 1 仅垂直跟踪,而不是水平跟踪。 @LairdPleng 我猜有一些复制和粘贴错误。检查您是否为每个摄像机使用了正确的播放器和摄像机变量。如果找不到错误,可以将代码粘贴到 pastebin.com 并在评论中链接。 令人惊讶的错别字,疲惫的眼睛可能会错过......是的,在 cam1 和 4 上确实存在一些 c&p 错误。很好的答案,很好的解释。谢谢。【参考方案2】:

我想建议您解决问题的另一种方法。

是否可以在地图边界添加高碰撞器,并为相机添加碰撞器?然后,您可以让相机跟随玩家(使用物理!),当玩家到达边界时,相机不会越过,因为对撞机。

然后,您可以轻松地拆分屏幕,了解您有多少玩家。 一名球员? --> 所有屏幕尺寸 两名球员? --> 摄像机 1 结束广告屏幕高度 / 2,摄像机 2 开始于屏幕高度 /2 +1 等等

【讨论】:

地图已经有碰撞器,可以防止玩家离开该区域。使用物理学并在相机中添加对撞机的想法听起来……令人困惑。你会如何建议这样做?您是否有任何指向以前使用过该方法的来源的链接? 它就像在 MMORPG 中一样用于不让相机低于地形(这是我的情况,它当然是 3D 的)。我不记得我 2 年前搜索过什么,但这里可能会找到一些线索 answers.unity3d.com/questions/14693/… 或 answers.unity3d.com/questions/19864/… 我认为同样的原理可以应用于您的 2D【参考方案3】:

大家好,有些人需要完整的代码来将许多儿童精灵放入空游戏对象的游戏对象中,例如:

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

此代码将循环到背景对象中并绑定所有儿童精灵:D,感谢@Stefan Hoffmann 的解释,我制作了一个弗兰克斯坦代码哈哈哈。我希望这对其他人有所帮助,对不起我的英语不好:(

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);
    


【讨论】:

【参考方案4】:

你应该使用这个简单而有用的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);

    
  
 

【讨论】:

这到底是如何回答这个问题的?问题不在于相机跟踪玩家(只需一行代码就可以完成!),而是让相机不会超出地图的边界。 这与OP所说的无关。

以上是关于unity3d (2d!) - 相机以玩家为中心,但永远不会超过“地图”边界的主要内容,如果未能解决你的问题,请参考以下文章

如何在统一 2d 中将相机连接到生成的玩家?

[Unity3D/2D]实现相机对人物角色的跟随效果/相机在一定范围内移动/内置插件实现

JavaFX中的2D相机?

记录相机成像原理

Unity3D摇杆

我想将图像数据(Texture2D)转换为 Base64 字符串并存储在 Unity3D(C#)中的 JSON 文件中