2017-03-03 54 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

謝謝!我認爲我的協同程序出現了一些錯誤,這個工程非常漂亮。正如你所說,我仍然需要消除碰撞,以前的代碼只是簡單地使用碰撞體和剛體似乎工作,現在我的角色可以穿過牆壁。 –