5

Visual C# 2005 で Tetris ゲームのプログラミングに取り組んでいます。これは、私がこれまでに設計した中で最も大規模なプログラムです。

形状クラスとブロック クラスを作成して、さまざまな Tetris ピースの位置、動き、表示を制御します。各シェイプに moveDown()、moveLeft()、および moveRight() 関数 (および対応する canMoveDown()、canMoveLeft()、canMoveRight() ブール関数を使用して、移動しても問題ないことを確認します) があります。これはすべて美しく機能しています。

下、右、左の矢印キーを使用して、ユーザーがブロックを移動できるようにしたいと考えています。また、タイマーを使用して、数ミリ秒ごとに 1 行ずつ自動的に形状が落ちるようにしたいと考えています。

KeyDown イベント ハンドラーを使用して、ユーザーが下、左、右の矢印キーを押したときを確認しています。これはそれほど難しいことではありません。問題は、斜めの動きを許可したいということであり、可能な限りスムーズに動作させたい. 私は、さまざまなレベルの成功で、この問題にアプローチするさまざまな方法を試しました。だけど、なかなかうまくいかない…。

私の最も成功したアプローチは、3 つのブール値変数を使用して、下、左、右の矢印キーが押されている時間を追跡することでした。KeyDown イベントではブール値を true に設定し、KeyUp イベントでは false に設定します。KeyDown イベントでは、ブール変数を使用してどの組み合わせが現在押されているかを確認して、ブロックの移動方法も伝えます。1つのことを除いて、それは本当にうまくいきました。

矢印キーの 1 つを押したままにし、次に 2 つ目の矢印キーを押してから 2 つ目のキーを放した場合、ブロックは、解放されていない最初の矢印キーの方向に移動し続けるのではなく、完全に移動を停止します。まだ。これは、2 番目のキーが KeyDown イベントをトリガーし、そのリリース時に KeyUp イベントが発生し、最初のキーが発生したにもかかわらず、KeyDown イベントの発生が完全に停止したためだと思います。

私は一生、この問題に対する満足のいく解決策を見つけることができません。

どんな助けでも大歓迎です=)

4

3 に答える 3

10

ほとんどのゲームはイベントを待ちません。必要に応じて入力デバイスをポーリングし、それに応じて動作します。実際、XNA を見てみると、更新ルーチンで呼び出す Keyboard.GetState() メソッド (または Gamepad.GetState()) があり、それに基づいてゲーム ロジックを更新することがわかります。結果。Windows.Forms を使用する場合、すぐに使用できるものはありませんが、GetKeyBoardState() 関数を P/Invoke してこれを利用することができます。これの良いところは、一度に複数のキーをポーリングできるため、一度に複数のキーの押下に反応できることです。これに役立つオンラインで見つけた簡単なクラスを次に示します。

http://sanity-free.org/17/obtaining_key_state_info_in_dotnet_csharp_getkeystate_implementation.html

実演するために、基本的にキーボード入力に基づいてボールを動かす単純な Windows アプリを作成しました。リンクしたクラスを使用して、キーボードの状態をポーリングします。一度に 2 つのキーを押したままにすると、斜めに移動します。

まず、Ball.cs:

    public class Ball
    {
        private Brush brush;

        public float X { get; set; }
        public float Y { get; set; }
        public float DX { get; set; }
        public float DY { get; set; }
        public Color Color { get; set; }
        public float Size { get; set; }

        public void Draw(Graphics g)
        {
            if (this.brush == null)
            {
                this.brush = new SolidBrush(this.Color);
            }
            g.FillEllipse(this.brush, X, Y, Size, Size);
        }

        public void MoveRight()
        {
            this.X += DX;
        }

        public void MoveLeft()
        {
            this.X -= this.DX;
        }

        public void MoveUp()
        {
            this.Y -= this.DY;
        }

        public void MoveDown()
        {
            this.Y += this.DY;
        }
    }

本当に何の変哲もない……。

Form1 のコードは次のとおりです。

    public partial class Form1 : Form
    {
        private Ball ball;
        private Timer timer;
        public Form1()
        {
            InitializeComponent();
            this.ball = new Ball
            {
                X = 10f,
                Y = 10f,
                DX = 2f,
                DY = 2f,
                Color = Color.Red,
                Size = 10f
            };
            this.timer = new Timer();
            timer.Interval = 20;
            timer.Tick += new EventHandler(timer_Tick);
            timer.Start();
        }

        void timer_Tick(object sender, EventArgs e)
        {
            var left = KeyboardInfo.GetKeyState(Keys.Left);
            var right = KeyboardInfo.GetKeyState(Keys.Right);
            var up = KeyboardInfo.GetKeyState(Keys.Up);
            var down = KeyboardInfo.GetKeyState(Keys.Down);

            if (left.IsPressed)
            {
                ball.MoveLeft();
                this.Invalidate();
            }

            if (right.IsPressed)
            {
                ball.MoveRight();
                this.Invalidate();
            }

            if (up.IsPressed)
            {
                ball.MoveUp();
                this.Invalidate();
            }

            if (down.IsPressed)
            {
                ball.MoveDown();
                this.Invalidate();
            }


        }


        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (this.ball != null)
            {
                this.ball.Draw(e.Graphics);
            }
        }
    }

シンプルな小さなアプリ。ボールとタイマーを作成するだけです。20 ミリ秒ごとにキーボードの状態をチェックし、キーが押された場合はキーを移動して無効にし、再描画できるようにします。

于 2009-05-24T02:45:37.467 に答える
1

ブロックを動かすためにキーダウンイベントを繰り返し送信するためにキーリピートに依存している場合、これはあなたがやりたい方法ではないと思います。ブロックは、キーの繰り返しとは無関係に一貫して移動する必要があります。したがって、重要なイベント中にブロックを移動するべきではありません。keydown および keyup イベント中にのみキーの状態を追跡し、他の場所での動きを処理する必要があります。実際の移動は、ある種のタイマー イベント (タイマー コントロールは、何も行われていない場合でも一定の間隔でイベントを発生させる) で発生するか、すべての状態を常にチェックし、必要に応じてオブジェクトを移動するメイン ループを用意する必要があります。2 番目のオプションを使用する場合は、"DoEvents" を調べる必要があります。プログラムは、keyup や keydown イベントなどの他のイベントを処理しません。そのため、各ループ内で DoEvents を呼び出して、重要なイベント (特にウィンドウの移動など) を処理する必要があります。常に何かを処理する必要がない場合は、System.Threading.Thread.Sleep を呼び出すこともできます。タイマー コントロールを使用する場合は、そのことについて心配する必要はありません。

于 2009-05-24T02:44:21.353 に答える
0

申し訳ありませんが、具体的なヘルプはありません...結局のところ、土曜日の夜です...しかし:

各キーには状態が必要です。keydown イベントを取得すると、どのキーがダウンしたかを確認し、維持しているキーステートを変更できます。キーが戻ってくると、もう一度、イベントを見ると、それがどれであったかがわかります。イベントが発生したキーの状態のみを変更します。つまり、keyup イベントですべての状態をリセットしないでください。そうしないと、メモリに保持しているキーの状態が壊れてしまいます。

于 2009-05-24T00:28:54.323 に答える