微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

我如何为我的回合制系统增加速度,以便如果怪物速度更快,它会先攻击

如何解决我如何为我的回合制系统增加速度,以便如果怪物速度更快,它会先攻击

我做了一个函数来比较玩家和敌人两个怪物的速度,但我不知道如何让他们进行攻击然后回到玩家可以再次按下按钮的状态,此时此刻如果我将按钮更改为 check_speed,则玩家攻击然后敌人攻击而速度不是一个因素,这将成为一个无限循环

这是代码

func check_speed(): # Gonna make it so speed becomes a factor
    if P_team.monster_invetory[0].speed > enemy_scene.speed:
        player_attack()
    else:
        enemy_attack()
        
func player_attack(): # Attacks the enemy
    print("Player attacking")
    enemy_scene.take_damage(P_team.monster_invetory[0].attack) # Calls the function to take damage
    yield(get_tree().create_timer(2),"timeout") #Wait for 2 seconds
    Enemy_battle_scene(enemy_scene) #This updates the display values
    
    var is_dead = enemy_scene.take_damage(P_team.monster_invetory[0].attack) # Checks if dead
    if is_dead:
        current_state = GameState.WON
    elif !is_dead:
        current_state = GameState.ENEMYTURN
        enemy_attack() # Here is where the enemy decides what to do,for Now it only attacks
        
func enemy_attack(): # Attacks the player
    print("Enemy attacking")
    P_team.monster_invetory[0].take_damage(enemy_scene.attack) # Calls the function to take damage
    yield(get_tree().create_timer(2),"timeout") #Wait for 2 seconds
    Player_battle_scene(P_team.monster_invetory[0]) #This updates the display values
    
    var is_dead = P_team.monster_invetory[0].take_damage(enemy_scene.attack) # Checks if dead
    if is_dead:
        current_state = GameState.LOST
    elif !is_dead:
        current_state = GameState.PLAYERTURN
        #player_attack()

func _on_Attack_button_pressed():
    if current_state != GameState.PLAYERTURN:
        return
    player_attack()
    #check_speed()

解决方法


新答案

任何其他名称的状态机。

您有状态:

enum GameState {START,CHOICE,PLAYERTURN,ENEMYTURN,WON,LOST,CAPTURE}
var current_state = GameState.START

我们可以通过将 current_state 变成一个属性并添加一个 "state_changed" 信号来使其更有用:

signal state_changed()
enum GameState {START,CAPTURE}
var current_state setget set_current_state
func set_current_state(new_value):
    current_state = new_value
    emit_signal("state_changed")

现在,每次我们设置 self.current_state 时,它都会触发信号。我们当然可以连接到它。

所以我们可以这样做:

func _ready():
    self.connect("state_changed",self,"_on_state_changed")
    self.current_state = GameState.START

func _on_state_changed():
    match current_state:
        GameState.START:
            _start()
        GameState.CHOICE:
            _choice()
        GameState.PLAYERTURN:
            _player_turn()
        GameState.ENEMYTURN:
            _enemy_turn()
        GameState.LOST:
            _lost()
        GameState.CAPTURE:
            _capture()

为什么不将 _on_state_changed 直接放在 set_current_state 中?因为信号是异步的。 此外,您不必在此脚本中处理所有这些。您甚至可以为每个状态编写一个脚本。

好的,让我们开始制作这些函数:

开始:

func _start():
    # initalization stuff
    self.current_state = GameState.CHOICE

选择:

func _choice():
    # We have to wait for the button press,we do nothing here
    pass
    
func _on_Attack_button_pressed():
    if current_state != GameState.CHOICE:
        return

    if _player_speed() > _enemy_speed(): # Checks which monster is faster
        self.current_state = GameState.PLAYERTURN
    else:
        self.current_state = GameState.ENEMYTURN

_player_speed_enemy_speed 的一些定义。

PLAYER_TURN 和 ENEMY_TURN:

var enemy_attk = false;
var player_attk = false;

func _player_turn():
    yield(_player_attack(),"completed")
    if _enemy_is_dead():
        self.current_state = GameState.WON
    else:
        if enemy_attk:
            player_attk = false
            enemy_attk = false
            self.current_state = GameState.CHOICE
        else:
            player_attk = true
            self.current_state = GameState.ENEMYTURN

func _enemy_turn():
    yield(_enemy_attack(),"completed")
    if _player_is_dead():
        self.current_state = GameState.LOST
    else:
        if player_attk:
            player_attk = false
            enemy_attk = false
            self.current_state = GameState.CHOICE
        else:
            enemy_attk = true
            self.current_state = GameState.PLAYERTURN

有一些_player_is_dead_enemy_is_dead_player_attack_enemy_attack的定义。

获胜、失败和捕获:

func _lost():
    # whatever happens
    pass
    
