Куда мне поместить VBA.DoEvents и Application.OnKey, если я использую бесконечное l oop? - PullRequest
0 голосов
/ 12 июля 2020

Я создаю игру на VBA и не знаю, как заставить Application.OnKey работать. Зачем мне это нужно? Как вы можете догадаться, я хочу, чтобы объект в моей игре двигался, когда игрок нажимает клавишу.

Игра - это, по сути, бесконечный l oop в подпрограмме RunGame (). Если я помещаю Application.OnKey в качестве события книги без запуска RunGame (), он работает, поэтому моя клавиатура и Application.OnKey могут понимать друг друга. Однако когда я запускаю RunGame (), ключевой прослушиватель не работает. Я уверен, что это потому, что я положил его не туда. Дело в том, что я пробовал это везде, и у меня закончились идеи! Не могли бы вы мне с этим помочь? Я вставляю здесь воспроизводимый пример.

Модуль «main»:

Option Explicit

Sub RunGame()

Call Application.OnKey("{Down}", "OnDownKey")

  Do While True
    '
    '
    'Some game's stuff here
    '
    '

    Call WasteTime(1)
  Loop

End Sub

Модуль «utils»:

Option Explicit

Sub WasteTime(TimeToWaste As Single)

  Dim i As Long
  
  For i = 1 To TimeToWaste * 1000
    VBA.DoEvents
    i = i
  Next
End Sub

Sub OnDownKey()
  MsgBox "You pressed down key"
End Sub

Кстати, если есть что Чтобы изменить в этом небольшом примере logi c, я буду признателен за ваши советы.

Ответы [ 2 ]

1 голос
/ 17 июля 2020

Мне нужно найти решение, поэтому я отвечу на свой вопрос. Советы от @Peyter помогли мне найти путь.

Во-первых, бесконечный l oop кажется мне абсолютно нормальным, я чувствую, что он работает более стабильно, чем Application.OnTime. Еще есть метод Чипа Пирсона, но я не нашел причины использовать его вместо бесконечного l oop (результат казался таким же).

Во-вторых, я тоже оставил Aplication.OnKey. . Я использовал решение с этого сайта . Это какая-то функция Windows и работает очень хорошо. Однако есть одна проблема. С этого момента нажатая клавиша будет означать для Excel как дополнительную, так и обычную деятельность. Например, когда я нажимаю стрелку вниз, я выбираю ячейку под ячейкой, в которой я нахожусь. Я хотел отключить обычное поведение для стрелок и букв, но не нашел подходящего способа сделать это. Поэтому я использовал уродливый;) В целом мои функции такие, как показано ниже.

Основной модуль:

Option Explicit

Sub RunGame()

  Do While True
    '
    '
    'Some game's stuff here
    '
    '

    Call WasteTime(1)
    IsKeyPressed
  Loop

End Sub

Утилиты модуля:

Option Explicit

Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long

Private Const VK_DOWN = &H28 'DOWN ARROW key

Sub WasteTime(TimeToWaste As Single)

  Dim i As Long
  
  For i = 1 To TimeToWaste * 1000
    VBA.DoEvents
  Next
End Sub

Sub IsKeyPressed()

  If GetAsyncKeyState(VK_DOWN) <> 0 Then
    OnDownKey
  End If
End Sub

Sub OnDownKey()
  MsgBox "You pressed down key"
End Sub

Он работает . Пока не знаю куда поставить VBA.DoEvents, но в l oop работает хорошо. Строка Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long передает привет функции Windows, которая теперь будет работать для нас. Private Const VK_DOWN = &H28 'DOWN ARROW key - это определение клавиши со стрелкой вниз, которая теперь будет называться VK_DOWN. IsKeyPressed() проверяет, нажал ли пользователь какую-то клавишу. If GetAsyncKeyState(VK_DOWN) <> 0 проверяет, был ли нажат VK_DOWN. На удивление для меня эта Windows функция очень хорошо работает с VBA, никаких лагов нет!

Итак, что я сделал, чтобы заблокировать обычное поведение нажатия стрелок или букв? Я выбрал одну ячейку, в которой хотел бы остаться - в моем примере это AG1. Затем я использовал события рабочего листа (дважды щелкните лист слева в редакторе VBA, и вы его откроете). Я использовал там две функции:

Option Explicit

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
  [AG1].Select
End Sub

Private Sub Worksheet_Change(ByVal Target As Range)
  If Target.Address = "$AG$1" Then
    Application.EnableEvents = False
    [AG1].Clear
    [AG1].Select
    Application.EnableEvents = True
    RunGame
  End If
End Sub

Первая делает невозможным выход из AG1, потому что каждый раз, когда вы меняете выбор, VBA выбирает AG1 за вас.

Второй срабатывает. когда вы пытаетесь изменить значение ячейки. Подпрограмма очистит ячейку, выделит ее и запустит RunGame (). Зачем нужно последнее? Потому что, когда пользователь начинает что-то вводить, бесконечное l oop приостанавливается, а когда пользователь нажимает Enter, бесконечное l oop не продолжается. Я действительно не знаю, приостановлено это или закончено. Но, надеюсь, я обойдусь без буквенных клавиш, так что мне не о чем беспокоиться.

0 голосов
/ 12 июля 2020

Почему вы используете бесконечное l oop? Gah.

Вместо этого используйте модель WithEvents VBA для ожидания ввода.

И используйте Application.Ontime для вызова макроса, который снова вызывает себя через заданный интервал. Если вы используете Infinite l oop, вы никогда не уничтожите объекты в этом sub, и VBA исчерпает память, что приведет к сбою игры. Когда у вас есть даже боковой скроллер, который перемещает кадр или что-то в этом роде, и вы используете AOT, тогда вместо одного плавного большого подзаголовка вы получаете 1 кадр, затем 2 кадра, и вы можете изменить траекторию вызовов AOT с помощью использование WithEvents для изменения logi c в ваших вызовах AOT, чтобы он переходил к другому макросу AOT.

...