Godot的全球statemachine处理程序
我正在尝试使这台有限的状态机起作用,但是它与播放器的运行状态无关,这是代码:
state.gd,
extends KinematicBody2D
class_name State
var state_machine = null
var player_velocity = Vector2()
var player_speed = 200
var player_energy = 50
enum playerStates{IDLE, WALK, RUN, USE, SHOOT}
enum ItemStates{ITEM_ON_GROUND, IS_EQUIPED}
enum Inventory{SELECTED, NOTSELECTED}
var parent = get_parent()
func _ready():
if Global.player_isIdle == State.state_machine == playerStates.IDLE:
pass
if Global.player_isWalking == State.state_machine == playerStates.WALK:
State.player_speed = Global.player_speed
State.player_speed = 200
if Global.player_isRunning == State.state_machine == playerStates.RUN:
State.player_speed = Global.player_speed
State.player_speed = 300
else:
Global.player_isWalking == State.state_machine == playerStates.WALK
func handle_input(_event: InputEvent):
if Global.player_velocity != Global.player_velocity.ZERO:
if Global.player_isWalking == State.state_machine == playerStates.WALK:
pass
if Global.player_isRunning == State.state_machine == playerStates.RUN:
if Input.is_action_pressed("sprint"):
print("RUN")
Global.player_velocity = Global.player_velocity.normalized() * player_speed
Global.player_velocity = move_and_slide(Global.player_velocity)
func _physics_process(_delta):
Global.player_velocity = parent.move_and_slide(Global.player_velocity)
```
Player.gd
```rust
onready var velocity = Global.player_velocity
...
func get_input():
velocity = Vector2()
#speed = Global.player_speed
if Input.is_action_pressed("sprint"):
Global.player_isRunning = true
#speed = 300
else:
#speed = 200
pass
if Input.is_action_pressed("move-right"):
Global.player_direction = "0"
velocity.x += 1
if $Footsteps.playing == false:
$Footsteps.play()
if Input.is_action_pressed("move-left"):
Global.player_direction = "1"
velocity.x -= 1
if $Footsteps.playing == false:
$Footsteps.play()
if Input.is_action_pressed("move-down"):
Global.player_direction = "2"
velocity.y += 1
if $Footsteps.playing == false:
$Footsteps.play()
if Input.is_action_pressed("move-up"):
Global.player_direction = "3"
velocity.y -= 1
if $Footsteps.playing == false:
$Footsteps.play()
velocity = velocity.normalized() * speed
velocity = move_and_slide(velocity)
```
Global.gd
```rust
extends Node2D
var player_initial_map_position = Vector2(50,50)
var player_direction = ["LEFT","RIGHT","UP","DOWN"]
var player_velocity = Vector2()
var player_speed
var player_isIdle : bool
var player_isWalking : bool
var player_isRunning : bool
var player_isUsing : bool
var player_isShooting : bool
var player_pickedUpItem : bool
var player_DropItem : bool
var pressed_button
var hasM4a1 : bool
var hasMiningDrill : bool
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我在解析您的图表时遇到了麻烦。为了避免您的咆哮,我决定忽略它。
在进入状态机之前,我想鼓励使用 types 。
我可以看到一些更改类型的变量,例如,您声明此数组:
但是 - 显然 - 用
string
>:这种事物对代码的正确性作为一个,这并不使我充满信心所有的。
属性蚂蚁匹配
我们从
enum
开始,例如您拥有的:然后我们将拥有该类型的属性:
然后我们可以将该属性匹配到
enum
的值无论您需要:它比使用语句使用
更干净。如果您事先知道所有州,这是一个很好的方法。但是,请注意,如果您修改
enum
,则需要考虑Match
您可能需要更新的每个地方。关于状态过渡,它们只是设置变量。例如:
我们可以通过拥有专用方法来设置状态来改进这一点。而且只有在那里写州。 例如:例如:
在那里您可以使用
Match
来处理状态转换:此外,您可以使用
setget> setget
。但是,请注意,除非您使用self
访问它,否则从同一类设置器将无法运行。 也需要一点二曲线。我们可以通过为每个状态使用功能来改进它:
这需要一些解释。
首先,当您声明
枚举
时,Godot创建了一个具有其值的字典。因此,我们可以使用它来查询类似enumname.keys()[value]
的值的名称,或者我们可以查询这样的名称的值enumname [value]
。其次,我们可以检查使用
has_method
的方法名称。 如果您确保声明它们,则不必这样做。但是我这里有完整性。,我们可以使用call
或call_deferred
来调用名称来调用方法。 根据我的经验call_deferred
是您想要的。它将避免在改变状态的状态过渡的情况下绩效问题。一个缺点是您失去了旧状态的跟踪,因为在调用方法的时间,状态已经更改了。节点
到目前为止,问题是:
我们可以通过拥有代码与之交互的专用状态机对象来解决第一个。因此,国家的所有检查和操纵都必须经过它。
要解决第二部分,我们可以上课,而不是使用
enum
。实际上,在Godot,我建议将它们Node
s。这导致场景树中的该组织:
statemachine
将具有一个脚本,该脚本将暴露您可以使用该属性来设置状态的属性。现在,我们可能不想传递node
,所以我将以string
>:让我们分解:
setget
。因此,当我们尝试从另一个脚本设置它们时,它会导致执行给定方法(set_state
和no_set
)。set_state
将尝试获取一个子节点,其中的名称是从给定的String
派生的。is_instance_valid
,如果我们没有得到有效node
(例如,我们得到null
)。节点
我们得到的是一样,在这种情况下,我们什么也不做。节点
我们得到的是statemachine
的孩子。 这是防御性编程检查。no_set
,除了推动错误之外,有意无能为力。它在那里提醒您不要直接设置node
- 并且要扩展,请防止它。现在,每个州都可以拥有自己的方法和属性。 实际上,使用节点带来了使用
导出var
使状态轻松从编辑器配置的机会。让我们说,例如,您需要以不同的方式处理输入根据状态,您可以在玩家中使用此功能:
这将导致在当前状态
node
上调用handle_input
。然后,每个状态节点
s可以定义该方法:我们需要确定状态如何访问播放器。 我将把它留给结束。
ah,对,国家过渡。以前,我们在
set_state
中有一个匹配
,我们正在根据名称调用方法。我们也做到了。例如,我们可以将当前
属性提供给所有状态node
s:然后我们将其设置为
set_state
请注意,我正在使用在中检查
node
是否具有特定属性。 再次,这是一项防御性编程检查。您可以避免,如果您确保它们都拥有它。此外,我们可以在
setget
on当前
上处理状态过渡:因此,我们没有<代码>匹配,否 语句根本任何地方。
具有行为的状态
我们一直在美国将方法提交,因此它们确实具有行为。它们是场景树中的
节点
。我们可以进一步推动它,并使用_Process
或_PHYSICS_PROCESS
。为此,我建议使用当前
变量启用和禁用它们:class sashitance
我想提到,我们可以为状态> code> node s。在那里,我们可以为所有范围放置常见的代码,类似的东西:
然后状态
节点
扩展了这一点,仅替换了所需的方法。注意我已经拆分
set_current
纳入set_current
和on_set_current ,其中
on_set_current
是要更换的部分。
而且我宁愿不列举它们。
实际上,我有一个哲学上的问题:这些状态还是行为?由于我们一直在美国将方法提交,因此它们确实具有行为。如果他们应该是行为,也许他们不应该具有内部状态。这将使您重复使用它们。 例如,如果您的敌人具有如下所述创建的状态计算机。他们每个人都有状态机的副本。 如果您只能有一个副本并重复使用它?
但是, 这需要使玩家成为其自己场景的根源,例如:
如果
player
是场景的根源。然后其他节点的所有者
将参考它。I'm having trouble parsing your diagram. To spare you the rant, I've decided to ignore it.
Before getting into the state machine, I want to encourage using types.
I can see some variables changing type, for example, you declare this array:
But - apparently - override it with a
String
:This sort of thing does not give me confidence on the correctness of the code as a whole.
Property ant match
We start with an
enum
, such as the one you have:Then we are going to have a property of that type:
Then we can match that property to the values of the
enum
wherever you need:That is cleaner than using
if
statements. And it is a good approach if you know all the states before hand. However, notice that if you modifyenum
, you need to consider every place where youmatch
that you might need to update.About the state transitions, they are simply setting the variable. For example:
We can improve upon that by having a dedicated method to set the state. And only ever write the state there. Which takes a little dicipline. For example:
And there you can handle state transitions with a
match
:Furthermore you could use
setget
. However, be aware that from the same class a setter would not run unless you useself
to access it. Which also takes a little dicipline.We can improve over that by using a function for each state:
This takes a little explanation.
First, when you declare an
enum
, Godot creates a dictionary with its values. So we can use it to query the name of a value like thisEnumName.keys()[value]
or we can query the value of a name like thisEnumName[value]
.Second, we can check for a method name with
has_method
. Which you would not have to do if you make sure to declare them. But I have here for completeness. And we can call a method by name usingcall
orcall_deferred
. In my experiencecall_deferred
is what you want. It will avoid performance issues with state transitions changing the state. A drawback is that you lose track of the old state, because for the time the method is called, the state already changed.Nodes
So far the issues are:
We can address the first one by having a dedicated State Machine object that our code interacts with. So that all the checks and manipulations of the state has to go through it.
To address the second part, we can make classes instead of using an
enum
. In fact, in Godot, I would suggest to make themNode
s.This leads to this organization in the scene tree:
The
StateMachine
would have a script, which will expose a property that you can use to set the state. Now, we probably don't want to pass aNode
, so I'll make it take aString
:Let us break this down:
setget
. So when we try to set them from another script, it results in executing the given methods (set_state
andno_set
).set_state
will try to get a child node with a name derived from the givenString
.is_instance_valid
in case we didn't get a validNode
(e.g. we gotnull
).Node
we got is the same we had, in which case we do nothing.Node
we got is a child of theStateMachine
. This a defensive programming check.no_set
, intentionally does nothing except pushing an error. It is there to remind you to not set theNode
directly - and to an extend, prevent it.Now each of the states can have their own methods and properties. In fact, using nodes brings the opportunity of using
export var
to make the states easily configurable from the editor.Let us say, for example, that you need to handle input differently depending on the state, you can have this in your player:
Which would result in calling an
handle_input
on the current stateNode
. Then each of the stateNode
s can define that method:We need to decide how the states are going to access the player. I'll leave that for the end.
Ah, right, the state transitions. Previously we had a
match
inset_state
an we were calling methods based on the name. We approach that too. For example, we can give acurrent
property to all stateNode
s:Then we will set it form
set_state
Notice here that I'm using
in
to check if theNode
has a particular property. Again, this is a defensive programming check. You could avoid that if you make sure they all have it.Furthermore, we can use
setget
oncurrent
to handle the state transitions:So we have no
match
and noif
statements anywhere at all.States with behavior
We have been putting methods in the states, so they do have behavior. And they are
Node
s in the scene tree. We can push that further, and use_process
or_physics_process
in them. For that I'd suggest enabling and disabling them with thecurrent
variable:Class inheritance
I want to mention that we can make a base class for the state
Node
s. And there we can put the common code for all the sates, something like this:And then the state
Node
extend that, replacing only the methods they need.Notice I have split
set_current
intoset_current
andon_set_current
, whereon_set_current
is the part intended to be replaced.For example:
Passing the player
There are multiple approaches to pass a reference to the player to the states. And I would rather not enumerate them.
In fact, I have a philosophical question for you: Are these states or behaviors? Since we have been putting methods in the states, they do have behavior. If they ought to be behaviors, perhaps they should not have internal state. Which would allow you to reuse them. For example, if you have enemies with an state machine created as described here. Each of them would have a copy of the state machine. But what if you could only have one copy and reuse it?
Anyway, to keep it simple, the solution I'll put forth is to use
owner
. This requires making the player the root of its own scene, like this:If the
Player
is the root of the scene. Thenowner
of the other nodes will refer to it.这来自 gdquest 。
希望它将对您有所帮助。
它非常适合我。
您可以制作许多州机器,
一个用于球员运动,一个用于进攻状态,还有更多。
This is from GDQuest.
Hopefully, it will help you.
It worked for me perfectly.
You can make many state machines,
one for player movement, one for attacking state and many more.