func _won():
    # whatever happens
    pass
    
func _capture():
    # whatever happens
    pass

我不知道。


现在,让我们将 PLAYERTURN、ENEMYTURN 合并到一个新的 BATTLE 状态。更新枚举:

enum GameState {START,BATTLE,CAPTURE}

更新匹配语句:

func _on_state_changed():
    match current_state:
        GameState.START:
            _start()
        GameState.CHOICE:
            _choice()
        GameState.BATTLE:
            _battle()
        GameState.LOST:
            _lost()
        GameState.CAPTURE:
            _capture()

按下按钮时,我们只需更改为 BATTLE:

func _on_Attack_button_pressed():
    if current_state != GameState.CHOICE:
        return

    self.current_state = GameState.BATTLE

现在是主要行为:

func _battle():
    if _player_speed() > _enemy_speed(): # Checks which monster is faster
        yield(_player_attack(),"completed")
        if _enemy_is_dead():
            self.current_state = GameState.WON
        else:
            yield(_enemy_attack(),"completed")
            if _player_is_dead():
                self.current_state = GameState.LOST
            else:
                self.current_state = GameState.CHOICE
    else:
        yield(_enemy_attack(),"completed")
        if _player_is_dead():
            self.current_state = GameState.LOST
        else:
            yield(_player_attack(),"completed")
            if _enemy_is_dead():
                self.current_state = GameState.WON
            else:
                self.current_state = GameState.CHOICE

让我们谈谈如何使用对象。首先,参考它们。您已经拥有 enemy_scene,让我们使用它。我们需要添加一个计数器部分 player_scene

onready var player_scene = P_team.monster_invetory[0]

顺便说一句,如果我是你,我会向 EnemyPosPlayerPos 添加一个脚本,以便我可以这样做:

$EnemyPos.update_view(enemy_scene)
$PlayerPos.update_view(player_scene)

现在,START 看起来像这样:

func _start():
    _update_view()
    print("Battle started")
    yield(get_tree().create_timer(0.2),"timeout")
    self.current_state = GameState.CHOICE

func _update_view():
    $PlayerPos.update(player_scene)
    $EnemyPos.update(enemy_scene)

建议:为这些场景添加“health_changed”信号,并将其分别连接到 $PlayerPos$EnemyPos,以便它们可以自动更新。 事实上,我们可以使用该信号来处理怪物死亡的时间。

攻击是一个问题,因为这些物体不知道它们的目标。以后会担心的。

现在更新 BATTLE 以使用 player_sceneenemy_scene

