C # - clone Tetris - Não é possível obter bloco de responder adequadamente a seta combinações de teclas

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

  •  05-09-2019
  •  | 
  •  

Pergunta

Eu estou trabalhando em programação de um jogo Tetris no Visual C # 2005. É o programa mais extenso Eu projetei ainda.

I criar uma classe de forma e uma classe bloco para controlar a localização, movimento e visualização das diferentes peças de Tetris. Eu tenho moveDown (), moveLeft (), moveRight () funções e para cada forma (e correspondente canMoveDown (), canMoveLeft (), canMoveRight () funções booleanas que verificam que é OK para mover). Este é todos trabalhando muito bem.

Eu quero usar o para baixo, direita e teclas de seta esquerda para que o usuário mover o bloco ao redor, além de usar um temporizador para ter a forma cair automaticamente uma linha a cada tantos milissegundos.

Eu estou usando o manipulador de eventos KeyDown para verificar quando o usuário pressiona a baixo, esquerda e seta para a direita. Isso não é tão difícil. O problema é que eu quero para permitir movimento diagonal, e eu quero que ele funcione tão bem possível. Eu tentei um monte de diferentes formas de abordar este problema, com níveis variados de sucesso. Mas eu não posso obtê-lo muito bem ...

A minha abordagem mais bem sucedido era usar três variáveis ??booleanas para manter o controle de quando as teclas de seta para a direita para baixo, esquerda, e estão sendo pressionado. Gostaria de definir as booleans para true no evento KeyDown, e para false no evento KeyUp. No evento KeyDown Eu também diria o bloco como se mover, usando as variáveis ??booleanas para verificar qual a combinação estava sendo pressionado. Funcionou muito bem, exceto por uma coisa.

Se eu pressionado uma das teclas de seta e mantido, em seguida, pressionado uma segunda chave de seta e, em seguida, lançou a segunda chave, o bloco iria parar de se mover em conjunto, em vez de continuar a se mover na direção da primeira tecla de seta que hasn' t sido lançado ainda. Eu acho que isso é porque a segunda chave acionou o evento KeyDown, e aquando do seu lançamento o evento KeyUp foi demitido, eo evento KeyDown parou de atirar completamente, embora a primeira chave é acionado.

Eu não posso para a vida me de encontrar uma solução satisfatória para este problema.

Qualquer ajuda seria muito apreciada =)

Foi útil?

Solução

A maioria dos jogos não espere para eventos. Eles sondar o dispositivo de entrada quando neccessary e agir accodringly. Na verdade, se você sempre dar uma olhada em XNA, você verá que não há um método Keyboard.GetState () (ou Gamepad.GetState ()) que você vai chamar em sua rotina de atualização e atualizar sua lógica jogo baseado no os resultados. Ao trabalhar com Windows.Forms, não há nada fora da caixa para fazer isso, porém você função pode P / Invoke o GetKeyboardState () para tirar proveito disso. A coisa boa sobre isso é que você pode pesquisar várias chaves de uma vez, e, portanto, você pode reagir a mais de uma tecla de cada vez. Aqui está uma classe simples que eu encontrei on-line que ajuda com isso:

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

Para demonstrar, eu escrevi um simples janelas aplicativo que basicamente move uma bola com base na entrada do teclado. Ele usa a classe I ligado você, para consultar o estado do teclado. Você vai notar que, se você segurar duas teclas ao mesmo tempo, ele vai se mover na diagonal.

Em primeiro lugar, 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;
        }
    }

Realmente nada sofisticados ....

Então aqui está o código 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);
            }
        }
    }

pequeno aplicativo simples. Apenas cria uma bola e um temporizador. A cada 20 milissegundos, ele verifica o estado do teclado, e se uma tecla é pressionada ele se move-lo e invalida de modo que possa pintar.

Outras dicas

Se você está confiando em repetição de tecla para enviar repetidamente os principais eventos para baixo para fazer o movimento bloco, eu não acho que esta é a maneira que você quiser fazê-lo. O bloco deve mover-se de forma consistente independente de repetição de tecla. Portanto, você não deve se mover o bloco durante os eventos-chave. Você só deve estar acompanhando o estado das chaves durante o keydown e KeyUp, e movimento pega em outro lugar. O movimento real deve ter lugar em algum tipo de evento timer (a incêndios controle temporizador de eventos em intervalos regulares, mesmo que nada está acontecendo) ou você deve ter um loop principal constantemente verificando o estado de tudo e mover objetos quando apropriado. Se você usar a segunda opção, você terá que olhar para "DoEvents" porque se você tiver o código que está sendo executado constantemente, sem nunca terminar a função, o programa não irá processar quaisquer outros eventos, como keyup e eventos KeyDown. Então você gostaria de chamar DoEvents dentro de cada loop para processar os eventos-chave (entre outras coisas como mover a janela). Você também pode querer chamar System.Threading.Thread.Sleep se você não precisa estar processando as coisas tão constantemente. Se você usa um controle timer, você não deve ter que se preocupar com nada disso.

Desculpe, nenhuma ajuda específica ... É sábado à noite depois de tudo ... no entanto:

Cada tecla tem um estado. Quando você começa um evento keydown, você pode dizer qual tecla foi para baixo, e modifiy o keystate você está mantendo. Quando a chave de volta para cima, mais uma vez, de olhar para o evento, você pode dizer o que era. Apenas modificar o estado da chave que o evento foi disparado contra:. Ou seja, não redefinir todos os estados com o evento keyup, caso contrário, o estado de chaves que você está segurando na memória será corrompido, como você está vendo

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top