Ray's making a Tetris clone, and he needs your help!

Thread in 'Discussion' started by Ray Ayanami, 10 May 2010.

  1. If you are a C# developer, this thread is for you.

    I'm working on a basic Tetris (TGM) clone called Reitromino 2 (the original Reitromino was a school project), in C#, a dangerous, forbidden language banned under international law.

    I'm able to implement randomizers, as well as piece "physics" for Sega RS, and various forms of ARS and pretty much basic graphical stuff. Unfortunately, there now comes two problems:

    1. Timers. Reitromino 1 tanked (in my opinion) because I was having trouble making a timer that would tick 60 times in a second, so I ended up coming up with a proprietary unit of time. For R2 I want to use actual seconds, divided into 60 frames each. This was part of my code for R1:
      public partial class GCon : UserControl
         // code removed
         private Timer m_frameTimer; // the timer
         // constants for milliseconds in 1 second and frames in one second
         private const int ONE_SECOND = 1000, FRAMES_PER_SECOND = 60; 
         // more code removed
         public GCon()
            // code removed
            m_gameTimer = new Timer();
            m_gameTimer.Interval = ONE_SECOND / FRAMES_PER_SECOND;
            m_gameTimer.Tick += new EventHandler(this.gameTimer_Tick);
         // a bunch of other irrelevant (for now) code removed
    2. Libraries and such. I'm sure if I knew a few I could improve my clone, particularly in the graphics department.
    Please forgive me if I come off as an idiot who should just take a .45 to his laptop and end it all; I'm a fairly beginner-level programmer.

    I'll ask additional questions and provide answers to any questions as I go along. I can also publically proide source code for R2 if needed; not so with R1 (you'll have to PM me) because I'm quite unsure if that's going to break academic integrity policies.
  2. Here is a video recorded by a Japanese, i serached from niconicodouga.
    Title : How to write a Tetris clone in one hour(Win32 API and C).
    Maybe it can help you!

    link : http://www.nicozon.net/watch/sm8517855
  3. @Ray what features are u trying to implement atm?

    i just wrote a 40Lines simulator. but i'm having trouble figuring out a suitable format for storing a replay (since it'll only be 40seconds i dont really care much about filesize of replay)..

    (soz for hijacking thread LOL)

    @ people who have programmed clones with replay, how do u store it?
    i was thinking something like save the time + buttons pressed but thats a bit hardcore.

    I also use a timer (there are no "frames") so i cant work off frames (e.g. at frame 1 user pressed harddrop, at frame 2 user pressed this etc)
  4. that's what i'm planning on doing. There really isn't much you can do in terms of reverse-engineering an exact piece movement sequence unless you have the actual keypresses/timing. you said you don't care about the filesize so I don't really see what the problem is?

    @Ray: I have no experience with C# D: Though I hear SDL is ported to it, and that's what I used in keyblox. I used OpenGL for the graphics...
  5. Yeah, you'll have to just a) save the initial seed for the randomizer so you can get the same piece sequence again and b) save every input with a timestamp.

    @Ray: One way to deal with things is to busyloop over Sleep(1) and incrementing a counter by the change in time from the end of the last iteration of the busy loop and the start of this one. Once the counter overflows the interval to start a frame, -- ~16.67ms -- do a frame and subtract that interval from the counter. It'll leave some "change" in the counter. Rather than just do frames at a fixed rate, this will ensure that frames are executed 60 times a second on average. You probably want a case to deal with the counter having a lot of extra "credit" in it when the game lags and can't execute frames on time.
    Last edited: 10 May 2010
  6. To start, I'm going to redo 40 Lines mode, only with actual seconds for time units.
  7. If you decide to use busy loops, one way is with Stopwatch. Another is using a native Windows library; the method you're using in the initial post uses Timer, which is a "sleep" method that gives other processes CPU time while your game waits. It says in the Pygame documentation that busy looping is more accurate than using a timer. I don't know which sort of timing is considered "best practice", or if both are, amongst game developers.

    Oh, I've never used C# before, so if you hit any issues with libraries or whatever, I can't help you there.
  8. Muf


    By far, far, far, the easiest way is to time using QueryPerformanceCounter in a busy loop. All the more elegant methods involve way too much engineering to get them to work properly. Yielding the CPU to other processes involves the kernel thread scheduler, which runs at worst at 40Hz (Win9x) and at best at 1000Hz (when using timeBeginPeriod(1)). Because of fractional frame durations, this means 50fps (hello Cave Story) renders fine (20ms per frame) but 60fps will under- and overshoot half the time (16.666ms per frame). Another functional-but-inherently-broken method is to simply wait for vblank, which will not work on a lot of systems (so you will get unlimited framerate), and will give you anywhere between 60fps, 85fps or 100fps depending on the user's monitor refresh rate in the cases when it does work.

    Like Kitaru said, you may also want to implement frame dropping.
  9. While muf may be correct about QueryPerfomanceCounter being a very precise way of measuring timing, it's not a perfect solution. From what I've read, it fails on multi-core or speed switching CPUs (such as in laptops); it'd work fine on fixed-frequency, single-core/CPU systems. There's ways to deal with multi-core systems, but speed switching is trickier. I did find a page explaining some (still imperfect) solutions for precise timing.

    The pages I've read about issues with QueryPerformanceCounter are kind of old, so I don't know if it no longer has problems in Windows XP/Vista/7 nowadays.
  10. Muf


    I never claimed it was. Just the most easy and widely used solution. Also, the page you linked says "Beware of QueryPerformanceCounter", not "OMG don't use QueryPerformanceCounter". In the case of games that have to run at 60fps, millisecond-or-less precision is less desirable than slight timing discrepancies. Games and video capture applications are two entirely different beasts.

    This. Microsoft guarantees that the hardware timer used for QPC will be accurate and consistent, and corner cases in the past have been due to bugs in the kernel and/or incorrect CPU drivers (hello AMD). Patches and fixes have been issued in all cases that I know of (both by AMD and by Microsoft through Windows Update) and simply keeping your system up to date should solve them. Some games use RDTSC directly, which is wrong and broken to begin with if you take into account variable clock rates and SMP.

    That said, QPC is still known to report slightly different values for function calls run on different cores depending on the system it's used on, so it's recommended to set core affinity for your timer thread whenever possible, or account for the possibility of your game logic going back and forward in time in small amounts. In the case of a simple busy wait for the next frame in a single-threaded game, that's entirely unnecessary.
  11. in delphi i simply use getTickcount at start of game and getTickcount() @ end of game (calls the windows api). THis leads to a time that is accurate to 16ms (i think... is there anything that would prevent this from happening)
  12. Muf


    16.0ms, yes. Not 16.666666₆₆₆₆₆₆ms. So you'll be getting 62.5fps instead of 60fps. Using Kitaru's method you can get a framerate that alternates between 62.5fps and 58.8fps to get an average of 60.0fps over a span of several frames, but it won't be pretty nor accurate.
  13. thats not a problem with my clone because it only redraws when he user hits stuff. theres no animations either.
    hence framerate = no issue woot.

Share This Page