Question

In an application I'm creating with GLKit, I have a slider puzzle where the user taps on tiles adjacent to an empty tile and the tile slides to the new position. To reduce the number of methods I had to perform a single task, I decided to try handling the entire sliding animation in one single loop.

- (void)slideSelectedTile
{
    while (!self.selectedTile.hasFinishedMoving)
    {
        [self.selectedTile moveByVector:self.tileDisplacement];
        [self render];
        [NSThread sleepForTimeInterval:.1];
    }
}

The problem is that the render calls do not work inside of this loop. The tile is only rendered once the entire loop is over and the game reaches the next glkViewDrawInRect call, where I also call render, which of course contains code for rendering each tile.

I have checked that the current thread is the main thread when I'm in the loop and that I can manually call render without having it in any sort of loop and it works. What am I doing wrong here?

Was it helpful?

Solution

What you are doing wrong here is you missed the whole concept. This while loop of yours will block the thread you use for drawing and displaying on your screen. Though the buffer is redrawn N times your view is not.

Some quick fixes might be calling perform selector on main thread (and do wait until its done) on your render method. This way you could probably even lose the sleep factor. Another way would be to create another animator thread on which slideSelectedTile would be called but again the render will have to be called on your main thread.

In any case without making a nice code design you will get to some issues later. What you should do is have your object store a source location, target location and current location. It should implement some move method that would compute the current location with interpolation: Linear interpolation would be source + (target-source)*(currentTime/animationTime). In most cases the currentTime and animationTime might actually be numbers of frames as in I want the animation to be done in 6 frames the animationTime is 6 while currentTime iterates in [0,6] and the animation is done when currentTime==animationTime. So in the end you would call something like animateTo:(CGPoint)location in:(NSInteger)frameCount which would set currentTime to zero, animationTime to frameCount, source to current location and target to location.

At this point the question is what calls the move method and what takes the current location: The current location must be used on the main thread in your render method. The move method might be called on some new thread which you can control on how often to call it but it can also be called on every frame before (or inside) the render method.

So my suggestion is when animating some openGL driven code you need to have some repeating calls on your render method for instance use a display link. Use this call to do all the rendering while separating all the movement to some external method which has nothing to do with the rendering or openGL at all. This way you can control the movement on a separate thread or in the same one.

About complicating that movement animation method and interpolation: If you implement it the way I described you will be able to stop in the middle of animation and animate it to some other location with no issues at all.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top