使用基于相机朝向的操纵杆的玩家运动

Posted

技术标签:

【中文标题】使用基于相机朝向的操纵杆的玩家运动【英文标题】:Player Movement using joystick based on camera facing 【发布时间】:2017-07-11 04:51:55 【问题描述】:

我正在开发一款离线 FPS 多人游戏。

当 Player Rotation 的值为 (0,0,0) 时,Player 会在方向上完美移动。但是,我的问题是当我使用触摸输入旋转相机时。玩家也可以旋转脸部并按下摇杆按钮来移动玩家,但此时玩家不应移动相机朝向的方向。

我的玩家操纵杆脚本

public class VirtualJoystick : MonoBehaviour, IDragHandler, IPointerUpHandler,IPointerDownHandler 

    private Image bgImg;
    private Image JoyStickImage;
    private Vector3 InputVector;

    private void Start()
        bgImg = GetComponent<Image> ();
        JoyStickImage = transform.GetChild (0).GetComponent<Image> ();

    

    public virtual void OnDrag(PointerEventData ped)
        Vector2 pos;
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle (bgImg.rectTransform, ped.position, ped.pressEventCamera, out pos)) 
            pos.x = (pos.x / bgImg.rectTransform.sizeDelta.x);
            pos.y = (pos.y / bgImg.rectTransform.sizeDelta.y);

            InputVector = new Vector3 (pos.x * 2 + 1, 0, pos.y * 2 - 1);
            InputVector = (InputVector.magnitude > 1) ? InputVector.normalized : InputVector;

            JoyStickImage.rectTransform.anchoredPosition = new Vector3 (InputVector.x * (bgImg.rectTransform.sizeDelta.x / 2.5f),
                                                                        InputVector.z * (bgImg.rectTransform.sizeDelta.y / 2.5f));


        
    

    public virtual void OnPointerDown(PointerEventData ped)

        OnDrag (ped);
    

    public virtual void OnPointerUp(PointerEventData ped)

        InputVector = Vector3.zero;
        JoyStickImage.rectTransform.anchoredPosition = Vector3.zero;
    

    public float Horizontal()

        if (InputVector.x != 0) 
            return InputVector.x;
         else 

            return Input.GetAxis ("Horizontal");
        

    
    public float Vertical()

        if (InputVector.z != 0)
            return InputVector.z;
        else
            return Input.GetAxis ("Vertical");

    

我的使用输入触摸的相机旋转脚本

public class SwipeCam : MonoBehaviour 

    private Vector3 firstPoint;
    private Vector3 secondPoint;
    private float xAngle = 0.0f;
    private float yAngle = 0.0f;
    private float xAngleTemp = 0.0f;
    private float yAngleTemp = 0.0f;

    void Start()
        xAngle = 0.0f;
        yAngle = 0.0f;
        this.transform.rotation = Quaternion.Euler (yAngle, xAngle, 0.0f);

    

    void Update() 

        if (Input.touchCount > 0) 

            for (int i = 0; i < Input.touchCount; i++) 

                Touch touch = Input.GetTouch (i);

                if (touch.position.x > Screen.width / 2) 

                    if (touch.phase == TouchPhase.Began) 

                        firstPoint = Input.GetTouch (0).position;
                        xAngleTemp = xAngle;
                        yAngleTemp = yAngle;
                    
                    if (touch.phase == TouchPhase.Moved)  
                        secondPoint = Input.GetTouch (0).position;

                        xAngle = xAngleTemp + (secondPoint.x - firstPoint.x) * 180.0f / Screen.width;
                        yAngle = yAngleTemp + (secondPoint.y - firstPoint.y) * 180.0f / -Screen.height;

                        yAngle = Mathf.Clamp (yAngle, -30f, 30f);


                        this.transform.rotation = Quaternion.Euler (yAngle, xAngle, 0.0f);
                        this.gameObject.GetComponentInParent<FPScontroller> ().transform.rotation = Quaternion.Euler (0.0f, xAngle, 0.0f);
                        //this.gameObject.GetComponentInParent<FPScontroller> ().transform.rotation = Quaternion.LookRotation(Vector3.forward,Vector3.up);


                    

                
            
        
    

我应该在哪里更改我的代码以修复面向玩家的相机方向问题。

这是我的播放器脚本 (FPSController.cs)

public class FPScontroller : MonoBehaviour 

。 // 这个脚本应该响应输入吗? 公共布尔canControl =真; 公共游戏对象lookObj; //这是包含MainCamera,Weapons等的根对象。 公共游戏对象操纵杆; bool useFixedUpdate = false;

//Check when run, walk or when can run or not
[HideInInspector]
public bool Running ;
[HideInInspector]
public bool Walking;
[HideInInspector]
public bool canRun;
[HideInInspector]
public Vector3 rorationDir;

//Ladder variables
private GameObject mainCamera = null;
[HideInInspector]
public bool onLadder = false;
//private float ladderHopSpeed = 6.0f;

// For the next variables, @System.NonSerialized tells Unity to not serialize the variable or show it in the inspector view.
// Very handy for organization!

