0

Всем привет! Создаю игру, в ней (как не удивительно) можно нажимать на клавиши и управлять персонажем. Как я могу при нажатии на определенные клавиши заблокировать нажатие других на некоторое время? Хотела применить многопоточность, однако не совсем понимаю, как это сделать. Есть пример такого кода, где я пытаюсь создать поток при нажатии на клавишу:

protected override void OnKeyDown(KeyEventArgs e)
    {
        Thread t = new Thread(() => PlayerControl.KeyDown(player, e));
        t.Start();
    }

Во-первых, этот код не работает, как должен, т.к. все нажатия воспринимаются абсолютно так же и Thread.Sleep(), который я вызываю в методе PlayerControl.KeyDown(), не заставляет поток "уснуть", тем самым заблокировав клавиатуру. Во-вторых, я понимаю, что это неправильно, ведь новый поток создается каждый раз, когда происходит обработка нажатия на клавишу, что очень даже плохо. Как все-таки реализовать это? Если есть способы и без использования классов Threading и Threading.Tasks, буду только рада выслушать =)

Реализация персонажа:

public class Player : RigidBody
{
    public EntityState State { get; set; } = EntityState.Idle;
    public int Damage { get; set; }
    public int HealthPoint { get; set; }
    public float Width { get; set; }
    public float Height { get; set; }
    public float Speed { get; set; } = 20f;
    public RectangleF Hitbox { get; set; }
    public float JumpForce { get; set; } = 50;
    public float RollForce { get; set; } = 60;
    public Direction Direction { get; set; } = Direction.Left;
public Player(PointF startPoint, SizeF size) : base()
{
    Width = size.Width;
    Height = size.Height;
    Hitbox = new RectangleF(Position, new SizeF(Width, Height));
    Position = startPoint;
}

public Player(PointF startPoint, SizeF size, int hp, int dmg) : base()
{
    Width = size.Width;
    Height = size.Height;
    Hitbox = new RectangleF(Position, new SizeF(Width, Height));
    Position = startPoint;
    HealthPoint = hp;
    Damage = dmg;
}

public void Run()
{
    Force = Direction == Direction.Right ? new Vector2(Speed, 0) : new Vector2(-Speed, 0);
}

public void StopRun()
{
    Force = new Vector2(0, 0);
}

public void Jump()
{
    if (Collisions.HasCollision(Hitbox).OnGround)
        Velocity = new Vector2(Velocity.X, Velocity.Y - JumpForce);
}

public void Roll()
{
    if (Collisions.HasCollision(Hitbox).OnGround)
        Velocity = Direction == Direction.Right
            ? new Vector2(RollForce, Velocity.Y)
            : new Vector2(-RollForce, Velocity.Y);
}

public void Hook()
{
    Velocity = Vector2.Zero;
    Force = Vector2.Zero;
    Velocity = new Vector2(-(Position.X - World.Monster.Position.X) * 0.7f, -(Position.Y - World.Monster.Position.Y) * 0.7f);
}

public override void Update(float dt)
{
    Hitbox = new RectangleF(new PointF(Position.X, Position.Y),
                                   new SizeF(Width, Height));
    var collision = Collisions.HasCollision(Hitbox);
    if (collision.OnGround)
        OnGroundCollision(collision.Platform.Position.Y - Height);
    CollisionWithMonster();
    base.Update(dt);
}

}

Реализация его контроллера:

public static class PlayerControl
{
    private static bool IsHooking = false;
    private static bool IsJumping = false;
    private static bool IgnoreFloor;
public static void KeyDown(Player player, KeyEventArgs e)
{
    if (e.KeyCode == Keys.D)
    {
        player.Direction = Direction.Right;
        player.State = EntityState.Move;
        player.Run();
    }
    if (e.KeyCode == Keys.A)
    {
        player.Direction = Direction.Left;
        player.State = EntityState.Move;
        player.Run();
    }
    if (e.KeyCode == Keys.Space && !IsJumping)
    {
        IsJumping = true;
        player.State = EntityState.Up;
        player.Jump();
    }
    if (e.KeyCode == Keys.Z)
    {
        player.State = EntityState.Roll;
        player.Roll();
    }
    if (e.KeyCode == Keys.R)
    {
        player.Hook();
    }
}

public static void KeyUp(Player player, KeyEventArgs e)
{
    if (e.KeyCode == Keys.D || e.KeyCode == Keys.A)
    {
        player.StopRun();
    }
    if (e.KeyCode == Keys.Space)
    {
        IsJumping = false;
    }
}

}

alecs
  • 1
  • мб блокировать не нажатие кнопок, а именно движение игрока? как у тебя реализован персонаж и его управление? отредактируй вопрос и добавь эту инфу в него – Pekor May 07 '22 at 16:59
  • 1
    Я не нашел ни одной причины здесь для многопоточки. А что, без использования многопоточки не работает как надо? Можно больше сказать, глядя на этот код, здесь вообще нельзя применять многопоточность, так как можно вообще сломать логику на гонках потоков. Добавьте переменную, значение которой проверйте на входе в методы обработки клавишь, например bool hold и проверяйте if (hold) return;. И этот код до боли мне Unity напоминает. Вот пример, как я делал управление. – aepot May 07 '22 at 17:58
  • Но я так и не понял, какое поведение требуется. – aepot May 07 '22 at 18:02
  • Требуется, чтобы блокировалось нажатие клавиш во время прыжка и переката – alecs May 07 '22 at 18:04
  • Ну, ничего сложного, один if и одна bool вас спасут. И не лезьте в многопоточку, с ней много проблем будет, например с UI компонентами можно работать только из основного потока. Если уж вам надо несколько дел одновременно делать, есть таймеры и асинхронность. https://ru.stackoverflow.com/a/1257322/373567, плюс изучите шаблон многопоточного проектирования Producer/Consumer, это даст больше свободы в понимании, как перекачивать данные и события из потока в поток и обратно. – aepot May 07 '22 at 18:08
  • @aepot Хорошо, это поняла. А как тогда можно сделать, чтобы, например, в течение какого-то времени игнорировалась коллизия при перекате? То есть когда движение начинается, то переменную IsRolling сделать true, когда закончится, то сделать false – alecs May 07 '22 at 18:16
  • Ну перед и после вызова меняйте переменную, не вижу проблемы. – aepot May 07 '22 at 19:08

0 Answers0