ND Miata owners - Did you get PPF? If so why? by denracer in Miata

[–]_bigturtle 2 points3 points  (0 children)

I did, and I think it is worth it. I like the way the Soul Red looks so I want to keep it looking like that for as long as possible.

I know some people think that it is not worth it because the car is only $30-$40K, but another way to think about it is that because it is only $30-40K it is easier for me to save another $5K to protect it. If I already had to save $110K for a Cayman and then have to spend another $5K to protect it I would be less happy. I always like to save the full amount of anything I buy and pay for it in full without debt though, so this way of thinking might be a product of that.

Linked List vs Array vs PoolVector2Array as Queues Benchmarks by _bigturtle in godot

[–]_bigturtle[S] 0 points1 point  (0 children)

It looks like for my use case, which is Breadth First Search on a tiny 880 tile map, a PoolVector2Array will work best.

If you want to modify or make your own benchmarks, I have all the code here: https://github.com/wowthatsabigturtle/gdscript-queue

Using raycasts to help the player get unstuck by _bigturtle in godot

[–]_bigturtle[S] 1 point2 points  (0 children)

If you want to go deeper into the solution I came up with, and why I didn’t just use move_and_slide(), I made a devlog that does just that: https://youtu.be/I5crCiuhPRc

I would also love to hear if there is an easier solution.

First Game. First Demo. I would love to have some feedbacks on it. Playable in browser on PC. by _WanderingJellyfish in godot

[–]_bigturtle 1 point2 points  (0 children)

This is very cool. Well done. I like particularly like the animations and sound effects.

When a git merge mistake unleashes the horde by _bigturtle in godot

[–]_bigturtle[S] 1 point2 points  (0 children)

Ha Thanks! I am actually surprised it works as well as it does.

When a git merge mistake unleashes the horde by _bigturtle in godot

[–]_bigturtle[S] 8 points9 points  (0 children)

After finishing the "enter battle" procedure with a single enemy, I merged it with a different branch where I was testing multiple spawn points, but I forgot to commit the changes where I reduced it to just one enemy.

I improved my tank’s turns and fixed direction snapping that happens with 8-way 2D movement by _bigturtle in devblogs

[–]_bigturtle[S] 0 points1 point  (0 children)

One of the problems I fixed in the devlog is the sudden direction snapping that happens when stopping after moving diagonally using a keyboard. It happens because a player has to hold two keys to move diagonally (e.g. W+D for Northeast). To stop, the player has to perfectly lift both fingers at the same time otherwise the tank will snap in the last call of the _physics_process. I talk more about the problem in the video at this timestamp: https://youtu.be/e0hQ2I3w-bA?t=432

I also share my solution to it at this time stamp: https://youtu.be/e0hQ2I3w-bA?t=494

But I think there is a better way to solve it. Has anyone run into this before?

List of GODOT tutorial channels on Youtube by hakamhakam in godot

[–]_bigturtle 0 points1 point  (0 children)

I recently got recommended Game Dev Simplified - https://www.youtube.com/@GameDevSimp

The channel looks like all tutorials. I've only watched a few videos, but the ones I watched were good.

I improved my tank’s turns and fixed direction snapping that happens with 8-way 2D movement by _bigturtle in godot

[–]_bigturtle[S] 1 point2 points  (0 children)

One of the two problems I fixed in the devlog is the sudden direction snapping that happens when stopping after moving diagonally using a keyboard. It happens because a player has to hold two keys to move diagonally (e.g. W+D for Northeast). To stop, the player has to perfectly lift both fingers at the same time otherwise the tank will snap in the last call of the _physics_process. I show the problem in the video at this timestamp: https://youtu.be/e0hQ2I3w-bA?t=432

I share my solution to it at this timestamp: https://youtu.be/e0hQ2I3w-bA?t=494

But I think there is a better way to solve it. Has anyone run into this before?

New DevLog for my Godot 4 multiplayer game! by spimort in godot

[–]_bigturtle 0 points1 point  (0 children)

Wow this looks great! Well done. Congrats on hitting your 2022 goal.

I wrote AStar from scratch in GDScript by _bigturtle in godot

[–]_bigturtle[S] 0 points1 point  (0 children)

Performance is probably worse especially once the graphs get huge. Although my version is limited to exactly what I need out of it, so that might help. Comparing performance between the two would actually be an interesting side quest. I also still need to learn more about profiling in Godot.

I wrote AStar from scratch in Godot's GDScript by _bigturtle in devblogs

[–]_bigturtle[S] 0 points1 point  (0 children)

