fps第一视角触屏和相机转动

本文介绍了一个名为OperationController的游戏开发脚本,该脚本负责处理玩家输入并控制游戏中的相机移动及射击行为。通过鼠标或触摸屏操作实现相机角度调整,并支持自动瞄准功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘自我司主程周峰的 operationcontroller.cs   ,其中关于dpi的处理和触屏滑动控制相机值得参考学习。。


using System;
using System.Net.NetworkInformation;
using UnityEngine;
using System.Collections;


public class OperationController : MonoBehaviour
{
    protected static OperationController _inst = null;
    protected bool IsShooting = false;//是否一直发射


    protected AutoAim AimControl;


    public Transform RotateTrans
    {
        get
        {
            if (CachedRotateTrans == null)
            {
                CachedRotateTrans = Camera.main.transform;
                if (CachedRotateTrans.parent != null)
                {
                    CachedRotateTrans = CachedRotateTrans.parent;
                }
            }


            return CachedRotateTrans;
        }
    }


    protected Transform CachedRotateTrans;


    public static OperationController Instance
    {
        get
        {
            if (_inst == null)
            {
                _inst = FindObjectOfType<OperationController>();
                if (_inst == null)
                {
                    var obj = new GameObject("__OperationController");
                    _inst = obj.AddComponent<OperationController>();
                }
            }
            return _inst;
        }
    }


    private void OnEnable()
    {
        _inst = this;
    }


    private void OnDisable()
    {
        _inst = null;
    }


    void ProcessMouse()
    {
        bool isShoot = false;
        if (Input.GetMouseButton(0))
        {
            if (AimControl)
                AimControl.enabled = false;
            if (IsInShootRange(Input.mousePosition))
            {
                isShoot = true;
            }
        }
        else
        {
            if (AimControl)
                AimControl.enabled = true;
        }


        //Debug.LogWarning("pull the trigger" + isShoot);
        WeaponController.Instance.PullTrigger(isShoot || (SceneLoader.Instance.IsAutoAim && AimControl.Target));


        if (IsShooting != isShoot)
        {
            if (isShoot)
            {
                IsShooting = true;
                WeaponController.Instance.BeginShooting();
            }
            else
            {
                IsShooting = false;
                WeaponController.Instance.StopShooting();
            }
        }
    }


    void ProcessTouch()
    {
        bool isShoot = false;
        if (Input.touchCount > 0)
        {//多指触摸阶段
            if (AimControl)
                AimControl.enabled = false;


            int firstOutRange = -1;
            
            isShoot = false;
            for (int i = 0; i < Input.touchCount; i ++)
            {
                var touch = Input.GetTouch(i);
                if (IsInShootRange(touch.position))
                {
                    isShoot = true;
                }
                else
                {
                    if (firstOutRange == -1)
                    {
                        firstOutRange = i;
                    }
                }
            }


            if (firstOutRange == -1)
            {
                firstOutRange = 0;
            }
            Touch finger = Input.GetTouch(firstOutRange);


            if (finger.deltaPosition.SqrMagnitude() > 0 && finger.phase == TouchPhase.Moved)
            {
                Vector2 angularVelocity = Vector2.zero;


                float dt = Time.deltaTime / finger.deltaTime;
                if (float.IsNaN(dt) || float.IsInfinity(dt))
                    dt = 1.0f;
                
                var deltaPosition = finger.deltaPosition * dt;
                angularVelocity = Convert(deltaPosition, DistanceUnit.Pixels, DistanceUnit.Centimeters ) * 10f;


                angularVelocity.y = -angularVelocity.y;
                Vector2 angularMove = /*Time.deltaTime **/ angularVelocity;
                
                Vector3 localAngles = RotateTrans.localEulerAngles;
                // pitch angle
                localAngles.x = Mathf.Clamp(NormalizePitch(localAngles.x + angularMove.y), MaxUpAngle, MinDownAngle);
                // yaw angle
                localAngles.y = Mathf.Clamp(NormalizePitch(localAngles.y + angularMove.x), MinYawAngle, MaxYawAngle);


                localAngles.z = 0;


                RotateTrans.localEulerAngles = localAngles; //RestrictCameraAngles(cam.transform.eulerAngles);


            }
          //  if (isShoot)
            {
            }
           
        }
        else
        {
            if (AimControl)
                AimControl.enabled = true;
            
        }


        WeaponController.Instance.PullTrigger(isShoot || (SceneLoader.Instance.IsAutoAim && AimControl.Target));


        if (IsShooting != isShoot)
        {
            if (isShoot)
            {
                IsShooting = true;
                WeaponController.Instance.BeginShooting();
               // Debug.LogWarning("bofang dongh lo    ");
            }
            else
            {
                IsShooting = false;
                WeaponController.Instance.StopShooting();
            }
        }




    }
    /// <summary>
    /// 以下代码拷贝自“fingerguesture”,
    /// </summary>


