Unity2D - 以网格方式移动2D对象,如定时方式

时间:2017-03-03 13:44:04

标签: c# unity3d unity5 unity2d

我一直试图让这项工作搁置一段时间,但我失败了。

我在自上而下的2D级别有一个Rigidbody2D,我试图简单地沿着坐标移动它(水平是网格状的),所以一步/按钮等于正好一个方形走路。它应该只能在四个方向中的一个方向上行走,并且无论用户何时停止行走运动,它都应该在正方形上结束。一个相当于我想达到的效果的游戏是lufia / fire of fire /任何类似的RPG系列。我已经尝试使用协同程序让更新函数在每一步之后等待一秒钟,但这似乎不起作用。我最接近的是下面的代码。谢谢你们!

public class PlayerMovement2D : MonoBehaviour
{    
Rigidbody2D rbody;
Animator anim;    
float speed = 1.25f;
Vector2 pos;
void Start()
{
    rbody = GetComponent<Rigidbody2D>();
    anim = GetComponent<Animator>();
    pos = rbody.position;
}
void FixedUpdate()
{
    Vector2 movement_vector = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
    if (movement_vector.x != 0 || movement_vector.y != 0)
    {
        if (movement_vector.x > 0)
        {
            pos += Vector2.right;
            pos.y = 0;
        }          
        else if (movement_vector.x < 0)
        {
            pos += Vector2.left;
            pos.y = 0;
        }
        else if (movement_vector.y > 0)
        {
            pos += Vector2.up;
            pos.x = 0;
        }
        else if (movement_vector.y < 0)
        {
            pos += Vector2.down;
            pos.x = 0;
        }
        anim.SetBool("IsWalking", true);
        anim.SetFloat("Input_x", movement_vector.x);
        anim.SetFloat("Input_y", movement_vector.y);
    }
    else
    {
        anim.SetBool("IsWalking", false);
    }
    rbody.position = Vector2.MoveTowards(rbody.position, pos, speed * Time.deltaTime);
    //transform.position = Vector3.MoveTowards(transform.position, pos, Time.deltaTime * speed);
    pos = rbody.position;
    }
}

3 个答案:

答案 0 :(得分:2)

我认为你想在协程中进行移动,当它处于活动状态时,禁止进一步移动直到它完成。

bool isIdle = true;

void Update() {
    if(isIdle) {
        // Your movement code that gives pos
        StartCoroutine(Move(pos));
    }
}


IEnumerator Move(Vector2 destination) {
    isIdle = false;
    do {
        transform.position = Vector2.MoveTowards(transform.position, destination, speed * Time.deltaTime);
        yield return new WaitForEndOfFrame();
    } while (transform.position != destination);
    isIdle = true;
}

如果您不理解,请告诉我,需要进一步澄清,或者这种方法是否有效!

答案 1 :(得分:2)

我认为您尝试编程的内容类似于Unity网站中教程集合中的RogueLike游戏。首先检查介绍视频,确认这是您计划实现的目标

https://unity3d.com/es/learn/tutorials/projects/2d-roguelike-tutorial/project-introduction?playlist=17150

如果是这样,他们就是这样处理的:

