简体   繁体   English

二维角色精灵 animation 与 3D 等距旋转相机?

[英]2D character sprite animation with 3D isometric rotating camera?

Im trying to write a script to control my players sprite animations that adjusts direction when the camera is rotated.我正在尝试编写一个脚本来控制我的玩家精灵动画,以便在相机旋转时调整方向。 It is a 2D sprite in a 3D isometric world with fixed cameras for each direction.它是 3D 等距世界中的 2D 精灵,每个方向都有固定的相机。 Right now I have three separate scripts for player controller, player animation and sprite renderer.现在我有三个独立的脚本,用于播放器 controller、播放器 animation 和精灵渲染器。

Player Controller:玩家Controller:

public class PlayerController : MonoBehaviour
{
    public float speed = 5f;
    
void Update()
    {
        var moveInput = new Vector3(Input.GetAxis("Horizontal"), 0f , Input.GetAxis("Vertical"));

        moveInput = Camera.main.transform.TransformDirection(moveInput);
        moveInput.y = 0;
        moveInput = moveInput.normalized;
    
        transform.position += moveInput * Time.deltaTime * speed;
    }
}

The controller is based off the cameras direction because multiple cameras are used for each direction. controller 基于相机方向,因为每个方向使用多个相机。

Player Animation:玩家Animation:

public class PlayerAnimation : MonoBehaviour

{
    public Rigidbody2D body;
    public SpriteRenderer spriteRenderer;
    public List<Sprite> nSprite;
    public List<Sprite> neSprite;
    public List<Sprite> eSprite;
    public List<Sprite> seSprite;
    public List<Sprite> sSprite;
    public float frameRate;
    float idleTime;
    Vector3 direction;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        direction = new Vector3(Input.GetAxis("Horizontal"), 0f , Input.GetAxis("Vertical")).normalized;
        
        direction = Camera.main.transform.TransformDirection(direction);

        HandleSpriteFlip();

        List<Sprite> directionSprites = GetSpriteDirection();

        if(directionSprites != null) //holding direction
        {
            float playTime = Time.time - idleTime; //time since we sterted walking
            int totalFrames = (int)(playTime * frameRate); //total frames since we started
            int frame =  totalFrames % directionSprites.Count; //current frame

            spriteRenderer.sprite = directionSprites[frame];
        }
        else //holding nothing, input is neutral
        {
            idleTime = Time.time;
        }
    }

    void HandleSpriteFlip()
    {
        if (!spriteRenderer.flipX && direction.x < 0)
        {
            spriteRenderer.flipX = true;
        }
        else if (spriteRenderer.flipX && direction.x > 0)
        {
            spriteRenderer.flipX = false;
        }
    }

    List<Sprite> GetSpriteDirection()
    {

        List<Sprite> selectedSprites = null;

        if (direction.y > 0) //north
        {
            if (Mathf.Abs(direction.x) > 0) //east or west
            {
                selectedSprites = neSprite;
            }
            else {//neutral
                selectedSprites = nSprite;
            }
        }
        else if (direction.y < 0) //south
        {
            if (Mathf.Abs(direction.x) > 0) //east or west
            {
                selectedSprites = seSprite;
            }
            else {//neutral
                selectedSprites = sSprite;
            }
        }
        else //neutral
        {
            if (Mathf.Abs(direction.x) > 0) //east or west
            {
                selectedSprites = eSprite;
            }
        }

        return selectedSprites;
    }
}

This player animation script that I was following made a lot of since to me but it was written with a 2D controller. How should I add the walk animations, can it tracks the players current facing direction to update the sprite renderer?我关注的这个播放器 animation 脚本对我来说很重要,但它是用 2D controller 编写的。我应该如何添加行走动画,它可以跟踪播放器当前面向的方向以更新精灵渲染器吗?

Sprite Renderer:精灵渲染器:

private SpriteRenderer spriteRenderer;

public Transform plane;
public Camera cam;

private const float step = 22.5f;

