mirror of
https://github.com/unity-atoms/unity-atoms.git
synced 2025-01-21 23:58:49 -05:00
Merge branch 'canary' into adam/remove-generated-docs
This commit is contained in:
commit
798fc99079
@ -4,7 +4,7 @@ using UnityEngine.Assertions;
|
||||
namespace UnityAtoms.BaseAtoms
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a GameObject to a GameObject Value List on OnEnable and removes it on OnDestroy.
|
||||
/// Adds a GameObject to a GameObject Value List on Start and removes it on OnDestroy.
|
||||
/// </summary>
|
||||
[AddComponentMenu("Unity Atoms/MonoBehaviour Helpers/Sync GameObject To List")]
|
||||
[EditorIcon("atom-icon-delicate")]
|
||||
@ -13,7 +13,7 @@ namespace UnityAtoms.BaseAtoms
|
||||
[SerializeField]
|
||||
private GameObjectValueList _list = default;
|
||||
|
||||
void OnEnable()
|
||||
void Start()
|
||||
{
|
||||
Assert.IsNotNull(_list);
|
||||
_list.Add(gameObject);
|
||||
|
@ -16,8 +16,6 @@ namespace UnityAtoms.Editor
|
||||
private TypeSelectorDropdown typeSelectorDropdown;
|
||||
|
||||
private SerializedProperty fullQualifiedName;
|
||||
private SerializedProperty typeNamespace;
|
||||
private SerializedProperty baseType;
|
||||
private SerializedProperty generatedOptions;
|
||||
|
||||
private static bool safeSearch = true;
|
||||
@ -26,8 +24,6 @@ namespace UnityAtoms.Editor
|
||||
{
|
||||
// Find Properties.
|
||||
fullQualifiedName = serializedObject.FindProperty(nameof(AtomGenerator.FullQualifiedName));
|
||||
typeNamespace = serializedObject.FindProperty(nameof(AtomGenerator.Namespace));
|
||||
baseType = serializedObject.FindProperty(nameof(AtomGenerator.BaseType));
|
||||
generatedOptions = serializedObject.FindProperty(nameof(AtomGenerator.GenerationOptions));
|
||||
|
||||
// Check if the current type is unsafe.
|
||||
@ -90,8 +86,6 @@ namespace UnityAtoms.Editor
|
||||
serializedObject.Update();
|
||||
|
||||
fullQualifiedName.stringValue = selectedType.AssemblyQualifiedName;
|
||||
typeNamespace.stringValue = selectedType.Namespace;
|
||||
baseType.stringValue = selectedType.Name;
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
});
|
||||
@ -251,7 +245,14 @@ namespace UnityAtoms.Editor
|
||||
parent.AddChild(dropdownItem);
|
||||
|
||||
// Use Hash instead of id! If 2 AdvancedDropdownItems have the same name, they will generate the same id (stupid, I know). To ensure searching for a unique identifier, we use the hash instead.
|
||||
idTypePairs.Add(dropdownItem.GetHashCode(), type);
|
||||
if(!idTypePairs.TryGetValue(dropdownItem.GetHashCode(), out var preExistingType))
|
||||
{
|
||||
idTypePairs.Add(dropdownItem.GetHashCode(), type);
|
||||
}
|
||||
else if(preExistingType.FullName != type.FullName) // type already exists, but it might be just the type itself (e.g. happens for me when using a ECS project)
|
||||
{
|
||||
Debug.LogError($"Could not add '{type.FullName}' to list, because it had a hash collision with: {idTypePairs[dropdownItem.GetHashCode()].FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,8 @@ namespace UnityAtoms.Editor
|
||||
public class AtomGenerator : ScriptableObject
|
||||
{
|
||||
[TextArea] public string FullQualifiedName;
|
||||
public string Namespace;
|
||||
public string BaseType;
|
||||
public string Namespace => string.IsNullOrWhiteSpace(FullQualifiedName) ? "" : Type.GetType(FullQualifiedName)?.Namespace;
|
||||
public string BaseType => string.IsNullOrWhiteSpace(FullQualifiedName) ? "" : Type.GetType(FullQualifiedName)?.Name;
|
||||
|
||||
public int GenerationOptions;
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityAtoms
|
||||
@ -35,17 +38,53 @@ namespace UnityAtoms
|
||||
|
||||
private Queue<T> _replayBuffer = new Queue<T>();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Set of all AtomVariable instances in editor.
|
||||
/// </summary>
|
||||
private static HashSet<AtomEvent<T>> _instances = new HashSet<AtomEvent<T>>();
|
||||
#endif
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (EditorSettings.enterPlayModeOptionsEnabled)
|
||||
{
|
||||
_instances.Add(this);
|
||||
|
||||
EditorApplication.playModeStateChanged -= HandlePlayModeStateChange;
|
||||
EditorApplication.playModeStateChanged += HandlePlayModeStateChange;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private static void HandlePlayModeStateChange(PlayModeStateChange state)
|
||||
{
|
||||
if (state == PlayModeStateChange.ExitingEditMode)
|
||||
{
|
||||
foreach (var instance in _instances)
|
||||
{
|
||||
instance._replayBuffer.Clear();
|
||||
instance.UnregisterAll();
|
||||
}
|
||||
}
|
||||
else if (state == PlayModeStateChange.EnteredPlayMode)
|
||||
{
|
||||
foreach (var instance in _instances)
|
||||
{
|
||||
instance._replayBuffer.Clear();
|
||||
instance.UnregisterAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// Clear all delegates when exiting play mode
|
||||
if (_onEvent != null)
|
||||
{
|
||||
var invocationList = _onEvent.GetInvocationList();
|
||||
foreach (var d in invocationList)
|
||||
{
|
||||
_onEvent -= (Action<T>)d;
|
||||
}
|
||||
}
|
||||
UnregisterAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -14,6 +14,7 @@ namespace UnityAtoms.FSM
|
||||
{
|
||||
GameObject go = new GameObject("FiniteStateMachineUpdateHook");
|
||||
_instance = go.AddComponent<FiniteStateMachineMonoHook>();
|
||||
DontDestroyOnLoad(go);
|
||||
}
|
||||
|
||||
return _instance;
|
||||
|
@ -2,12 +2,14 @@
|
||||
|
||||
- Introduction
|
||||
- [Installation](./introduction/installation.md)
|
||||
- [Overview and philosopy](./introduction/overview.md)
|
||||
- [Philosophy](./introduction/philosophy.md)
|
||||
- [Overview](./introduction/overview.md)
|
||||
- [Preferences](./introduction/preferences.md)
|
||||
- [FAQ](./introduction/faq.md)
|
||||
- Tutorials
|
||||
- [Creating Atoms](./tutorials/creating-atoms.md)
|
||||
- [Variables](./tutorials/variables.md)
|
||||
- [Variable Pre Change Transformers](./tutorials/variable-transformers.md)
|
||||
- [Events](./tutorials/events.md)
|
||||
- [Listeners](./tutorials/listeners.md)
|
||||
- [Actions](./tutorials/actions.md)
|
||||
|
@ -101,4 +101,4 @@ To dive right in, create your first Atom using any of the available techniques m
|
||||
|
||||
## Learn more
|
||||
|
||||
Go to [Overview and philosopy](./overview.md) to learn more about Unity Atoms and how to tune in to the correct mindset when using them.
|
||||
Go to [overview ](./overview.md) to learn more about Unity Atoms and how to tune in to the correct mindset when using them.
|
||||
|
@ -1,17 +1,17 @@
|
||||
---
|
||||
id: overview
|
||||
title: Overview and philosopy
|
||||
title: Overview
|
||||
hide_title: true
|
||||
sidebar_label: Overview and philosopy
|
||||
sidebar_label: Overview
|
||||
---
|
||||
|
||||
# Overview and philosopy
|
||||
# Overview
|
||||
|
||||
This chapter outlines the theoretical concepts behind Unity Atoms. This knowledge helps you better understand a new way of thinking about data and state in your project.
|
||||
This chapter provides an overview of the building blocks and concepts of Unity Atoms. This knowledge helps you better understand a new way of thinking about data and state in your project.
|
||||
|
||||
## Fundamentals
|
||||
|
||||
Unity Atoms is an event based system that encourages the game to be as data-driven as possible. The four most fundamental parts of Unity Atoms are:
|
||||
Unity Atoms is an event based system that encourages the game to be as data-driven as possible. The five most fundamental building blocks of Unity Atoms are:
|
||||
|
||||
- Data
|
||||
- Events
|
||||
@ -119,4 +119,4 @@ A List is an array of Variables that is stored as a Scriptable Object. The Varia
|
||||
|
||||
### Collections
|
||||
|
||||
A collection is a set of Variables associated with a StringReference key and is stored as a Scriptable Object. The Variables stored in a Collection can be of different types.
|
||||
A collection is a set of Variables associated with a StringReference key and is stored as a Scriptable Object. The Variables stored in a Collection can be of different types.
|
31
docs/introduction/philosophy.md
Normal file
31
docs/introduction/philosophy.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
id: philosophy
|
||||
title: Philosophy
|
||||
hide_title: true
|
||||
sidebar_label: Philosophy
|
||||
---
|
||||
|
||||
# Philosophy
|
||||
|
||||
This page describes the foundation and philosophy of which Unity Atoms is built upon.
|
||||
|
||||
## ScriptableObject Architecture (SOA)
|
||||
[ScriptableObject](https://docs.unity3d.com/Manual/class-ScriptableObject.html) Architecture (SOA) is a design pattern that aims to facilitate scalable, maintainable, and testable game development using Unity. This concept was popularized by [Ryan Hipple in his 2017 GDC talk](https://www.youtube.com/watch?v=raQ3iHhE_Kk) where he introduced the concept of ScriptableObjects as the glue that makes it possible to create modular and data-driven systems within Unity.
|
||||
|
||||
## Pillars of SOA
|
||||
|
||||
There are 3 main pillars of SOA:
|
||||
|
||||
- **Modular** - Systems in your game should not be directly depdendent on each other. Hard references between systems makes them less flexible and harder to reiterate on and reassemble. To make systems that are modular, it's a good rule to keep scenes as clean slates, meaning that we avoid `DontDestoryOnLoad` objects as much as possible. Furthermore, prefabs should as much as possible work on their own without any manager or other systems needed in the scene. Last but not least, Unity is a component based engine, create and use components that do one thing and one thing only.
|
||||
- **Editable** - The systems you create should be data-driven, eg. take data as input and process that data. The input and output of those systems should be editable and this is achieved by utilizing Unity's inspector. This approach allows us to change the game without changing code. Writing editable and with modular systems also allows us to reassemble systems in different ways, which is a great way to iterate on new game mechanics. Lastly, being editable means that we can change stuff at runtime, which greatly reduces iteration times.
|
||||
- **Debuggable** - If your systems are modular the easier they are to test in isolation. Furtermore, editable systems naturally provides debug views where you can see the data that is being processed.
|
||||
|
||||
## How SOA achieves its goals
|
||||
|
||||
- **Data** - Instead of hard coding data within our code, we externalize the data using ScriptableObjects. This allows for the separation of data and logic, making it easier to manage data and reuse it in different parts of the game.
|
||||
- **Events** - We use ScriptableObjects as events. This allows us to broadcast data across different parts of our game without having to know who is listening.
|
||||
|
||||
## UnityAtoms: A SOA implementation
|
||||
UnityAtoms offers a robust implementation of the SOA. It's an extension of the ScriptableObject system, with a focus on making it easier to create and manage ScriptableObject-based code. It provides Variables, Instancers, Collections, Events, Actions, and Conditions, among others, which can be combined to implement complex game logic. Furthermore, it provides additional features built on top of the SOA architecture like FSM, Tagging, Input System and more.
|
||||
|
||||
SOA and UnityAtoms is a great way to structure your Unity projects, providing a more maintainable, scalable, and testable codebase. The modular nature of this architecture allows for easy expansion of your game, ensuring that your codebase remains manageable as your game grows in complexity.
|
@ -7,4 +7,58 @@ sidebar_label: Tags
|
||||
|
||||
# Unity Atoms / Tags
|
||||
|
||||
A replacement to Unity´s tags based on Unity Atoms.
|
||||
A replacement to Unity's tags based on Unity Atoms.
|
||||
|
||||
The **Tags** subpackage of Unity Atoms is a powerful tool that enhances Unity's existing tag functionality by allowing for dynamic and scriptable tag operations.
|
||||
|
||||
## Using the Tags subpackage
|
||||
|
||||
### Adding and removing tags from a GameObject
|
||||
|
||||
To assign a tag to a GameObject, use the `AtomTags` component that comes with the AtomTags subpackage. The easiest way is to add the Tags in the Inspector, but it's also possible to add/remove tags at runtime using `StringConstants`:
|
||||
|
||||
Here's an example:
|
||||
```
|
||||
StringConstant myTag;
|
||||
|
||||
...
|
||||
|
||||
Tag tagComponent = gameObject.AddComponent<AtomTags>();
|
||||
tagComponent.AddTag(myTag);
|
||||
tagComponent.RemoveTag(myTag.Value);
|
||||
```
|
||||
### Get tags of an GameObjects
|
||||
|
||||
To see if a GameObject has a specific tag:
|
||||
```
|
||||
GameObject go;
|
||||
...
|
||||
go.HasTag("a");
|
||||
```
|
||||
|
||||
or any tag within a list:
|
||||
|
||||
```
|
||||
go.HasAnyTag(new List<string>() { "d", "f", "h", "j", "l" });
|
||||
```
|
||||
|
||||
Or to get all the tags a gameobject has:
|
||||
|
||||
```
|
||||
AtomTags.GetTagsForGameObject(go);
|
||||
```
|
||||
|
||||
### Querying GameObjects by Tags
|
||||
|
||||
To find all GameObjects with a specific tag, use the `FindObjectsWithTag` method:
|
||||
|
||||
```
|
||||
IEnumerable<GameObject> enemies = AtomTags.FindByTag("Enemy");
|
||||
```
|
||||
|
||||
This will return all GameObjects with the tag "Enemy".
|
||||
|
||||
## Performance
|
||||
|
||||
The Tag packages is fine tuned to reduce heap allocations as much as possible. Some results are discussed [here](https://github.com/unity-atoms/unity-atoms/pull/12#issuecomment-470656166).
|
||||
|
||||
|
@ -11,6 +11,6 @@ There are several ways of creating Atoms in your project. The recommended way is
|
||||
|
||||
![creating-atoms](../assets/creating-atoms/atoms-creation.gif)
|
||||
|
||||
Another way to create Atoms is to select `Unity Atoms` in the context menu. The items are listed by category and by type. Custom Atom types created using the `Generator` (introduced by a later tutorial) also appear in this menu.
|
||||
Another way to create Atoms is to select `Unity Atoms` in the context menu. The items are listed by category and by type. Custom Atom types created using the `Generator` also appear in this menu (see the dedicated [generator tutorial](./generator.md) for more info on the generator).
|
||||
|
||||
In the following tutorials this is simply referred to as "creating an Atom" using any of the above mentioned techniques.
|
||||
|
54
docs/tutorials/variable-transformers.md
Normal file
54
docs/tutorials/variable-transformers.md
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
id: variable-transformers
|
||||
title: Variable Pre Change Transformers
|
||||
hide_title: true
|
||||
sidebar_label: Variable Pre Change Transformers
|
||||
---
|
||||
|
||||
# Variable Pre Change Transformers
|
||||
|
||||
Variable pre change transformers provide a way to modify the value of a Variable before it gets changed. These are used to introduce customized logic into the process of updating the value of an Variable. For example, a pre change transformer could be used to limit a player's health within certain boundaries, such as ensuring that it never drops below zero.
|
||||
|
||||
In this tutorial, we'll look at how to use a pre change transformer in UnityAtoms.
|
||||
|
||||
## Step 1: Create a Pre Change Transformer
|
||||
|
||||
Firstly, we need to create a new class for our pre change transformer. Let's say we are going to create a transformer that ensures the health of our player never falls below 0.
|
||||
|
||||
Create a new C# script and name it `MinHealthTransformer`. This script should extend `IntIntFunction` and override the `Call` method:
|
||||
|
||||
```
|
||||
using UnityAtoms.BaseAtoms;
|
||||
|
||||
[EditorIcon("atom-icon-sand")]
|
||||
[CreateAssetMenu(menuName = "Unity Atoms/Functions/Transformers/MinHealth Int (int => int)", fileName = "MinHealth")]
|
||||
public class MinHealthTransformer : IntIntFunction
|
||||
{
|
||||
public override int Call(int value)
|
||||
{
|
||||
if (value < 0)
|
||||
return 0;
|
||||
else
|
||||
return value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the `Call` method, we're checking if the new value of health is less than 0. If so, we're returning 0 instead of the new value. This ensures the player's health never drops below 0.
|
||||
|
||||
## Step 2: Use the Pre Change Transformer
|
||||
|
||||
To use this transformer, we need to create a ScriptableObject of this type.
|
||||
|
||||
In the Unity Editor:
|
||||
|
||||
1. Right-click in the project window and choose `Create > UnityAtoms > Functions > Transformers > MinHealth Int (int => int)`.
|
||||
2. Name the Object `MinHealthTransformer`.
|
||||
3. In the inspector window, set the `Pre Change Transform` to be `MinHealthTransformer`.
|
||||
|
||||
Now, whenever the `PlayerHealth` value is changed, the `MinHealthTransformer`'s `PreChangeCheck` method will be called, ensuring the health never drops below 0.
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
As the name suggests, pre change transformers are executed _before_ the change events are invoked.
|
@ -7,7 +7,7 @@ sidebar_label: Variables
|
||||
|
||||
# Variables, Constants, and References
|
||||
|
||||
Below follows a step-by-step example of managing a player's health using Unity Atoms. If you haven't read the [Creating Atoms](./creating-atoms.md) and [Overview and philosopy](../introduction/overview.md) section you should do that before proceeding.
|
||||
Below follows a step-by-step example of managing a player's health using Unity Atoms. If you haven't read the [Creating Atoms](./creating-atoms.md), [Philosophy](../introduction/philosophy.md) and [Overview](../introduction/overview.md) section you should do that before proceeding.
|
||||
|
||||
_NOTE: This tutorial is based on [this](https://medium.com/@adamramberg/unity-atoms-tiny-modular-pieces-utilizing-the-power-of-scriptable-objects-e8add1b95201) blog post. The blog post is based on a previous version of Unity Atoms and as such will vary in content and appearance. The ideas presented by the blog post still apply._
|
||||
|
||||
|
@ -14,8 +14,12 @@
|
||||
"sidebar_label": "Installation"
|
||||
},
|
||||
"introduction/overview": {
|
||||
"title": "Overview and philosopy",
|
||||
"sidebar_label": "Overview and philosopy"
|
||||
"title": "Overview",
|
||||
"sidebar_label": "Overview"
|
||||
},
|
||||
"introduction/philosophy": {
|
||||
"title": "Philosophy",
|
||||
"sidebar_label": "Philosophy"
|
||||
},
|
||||
"introduction/preferences": {
|
||||
"title": "Preferences",
|
||||
@ -100,6 +104,10 @@
|
||||
"title": "Variable Instancer",
|
||||
"sidebar_label": "Variable Instancer"
|
||||
},
|
||||
"tutorials/variable-transformers": {
|
||||
"title": "Variable Pre Change Transformers",
|
||||
"sidebar_label": "Variable Pre Change Transformers"
|
||||
},
|
||||
"tutorials/variables": {
|
||||
"title": "Variables",
|
||||
"sidebar_label": "Variables"
|
||||
|
@ -2,6 +2,7 @@
|
||||
"docs": {
|
||||
"Introduction": [
|
||||
"introduction/installation",
|
||||
"introduction/philosophy",
|
||||
"introduction/overview",
|
||||
"introduction/preferences",
|
||||
"introduction/faq"
|
||||
@ -23,6 +24,7 @@
|
||||
"type": "subcategory",
|
||||
"label": "Intermediate",
|
||||
"ids": [
|
||||
"tutorials/variable-transformers",
|
||||
"tutorials/variable-instancer",
|
||||
"tutorials/event-instancer",
|
||||
"tutorials/generator",
|
||||
|
Loading…
Reference in New Issue
Block a user