"Le problème qui me dérangeait le plus était le pompage de CPU, ce qui parrait inévitable apparamant :s"
Forcément, si tu fais une boucle infinie, c'est à dire du code qui est exécuté continuellement sans interruption, ton processeur calcul continuellement sans interruption

En utilisant un Timer, tu peux gérer le FPS maximum que tu veux (= 1 / interval), et donc la charge du CPU sera réduite si le code peut-être exécuté pendant le laps de temps entre 2 appels à Timer_Tick. Mais dans un jeu, on ne se gène pas de prendre toutes les resources CPU pour avoir un max de performances, donc on utilise la boucle infinie.
"Et le problème d'une boucle globale qui n'est pas en tache de fond (dans un thread ou backgroundworker à part), c'est l'imposibilité de récupérer les events utilisateurs sans passer par application.doevents, dont je ne raffole pas."
Dans un "vrai jeu", il n'y a pas d'événements (que je sache), les états des entrées utilisateur (clavier et souris) sont testé à chaque itération de la boucle principale. C'est du plus bas niveaux que les événements du genre Mouse_Mouve. Mais rien ne t'empêche de créer ton propre événement MouseMove si la position du curseur à changé entre 2 itérations de la boucle.
"Je pense donc que le moteur du jeu sera comme je l'avais pensé : dans un backgroundWorker pour tout ce qui concerne directement la chute des lettres (boucle en tache de fond) et le reste en événementiel."
J'ai des doutes... Pourquoi la fluidité de tes lettres serait-elle meilleure si tu gère l'affichage dans un autre thread? Un autre thread ne sera pas plus performant que le thread principal pour afficher tes éléments, bien au contraire. Sur une machine monocoeur, le multithreading réduit les performances globales, car le CPU doit passer d'un contexte à l'autre (threads). Dans un "vrai jeu", il n'y a pas de multithreading non plus d'ailleurs. Aujourd'hui le multithreading est envisageable puisque nous avons des machines multicoeur (moi pas encore ^^), on peut par exemple gérer les mouvements (l'affichage) sur un thread et l'IA sur un autre, mais chaque thread reste tout de même très linéaire et sans événements, et les échanges de données entre les 2 threads sont réduits au maximum.
Le multithreading est bien pour avoir l'impression que 2 applications s'exécutent en même temps, mais si on veut un maximum de performances, on évite les changements de contextes et on travail avec un seul thread (comme je l'ai dis au dessus, ceci devient discutable).
Et fais gaffe avec le BackgroundWorker, il s'agit d'un thread en tâche de fond, et donc sa priorité est basse (il ne s'éxécute seulement lorsque du temps CPU est libre).
"Ah, et pour ce qui est du problème d'affichage a cause du threading, je peux tjr créer des fonctions déléguées ou encore mettre la propriété checkforillegal ... a false, ce qui permet de modifer le GUI en passant par des thread tiers."
Pour les déléguées c'est une très mauvaise idée. L'utilisation des déléguées permet d'attendre que le thread appelé soit libre pour y exécuter du code, il y a donc un impact très négatif sur les performances (on passe d'un thread à l'autre, c'est lent)
Pour le
CheckForIllegal=False c'est une idée. Je ne sais pas ce que ça peut donner, mais à ta place j'essaierais voir ce que ça donne