public Sprite N, NW, W, SW, S, SE, E, NE;
public void Start() => spriteRenderer = GetComponent<SpriteRenderer>();
public void Update()
{
    var projected = Vector3.ProjectOnPlane(cam.transform.forward, plane.up);
    var angle = Vector3.SignedAngle(projected, plane.forward, plane.up);
    
    var AbsAngle = Mathf.Abs(angle);
    
    if (AbsAngle < step) spriteRenderer.sprite = N;
    else if (AbsAngle < step*3) spriteRenderer.sprite = Mathf.Sign(angle) < 0 ? NW : NE;
    else if (AbsAngle < step*5) spriteRenderer.sprite = Mathf.Sign(angle) < 0 ? W : E;
    else if (AbsAngle < step*7) spriteRenderer.sprite = Mathf.Sign(angle) < 0 ? SW : SE;
    else spriteRenderer.sprite = S;
    
    Billboard(spriteRenderer.transform, cam);
}
public void Billboard(Transform character, Camera mainCamera)
{
    var dir = plane.position - mainCamera.transform.position;
    var LookAtRotation = Quaternion.LookRotation(dir);
    
    var LookAtRotationOnly_Y = Quaternion.Euler(character.rotation.eulerAngles.x, LookAtRotation.eulerAngles.y,character.eulerAngles.z);
    character.rotation = LookAtRotationOnly_Y;
}

This is from the question commented below and it works to update the camera on a stationary object, but how would i use it for a moving player that changes facing direction.这是来自下面评论的问题,它可以在静止的 object 上更新相机,但我如何将它用于改变朝向的移动玩家。

To solve this problem, I propose a special technique here.为了解决这个问题,我在这里提出了一种特殊的技术。 All you have to do is replace the sprites with sprite animations.您所要做的就是用精灵动画替换精灵。 It does take a bit of patience though.不过确实需要一点耐心。 First you have to create motion animations as well as idle in all directions as below and then make sure the animation is executed with the play key.首先,您必须创建运动动画以及如下各个方向的空闲动画,然后确保使用播放键执行 animation。

在此处输入图像描述

After doing this you need an animator.完成此操作后,您需要一名动画师。 But not like the old days when you connected them with a transition.但不像过去那样将它们与过渡联系起来。 There are 4 directions below, you can enter all 16 states in the animator as below and set the default to none without any animation. The purpose of this animator is only to store animations.下面有4个方向,你可以在animator中输入所有16个状态如下,设置默认为none,没有任何animation。这个animator的目的只是为了存储动画。 Changes will be made by the script.脚本将进行更改。 We only need the names of the states, carefully follow the string codes and name them as follows.我们只需要州的名称,仔细按照字符串代码命名,如下所示。

Animation string codes : Animation 字符串代码

  • for Move : "move" + "{direction key}"对于移动:“移动”+“{方向键}”
  • for Idle : "idle" + "{direction key}"对于空闲:“空闲”+“{方向键}”

在此处输入图像描述

在此处输入图像描述

After performing the above steps, it's time to actually change states instead of changing sprites.执行上述步骤后,是时候真正改变状态而不是改变精灵了。 To do this, we no longer need to have the SpriteRenderer component, instead we get the Animator .为此,我们不再需要SpriteRenderer组件,而是获得了Animator

public Animator animator;

public void Start() => animator = GetComponent<Animator>(); //

In the continuation of the program, instead of changing the sprite in different directions, you should change the states according to the direction of the character.在程序的延续中,你应该根据角色的方向改变状态,而不是改变不同方向的精灵。 here we have a stateName variable that chooses which state to play at the end of the code.这里我们有一个stateName变量,它选择在代码末尾播放哪个 state。 If you change the forward character through the movement code, here it is enough to select the speed of movement to select "idle" or "move" , then the direction extension is connected to the name state and the desired animation will be executed.如果通过移动代码改变正向字符,这里就足够了 select 移动速度为 select "idle""move" ,然后方向扩展连接到名称 state 并且将执行所需的 animation 。

// this is condition of choosing between idle or move, you can get direction vector like before

var stateName = GetDirection().magnitude != 0 ? "move" : "idle"; 

if (AbsAngle < step) stateName += "N";
else if (AbsAngle < step*3) stateName += Mathf.Sign(angle) < 0 ? "NW" : "NE";
else if (AbsAngle < step*5) stateName += Mathf.Sign(angle) < 0 ? "W" : "E";
else if (AbsAngle < step*7) stateName += Mathf.Sign(angle) < 0 ? "SW" : "SE";
else stateName += "S";

if (!animator.GetCurrentAnimatorStateInfo(0).IsName(stateName)) animator.Play(stateName);
public Vector3 GetDirection() => new Vector3(Input.GetAxis("Horizontal"), 0f , Input.GetAxis("Vertical")).normalized;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM