3

我正在使用一个 do while 循环,在每个循环之后,需要询问用户他/她接下来想做什么。该程序是一个口袋妖怪风格的战斗RPG,除了让口袋妖怪战斗,你自己和怪物战斗。

我正在寻找的是一种口袋妖怪/最终幻想风格的战斗。

这是我处理战斗的潜艇:

  Private Sub Battle(ByVal Name As String, ByVal life As Integer, ByVal damage As Integer)

    lbl_MonsterName.Text = Name
    mobCurrHealth = mobMaxHealth
    lbl_MonsterHP.Text = mobCurrHealth & " / " & mobMaxHealth
    bar_monsterHP.Value = mobCurrHealth
    bar_monsterHP.Maximum = mobMaxHealth


    txtbx_Action.AppendText(Environment.NewLine & "You find a " & Name)
    lbl_MonsterName.Visible = True
    lbl_MonsterHP.Visible = True
    bar_monsterHP.Visible = True

    wait(2000)

    txtbx_Action.AppendText(Environment.NewLine & "What would you like to do?")
    btn_Attack.Visible = True
    btn_Run.Visible = True

'Here I want the program to wait for button input, but i still have to find a way to do that        


End Sub

在“您想做什么”行之后,将显示 2 (3) 个按钮:

  • 攻击
  • (存货)

但是,正如您所看到的(我确实知道出了什么问题,我只是不知道如何解决它)我的战斗潜艇在按钮可见后结束。

我希望潜艇等到我按下了这些按钮中的任何一个,然后再采取相应的行动。

我怎样才能做到这一点?


编辑:

这是我使用的代码,其中子“Battle”被称为:

   Private Sub btn_Adventure_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Adventure.Click

    btn_Adventure.Visible = False

    txtbx_Action.Visible = True

    Dim rng As New Random
    Dim MobGen As Integer = rng.Next(1, 4)

    Select Case MobGen

        Case 1
            mobName = "Rat"
            mobMaxHealth = 5
            mobDamage = 2
        Case 2
            mobName = "Bat"
            mobMaxHealth = 7
            mobDamage = 3
        Case 3
            mobName = "Snake"
            mobMaxHealth = 9
            mobDamage = 4
        Case 4
            mobName = "Wolf"
            mobMaxHealth = 11
            mobDamage = 5

    End Select

    txtbx_Action.AppendText(Environment.NewLine & "You run around the forest trying to find something to kill")
    wait(1500)
    Battle(mobName, mobMaxHealth, mobDamage)


    btn_Adventure.Visible = True

End Sub

另外,如果您认为我现在处理程序的方式非常错误,请告诉我如何改进它!这是一个尝试更好地学习 vb.net 语言的爱好项目,因此非常感谢任何建议或建设性批评!

4

6 回答 6

3

当您单击您正在寻找的按钮时,会发生按钮事件。实际上,我已经开始为自己的成长重写口袋妖怪,并且我使用了 button_click 事件。

Button_Click 子程序是在单击按钮时运行的子程序。当程序悬停,等待输入并单击按钮时,程序将跳转到与单击的按钮关联的子程序。

为了生成“button_click”子,您可以在查看表单时双击一个按钮以自动生成“Button_Click”子。还有其他方法,但我个人发现它们使用起来更棘手,更容易出错。

这里将发生的是你的“战斗”潜艇将结束,战斗将被建立。该程序将悬停,等待发生某些事情。当您单击其中一个按钮时,您的程序将跳转到单击的任何按钮的子程序中。它不需要程序在按下按钮之前一直保持循环,并且您不必向现有的战斗潜艇添加任何代码(您可以让它结束)。

这里有一个小例子可以帮助您入门。填充 button_click 子程序的代码是您可以根据自己的目的进行调整的代码,但您的设置应该与此非常相似。它是用 Visual Basic 2010 Express 编写的。

Private Sub btn_Attack_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Attack.Click
    If mobCurrHealth > 0 And youCurrHealth > 0 Then
        'Both the monster and you are alive
        'Execute attack code
    End If
End Sub

Private Sub btn_Run_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Run.Click
    If mobCurrHealth > 0 And youCurrHealth > 0 Then
        'Both the monster and you are alive
        'Execute run code
    End If
End Sub

重要的行是您可能会自动生成的子声明行。如果您没有通过双击按钮自动生成您的子程序,请确保将“句柄”设置为您要激活子程序的按钮,然后单击“.Click”。一旦按下按钮,您想要发生的任何事情都取决于您。上面的示例检查以确保您和怪物都还活着,但它可以是任何东西。

