unity3d 跟随效果

2018-01-03 14:53:04 q764424567 阅读数 5351
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

让物体跟随鼠标移动是很简单的,只需要两行代码:

using UnityEngine;

public class FollowMouse : MonoBehaviour
{
    void Update()
    {
        Vector3 m_MousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10);
        transform.position = Camera.main.ScreenToWorldPoint(m_MousePos);
    }
}

效果图:
在这里插入图片描述

但是发现问题没有?
我们用了一个固定的参数:
在这里插入图片描述
这个参数的意思是:
摄像机到物体的距离,这个距离如果你没有动摄像机和物体的话(也就是两个都是默认值)就相距10单位:
在这里插入图片描述
在这里插入图片描述

但是!如果两个对象的距离位置变化了呢,这怎么办呢
我们的代码就可以变化一下:

using UnityEngine;

public class FollowMouse : MonoBehaviour
{
    void Update()
    {
        //首先获取到当前物体的屏幕坐标
        Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
        //让鼠标的屏幕坐标的Z轴等于当前物体的屏幕坐标的Z轴,也就是相隔的距离
        Vector3 m_MousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, pos.z);
        //将正确的鼠标屏幕坐标换成世界坐标交给物体
        transform.position = Camera.main.ScreenToWorldPoint(m_MousePos);
    }
}

我们先获取到距离,然后转化为世界坐标就搞定了
效果图:
在这里插入图片描述

2017-03-14 23:18:01 zbc521 阅读数 1404
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

实现相机的跟随
调整好相机和小球的位置,选择相机,然后GameObject->Align with view。首先将相机放置到小球下成为其子物体,
由于小球放大了两倍,所以相机的便宜位置offset发大2倍,最后将相机从小球的下面移出。


下面是小球的脚本Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour {
    public float velocity = 5;
    private Rigidbody rigidbody;
	void Start () {
        rigidbody = this.GetComponent<Rigidbody>();
	}
	
	void Update () {

        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 vel = rigidbody.velocity;


       rigidbody.velocity = new Vector3(h * velocity, vel.y, v * velocity);

    }
}

下面是相机的脚本FollowTarget.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FollowTarget : MonoBehaviour {
    public Vector3 offset;
    private Transform player;

    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player").transform;
    }


    void Update()
    {
        transform.position = player.position + offset;
    }

}
最后效果





2017-03-12 18:12:13 qq_31411825 阅读数 4447
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。


Unity 3d 实现物体跟随摄像机视野运动

