0
\$\begingroup\$

OK so I have a method that calls itself as this was the only way I could think to get the behaviour I needed. Its a tetris clone i am making and this method is for checking for full lines across X axis.

    void CheckForFullLine(int starting_Y)
{
    for (int y = starting_Y; y < frozenCells.GetLength(1); y++)
    {
        for (int x = 0; x < frozenCells.GetLength(0); x++)
        {
            if (!frozenCells[x, y].isFilled)
            {
                CheckForFullLine(starting_Y + 1);
                return;
            }

            if (x == CELL_COUNT_X - 1)
            {
                tetrisLinesThisTick++;
                Debug.Log("lines this tick: " + tetrisLinesThisTick + "on tick: " + tick);
                ClearLine(y);
            }
        }
    }
}

but when i test the game, most of the time (more than 9/10 times) when you get multiple lines, the debug log says we only got 1 line in that 'tick'.

Here is the update method where at the top I set tetrisLinesPerTick to 0 at the start of each frame.

    private void Update()
{
    tick++;
    // reset the tetris-lines this tick value:
    tetrisLinesThisTick = 0;

    ClearScreen();
    CheckForFullLine(0);
    HandleMovementAndBlockCreation();
    DrawFallingPiece();
    DrawFrozenBricks();
}

For example if I get 2 lines in one go, I get the debug log saying '...linesPerTick 1' twice in quick succession.

Strangely , occasionaly I do get the 2 or even 3 lines and it says the correct amount, but this is rare and i don't know how i reproduce that yet.

Another very strange thing (the relevant code might not be here for this part..) ...quite rarely one of the frozen blocks will turn grey after falling, then I can still 'use' it in game to make more lines, but it doesnt disappear like the others, then make a couple more lines using this grey block and it disappears as normal :S (well just in case you feel like helping me with this part, he is more code :P):

    void ClearLine(int line)
{
    // TODO: Add score

    // remove complete line
    for (int x = 0; x < frozenCells.GetLength(0); x++)
    {
        frozenCells[x, line].isFilled = false;
    }

    //shift all lines down        // todo: figure out if its a multi-line tetris to give better scores...
    for (int y = line; y < frozenCells.GetLength(1); y++)
    {
        for (int x = 0; x < frozenCells.GetLength(0); x++)
        {
            if (frozenCells[x, y].isFilled)
            {
                if (y > 0)
                {
                    frozenCells[x, y].isFilled = false;
                    frozenCells[x, y - 1].isFilled = true;
                }
            }
        }
    }

}

For some reason, I feel it may be the recursive method i made and the fact that 'linesPerTick' value is getting set back to zero prior to the other lines being removed (in the same move), is what is causing my problem but i could be completely wrong about it!

The whole project is just one .cs file and a block texture i made in GIMP, I could paste the whole script here if you want to try it please let me know. Cheers for any help!