祝您游戏开发顺利!

于 2013-05-24T15:10:22.693 回答
3

更新:使用await

我正在参加一个 TechEd 研讨会,该研讨会提出了一种可能更好的处理这种情况的方法:async. 在此处查看演示:第 9 频道:提示 3:将事件包装在返回任务的 API 中并等待它们

基本上,您可以将事件包装在 an 中await,然后让您的 UI 线程等待事件完成(不会导致它挂起)。这是无需跟踪复杂状态的故事板的好方法。它不会在所有情况下都有效,但如果它可能是传统的基于状态的解决方案的更简单的替代方案。


基于状态的方法

您对此的思考方式非常直观,但并不完全正确。一般来说,编程可以被认为是在一系列不同状态之间的转换。这本质上就是您在显示和隐藏各种控件时所做的事情——隐式地从一种状态转换到另一种状态。

我认为将隐式状态更改显式化对您最有帮助。您可以定义游戏可以“休息”的不同状态,然后根据用户输入从一种状态转换到另一种状态。

因此,例如,您可以定义一个名为 CurrentState 的类级别属性和一个包含您可能希望表示的各种状态的 Enumeration。游戏状态改变时触发的事件可能会导致打开和关闭控件。

使用 CurrentState 属性,您可以检测游戏所处的状态并对相同的输入做出不同的响应(如果这是一个问题)。显然我无法访问您的整个代码,但希望您能在下面找到鼓舞人心的代码。意识到您将需要使用您的创造性判断:您不一定要复制粘贴下面的代码并让您的游戏适合它。

你的游戏状态的枚举。在程序集级别(在任何类之外)定义它:

Public Enum GameState
    Adventure
    Battle
    BattleCompleted
End Enum

您的游戏类(具有 CurrentState 属性、StateChanged 事件和存根方法,显示了使用状态的一种可能方式):

Public Class Game

    ' This defines your custom event which will be
    ' raised when the CurrentState property changes
    Public Event GameStateChanged As EventHandler(Of GameStateChangedEventArgs)

    Private _currentState As GameState = GameState.Adventure
    Private Property CurrentState() As GameState
        Get
            Return _currentState
        End Get
        Set(ByVal value As GameState)
            ' Don't raise GameStateChanged if setting the GameState to the same value
            If value = _currentState Then Exit Property

            ' If your application code changes the CurrentState
            ' property, raise the GameStateChanged event
            Dim l_oldState = _currentState
            _currentState = value
            RaiseEvent GameStateChanged(Me, New GameStateChangedEventArgs(l_oldState, _currentState))
        End Set
    End Property

    ' This is the event handler for your GameStateChanged event.
    ' When the game state changes, you can respond by updating the UI.
    Public Sub Game_GameStateChanged(ByVal sender As Object, ByVal e As GameStateChangedEventArgs) Handles Me.GameStateChanged
        If e.ToState = GameState.Adventure Then
            ' Turn on adventure mode buttons, etc
        ElseIf e.FromState = GameState.Adventure Then
            ' Turn off adventure mode buttons, etc
        End If
        If e.ToState = GameState.Battle Then
            ' Turn on battle monster controls, etc
        ElseIf e.FromState = GameState.Battle Then
            ' Turn off monster controls, etc
        End If
        If e.ToState = GameState.BattleCompleted Then
            ' Turn on post-battle controls, etc
        ElseIf e.FromState = GameState.BattleCompleted Then
            ' Turn off post-battle controls, etc
        End If
    End Sub

    ' Now, all you need to do is set the state of your game...
    Private Sub Adventure()
        Me.CurrentState = GameState.Adventure

        Battle()
    End Sub

    Private Sub Battle()
        Me.CurrentState = GameState.Battle

        ' Do the battle

        PostBattle()
    End Sub

    Private Sub PostBattle()
        Me.CurrentState = GameState.BattleCompleted
    End Sub

End Class

StateChanged EventArgs 类。这完全是可选的,但在处理状态转换时为您提供了一点额外的灵活性。

Public Class GameStateChangedEventArgs
    Inherits EventArgs

    Private _fromState As GameState
    Public ReadOnly Property FromState() As GameState
        Get
            Return _fromState
        End Get
    End Property

    Private _toState As GameState
    Public ReadOnly Property ToState() As GameState
        Get
            Return _toState
        End Get
    End Property

    Public Sub New(ByVal fromState As GameState, ByVal toState As GameState)
        _fromState = fromState
        _toState = toState
    End Sub

