使用transform.rotation = Quaternion.AngleAxis

时间:2018-12-20 17:07:55

标签: c# unity3d

我正在尝试使Player预制脚和鞋子在不向前转动每只鞋子的情况下明智地移动“前进”世界空间。我只希望它指向前方。

我有一个播放器预制件。在预制件上,我有一个脚本,两个游戏对象中每个都有一个圆柱体充当“腿”,另外两个游戏对象中有一个我在每个游戏对象中制成的鞋模。鞋子游戏对象在腿游戏对象内。因此例如:

播放器预制
->右腿游戏对象
->左腿游戏对象
-> RightShoe游戏对象
->左鞋Gameobject  
在每个游戏对象下都有用于腿或鞋的相应模型。

我已经编写了代码,以便玩家的腿可以“移动” /“改变旋转角度”,使其看起来像是在走路(我不知道任何动画,所以这是我唯一知道的方式)。也有代码,使移动玩家可以使用AWSD,而观看或旋转玩家可以使用鼠标,就像任何典型的FPS游戏一样,除了我在第三人称视角下的游戏。

我的玩家的腿部确实相对于世界空间“向前”移动,所以这不是问题,但是当我使用鼠标旋转或朝另一个方向(向左或向右)看时,玩家的鞋子也会向左或向右旋转。起初我以为这双鞋出了点问题,但是我没有为这双鞋写任何代码,只为腿部写了代码。由于我的腿是圆柱形的,所以我没有注意到腿也旋转。我只有在将圆柱状的腿设置为更长的椭圆形或卵形后才发现这一点。