using UnityEngine;
using System.Collections;

    //The abstract keyword enables you to create classes and class members that are incomplete and must be implemented in a derived class.
    public abstract class MovingObject : MonoBehaviour
    {
        public float moveTime = 0.1f;           //Time it will take object to move, in seconds.
        public LayerMask blockingLayer;         //Layer on which collision will be checked.


        private BoxCollider2D boxCollider;      //The BoxCollider2D component attached to this object.
        private Rigidbody2D rb2D;               //The Rigidbody2D component attached to this object.
        private float inverseMoveTime;          //Used to make movement more efficient.


        //Protected, virtual functions can be overridden by inheriting classes.
        protected virtual void Start ()
        {
            //Get a component reference to this object's BoxCollider2D
            boxCollider = GetComponent <BoxCollider2D> ();

            //Get a component reference to this object's Rigidbody2D
            rb2D = GetComponent <Rigidbody2D> ();

            //By storing the reciprocal of the move time we can use it by multiplying instead of dividing, this is more efficient.
            inverseMoveTime = 1f / moveTime;
        }


        //Move returns true if it is able to move and false if not. 
        //Move takes parameters for x direction, y direction and a RaycastHit2D to check collision.
        protected bool Move (int xDir, int yDir, out RaycastHit2D hit)
        {
            //Store start position to move from, based on objects current transform position.
            Vector2 start = transform.position;

            // Calculate end position based on the direction parameters passed in when calling Move.
            Vector2 end = start + new Vector2 (xDir, yDir);

            //Disable the boxCollider so that linecast doesn't hit this object's own collider.
            boxCollider.enabled = false;

            //Cast a line from start point to end point checking collision on blockingLayer.
            hit = Physics2D.Linecast (start, end, blockingLayer);

            //Re-enable boxCollider after linecast
            boxCollider.enabled = true;

            //Check if anything was hit
            if(hit.transform == null)
            {
                //If nothing was hit, start SmoothMovement co-routine passing in the Vector2 end as destination
                StartCoroutine (SmoothMovement (end));

                //Return true to say that Move was successful
                return true;
            }

            //If something was hit, return false, Move was unsuccesful.
            return false;
        }


        //Co-routine for moving units from one space to next, takes a parameter end to specify where to move to.
        protected IEnumerator SmoothMovement (Vector3 end)
        {
            //Calculate the remaining distance to move based on the square magnitude of the difference between current position and end parameter. 
            //Square magnitude is used instead of magnitude because it's computationally cheaper.
            float sqrRemainingDistance = (transform.position - end).sqrMagnitude;

            //While that distance is greater than a very small amount (Epsilon, almost zero):
            while(sqrRemainingDistance > float.Epsilon)
            {
                //Find a new position proportionally closer to the end, based on the moveTime
                Vector3 newPostion = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime);

                //Call MovePosition on attached Rigidbody2D and move it to the calculated position.
                rb2D.MovePosition (newPostion);

                //Recalculate the remaining distance after moving.
                sqrRemainingDistance = (transform.position - end).sqrMagnitude;

                //Return and loop until sqrRemainingDistance is close enough to zero to end the function
                yield return null;
            }
        }


        //The virtual keyword means AttemptMove can be overridden by inheriting classes using the override keyword.
        //AttemptMove takes a generic parameter T to specify the type of component we expect our unit to interact with if blocked (Player for Enemies, Wall for Player).
        protected virtual void AttemptMove <T> (int xDir, int yDir)
            where T : Component
        {
            //Hit will store whatever our linecast hits when Move is called.
            RaycastHit2D hit;

            //Set canMove to true if Move was successful, false if failed.
            bool canMove = Move (xDir, yDir, out hit);

            //Check if nothing was hit by linecast
            if(hit.transform == null)
                //If nothing was hit, return and don't execute further code.
                return;

            //Get a component reference to the component of type T attached to the object that was hit
            T hitComponent = hit.transform.GetComponent <T> ();

            //If canMove is false and hitComponent is not equal to null, meaning MovingObject is blocked and has hit something it can interact with.
            if(!canMove && hitComponent != null)

                //Call the OnCantMove function and pass it hitComponent as a parameter.
                OnCantMove (hitComponent);
        }


        //The abstract modifier indicates that the thing being modified has a missing or incomplete implementation.
        //OnCantMove will be overriden by functions in the inheriting classes.
        protected abstract void OnCantMove <T> (T component)
            where T : Component;
    }

链接到本教程的这一部分: https://unity3d.com/es/learn/tutorials/projects/2d-roguelike-tutorial/moving-object-script?playlist=17150

答案 2 :(得分:2)

您可以结合Unity的{​​{1}}系统使用Vector2.Lerp方法。

Coroutine

此方法假定您可以沿指定方向移动。但在开始public class Movement : MonoBehaviour { IEnumerator m_MoveCoroutine; float m_SpeedFactor; void Update() { // if character is already moving, just return if ( m_MoveCoroutine != null ) return; // placeholder for the direction Vector2 direction; // between { -1, -1 } and { 1, 1 } // calculate your direction based on the input // and set that direction to the direction variable // eg. direction = new Vector2(Input.GetAxisRaw("Horizontal") > 0 ? 1 : -1,...) // then check if direction is not { 0, 0 } if( direction != Vector2.zero ) { // start moving your character m_MoveCoroutine = Move(direction); StartCoroutine(m_MoveCoroutine); } } IEnumerator Move(Vector2 direction) { // Lerp(Vector2 a, Vector2 b, float t); Vector2 orgPos = transform.Position; // original position Vector2 newPos = orgPos + direction; // new position after move is done float t = 0; // placeholder to check if we're on the right spot while( t < 1.0f ) // loop while player is not in the right spot { // calculate and set new position based on the deltaTime's value transform.position = Vector2.Lerp(orgPos, newPos, (t += Time.deltaTime * m_SpeedFactor)); // wait for new frame yield return new WaitForEndFrame(); } // stop coroutine StopCoroutine(m_MoveCoroutine); // get rid of the reference to enable further movements m_MoveCoroutine = null; } } Move之前,您仍应检查新职位是否“适合行走”。