End Class

这绝不是进行程序设计的唯一方法,但在基于事件的环境中工作时,它是一种有用的架构。

于 2013-05-24T14:23:47.910 回答
2

您似乎对 Windows GUI 程序的实际工作方式缺乏基本的了解。当您包含如下代码行时:

button.Visible = true;

您实际上并没有使按钮立即可见。将会发生的事情是,Windows 窗体框架将设置一个标志,指示按钮应该是可见的,然后它将向按钮的消息队列发送一条消息,要求重新绘制按钮。如果代码是从 UI 线程运行的(此处似乎就是这种情况),则必须将控制权交还给操作系统,然后才能接收和处理此类消息。

您的方法的主要缺陷是您试图在单个函数中完成所有操作。实际上,您可能需要以不同的方式构建整个程序。Cyborgx37在他的回答中有正确的基本思想,但也许你不太明白。

基本思想是,您的程序需要有各种函数来负责处理整个游戏逻辑的某些单独块,以及定义游戏在任何给定时间的当前状态的变量和数据结构的集合。并且这些函数需要适当考虑 GUI 事件处理的工作方式。

让我们来看一个概述。这并不准确,但总的来说应该非常接近您想要做的事情。

当您开始游戏时,您将以适合游戏开始的任何方式初始化变量和数据结构(以下称为“游戏状态”)。这可能包括从磁盘、数据库加载信息或通过代码动态创建信息。这将包括图形数据、声音数据等。

接下来,您将创建游戏的显示。请注意,创建显示和重绘显示是两件不同的事情。

“创建显示”是指打开窗口(如果需要)、添加任何 UI 控件(如您需要的按钮)等。

相比之下,“重绘显示”是实际推动像素的过程。如果您的程序使用图形库调用来更新屏幕上的任何内容,您的代码将需要实现一个函数,当您的程序收到“PAINT”消息时将调用该函数。

请注意,如果您将游戏严格用作 Windows GUI 应用程序,则可能不需要显式创建“重绘屏幕”功能。通常,如果所有内容都由 UI 控件绘制,则不需要一个。

此时,您可能想要强制执行屏幕重绘事件。在 Windows GUI 应用程序中,这通常通过向窗口发送“无效”消息来完成。处理完后,所有内容都将根据需要重新绘制。诸如标准 UI 控件之类的东西通常会在您的程序没有任何特殊操作的情况下重新绘制自己。

现在可能是您需要某种用户输入才能继续的时候,即使它只是单击“开始游戏”。如果您选择了按钮,则每个按钮都应链接到一个事件处理函数,该函数包含特定于该特定选择的代码。该代码应根据需要更新游戏的变量和数据结构。

Cyborgx37展示了一个示例,其中单个变量指定了游戏状态。在实践中,该变量只是冰山一角,还会涉及各种其他变量和数据结构。基本的游戏状态可以指示各种各样的事情......比如“等待开始”或“用户按下攻击”。其他变量可能表明正在攻击哪个怪物、玩家和怪物所在的区域、玩家当前的健康状况等。

用来描述“游戏状态”的所有基本信息可能都希望作为一个类组织在一起。这不应包括有关应单独维护的 UI 设置之类的细节。

如果用户输入意味着需要在屏幕上更改某些内容,则事件处理程序应更新游戏状态并删除/添加/更改涉及的任何 UI 控件。您可能需要一个能够配置任何特定游戏状态所需的屏幕控制的功能。确保这些按钮始终配置有正确的事件处理函数。

如果用户输入意味着屏幕上动态绘制的内容需要更新,它应该向窗口(或特定控件)发送“无效”消息。

您的代码示例在几个地方调用了wait() 。这通常是暂停的错误方法,因为您正在暂停整个线程。但是,这并不意味着基本的基本思想是错误的。想要在两件事发生之间有一定的时间是完全可以的。这就是计时器进来的地方。

计时器允许您根据需要更新游戏状态以响应时间流逝。这可能出于多种原因需要,但对于游戏来说,最常见的是动画。例如,也许您有一张需要每秒进行 5 次动画处理的图像。您将创建一个设置为以 5hz 计时的计时器,并在事件处理程序函数中,它会增加动画帧索引,然后将“无效”消息发送到窗口或带有图像的控件。

您可以将多个计时器设置为不同的频率,但最好尽可能组合。如果屏幕上有十几个不同的东西需要动画,请使用单个计时器,其中处理函数分别检查每个项目。

涉及的内容更多,但这些是我认为您需要走上正确轨道的基础知识。

