Godot/Gdscript网格运动培训

发布于 2025-01-21 19:35:24 字数 1896 浏览 5 评论 0原文

因此,我一直在使用KidsCancode的Godot教程/文档来帮助我为我正在从事的项目创建一个基于口袋妖怪的网格运动。出于所有意图和目的,我想在早期的手持式口袋妖怪游戏中创建一个与该运动系统一样接近的动作系统。

我想在开始之前添加两件事。第一,我已经喜欢Kidscancode试图教授基于网格的运动的方式,因此,虽然编码的其他方法可能更简单,例如可以在诸如此类的视频中找到的方式(

因此,首先,这是我的 player场景节点树。最重要的部分是Raycast2d和Tween节点。

这是我的主要区域2D播放器节点的代码:

extends Area2D

const tile_size = 16
export var speed = 5

var inputs = { "ui_right": Vector2.RIGHT,
            "ui_left": Vector2.LEFT,
            "ui_up": Vector2.UP,
            "ui_down": Vector2.DOWN }

func _ready():
    position = position.snapped(Vector2.ONE * tile_size/2)

func _unhandled_input(event):
    if $Tween.is_active():
        return
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(inputs[dir])

func move(dir):
    $RayCast2D.cast_to = inputs[dir] * tile_size
    $RayCast2D.force_raycast_update()
    if !$RayCast2D.is_colliding():
        move_tween(dir)

func move_tween(dir):
    $Tween.interpolate_property(self, "position", position,
        position + inputs[dir] * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
    $Tween.start()

快速解释,func _ready():将播放器捕捉到网格。 func _unhandled_input(事件):然后检查是否发生了补间,如果没有发生,请调用func move(dir)。此功能射线播放到给定的方向输入,强制射线播放更新,如果没有静态主体在给定的方向,请调用func move_tween(dir)。最后一个功能将补间插值处理到给定的方向,并启动二元过程。就是这样。再一次,这曾经工作正常。

但是,现在,当我尝试运行此操作时,我会遇到一个错误“无效的获取索引'(0,1)'(在基础上:'dictionary')“ where”(0,1)“基于我尝试的方向更改在游戏运行时搬进来。

在调试器码头中,在堆栈帧的下方,它在“ 22- at function; move; move” $ raycast2d.cast_to = inputs = inputs [dir] * tile_size和“ 19- at function:_unhandled_input” 移动(输入[dir])。

网站上的代码只有以下是(dir)而不是(inputs [dir])。但是这样做只会给我另一个错误。如果有人比我更聪明地知道发生了什么,我将非常感谢任何洞察力。谢谢 !

So I've been using KidsCanCode's Godot tutorials/documentation to help me create a Pokemon-like grid-based movement for a project I'm working on. For all intents and purposes, I would like to create a movement system as close to that in the earlier handheld Pokemon games as possible.

I would like to add two things before I start; one, I have grown fond of the way KidsCanCode attempted to teach grid-based movement, so while other ways of coding it may be simpler such as those that can be found on videos such as this one (https://www.youtube.com/watch?v=jSv5sGpnFso), I would like to hard-headidly stick to this method of coding it... you'll see what I mean when you read the code. Lastly, I would like to add that I had this code working before ! I actually haven't made any changes to the code since it was last working, however, for some reason it no longer seems to work, I'm not sure if that's due to Godot updating since, but hopefully someone can help me out with that.

So first of all, this is my player scene node tree. The most important parts of this being the RayCast2D and Tween nodes.

And this is my code for the main Area2D Player node:

extends Area2D

const tile_size = 16
export var speed = 5

var inputs = { "ui_right": Vector2.RIGHT,
            "ui_left": Vector2.LEFT,
            "ui_up": Vector2.UP,
            "ui_down": Vector2.DOWN }

func _ready():
    position = position.snapped(Vector2.ONE * tile_size/2)

func _unhandled_input(event):
    if $Tween.is_active():
        return
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(inputs[dir])

func move(dir):
    $RayCast2D.cast_to = inputs[dir] * tile_size
    $RayCast2D.force_raycast_update()
    if !$RayCast2D.is_colliding():
        move_tween(dir)

func move_tween(dir):
    $Tween.interpolate_property(self, "position", position,
        position + inputs[dir] * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
    $Tween.start()

To quickly explain, func _ready(): snaps the player to the grid. func _unhandled_input(event): then checks to see if a Tween is occurring, and if not, calls func move(dir). This function raycasts to the given direction input, forces a raycast update, and if no static body is in the given direction, calls func move_tween(dir). This last functions handles tween interpolation to the given direction and starts the tweening process. That's pretty much it. Once again, this used to work just fine.

However, now when I try to run this, I get an error "Invalid get index '(0, 1)' (on base: 'Dictionary')" where "(0, 1)" changes based on what direction I tried to move in when the game was running.

In the Debugger dock, underneath Stack Frames, it gives me errors on lines "22 - at function; move" $RayCast2D.cast_to = inputs[dir] * tile_size and "19 - at function: _unhandled_input" move(inputs[dir]).

The code on the website had these say (dir) only instead of (inputs[dir]). But doing so only gives me another error. If anyone smarter than me has any idea what's going on, I would very much appreciate any and all insight. Thank you !

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

诗化ㄋ丶相逢 2025-01-28 19:35:24

了解问题

还可以,让我们看看。变量输入具有您的字典:

var inputs = { "ui_right": Vector2.RIGHT,
            "ui_left": Vector2.LEFT,
            "ui_up": Vector2.UP,
            "ui_down": Vector2.DOWN }

键是string,并且值为vector2

因此,这里:

    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(inputs[dir])

变量dir将是String。这是您所需的is_action_pressed,所以这是正确的。

输入[dir]将是vector2。这意味着在移动中,您将vector2作为参数中。

现在,在中移动您说::

func move(dir):
    $RayCast2D.cast_to = inputs[dir] * tile_size

,但请记住,您通过的参数是vector2input> Input的键都是字符串。因此,它在这里失败:输入[dir]


使用类型类型的类似问题的预警

可以帮助您尽早确定此类问题。可悲的是,在Godot 3.x中,无法指定字典的键和值。

可以说,您可以使用c#并使用.net dictionary< tkey,tvalue>来自system.collections.collections.generic,它可以指定键和值类型。但是,我们不是在这里谈论这些词典。

您可以用GDScript判断的是您的参数是vector2

func move(displacement:Vector2):
    # …

string

func move(dir:String):
    # …

godot可以告诉当您用错误的参数调用它们时。


另一件事将有所帮助。尽管它更多地在纪律方面,但要保持一致的名称。如果您使用的名称在您的系统中具有具体的含义,它们将为您提供帮助。

例如,您调用移动这样:

move(inputs[dir])

这意味着您所传递的内容不称为dir※。但是您的移动按这样的定义:

func move(dir):
    # …

移动期望您称之为dir。您会看到,当您键入MOVE的调用时。

※:我会说您正在传递Inputs的值之一,因此所传递的内容称为Input。或者,您可以将它们称为Action,鉴于您在is_action_pressed中使用它们。同样,这将以帮助您的方式使用名称。


解决问题的

方法是通过使用字符串inputs in _UNHANDLED_INPUT仅使用带有输入)。并从那里使用vector2。这意味着:

  • 如果将来您想要一个不是来自其中一个输入的运动,其他方法也将很有用。
  • 您没有重复在dictionary中查找的努力。

诚然,对于您的游戏来说,这些并不重要。最终,您要做的就是取决于您。但是,请考虑将这种方法提交您的考虑。

这是代码(我添加了一些类型的注释):

extends Area2D

const tile_size:float = 16
export var speed:float = 5

var inputs = { "ui_right": Vector2.RIGHT,
            "ui_left": Vector2.LEFT,
            "ui_up": Vector2.UP,
            "ui_down": Vector2.DOWN }

func _ready():
    position = position.snapped(Vector2.ONE * tile_size/2)

func _unhandled_input(event:InputEvent) -> void:
    if $Tween.is_active():
        return
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(inputs[dir])

func move(displacement:Vector2) -> void:
    $RayCast2D.cast_to = displacement * tile_size
    $RayCast2D.force_raycast_update()
    if !$RayCast2D.is_colliding():
        move_tween(displacement)

func move_tween(displacement:Vector2) -> void:
    $Tween.interpolate_property(self, "position", position,
        position + displacement * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
    $Tween.start()

或者您可以每次都使用String思考并查询字典。我相信,这是您的意图。像这样:

extends Area2D

const tile_size:float = 16
export var speed:float = 5

var inputs = { "ui_right": Vector2.RIGHT,
            "ui_left": Vector2.LEFT,
            "ui_up": Vector2.UP,
            "ui_down": Vector2.DOWN }

func _ready():
    position = position.snapped(Vector2.ONE * tile_size/2)

func _unhandled_input(event:InputEvent) -> void:
    if $Tween.is_active():
        return
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(dir)

func move(dir:String) -> void:
    $RayCast2D.cast_to = input[dir] * tile_size
    $RayCast2D.force_raycast_update()
    if !$RayCast2D.is_colliding():
        move_tween(dir)

func move_tween(dir:String) -> void:
    $Tween.interpolate_property(self, "position", position,
        position + input[dir] * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
    $Tween.start()

请注意,_unhandled_inputdir传递给移动移动的方式相同,将dir传递给move_tween

Understanding the problem

Alright, let us see. The variable inputs has your dictionary:

var inputs = { "ui_right": Vector2.RIGHT,
            "ui_left": Vector2.LEFT,
            "ui_up": Vector2.UP,
            "ui_down": Vector2.DOWN }

The keys are String, and the values are Vector2.

Thus, here:

    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(inputs[dir])

The variable dir is going to be a String. Which is what you need for is_action_pressed, so that is correct.

And inputs[dir] is going to be a Vector2. Which means that in move you are getting a Vector2 as argument.

Now, in move you say::

func move(dir):
    $RayCast2D.cast_to = inputs[dir] * tile_size

But remember that the argument you are passing is a Vector2, and the keys of input are all String. So it fails here: inputs[dir].


Early warning for similar problems

Using types can help you identify this kind of problems early. Sadly in Godot 3.x there is no way to specify the the keys and values of a Dictionary.

Arguably you could use C# and use .NET Dictionary<TKey,TValue> from the System.Collections.Generic, which would let you specify the key and value types. Yet, we are not talking about those dictionaries here.

What you can tell with GDScript is that your parameters are either Vector2:

func move(displacement:Vector2):
    # …

Or String

func move(dir:String):
    # …

This way Godot can tell you when you are calling them with the wrong parameter.


Another thing that will help. Although it is more on the discipline side, is to keep consistent names. If the names you use have a concrete meaning in your system, they will help you.

For instance, you call move like this:

move(inputs[dir])

Meaning that what you are passing is not called dir※. But you have move defined like this:

func move(dir):
    # …

So move expects something you call a dir. And you would see that when you are typing the call to move.

※: I'd say you are passing one of the values of inputs, so what you are passing is called an input. Or you could call them action, given that you use them in is_action_pressed. Which, again, would be using names in a way that helps you.


Solving the problem

The way I would solve this is by using the String and inputs in _unhandled_input only (after all, that function is meant to deal with inputs). And work with Vector2 from there on. This means that:

  • The other methods would also be useful if in the future you wanted a movement that does not come from one of the inputs.
  • You are not repeating the effort of looking up in the Dictionary.

Admittedly, these aren't a huge deal for your game right now. And ultimately what you do is up to you. Yet, consider this approach submitted to your consideration.

This is the code (I have added some type annotations):

extends Area2D

const tile_size:float = 16
export var speed:float = 5

var inputs = { "ui_right": Vector2.RIGHT,
            "ui_left": Vector2.LEFT,
            "ui_up": Vector2.UP,
            "ui_down": Vector2.DOWN }

func _ready():
    position = position.snapped(Vector2.ONE * tile_size/2)

func _unhandled_input(event:InputEvent) -> void:
    if $Tween.is_active():
        return
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(inputs[dir])

func move(displacement:Vector2) -> void:
    $RayCast2D.cast_to = displacement * tile_size
    $RayCast2D.force_raycast_update()
    if !$RayCast2D.is_colliding():
        move_tween(displacement)

func move_tween(displacement:Vector2) -> void:
    $Tween.interpolate_property(self, "position", position,
        position + displacement * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
    $Tween.start()

Or you can using String thought out, and querying the dictionary every time. Which, I believe, is what you intended. Like this:

extends Area2D

const tile_size:float = 16
export var speed:float = 5

var inputs = { "ui_right": Vector2.RIGHT,
            "ui_left": Vector2.LEFT,
            "ui_up": Vector2.UP,
            "ui_down": Vector2.DOWN }

func _ready():
    position = position.snapped(Vector2.ONE * tile_size/2)

func _unhandled_input(event:InputEvent) -> void:
    if $Tween.is_active():
        return
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(dir)

func move(dir:String) -> void:
    $RayCast2D.cast_to = input[dir] * tile_size
    $RayCast2D.force_raycast_update()
    if !$RayCast2D.is_colliding():
        move_tween(dir)

func move_tween(dir:String) -> void:
    $Tween.interpolate_property(self, "position", position,
        position + input[dir] * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
    $Tween.start()

Notice here that _unhandled_input is passing dir to move. The same way that move is passing dir to move_tween.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文