无论我尝试什么,我都无法在 KeyDown 上退出循环

No Matter What I Try, I Cannot Exit Loop On KeyDown

这真的让我感到沮丧。

基本上,这是我为 GTA IV 编写的脚本的一部分(完成后我可能会在我的网站上发布 mod),我需要在用户按下定义的键时退出此循环在游戏中。当然,我不能使用 KeyPreview 因为这是一个 class 库 (GTA IV 插件必须使用 .net.dll)。

这是我到目前为止尝试过的(显然不是完整的代码),正如许多网站上所建议的那样:

循环:

Sub CarFunctions()
    Dim veh = Player.Character.CurrentVehicle
    Do
        Application.DoEvents()
        If CancelLoop = True Then
            Exit Do
        End If
        Native.Function.Call("SET_VEH_INDICATORLIGHTS", veh, True)
        Native.Function.Call("FORCE_CAR_LIGHTS", veh, 1)
        Wait(500)
        Native.Function.Call("SET_VEH_INDICATORLIGHTS", veh, False)
        Native.Function.Call("FORCE_CAR_LIGHTS", veh, 2)
        Wait(500)
    Loop
End Sub

KeyDown 事件:

Private Sub VehicleControls_KeyDown(sender As Object, e As GTA.KeyEventArgs) Handles Me.KeyDown
    Try
        Dim veh = Player.Character.CurrentVehicle
        If e.Key = Keys.NumPad5 Then
            CancelLoop = True
        End If
    Catch ex As Exception
        Game.Console.Print(ex.ToString)
    End Try
End Sub

据我所知,问题是程序在循环时没有检测到任何按键事件。我尝试使用多线程,但它在游戏中抛出异常,我无法使用调用方法。

提前致谢。

一般'exit control structure'关键词,如exit do,应该避免。

https://msdn.microsoft.com/en-us/library/eked04a7.aspx

Dim index As Integer = 0
Do
    Debug.Write(index.ToString & " ")
    index += 1
Loop Until index > 10

我认为循环 until 就是您要查找的内容。您可能必须使用 while 循环而不是 do while 来支持您的特定逻辑。这是我的尝试。

Sub CarFunctionsTask()
  Task.Factory.StartNew(Sub()
    Dim veh = Player.Character.CurrentVehicle
    Do
        If CancelLoop = False Then
            Native.Function.Call("SET_VEH_INDICATORLIGHTS", veh, True)
            Native.Function.Call("FORCE_CAR_LIGHTS", veh, 1)
            Wait(500)
            Native.Function.Call("SET_VEH_INDICATORLIGHTS", veh, False)
            Native.Function.Call("FORCE_CAR_LIGHTS", veh, 2)
            Wait(500)
        End If

    Loop Until CancelLoop
  End Sub)
End Sub

我认为 codemonkeyliketab 接近答案,因为循环需要在后台线程上 运行 才能不阻止按键事件。我会推荐类似于他所做的事情,但使用调用上下文来执行 Native.Function.Call 行,如下所示:

Sub CarFunctions()
    Dim veh = Player.Character.CurrentVehicle
    Dim callingContext = System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext

    System.Threading.Tasks.Task.Factory.StartNew(
        Sub()
            While CancelLoop = False
                Dim t As Task
                t = System.Threading.Tasks.Task.Factory.StartNew(
                    Sub()
                        Native.Function.Call("SET_VEH_INDICATORLIGHTS", veh, True)
                        Native.Function.Call("FORCE_CAR_LIGHTS", veh, 1)
                    End Sub,
                    System.Threading.CancellationToken.None,
                    System.Threading.Tasks.TaskCreationOptions.None,
                    callingContext)
                t.Wait()
                Wait(500)
                t = System.Threading.Tasks.Task.Factory.StartNew(
                    Sub()
                        Native.Function.Call("SET_VEH_INDICATORLIGHTS", veh, False)
                        Native.Function.Call("FORCE_CAR_LIGHTS", veh, 2)
                    End Sub,
                    System.Threading.CancellationToken.None,
                    System.Threading.Tasks.TaskCreationOptions.None,
                    callingContext)
                t.Wait()
                Wait(500)
            End While
        End Sub)
End Sub

在 Mike Guthrie 和 codemonkeyliketab 的帮助下,我通过在循环中使用 isKeyPressed() 设法解决了这个问题(但是不是很完美):

Do
    If Me.isKeyPressed(Keys.Decimal) Then
        Exit Do
    End If
    Native.Function.Call("SET_VEH_INDICATORLIGHTS", veh, True)
    Native.Function.Call("FORCE_CAR_LIGHTS", veh, 1)
    Wait(500)
    Native.Function.Call("SET_VEH_INDICATORLIGHTS", veh, False)
    Native.Function.Call("FORCE_CAR_LIGHTS", veh, 2)
    Wait(500)
Loop

谢谢大家的回答。