2016-07-15 130 views
1

我使用CocosSharp創建了一個本質上是糖果粉碎的應用程序,這是我實際上第一次使用的東西,同樣適用於C#和Xamarin。要執行動畫,我使用提供的MoveTo方法,但我不確定在繼續執行代碼之前應該如何等待動畫完成。目前,我正在使用await Task.Delay()與while循環一起完成此任務,但這種「感覺」並且看起來「髒」。我想知道等待動畫完成的正確方法是什麼?等待MoveTo操作完成的正確方法是什麼?

下面是一些代碼,我已經寫了:

// Checks to see if a swap is possible, if it is then it will do so 
    // otherwise it will call for a failed swap animation 
    public async void trySwap(int horzDelta, int vertDelta, int fromRow, int fromCol) 
    { 
     //debugLabel.Text = "checking to see if a swap is possible."; 
     int toRow = fromRow + vertDelta; 
     int toCol = fromCol + horzDelta; 

     // Make sure that the user didn't swipe out of the grid as there isn't any candies to swap with out there 
     if (toRow < 0 || toRow >= gridRows) 
     { 
      return; 
     } 
     if (toCol < 0 || toCol >= gridColumns) 
     { 
      return; 
     } 

     candy toCandy = candyAt(toRow, toCol); 
     candy fromCandy = candyAt(fromRow, fromCol); 
     //debugLabel.Text = "Switching candy at [" + fromRow + ", " + fromCol + "] with candy at [" + toRow + ", " + toCol + "]."; 

     Swap swap = new Swap(); 
     swap.candyA = fromCandy; 
     swap.candyB = toCandy; 

     if (isSwapPossible(swap)) 
     { 
      // Swap them 
      animateSwap(swap); 
      await Task.Delay(300); // Wait for the swap animation to finish before continuing 
      dropped = false; // Sets dropped to false, it will be used to check if the game finished dropping all of the candies 
      filledAgain = false; 
      finishedRemoving = false; 
      do 
      { 
       // My reason to add the while loops with the awaits is that the App seems to come back to this do while even before the 
       // the method completely finish running. I'm guessing that awaits in the methods is forcing the App to continue running while it's awaiting 
       // to continue within the method. It's possible that the called methods are running on a separate threads from the thread that is running this 
       // method, so while those threads are put on hold, the App jumps back to this thread. After putting in while loops the app does seems to work like 
       // I want it to so I'm probably on the right track, thought there must be a better way to accomplish as the current way looks ugly. 

       removeMatches();  // Remove the matches 
       while (!finishedRemoving) 
       { 
        await Task.Delay(50); 
       } 
       dropCandies();   // Move the candies down 
       while (!dropped)  // As long as the dropCandies method isn't finished it will keep adding an await 
       { 
        await Task.Delay(50); 
       } 
       fillUpColumns();  // Fill the grid back up 
       while (!filledAgain) 
       { 
        await Task.Delay(50); 
       } 
       detectPossibleSwap(); // Need to update the list of possible swaps 
       await Task.Delay(300); 
      } 
      while (deleteChains.Count != 0); 
      decrementMoves(); 

      // In the case that grid ends up with no possible swaps, we need to refill the grid new candies 
      if (possibleSwaps.Count == 0 && movesLeft != 0) 
      { 
       reshuffle(); 
       while (!doneShuffling) 
       { 
        await Task.Delay(50); 
       } 
      } 
     } 
     else 
     { 
      // failedSwapAnimation only needs to run if there's valid candies 
      if (swap.candyA != null && swap.candyB != null) 
      { 
       // Swap is not possible so run the failed swap animation 
       failedSwapAnimation(swap); 
       // Waiting to make sure the animation has been completed 
       await Task.Delay(300); 
      } 
      else 
      { 
       // The method enables the user interaction again and returns the call point without any type of animation 
       // as the user tried to do a swap with an empty location 
       enableListeners(); 
       return; 
      } 
     } 
     // Turn user interaction back on as all of the matches were removed and the grid filled back up 
     if (movesLeft != 0) 
     { 
      enableListeners(); 
     } 
    } 