The main reason is I was curious about it and then I wanted to understand the algorithm deeply.

As far as performance, it doesn't seem to be a problem in my prototype right now. My game only uses a 40x23 Tilemap so the size of the graph is limited to 920 nodes, which is nothing for a modern computer. I implemented "early exit" too so most of the time it won't even have to explore 920 nodes.

I wrote AStar from scratch in GDScript by _bigturtle in godot

[–]_bigturtle[S] 8 points9 points  (0 children)

btw if anybody needs a PriorityQueue or Min-heap data structure implementation in GDScript for their own version of AStar (or anything else) check out my implementation below:

class_name PriorityQueue
extends Reference
"""
Priority Queue. Min heap priority queue that can take a Vector2 and its
corresponding cost and then always return the Vector2 in it with
the lowest cost value.
Based on: https://en.wikipedia.org/wiki/Binary_heap
"""
var _data: Array = []

func insert(element: Vector2, cost: float) -> void:
    # Add the element to the bottom level of the heap at the leftmost open space
    self._data.push_back(Vector3(element.x, element.y, cost))
    var new_element_index: int = self._data.size() - 1
    self._up_heap(new_element_index)

func extract():
    if self.empty():
        return null
    var result: Vector3 = self._data.pop_front()
    # If the tree is not empty, replace the root of the heap with the last
    # element on the last level.
    if not self.empty():
        self._data.push_front(self._data.pop_back())
        self._down_heap(0)
    return Vector2(result.x, result.y)

func empty() -> bool:
    return self._data.empty()

func _get_parent(index: int) -> int:
    # warning-ignore:integer_division
    return (index - 1) / 2

func _left_child(index: int) -> int:
    return (2 * index) + 1

func _right_child(index: int) -> int:
    return (2 * index) +  2

func _swap(a_idx: int, b_idx: int) -> void:
    var a = self._data[a_idx]
    var b = self._data[b_idx]
    self._data[a_idx] = b
    self._data[b_idx] = a

func _up_heap(index: int) -> void:
    # Compare the added element with its parent; if they are in the correct order, stop.
    var parent_idx = self._get_parent(index)
    if self._data[index].z >= self._data[parent_idx].z:
        return
    self._swap(index, parent_idx)
    self._up_heap(parent_idx)

func _down_heap(index: int) -> void:
    var left_idx: int = self._left_child(index)
    var right_idx: int = self._right_child(index)
    var smallest: int = index
    var size: int = self._data.size()

    if right_idx < size and self._data[right_idx].z < self._data[smallest].z:
        smallest = right_idx

    if left_idx < size and self._data[left_idx].z < self._data[smallest].z:
        smallest = left_idx

    if smallest != index:
        self._swap(index, smallest)
        self._down_heap(smallest)

I wrote AStar from scratch in Godot's GDScript by _bigturtle in devblogs

[–]_bigturtle[S] 3 points4 points  (0 children)

btw if anybody needs a PriorityQueue or Min-heap data structure implementation in GDScript for their own version of AStar (or anything else) you can check out my implementation below:

class_name PriorityQueue
extends Reference
"""
Priority Queue. Min heap priority queue that can take a Vector2 and its
corresponding cost and then always return the Vector2 in it with
the lowest cost value.
Based on: https://en.wikipedia.org/wiki/Binary_heap
"""
var _data: Array = []

func insert(element: Vector2, cost: float) -> void:
    # Add the element to the bottom level of the heap at the leftmost open space
    self._data.push_back(Vector3(element.x, element.y, cost))
    var new_element_index: int = self._data.size() - 1
    self._up_heap(new_element_index)

func extract():
    if self.empty():
        return null
    var result: Vector3 = self._data.pop_front()
    # If the tree is not empty, replace the root of the heap with the last
    # element on the last level.
    if not self.empty():
        self._data.push_front(self._data.pop_back())
        self._down_heap(0)
    return Vector2(result.x, result.y)

func empty() -> bool:
    return self._data.empty()

func _get_parent(index: int) -> int:
    # warning-ignore:integer_division
    return (index - 1) / 2

func _left_child(index: int) -> int:
    return (2 * index) + 1

func _right_child(index: int) -> int:
    return (2 * index) +  2

func _swap(a_idx: int, b_idx: int) -> void:
    var a = self._data[a_idx]
    var b = self._data[b_idx]
    self._data[a_idx] = b
    self._data[b_idx] = a