有没有办法让我的鞋子做和腿一样的“动画”,但又不能使其旋转到位?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PlayerController : NetworkBehaviour
{

    public float speedH = 2.0f;
    private float yaw = 0.0f;

    public float WalkingTime;  //timer for walking animation
    public GameObject PlayerLeftLeg;
    public GameObject PlayerRightLeg;

    private float PlayerStatMenuTimer;
    public GameObject PlayerStatsMenu;

    public GameObject ThePlayer;


    // Update is called once per frame
    void Update () {

        if (!isLocalPlayer)
        {
            return;
        }

        //keep track of time for player stat menu
        //if not here than menua will show and hide like a thousand times when pressed once due to update reading code per frame
        PlayerStatMenuTimer = PlayerStatMenuTimer + 1 * Time.deltaTime;


        //moving player left right forward backward
        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 50.0f;
        var z = Input.GetAxis("Vertical") * Time.deltaTime * 50.0f;
        transform.Translate(x, 0, z);


        //rotating player or "Looking"
        yaw += speedH * Input.GetAxis("Mouse X");
        transform.eulerAngles = new Vector3(0.0f, yaw, 0.0f);



        //if player is using WASD to move then do leg moving animation
        //if not moving then set legs to be still and reset in standing position
        //FYI:  "transform.TransformVector(1,0,0)" was used instead of "Vector3.forward" was because
        //   vector3.forward is local space, so when i rotate player the sense of "forward" also changes, thus i needed
        //  a code that uses the world space, thus i used "transform.TransformVector(1,0,0)"
        if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
        {
            CmdWalk();
            RpcWalk();
        }
        else
        {
            //if player not walking then reset
            PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
            PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
            WalkingTime = 0;
        }



        //get hidden mouse pointer back and unlock
        if (Input.GetKey(KeyCode.Escape))
        {
            Cursor.lockState = CursorLockMode.None;
        }



        //opens and closes stat menu
        if (Input.GetKey(KeyCode.Return) && (PlayerStatMenuTimer>=1) && (PlayerStatsMenu.activeSelf==false))
        {
            Cursor.lockState = CursorLockMode.None;
            PlayerStatsMenu.SetActive(true);
            PlayerStatMenuTimer = 0;

            //call the script "GetplayerStats" and call function "retrieceplayerstats"
            var GetStats = GetComponent<GetPlayerStats>();
            GetStats.RetrievePlayerStats();

        }
        else if (Input.GetKey(KeyCode.Return) && PlayerStatMenuTimer >= 1 && PlayerStatsMenu == true)
        {
            Cursor.lockState = CursorLockMode.Locked;
            PlayerStatsMenu.SetActive(false);
            PlayerStatMenuTimer = 0;
        }
    }



    private void Awake()
    {
        //this code locks mouse onto center of window
        //Screen.lockCursor = true;
        Cursor.lockState = CursorLockMode.Locked;
    }


    //initiaztes when started up
    void Start()
    {
        //calls script "SpawnItems" and function "RefreshItems" which will update the players items being shown
        ThePlayer.GetComponent<SpawnItems>().RefreshItems();
    }



    //so COMMAND is for server to client
    //it shows walking for local player
    [Command]
    void CmdWalk()
    {
        //timer
        WalkingTime += Time.deltaTime;

        //right leg stepping forward
        if (WalkingTime > 0 && WalkingTime < .4)
        {
            PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * WalkingTime), transform.TransformVector(1, 0, 0));
            PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * WalkingTime), transform.TransformVector(1, 0, 0));
        }

        //left leg stepping forward
        if (WalkingTime > .4 && WalkingTime < 1.2)
        {
            PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x + (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
            PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x - (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
        }

        //right leg stepping forward
        if (WalkingTime > 1.2 && WalkingTime < 1.59)
        {
            PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
            PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
        }

        //resetting
        if (WalkingTime > 1.6)
        {
            PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
            PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
            WalkingTime = 0;
        }
    }



    //so RPC is for Client to Server
    //it shows walking for other client players
    //https://stackoverflow.com/questions/53784897/unity-moving-player-leg-multiplayer
    [ClientRpc]
    void RpcWalk()
    {
        //timer
        WalkingTime += Time.deltaTime;

        //right leg stepping forward
        if (WalkingTime > 0 && WalkingTime < .4)
        {
            PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * WalkingTime), transform.TransformVector(1, 0, 0));
            PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * WalkingTime), transform.TransformVector(1, 0, 0));
        }

        //left leg stepping forward
        if (WalkingTime > .4 && WalkingTime < 1.2)
        {
            PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x + (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
            PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x - (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
        }

        //right leg stepping forward
        if (WalkingTime > 1.2 && WalkingTime < 1.59)
        {
            PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
            PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
        }

        //resetting
        if (WalkingTime > 1.6)
        {
            PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
            PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
            WalkingTime = 0;
        }
    }
}

我已经为播放器控制器提供了整个脚本,您需要关注的只是CMDWALK或RPC WALK,它们都是同一段代码。

如果有人需要有关腿部运动世界空间的更多信息,请查看此链接,这是我问的另一个问题Unity Moving Player Leg Multiplayer

1 个答案:

答案 0 :(得分:1)

问题在于您正在将Euler角与四元数值混合。这绝不是一个好主意。尽管在四元数空间中可以唯一地表示欧拉角,但围绕一个四元数的另一种方法是在欧拉空间中具有多种表示。因此,您对rotation.x的直接访问是不可靠的(至少不像在AngleAxis中那样使用它。

此外,您正在此处处理全局轮换。您应该改用局部旋转以避免嵌套旋转对象出现问题。


但是在您的情况下,您应该已经说过简单使用

transform.Rotate(60 * WalkingTime, 0, 0, Space.Self); 

只需重置

transform.localRotation = Quaternion.Identity; 

如您在上一个问题的my answer(“更新”部分)中所述,如果您是最初调用该服务的服务器或客户端,则不要忘记跳过ClientRpc避免重复动作。并且不要在同一位置同时调用Cmd和Rpc,这可能会导致奇怪的行为,因为Rpc只能由服务器调用。调用一个方法walk,然后再调用Cmd,然后再调用Rpc