這裏,實際上是調用moveTo方法的方法:

// Visually animates the swap using the CCMoveTo function provided by CocosSharp, 
    // also updates the grid location of the candies 
    private void animateSwap(Swap swap) 
    { 
     const float timeToTake = 0.3f; // in seconds 
     CCFiniteTimeAction coreAction = null; 

     // Store the positions of the candies to be used to swap them 
     CCPoint positionA = new CCPoint(swap.candyA.Position); 
     CCPoint positionB = new CCPoint(swap.candyB.Position); 

     // Animate the swapping of the candies 
     coreAction = new CCMoveTo(timeToTake, positionB); 
     swap.candyA.AddAction(coreAction); 
     coreAction = new CCMoveTo(timeToTake, positionA); 
     swap.candyB.AddAction(coreAction); 

     // Update the row and column positions for each candy 
     swap.candyA.setPosition(convertYToRow(positionB.Y), convertXToColumn(positionB.X)); 
     swap.candyB.setPosition(convertYToRow(positionA.Y), convertXToColumn(positionA.X)); 

     // Update the position of the candies within the grid 
     grid[swap.candyA.getRow(), swap.candyA.getColumn()] = swap.candyA; 
     grid[swap.candyB.getRow(), swap.candyB.getColumn()] = swap.candyB; 
    } 

    // Animation for a failed swap 
    private async void failedSwapAnimation(Swap swap) 
    { 
     const float timeToTake = 0.1f; // in seconds 
     CCFiniteTimeAction coreAction = null; 
     CCFiniteTimeAction secondAction = null; 

     // Store the positions of the candies to be used to swap them 
     CCPoint positionA = new CCPoint(swap.candyA.Position); 
     CCPoint positionB = new CCPoint(swap.candyB.Position); 

     // Animate moving the candies back and forth 
     coreAction = new CCMoveTo(timeToTake, positionB); 
     secondAction = new CCMoveTo(timeToTake, positionA); 
     swap.candyA.RunActions(coreAction, secondAction); 
     coreAction = new CCMoveTo(timeToTake, positionA); 
     secondAction = new CCMoveTo(timeToTake, positionB); 
     swap.candyB.RunActions(coreAction, secondAction); 

     // Wait for the animation to complete before moving on 
     await Task.Delay(300); 
    } 

    // Method to find all chains in the grid 
    private void removeMatches() 
    { 
     List<Chain> horizontalChains = detectHorizontalMatches(); 
     List<Chain> verticalChains = detectVerticalMatches(); 

     // Logic to remove the candies from the grid goes here, possibly call a method that takes the list of chains to work with 
     // Don't forget that candies have to be removed from the grid and then afterwards the sprites need to be removed from the screen separately 
     // which can be handle by another method 
     foreach (Chain item in verticalChains) 
     { 
      horizontalChains.Add(item); 
     } 
     deleteChains = horizontalChains; 
     removeCandies(horizontalChains); 
    } 

    // Remove the candy objects from the screen and the grid 
    private async void removeCandies(List<Chain> chains) 
    { 
     if (finishedRemoving != false) 
     { 
      finishedRemoving = false; 
     } 

     foreach (Chain chain in chains) 
     { 
      foreach (candy candy in chain.candies) 
      { 
       // Remove the candy from the grid 
       grid[candy.getRow(), candy.getColumn()] = null; 
       CCSprite removeCandy = candy.getSprite(); 
       if (removeCandy != null) 
       { 
        const float timeToTake = 0.3f; // in seconds 
        CCFiniteTimeAction coreAction = null; 
        CCAction easing = null; 

        coreAction = new CCScaleTo(timeToTake, 0.3f); 
        easing = new CCEaseOut(coreAction, 0.1f); 
        removeCandy.RunAction(coreAction); 

        await Task.Delay(50); // Wait for the scaling animation to show a bit before continuing on to remove the candy 
        //pointGone = false; 
        //pointLabel(candy.getRow(), candy.getColumn()); 
        //while (!pointGone) 
        //{ 
        // await Task.Delay(1); 
        //} 
        removeCandy.RemoveFromParent(); // This should remove the candy from the screen 
        handlePoints(); 
       } 
      } 
      // Wait for all of the candies to be removed before moving on to the next chain in the list of chains 
      await Task.Delay(300); 
     } 
     // Since the method is finished removing all of chains, needed to set the finishedRemoving bool variable to true 
     // so the calling method can get out of it's await loop 
     finishedRemoving = true; 
    } 

    // Drops the candies down 
    private async void dropCandies() 
    { 
     // Makes sure that dropped bool variable is set false before continuing 
     if (dropped != false) 
     { 
      dropped = false; 
     } 
     for (int col = 0; col < gridColumns; col++) 
     { 
      for (int row = 8; row > 0; row--) 
      { 
       if (level.tiles[row, col] == 1) 
       { 
        candy Candy = candyAt(row, col); 
        if (Candy == null) 
        { 
         // Find which row number to drop the candy from 
         int tempRow = row - 1; 
         while (tempRow >= 0 && grid[tempRow, col] == null) 
         { 
          tempRow--; 
         } 
         // Only runs if there's a row that has a candy in it 
         if (tempRow >= 0) 
         { 
          CCPoint position = new CCPoint(70 + (62 * col), 810 - (70 * row)); 
          Candy = candyAt(tempRow, col); 
          Candy.AddAction(new CCEaseOut(new CCMoveTo(0.3f, position), 0.3f)); 
          Candy.setPosition(row, col); // Update the row and column of the candy 
          grid[row, col] = Candy;    // Update the position of the candy within the grid 
          grid[tempRow, col] = null; 
          // Wait for the candy to drop before moving to on the next candy 
          await Task.Delay(50); 
         } 
        } 
       } 
      } 
     } 

     // Since the method should have gone through the entire grid and finished dropping the candies 
     // need to set dropped to true so the calling method can get out of the await loop 
     dropped = true; 
    } 

    // Fill the holes at the top of the of each column 
    private void fillUpColumns() 
    { 
     int candyType = 0; 
     if (filledAgain != false) 
     { 
      filledAgain = false; 
     } 
     for (int col = 0; col < gridColumns; col++) 
     { 
      // Starting at the top and working downwards, add a new candy where it's needed 
      for (int row = 0; row < gridRows && grid[row, col] == null; row++) 
      { 
       if (level.tiles[row, col] == 1) 
       { 
        int newCandyType = 0; 
        // Have to first create a new candy outside of the while loop or otherwise the IDE won't let me use the variable newCandy 
        // as it will be seen as using an unassigned variable, even though it will be assigned a new candy in the while loop 
        candy newCandy = new candy(rand, row, col); 
        newCandyType = newCandy.getType(); 
        // Make sure that each candy that is being added isn't the same as the one that was added previously 
        while (newCandyType == candyType) 
        { 
         newCandy = new candy(rand, row, col); 
         newCandyType = newCandy.getType(); 
        } 
        candyType = newCandyType; 
        grid[row, col] = newCandy; 

        // Once all of the candy is created to fill the grid back up 
        // Use an animation to add it to the screen 
        animateAddingNewCandies(row, col); 
       } 
      } 
     } 
     // Since the entire grid was filled back up with candies, need to set the filledAgain bool variable to true 
     // so the calling method can get out the await loop 
     filledAgain = true; 
    } 

對於那些想看到完整的代碼更好地理解問題,我可以在這裏發佈github鏈接。我現在不會包含它,因爲我不確定這是否允許。對於一些評論感到抱歉,因爲他們當中的一些人只是在寫下我的想法。

+0

序列也許這個鏈接將幫助? http://gamedev.stackexchange.com/questions/41912/wait-till-all-ccactions-have-completed – jgoldberger

回答

1

我會使用與CCMoveTo和CCCallFunc

var moveAction = new CCMoveTo(1.0f, someplace); 
var moveCompletedAction = new CCCallFunc(this.FunctionToCallWhenMoveIsComplete); 
CCSequence mySequence = new CCSequence(moveAction, moveCompletedAction); 
mysprite.RunAction(mySequence); 


.... 

void FunctionToCallWhenMoveIsComplete() 
{ 
    // run your post move code here 
}