I'm currently creating a tile-based game like a CandyCrush style direction movement (no problem on only down movement) trying to smooth, not laggy tile movement with a board piece, and I'm writing a report about it now.

However, I found it hard to properly describe it. I am having some weird issues with my tile movement code. I am trying to make it so it is a smoothly, in order, and tileses not sorted, buggy, bla bla. Need more optimization...


-I removed DiagonallyMover on code, now It's simply. -But, Vector3 fallDestination is always get lastest null piece. Not sorted.


-I added my movement codes here. -When moveDirection's x and y not 0, It's moving transversely.

My code working like this ;


What I wan't ;


Can someone fix this code, please ? :(

My Movement Code :

public bool UpdateTilePhysics(DestructionBoardPiece curPiece) {
    bool hasTileReachedFallDestination = this.HasTileReachedFallDestination(ref this.fallDestination);
    if (!hasTileReachedFallDestination) {
        this.moveVelocity = Mathf.Min(this.moveVelocity + this.acceleration * Time.smoothDeltaTime, this.maxVelocity);
    } else {
        if (this.isSpawnedFromBehindMask) {
            this.distanceSinceSpawned = Mathf.Max(1f, this.distanceSinceSpawned);
        base.LocalPosition = this.fallDestination;
    return hasTileReachedFallDestination;

protected void UpdateFallingTilePosition(DestructionBoardPiece curPiece) {
    if (this.isSpawnedFromBehindMask) {
        Vector3 a = this.fallDestination;
        a.z = base.LocalPosition.z;
        this.moveDirection = (a - base.LocalPosition).normalized;
        base.LocalPosition += this.moveDirection * this.moveVelocity * Time.deltaTime;
        this.distanceSinceSpawned += this.moveVelocity * Time.deltaTime;
    this.moveDirection = (this.fallDestination - base.LocalPosition).normalized;
    base.LocalPosition += this.moveDirection * this.moveVelocity * Time.deltaTime;

My Gravity Code :

protected IEnumerator ActiveGravityChecker() {
    DestructionBoardPiece destructionBoardPiece = null;
    DestructionBoardPiece destructionBoardPiece2 = null;
    DestructionBoardPiece destructionBoardPiece3 = null;
    while (this.GravityUpdateEnabled) {
        if (!base.enabled || !this.GravityEnabled || base.BoardPiece == null) {
            yield return null;
        } else {
            this.debugActiveGravity = true;
            destructionBoardPiece = (base.BoardPiece as DestructionBoardPiece);
            if (destructionBoardPiece3 == null) destructionBoardPiece3 = destructionBoardPiece;
            this.fallDestination = destructionBoardPiece.LocalPosition;
            bool canMoveDown = false;
            if (destructionBoardPiece.LockCount <= 0 && destructionBoardPiece.BottomLink != null && !destructionBoardPiece.BottomLink.IsBlocked && ((this.tileMovedDiagonally && this.HasReachedBoardPieceArea()) || !this.tileMovedDiagonally)) {
                if (destructionBoardPiece.BottomLink.Tile == null) {
                    destructionBoardPiece2 = destructionBoardPiece.BottomLink;
                    canMoveDown = true;
                    this.tileMovedDiagonally = false;
                } else if (destructionBoardPiece.BottomLink.Tile.IsMoving && this.HasTileInArea(destructionBoardPiece.BottomLink.Tile as DestructionTile)) {
                    this.moveVelocity = (destructionBoardPiece.BottomLink.Tile as DestructionTile).moveVelocity;
            if (destructionBoardPiece.LockCount <= 0 && !canMoveDown && this.HasReachedBoardPieceArea()) {
              //Removed DiagonallyMover Code
            if (canMoveDown) {
                this.fallDestination = destructionBoardPiece2.LocalPosition;
                if (destructionBoardPiece.Tile == this) {
            if (!canMoveDown && this.UpdateTilePhysics(destructionBoardPiece) && (destructionBoardPiece2 == null || (destructionBoardPiece2.Tile != null && !destructionBoardPiece2.Tile.IsMoving) || destructionBoardPiece == destructionBoardPiece2 || destructionBoardPiece.LockCount > 0)) {
                destructionBoardPiece.Tile = this;
                this.TileStoppedMoving(ref destructionBoardPiece3);
                base.IsMoving = false;
                this.moveVelocity = 0f;
            yield return null;
    this.debugActiveGravity = false;
    yield break;
  • \$\begingroup\$ Could you use some kind of linked list that contains nodes? Those nodes are the actual path a block will take when the next block on the path is removed? You will need to get your block to move to each null block along the way to get to the destination tile. \$\endgroup\$
    – Savlon
    Commented Mar 10, 2016 at 7:58

2 Answers 2


I did not look at your code.

What you're trying to reproduce seems to have two "tracks" that the candies follow. They seem to only advance if there is room available in front of them on their own track.

Your implementation seems to make the candies go directly where they should be at, which makes them go out of their own track. Also, some of your candies look like they change track.

I lied, I took a look at your code.

You code looks very very complex, and debugging it looks hard.

I would suggest you implement a simple 'track' system that the candies would follow. You fake the gravity over the track. Or, if we look at it in another way, it would be the same behaviour as what we see on a busy street a Monday morning: cars in a heavy traffic.

Your first tracks should probably be as set of straight lines, then you could add curves.

  • \$\begingroup\$ Oky, I removed 8-direction mover. Its working now. But, another problem is fallDestination coordinate. It get lastes board piece if boardPiece.Tile == null. How can i fix my code ? \$\endgroup\$
    – Dentrax
    Commented Mar 8, 2016 at 14:27
  • \$\begingroup\$ Remember that the people who take their precious time to give you an answer are not here to fix your code for you. \$\endgroup\$
    – user35344
    Commented Mar 8, 2016 at 19:32
  • \$\begingroup\$ @FurkanTürkal I did not look at your updated code, but from the gif you posted, the tiles still look like they're going directly to their end destination. If you're driving on the street, you stay on the street, you don't take shortcuts by driving over your neighbours' lawn. That's what you should aim for. \$\endgroup\$
    – Vaillancourt
    Commented Mar 8, 2016 at 19:37

It looks like you've wandered down a path I once did when working on a similar movement system for tiles a few years ago. I ended up taking a similar route as was taken in Candy Crush.

The first thing to consider is that in your example, you remove tiles while other tiles are still moving. Candy Crush does not do that. You swap two pieces and then the game reacts, clearing those tiles, and moving everything around. It then allows you to interact again.

The advantage of this is it creates distinct points in your logic where you calculate where pieces move to in the tile system and then it animates to those points.

Beyond that, look to Alexandre's advice, as a path system for movement is much more suited for this application. This removes all ambiguity in which direction pieces should flow around the board and removes a lot of the complexity in your system.

  • \$\begingroup\$ I already use a path system. Every piece have direction data. They take the data from the "accelerationMap" on the my level json file. When every piece direction is same, its not problem. If different, its have some issue like first gif. So, I must change my tile mover algorithm, its long, complex, etc. Can you help me, for this movement system ? Or are there any example for 8-direction movement algorithm ? Thanks... \$\endgroup\$
    – Dentrax
    Commented Mar 7, 2016 at 19:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .