为您推荐:
精华内容
最热下载
问答
  • 5星
    12.28MB qq_32688843 2021-06-07 17:54:37
  • 5星
    116.98MB weixin_43474701 2021-07-16 15:38:05
  • 5星
    269KB qq_40408843 2021-08-25 15:38:29
  • 5星
    1KB qq_32688843 2021-03-16 13:44:16
  • 5星
    316.33MB zb1165048017 2020-12-15 11:22:17
  • 5星
    85.2MB m0_37546766 2021-08-16 17:04:06
  • 5星
    3.37MB weixin_38239050 2021-07-21 16:01:10
  • 5星
    35.97MB shenshaonan 2021-06-17 16:53:41
  • 5星
    68KB qq_39735878 2021-07-15 09:56:09
  • 4星
    76B Allen7474 2021-02-21 15:23:58
  • 106B weixin_42197129 2021-03-05 15:23:50
  • 4.08MB xiaocong0 2020-04-04 21:35:56
  • 1.使用Final IK来控制武器的瞄准 2.第三人称视角控制

    ===================更新一下源工程链接:
    链接:https://pan.baidu.com/s/15bxH-MPregp2ZIN92fK7XA 提取码:e7bp
    ===================(与本文相比有修改)

    效果视频演示:
    https://www.bilibili.com/video/av88249417
    完整代码在最下面

    最近练习Unity,想做一个第三人称射击游戏的Demo。首先来做一个武器瞄准效果,即让枪能指向目标。

    查了很久不知道怎么实现,后来查到了IK这个概念,IK就是反向动力学,按我的理解是让骨骼中的下层带动上层运动,与FK(前向动力学)相反,比如伸手去触摸物体时带动整个身体前倾。所以武器的瞄准就可以用通过IK来做,让手和武器瞄准目标,带动身体旋转,这样就很自然。

    如图:
    在这里插入图片描述
    Unity自带的IK实在是不好用,连这个功能都很难实现,于是我用了一个插件:Final IK,用里面一个叫AimIK的组件就可以解决了。
    附上Final IK1.6的链接:
    链接:https://pan.baidu.com/s/1zY4bluDzi8xSSWPtJBLGWQ
    提取码:eovc
    教程参考这篇博客:
    https://blog.csdn.net/weixin_38239050/article/details/101831392
    Final IK的使用不是本文的重点。

    用代码来设置瞄准的思路是:从摄像机的位置向摄像机的forward方向发出一定距离的射线,如果射线打到物体就让把它设为瞄准目标,否则瞄准射线的终点。注意射线可能需要屏蔽玩家本身碰撞体的干扰
    设置AimIK的target位置的代码为:

    aimIK.solver.target.position = targetPos;
    

    例如射线打到地面:
    在这里插入图片描述

    -------------------------------------------

    然后是视角控制,虽然Unity有相应的插件,但是直接用就没意思了。本来以为比较简单,没想到还是搞了几天(太菜了)
    实现的功能如下:
    1.摄像机平滑跟随玩家
    2.摄像机绕玩家旋转,并限制角度
    3.往上看时随角度增大而拉近摄像机,往下看时随角度增大而拉远摄像机
    4.瞄准时拉近摄像机,停止瞄准时回到默认位置
    5.碰到墙壁遮挡时拉近摄像机

    下面依次介绍,完整代码在最下面

    1.摄像机平滑跟随

    记录摄像机与人物的位置偏移向量

    playerOffset = player.position - transform.position;
    

    当摄像机旋转后需要更新playerOffset,再次计算上面这句代码。

    更新playerOffset后需要在下一帧使用插值让摄像机移动到player.position - playerOffset这个位置

    transform.position = Vector3.Lerp(transform.position, player.position - playerOffset, moveSpeed * Time.deltaTime);
    

    2.摄像机绕玩家旋转,并限制角度

    主要使用transform.RotateAround函数,让摄像机绕人物旋转。由于人物可以移动,而摄像机的位置是使用插值改变的,当人物移动时,playerOffset这个向量的长度和旋转角度会发生改变,人物走的越远,偏差会越大,不符合要求。

    我的办法是当摄像机绕玩家旋转后,让playerOffset向量同步旋转相同的角度,更新playerOffset。旋转后playerOffset的长度不变,这样可保证player.position-playerOffset始终是摄像机的最终位置。

    如下图示意:
    在这里插入图片描述
    在这里插入图片描述
    RotateAround的用法为:

    RotateAround(Vector3 point, Vector3 axis, float angle);
    

    point:要围绕的点;
    axis:要围绕的轴,如x,y,z
    angel:旋转的角度

    所以摄像机水平和垂直绕玩家旋转为:(注意旋转的轴不同

    transform.RotateAround(player.position, Vector3.up, axisX);
    transform.RotateAround(player.position, transform.right, -axisY);
    

    其中axisX和axisY为对应鼠标的偏移量xTime.deltatimex旋转速度:

    float axisX = Input.GetAxis("Mouse X") * rotateSpeed * Time.deltaTime;
    float axisY = Input.GetAxis("Mouse Y") * rotateSpeed * Time.deltaTime;
    

    而让playerOffset向量绕一个点旋转则要先获取水平和垂直旋转的四元数:(同样注意绕的轴

    Quaternion rotX = Quaternion.AngleAxis(axisX, Vector3.up);
    Quaternion rotY = Quaternion.AngleAxis(-axisY, transform.right);
    

    然后让这两个四元素与向量相乘就可以旋转向量而长度不变:(注意这里相乘不具有交换性,不要改变顺序或是简写

    playerOffset = rotX * rotY * playerOffset;
    

    这里获取向量旋转看不懂可以参考文章:
    https://gameinstitute.qq.com/community/detail/127450

    要限制角度,我们要先让它旋转,然后获取旋转后垂直方向的欧拉角:

    float x = (transform.rotation).eulerAngles.x;
    

    但是欧拉角的范围是0-360度循环的,不利于判断,要转换范围成-180度-180度,向上为负,向下为正。然后判断它是否在我们给定的范围内如果超出范围则还原摄像机在垂直方向的旋转,并且让playerOffset只进行水平旋转;否则让playerOffset进行水平和垂直的旋转。

    //欧拉角范围为0~360,这里要转为-180~180方便判断
    if (x > 180) x -= 360;
    
    if (x < minAngle || x > maxAngle)//超出角度
    {
    	//还原位置和旋转
    	transform.position = posPre;
    	transform.rotation = rotPre;
    
    	//更新offset向量,offset与本物体同步旋转
    	//我们需要通过这offset去计算本物体(包括摄像机)应该平滑移向的位置
    	//如果仅仅使用RotateAround函数,当人物在移动时会出现误差
    	playerOffset = rotX*playerOffset;   
    }
    else//垂直视角符合范围的情况
    {
    	//更新offset向量,offset与本物体同步旋转
    	playerOffset = rotX * rotY * playerOffset;
    }
    

    最高视角:(根据minAngle,根据需要设定,我设为-40)
    在这里插入图片描述
    最低视角:(根据maxAngle,根据需要设定,我设为50)
    在这里插入图片描述

    3.往上看时随角度增大而拉近摄像机,往下看时随角度增大而拉远摄像机

    摄像机与人物的距离需要时可变的,比如在瞄准时应该将摄像机拉近人物,但是如果直接改变摄像机的位置又会破坏跟随玩家和自由视角的功能。

    为了使摄像机在能够根据需要偏移的同时又不影响平滑跟随玩家,我的办法是把上面的控制代码挂在一个空物体上,将摄像机作为这个空物体的子物体,这样空物体跟随玩家时,摄像机也会做相同的位移。而让摄像机的位置产生偏移只需要改变它的localPosition,也就是它与父物体的相对位置。

    如图:TPSCameraParent为空物体
    在这里插入图片描述
    摄像机偏移是改变TPSCamera的localPosition.z,让TPSCamera相对于TPSCameraParent前后移动。定义一个总的偏移量:

    float localOffset = 0;
    

    这个偏移量的影响因素有三个:
    1.垂直视角角度
    2.是否瞄准
    3.是否有遮挡

    下面来看垂直视角角度的影响:

    上面我们获取到了垂直方向旋转欧拉角float x,就可以根据x与我们给定的最大角度的比值来设定摄像机的前后偏移:

    //更据角度设置摄像机位置偏移
                if (x < 0)//往上角度为负
                {
                    //往上看时距离拉近
                    localOffsetAngle = (x / minAngle) * localOffsetAngleUp;
                }
                else
                {
                    //往下看时距离拉远
                    localOffsetAngle = -(x / maxAngle) * localOffsetAngleDown;
                }
    

    其中localOffsetAngle为根据角度计算的偏移量,localOffsetAngleUp和localOffsetAngleDown分别为向上看和向下看时这个偏移量的最大值。当x=0时即摄像机平视前方时,这个偏移量=0。

    那怎么让摄像机能够前后偏移呢?让总偏移量加上它:

    localOffset+=localOffsetAngleMax;
    

    在最后使摄像机平滑移动到偏移位置:

    Vector3 offsetPos = new Vector3(0, 0, localOffset);//这是相机应该移向的位置
    //使相机平滑移动到这个位置
    cam.transform.localPosition = Vector3.Lerp(cam.transform.localPosition, offsetPos, localOffsetSpeed * Time.deltaTime);
    

    俯视时拉远摄像机:
    在这里插入图片描述
    仰视时拉近摄像机:
    在这里插入图片描述

    4.瞄准时拉近摄像机,停止瞄准时回到默认位置

    规定当鼠标右键按住时瞄准,松开时停止瞄准。

    再定义一个偏移量localOffsetAim和一个bool值isAiming

    public float localOffsetAim = 2;//根据是否瞄准而产生的偏移量,表示瞄准时摄像机应该前进多远距离,根据需要设值
    private bool isAiming = false;//是否正在瞄准
    

    然后每帧判断鼠标事件:

    if (Input.GetMouseButtonDown(1))//鼠标右键按下为瞄准
       {
       	isAiming = true;
       }
    if (Input.GetMouseButtonUp(1))//鼠标右键松开停止瞄准
    {
       	isAiming = false;
    }
    

    接着根据isAiming来决定是否让localOffset加上这个偏移量:

    //根据是否瞄准而调整
    if (isAiming)
    {
    	localOffset += localOffsetAim;
    }
    

    瞄准时拉近摄像机:
    在这里插入图片描述
    正常距离:
    在这里插入图片描述

    5.碰到墙壁遮挡时拉近摄像机

    当人物身后有墙壁或是其他物体靠近时,会遮挡视线:
    在这里插入图片描述
    这时候需要将摄像机拉近到合适距离,所以再定义一个偏移量:

    float localOffsetCollider = 0;
    

    然后在一帧中逐渐增加这个偏移量去试探有没有遮挡,试探的方法还是利用射线检测,看是否能打到除玩家外的碰撞体,我这里玩家身上挂的是CapsuleCollider:

    private bool CheckView(Vector3 checkPos)
        {
            //发出射线来检测碰撞
            RaycastHit hit;
            //射线终点为玩家物体的中间位置
            Vector3 endPos = player.position + player.up * player.GetComponent<CapsuleCollider>().height * 0.5f;
    
            Debug.DrawLine(checkPos,endPos, Color.blue);
    
            //从checkPos发射一条长度为起点到终点距离的射线
            if (Physics.Raycast(checkPos,endPos-checkPos,out hit,(endPos-checkPos).magnitude)){
                if (hit.transform == player)//如果射线打到玩家说明没有遮挡
                    return true;
                else//如果射线打击到其他物体说明有遮挡
                    return false;
            }
            return true;//如果射线没有打到任何物体也说明没有遮挡
        }
    

    根据是否有遮挡而调整localOffsetCollider

            Vector3 checkPos = transform.position + cam.transform.forward * localOffset;//这是没有调整前相机应该移向的位置
            for(localOffsetCollider=0; !CheckView(checkPos);localOffsetCollider+=0.2f)//让localOffset递增直至没有遮挡
            {
                //更新checkPos为我们想要移动到的位置,再去试探
                checkPos = transform.position + cam.transform.forward * (localOffset+localOffsetCollider);
            }
    

    再让localOffset加上这个试探出的localOffsetCollider。

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

    完整代码及使用

    分为两个脚本:
    1.TPSCamera.cs

    using UnityEngine;
    
    public class TPSCamera : MonoBehaviour
    {
        public static TPSCamera _instance;//用作单例模式
        public Camera cam;//摄像机,是本物体下的子物体
        public Transform player;//玩家物体的Transform
        public Vector3 playerOffset;//本物体与玩家位置的偏移向量
    
        public float rotateSpeed;//控制旋转速度
        public float moveSpeed;//控制跟随的平滑度
    
        public float minAngle;//垂直视角的最小角度值
        public float maxAngle;//垂直视角的最大角度值
    
        public float localOffsetSpeed = 8;//控制相机与父物体偏移时的平滑度
        public float localOffsetAim = 2;//根据是否瞄准而产生的偏移量,表示瞄准时摄像机应该前进多远距离,根据需要设值
        private float localOffsetAngle = 0;//根据垂直视角角度而产生的偏移量
        public float localOffsetAngleUp = 1.5f;//根据向上的角度而产生的偏移量的最大值
        public float localOffsetAngleDown = 1.5f;//根据向下的角度而产生的偏移量的最大值
        private float localOffsetCollider = 0;//根据玩家与摄像机间是否有遮挡而产生的偏移量
    
        private bool isAiming = false;//是否正在瞄准
    
        private void Awake()
        {
            _instance = this;
            player = GameObject.Find("Player").transform;//根据名字找到玩家物体
            playerOffset = player.position - transform.position;//初始化playerOffset
            cam = transform.GetComponentInChildren<Camera>();//获取子物体的Camera组件
        }
    
        private void Update()
        {
            if (Input.GetMouseButtonDown(1))//鼠标右键按下为瞄准
            {
                isAiming = true;
            }
            if (Input.GetMouseButtonUp(1))//鼠标右键松开停止瞄准
            {
                isAiming = false;
            }
            SetPosAndRot();//设置视角旋转后的位置和朝向
            Cursor.visible = false;//隐藏鼠标
        }
    
        /// <summary>
        /// 上下移动鼠标时,相机围绕玩家旋转,并且限制旋转角度
        /// </summary>
        public void SetPosAndRot()
        {
            //更新本物体的position,相机会和本物体做相同的位移,使相机平滑跟随玩家
            transform.position = Vector3.Lerp(transform.position, player.position - playerOffset, moveSpeed * Time.deltaTime);
            
            //获取鼠标移动量
            float axisX = Input.GetAxis("Mouse X") * rotateSpeed * Time.deltaTime;
            float axisY = Input.GetAxis("Mouse Y") * rotateSpeed * Time.deltaTime;
    
            //计算水平和垂直的旋转角
            Quaternion rotX = Quaternion.AngleAxis(axisX, Vector3.up);
            Quaternion rotY = Quaternion.AngleAxis(-axisY, transform.right);
    
            //摄像机在水平方向绕玩家旋转
            transform.RotateAround(player.position, Vector3.up, axisX);
            
            //保存未旋转垂直视角前的position和rotation
            Vector3 posPre = transform.position;
            Quaternion rotPre = transform.rotation;
    
            //先垂直绕玩家旋转,注意这里旋转的轴为transform.right
            transform.RotateAround(player.position, transform.right, -axisY);
    
            //判断垂直角度是否符合范围
            float x = (transform.rotation).eulerAngles.x;
            //欧拉角范围为0~360,这里要转为-180~180方便判断
            if (x > 180) x -= 360;
            if (x < minAngle || x > maxAngle)//超出角度
            {
                //还原位置和旋转
                transform.position = posPre;
                transform.rotation = rotPre;
    
                //更新offset向量,offset与本物体同步旋转
                //我们需要通过这offset去计算本物体(包括摄像机)应该平滑移向的位置
                //如果仅仅使用RotateAround函数,当人物在移动时会出现误差
                playerOffset = rotX*playerOffset;   
            }
            else//垂直视角符合范围的情况
            {
                //更新offset向量,offset与本物体同步旋转
                playerOffset = rotX * rotY * playerOffset;
    
                //更据角度设置摄像机位置偏移
                if (x < 0)//往上角度为负
                {
                    //往上看时距离拉近
                    localOffsetAngle = (x / minAngle) * localOffsetAngleUp;
                }
                else
                {
                    //往下看时距离拉远
                    localOffsetAngle = -(x / maxAngle) * localOffsetAngleDown;
                }
            }
    
            //设置摄像机与父物体的偏移,三个影响因素
            SetLocalOffset(); 
        }
    
        /// <summary>
        /// 根据是否瞄准、垂直视角和是否有遮挡来调整摄像机与父物体的偏移
        /// </summary>
        public void SetLocalOffset()
        {
            float localOffset = 0;//摄像机与父物体(即本脚本所在的空物体)的偏移
            //根据垂直视角调整
            localOffset += localOffsetAngle;
            //根据是否瞄准而调整
            if (isAiming)
            {
                localOffset += localOffsetAim;
            }
    
            //根据是否有遮挡而调整
            Vector3 checkPos = transform.position + cam.transform.forward * localOffset;//这是没有调整前相机应该移向的位置
            for(localOffsetCollider=0; !CheckView(checkPos);localOffsetCollider+=0.2f)//让localOffset递增直至没有遮挡
            {
                //更新checkPos为我们想要移动到的位置,再去试探
                checkPos = transform.position + cam.transform.forward * (localOffset+localOffsetCollider);
            }
            localOffset += localOffsetCollider;//加上这个试探出的偏移量
    
            Vector3 offsetPos = new Vector3(0, 0, localOffset);//这是调整后相机应该移向的位置
            //使相机平滑移动到这个位置
            cam.transform.localPosition = Vector3.Lerp(cam.transform.localPosition, offsetPos, localOffsetSpeed * Time.deltaTime);
        }
    
        /// <summary>
        /// 检查玩家与摄像机之间是否有碰撞体遮挡
        /// </summary>
        /// <param name="checkPos">假设相机的位置</param>
        /// <returns></returns>
        private bool CheckView(Vector3 checkPos)
        {
            //发出射线来检测碰撞
            RaycastHit hit;
            //射线终点为玩家物体的中间位置
            Vector3 endPos = player.position + player.up * player.GetComponent<CapsuleCollider>().height * 0.5f;
    
            Debug.DrawLine(checkPos,endPos, Color.blue);
    
            //从checkPos发射一条长度为起点到终点距离的射线
            if (Physics.Raycast(checkPos,endPos-checkPos,out hit,(endPos-checkPos).magnitude)){
                if (hit.transform == player)//如果射线打到玩家说明没有遮挡
                    return true;
                else//如果射线打击到其他物体说明有遮挡
                    return false;
            }
            return true;//如果射线没有打到任何物体也说明没有遮挡
        }
    
    }
    
    

    这个脚本挂在TPSCameraParent上,即摄像机的父物体上
    相关参数根据需要设定,我的如下,供参考:
    在这里插入图片描述

    2.ShootControl.cs

    using RootMotion.FinalIK;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ShootControl : MonoBehaviour
    {
        public TPSCamera tpsCamera;//控制相机视角的脚本
        public Camera cam;//摄像机
        public float range;//射线距离
        private float offsetDis;//摄像机与玩家的距离
        public Vector3 targetPos;//目标位置
        private AimIK aimIK;//对应的final ik组件
        public float speed;//移动速度
        public float rotateSpeed;//旋转速度
    
        private void Awake()
        {
            tpsCamera = GameObject.Find("TPSCameraParent").GetComponent<TPSCamera>();//获取相机的父物体
            cam = tpsCamera.GetComponentInChildren<Camera>();//相机为tpsCamera的子物体
            aimIK = GetComponent<AimIK>();//获取AimIk组件
            offsetDis = Vector3.Distance(transform.position, cam.transform.position);//初始化offsetDis
        }
    
        private void Update()
        {
            SetTarget();//设置瞄准的目标位置
            OnKeyEvent();//处理按键响应
        }
    
        /// <summary>
        /// 设置瞄准的目标
        /// 从摄像机位置向摄像机正方向发射射线(即从屏幕视口中心发出)
        /// 射线的长度=range,可以近似设为子弹的射程
        /// 若射线打到非玩家的物体则将该物体设为目标
        /// 若射线没有打到物体则将目标设为射线的终点
        /// </summary>
        public void SetTarget()
        {
            //从摄像机位置向摄像机正方向发射射线(即从屏幕视口中心发出)
            RaycastHit hit;
            if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, range))
            {
                //若射线打到非玩家的物体则将该物体设为目标
                //我这里并没有进行判断该物体是否是玩家,因为我设置的玩家位于屏幕的偏左下位置,射线不会穿过玩家
                //需要的话,可以给玩家设定layer,然后让射线屏蔽这个layer
                targetPos = hit.point;
            }
            else
            {
                //若射线没有打到物体则将目标设为射线的终点
                targetPos = cam.transform.position + (cam.transform.forward * range);
            }
            //画出射线便于观察(不会显示在game中)
            Debug.DrawRay(cam.transform.position, cam.transform.forward * range, Color.green);
    
            //按下鼠标右键时开启AimIK,进入瞄准状态
            if (Input.GetMouseButtonDown(1))
            {
                aimIK.enabled = true;
            }
    
            //按住鼠标右键时为瞄准状态,人物身体始终朝向摄像机的前方
            if (Input.GetMouseButton(1))
            {
                RotateBodyToTarget();
            }
            else//松开右键时为自由视角状态,关闭AimIK,不进行瞄准
            {
                //注意这里使用Disale(),不要直接enabled=false,原因不清楚
                aimIK.Disable();
            }
        }
    
        /// <summary>
        /// 旋转玩家身体,使玩家朝向摄像机的水平前方
        /// </summary>
        private void RotateBodyToTarget()
        {
            Vector3 rotEulerAngles = cam.transform.eulerAngles;//获取摄像机的旋转的欧拉角
            rotEulerAngles.x = 0;//垂直方向不进行旋转
            //使用插值让玩家平滑转向
            transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(rotEulerAngles), rotateSpeed * Time.deltaTime);
            SetAimIKTarget();//更新AimIK的target的位置
        }
    
        /// <summary>
        /// 更新AimIK的target的位置
        /// </summary>
        private void SetAimIKTarget()
        {
            //将AimIK的target位置设为之前射线检测到的位置
            aimIK.solver.target.position = targetPos;
        }
    
        /// <summary>
        /// 管理键盘的响应,这里只用来控制玩家移动,不重要,可以忽略
        /// </summary>
        private void OnKeyEvent()
        {
            //Horizontal和Vertical的默认按键为ad←→和ws↑↓
            float h = Input.GetAxis("Horizontal");
            float v = Input.GetAxis("Vertical");
            if (h != 0 || v != 0)
            {
                Vector3 moveDir = new Vector3(h, 0, v);
                transform.Translate(moveDir * speed * Time.deltaTime);
                RotateBodyToTarget();
            }
        }
    }
    

    挂在玩家身上,玩家名为“Player”,参数参考:
    在这里插入图片描述
    玩家身上还需挂载的组件:
    在这里插入图片描述
    AimIK参数:(建议先看一下AimIK的使用,如果不需要瞄准功能的话可以不用ShootControl和AimIK组件)
    在这里插入图片描述
    其中Target为一个任意的空物体,拖进去,FirePos为武器的空子物体,位于枪口位置。
    如图:
    在这里插入图片描述
    初始时将需要跟随的人物放在摄像机视线的左下角,否则需要在ShootControl.cs中的射线检测那里屏蔽掉玩家所在的层。
    在这里插入图片描述
    另外准星绘制参考这个博客:
    https://blog.csdn.net/xboxbin/article/details/88069638

    展开全文
    realfancy 2020-02-11 18:36:02
  • 3.83MB lg_0303 2019-04-20 11:04:09
  • 4星
    1.49MB chair69 2013-04-24 12:10:47
  • 4星
    73B heyuchang666 2015-09-17 14:09:33
  • 3星
    1.85MB bxgexpo 2012-12-18 10:26:58
  • 32.85MB m0_48554728 2021-03-30 18:59:21
  • 5KB crh5354 2015-08-01 09:21:47
  • 4星
    40.9MB yy763496668 2016-02-26 15:33:42
  • 1.34MB wxstar1210 2017-12-28 14:09:30
  • unity第三人称射击游戏Previous article 上一篇文章 The economics literature distinguishes the quality of a game’s information (perfect vs. imperfect) from the completeness of a game’s information ...

    unity第三人称射击游戏

    Previous article

    上一篇文章

    The economics literature distinguishes the quality of a game’s information (perfect vs. imperfect) from the completeness of a game’s information (complete vs. incomplete). Perfect information means that every player is perfectly informed of all the actions that happened in the game, including the starting state of the game. Complete information means that players possess all relevant information about their opponents, including their objectives, their possible actions, and their preferences. In economics, games are classified according to these two dimensions:

    经济学文献从游戏的信息(完全与不完全)的完整性区分了游戏的信息质量 (完美与不完美)。 完美的信息意味着每个玩家都可以完美地了解游戏中发生的所有动作,包括游戏的开始状态。 完整的信息意味着玩家拥有有关对手的所有相关信息,包括他们的目标,他们可能采取的行动以及他们的偏好。 在经济学中,游戏根据以下两个方面进行分类:

    In the previous article, we talked about social deduction games — a genre of game where the influence currency (specifically, social influence) is used to mediate the initial imbalance between the power and information currencies of two teams. The information in social deduction games is imperfect and incomplete because while the rules, the objectives, and the possible information in the game are common knowledge, some players have no knowledge of the starting state of the game, or of which actions were taken by which players. Effectively, the “good” team in a social deduction game starts somewhere in the bottom-left quadrant and the “bad” team starts somewhere in the top-right quadrant; the bad team then tries to thwart the good team’s attempts to reach information parity.

    在上一篇文章中,我们讨论了社交演绎游戏-一种游戏类型,其中影响力货币(特别是社会影响力)用于调解两个团队的力量和信息货币之间的初始失衡。 社交演绎游戏中的信息是不完美且不完整的,因为尽管游戏中的规则,目标和可能的信息是常识,但有些玩家却不知道游戏的开始状态或采取了哪些行动。玩家。 实际上,社交演绎游戏中的“好”团队从左下象限开始,而“差”团队则从右上象限开始。 坏团队然后试图挫败好团队达到信息均等的尝试。

    In this article, we discuss perfect information games — the right half of our diagram. As it so happens, almost all of these games also have complete information. Perfect incomplete information games are (unsurprisingly) rare, but they do exist and I will touch on them in future articles.

    在本文中,我们讨论了完美的信息游戏-图的右半部分。 碰巧的是,几乎所有这些游戏都具有完整的信息。 完美的不完整信息游戏很少(毫无疑问),但是它们确实存在,我将在以后的文章中进行介绍。

    热身 (Warming Up)

    Many beloved perfect information games started off as war games designed to help military generals sharpen their strategic thinking. Chess, xiangqi, and shogi likely have a common ancestor in chaturanga, a game played in the Gupta Empire in the 6th century AD. Chaturanga translates to “having four parts”, referring to the four divisions of an army — infantry, cavalry, chariotry, and elephantry — and involves encircling the enemy king (“checkmate” translates to “the king is dead” or “the king is helpless”, from the Persian shah mat). Go, also an encirclement game (but of territory, not of a particular piece), was created much earlier in China (possibly as early as 2000 BC) and eventually became xiangqi’s upper-class cousin, played by elites. These games have stood the test of time and are as much cultural artifacts as they are games. They have captivated generation after generation as players continue to uncover new insights about the games’ strategic depth — insights whose discovery is being accelerated today by artificial intelligence projects like AlphaGo. War games aside, though, “perfect information” is less a genre of game and more a specific property of the game’s information currency.

    许多备受喜爱的完美信息游戏开始于战争游戏,旨在帮助军事将领们提高其战略思维。 国际象棋,象棋和将棋在加图兰加可能是一个共同的祖先该游戏是在公元6世纪古普塔帝国玩的。 乔图兰加(Chaturanga)译为“具有四个部分”,指的是陆军的四个师–步兵,骑兵,战车和大象–包括包围敌方国王(“将军”翻译成“国王已死”或“国王已死”)。无助”, 来自波斯语shah mat )。 围棋,也是一种包围游戏(但不是领土,而是一块领土),是在中国更早( 可能早在公元前2000年 )创造的,并最终成为了由精英们扮演的象棋的上流表弟。 这些游戏经受了时间的考验,并且与游戏一样具有许多文化产物。 随着玩家不断发现有关游戏战略深度的新见解,他们一代又一代着迷。这些见解如今已被AlphaGo等人工智能项目所加速。 但是,除了战争游戏以外,“完美信息”不是游戏的一种,而是游戏信息货币的一种特定属性。

    One consequence of having a perfect information game is that if the game mechanics allow players to directly interact, the game must be turn-based. If players were allowed to act simultaneously, then players would not have perfect knowledge of all previous actions in the game. Because perfect information games are often turn-based, the actions in these games (and the resulting game states) are amenable to being visually represented as trees. If you’ve ever taken an advanced course in game theory, probability, economics, or computer science, you’ll have seen diagrams like these:

    拥有完善的信息游戏的一个后果是,如果游戏机制允许玩家直接互动,则游戏必须基于回合制。 如果允许玩家同时行动,那么玩家将不会完全了解游戏中的所有先前动作。 由于完美的信息游戏通常是基于回合制的,因此这些游戏中的动作(以及最终的游戏状态)都可以直观地表示为树。 如果您曾经学习过博弈论,概率论,经济学或计算机科学方面的高级课程,那么您将看到如下图:

    Image for post
    Image for post
    Image for post
    Image for post

    We’ll avoid getting into the weeds of specific game trees in this article, but the one relevant concept here is the notion of a branching factor. For an individual node (game state) in the tree, the branching factor is the number of new nodes generated from the original one; each possible action you can take from that node gives rise to a new “branch” and a new resulting node. We are usually interested in the average or effective branching factor of a typical node at a given level of the tree.

    在本文中,我们将避免涉及特定游戏树的杂草,但是此处一个相关的概念是分支因子的概念。 对于树中的单个节点(游戏状态),分支因子是从原始节点生成的新节点数。 您可以从该节点执行的每个可能操作都会产生一个新的“分支”和一个新的结果节点。 我们通常对树的给定级别上典型节点的平均或有效分支因子感兴趣。

    The difficulty of a perfect information game is directly proportional to its average branching factor. Perfect information games can take years to master and are notoriously difficult. Not because players lack meaningful information, but because they are overwhelmed by the burden of understanding its implications. You can only think so many moves ahead.

    完美信息游戏的难度与它的平均分支因子成正比。 完美的信息游戏可能需要花费数年才能掌握,而且非常困难。 不是因为玩家缺乏有意义的信息,而是因为他们不堪理解其含义的负担。 您只能认为前进的步伐很多。

    Image for post
    A selection of perfect information games, increasing in complexity
    精选完善的信息游戏,增加了复杂性

    In the first article of this series, we discussed how currencies that never differ in quantity across teams can be excluded from our analysis. Since all players have access to exactly the same information in perfect information games by definition, information is not a relevant currency here. We are therefore left to grapple with just power and influence.

    在本系列的第一篇文章中 ,我们讨论了如何从团队中排除数量上永不相同的货币。 根据定义,由于所有玩家都可以在完美的信息游戏中获得完全相同的信息,因此这里的信息不是一种重要的货币。 因此,我们只剩下权力和影响力。

    功率 (Power)

    Because we are by and large talking about turn-based games, one power currency that deserves attention is turn order.

    因为我们基本上都在谈论回合制游戏,所以值得关注的一种强国货币是回合制。

    In games with two teams, there is often a slight advantage associated with going first. This is especially true in games with high branching factors like chess and go, where small advantages accumulate like compound interest over several levels of the game tree to create winning states. Different games compensate for this known power imbalance in various ways — players may play multiple games as both sides in competitive settings, the advantaged side may be given a handicap, or the win condition for the advantaged side may be altered altogether. For example, in armageddon chess, the player with the White pieces is given more time on their clock, but Black wins the game if they secure at least a draw (while White has to win outright).

    在有两支球队的比赛中,先发通常会带来一点优势。 在象棋和围棋之类具有高分支因子的游戏中尤其如此,在这种情况下,小的利益会像在多个游戏树级别上的复利一样积累,以创建获胜状态。 不同的游戏以各种方式补偿这种已知的力量失衡-玩家可以在竞争环境中作为双方玩多场游戏,可以给优势方让分,或者可以完全改变优势方的获胜条件。 例如,在世界末日象棋中,拥有白色棋子的玩家有更多的时间来计时,但如果黑人至少赢得平局,则黑棋将赢得比赛(而白棋必须完全获胜)。

    In games with three or more teams, the advantage of a particular position in the turn order becomes complicated to assess, especially if players can form impromptu, implicit agreements to eliminate other players first. Multiplayer variants of perfect information games have an even larger branching factor than their two-team counterparts, but they can be a great way to introduce a cooperative dynamic to games that makes turn order more irrelevant.

    在拥有三支或更多支球队的比赛中,评估转弯顺序中特定位置的优势变得很困难,尤其是如果玩家可以形成即席隐含协议以首先淘汰其他玩家。 完美信息游戏的多人游戏变体的分支因子甚至比两团队的同类游戏更大,但它们可能是将合作动态引入游戏的绝佳方式,从而使回合顺序变得无关紧要。

    Image for post
    Image for post
    Image for post
    Image for post
    Multiplayer perfect information games: three player chess (top left), forchess (top right), four player chess (bottom left), and Chinese Checkers (bottom right)
    多人完美信息游戏:三人棋(左上),前叉(右上),四人棋(左下)和中国跳棋(右下)

    The most prominent power currency is the resources of the game itself — typically your pieces. In games like Ludo, Othello, and Go, all pieces move in the same way and therefore have equal value. Points are accumulated based on the particular arrangement of the pieces on the board (the theme generally being “capture territory”), not on the inherent value of the pieces themselves.

    最突出的动力货币是游戏本身资源 -通常是您的作品。 在Ludo,Othello和Go等游戏中,所有棋子都以相同的方式移动,因此具有同等的价值。 积分是根据棋子在棋盘上的特定排列(主题通常是“捕获区域”)而不是棋子本身的内在价值来累积的。

    Image for post
    Image for post
    Image for post
    Examples of pieces in Ludo, Othello, and go
    Ludo,Othello和go中的作品示例

    In games like checkers, chess, and shogi, pieces can move in different ways and therefore have different values. A numerical value might be assigned to the value of the pieces, but experts frequently disagree on what the correct values should be, especially if they are tangential to the game’s win condition (the theme generally being “capture your opponent’s most important piece”).

    在诸如跳棋,国际象棋和将棋的游戏中,棋子可以以不同的方式移动,因此具有不同的价值。 可能会为碎片的值分配一个数值,但是专家经常不同意正确的值,特别是如果这些值与游戏的获胜条件相切时(主题通常是“捕获对手最重要的碎片”)。

    Image for post
    Image for post
    Image for post
    Examples of pieces and boards in checkers, chess, and shogi
    棋盘,象棋和将棋中棋子的例子

    Most perfect information games start with equal resources for all teams. This simplifies the analysis of power considerably and makes the values of individual pieces less relevant because anything I can do to you, you can do to me. As the game progresses, the balance of power shifts, and in most situations, a decisive power advantage should lead to victory. However, power imbalances matter more in games which feature higher variance between the abilities of individual pieces. For example, in chess, the total power of your team is almost always decreasing, and the queen dominates every other piece by far. In such an ecosystem, it is almost impossible to recover from a significant power imbalance short of an immense amount of compensation in your influence currency. Contrast this with shogi, where pieces can reenter the game after they are captured, and the most powerful piece is only the rook. This often leads to mutual checkmating attacks even in the presence of significant power imbalance.

    对于所有团队来说,最完美的信息游戏都以平等的资源开始。 这大大简化了功率分析,并使各个零件的价值不那么相关,因为我对您可以做的一切,您都可以对我做。 随着比赛的进行,力量的平衡会发生变化,在大多数情况下,决定性的力量优势应该会导致胜利。 但是,功率不平衡在游戏中更为重要,因为游戏的各个部分的能力之间存在较大差异。 例如,在国际象棋中,您的团队的总实力几乎总是在下降,而女王/王后则一直统治着其他所有领域。 在这样的生态系统中,几乎不可能从重大的电力失衡中恢复过来,而没有影响货币的巨额补偿。 与将棋相比,将棋子可以在被捕获后重新进入游戏,而最强大的棋子只是新手。 即使存在严重的功率不平衡,这也经常导致相互验证攻击。

    Nevertheless, different teams in perfect information games can start with different resources — or accumulate different resources along the way. Consider some variants of chess:

    但是,在完美的信息游戏中,不同的团队可以从不同的资源开始-或在此过程中积累不同的资源。 考虑一下国际象棋的一些变体:

    Image for post
    Image for post
    Left: monster chess — White gets two moves for every move Black gets. Right: horde chess — one team starts with 32 pawns, all of which must be eliminated.
    左:怪兽棋—白棋每走一步,黑棋就走两步。 正确:部落象棋-一队以32个棋子开始,所有棋子都必须消除。

    Or consider other genres of games altogether, like Bananagrams — a Scrabble-like game where you must use your own stash of lettered tiles to build a valid crossword before anyone else, while drawing additional tiles from a communal pile — and Monopoly. Because players can see everyone’s tiles in real time but can only interact with their own, Bananagrams is one of those rare perfect information games that is not turn-based. Players start off with different resources (i.e. different letters) and continue to receive different resources throughout the game, but the game is balanced around the fact that the English language contains enough words to accommodate most combinations of letters. Similarly, players in Monopoly accumulate different resources (money, properties, houses, hotels) along the way. The game is balanced around when these resources are dispensed, and what benefits are afforded to players who own a particular set of color groups, utilities, etc.

    或完全考虑其他类型的游戏,例如Bananagrams(类似Scrabble的游戏),在这种游戏中,您必须使用自己的带字母的积木藏起来,在其他任何人之前构建有效的填字游戏,同时从公共堆中获取其他积木和垄断。 由于玩家可以实时看到每个人的图块,但只能与自己的图块交互,因此Bananagrams是那些罕见的非回合制完美信息游戏之一。 玩家以不同的资源(即不同的字母)开始,并在整个游戏中继续获得不同的资源,但是游戏围绕以下事实进行了平衡:英语包含足够的单词以容纳大多数字母组合。 同样,“垄断”中的参与者在此过程中积累了不同的资源(金钱,财产,房屋,酒店)。 游戏在分配这些资源的时间以及拥有一组特定颜色组,实用程序等的玩家所能享受的收益方面保持平衡。

    Image for post
    Image for post
    Bananagrams and Monopoly — two perfect information games where players accumulate different resources
    Bananagrams和Monopoly-两种完美的信息游戏,玩家可以累积不同的资源

    Just to drive the point home, these games are still perfect information games because all of these resources are known about from the beginning of the game. Players can plan around them — even when randomness plays a role in a player’s actions.

    这些游戏只是将信息带回家,仍然是完美的信息游戏,因为所有这些资源都是从游戏一开始就知道的。 玩家可以围绕它们进行计划-即使随机性在玩家的行为中起作用。

    The last power currency worth mentioning is time. A player can spend endless amounts of time contemplating their move in a game with a high branching factor, so time is a very pragmatic currency for managing the duration of a game and encouraging more daring play. Typically time is implemented as a currency for the win condition: players lose if they run out of the time before their opponents (think chess clocks and increments, or the byoyomi system in go).

    最后一个值得一提的动力货币是时间 。 玩家可以花费大量时间来考虑在分支因子较高的游戏中的移动,因此时间是管理游戏持续时间并鼓励更大胆游戏的非常实用的货币。 通常情况下,时间是作为获胜条件的一种货币来实现的:如果玩家在对手之前的时间用完了,他们就会输钱(想想国际象棋的钟表和增量,或者在使用byoyomi系统)。

    影响 (Influence)

    Unlike in social deduction games, where the influence currency is immediately accessible because of our innate familiarity with the “rules” of social interaction, influence in a perfect information game can be inscrutable to the novice. That is not to say that social influence is any less complex or multifaceted, but understanding influence in games where everything is known is a lifelong endeavor with many peaks to traverse and a near-infinite skill cap. To put this in perspective, consider two facts: 1) any decent AI for a perfect information game can easily defeat the human world champion in that game, and 2) the best AIs still lose to each other because they do not fully understand the influence currency.

    与社交演绎游戏不同,在社交演绎游戏中,由于我们天生就熟悉社交互动的“规则”,因此可以立即获得影响力货币,而对于新手来说,在完美的信息游戏中的影响力是难以理解的。 这并不是说社会影响力没有那么复杂或多方面,而是要了解在一切已知的游戏中的影响力是一项毕生的努力,需要穿越许多高峰,并且技能上限几乎是无限的。 为了正确理解这一点,请考虑以下两个事实:1)一款完美的信息游戏中任何一款像样的AI都可以轻易击败该游戏中的人类世界冠军; 2)最好的AI仍然会彼此失去,因为它们无法完全理解其影响力。货币。

    At its core, your understanding of influence comes down to your ability to look at the current state of the game and assess how well each team is doing, what each team’s plan should be, and why. Perfect information games test how reliable your heuristics are for navigating and pruning the morass of possible actions you could take when you invariably find yourself in a part of the game tree that you’ve never encountered before — which is almost guaranteed to happen every time you play. Getting better at these games involves assessing myriad factors about a game state. Not just who has more resources (power imbalance), but who has more “space”, whose pieces are better “developed”, who has the “initiative”, who is “attacking” and “defending”, who is making better use of their “tempi”, and so on — where all these terms mean something specific in different games and are highly context dependent.

    从本质上讲,您对影响力的理解取决于您查看游戏当前状态并评估每个团队的表现,每个团队的计划应该是什么以及为什么的能力。 完美的信息游戏可以测试您的试探法在导航和修剪各种杂物时的可靠性,而这些杂物总是会出现在您从未遇到过的游戏树的一部分中时,几乎可以保证每次您都会遇到这种情况玩。 在这些游戏中变得更好涉及评估有关游戏状态的众多因素。 不仅谁拥有更多的资源(权力失衡),谁拥有更多的“空间”,谁拥有更好的“开发”能力,谁拥有“主动性”,谁拥有“进攻”和“捍卫”能力,谁就能更好地利用他们的“ tempi”,依此类推-所有这些术语都表示不同游戏中的特定内容,并且高度依赖于上下文。

    The critical skill in these games is knowing when and how to trade power for influence. This is something AI has been enlightening us on for a long time, especially so in recent years with the advent of neural-network AIs. These AIs do not rely on human-given heuristics. Instead, they build up their own positional understanding by playing games against themselves millions of times. I’ll discuss AlphaGo and AlphaZero in more detail in the next article.

    这些游戏中的关键技能是知道何时以及如何用力量交换影响力。 这是AI长期以来一直启发我们的事情,尤其是近年来随着神经网络AI的出现。 这些AI不依赖于人类赋予的启发式算法。 相反,他们通过与自己进行数百万次比赛来建立自己的位置理解。 在下一篇文章中,我将更详细地讨论AlphaGo和AlphaZero。

    下次之前... (Before next time…)

    In perfect information games, players are armed with the power currencies of turn order, resources, and time as they wade through a deluge of possible actions whose consequences cannot be calculated to endgame. Perfect information games compensate for the irrelevance of the information currency with a richness and inexhaustible depth in their influence currency.

    在完美的信息游戏中,玩家在经历大量可能的行动而无法计算最终结果时,会掌握着回合顺序,资源和时间的强大货币。 完美的信息游戏以其影响力货币的丰富性和不竭深度来补偿信息货币的无关紧要。

    With our introduction to social deduction games and perfect information out of the way, we have set the stage for a deeper exploration of the implications of the market economy framework. In the next article I will talk about the art of actually winning these kinds of games by carefully managing your power and influence currencies, which I will analogize to cash and unrealized gain in a stock market.

    通过介绍社交演绎游戏和完美的信息,我们为深入研究市场经济框架的影响奠定了基础。 在下一篇文章中,我将讨论通过谨慎地管理您的力量和影响货币来真正赢得这类游戏的艺术,我将其模拟为股票市场中的现金和未实现的收益。

    下一篇文章… (Next article…)

    翻译自: https://medium.com/there-will-be-games/on-games-part-3-perfect-information-games-95c0b9600070

    unity第三人称射击游戏

    展开全文
    weixin_26705191 2020-08-20 15:11:33
  • 首先我们需要先理解一下第三人称相机控制的原理: 我们要实现的效果就是:获取鼠标的移动输入,转化成角色以及相机的旋转,通过鼠标输入Input.GetAxis(“Mouse X”)让相机围绕目标角色旋转(transform.RotateAround...

    (关于其他几种角色移动的控制在我之前的博客有写到)

    首先我们需要先理解一下第三人称相机控制的原理:
    我们要实现的效果就是:获取鼠标的移动输入,转化成角色以及相机的旋转,通过鼠标输入Input.GetAxis(“Mouse X”)让相机围绕目标角色旋转(transform.RotateAround)角色通过相机改变的角度改变自身方向(Quaternion.Euler)。

    那么先介绍一下transform.RotateAround(目标物体位置,轴,旋转角度)方法,这个方法是让自身围绕目标物体旋转,所以我们用

    float inputX = Input.GetAxis("Mouse X");
    float inputY = Input.GetAxis("Mouse Y");
    transform.RotateAround(CameraPivot.transform.position, Vector3.up, rotateSpeed * inputX);
    transform.RotateAround(CameraPivot.transform.position, TargetBody.transform.right, -rotateSpeed * inputY);
    

    CameraPivot是相机的焦点物体(最好是设置角色的头部);
    然后 我们将相机的这个旋转角度记录下来(transform.localEulerAngles),并且用Quaternion.Euler改变角色的面向;

    Quaternion TargetBodyCurrentRotation = TargetBody.transform.rotation;
    TargetBody.transform.rotation = Quaternion.Lerp(TargetBodyCurrentRotation, Quaternion.Euler(new Vector3(TargetBody.transform.localEulerAngles.x, transform.localEulerAngles.y, TargetBody.transform.localEulerAngles.z)), TargetBodyRotateLerp);
        
    

    为了方便理解,给大家画一个示意图:

    在这里插入图片描述
    下面给大家简单操作一下!
    还是先创建一个新的场景,里面添加一个plane和一个cube并且调整一下相机位置:
    在这里插入图片描述
    然后上代码!把代码加载摄像机上:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
     
    public class cameraFollow : MonoBehaviour {
        [Header("相机距离")]
        public float freeDistance = 2;
        [Header("相机最近距离")]
        public float minDistance = 0.5f;
        [Header("相机最远距离")]
        public float maxDistance = 3;
        [Header("是否可控制相机距离(鼠标中键)")]
        public bool canControlDistance=true;
        [Header("更改相机距离的速度")]
        public float distanceSpeed = 1;
     
        [Header("视角灵敏度")]
        public float rotateSpeed = 1;
        [Header("物体转向插值(灵敏度,取值为0到1)")]
        public float TargetBodyRotateLerp = 0.3f;
        [Header("需要转向的物体")]
        public GameObject TargetBody;//此脚本能操作转向的物体
        [Header("相机焦点物体")]
        public GameObject CameraPivot;//相机焦点物体  
     
        [Header("是否可控制物体转向")]
        public bool CanControlDirection = true;
        
        private Vector3 offset;//偏移
    
        // Use this for initialization
        void Start () {
            //获取目标与相机之间的差值
            offset = transform.position - CameraPivot.transform.position;
    
        }
     
        
     
        void FreeCamera()
        {
            offset = offset.normalized * freeDistance;
    
             transform.position =  Vector3.Lerp(transform.position,CameraPivot.transform.position + offset,1f) ;//更新位置
     
            if (CanControlDirection)//控制角色方向开关
            {
                Quaternion TargetBodyCurrentRotation = TargetBody.transform.rotation;
                TargetBody.transform.rotation = Quaternion.Lerp(TargetBodyCurrentRotation, Quaternion.Euler(new Vector3(TargetBody.transform.localEulerAngles.x, transform.localEulerAngles.y, TargetBody.transform.localEulerAngles.z)), TargetBodyRotateLerp);
        
            }
     
            //限制相机距离
            if(canControlDistance)//控制距离开关
            {
                freeDistance -= Input.GetAxis("Mouse ScrollWheel") * distanceSpeed;
            }
            freeDistance = Mathf.Clamp(freeDistance, minDistance, maxDistance);
     
          
            //保证相机的注视
            transform.LookAt(CameraPivot.transform.position);
            
            
            //控制相机随鼠标的旋转
            float inputY = Input.GetAxis("Mouse Y");
            float inputX = Input.GetAxis("Mouse X");
            transform.RotateAround(CameraPivot.transform.position, Vector3.up, rotateSpeed * inputX);
            transform.RotateAround(CameraPivot.transform.position, TargetBody.transform.right, -rotateSpeed * inputY);
           
            
            //旋转之后以上方向发生了变化,需要更新方向向量
            offset = transform.position - CameraPivot.transform.position;
            offset = offset.normalized * freeDistance;
            transform.position = CameraPivot.transform.position + offset;
               
            
        
        }
     
        // Update is called once per frame
        void FixedUpdate () {
            FreeCamera();
    	}
     
        private void Update()
        {
            
        }
    }
    

    稍微配置一下参数:
    在这里插入图片描述
    之后 就有相机的初步效果了:
    在这里插入图片描述
    给cube添加上移动的代码 就完成啦!

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Control : MonoBehaviour {
    	float ver = 0;
    	float hor = 0;
    	public float turnspeed = 10f;
    	public float speed = 6f;
    	// Use this for initialization
    	void Start () {
    
    	}
    	// Update is called once per frame
    	void Update () {
    		hor = Input.GetAxis ("Horizontal");
    		ver = Input.GetAxis ("Vertical");
    	}
    	void move (float hor, float ver) {
    		transform.Translate (hor * speed * Time.deltaTime, 0, ver * speed * Time.deltaTime);
    	}
    	void FixedUpdate () {
    		if (hor != 0 || ver != 0) {
    			//转身
    			move (hor, ver);
    		}
    	}
    
    }
    
    

    在这里插入图片描述
    完成!

    展开全文
    ChinaNinjaQAQ 2019-04-20 15:45:02
  • 我们需要用到的原理就是unity射击线函数。具体的原理是,从相机的跟随点向相机的方向发射一条距离为默认距离的相机射线,如该射线能检测到墙体,则相机向前移动。 下面开始发源码, 注:使用的时候要将墙体加碰撞...

    在制作3d射击类游戏中,会遇到跟随角色的摄像机会穿透墙壁导致视野不佳情况,下面,介绍一下具体的解决方法。
    我们需要用到的原理就是unity中射击线函数。具体的原理是,从相机的跟随点向相机的方向发射一条距离为默认距离的相机射线,如该射线能检测到墙体,则相机向前移动。
    在这里插入图片描述
    下面开始发源码,
    注:使用的时候要将墙体加碰撞组件,并且设置其tag为"Cube"

    /*
     * 相机跟随玩家移动,并控制玩家转向,适合第三人称游戏
     */
    using CoverShooter;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CameraMoveWithPlayer : MonoBehaviour
    {
        [Header("相机距离")]
        public float freeDistance = 2;
        [Header("相机最近距离")]
        public float minDistance = 0.5f;
        [Header("相机最远距离")]
        public float maxDistance = 3;
        [Header("是否可控制相机距离(鼠标中键)")]
        public bool canControlDistance = true;
        [Header("更改相机距离的速度")]
        public float distanceSpeed = 1;
    
        [Header("视角灵敏度")]
        public float rotateSpeed = 1;
        [Header("物体转向插值(灵敏度,取值为0到1)")]
        public float TargetBodyRotateLerp = 0.3f;
        [Header("需要转向的物体")]
        public GameObject TargetBody;//此脚本能操作转向的物体
        [Header("相机焦点物体")]
        public GameObject CameraPivot;//相机焦点物体  
    
        [Header("是否可控制物体转向")]
        public bool CanControlDirection = true;
        //相机的距离比例,该向量防止相机撞墙
        private float proportion = 1f;
    
        private Vector3 CubePosition;
        private Vector3 m_rayDirection;
    
        private RaycastHit m_hit;
        Transform m_transform;
        private Transform target;
        private Ray m_ray;
        private Vector3 offset;//偏移
        private RaycastHit[] _hits = new RaycastHit[64];                       // Start is called before the first frame update
    
        private void Awake()
        {
            m_transform = transform;
            CubePosition = m_transform.position;
            target = TargetBody.transform;
        }
        void Start()
        {
            Cursor.lockState = CursorLockMode.Locked;
            //获取目标与相机之间的差值
            offset = transform.position - CameraPivot.transform.position;
        }
    
        void FreeCamera()
        {
            if (CanControlDirection)//控制角色方向开关
            {
                Quaternion TargetBodyCurrentRotation = TargetBody.transform.rotation;
                TargetBody.transform.rotation = Quaternion.Lerp(TargetBodyCurrentRotation, Quaternion.Euler(new Vector3(TargetBody.transform.localEulerAngles.x, transform.localEulerAngles.y, TargetBody.transform.localEulerAngles.z)), TargetBodyRotateLerp);
    
            }
    
            //控制相机随鼠标的旋转
            float inputY = Input.GetAxis("Mouse Y");
            float inputX = Input.GetAxis("Mouse X");
            transform.RotateAround(CameraPivot.transform.position, Vector3.up, rotateSpeed * inputX);
            transform.RotateAround(CameraPivot.transform.position, TargetBody.transform.right, -rotateSpeed * inputY);
    
            transform.LookAt(CameraPivot.transform.position);
            //旋转之后以上方向发生了变化,需要更新方向向量
            offset = transform.position - CameraPivot.transform.position;
            offset = offset.normalized * freeDistance * proportion;
            //更新相机的位置
            transform.position = CameraPivot.transform.position + offset;
        }
    
    
        private void PreventThroughWall()
        {
            //相机根据物体的位置发射一条反向的的射线
            m_rayDirection = m_transform.position - target.position;
            //将该向量规范化,即向量的模为1
            m_rayDirection.Normalize();
            //从相机的跟随目标向相机发射一条距离为相机默认距离的向量
            m_ray = new Ray(target.position, m_rayDirection * freeDistance);
    
            //如果可以检测到碰撞物体,并且碰撞物体的tag未"Cube"
            if (Physics.Raycast(m_ray, out m_hit) && m_hit.collider.tag.Equals("Cube"))
            {
                //获取射击点的坐标
                CubePosition = new Vector3(m_hit.point.x, m_hit.point.y, m_hit.point.z);
                //获取射击点与检测点的距离
                float distance = Vector3.Distance(CubePosition, target.position);
                //更新相机的距离比例
                proportion = Mathf.Min(1.0f, distance / freeDistance);
            }
            else
            {
                proportion = 1.0f;
                offset = offset.normalized * freeDistance;
                transform.position = Vector3.Lerp(transform.position, CameraPivot.transform.position + offset, 1f);//更新位置
                if (canControlDistance)
                {
                    freeDistance -= Input.GetAxis("Mouse ScrollWheel") * TargetBodyRotateLerp * Time.deltaTime;
                    freeDistance = Mathf.Clamp(freeDistance, minDistance, maxDistance);
                }
            }
        }
        // Update is called once per frame
        void FixedUpdate()
        {
            PreventThroughWall();
            FreeCamera();
        }
    
        // Update is called once per frame
        void Update()
        {
            //PreventThroughWall();
        }
    }
    
    
    展开全文
    qq_43788640 2019-06-21 09:39:23
  • qq_40666620 2020-03-31 08:55:05
  • cumian8165 2020-08-10 07:31:32
  • weixin_30711681 2020-05-28 10:24:00
  • wxblzzz 2018-04-17 17:08:59
  • 86KB liufeng778 2015-09-18 16:18:58
  • weixin_39854369 2021-02-11 13:53:39
  • 5星
    3.99MB qq_26621551 2016-08-02 16:07:41
  • 5星
    99B qq_39646949 2021-08-11 19:37:54
  • 12.76MB baidu_24736269 2017-11-22 11:40:40
  • 6.08MB my_blankness 2017-06-18 20:10:18
  • 375KB qq_37526857 2018-07-17 15:47:56
  • weixin_43502713 2021-05-13 15:25:44

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,100
精华内容 440
关键字:

unity第三人称射击