    const float DESKTOP_SCREEN_STANDARD_DPI = 96; // default win7 dpi
    const float INCHES_TO_CENTIMETERS = 2.54f; // 1 inch = 2.54 cm
    const float CENTIMETERS_TO_INCHES = 1.0f / INCHES_TO_CENTIMETERS; // 1 cm = 0.3937... inches


    static float screenDPI = 0;


    public static float ScreenDPI
    {
        get
        {
            // not intialized?
            if (screenDPI <= 0)
            {
                screenDPI = Screen.dpi;


                // on desktop, dpi can be 0 - default to a standard dpi for screens
                if (screenDPI <= 0)
                    screenDPI = DESKTOP_SCREEN_STANDARD_DPI;


#if UNITY_IPHONE
                // try to detect some devices that aren't supported by Unity (yet)
                if( iPhone.generation == iPhoneGeneration.Unknown ||
                    iPhone.generation == iPhoneGeneration.iPadUnknown ||
                    iPhone.generation == iPhoneGeneration.iPhoneUnknown )
                {
                    // ipad mini 2 ?
                    if( Screen.width == 2048 && Screen.height == 1536 && screenDPI == 260 )
                        screenDPI = 326;
                }
#endif
            }


            return screenDPI;
        }


        set { screenDPI = value; }
    }
    public enum DistanceUnit
    {
        Pixels,
        Inches,
        Centimeters,
    }
    public static float Convert(float distance, DistanceUnit fromUnit, DistanceUnit toUnit)
    {
        float dpi = ScreenDPI;
        float pixelDistance;


        switch (fromUnit)
        {
            case DistanceUnit.Centimeters:
                pixelDistance = distance * CENTIMETERS_TO_INCHES * dpi; // cm -> in -> px
                break;


            case DistanceUnit.Inches:
                pixelDistance = distance * dpi; // in -> px
                break;


            case DistanceUnit.Pixels:
            default:
                pixelDistance = distance;
                break;
        }


        switch (toUnit)
        {
            case DistanceUnit.Inches:
                return pixelDistance / dpi; // px -> in


            case DistanceUnit.Centimeters:
                return (pixelDistance / dpi) * INCHES_TO_CENTIMETERS;  // px -> in -> cm


            case DistanceUnit.Pixels:
                return pixelDistance;
        }


        return pixelDistance;
    }
    //protected int currentTouchID = -1;
    
    public static float NormalizePitch(float angle)
    {
        angle %= 360;
        if (angle > 180.0f)
            angle -= 360.0f;


        return angle;
    }




    // convert a 2D motion vector from one distance unit to another one
    public static Vector2 Convert(Vector2 v, DistanceUnit fromUnit, DistanceUnit toUnit)
    {
        return new Vector2(Convert(v.x, fromUnit, toUnit),
                            Convert(v.y, fromUnit, toUnit));
    }


#if UNITY_EDITOR
    private static Vector2 LastMousePos;
    
#endif 