// The current global direction we want the character to move in.
[System.NonSerialized]
public Vector3 inputMoveDirection = Vector3.zero;

// Is the jump button held down? We use this interface instead of checking
// for the jump button directly so this script can also be used by AIs.
[System.NonSerialized]
public bool inputJump = false;

[HideInInspector]
public bool inputRun = false;

[HideInInspector]
public bool inputCrouch = false;

[HideInInspector]
public bool inputProne = false;

[System.Serializable]
public class  FPScontrollerMovement 
    // The maximum horizontal speed when moving
    [HideInInspector]
    public float maxForwardSpeed = 10.0f;
    [HideInInspector]
    public float maxSidewaysSpeed = 10.0f;
    [HideInInspector]
    public float maxBackwardsSpeed = 10.0f;

    //Run and walk variables
    public float WalkSpeed = 6.0f;
    public float RunSpeed = 9.0f;
    //Crouch
    public bool canCrouch = true;
    public float CrouchSpeed = 3.0f;
    public float crouchHeight = 1.5f;
    public float crouchSmooth = 8;
    //prone
    public bool canProne = true;
    public float ProneSpeed = 1.5f;
    public float proneHeight = 0.7f;



    // Curve for multiplying speed based on slope (negative = downwards)
    public AnimationCurve slopeSpeedMultiplier = new AnimationCurve(new Keyframe(-90, 1), new Keyframe(0, 1), new Keyframe(90, 0));

    // How fast does the character change speeds?  Higher is faster.
    public float maxGroundAcceleration = 30.0f;
    public float maxAirAcceleration = 20.0f;

    // The gravity for the character
    public float gravity = 10.0f;
    public float maxFallSpeed = 20.0f;

    [HideInInspector]
    public bool enableGravity = true;

    // For the next variables, @System.NonSerialized tells Unity to not serialize the variable or show it in the inspector view.
    // Very handy for organization!

    // The last collision flags returned from controller.Move
    [System.NonSerialized]
    public CollisionFlags collisionFlags;

    // We will keep track of the character's current velocity,
    [System.NonSerialized]
    public Vector3 velocity;

    // This keeps track of our current velocity while we're not grounded
    [System.NonSerialized]
    public Vector3 frameVelocity = Vector3.zero;

    [System.NonSerialized]
    public Vector3 hitPoint = Vector3.zero;

    [System.NonSerialized]
    public Vector3 lastHitPoint = new Vector3(Mathf.Infinity, 0, 0);

public FPScontrollerMovement movement = new FPScontrollerMovement();

    void Awake () 
    if (GetComponent<NetworkView> ().isMine) 

    joystick = GameObject.Find ("Joystick");
    controller = gameObject.GetComponent<CharacterController>();
    standartHeight = controller.height;
    /*if(GameObject.FindWithTag("LookObject") != null)
        lookObj = GameObject.FindWithTag("LookObject");
    */
    centerY = controller.center.y;
    tr = transform;

    canRun = true;
    canStand = true;
    StartCoroutine(setupBools());

    


    void Update () 
    if (GetComponent<NetworkView> ().isMine) 
        if (!useFixedUpdate) 
            UpdateFunction ();
        

        movement.velocity.x = joystick.GetComponent<VirtualJoystick> ().Horizontal () * 5f;
        movement.velocity.z = joystick.GetComponent<VirtualJoystick> ().Vertical () * 5f;



        //Run input
        if (Input.GetAxis ("Vertical") > 0.1f && inputRun && canRun && !onLadder && Walking) 
            if (canStand && canStandCrouch) 
                OnRunning ();
            
         else 
            OffRunning ();
           

        //Check when walk or not
        if ((movement.velocity.x > 0.01f  || movement.velocity.z > 0.01f) || (movement.velocity.x < -0.01f || movement.velocity.z < -0.01f)) 
            RunAnimation1 ();
            Debug.Log ("Forward");
            Walking = true;
        else if (movement.velocity.x > 0.01f) 
            Walking = true;
            Debug.Log ("Right");
         else if (movement.velocity.x < -0.01f) 
            Walking = true;
            Debug.Log ("Left");
         else 
            RunAnimation ();
            Walking = false;
        


        if (!canControl)
            return;

        if (movement.canCrouch) 
            if (!onLadder)     
                Crouch ();
            
        

        if (movement.canProne) 
            if (!onLadder)     
                Prone ();
            
        

        if (onLadder) 
            grounded = false;
            crouch = false;
            prone = false;
        

        if (!crouch && !prone && controller.height < standartHeight - 0.01f) 
            controller.height = Mathf.Lerp (controller.height, standartHeight, Time.deltaTime / movement.crouchSmooth);
            controller.center = new Vector3 (controller.center.x, Mathf.Lerp (controller.center.y, centerY, Time.deltaTime / movement.crouchSmooth), controller.center.z);
            lookObj.transform.localPosition = new Vector3 (lookObj.transform.localPosition.x, Mathf.Lerp (lookObj.transform.localPosition.y, standartHeight, Time.deltaTime / movement.crouchSmooth), lookObj.transform.localPosition.z);
        
    


