【Unity3D】相机

1 简介

相机用于渲染游戏对象,每个场景中可以有多个相机,每个相机独立成像,每个成像都是一个图层,最后渲染的图层在最前面显示。

相机的属性面板如下:

  • Clear Flags:设置清屏颜色,Skybox(天空盒)、Solid Color(纯色)、Depth Only(仅深度,画中画效果)、Don't Clear(不渲染);

  • Background:清屏颜色,当 Clear Flags 为 Skybox 或 Solid Color 时,才有 Background;

  • Culling Mask:剔除遮蔽,选择相机需要绘制的对象层;

  • Projection:投影方式,Perspective(透视投影)、Orthographic(正交投影);

  • Field of View:相机视野范围,默认 60°,视野范围越大,能看到的物体越多,看到的物体越小;

  • Clipping Planes:裁剪平面,视锥体中近平面和远平面的位置;

  • Viewport Rect:视口在屏幕中的位置和大小(相对值),以屏幕左下角为原点;

  • Depth:渲染图层深度,用于设置相机渲染图层的显示顺序;

  • Rendering Path:渲染路径,用于调整渲染性能(可以在 Stats 里查看性能)

  • Target Texture:将相机画面存储到一个纹理图片(RenderTexture)中。

    补充:基于 Target Texture 属性,可以使用 RawImage 显示次相机渲染的 RenderTexture,实现带主题边框的小地图、八倍镜等效果。

2 应用

本节将通过实现小地图案例展示相机的应用。

1)游戏界面

2)游戏对象层级结构

3)Transform 组件参数

  1. 相机 Transform 组件参数
Name Type Position Rotation Scale Viewport Rect
Main Camera Camera (0, 4, -5) (38, 0, 0) (1, 1, 1) (0, 0, 1, 1)
MinimapCamera Camera (0, 12, 0) (90, 0, 0) (1, 1, 1) (0.8, 0.7, 0.2, 0.3)
  1. 玩家 Transform 组件参数
Name Type Position Rotation Scale Color/Texture
Player Empty (0, 0.25, -5) (0, 0, 0) (1, 1, 1) #228439FF
Botton Cube (0, 0, 0) (0, 0, 0) (2, 0.5, 2) #228439FF
Top Cube (0, 0.5, 0) (0, 0, 0) (1, 0.5, 1) #228439FF
Gun Cylinder (0, 0, 1.5) (90, 0, 0) (0.2, 1, 0.4) #228439FF
FirePoint Empty (0, 1.15, 0) (0, 0, 0) (1, 1, 1) ——

补充:Player 游戏对象添加了刚体组件。

  1. 敌人 Transform 组件参数
Name Type Position Rotation Scale Color/Texture
Enemy1 Empty (7, 0.25, 1) (0, 120, 0) (1, 1, 1) #F83333FF
Enemy2 Enemy (-1, 0.25, 5) (0, 60, 0) (1, 1, 1) #EA9132FF
Enemy3 Enemy (-5, 0.25, -1) (0, -40, 0) (1, 1, 1) #5DC2F4FF

说明:Enemy1~Enemy3 都是由 Player copy 而来。

  1. 地面和炮弹 Transform 组件参数
Name Type Position Rotation Scale Color/Texture
Plane Plane (0, 0, 0) (0, 0, 0) (10, 10, 10) GrassRockyAlbedo
Bullet Sphere (0, 0.5, -5) (0, 0, 0) (0.3, 0.3, 0.3) #228439FF

补充:炮弹作为预设体拖拽到 Assets/Resources/Prefabs 目录下,并且添加了刚体组件。

4)脚本组件

MainCameraController.cs

using UnityEngine;
 
public class MainCameraController : MonoBehaviour {
    private Transform player; // 玩家
    private Vector3 relaPlayerPos; // 相机在玩家坐标系中的位置
    private float targetDistance = 15f; // 相机看向玩家前方的位置
 
    private void Start() {
        relaPlayerPos = new Vector3(0, 4, -8);
        player = GameObject.Find("Player/Top").transform;
    }
 
    private void LateUpdate() {
        CompCameraPos();
    }
 
    private void CompCameraPos() { // 计算相机坐标
        Vector3 target = player.position + player.forward * targetDistance;
        transform.position = transformVecter(relaPlayerPos, player.position, player.right, player.up, player.forward);
        transform.rotation = Quaternion.LookRotation(target - transform.position);
    }
 
    // 求以origin为原点, locX, locY, locZ 为坐标轴的本地坐标系中的向量 vec 在世界坐标系中对应的向量
    private Vector3 transformVecter(Vector3 vec, Vector3 origin, Vector3 locX,  Vector3 locY,  Vector3 locZ) {
        return vec.x * locX + vec.y * locY + vec.z * locZ + origin;
    }
}

说明: CameraController 脚本组件挂在 MainCamera 游戏对象上。

MinmapCameraController.cs

using UnityEngine;

public class MinimapController : MonoBehaviour {
    private Transform player; // 玩家
    private float height = 12; // 相机离地面的高度
    private bool isFullscreen = false; // 小地图相机是否全屏
    private Rect minViewport; // 小地图视口位置和大小(相对值)
    private Rect FullViewport; // 全屏时视口位置和大小(相对值)
 