    void Update()
    {
        if (GameTimer.Instance.Pause)
        {
            UIBattleManager.Instance.AimPoint.gameObject.SetActive(false);
            return;
        }
        if (BossData.Instance && BossData.Instance.CantTouch)
        {
            UIBattleManager.Instance.AimPoint.gameObject.SetActive(false);
            return;
        }


        UIBattleManager.Instance.AimPoint.gameObject.SetActive(true);


#if UNITY_EDITOR
        if (Input.GetMouseButton(0))
        {
            Vector2 angularVelocity = Vector2.zero;
            if (LastMousePos == Vector2.zero)
            {
                LastMousePos = Input.mousePosition;
              //  return;
            }
            
            var deltaPosition = new Vector2(Input.mousePosition.x - LastMousePos.x,
                Input.mousePosition.y - LastMousePos.y);


            LastMousePos = Input.mousePosition;
            if (deltaPosition != Vector2.zero)
            {
                angularVelocity.x = deltaPosition.x / Screen.width * 40f;
                angularVelocity.y = -deltaPosition.y / Screen.height * 40f;


                Vector2 angularMove = angularVelocity;
                //var cam = Camera.main;


                Vector3 localAngles = RotateTrans.eulerAngles;
                // pitch angle
                localAngles.x = Mathf.Clamp(NormalizePitch(localAngles.x + angularMove.y), MaxUpAngle, MinDownAngle);


           //     Debug.LogWarning(localAngles.y +"     "+ angularMove.x + "     " + (localAngles.y + angularMove.x) + "   " + MinYawAngle + "   " + MaxYawAngle+ "    " + originEuler.y);
                // yaw angle
                localAngles.y = Mathf.Clamp(NormalizePitch(localAngles.y + angularMove.x), MinYawAngle, MaxYawAngle);


                localAngles.z = 0;


                //{
                //    var horizon = finger.deltaPosition.x/Screen.width*150f;
                //    var vertical = finger.deltaPosition.y/Screen.height*150f;
                //    cam.transform.Rotate(-vertical, horizon, 0);
                RotateTrans.eulerAngles = localAngles; //RestrictCameraAngles(cam.transform.eulerAngles);
                
            }


        }
        else
        {
            LastMousePos = Vector2.zero;
        }
        ProcessMouse();
#else
        ProcessTouch();
#endif


        
    }


    //public Vector3 RestrictCameraAngles(Vector3 euler)
    //{
    //    Debug.LogWarning(euler);
    //    if (euler.y > 180)
    //        euler.y = euler.y - 360;


    //    euler.y = Mathf.Clamp(euler.y, originEuler.y - MinYawAngle, originEuler.y + MaxYawAngle);
    //    if (euler.y < 0)
    //        euler.y += 360;




    //    if (euler.x > 180)
    //        euler.x = euler.x - 360;
    //    euler.x = Mathf.Clamp(euler.x, originEuler.x - MaxUpAngle, originEuler.x + MinDownAngle);
    //    if (euler.x < 0)
    //        euler.x += 360;


    //    euler.z = 0;
    //    return euler;
    //}


    public void OnInitScene()
    {
        originEuler = RotateTrans.localEulerAngles;
        if (originEuler.x > 180)
            originEuler.x = originEuler.x - 360;


        if (originEuler.y > 180)
            originEuler.y = originEuler.y - 360;


        Debug.LogWarning(MinDownAngle + " MinDownAngle " + MaxUpAngle + " MaxUpAngle " + MaxYawAngle + " MaxYawAngle " +
                         MinYawAngle + " MinYawAngle ");


        if (SceneLoader.Instance.IsAutoAim && AimControl == null)
            AimControl = gameObject.AddComponent<AutoAim>();
        RecalculateShootRange();
        EnemyController.Instance.SetSceneCfg(SceneLoader.Instance.CurrentCfgData);//DatabaseExporter.DataBaseManager.Instance.SceneCfgDataRecords[0]);
        DynamicSceneObjController.Instance.SetSceneCfg(SceneLoader.Instance.CurrentCfgData);//DatabaseExporter.DataBaseManager.Instance.SceneCfgDataRecords[0]);
        Player.Instance.ResetHp();
        //加载武器
        WeaponController.Instance.DoChangeWeapon();


        IsShooting = false;//是否一直发射
    }


    private Vector3 originEuler;
    private float MinDownAngle
    {
        get { return originEuler.x + StaticData.DownPitchAngle; }
    }


    private float MaxUpAngle
    {
        get { return originEuler.x - StaticData.UpPitchAngle; }
    }


    private float MaxYawAngle
    {
        get { return originEuler.y + StaticData.LeftYawAngle; }
    }


    private float MinYawAngle
    {
        get { return originEuler.y - StaticData.RightYawAngle; }
    }


    /// <summary>
    /// 葛文鹏添加,敌机彩弹等指示使用这个值
    /// </summary>
    /// <returns></returns>
    public Vector3 getOriginEuler()
    {
        return originEuler;
    }