Created by miccall (转载请注明出处 miccall.tech

1. VR实现机器人导航

 - 
 项目里要求一个机器人跟随在角色旁边,一直飞着,就像一个精灵一样,总在主角的身边,移动,转头,都要移动到合适的位置 。还得让他面向主角,今天就来实现这个样例 。 
 -

这里写图片描述

2. 问题思考

 - 1. 物体移动到某个给定的位置(target)
 - 2. 物体转动到面向摄像机 
 - 3. 出现在相机的视野当中 
 - 4. 自定义物体在摄影机的Screen中的位置 

3.实现以及方法

  - 
  移动的话 ,本来可以用动画来实现 ,因为动画还没有做好,我就用一个cube当作那个机器人做样例了 。 
  首先有个cube之后 ,给他放一个移动的脚本。这里我给他命名为PlayerTank 。
  我们的目的就是让他运动到某个target ,所以我们得给他指定一个followTransform 。 同时还有他的移动速度和转动速度 。
  为了使他移动不是很突兀,我的思路是他先转动到面向follow物体,然后在直线移动到给物体 。所以算法很快写好了 。
  —
void LookTransform(Transform Mtransform)
    {
        Vector3 tarPos = Mtransform.position;
        Vector3 dirRot = tarPos - transform.position;
        Quaternion tarRot = Quaternion.LookRotation(dirRot);
        transform.rotation = Quaternion.Slerp(transform.rotation, tarRot, rotSpeed * Time.deltaTime);
    }
-
  简单解释一下,就是先确定物体的位置,然后求出指向他的方向,并用插值的方法,让物体转动到面向指定的物体 。
  好了,既然有了朝向的运动方向,那么走到这方向,就很简单了。
-
      transform.Translate(new Vector3(0, 0, movementSpeed * Time.deltaTime));
-
  那么什么时候停止运动呢 ,我想了一下,决定用位置的差来判断
  就是
-
Vector3.Distance(transform.position, followTransform.position);
-
 好了,既然停止的方法也有了,最后要解决的问题就是朝向摄像机了。
 突然一想,这是问题么,对,这不是问题 ,哈哈,刚刚写的那个算法,给一个摄像机就解决了嘛 。
 然后给出具体的判断逻辑 。
-
        //该物体 接近要  到达的目标   指定位置后就停止  
        if (Vector3.Distance(transform.position, followTransform.position) < 3f)
        {
            //当物体道到位置时 让物体面 向摄像机 
            LookTransform(Camre);
            return;
        }
        else
        {
            //让物体转向 将要运动 的方向 
            LookTransform(followTransform);
            transform.Translate(new Vector3(0, 0, movementSpeed * Time.deltaTime));
        }
-
  这样就解决了物体移动到target了,下一步就是固定target的位置,让他在摄像机的固定位置了 。
  新建一个脚本文件CameraView,挂在摄像机上。为了方便调试,我又用了FPS脚本,就是第一人称视角跟随鼠标转动,就跟cs里面的玩法一样,(百度一大推代码)。
  第二个调试算法是一个国外大牛写的 ,他可以给定一个距离,画出摄像机的视野范围
-

这里写图片描述

-
  这里我画了两个边 ,一个是距离摄像机8.5米 用黄色表示,距离摄像机12米的用红色表示。
  应为篇幅问题和详略问题,这里不多解释这个算法,有兴趣的可以去研究一下,这里我们引用一下就行了。
-
Vector3[] GetCorners(float distance)
    {
        Vector3[] corners = new Vector3[4];

        float halfFOV = (theCamera.fieldOfView * 0.5f) * Mathf.Deg2Rad;
        float aspect = theCamera.aspect;

        float height = distance * Mathf.Tan(halfFOV);
        float width = height * aspect;

        // UpperLeft
        corners[0] = tx.position - (tx.right * width);
        corners[0] += tx.up * height;
        corners[0] += tx.forward * distance;

        // UpperRight
        corners[1] = tx.position + (tx.right * width);
        corners[1] += tx.up * height;
        corners[1] += tx.forward * distance;

        // LowerLeft
        corners[2] = tx.position - (tx.right * width);
        corners[2] -= tx.up * height;
        corners[2] += tx.forward * distance;

        // LowerRight
        corners[3] = tx.position + (tx.right * width);
        corners[3] -= tx.up * height;
        corners[3] += tx.forward * distance;

        return corners;
    }
void FindUpperCorners()
{
        Vector3[] corners = GetCorners(upperDistance);
        // for debugging
        Debug.DrawLine(corners[0], corners[1], Color.yellow); // UpperLeft -> UpperRight
        Debug.DrawLine(corners[1], corners[3], Color.yellow); // UpperRight -> LowerRight
        Debug.DrawLine(corners[3], corners[2], Color.yellow); // LowerRight -> LowerLeft
        Debug.DrawLine(corners[2], corners[0], Color.yellow); // LowerLeft -> UpperLeft
}
-
  debug的时候,直接调用FindUpperCorners()就可以了 。
  刚开始的时候 ,我就用的这个调试 ,给出一个位置,然后计算他的偏移量,调试了很久,没有一个良好的效果,我决定换个思路了 ,为了普遍大众 ,我还是把这个调试方法贴出来了,有需要的可以试试 。 
  第二个我就去翻api了 ,因为我貌似记得有个屏幕坐标和世界坐标转化的什么鬼方法来着。果然不出我所料,这个方法的确是相当的好用的呀 。
  试了一下官方给的调试方法,画了一个点出来 。
-
    void OnDrawGizmosSelected()
    {
        Vector3 p = theCamera.ScreenToWorldPoint(new Vector3(100, 200, 8));
        Gizmos.color = Color.blue;
        //target.position = p;
        Gizmos.DrawSphere(p, 1F);
    }
-
   好了,就连我最后决定用的位置也标明了。
   然后,我就写了一个很简单的方法来达到目的。
-
    void maketarget()
    {
        Vector3 p = theCamera.ScreenToWorldPoint(new Vector3(RH, RV, upperDistance));
        target.position = p;
    }
-
  写完我都吓了一跳 ,竟然如此简单。还是简单解释一下 ,RH 是水平偏移量,RV是垂直偏移量,upperDistance是距离摄像机的一个平面位置 。
  接下来就是运行看效果了 。
-

4.中途出现的小BUG

-
   莫名其妙的做圆周运动 ,然后我分析了线速度,角速度和半径的关系 ,然后总结出一个基本的规律,他应该是当运动到某个特定的位置 ,正好满足了圆周运动的关系,然后我们调整movementSpeed 和rotSpeed 的值,让他么尽可能的和Distance消除乘积关系,这样出现的几率就微乎其微了 。
   感想 -- 其实unity和现实物理,理论物理 还是有很大的不同。
-
2017-08-19 22:04:33 lin_xiao_zhi 阅读数 1223
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

我们分别在工程里两个对象Player,pet


之后分别表写两个脚本首先 是pet脚本写完把它拖拽到pet游戏对象身上,下面代码 可供 参考 

   //宝宝跟随的目标
    public Transform target;
    //宝宝跟随目标的偏移量
    public Vector3 offset;
// Use this for initialization
void Start () {
        
}
// Update is called once per frame
void Update () {
        Debug.Log(Vector3.Lerp(new Vector3(0, 0, 0), new Vector3(0, 0, 10), Time.deltaTime));
    }
    void LateUpdate()
    {
        offset = target.forward * -2 + target.up * 2;
        transform.position = Vector3.Lerp(transform.position, target.position + offset, Time.deltaTime); 
        transform.rotation =  target.rotation ;
    }

接下来是Player的 脚本代码,我们这里用的是键盘进行控制,大家可修改控制的方法

void Start () {

}

// Update is called once per frame
void Update () {
        if (Input.GetKey(KeyCode.W))
        {
            transform.Translate(Vector3.forward * Time.deltaTime);
        }
        if (Input.GetKey(KeyCode.S))
        {
            transform.Translate(Vector3.back * Time.deltaTime);
        }
        if (Input.GetKey(KeyCode.A))
        {
            transform.Rotate(transform.up,-30*Time.deltaTime);
        }
        if (Input.GetKey(KeyCode.D))
        {
            transform.Rotate(transform.up, 30 * Time.deltaTime);
        }
    }


欢迎大家来交流,有更好意见可评论 

2020-02-29 15:32:00 Meytones 阅读数 426
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

从2D游戏到3D游戏的进化,最重要的就是游戏视角的控制。奠定了3D游戏操作模式的1998年的神话级游戏《塞尔达传说:时之笛》中,对3D下游戏视角设计的关键即在于“镜头跟随”与“视角锁定”。本文将在unity中实现该效果。

实现过程

实现的思路非常简单:

  • 在初始化时设定摄像机和人物之间的相对位置和相对旋转角;
  • 在需要锁定时找到人物最近的单位,使人物转向该单位,并将摄像机的水平面的旋转角设置至和人物相同;
  • 如果一定范围内没有单位,则人物不需转向,摄像机仍然执行;

这里设置了positionTarget和angleTarget两个变量,用来表示移动的目标状态,同时使用Lerp函数实现平滑运动。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    public GameObject player;
    Vector3 distanceToPlayer;
    Vector3 positionTarget,angleTarget;
    LayerMask enemyLayer;
    float temp;

    // Start is called before the first frame update
    void Start()
    {
        distanceToPlayer = this.transform.position - player.transform.position;
        enemyLayer = LayerMask.GetMask("Enemy");
        angleTarget = this.transform.eulerAngles;
    }

    // Update is called once per frame
    void Update()
    { 
        CameraFollow();
        if (Input.GetKey(KeyCode.Space))
        {
            CameraAngle();
        }
        this.transform.eulerAngles = Vector3.Lerp(this.transform.eulerAngles, angleTarget, 0.3f);
        this.transform.position = Vector3.Lerp(this.transform.position, positionTarget, 0.3f);
    }

    void CameraFollow()
    {
        positionTarget = player.transform.position + Quaternion.AngleAxis(player.transform.rotation.eulerAngles.y, new Vector3(0, 1, 0)) * (distanceToPlayer);
    }

    void CameraAngle()
    {
        //找到最近的单位
        Collider[] collidersAround = Physics.OverlapSphere(player.transform.position, 400);//Overlap一系的函数在检测总数不是太大的时候会优先获取更近的碰撞器
        if (collidersAround.Length == 0)
        {
        //搜索不到目标就指向正前方
            angleTarget = new Vector3(this.transform.eulerAngles.x, player.transform.eulerAngles.y, this.transform.eulerAngles.z); 
            return;
        }
        int closestEnemy = 0;
        float closestDistance = 401;
        for (int i = 0; i < collidersAround.Length; i++)
        {
            temp = (this.transform.position - collidersAround[i].gameObject.transform.position).sqrMagnitude;
            closestEnemy = temp < closestDistance ? i : closestEnemy;
            closestDistance = temp < closestDistance ? temp : closestDistance;
        }
		player.transform.LookAt(collidersAround[closestEnemy].gameObject.transform.position);
        positionTarget = player.transform.position + Quaternion.AngleAxis(player.transform.rotation.eulerAngles.y, new Vector3(0, 1, 0)) * (distanceToPlayer);
        angleTarget = new Vector3(this.transform.eulerAngles.x, player.transform.eulerAngles.y, this.transform.eulerAngles.z);   
    }

}