    private void Start() {
        player = GameObject.Find("Player/Top").transform;
        minViewport = GetComponent<Camera>().rect;
        FullViewport = new Rect(0, 0, 1, 1);
    }

    private void Update() {
        if (Input.GetMouseButtonDown(0) && IsClickMinimap()) {
            if (isFullscreen) {
                GetComponent<Camera>().rect = minViewport;
            } else {
                GetComponent<Camera>().rect = FullViewport;
            }
            isFullscreen = !isFullscreen;
        }
    }
 
    private void LateUpdate() {
        Vector3 pos = player.position;
        transform.position = new Vector3(pos.x, height, pos.z);
    }

    public bool IsClickMinimap() { // 是否单击到小地图区域
        Vector3 pos = Input.mousePosition;
        if (isFullscreen) {
            return true;
        }
        if (pos.x / Screen.width > minViewport.xMin && pos.y / Screen.height > minViewport.yMin) {
            return true;
        }
        return false;
    }
}

说明: MinimapController 脚本组件挂在 MinimapCamera 游戏对象上。

PlayerController.cs

using UnityEngine;

public class PlayerController : MonoBehaviour {
    private MinimapController minimapController; // 小地图控制器
    private Transform firePoint; // 开火点
    private GameObject bulletPrefab; // 炮弹预设体
    private float tankMoveSpeed = 4f; // 坦克移动速度
    private float tankRotateSpeed = 2f; // 坦克转向速度
    private Vector3 predownMousePoint; // 鼠标按下时的位置
    private Vector3 currMousePoint; // 当前鼠标位置
    private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间
    private float bulletCoolTime = 0.15f; // 炮弹冷却时间
 
    private void Start() {
        minimapController = GameObject.Find("MinimapCamera").GetComponent<MinimapController>();
        firePoint = transform.Find("Top/Gun/FirePoint");
        bulletPrefab = (GameObject) Resources.Load("Prefabs/Bullet");
    }
 
    private void Update() {
        Move();
        Rotate();
        Fire();
    }
 
    private void Move() { // 坦克移动
        float hor = Input.GetAxis("Horizontal");
        float ver = Input.GetAxis("Vertical");
        if (Mathf.Abs(hor) > float.Epsilon || Mathf.Abs(ver) > float.Epsilon) {
            Vector3 vec = transform.forward * ver + transform.right * hor;
            GetComponent<Rigidbody>().velocity = vec * tankMoveSpeed;
            Vector3 dire = new Vector3(hor, ver, 0);
            dire =  dire.normalized * Mathf.Min(dire.magnitude, 1);
        }
    }
 
    private void Rotate() { // 坦克旋转
        if (Input.GetMouseButtonDown(1)) {
            predownMousePoint = Input.mousePosition;
        } else if (Input.GetMouseButton(1)) {
            currMousePoint = Input.mousePosition;
            Vector3 vec = currMousePoint - predownMousePoint;
            GetComponent<Rigidbody>().angularVelocity = Vector3.up * tankRotateSpeed * vec.x;
            predownMousePoint = currMousePoint;
        }
    }
 
    private void Fire() { // 坦克开炮
        fireWaitTime += Time.deltaTime;
        if (Input.GetMouseButtonDown(0) && !minimapController.IsClickMinimap() || Input.GetKeyDown(KeyCode.Space)) {
            if (fireWaitTime > bulletCoolTime) {
                BulletInfo bulletInfo = new BulletInfo("PlayerBullet", Color.red, transform.forward, 10f, 15f);
                GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity);
                bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);
                fireWaitTime = 0f;
            }
        }
    }
}

说明: PlayerController 脚本组件挂在 Player 游戏对象上。

BulletController.cs

using UnityEngine;
using UnityEngine.UI;

public class BulletController : MonoBehaviour {
    private BulletInfo bulletInfo; // 炮弹信息

    private void Start () {
        gameObject.name = bulletInfo.name;
        GetComponent<MeshRenderer>().material.color = bulletInfo.color;
        float lifeTime = bulletInfo.fireRange / bulletInfo.speed; // 存活时间
        Destroy(gameObject, lifeTime);
    }

    private void Update () {
        transform.GetComponent<Rigidbody>().velocity = bulletInfo.flyDir * bulletInfo.speed;
    }

    public void SetBulletInfo(BulletInfo bulletInfo) {
        this.bulletInfo = bulletInfo;
    }
}

说明:BulletController 脚本组件挂在 Bullet 游戏对象上(代码里动态添加)。

BulletInfo.cs

using UnityEngine;

public class BulletInfo {
    public string name; // 炮弹名
    public Color color; // 炮弹颜色
    public Vector3 flyDir; // 炮弹飞出方向
    public float speed; // 炮弹飞行速度
    public float fireRange; // 炮弹射程

    public BulletInfo(string name, Color color, Vector3 flyDir, float speed, float fireRange) {
        this.name = name;
        this.color = color;
        this.flyDir = flyDir;
        this.speed = speed;
        this.fireRange = fireRange;
    }
}

5)运行效果

声明:本文转自【Unity3D】相机

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容