    public bool IsOutOfSight(Quaternion rot)
    {


        
        var euler = rot.eulerAngles;
        euler.x = NormalizePitch(euler.x);
        euler.y = NormalizePitch(euler.y);


 //       Debug.LogWarning(euler + " " + MinDownAngle);
        if (MinDownAngle < euler.x) return true;
 //       Debug.LogWarning("check the x min down angle is correct" + MinDownAngle);
        if (MaxUpAngle > euler.x) return true;
//        Debug.LogWarning("check the x is correct" + MaxUpAngle);
        if (MaxYawAngle < euler.y) return true;
   //     Debug.LogWarning("check the y is correct" + MaxYawAngle);


        if (MinYawAngle > euler.y) return true;
     //   Debug.LogWarning("check the y is correct" + MinYawAngle);


        return false;


    }
    
    ///触控区域,用于控制射击
    private  Vector2 ShootXRange;
    private  Vector2 ShootYRange;
    private  Vector2 ScreenCenter;




    void RecalculateShootRange()
    {
        ShootXRange = new Vector2(Mathf.Floor(Screen.width * (1 - StaticData.HorizontalSize)), Screen.width);
        ShootYRange = new Vector2(0, Screen.height * StaticData.VerticalSize);


        ///屏幕中心点等于1/2屏幕大小
        ScreenCenter = new Vector2(Screen.width*0.5f, Screen.height *0.5f);
    }


    public bool IsInShootRange(Vector2 touchPos)
    {


        if (touchPos.x < ShootXRange.x || touchPos.x > ShootXRange.y)
            return false;
        if (touchPos.y < ShootYRange.x || touchPos.y > ShootYRange.y)
            return false;
        return true;
        //  return true;
    }


    public Vector2 SightPos
    {
        get { return ScreenCenter; }
    }




    public void OnRemoveEnemy(EnemyBase enemy)
    {
        if (AimControl) AimControl.OnRemoveEnemy(enemy);
    }




}

