grid push
@ -1,37 +1,63 @@
|
||||
<!--210218,210107-->
|
||||
<h1>designing a tutorial with bingo cards </h1>
|
||||
<h1>how to make a plugin for godot engine </h1>
|
||||
april 15, 2021<br>
|
||||
#gamedesign #tutorial<br>
|
||||
#godotengine <br>
|
||||
<br>
|
||||
Give your players more freedom during the tutorial by having them play bingo. <br>
|
||||
If Godot Engine doesn't have the functionality you need, you can extend it with your own plugins. You can also use it to make specialized nodes to suit your project. <br>
|
||||
<br>
|
||||
<h2>what's bingo? </h2><br>
|
||||
<h2>how to make a plugin </h2><br>
|
||||
<br>
|
||||
Bingo is a game where the player is given a card with a grid filled with random squares. Someone will call out squares, and the player will cover the called squares with tokens. The goal is to fill a line of squares with tokens: a diagonal, a horizontal, or a vertical. Sometimes the game is played until a full card is covered. Since each card has different squares, it's a game of chance. <br>
|
||||
Navigate to your project folder. Within it, make a folder called <b>addons</b>. Within the addons folder, make a folder and name it after your plugin. Within the plugin folder, you will need a few files: plugin.cfg, custom_node.gd, a .gd file for each custom node your plugin will add, and a .png icon for your custom nodes. <br>
|
||||
<br>
|
||||
Bingo's a popular gambling game in America, but it can be modified into an educational game, too. My music class played spurts of classical music in lieu of calling squares, so we had to identify the instrument in order to cover squares. <br>
|
||||
<h3>plugin.cfg </h3><br>
|
||||
Within the plugin file, make a file called <b>plugin.cfg</b>. Open it in a simple text editor like Notepad and use this template to make your config: <br>
|
||||
<br>
|
||||
[plugin]<br>
|
||||
name="World"<br>
|
||||
description="A world system."<br>
|
||||
author="chimchooree"<br>
|
||||
version="0.4"<br>
|
||||
script="custom_node.gd"<br>
|
||||
<br>
|
||||
<h2>using bingo to design a videogame </h2><br>
|
||||
Obviously, fill the space within the quotation marks with your own information. The script's value should be kept the same, though. Once you have these six lines in your file, save and close. Avoid writing these files in a full word processor like Microsoft Word because they tend to fill your documents with extra characters. Even if they aren't displayed in the word processor, they will make your file unreadable to Godot. <br>
|
||||
<br>
|
||||
I like the bingo board as a game world because there are multiple paths to winning the game. The design of the board strikes a nice balance between game designer choice and player choice. The player is free to choose their own board based on their own criteria, but the designer ensured each board is reasonably equal. There's no board with 24 B4 squares on it, for instance. <br>
|
||||
<br>
|
||||
This balance of freedom and control is great for something like a game tutorial. Usually, tutorials are set in extremely confined areas with strong limits on player exploration and choice. That's a good idea in theory because it will teach the player exactly what he needs to know at a pace that won't overwhelm him, but it's boring in practice. <br>
|
||||
<h3>custom_node.gd </h3><br>
|
||||
Within the plugin file, make another file called <b>custom_node.gd</b>. The contents of this node should look like this: <br>
|
||||
<br>
|
||||
You can think of a traditional tutorial as a row of squares on a bingo card. The squares are probably something like "move forward," "talk to an NPC," "double jump," "equip gun," and "hit the bulls-eye." If that was only one line on the board and all the other lines were filled with similar objectives, the player has a little more freedom to choose their own tutorial. The designer retains control over what the player learns because he can ensure each line is reasonably equal in its ability to prepare the player for the real game. The designer loses control of the order in which tasks are completed, but it's guaranteed that the player finished a complete set of tasks. <br>
|
||||
tool <br>
|
||||
extends EditorPlugin<br>
|
||||
<br>
|
||||
<h2>blessfrey bingo </h2><br>
|
||||
func _enter_tree():<br>
|
||||
add_custom_type("Room", "Node2D", preload("room.gd"), preload("World.png"))<br>
|
||||
<br>
|
||||
<center><img src="/static/img/ent/blessfreybingo.png" alt="(image: a bingo card filled with game objectives like Visit Nurse's Office, Talk to Chloe, Win a Sparring Match, and Buy Cheese Crackers.)"></center> <br>
|
||||
func _exit_tree():<br>
|
||||
remove_custom_type("Room")<br>
|
||||
<br>
|
||||
To play the main content in Blessfrey, the player needs to have basic knowledge of combat, dialog, different characters, and different areas on the map. There's also less vital information that the player should encounter quickly, like shopping, using phone apps, and randomized loot. So, almost every line has some basic combat, character interaction, and zone exploration. There's some more obscure tasks scattered around to hint at what kind of things are possible in the game. <br>
|
||||
To add a type, supply the <b>add_custom_type</b> method with 4 parameters: the name of your custom node, the node type (Node2D, AnimatedSprite, etc), your custom node's preloaded script, and a preloaded icon. Every type you add must also be removed. Use this process to add as many custom nodes as you need. <br>
|
||||
<br>
|
||||
<h2>bingo and the players </h2><br>
|
||||
<br>
|
||||
If you physically present the full card to the players, they will respond differently. Completionists might want to cover the entire board before leaving the tutorial area, so it would be fun to reward them. 1 bingo is a ticket out of Tutorial Land, but there can be further rewards for additional bingos and a big prize for the full board. Speedrunners (any%) are going to figure out which path is the fastest and only take that one. Most players are probably going to explore at their own pace and complete the objectives that seem easy or cool, though. <br>
|
||||
<h3>.gd files </h3><br>
|
||||
For each custom node you declare in the custom_node.gd file, you'll need to supply a .gd script. These are going to look just like your other scripts in the editor. You can write them in full now or leave them relatively blank, but at least declare the type at the top. <br>
|
||||
<br>
|
||||
<h2>in closing </h2><br>
|
||||
So long as the board and potential rewards are balanced, why not consider taking some inspiration from a bingo board when designing your tutorial? There doesn't have to be a literal board anywhere. The idea of alternate but equal paths to completing a tutorial already brings in more potential for player choice while retaining designer control. <br>
|
||||
Since my example calls for a Node2D with a "room.gd", I can make a file within the plugins folder called <b>room.gd</b>. I'll keep the contents extremely simple for now: <br>
|
||||
<br>
|
||||
Last updated April 15, 2021 <br>
|
||||
extends Node2D <br>
|
||||
<br>
|
||||
<br>
|
||||
<h3>icon </h3><br>
|
||||
Icon must be .png files with the dimensions of 16x16 pixels. They are just for you to see within the editor's node tree, so you can draw something fancy or just squish down the same Godot head everyone uses. You can also make a unique icon for each node or just use the same one across all nodes. Just make sure the file name matches what's used in custom_node.gd. <br>
|
||||
<br>
|
||||
If you want to use my squished icon, that's 100% fine with me. If you need to check the license for the original Godot head, though, it's on their <a href="https://github.com/godotengine/godot/blob/master/LOGO_LICENSE.md">git repo</a>. <img src="/static/img/ent/plugin_icon.png" alt="(image: Squished Godot head)"> <br>
|
||||
<br>
|
||||
When choosing whether to make something beautiful or lazy, let Colossians 3:23 echo in your head: "And whatever you do, do it heartily, as to the Lord and not to men." <br>
|
||||
<br>
|
||||
<br>
|
||||
<h2>how to add a plugin </h2><br>
|
||||
Now, inside Godot Engine's editor, go to the top-left menu and select Project>Project Settings and go to the Plugins tab. If your files came out okay, your new plugin should appear in the listing with all the data from your plugin.cfg file. Under the Status heading, make sure the plugin is set to "Enable" or "Active". To check whether your plugin's working, try to add a new node to a scene and search for your custom type. It should pop up with your custom icon in the search. If you add it, it will already have its custom script attached and ready to go. <br>
|
||||
<br>
|
||||
Happy coding! <br>
|
||||
<br>
|
||||
<br>
|
||||
Last updated April 30, 2021 <br>
|
||||
<br>
|
||||
|
@ -0,0 +1,46 @@
|
||||
<!--210218,210107-->
|
||||
<h1>an enemy patrol in godot </h1>
|
||||
april 29, 2021<br>
|
||||
#ai #knowledgebase <br>
|
||||
<br>
|
||||
My patrol routes look like a series of waypoints (Position2Ds) which are the children of a patrol (Node2D). The patrol node is in a group named after the patrol route. Whenever the enemy decides to patrol, adds all the waypoints into an array. The nearest waypoint is set as the enemy's next point. The enemy then pathfinds by building a constellation of dots leading to that next point and moves fowards. To determine whether it is successfully moving towards or has reached the next point, the Knowledge Base is called in to help. <br>
|
||||
<br>
|
||||
The Knowledge Base is a system of event handlers and a message bus used originally to accomodate achievements. Since it is a clean, self-contained system that is aware of and can respond toevery event that happens in the game, it has grown to be the driving force behind dynamic events and game progression. <br>
|
||||
<br>
|
||||
<h2>fast explanation of the knowledge base </h2><br>
|
||||
<br>
|
||||
Essentially, individual, granular events are assigned a knowledge key. The Knowledge Base stores all knowledge in the game. Upon the encounter of a key in the wild, the key is used to "learn" a piece of knowledge by the Knowledge Base. Event Handlers are subscribed to the MessageBus, and once certain pieces knowledge are learned, they can generate an outcome. <br>
|
||||
<br>
|
||||
So let's say every tea has a unique knowledge key. Upon drinking mint tea, the "item_used" event is passed to the MessageBus, the Knowledge Base learns the "mint_tea" knowledge, and the tea achievement event handler takes note. The Knowledge Base groups all the tea knowledge arrays into the tea category. Once the tea category is complete, the tea drinker achievement is awarded to the player. <br>
|
||||
<br>
|
||||
I discuss its design and use more in other articles. <br>
|
||||
<br>
|
||||
<h2>why involve the achievement system? </h2><br>
|
||||
<br>
|
||||
The character or its AI could track its own proximity and vectors, but tying character movement into this achievement system allows more creativity in how the game world can respond to patrols. When event handlers can listen for certain characters to reach certain points, events like having guards switch shifts or get suspicious of missing buddies can happen naturally instead of being hard-coded. I could even do silly things like have an "Arbiter of Left" who censures any character who moves left within his zone. At the very least, I could do what many games do and include a step counter somewhere in a statistics menu. <br>
|
||||
<br>
|
||||
<h2>getting back into the patrol flow </h2><br>
|
||||
<br>
|
||||
So, to determine the enemy's progress towards reaching the next point, the enemy's going to generate an event handler for the "moved" event upon entering the patrol state. This event handler is going to be subscribed to "moved." Every time the enemy moves, it's going to publish "moved" and itself to the MessageBus, so the event handler can be notified. The event handler is going to take the enemy and evaluate whether it's reached the next point or an intermediary dot yet. The handler can also evaluate whether it's on course. Once the event handler knows how the enemy's doing, it will shoot off the appropriate signal. <br>
|
||||
<br>
|
||||
The enemy's AI will receive the signal and respond. If it's arrived at the next point, it will get the index of the current next point (the child order of the patrol node) and set the waypoint at the following index as the new next point. Then it can repeat the process of moving to this next point. If the AI finds out the enemy isn't on course, if can check to see if it's stuck on the terrain, affected by a paralysis status effect, or being pushed around by other characters, then respond appropriately. <br>
|
||||
<br>
|
||||
<h2>pathfinding and moving </h2><br>
|
||||
Pathfinding is mostly handled by the Godot Engine, but there's an important distinction to maintain between the waypoints that form a patrol route and intermediary pathfinding points between a character and a waypoint. Lack of clarity in my design led to a few weeks of confusion. <br>
|
||||
<br>
|
||||
The "path_to_object" method handles finding a path between the character and goal point. It sets the given object as the next point and sets the character's "dots" as the discrete line between itself and the next point. These dots are determined by the zone's Navigation2D's built-in get_simple_path method. The dots are stored in the state machine. Upon getting new dots, it sets its current dot by popping the front dot off the array. The patrol state, every time Execute is called, sets the enemy's velocity as the current dot - the character's global position. It also published "moved" to the MessageBus for the event handler to handler. To actually move, the enemy's KinematicBody2D calls move_and_collide from its _process method with the parameter get_velocity() * get_speed() * delta. <br>
|
||||
<br>
|
||||
<h2>cleaning up </h2><br>
|
||||
<br>
|
||||
Since the enemy might switch out of the patrol state at any moment, the setup and teardown are important. I also was sure to use signals when communicating to the patrol state instead of direct calls. <br>
|
||||
<br>
|
||||
Every state in Blessfrey is structured into an Enter, Execute, and Exit method. Enter is called once upon state transition, Execute is called every time the AI's timer ticks, and Exit is called once upon transitioning out of the state. The Enter and Exit are where setup and teardown are called respectively. <br>
|
||||
<br>
|
||||
The setup instances the moved handler, subscribes it to "moved" through the message bus, and connects all the necessary signals between the event handler + patrol state and the patrol state + state machine. The teardown disconnects all these signals and unsubscribes + queue_frees the event handler. That way, all loose ends are tied up. <br>
|
||||
<br>
|
||||
<h2>finally - the enemy patrol! </h2><br>
|
||||
Finally got the enemy patrol into the game. It required a redesign of the character movement system, the development of new features for the Knowledge Base, and tons of debugging, but I'm pretty happy with my little enemies. Next, I'll refactor movement, since there's still some fragments of the old movement system cluttering my AI scripts, but after that, I'll try publishing a release version for HTML5 to put up on my website. Looking forward to it! <br>
|
||||
<br>
|
||||
<br>
|
||||
Last updated April 30, 2021 <br>
|
||||
<br>
|
@ -0,0 +1,46 @@
|
||||
<!--210218,210107-->
|
||||
<h1>an enemy patrol in godot </h1>
|
||||
april 29, 2021<br>
|
||||
#ai #knowledgebase <br>
|
||||
<br>
|
||||
My patrol routes look like a series of waypoints (Position2Ds) which are the children of a patrol (Node2D). The patrol node is in a group named after the patrol route. Whenever the enemy decides to patrol, adds all the waypoints into an array. The nearest waypoint is set as the enemy's next point. The enemy then pathfinds by building a constellation of dots leading to that next point and moves fowards. To determine whether it is successfully moving towards or has reached the next point, the Knowledge Base is called in to help. <br>
|
||||
<br>
|
||||
The Knowledge Base is a system of event handlers and a message bus used originally to accomodate achievements. Since it is a clean, self-contained system that is aware of and can respond toevery event that happens in the game, it has grown to be the driving force behind dynamic events and game progression. <br>
|
||||
<br>
|
||||
<h2>fast explanation of the knowledge base </h2><br>
|
||||
<br>
|
||||
Essentially, individual, granular events are assigned a knowledge key. The Knowledge Base stores all knowledge in the game. Upon the encounter of a key in the wild, the key is used to "learn" a piece of knowledge by the Knowledge Base. Event Handlers are subscribed to the MessageBus, and once certain pieces knowledge are learned, they can generate an outcome. <br>
|
||||
<br>
|
||||
So let's say every tea has a unique knowledge key. Upon drinking mint tea, the "item_used" event is passed to the MessageBus, the Knowledge Base learns the "mint_tea" knowledge, and the tea achievement event handler takes note. The Knowledge Base groups all the tea knowledge arrays into the tea category. Once the tea category is complete, the tea drinker achievement is awarded to the player. <br>
|
||||
<br>
|
||||
I discuss its design and use more in other articles. <br>
|
||||
<br>
|
||||
<h2>why involve the achievement system? </h2><br>
|
||||
<br>
|
||||
The character or its AI could track its own proximity and vectors, but tying character movement into this achievement system allows more creativity in how the game world can respond to patrols. When event handlers can listen for certain characters to reach certain points, events like having guards switch shifts or get suspicious of missing buddies can happen naturally instead of being hard-coded. I could even do silly things like have an "Arbiter of Left" who censures any character who moves left within his zone. At the very least, I could do what many games do and include a step counter somewhere in a statistics menu. <br>
|
||||
<br>
|
||||
<h2>getting back into the patrol flow </h2><br>
|
||||
<br>
|
||||
So, to determine the enemy's progress towards reaching the next point, the enemy's going to generate an event handler for the "moved" event upon entering the patrol state. This event handler is going to be subscribed to "moved." Every time the enemy moves, it's going to publish "moved" and itself to the MessageBus, so the event handler can be notified. The event handler is going to take the enemy and evaluate whether it's reached the next point or an intermediary dot yet. The handler can also evaluate whether it's on course. Once the event handler knows how the enemy's doing, it will shoot off the appropriate signal. <br>
|
||||
<br>
|
||||
The enemy's AI will receive the signal and respond. If it's arrived at the next point, it will get the index of the current next point (the child order of the patrol node) and set the waypoint at the following index as the new next point. Then it can repeat the process of moving to this next point. If the AI finds out the enemy isn't on course, if can check to see if it's stuck on the terrain, affected by a paralysis status effect, or being pushed around by other characters, then respond appropriately. <br>
|
||||
<br>
|
||||
<h2>pathfinding and moving </h2><br>
|
||||
Pathfinding is mostly handled by the Godot Engine, but there's an important distinction to maintain between the waypoints that form a patrol route and intermediary pathfinding points between a character and a waypoint. Lack of clarity in my design led to a few weeks of confusion. <br>
|
||||
<br>
|
||||
The "path_to_object" method handles finding a path between the character and goal point. It sets the given object as the next point and sets the character's "dots" as the discrete line between itself and the next point. These dots are determined by the zone's Navigation2D's built-in get_simple_path method. The dots are stored in the state machine. Upon getting new dots, it sets its current dot by popping the front dot off the array. The patrol state, every time Execute is called, sets the enemy's velocity as the current dot - the character's global position. It also published "moved" to the MessageBus for the event handler to handler. To actually move, the enemy's KinematicBody2D calls move_and_collide from its _process method with the parameter get_velocity() * get_speed() * delta. <br>
|
||||
<br>
|
||||
<h2>cleaning up </h2><br>
|
||||
<br>
|
||||
Since the enemy might switch out of the patrol state at any moment, the setup and teardown are important. I also was sure to use signals when communicating to the patrol state instead of direct calls. <br>
|
||||
<br>
|
||||
Every state in Blessfrey is structured into an Enter, Execute, and Exit method. Enter is called once upon state transition, Execute is called every time the AI's timer ticks, and Exit is called once upon transitioning out of the state. The Enter and Exit are where setup and teardown are called respectively. <br>
|
||||
<br>
|
||||
The setup instances the moved handler, subscribes it to "moved" through the message bus, and connects all the necessary signals between the event handler + patrol state and the patrol state + state machine. The teardown disconnects all these signals and unsubscribes + queue_frees the event handler. That way, all loose ends are tied up. <br>
|
||||
<br>
|
||||
<h2>finally - the enemy patrol! </h2><br>
|
||||
Finally got the enemy patrol into the game. It required a redesign of the character movement system, the development of new features for the Knowledge Base, and tons of debugging, but I'm pretty happy with my little enemies. Next, I'll refactor movement, since there's still some fragments of the old movement system cluttering my AI scripts, but after that, I'll try publishing a release version for HTML5 to put up on my website. Looking forward to it! <br>
|
||||
<br>
|
||||
<br>
|
||||
Last updated April 30, 2021 <br>
|
||||
<br>
|
After Width: | Height: | Size: 630 B |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 14 KiB |