Browse Source

Small fixes + extension methods

Eideren 11 months ago
parent
commit
d80f4ab3b6
1 changed files with 33 additions and 34 deletions
  1. 33 34
      en/manual/scripts/best-practice.md

+ 33 - 34
en/manual/scripts/best-practice.md

@@ -23,10 +23,10 @@ Examples of such are:
 - Balance settings, tweaking constants and formulas from the editor to improve iteration when testing your game
 - Balance settings, tweaking constants and formulas from the editor to improve iteration when testing your game
 - Mission/quest, referencing quests inside of components to unlock spots in game when they are completed, giving the ability for your designer to set those up
 - Mission/quest, referencing quests inside of components to unlock spots in game when they are completed, giving the ability for your designer to set those up
 - Loot tables, having a list of `UrlReference<Prefab>` with a probability of drop to easily re-use across multiple mobs
 - Loot tables, having a list of `UrlReference<Prefab>` with a probability of drop to easily re-use across multiple mobs
-- As an all-purpose robust 'key' type, see ## Strings as keys, testing strings for equality
+- As an all-purpose robust 'key' or 'identifier' type, see [this section](#strings-as-keys-or-identifiers)
 
 
 ### Do not mutate Assets
 ### Do not mutate Assets
-To that point, make sure to only mutate assets when it makes sense to do so. Remember that a single asset may be referenced by hundreds of components and systems, those may not expect them to change at runtime. Adhering strictly to this idea also ensures that your game's state doesn't leak through them when loading a new session, game mode, or whatever else.
+To that point, make sure to only mutate assets when it makes sense to do so. Remember that a single asset may be referenced by hundreds of components and systems, those may not expect them to change at runtime. Adhering strictly to this idea also ensures that your game's state does not leak through them when loading a new session, game mode, or whatever else.
 For example, let's say you have an Axe Asset which has a list of modifier, you save the game, progress for a bit then add a modifier, but end up reloading ot the previous save, the modifier will carry over to that previous game state.
 For example, let's say you have an Axe Asset which has a list of modifier, you save the game, progress for a bit then add a modifier, but end up reloading ot the previous save, the modifier will carry over to that previous game state.
 
 
 ### Scene quirk
 ### Scene quirk
@@ -89,20 +89,20 @@ public class MySingleton : SyncScript, IService
 }
 }
 ```
 ```
 
 
-## Strings as Keys/Identifiers
-This is a very popular anti-pattern, strings that are used as keys or identifiers shows up all over the place, here's a short example describing such an usage:
-The quest you're implementing requires the player to gather 10 bear arses, your check for that is to loop through the list of items the player has and check that the item's name match "bear arses".
+## Strings as Keys or Identifiers
+This is a very popular anti-pattern, strings that are used as keys or identifiers shows up all over the place, here's a short example describing such a usage:
+The quest you're implementing requires the player to gather 10 bear arses, your check for that is to loop through the list of items the player has and check that the item's name match `bear arses`.
 
 
-Here's a couple reasons why this is a bad idea:
+Here's a couple of reasons why this is a bad idea:
 - Hard to maintain; if your item ends up changing names because bears are banned in Freedonia, your checks will silently fail
 - Hard to maintain; if your item ends up changing names because bears are banned in Freedonia, your checks will silently fail
-- Fragile, your string isn't checked against anything, typos - "bear ass" wouldn't work, careful with leading or trailing whitespaces ...
-- Obtuse, designers not aware of how your system work may not understand what they should input there
-- No uniqueness, if your system expects those to be unique, i.e.: you are looking for a very specific "bear arse", but you multiple different bear arses
-- Hard to monitor, strings are too ubiquitous, hard to quickly retrieve all instances/usages of those to build a database for different purposes, like translations, validations ...
+- Fragile; your string isn't checked against anything, typos - `bear ass` wouldn't work, careful with leading or trailing whitespaces ...
+- Obtuse; designers not aware of how your system work may not understand what they should input there
+- Non-explicit uniqueness requirement, if your system expects those to be unique, i.e.: you are looking for a very specific "bear arse"; but you could have multiple different items all named `bear arse`
+- Hard to keep track of; strings are too ubiquitous, hard to quickly retrieve all instances/usages of those to build a database for different purposes, like translations, validations ...
 
 
-All of this will inevitably lead to bugs, or additional work to avoid them, time you could definitely use to take care of other, more fun parts of your game.
+All of this will inevitably lead to bugs, or additional work to avoid them - time you could definitely use to take care of other, more fun parts of your game.
 
 
-There are a couple ways to avoid this, one of the more robust ones is to rely on assets themselves; SEE # CUSTOM ASSETS
+There are a couple ways to avoid this, one of the more robust ones is to rely on assets themselves; see [custom assets](custom-assets.md)
 ```cs
 ```cs
 public class Item { }
 public class Item { }
 
 