<think>嗯,用户问的是在Unity中如何实现FPS游戏中鼠标移动视角转动。首先,我需要确定用户的需求。他们可能刚开始学习Unity,想做一个第一人称射击游戏,但不知道如何让视角随着鼠标移动。这可能涉及到摄像机控制输入处理。 首先,我应该回忆一下Unity中处理输入的方法。Unity的Input类可以获取鼠标的移动量,比如Input.GetAxis("Mouse X")Input.GetAxis("Mouse Y")。然后,需要将这些输入应用到摄像机的旋转上,以实现视角转动。不过,可能还需要考虑角色的旋转,通常是左右移动鼠标控制角色左右旋转,上下移动控制摄像机的俯仰,而摄像机会作为角色模型的子物体,这样角色旋转时摄像机也会跟着转。 接着,考虑欧拉角的问题。直接旋转可能会导致万向节死锁,但FPS游戏中通常只用Y轴X轴旋转,所以可能不需要四元数。需要将水平输入应用到角色的Y轴旋转,垂直输入应用到摄像机的X轴旋转,同时限制垂直角度,避免摄像头翻转过头,比如限制在-80到80度之间。 还要注意鼠标的锁定隐藏,这样鼠标不会移出游戏窗口,并且不会看到光标,提升游戏体验。Unity中可以用Cursor.lockState = CursorLockMode.LockedCursor.visible = false来实现。 然后,可能需要编写一个脚本,挂在玩家角色上,或者分开挂在角色摄像机上。脚本中需要获取Transform组件,在Update中处理输入,调整旋转。可能需要使用Transform.Rotate方法,或者直接修改欧拉角。 另外,可能要考虑性能问题,比如在Update中处理输入是否合适,不过对于鼠标输入来说,Update是合适的,因为需要每帧检测。 用户可能对代码结构不太熟悉,所以需要给出详细的步骤,比如创建脚本、挂载到对象、变量设置等。示例代码需要注释清楚,说明每一部分的作用,比如获取输入、旋转角色摄像机、角度限制等。 可能还需要提到摄像机的父子关系,即将摄像机作为玩家角色的子物体,这样角色左右旋转时,摄像机也会跟着转,而上下旋转只影响摄像机的局部旋转。这样分离水平垂直旋转,避免冲突。 用户可能遇到的常见问题包括:视角旋转方向相反,这时候可能需要调整输入的符号;角度限制不起作用,可能需要检查Clamp函数的参数;鼠标没有锁定,导致视角转动不流畅,需要检查Cursor的设置。 另外,可能需要考虑不同的坐标系,比如全局本地旋转,使用Transform.Rotate时使用Space.Self还是Space.World,通常应该是Space.Self,即本地坐标系。 最后,总结实现步骤,确保用户能够一步步跟随,并测试调整参数,比如旋转速度角度限制,以适应不同的游戏需求。</think>在 Unity 中实现 FPS 游戏的鼠标视角转动,核心是 **通过鼠标输入控制角色摄像机的旋转**。以下是详细实现步骤: --- ### **1. 基础原理** - **水平移动(左右看)**:鼠标横向移动控制角色(或摄像机)的 **Y 轴旋转**(左右转头)。 - **垂直移动(上下看)**:鼠标纵向移动控制摄像机的 **X 轴旋转**(抬头/低头),通常需要限制角度范围。 - **鼠标锁定**:隐藏鼠标光标并锁定到屏幕中心,避免鼠标移出窗口。 --- ### **2. 实现步骤** #### **(1) 创建角色摄像机结构** - 创建一个空物体(如 `Player`)作为玩家角色。 - 将摄像机(`Main Camera`)作为 `Player` 的子物体,并放置在角色眼睛位置。 - 结构示例: ``` Player (带有角色控制器/刚体) └── Main Camera(视角控制) ``` #### **(2) 编写鼠标控制脚本** 创建一个脚本(如 `MouseLook.cs`),挂载到 `Player` 或 `Camera` 上: ```csharp using UnityEngine; public class MouseLook : MonoBehaviour { [Header("控制对象")] public Transform playerBody; // 角色身体的Transform(控制水平旋转) public Transform cameraTransform; // 摄像机的Transform(控制垂直旋转) [Header("灵敏度")] public float mouseSensitivity = 100f; [Header("垂直视角限制")] public float minVerticalAngle = -80f; public float maxVerticalAngle = 80f; private float xRotation = 0f; // 记录当前垂直旋转角度 void Start() { // 锁定鼠标到屏幕中心并隐藏 Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } void Update() { // 获取鼠标输入(乘以 Time.deltaTime 实现帧率无关的灵敏度) float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime; float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime; // 控制垂直旋转(上下看) xRotation -= mouseY; // 注意:Unity的Y轴输入方向需要取反 xRotation = Mathf.Clamp(xRotation, minVerticalAngle, maxVerticalAngle); cameraTransform.localRotation = Quaternion.Euler(xRotation, 0f, 0f); // 控制水平旋转(左右看) playerBody.Rotate(Vector3.up * mouseX); } } ``` --- ### **3. 参数设置** 1. **关联对象**: - 将 `Player` 物体的 Transform 拖拽到脚本的 `playerBody` 字段。 - 将 `Main Camera` 的 Transform 拖拽到 `cameraTransform` 字段。 2. **调整灵敏度**:通过 `mouseSensitivity` 调整鼠标灵敏度。 3. **限制垂直视角**:通过 `minVerticalAngle` `maxVerticalAngle` 防止视角翻转(例如 -80°~80°)。 --- ### **4. 注意事项** - **帧率无关性**:`Time.deltaTime` 确保不同帧率下的旋转速度一致。 - **输入方向**:垂直输入(`mouseY`)需要取反,因为 Unity 的 `Mouse Y` 默认方向与视角上下移动方向相反。 - **父子层级**:摄像机必须是角色(`Player`)的子物体,确保角色水平旋转时摄像机会跟随。 - **坐标系**:使用 `localRotation` 控制摄像机的局部旋转,避免全局坐标系干扰。 --- ### **5. 扩展优化** - **平滑插值**:使用 `Mathf.Lerp` 或 `Quaternion.Slerp` 实现视角平滑转动。 - **输入设备兼容**:通过 Unity 的 Input System 支持手柄或自定义键位。 - **摄像机抖动**:添加代码实现奔跑、受伤时的视角晃动效果。 --- ### **6. 测试效果** - 运行游戏后,移动鼠标应实现以下效果: - 左右移动鼠标 → 角色水平旋转。 - 上下移动鼠标 → 摄像机垂直旋转(角度受限制)。 - 鼠标光标不可见且锁定在窗口中心。 --- 通过这种方式,你可以快速实现 FPS 游戏的鼠标视角控制基础功能,后续可根据需求添加更多高级特性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值