Rewrite the ruby version of rbtris(which use ruby2d gem) use sdl.cr, the screen refresh doesn't seem to work correctly

I am trying rewrite the ruby version of tetris(which use ruby2d gem) use sdl.cr, but the screen refresh doesn’t seem to work correctly.

Following is a screencast which don’t show no blocks except the background color, but when mouse hover on the game, we can see the block flash and then disappear again

1

Any idea?

The source code is there:

Thanks

1 Like

You likely draw the object then draw the background, which erases the object. Looking at the code, you erase before waiting on events, which might be a hint.

1 Like

When creating C bindings in Crystal, I always struggle with how to handle garbage collection (GC).

First, I need to protect callbacks from the GC. On top of that, I have to properly distinguish between memory I allocated myself and memory allocated by the C side. Nested structures and structures that contain strings as members need extra attention. Memory allocated on the C side often requires a dedicated free method to free it, and this is often called from a finalize method during GC. However, when I allocate memory myself on the Crystal side, I have to be careful not to accidentally free it twice. Or maybe I should just use LibC.malloc from the beginning.

In any case, gdb is really helpful for figuring out what’s going on in these situations.

Looking at the code, you erase before waiting on events, which might be a hint.

I don’t quite understand, how can I erase?

I even remove following three lines code, to not render the background, still behavior not same as Ruby2D (latter looks very smooth, but use sdl.cr looks laggy), please check following screencast.

      @background.show
      @play_background.show

      @renderer.clear

1

I mean exactly what I said: you render the background, WAIT for events, then draw the objects, loop, render the background, which erases everything, and WAIT for events again. The screen either stays empty or blocks aren’t reactive.

You can’t wait four events in a game loop, because when you wait nothing happens, while the screen should continue to render.

Most samples in the SDL shard don’t need to continuously render, so they can wait (nothing to update on screen until something happens). But here you need to keep rendering, so you must POLL (and make sure to keep maximum framerate).

Thanks for point it out, i add a new commit for try following your suggestion.

But, i still meet some issue:

  1. It’s really flashing (not as smooth as use Ruby2D gem) (please see following screencast)

1

  1. The blocks only moving(and flashing) when mouse hover on game, otherwise, the screen is static.

  2. When running for a while, there will be no response issue. (see following screenshot)

The issue is still the same using channel only moved the wait to another fiber that will block until something happens, and when a C call blocks, it blocks the whole thread, so other fibers can’t run.

Moving the mouse, for example, triggers events; as the mouse is moving over the screen, the loop can run and the screen is updated, but it’s wonky.

You must POLL for events with SDL::Event.poll. That will return an event if there’s any but won’t block. You can do that right into the game loop. See sdl.cr/samples/14_animated_sprites_and_vsync.cr at master · ysbaddaden/sdl.cr · GitHub for example.

  • loop:
    • poll event (or keep rendering)
    • renderer.clear
    • … render objects …
    • renderer.present (aka push image to screen)

The SDL shard is but a thin wrapper for the libsdl C library. It only adds a bit of abstraction and pointer safety. Read the libsdl documentation to understand all the concepts, and if anything can block, then it will block the whole thread, so be careful.

1 Like

Done to use SDL::Event.poll, it’s works! (don’t need mouse to hover to active the screen update)

The game screen still flashing and lose response, I guess it caused by this?

I am definitely a naive player in game development, how to do this?

the latest code is here

Thank you.

I don’t understand why you need spawn\channel\semaphore here. Isn’t it enough to just (pseudocode)

def run
  loop do
    sleep 0.005.seconds
    # render everything
        .... 
        if something_changed
          update_field
          update_labels
        end
        .... 
        @renderer.clear
        @background.show
        @play_background.show
        @renderer.present
    # poll events
     case (event = SDL::Event.poll)
       when SDL::Event::Quit
         break
       when SDL::Event::Keyboard
         break if event.mod.lctrl? && event.sym.q?
         # process keys here
         on_key_down(event) if is_key_down(event)
         on_key_hold(event) if is_key_hold(event)
     # also process mouse?
     end
  end
end
1 Like

Thanks, I used to write code like this, I revert the code back to write code all in one loop.

Although still not solve the flashing issue.

1

On the first glance it seems like you’re jumping out of your semaphore (with next) in most cases before actually rendering the blocks.

Haven’t tested it yet, but this should explain the tiles showing up only rarely (when all the conditions are actually met to do so).