func _battle():
    if player_scene.speed > enemy_scene.speed:
        yield(attack(player_scene,enemy_scene),"completed")
        if enemy_scene.current_health == 0:
            self.current_state = GameState.WON
        else:
            yield(attack(enemy_scene,player_scene),"completed")
            if player_scene.current_health == 0:
                self.current_state = GameState.LOST
            else:
                self.current_state = GameState.CHOICE
    else:
        yield(attack(enemy_scene,"completed")
        if player_scene.current_health == 0:
            self.current_state = GameState.LOST
        else:
            yield(attack(player_scene,"completed")
            if enemy_scene.current_health == 0:
                self.current_state = GameState.WON
            else:
                self.current_state = GameState.CHOICE

func attack(attacker,target):
    print(attacker.name," attacking")
    target.take_damage(attacker.attack) # Calls the function to take damage
    yield(get_tree().create_timer(2),"timeout") #Wait for 2 seconds
    _update_view()

请注意,使用“health_changed”信号时,此代码要简单得多。因为您不必处理 _battle 上的输赢条件,也不必从 _update_view 调用 attack

最后,我们可以编写一个将它们放入数组的版本:

func _battle():
    var participants = [player_scene,enemy_scene]
    participants.sort_custom(self,"check_speed")
    for attacker in participants:
        var target = _choose_target(attacker,participants)
        yield(attack(attacker,target),"completed")

        if player_scene.current_health == 0:
            self.current_state = GameState.LOST
            return
        if enemy_scene.current_health == 0:
            self.current_state = GameState.WON
            return

    self.current_state = GameState.CHOICE

func check_speed(a,b):
    return a.speed > b.speed:

func _choose_target(attacker,participants):
    for participant in participants:
        if participant == attacker:
            continue

        return participant

同样,使用“health_changed”信号会更简单。

这仍然需要一些修改才能完全支持两个以上的怪物:

  • 要赢得它,将检查是否所有敌方怪物都死了,而不仅仅是一个。
  • 丢失它会检查是否所有盟友怪物都死了,而不仅仅是一个。
  • 在选择目标时,请确保选择合适的团队并考虑攻击者。 您也可以考虑给予玩家选择目标的控制权。

当然,您可以将数组保留在 _battle 之外,并在参与者死亡时将其移除。然后检查是否所有敌方怪物都死了,实际上是检查所有剩余的怪物是否都是盟友(反之亦然)。您可以通过计算那里有多少盟友或敌方怪物来做到这一点,并在它们死亡时递减它。 你可以连接到“health_changed”信号。


啊,是的,一个允许怪物根据其速度进行多次攻击的版本。您将需要这些场景的新属性。我称之为exhaustion,战斗开始时应该是0

func _battle():
    var participants = [player_scene,enemy_scene]
    var player_attacked = false
    while (true):
        var attacker = get_next(participants)

        if player_scene == attacker:
            if player_attacked:
                # It is time for the player to attack again.
                # Return control to the player.
                self.current_state = GameState.CHOICE
                return
            else:
                player_attacked = true

        var target = _choose_target(attacker,participants)

        yield(attack(attacker,"completed")
        attacker.exhaustion += 1.0 / attacker.speed # <--

        if player_scene.current_health == 0:
            self.current_state = GameState.LOST
            return
        if enemy_scene.current_health == 0:
            self.current_state = GameState.WON
            return

func get_next(participants):
    participants.sort_custom(self,"check_order")
    return participants[0]

func check_order(a,b):
    if a.exhaustion < b.exhaustion:
        return true

    if a.exhaustion == b.exhaustion:
        return a.speed > b.speed:

    return false

正如您在此处看到的,规则是具有较少 exhaustion 的怪物先行。如果两个怪物的 exhaustion 相同,那么 speed 多的那个先走。并且每次怪物攻击时,其 exhaustion 都会增加。到多少? speed 的倒数。因此,speed 越少的怪物消耗得越快,而speed 越多的怪物消耗得越慢。因此,如果一个怪物的 speed 比另一个多,那么它最终会像第一个那样对每一个进行多次攻击。

注意:我称其为“耗竭”,只是为了让您对价值如何变化有一些直觉。但是,重要的是不要在一场战斗中保留它,它必须重置为 0。如果你想象一只筋疲力尽的怪物与一个没有筋疲力尽的怪物,那么没有筋疲力尽的一个会在另一个可以做任何事情之前受到很多攻击。

我们需要跟踪玩家是否攻击,正如我在原始答案中提到的那样,以打破循环。


原答案

这取决于您希望 Speed 的工作方式,我只会展示最简单的方法。但首先让我们从基线开始。

让我们说,不考虑速度,玩家攻击,然后怪物攻击,然后等待下一次输入。

这意味着做这样的事情:

func _on_Attack_button_pressed():
    # ...
    player_attack()
    enemy_attack()

除了那些函数 yield。所以我们需要这个:

func _on_Attack_button_pressed():
    # ...
    yield(player_attack(),"completed")
    yield(enemy_attack(),"completed")

原因是一个产生结果的函数返回一个 GDScriptFunctionState

关于 GDScriptFunctionState 的文档:

在函数内调用@GDScript.yield 将导致该函数作为该类型的对象屈服并返回其当前状态

这里我使用了 "completed"GDScriptFunctionState 信号,如关于 Coroutines & signals 的文档中所建议的:

如果你不确定一个函数是否可以让步,或者它是否可以多次让步,你可以有条件地让步到完成的信号


现在,让“速度”工作的最简单方法是将速度视为转弯顺序。类似于 D&D 计划。这意味着我们所做的只是根据速度对这些操作进行排序。所以你做这样的事情:

func _on_Attack_button_pressed():
    # ...
    if player_goes_first():
        yield(player_attack(),"completed")
        yield(enemy_attack(),"completed")
    else:
        yield(enemy_attack(),"completed")
        yield(player_attack(),"completed")

给出你的代码,我猜 player_goes_first 只会返回 P_team.monster_invetory[0].speed > enemy_scene.speed。见Functions

我们也可以这样写:

func _on_Attack_button_pressed():
    # ...
    if player_speed() > enemy_speed():
        yield(player_attack(),"completed")

其中 player_speedenemy_speed 是返回适当值的函数。

如果您需要三个参与者,您可以想象一下代码 (something like this)。


但是,您的代码将受益于使用具有 speed 属性和 attack 方法的对象(这些对象可能是也可能不是节点,请参阅 Classes)。

如果你有对象,支持更多参与者会更容易:

  • 将参与者放在一个数组中。
  • speedsort_custom 对它们进行排序。
  • 遍历数组,并对每个数组调用 attack

另一种方法是使用 get_next 函数返回下一个要调用 attack 的对象。然后循环调用 get_next。您将有一个变量来跟踪玩家是否进行了攻击(通过检查您从 get_next 获得的内容是否是玩家对象),并在循环玩家攻击两次之前停止(因为用户应该按下按钮再次攻击)

使用 get_next 函数可以实现更复杂的逻辑。例如,让参与者根据其 speed 进行多次攻击。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。