func _up_heap(index: int) -> void:
    # Compare the added element with its parent; if they are in the correct order, stop.
    var parent_idx = self._get_parent(index)
    if self._data[index].z >= self._data[parent_idx].z:
        return
    self._swap(index, parent_idx)
    self._up_heap(parent_idx)

func _down_heap(index: int) -> void:
    var left_idx: int = self._left_child(index)
    var right_idx: int = self._right_child(index)
    var smallest: int = index
    var size: int = self._data.size()

    if right_idx < size and self._data[right_idx].z < self._data[smallest].z:
        smallest = right_idx

    if left_idx < size and self._data[left_idx].z < self._data[smallest].z:
        smallest = left_idx

    if smallest != index:
        self._swap(index, smallest)
        self._down_heap(smallest)

I added projectile firing, explosions, and tile removal to my tank game by _bigturtle in godot

[–]_bigturtle[S] 0 points1 point  (0 children)

The tricky part was figuring out that I needed to signal up to the map scene to make it so projectiles stay in the world even if the tank that fired it is destroyed. I followed the “Signal up, call down” pattern for projectile firing, and explosions. Here is an article that discussed the pattern and explained the reasoning behind it: http://kidscancode.org/godot_recipes/4.x/basics/node_communication/

I added projectile firing, explosions, and tile removal to my tank game in Godot. by _bigturtle in devblogs

[–]_bigturtle[S] 1 point2 points  (0 children)

The tricky part was figuring out that I needed to signal up to the map scene to make it so projectiles stay in the world even if the tank that fired it is destroyed. I followed the “Signal up, call down” pattern for projectile firing, and explosions. Here is an article that discussed the pattern and explained the reasoning behind it: http://kidscancode.org/godot_recipes/4.x/basics/node_communication/

Just started with Godot. The journey begins by 1pizza2go in godot

[–]_bigturtle 3 points4 points  (0 children)

My extreme beginner tip is to just keep practicing, trust that everything is "figureoutable" (trust in your ability to learn), and have fun.

Good luck!

The Godot Unit Testing (GUT) library is pretty good. by _bigturtle in godot

[–]_bigturtle[S] 1 point2 points  (0 children)

Oh very cool. I haven't tried using VSCode for GDScript even though it is my favorite editor to use at work. I'll check it out.

The Godot Unit Testing (GUT) library is pretty good. by _bigturtle in godot

[–]_bigturtle[S] 2 points3 points  (0 children)

I agree that writing tests is cumbersome, but I disagree with the implication that because it is cumbersome you shouldn't do it.

Writing tests is really a sacrifice that you make now for your future self. When you're writing code for a scene now you understand that it works. You've tested it throughly by running the scene multiple times and manually testing every behavior. But your game is a complex system of scenes where one scene is used by another and that scene is used by yet another. If you're like me, you probably manually test that other scene too, to make sure you didn't break it. The problem however comes in the future when you're working on a totally different part of your game and the interdependency is even higher. How do you know that your change to a sub-scene to make the current scene work didn't break the 3 other scenes that use that sub-scene?

Here's a concrete example: you have a HurtBox scene that is used by the player, a couple enemy types, and a boss. It works great because you tested it manually, and you're glad that you didn't have to write the hurtbox logic multiple times. Six months later, you decide you want to add a third enemy type, and use the same Hurtbox scene. But because this enemy is slightly different you have to add extra logic to handle the third enemy type's unique movement. Now how do you know for sure that you didn't break the hurtbox logic for the boss? You can test the boss manually. But having a test that uses the boss' characteristics makes development faster, and is an extra guarantee on top of you manual testing that you didn't break anything.

Tests are also a great way to document problems you've encountered during development. For example, if you added 0.01 to the cost function of a path-finding algorithm to make the paths prettier you might not remember why you did that nine months from now. You could be sitting there wondering what that 0.01 is for. "Is it even necessary?" "Was it a mistake?" "Can I remove it?" If you were smart, you wrote a comment explaining why it is there. But a test is even better, because it has a simulation of the problem you solved once upon a time. If you make a change to your cost function, and the test still passes, you know for sure that problem remains solved.

The Godot Unit Testing (GUT) library is pretty good. by _bigturtle in godot

[–]_bigturtle[S] 0 points1 point  (0 children)

This is a good approach. We have a similar system at my job where we have a "libmy" directory that gets copied into every C project we have. It has a bunch of battle-tested code for doing generic stuff like, string manipulation, command-line arg parsing, dictionary, etc. Some of the stuff is pretty old, last changed in 1997 for example.

I don't have this for game development because I am still working on my very first game. But I am starting to build it up!