但是这样写完一运行,发现不对了:
在这里插入图片描述
每次转一圈到0°的时候视角会“刷”一下外翻一圈。

原因很好理解,就是因为越过360°(0°)的时候,获取的欧拉角会产生一个突变,此时Lerp运算后的变化方向会变化:
在这里插入图片描述
按照图中的变化,会在某一时刻发生角度突然变小,表现出的就是镜头“天旋地转”。

那怎么解决呢?这里要在突变时使两角度的差值减小360°,换句话说,只需要在设置angleTarget的时候保证从当前角度到目标角度的差值小于180°即可(此时一定为变化最小的过程)。
也就是把上文中的CameraAngle函数改成:

    void CameraAngle()
    {
        Collider[] collidersAround = Physics.OverlapSphere(player.transform.position, 400);
        if (collidersAround.Length == 0)
        {
        	angleTarget = new Vector3(this.transform.eulerAngles.x, player.transform.eulerAngles.y, this.transform.eulerAngles.z); 
            return;
        }
        int closestEnemy = 0;
        float closestDistance = 401;
        for (int i = 0; i < collidersAround.Length; i++)
        {
            temp = (this.transform.position - collidersAround[i].gameObject.transform.position).sqrMagnitude;
            closestEnemy = temp < closestDistance ? i : closestEnemy;
            closestDistance = temp < closestDistance ? temp : closestDistance;
        }
        player.transform.LookAt(collidersAround[closestEnemy].gameObject.transform.position);
        positionTarget = player.transform.position + Quaternion.AngleAxis(player.transform.rotation.eulerAngles.y, new Vector3(0, 1, 0)) * (distanceToPlayer);
        angleTarget = new Vector3(this.transform.eulerAngles.x, player.transform.eulerAngles.y, this.transform.eulerAngles.z);        
        //当越过0发生突变时(不是最短变化路径)加以矫正
        if (Mathf.Abs(player.transform.eulerAngles.y - this.transform.eulerAngles.y) > 180)
        {
            angleTarget = new Vector3(this.transform.eulerAngles.x, player.transform.eulerAngles.y + (player.transform.eulerAngles.y > this.transform.eulerAngles.y ? -360 : 360), this.transform.eulerAngles.z);
        }
    }

最终实现的效果如下:

在这里插入图片描述


本次分享的内容到这里就结束了,我写博文的目的最初是为了帮助自己掌握知识,如果这篇分享能够稍微帮助到你,那当然再好不过了。有什么问题也请各位多指正!谢谢大家的关注!