void RunAnimation()
    GetComponent<NetworkView> ().RPC ("SysnAnimation", RPCMode.All, 0);

void RunAnimation1()
    GetComponent<NetworkView> ().RPC ("SysnAnimation", RPCMode.All, 1);

void RunAnimation2()
    GetComponent<NetworkView> ().RPC ("SysnAnimation", RPCMode.All, 2);


[RPC]
void SysnAnimation(int index)
    if (index == 0) 
        GetComponent<Animator> ().Play ("Idle Aim");
     else if (index == 1) 
        GetComponent<Animator> ().Play ("Walk Aiming");
     else if (index == 2) 
        GetComponent<Animator> ().Play ("Jump");
    


 void OnRunning ()
    Debug.Log ("Run");
    Running = true;
    movement.maxForwardSpeed = movement.RunSpeed;
    movement.maxSidewaysSpeed = movement.RunSpeed;
    //Make bigger extra height when player run to increase jump distance
    jumping.extraHeight = jumping.baseHeight + 0.15f;


void OffRunning ()
    Running = false;
    if(crouch || prone)
        return;
    movement.maxForwardSpeed = movement.WalkSpeed;
    movement.maxSidewaysSpeed = movement.WalkSpeed;
    movement.maxBackwardsSpeed = movement.WalkSpeed/2;
    //Change extraheight value to default when player walk
    jumping.extraHeight = jumping.baseHeight;

【问题讨论】:

【参考方案1】:

您的相机和操纵杆代码看起来不错,但这不是问题所在。

我假设您的玩家移动代码如下所示:

获取输入 X 和 Y 将玩家向右移动 X,向前移动 Y

在代码形式中,可能看起来像这样:

//returns the world-space direction that player wants to move
Vector3 GetDesiredMovement(float inputForward, float inputRight) 
    //get a vector pointing to player's right
    Vector3 dirRight = Camera.main.transform.right;
    dirRight.y = 0f;
    dirRight.Normalize();

    //get a vector pointing to player's front
    Vector3 dirForward = Camera.main.transform.forward;
    dirForward.y = 0f;
    dirForward.Normalize();

    //calculate desired movement based on input
    Vector3 desiredMovement = (dirForward * inputForward) + (dirRight * inputRight);
    desiredMovement.Normalize();
    return desiredMovement;

如果“右”和“前”需要相对于场景中的其他对象(例如相机)怎么办?这比您想象的要容易:只需直接从相机的变换组件中读取这些值。

您可以通过仅替换上面示例中的两行来做到这一点:

Vector3 dirRight = Camera.main.transform.right;

Vector3 dirForward = Camera.main.transform.forward;

【讨论】:

感谢回答,但我的播放器脚本中没有类似这种类型的代码。 @DarshanSoni 在您的项目中,必须有一些代码读取操纵杆输入并导致玩家移动。这就是您需要调整的代码。 检查我的更新我上传了我的播放器脚本,并且只有在更新功能中我读取了播放播放器动画的操纵杆输入。 在我的 playerScript 更新函数中有两行读取玩家移动的操纵杆输入。 movement.velocity.x = joystick.GetComponent&lt;VirtualJoystick&gt; ().Horizontal () * 5f; movement.velocity.z = joystick.GetComponent&lt;VirtualJoystick&gt; ().Vertical () * 5f; 但是那不是读取玩家的轮换。用操纵杆输入读取播放器旋转有什么变化。请告诉我我做了什么变化。 @DarshanSoni 不用直接分配xz 分量,使用我上面提供的方法组成一个向量会更容易。【参考方案2】:

我解决了基于玩家移动相机方向的问题。

在我的播放器脚本中有两行读取操纵杆输入:

    movement.velocity.x = joystick.GetComponent<VirtualJoystick> ().Horizontal () * 5f;
    movement.velocity.z = joystick.GetComponent<VirtualJoystick> ().Vertical () * 5f;`

我把它们改成了这样:

Vector3 DirectionVector = 
            new Vector3 (joystick.GetComponent<VirtualJoystick> ().Horizontal (), 0f, joystick.GetComponent<VirtualJoystick> ().Vertical ());
movement.velocity = transform.rotation * DirectionVector * 10f;

在运动矢量中直接添加摇杆值。我只是将它乘以操纵杆输入向量并解决我的问题。

现在玩家会根据玩家的旋转和摄像机的朝向移动。 谢谢大家的帮助。

【讨论】:

以上是关于使用基于相机朝向的操纵杆的玩家运动的主要内容,如果未能解决你的问题,请参考以下文章

具有基本矩阵变换(WebGL)的类FPS相机运动

如何区分方向键运动和操纵杆运动?

Unity3D血条(HP)

UNITY怎么让粒子朝向发射方向运动

请问Unity3D中,第三人称视角使用角色控制器的Move函数移动时,怎么实现由鼠标左右移动控制运动朝向?

使用vue学习three.js之移动相机-使相机沿指定路径运动,相机巡检,是物体在指定路径上运动