于 2013-05-25T01:00:33.727 回答
2

你必须稍微改变你的方法。您应该考虑每个按钮都在做特定的工作,并且不应该等待任何人。

在您的 btn_Adventure.Click 上应该初始化战斗,而不是照顾它。

Private Sub btn_Adventure_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Adventure.Click

    GetModToAttack()
    InitialiseUI() ' Show the btnAttack, btnRun, show the "What would you like to do text", ...

End Sub

现在 UI 会自己等待用户输入,当用户点击按钮时,你处理动作

Private Sub btn_Attack_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Attack.Click

    ' Handle attack action
    ' Handle UI

End Sub
于 2013-05-24T17:10:09.517 回答
2

在这个完整的示例中,我避免了绑定和其他内容,以使其更易于理解并尽可能简短。

Public AllMobs As New List(Of Mob)
Public Player As Mob
Public CurrentMob As Mob
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    AllMobs.Add(New Mob() With {.Name = "rat", .Damage = 2, .MaxHealth = 5, .CurrentHealth = 5})
    AllMobs.Add(New Mob() With {.Name = "bat", .Damage = 3, .MaxHealth = 7, .CurrentHealth = 7})
    AllMobs.Add(New Mob() With {.Name = "wolf", .Damage = 5, .MaxHealth = 11, .CurrentHealth = 11})
    Player = New Mob() With {.Name = "Gutanoth", .Damage = 1, .MaxHealth = 100, .CurrentHealth = 100}
    GetNextFight()
End Sub
Private Sub GetNextFight()
    If CurrentMob Is Nothing Then
        CurrentMob = SpawnMob()
        txtbx_Action.AppendText("You continue into the forest" & vbNewLine)
        txtbx_Action.AppendText(CurrentMob.Name + " is here" & vbNewLine)
    End If
End Sub
Private Sub NextBattleAction()
   If Player.IsDead Then
        MessageBox.Show("You died. Lose!")
        Close()
    ElseIf CurrentMob.IsDead Then
        txtbx_Action.AppendText("---Victorious!" & vbNewLine)
        AllMobs.Remove(CurrentMob)
        CurrentMob = Nothing ' destroy mob object
    End If
    If AllMobs.Count = 0 Then
        MessageBox.Show("You've killed them all. Win!")
        Close()
    Else
        GetNextFight()
    End If
    If CurrentMob IsNot Nothing Then
        lbl_MonsterHP.Text = CurrentMob.CurrentHealth & " / " & CurrentMob.MaxHealth
    End If
End Sub
Private Function SpawnMob() As Mob
    If AllMobs.Count = 0 Then Return Nothing
    Dim rng As New Random
    Dim selectedIndex As Integer = rng.Next(AllMobs.Count)
    Return AllMobs(selectedIndex)
End Function
Private Sub btn_Attack_Click(sender As Object, e As EventArgs) Handles btn_Attack.Click
    CurrentMob.Attack(Player)
    txtbx_Action.AppendText(CurrentMob.Name & " attacks" & vbNewLine)
    Player.Attack(CurrentMob)
    txtbx_Action.AppendText("you attack " & vbNewLine)
    NextBattleAction()
End Sub
Private Sub btn_Run_Click(sender As Object, e As EventArgs) Handles btn_Run.Click
    txtbx_Action.AppendText("you run away" & vbNewLine)
    CurrentMob = Nothing ' make no current mob 
    GetNextFight()
End Sub

这应该放在一个单独的类文件中Mob.vb

Public Class Mob
    Property Name() As String
    Property MaxHealth() As Integer
    Property CurrentHealth() As Integer
    Property Damage As Integer
    Public Sub Attack(targetMob As Mob)
        targetMob.CurrentHealth = targetMob.CurrentHealth - Damage
    End Sub
    Public Function IsDead() As Boolean
        If CurrentHealth <= 0 Then Return True Else Return False
    End Function
End Class
于 2013-05-24T16:35:18.137 回答
0

品牌VarAVarB班级级别:

Private VarA, VarB As Integer ' Or whatever type

然后为循环的开始逻辑添加一个函数:

Private Sub NextIteration() ' Give this a better name, or even make it part of Update
    If VarA <= 0 OrElse VarB <= 0 Then
        ' Loop’s over; hide the buttons or something
    Else
        Update()
    End If
End Sub

然后分别处理所有动作按钮,执行动作,并NextIteration()在每个处理程序结束时调用Click。打电话NextIteration()开始。

于 2013-05-22T13:07:58.860 回答