2017-03-03 72 views
1

我一直在努力使这项工作有一段时间,我失败了。Unity2D - 以时间方式移动网格中的2D对象

我有一个Rigidbody2D在自上而下的2D级别,我试图简单地沿着坐标移动它(这些级别是网格状的),所以一步/按下按钮等于一个正方形走过。只能沿四个方向中的一个方向行走,不管用户什么时候停止步行运动,它都应该在方形上结束。相当于我想达到的一个好游戏是火焰/呼吸/任何类似的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; 
    } 
} 
+0

如果您尝试了任何方法或接受解决您的问题的答案,请告诉我们。 – Maakep

回答

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

+1

嘿,谢谢你的建议!我的一位同事在我发布之后提出了这个建议,所以我打算无论如何都要检查一下。我能够用另一个评论来修正我的代码,但我也一定要阅读/观看这个。谢谢! –

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

嘿弗雷德里克,我很抱歉我没有更快回答。我尝试了你的代码,并且它本身崩溃了Unity /陷入了一段时间循环。我试着简单地使用协程位,然后改变它以适应我的代码,但那并不如下面的解决方案。无论如何,谢谢你的帮助! –

2

您可以使用UnityCoroutine系统的组合Vector2.Lerp方法。

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; 
    } 
} 

此方法假定您可以在指定的方向移动。但在开始MoveCoroutine之前,您仍应该检查您的新位置是否“可行走”。

+0

谢谢!我认为我的协同程序出现了一些错误,这个工程非常漂亮。正如你所说,我仍然需要消除碰撞,以前的代码只是简单地使用碰撞体和刚体似乎工作,现在我的角色可以穿过墙壁。 –