C # - clone di Tetris - Impossibile ottenere blocco per rispondere adeguatamente alle combinazioni di tasti freccia

StackOverflow https://stackoverflow.com/questions/902767

  •  05-09-2019
  •  | 
  •  

Domanda

Sto lavorando sulla programmazione un gioco di Tetris in Visual C # 2005. Questo è il programma più esteso che ho ancora disegnato.

Creo una classe forma e una classe blocco per controllare la posizione, movimento, e la visualizzazione dei diversi pezzi Tetris. Ho MoveDown (), moveLeft (), funzioni e MoveRight () per ogni forma (e corrispondenti canMoveDown (), canMoveLeft (), canMoveRight () funzioni booleane che verificano è consentito muoversi). Questo è tutto funziona meravigliosamente.

Voglio usare il basso, a destra e tasti freccia sinistra per consentire all'utente di spostare il blocco attorno, oltre a utilizzare un timer per avere la forma cadere automaticamente una riga ogni tanti millisecondi.

Sono utilizzando il gestore di eventi KeyDown per controllare quando l'utente preme il basso, a sinistra, e il tasto freccia destra. Questo non è così difficile. Il problema è che voglio per consentire il movimento in diagonale, e voglio farlo funzionare nel modo migliore possibile. Ho provato un sacco di diversi modi di affrontare questo problema, con vari livelli di successo. Ma io non riesco a farlo perfettamente ragione ...

Il mio approccio di maggior successo è stato quello di utilizzare tre variabili booleane per tenere traccia di quando il basso, a sinistra, e tasti freccia destra sono in corso verso il basso. Vorrei impostare le booleani true nell'evento KeyDown e false in caso KeyUp. In caso KeyDown Vorrei anche dire al blocco di come muoversi, utilizzando le variabili booleane per verificare quale combinazione è attualmente in fase di pressione. Ha funzionato molto bene, tranne una cosa.

Se premuto uno dei tasti freccia e tenuto, quindi pressata una seconda freccia e poi rilasciato il secondo tasto, il blocco si fermano del tutto, invece di continuare a muoversi nella direzione della prima freccia che hasn' t stato ancora rilasciato. Penso che questo sia perché la seconda chiave ha attivato l'evento KeyDown, e alla sua uscita stato licenziato l'evento KeyUp, e l'evento KeyDown smesso di sparare completamente, anche se la prima chiave è sparato.

non posso per la vita me di trovare una soluzione soddisfacente a questo problema.

Qualsiasi aiuto sarebbe molto apprezzato =)

È stato utile?

Soluzione

La maggior parte dei giochi non aspettano gli eventi. Essi polling il dispositivo di input quando neccessary e agiscono accodringly. In realtà, se mai dà un'occhiata a XNA, vedrete che c'è un metodo Keyboard.GetState () (o Gamepad.GetState ()) che si chiama nella vostra routine di aggiornamento, e aggiornare la vostra logica di gioco basato sul i risultati. Quando si lavora con Windows.Forms, non c'è niente fuori dalla scatola per fare questo, ma è possibile P / Invoke la funzione GetKeyboardState () per trarre vantaggio da questo. La cosa buona di questo è che è possibile interrogare più tasti contemporaneamente, e quindi si può reagire a più di un tasto alla volta. Ecco una semplice classe che ho trovato on-line che aiuta con questo:

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

Per dimostrare, ho scritto una semplice applicazione di Windows che si muove fondamentalmente una sfera intorno sulla base di input da tastiera. Esso utilizza la classe ho collegato a, per interrogare lo stato della tastiera. Si noterà che, se si tiene premuto due tasti alla volta, si muoverà in diagonale.

In primo luogo, 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;
        }
    }

In realtà niente di speciale a tutti ....

Poi ecco il codice 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);
            }
        }
    }

Semplice piccola applicazione. Crea solo una palla e un timer. Ogni 20 millisecondi, esso controlla lo stato della tastiera, e se viene premuto un tasto si muove e invalida in modo che possa ridisegnare.

Altri suggerimenti

Se vi affidate su ripetizione chiave per inviare ripetutamente il tasto giù eventi per far muovere blocco, non credo che questo è il modo in cui si desidera farlo. Il blocco deve muoversi in modo coerente indipendentemente ripetizione dei tasti. Pertanto non si dovrebbe essere in movimento il blocco durante gli eventi chiave. Si dovrebbe essere solo il monitoraggio dello stato dei tasti durante gli eventi KeyDown e KeyUp, e gestire il movimento altrove. Il movimento reale dovrebbe avvenire sia in una sorta di evento timer (un controllo timer incendi eventi a intervalli regolari, anche se nulla è in corso) o si dovrebbe avere un ciclo principale costantemente la verifica dello stato di tutto e oggetti in movimento al momento opportuno. Se si utilizza la seconda opzione, è necessario guardare in "DoEvents", perché se si dispone di codice che è costantemente in esecuzione senza mai finire la funzione, il programma non elaborerà tutti gli altri eventi come keyup e gli eventi KeyDown. Così si vorrebbe chiamare DoEvents all'interno di ogni ciclo per elaborare gli eventi chiave (tra le altre cose, come spostare la finestra). Si potrebbe anche voler chiamare System.Threading.Thread.Sleep se non c'è bisogno di essere l'elaborazione di cose del tutto così costantemente. Se si utilizza un controllo del timer, non dovreste preoccuparvi di nulla di tutto ciò.

Siamo spiacenti, nessun aiuto specifico ... è sabato sera, dopo tutto ... comunque:

Ogni tasto ha bisogno di uno stato. Quando si ottiene un evento KeyDown, si può dire quale tasto è andato giù, e modifiy il keystate si gestiscono. Quando la chiave viene sostegno, ancora una volta, dal guardare l'evento, si può dire che era. modificare solo lo stato del tasto che l'evento è stato sparato contro:. vale a dire non si azzera tutti gli Stati con l'evento KeyUp, altrimenti lo stato di chiavi che si sta tenendo in memoria sarà danneggiata, come si sta vedendo

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top