@@ -112,34 +112,33 @@ public int AmountRequired = 10;
 
 
 public bool HasTheItem()
 public bool HasTheItem()
 {
 {
-  if (Inventory.TryGetAmount(ItemToCheck, out var amount) && amount >= AmountRequired)
-    return true;
-  return false;
+    if (Inventory.TryGetAmount(ItemToCheck, out var amount) && amount >= AmountRequired)
+        return true;
+    return false;
 }
 }
-
 ```
 ```
-Assets are unique, they have an identity since they are reference types, they are
-This is too fragile and obtuse, we can use an asset for this instead. Create a new asset type, it doesn't need any property. Use that asset's type as input for your custom condition and your unique trigger.
-- It doesn't break if we end up changing the name.
-    - Particularly useful for persistence as we could now track them between versions and check if we introduce a breaking change to the player's progression save
-- We can see straight from the editor where it is used through the reference window when selecting the asset.
-    - Meaning that it is now even more straightforward to remove it from the game if you end up not needing it, go through each reference and replace it appropriately
-- From a documentation standpoint, we don't need to keep a document going over each of them, we can just look at the directory were they are stored in the editor.
-
+- Robust; you can change its name and its path, as long as you do not delete it or change its internal id all components referencing it will keep that reference.
+- Easy to use and understand; If a component requires a specific asset you don't have an infinite amount of possibilities, you can either set it to an existing one or create a new one. It's far more foolproof too now that typos are out of the equation.
+- Easy to keep track of; each type has a unique extension which you can search for in your file explorer, they exist on disk, and so can be organized into the same directory.
+  - The editor's reference window lists all assets that use the selection, this greatly helps when you need to swap the identifier for another or remove it altogether, just go through all the assets that refer to it.
+  - you won't need to keep a document going over each identifier you might have in game, one just has to look at the directory were they are stored in the editor.
+- Easy to extend; your identifier can now be more than just that, you can attach properties to it, perhaps a description to keep more information about this key.
 
 
 ## Avoid writing extension methods for access shortcuts
 ## Avoid writing extension methods for access shortcuts
 
 
-`static Entity GetFirstChild(this Entity Entity) => Entity.Transform.Children[0].Entity`
-
-It's a double-edged:
-- Make sure that accessing what you are hiding is never error prone, even more so if the name of the method does not make that obvious. You may be reducing the time wasted from typing, but you could very well increase the time you would take to debug it when it does error out.
-- It may very well be a slippery slope to introduce even more shortcuts to other properties or methods of the object you are presenting
-- You might be reducing the skill floor required for users not accustomed to the API, but you're also hindering their growth as they now rely on this extension method, and likely are not aware of were the rest of the depth that that object provides
-- It might imply to the reader that your shortcut is different to the
-
-
-This is a slippery slope, as once you start creating this shortcut, others may see this extension and request other shortcuts
+This is specifically referring to methods of this kind:
+```
+static Entity GetFirstChild(this Entity Entity) => Entity.Transform.Children[0].Entity;
+// Or
+static void AddAsFirstChild(this Entity Entity, Entity entity) => Entity.Transform.Children.Insert(0, entity);
+```
 
 
+It's a double-edged sword:
+- You are reducing the skill floor required for users not accustomed to the API, but you're also hindering their growth as they now rely on your shortcut instead of seeking it for themselves, making them aware of concepts and objects neighboring that one, giving them a clearer view of how all the objects fit together. What if they need to access all children, from this extension method they would not be aware that the transform component stores them, that he could just access it directly for that.
+- Make sure that accessing what you are hiding is never error-prone, even more so if the name of the method does not make that obvious. You may be reducing the time wasted from typing, but you could very well increase the time you would take to debug it when it does create issues.
+- It may very well be a slippery slope to introduce even more shortcuts to other properties or methods of the object you are presenting, how about creating an extension for the second child of the entity, the third ...
+- Polluting intellisense; in most cases this is a non-issue, but collection types are a prime example of this. Discoverability for extension methods through intellisense is nigh-on-impossible, there are just far too many extension methods introduced by linq.
+- It might imply to the user that your shortcut is somehow different compared to accessing it without the shortcut.
 
 
 ## static EventKeys
 ## static EventKeys