Как назначить клавишу со стрелкой для кнопки в C#?

Прямо сейчас я делаю игру, и в ней есть персонаж для перемещения игрока. Я просто новичок в программировании.

Есть 8 кнопок, и каждая кнопка идет в направлении. Например, это моя программа для

private void btnUp_Click(object sender, EventArgs e)
{
    //move up
    y = y - 1;
    MovePlayer();
    UpdateLabelLocation();
}
public void MovePlayer()
{
    picPlayer.Location = new Point(x, y);
}

public void UpdateLabelLocation()
{
    lblLocation.Text = "Location: (" + x + ", " + y + ")";
}

Я хочу заставить его двигаться, когда я нажимаю клавиши вверх, вниз, влево или вправо. Также, если возможно, я хочу сделать так, чтобы при нажатии одновременно вправо и вверх это вызывало:

private void btnRightUp_Click(object sender, EventArgs e)
{
    //move player
    y = y - 1;
    x = x + 1;
    MovePlayer();
    UpdateLabelLocation();
}

Я ценю помощь.

1 ответ

Решение

По сути, вы создаете свой собственный простой игровой движок. Как отметили комментаторы, вам лучше использовать существующий игровой движок, такой как Unity. Это гораздо проще и более раскрепощенно, чем работать с WinForms.

Тем не менее, если вы действительно хотите продолжить это, я настоятельно рекомендую вам переместить код в Click обработчики метода. Это уменьшает дублирование кода. т.е.

private void DownButton_Click(...)
{
    MovePlayer(0, 1);
}

private void UpButton_Click(...)
{
    MovePlayer(0, -1);
}

public void MovePlayer(float xStep, float yStep)
{
    x += xStep;
    y += yStep;
    MovePlayer();
    UpdateLabelLocation();
}

Чтобы двигаться вниз и влево, вы бы позвонили MovePlayer(-1, 1);

Чтобы двигаться вверх и вправо, вы бы позвонили MovePlayer(1, -1);

Далее вам нужно будет ответить на события KeyPress. т.е.

public void Form_KeyPress(object sender, KeyPressEventArgs args)
{
    switch (args.KeyChar) {
        case 'a': // Left
            args.Handled = true;
            MovePlayer(-1, 0);
            break;
        case 'd': // Right
            args.Handled = true;
            MovePlayer(1, 0);
            break;
        case 'w': // Up
            args.Handled = true;
            MovePlayer(0, -1);
            break;
        case 's': // Down
            args.Handled = true;
            MovePlayer(0, 1);
            break;
    }
}

Обратите внимание, что если у вас есть другой элемент управления, который принимает ввод с клавиатуры (например, TextBox), он будет перехватывать нажатие клавиши. Чтобы обойти это, используйте KeyPreview, чтобы заставить окно предварительно просмотреть ввод. args.Handled = true предотвращает маршрутизацию события на дочерние элементы управления после вашего кода.

К сожалению, WinForms не записывает несколько нажатых клавиш одновременно, поэтому одного нажатия клавиши недостаточно для управления поворотом. Вы можете обойти это, подключив KeyDown и KeyUp, но это больше проблем, чем стоит.

Вот более надежное решение. Имейте в виду, что следующее не является потокобезопасным, поэтому, если вы планируете вводить другие потоки, вам нужно использовать соответствующую блокировку.

HashSet<KeyCode> state = new HashSet<KeyCode>();
float speed = 120; // 120 pixels/second.

private void Form_KeyDown(object sender, KeyEventArgs args)
{
    var key = args.KeyCode;
    state.Add(key);

    // Fire pressed when a key was up.
    if (!state.Contains(key)) {
        state.Add(key);
        OnKeyPressed(key);
    }
}

private void Form_KeyUp(object sender, KeyEventArgs args)
{
    var key = args.KeyCode;
    state.Remove(key);

    // Fire release when a key was down.
    if (state.Contains(key)) {
        state.Remove(key);
        OnKeyReleased(key);
    }
}

// Runs when key was up, but pressed just now.
private void OnKeyPressed(KeyCode key)
{
    // Trigger key-based actions.
}

// Runs when key was down, but released just now.
private void OnReleased(KeyCode key)
{
    // Trigger key-based actions, but on release instead of press.
}

private bool IsDown(KeyCode key)
{
    return state.Contains(key);
}

// Trigger this periodically, at least 20 times a second(ideally 60).
// An option to get you started is to use a windows timer, but 
// eventually you'll want to use high precision timing instead.
private void Update()
{
    var deltaTime = // Calculate the seconds that have passed since the last update. 
    // Describing it is out of the scope of this answer, but see the links below.

    // Determine horizontal direction. Holding both 
    // A & D down cancels movement on the x-axis.
    var directionX = 0;
    if (IsDown(KeyCode.A)) {
        directionX--;
    }
    if (IsDown(KeyCode.D)) {
        directionX++;
    }

    // Determine vertical direction. Holding both 
    // W & S down cancels movement on the y-axis.
    var directionY = 0;
    if (IsDown(KeyCode.W)) {
        directionY--;
    }
    if (IsDown(KeyCode.S)) {
        directionY++;
    }

    // directionX & directionY should be normalized, but 
    // I leave that as an exercise for the reader.
    var movement = speed * deltaTime;
    var offsetX = directionX * movement;
    var offsetY = directionY * movement;
    MovePlayer(offsetX, offsetY);
}

Как вы можете видеть, в этом есть немало проблем. Если вы хотите более детальный выбор времени, загляните в эту статью. В конце концов вам захочется перейти к игровому циклу, но это еще одна тема, выходящая за рамки этого ответа.

Другие вопросы по тегам