new article

small-nav
Mimi Momo 4 years ago
parent c13d2c03d9
commit 157f282dcf

@ -1,46 +1,99 @@
<!--210218,210107--> <!--210218,210107-->
<h1>an enemy patrol in godot </h1> <h1>playing FlightRising with spreadsheets </h1>
april 29, 2021<br> may 13, 2021<br>
#ai #knowledgebase <br> #spreadsheets #petsites <br>
<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> My dragon breeding spreadsheet is really coming together, so I thought sharing it would be a fun break from AI. <br>
<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> I love petsites and got into FlightRising near the start of the pandemic. In FlightRising, you breed, feed, train, and dress your pet dragons. The breeding is a major draw of the site. A dragon's appearance is determined by the breeds, genes, and colors of their parents, which have varying levels of dominance. If you want a specific set of colors and genes for your dragon, you'll probably have to begin a breeding project with the closest dragons you can find until they finally give you the desired offspring. When a dragon breeds, they will have a clutch of 1-5 eggs, which take a minimum of 6 days to hatch. The parents also experience a breeding cooldown, which varies depending on the rarity of their breed. <br>
<br> <br>
<h2>fast explanation of the knowledge base </h2><br> There's a lot of information to track - dream dragons, breeding pairs that could potentially give you that offspring, and the breeding schedule based on the cooldowns of your pairs. Spreadsheets are perfect for managing everything for you, though. <br>
<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> <h2>spreadsheets </h2><br>
<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> I use <a href="https://www.libreoffice.org/discover/calc/">LibreOffice Calc</a> for my spreadsheets, but it's practically the same as Excel. <br>
<br> <br>
I discuss its design and use more in other articles. <br> There's sheets for an overview of my breeding pairs, detailed information about individual parents, information about each breeding project, calculators for genes, colors, and dates, information about rarities. <br>
<br> <br>
<h2>why involve the achievement system? </h2><br> If you want the spreadsheet, too, you can download my template here. I'll explain each section and function, so you can modify it to suit your own projects. <br>
<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> <h3>individual dragons </h3><br>
The Singles sheet has a row for each parent and field for sex, breeding status, breed, breed's cooldown, date bred, nest ready, date ready, and a cooldown countdown. I'm not usually interested in this information by itself, but the Pairs sheet heavily references it. <br>
<h4>sex </h4><br>
The sex data is limited to either ♂ or ♀ and can be selected using a dropdown menu. Your answer will turn the cell blue or pink. You make a dropdown menu by setting the Data Validity (Data > Validity...). I set the Criteria to allow a cell range and used a column in the Calculator sheet as the source. The color is dictated by Conditional Formatting (Format > Conditional Formatting). I have one condition for girls and one for boys. I set the condition to look for 'cell value is' 'equal to' then enter "♂" or "♀" with the quotes. Then for Apply Style, I selected a blue background for ♂ cells and a pink one for ♀s. The cell range is the B column. <br>
<h4>breeding status </h4><br>
The status is determined by a formula that checks the breed's cooldown and date bred. If the cooldown is over, the cell says "Ready. If not, it says "Cooldown. The formula is =IF(H2<=TODAY(),"Ready","Cooldown"). IF takes three parameters here: the condition (if the date ready is today or earlier), the text to display if the condition's true, and the text for false. <br>
<br> <br>
<h2>getting back into the patrol flow </h2><br> <h4>breed </h4><br>
The breed, like the sex, is a Data Validity-determined dropdown menu. The list of breeds is available as a column in Calculator. <br>
<h4>breed's cooldown </h4><br>
Each breed has a different cooldown duration. This field uses a formula to refer to the Breed field and search in Calculator for the corresponding cooldown information. The formula is =VLOOKUP($D2,$Calculator.$G$3:$H$18,2,0). Here, I take the breed, take it to the breed + cooldown columns in the Calculator sheet, and return with the data from the 2nd column in that group. <br>
<h4>date bred </h4><br>
I directly type in the date here every time I breed that dragon. If the dragon is too young for breeding, I use its birthday instead. If I forget the birthday is listed directly on the dragon's profile (it happens), the date calculator on Calculator can give me the proper date. <br>
<h4>nest ready </h4><br>
This is a simple formula - the date bred + 6 days. <br>
<h4>date ready to breed again </h4>
This is another simple formula - the date bred + the cooldown. <br>
<h4>cooldown countdown </h4><br>
This one is relatively simple. It's just today minus the cooldown, but I added some steps to add " days" after the number. If there are 0 or less days, I opted for it to say nothing because the default "#N/A" is annoying to look at. The formula is =IF($H2-TODAY()>0,CONCAT($H2-TODAY()," days"),""). You can see IF's three parameters: (condition) there's more than 0 days until cooldown ends, (if true) return that number + " days", (if false), return nothing. CONCAT concatenates the two parameters it's given, so it finds the number of days and adds " days". If it's just 1 day, it will say "1 days". I could use another IF to fix that, but I barely refer to this sheet anyways. <br>
<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> <h3>dragon pairs </h3><br>
The Pairs sheet is the sheet I check every time a nest opens. At a glance, it tells me which pairs are ready and which ones will be soon. It also lets me check whether my goal is within range of the pair and which of their offspring most closely resembles my goal. <br>
<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> The fields are Project, Male, Female, Status, Date Ready, Countdown, Colors, Genes, Best Son, and Best Daughter. <br>
<h4>project</h4> <br>
I add the project, so I know which dragon I'm hoping from these parents. <br>
<h4>male + female</h4> <br>
I add the dragon pair's names here. <br>
<h4>status </h4> <br>
This is the first formula: =IF(AND((VLOOKUP($B2,$Singles.$A:$C,3,)="Ready"),(VLOOKUP(C2,$Singles.A:C,3,)="Ready")),"Ready","Cooldown"). There's a new function AND, which just takes its parameters and considers them together. All together, the formula wants to take each name in the pair, hunt down that dragon's row in the Singles sheet, and check its status. I use AND so that the condition won't be true unless both dragons are ready. <br>
<h4>date ready </h4><br>
I find the date for when the pair is ready with =MAX(VLOOKUP($B2,$Singles.$A:$H,8,),VLOOKUP($C2,$Singles.$A:$H,8,)). MAX takes its parameters and returns the larger value. Basically, it wants to use each dragon's name to check when their cooldown will be ready on the Singles sheet. I use MAX because the pair isn't ready until the parent with the longest cooldown is over. <br>
<h4>countdown </h4><br>
The formula =IF($E2-TODAY()>0,CONCAT($E2-TODAY(), " days"),"") is similar to the one from Singles, but this time, I used Conditional Formatting to make countdowns of 1-5 days an eye-catching yellow. <br>
<h4>colors + genes</h4> <br>
I can't always find parents that are in range of my goal, so noting the range helps me prioritize. PST stands for Primary, Secondary, and Tertiary, since each dragon's appearance is determined by three colors and three genes. I really only track the gem genes, since they can only be obtained through breeding or real world money. If I want an-game cash gene, it's a lot faster just to buy it than breed it, so I don't even consider it in my spreadsheets. <br>
<h4>best son + best daughter</h4> <br>
Space is limited, so I want to know which dragons have the most to contribute to projects. It can take several generations of dragons to get the desired offspring, so I want to ensure the possibility range is becoming as tight as possible with each generation. <br>
<h3>calculator + data </h3><br>
The Calculator sheet is a catch-all. I have data needed for Data Validity on other sheets here, and I keep calculators to quickly determine what information I should enter into other sheets. <br>
<br> <br>
<h2>pathfinding and moving </h2><br> <h4>colors </h4><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> FlightRising's dragon colors exist in a color wheel. If one dragon has a Moon primary color and mates with an Orca dragon, the offspring can have a primary in Moon, Orca, or any of the colors in-between. Since the colors are arbitrarily selected and ordered by the developers, you won't know what to expect without referring to a color chart. <br>
<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> When going for a particular color, I want the parents to be as close to that color as possible. I could manually count the colors in-between, but since there's over 100 colors, it can be time-consuming. Instead, I have all the colors in a column and a little calculator to tell me how far over or under the parent's colors are. I enter primary, secondary, and tertiary colors of the goal dragon and dream dragon. The distance calculates magnitude and direction from the goal separately. <br>
<br> <br>
<h2>cleaning up </h2><br> The formula for magnitude is =ABS(MATCH($C3,$F$2:$F$178, )-MATCH($B3,$F$2:$F$178, )). MATCH takes the color and returns the position within the color chart. ABS gives the absolute value of its parameter. In other words, I subtract the parent's colors from the goal's colors and get the absolute value. <br>
<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> The formula for direction is =IF((MATCH($C3,$F$2:$F$178,)-MATCH($B3,$F$2:$F$178,))>0,"↓","↑"). Here, it displays ↓ if the parent's colors are below the goal's, and ↑ if otherwise. <br>
<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> The calculator ignores the fact that the color chart is a circle. Honestly, I don't breed along the extremes (white and pink), so I haven't attempted to fix this yet. <br>
<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> <h4>breeding info</h4> <br>
These are the columns used by the breeding pages for validity and VLOOKUP. <br>
<br> <br>
<h2>finally - the enemy patrol! </h2><br> <h4>breeding day calculator</h4> <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> If I need to know how long ago a dragon was bred, I can use today's date and the cooldown to find it out. The formula is very simple, just the given date minus the cooldown. <br>
<br> <br>
<h3>project sheets </h3><br>
Here I list the dragons by project, tracking their geneology, sex, colors, genes, and average distance from goal colors. <br>
<h4>father + mother </h4><br>
Dragons cannot breed with relatives within 7 generations. To ensure I'm keeping bloodlines separate, I plan a family tree. Using the father and mother's names, I can remember which family the dragon's in or if it's related to the other dragons at all. <br>
<br> <br>
Last updated April 30, 2021 <br> <h4>sex </h4><br>
Male or female, with colored conditional formatting so I can scan by sex more easily. <br>
<br>
<h4>primary, secondary, + tertiary colors </h4><br>
I usually need to use the color calculator on Calculators for this information. I list the parent's distance from the goal color in each field. In the first row, I list my goal colors for reference. <br>
<br>
<h4>gene 1, 2, + 3 </h4><br>
If the parent has one of the goal genes, I list it here. That way, I can prioritize by gene. I keep the other fields empty. For expensive genes, I only allow parents with genes of equal rarity, so the chance of passing down the goal gene is always 50/50. If my goal is a gene I can just buy, I don't care to track it since buying is easier than breeding. <br>
<br>
<h4>average </h4><br>
Genes are 50/50, but my goal colors are usually around a 1/20 chance. Consequently, I'm much more concerned about the color of a dragon than the genes. To give me a general estimate of my best parents, I use =AVERAGE(E3,G3,I3). The AVERAGE function adds its parameters and divides by the quantity of parameters given. If I had a parent with perfect colors, it would have an average of 1, so ideally, dragons with the lowest average are my most valuable for breeding. When choosing pairs, I consider genes, too, though. <br>
<br>
<h2>that's all~ </h2>
In closing, I'll share a few dragon pictures. Next time, I'll write about more of my development process for Blessfrey. <br>
<br>
Last updated May 9, 2021 <br>
<br> <br>

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Loading…
Cancel
Save