\$\endgroup\$
8
  • \$\begingroup\$ Unless you're using a Coroutine, every method you call in Unity will run to completion within one update tick. There's literally no method by which the engine can stop the script and continue rendering the frame unless you explicitly return or yield control back to it. So the error here is in your code not doing the steps you expect it to before finishing, not Unity continuing with its update tick before you've finished. \$\endgroup\$
    – DMGregory
    Commented Jun 20, 2019 at 14:27
  • \$\begingroup\$ thanks very much for the help. I have changed the code quite a bit now, its nearly working but still some problems clearing all the lines. I am going to ask a new question very soon and put a link here. Maybe I can edit this question actually \$\endgroup\$ Commented Jun 20, 2019 at 14:31
  • \$\begingroup\$ @DMGregory if I was to make ClearLines a IEnumerator and then have a yield inside that method somewhere is that the right track for me? (I used to know how to use these but its been a long time and i think i used to only be able to do it for waiting for set seconds, not until a certain loop and action had ended ... I mean it would be a dirty-fix but in my head im thinking there could be a sound effect 0.5secs long and I could force it to wait that time whilst clearing lines? is there a better way maybe? \$\endgroup\$ Commented Jun 20, 2019 at 15:01
  • 1
    \$\begingroup\$ It's best, once a question has an answer, not to alter it to the extent that it demands a different answer. Remember, this Q&A isn't just about helping you privately troubleshoot your game, it's also about creating a reference document that's useful to future developers struggling with similar issues. When you change the question such that it no longer describes your original issue, other devs struggling with similar issues won't be able to learn from this example. \$\endgroup\$
    – DMGregory
    Commented Jun 20, 2019 at 15:05
  • 1
    \$\begingroup\$ LOL @AC I think i have it fixed and it was a super simple and silly issue. It was just the order I called the methods in the update() loop, i was clearing the row before the movement and block creation. since I switch them around and put handle movement/block creation first it seems to be working perfectly . thanks for all help \$\endgroup\$ Commented Jun 20, 2019 at 15:51

1 Answer 1

4
\$\begingroup\$

This is almost certainly an XY problem -- I can't imagine that it makes sense to use a recursive function for this.

If I'm reading your code right (it's late) then you're trying have it stop processing each row as soon as it finds the first empty square instead of processing the whole row, which makes sense. Instead of calling your function again, how about simply continueing on to the next step in your outer Y loop?

I think you've probably also got a logic problem where you call ClearLine upon finding a full row, and that shifts everything down while you're still in the middle of looking for full rows. If your code finds that row 3 is full, then it increments the score counter AND shifts row 4 down into row 3, and then continues checking at row 4. Since you've already shifted 4 to 3, that row never gets checked. Personally, I'd be tempted to move all of the rows after doing all the score calculation, but there are lots of solutions (including just re-checking the same row IF you've called clearline).

Often, I find that working through these problems on some scratch paper can give you a lot of insight into what your code is actually doing when you think it should be doing something else. This is a small enough problem that working though a loop or two shouldn't be too painful ;-)

FWIW, since you're really only copying one row to another regardless of whether it's filled or not, you could probably simplify your ClearLine to something like this pseudocode:

void ClearLine(int line) {
  int topRow = frozenCells.GetLength(1)-1
  for (int y=line; y<topRow; y++) {
    for (int x = 0; x < frozenCells.GetLength(0); x++) {
      frozenCells[x,y].isFilled = frozenCells[x,y+1]
    }
  }
  // The new top row should be empty
  for (int x = 0; x < frozenCells.GetLength(0); x++) {
    frozenCells[x,topRow].isFilled = false
  }
}
\$\endgroup\$
9
  • \$\begingroup\$ cheers for help, never thought of starting at the top row. Will test the code out soon as poss and let you know if it worked. much appreciated \$\endgroup\$ Commented Jun 20, 2019 at 11:21
  • \$\begingroup\$ ah, i never even knew about 'continue' that seems like exactly what i need i will get stuck into some coding again now before work. top man \$\endgroup\$ Commented Jun 20, 2019 at 12:05
  • \$\begingroup\$ sorry to keep popping back with new comments. But how best can I do the 'ClearLine' part only AFTER all full lines are calculated... I'm sure that is the problem as you suggest \$\endgroup\$ Commented Jun 20, 2019 at 12:37
  • \$\begingroup\$ also when i changed the recursive method call to just 'continue;' now if I move a block down to the bottom-right corner of gameboard, it triggers the other part of the method (after continue , which should only be triggered if all blocks in that row are filled \$\endgroup\$ Commented Jun 20, 2019 at 12:41
  • 2
    \$\begingroup\$ I put my quotes around it because I don't believe it's evil -- just misunderstood. There are cases for it, but given that it can make code harder to read/debug and can have performance implications, it's much MUCH more likely to see it misused. Honestly, recursion is kind of in the same boat (to a much lesser degree). It's important to understand the concept of recursion, but unless the problem itself is embarrassingly recursive I'd expect an iterative solution to be simpler to maintain/debug and likely more performant. And btw, continue can also complicate code if it's not really needed... \$\endgroup\$
    – A C
    Commented Jun 20, 2019 at 15:20

You must log in to answer this question.

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