├── .gitignore ├── AssetLoading └── README.md ├── Backlog └── README.md ├── BaseBuilding ├── README-zh-TW.md └── README.md ├── CleanCode └── README.md ├── ContinuousIntegration ├── Assets │ ├── Scenes.meta │ ├── Scenes │ │ ├── SampleScene.unity │ │ └── SampleScene.unity.meta │ ├── Scripts.meta │ ├── Scripts │ │ ├── Editor.meta │ │ └── Editor │ │ │ ├── UnitTests.meta │ │ │ └── UnitTests │ │ │ ├── TestAssetBundles.cs │ │ │ ├── TestAssetBundles.cs.meta │ │ │ ├── TestShaders.cs │ │ │ └── TestShaders.cs.meta │ ├── Shaders.meta │ └── Shaders │ │ ├── DemoShader.shader │ │ └── DemoShader.shader.meta ├── BuildScripts │ ├── Jenkins │ │ └── Jenkinsfile │ └── Steam │ │ ├── app_build.vdf │ │ ├── depot_build_1.vdf │ │ └── depot_build_2.vdf ├── Documentation │ ├── Jenkins1.png │ ├── Jenkins2.png │ ├── Pipeline1.png │ ├── Pipeline2.png │ └── Pipeline3.png ├── Packages │ └── manifest.json ├── ProjectSettings │ ├── AudioManager.asset │ ├── ClusterInputManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── NavMeshAreas.asset │ ├── NetworkManager.asset │ ├── Physics2DSettings.asset │ ├── PresetManager.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── UnityConnectSettings.asset │ └── VFXManager.asset ├── README-zh-TW.md └── README.md ├── Decoupling ├── Assets │ ├── Data.meta │ ├── Data │ │ ├── PlayerHealth.asset │ │ ├── PlayerHealth.asset.meta │ │ ├── PlayerMana.asset │ │ └── PlayerMana.asset.meta │ ├── Materials.meta │ ├── Materials │ │ ├── Concrete.mat │ │ ├── Concrete.mat.meta │ │ ├── Jeans.mat │ │ ├── Jeans.mat.meta │ │ ├── Skin.mat │ │ └── Skin.mat.meta │ ├── Prefabs.meta │ ├── Prefabs │ │ ├── Dungeon.prefab │ │ ├── Dungeon.prefab.meta │ │ ├── Player.prefab │ │ ├── Player.prefab.meta │ │ ├── ProgressBar.prefab │ │ ├── ProgressBar.prefab.meta │ │ ├── UI.prefab │ │ └── UI.prefab.meta │ ├── Scenes.meta │ ├── Scenes │ │ ├── Game.unity │ │ └── Game.unity.meta │ ├── Scripts.meta │ ├── Scripts │ │ ├── BoundedFloat.cs │ │ ├── BoundedFloat.cs.meta │ │ ├── Player.cs │ │ ├── Player.cs.meta │ │ ├── StatusBar.cs │ │ └── StatusBar.cs.meta │ ├── csc.rsp │ └── csc.rsp.meta ├── Documentation │ ├── Decoupling.dot │ ├── Decoupling.png │ ├── Dependencies.dot │ ├── Dependencies.png │ ├── HealthBar.png │ ├── HealthBar2.png │ └── ScriptableObjects.png ├── Packages │ └── manifest.json ├── ProjectSettings │ ├── AudioManager.asset │ ├── ClusterInputManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── NavMeshAreas.asset │ ├── Physics2DSettings.asset │ ├── PresetManager.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── UnityConnectSettings.asset │ ├── VFXManager.asset │ └── XRSettings.asset ├── README-zh-TW.md └── README.md ├── GameMaker ├── README-zh-TW.md └── README.md ├── GitHubEnterprise └── README.md ├── LICENSE ├── README-zh-TW.md ├── README.md └── StateMachines ├── Assets ├── Data.meta ├── Data │ ├── JumpController1.controller │ ├── JumpController1.controller.meta │ ├── JumpController2.controller │ ├── JumpController2.controller.meta │ ├── JumpController3.controller │ ├── JumpController3.controller.meta │ ├── JumpController4.controller │ ├── JumpController4.controller.meta │ ├── JumpController5.controller │ ├── JumpController5.controller.meta │ ├── Platform.anim │ ├── Platform.anim.meta │ ├── Platform.controller │ └── Platform.controller.meta ├── Plugins.meta ├── Scenes.meta ├── Scenes │ ├── SampleScene.unity │ └── SampleScene.unity.meta ├── Scripts.meta ├── Scripts │ ├── JumpControllerMessageHandler.cs │ ├── JumpControllerMessageHandler.cs.meta │ ├── JumpControllerParameterProvider.cs │ ├── JumpControllerParameterProvider.cs.meta │ ├── Player.cs │ ├── Player.cs.meta │ ├── SendMessageState.cs │ └── SendMessageState.cs.meta ├── csc.rsp └── csc.rsp.meta ├── Documentation ├── CoyoteTime.png ├── DoubleJump.png ├── JumpController.png ├── JumpController2.png ├── SendMessage.png ├── StateMachine.dot └── StateMachine.png ├── Packages ├── manifest.json └── packages-lock.json ├── ProjectSettings ├── AudioManager.asset ├── ClusterInputManager.asset ├── DynamicsManager.asset ├── EditorBuildSettings.asset ├── EditorSettings.asset ├── GraphicsSettings.asset ├── InputManager.asset ├── NavMeshAreas.asset ├── NetworkManager.asset ├── Physics2DSettings.asset ├── PresetManager.asset ├── ProjectSettings.asset ├── ProjectVersion.txt ├── QualitySettings.asset ├── TagManager.asset ├── TimeManager.asset ├── UnityConnectSettings.asset ├── VFXManager.asset └── XRSettings.asset ├── README-zh-TW.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | [Bb]uild/ 5 | [Bb]uilds/ 6 | [Ll]ogs/ 7 | Assets/AssetStoreTools* 8 | 9 | # Visual Studio cache directory 10 | .vs/ 11 | 12 | # Autogenerated VS/MD/Consulo solution and project files 13 | ExportedObj/ 14 | .consulo/ 15 | *.csproj 16 | *.unityproj 17 | *.sln 18 | *.suo 19 | *.tmp 20 | *.user 21 | *.userprefs 22 | *.pidb 23 | *.booproj 24 | *.svd 25 | *.pdb 26 | *.opendb 27 | 28 | # Unity3D generated meta files 29 | *.pidb.meta 30 | *.pdb.meta 31 | 32 | # Unity3D Generated File On Crash Reports 33 | sysinfo.txt 34 | 35 | # Builds 36 | *.apk 37 | *.unitypackage 38 | 39 | # Project 40 | /StateMachines/Assets/Plugins/ 41 | -------------------------------------------------------------------------------- /AssetLoading/README.md: -------------------------------------------------------------------------------- 1 | # Asset loading 2 | Scenes, prefabs, Resources, StreamingAssets, AssetBundles, Addressables. What to use when and why? 3 | 4 | # Problem 5 | There are four ways in Unity to load an asset into memory. 6 | 1. `SceneManager.LoadScene` 7 | 2. `Resources.Load` 8 | 3. `AssetBundle.LoadAsset` 9 | 4. `Addressables.LoadAsset` 10 | 11 | There are three ways to unload an asset from memory. 12 | 1. Load a different scene. 13 | 2. `Resources.UnloadUnusedAssets` 14 | 3. `AssetBundle.Unload` 15 | 16 | The [documentation](https://docs.unity3d.com/Manual/LoadingResourcesatRuntime.html) tells us _what_ those methods do, but it does not tell us which one we should use. There are plenty of [questions](https://forum.unity.com/threads/resource-vs-addressable-for-memory-management.836863/) and discussions all over the internet trying to find these answers, but they either go nowhere or reach a dubious conclusion. How do we know who is right? 17 | 18 | # Solution 19 | We can look into Unity's asset memory system in detail using the [memory profiler package](https://docs.unity3d.com/Packages/com.unity.memoryprofiler@0.4/manual/index.html) to find out how things are _really_ working. We can use that knowledge to assess the advantages and disadvantages of the different ways of loading. Given that, we can assess which way is appropriate for our use case. 20 | 21 | # Memory profiler 22 | Memory profiler screenshot here. 23 | What is an asset? 24 | What is not an asset? 25 | 26 | # Loading 27 | 28 | ## Scenes 29 | `SceneManager.LoadScene` loads all assets referenced by the scene. 30 | Implicit references. 31 | Recursive dependencies. 32 | Prefabs. 33 | ScriptableObjects. 34 | Asset packing. 35 | Scene bundle. 36 | Asset deduplication. 37 | Shared assets. 38 | Additive scene loading. 39 | 40 | ## Resources 41 | `Resources.Load` loads an asset in a "Resources" directory by name. 42 | Resources bundle. 43 | Asset deduplication between resources and scenes. 44 | Shared assets. 45 | 46 | ## StreamingAssets 47 | Has nothing to do with anything, despite its name. 48 | Put your built asset bundles here. 49 | 50 | ## AssetBundles 51 | `AssetBundle.LoadAsset` loads an asset from an asset bundle. 52 | `AssetBundle.Load` first. 53 | Asset deduplication between bundles. 54 | Asset deduplication between bundles and scenes. 55 | Scene asset bundles. 56 | Asset deduplication between bundles and resources. 57 | 58 | ## Addressables 59 | `Addressables.LoadAsset` loads an asset by address. 60 | Implicitly loads bundle. 61 | Reference counting. 62 | Handles. 63 | `Addressables.InstantiateAsync`. 64 | Asset deduplication between groups. 65 | Asset deduplication between groups and scenes. 66 | Addressable scenes. 67 | Asset deduplication between groups and resources. 68 | Asset deduplication between groups and bundles. 69 | 70 | # Unloading 71 | 72 | ## Unloading the scene 73 | Load a different scene. 74 | `DontDestroyOnLoad`. 75 | `SceneManager.UnloadScene` does not unload assets. 76 | 77 | ## Unloading unused assets 78 | `Resources.UnloadUnusedAssets`. 79 | What does "unused" mean? 80 | Addressables vs. `UnloadUnusedAssets`. 81 | 82 | ## Unloading asset bundles 83 | `AssetBundle.Unload`. 84 | Force vs. no force. 85 | Broken references. 86 | Broken asset links. 87 | Asset duplication in memory. 88 | 89 | ## Unloading Addressables 90 | Reference counting. 91 | `UnloadAsset`. 92 | `Release`. 93 | Group unloading. 94 | Addressables profiler. 95 | 96 | # Analysis 97 | Scenes easiest. 98 | Resources easy. Does not scale. 99 | AssetBundles must die. 100 | Addressables finicky. 101 | 102 | # Use cases 103 | 104 | ## No dynamic content 105 | Use scenes. 106 | 107 | ## Dynamic loading 108 | Use `Resources`. 109 | 4GB limit. 110 | Use scene transitions with loading screen to unload. 111 | Don't roll your own asset library system. 112 | 113 | ## Streaming 114 | Use Addressables. 115 | Careful with those handles. 116 | Careful with packing. 117 | 118 | ## Content updates 119 | Hosted asset bundles. Smaller app size. Frequent content updates. 120 | You don't have those. Just update the app including the content. 121 | 122 | ## Patching 123 | 500MB patch limit on Switch. 124 | Asset packing. 125 | Incremental AssetBundles. 126 | Incremental Addressables. 127 | 128 | # Further reading 129 | 130 | # Translations 131 | 132 | - [台灣繁體中文 (zh-TW)](README-zh-TW.md) 133 | 134 | If you find this workshop useful and speak another language, I'd very much appreciate any help translating the chapters. Clone the repository, add a localized copy of the README.md, for example README-pt-BR.md, and send me a pull request. 135 | -------------------------------------------------------------------------------- /Backlog/README.md: -------------------------------------------------------------------------------- 1 | # Backlog 2 | 3 | ## Tricks 4 | - Animator instead of state machine 5 | - Timeline instead of lerp 6 | 7 | ## Decoupling 8 | - ~~ScriptableObject instead of Singleton~~ 9 | - Runtime sets (observer) 10 | 11 | ## Correctness 12 | - ScriptableObject instead of CSV, json, etc. 13 | - Required attribute 14 | - Broken reference check 15 | - Code reviews 16 | - Tests and simulations 17 | - Refactoring 18 | - Parallel implementations 19 | 20 | ## Animation 21 | - Timeline 22 | - Animator 23 | 24 | ## Debugging 25 | - Custom Inspectors 26 | - Animator as state machine inspector 27 | - Gizmos 28 | 29 | ## Profiling 30 | - Profiler 31 | - Memory Profiler 32 | - GC alloc 33 | - Profiling statements 34 | 35 | ## Optimization 36 | - Data locality 37 | - Data oriented design 38 | - Moving static data out of prefabs (flyweight) 39 | - Debug stats on scriptable object (object pool tuning) 40 | - Double buffer 41 | - Schedulers 42 | - Leaky bucket 43 | - DOTS 44 | 45 | ## Unsorted 46 | - Game design patterns 47 | - Collaboration 48 | - Procedural generation 49 | - Machine learning 50 | - Shaders 51 | - Universal render pipeline 52 | - Post processing effects 53 | - Mentoring 54 | - UML 55 | - Serializable attribute 56 | - Hot reload 57 | - sealed keyword 58 | - ContextMenu attribute 59 | - TRANSFORM context menu 60 | 61 | ## CI 62 | 63 | ## Method 64 | Verify project automatically. 65 | 66 | ## Prerequisites 67 | - Version control 68 | - Jenkins 69 | - Automated tests 70 | - Unit test 71 | - Asset test 72 | - Integration test 73 | - Build server 74 | - PostProcessorBuild attribute 75 | 76 | ## Other 77 | - SteamPipe 78 | - F8 feedback 79 | - Analytics 80 | - Sentry 81 | 82 | ## Details 83 | Manage Jenkins -> Global Tool Configuration -> Git 84 | https://stackoverflow.com/questions/51500698/where-to-find-option-to-change-workspace-path-location-in-jenkins 85 | Actually has to modify config.xml in the Jenkins install dir. See workspaceDir element. 86 | Better yet, add a local agent. Start like Launch agent via execution of command on the master c:\Program Files (x86)\Jenkins\jre\bin\java -jar W:\Jenkins\agent.jar 87 | 88 | ## Notes 89 | 1. Install Jenkins from .msi (doesn't ask any options) 90 | 2. Open localhost:8080 91 | 3. Unlock with initial admin password (follow instructions on screen) 92 | 4. Select plugins to install (more later) 93 | 5. Select none (top), select GitHub, GitHub Authentication (doesn't work?!), Pipeline, install. 94 | 6. Skip creating first admin user. Continue as admin (bottom). 95 | 7. Accept default instance configuration (http://localhost:8080, no https!). Save and finish. 96 | 8. Manage Jenkins -> Configure System -> Usage: ony build jobs with label expressions matching this node 97 | 98 | 9. Manage Jenkins -> Global Security -> Agents -> TCP port for inbound agents: random 99 | 10. Manage Jenkins -> Manage Nodes -> New Node (name Agent1, permanent agent, keep defaults) 100 | 11. Manage Jenkins -> Manage Nodes -> Agent1 -> agent.jar save file. 101 | 12. Create batch file "c:\Program Files (x86)\Jenkins\jre\bin\java" -jar agent.jar -jnlpUrl http://localhost:8080/computer/Agent1/slave-agent.jnlp -secret 7a729911603e5918b83052a24e7258dc26ad85359831d6e040d0999f5de5eb8b 102 | 13. Install as service... 103 | 104 | 14. Manage Jenkins -> Global Tool Configuration -> Git -> Delete -> Add Git -> JGit 105 | 15. Add jobs... 106 | 107 | ## TODO: 108 | - add broken reference check 109 | - jgit doesn't support LFS 110 | - consider installing with standard plugins to simplify things 111 | - git lfs checkout 112 | - undo changes from previous build without deleting library folder 113 | - delete build output 114 | - upload to steam 115 | - download git on the agent https://git-scm.com/download/win 116 | -- select Git LFS when installing! 117 | - use JGit on the master to download the latest jenkinsfile 118 | - gotta use a multibranch pipeline for github. blue ocean does a decent job creating it, but we have to manually switch to jenkinsfile and remove clean before/after checkout. 119 | 120 | ## See also 121 | [Unity Cloud Build](https://unity3d.com/unity/features/cloud-build) does not work with consoles. 122 | 123 | 124 | ## Further reading 125 | - [Continuous Integration](https://martinfowler.com/articles/continuousIntegration.html) by Martin Fowler 126 | 127 | ## References 128 | - https://martinfowler.com/articles/continuousIntegration.html 129 | - https://jenkins.io/blog/2017/04/05/welcome-to-blue-ocean/ 130 | - https://jenkins.io/blog/2017/04/06/welcome-to-blue-ocean-editor/ 131 | - https://jenkins.io/blog/2017/04/11/welcome-to-blue-ocean-pipeline-activity/ 132 | - https://jenkins.io/blog/2017/04/12/welcome-to-blue-ocean-dashboard/ 133 | 134 | - [Unite 2015 - Continuous Integration with Unity](https://www.youtube.com/watch?v=kSXomLkMR68) 135 | - [Continuous integration and automated testing](http://itmattersgames.com/2019/02/18/continuous-integration-and-automated-testing/) 136 | - [Unity Build Automation with Jenkins](https://smashriot.com/unity-build-automation-with-jenkins/) 137 | - [Jenkins for Unity](https://github.com/CarlHalstead/Jenkins-for-Unity) 138 | - [Unity build automation with Jenkins](https://benhoffman.tech/general/2018/07/12/unity-build-automation-with-jenkins.html) 139 | 140 | 141 | - [Setting Up a Build Server for Unity with Jenkins](https://www.youtube.com/watch?v=4J3SmhGxO1Y) 142 | -------------------------------------------------------------------------------- /BaseBuilding/README-zh-TW.md: -------------------------------------------------------------------------------- 1 | # 基地建造系統實作挑戰 2 | 使用 Unity 來製作一個基地建造系統原型。 3 | 4 | 使用者要可以利用房間與走廊物件來建立各種不同結構的基地。可參考《[Subnautica](https://www.youtube.com/watch?v=Q-oa3bzir9A)》與《[No Man's Sky](https://www.youtube.com/watch?v=hpsSJrh5WzY&t=165)》的基地建造方式來汲取靈感。 5 | 6 | 這個問題的答案是開放式的,沒有絕對正確或錯誤的做法。技術或想法的運用請依照自己的認知來自由發揮。 7 | 8 | ## 遊戲玩法概觀 9 | 使用者可以建造、拆除、進入並在基地內部移動。 10 | 11 | ## 建造元件 12 | 使用者可以使用兩種基礎的建造元件: 13 | - 每面牆上都有門的房間 14 | - 兩端有門的走廊 15 | 16 | ## 建造流程需求 17 | 使用者可以將元件設置在地表,或者銜接到既有的基地上。元件相連時會自動對齊。相連的元件中間不會有門,而是自動連通。 18 | 19 | 元件禁止擺放成交疊的狀態。系統會有視覺回饋給使用者來提示目前的元件擺放方式是否合法。 20 | 21 | # 挑戰提交方式 22 | 若想要參與本挑戰,請提供我你所實作的原型版本的 GitHub 連結。參與挑戰即代表同意讓程式碼在公開的場合被拿出來討論,本挑戰目的是希望所有人在過程中都能學到一些東西。 23 | 24 | # 解法程式討論 25 | 討論過程會在 [IGDShare Discord](https://discord.gg/FNkHgBN) 上直播進行,並預定錄影下來供未來參考。 26 | 27 | # 翻譯 28 | 如果你覺得這個工作坊有其價值,並通曉另一個語言,我們非常歡迎任何幫助工作坊內容進行翻譯的協助。把本儲存庫內容 clone 下來後,增加一份特定語言在地化的 README.md,例如 README-pt-BR.md,並送 PR 給我們。 -------------------------------------------------------------------------------- /BaseBuilding/README.md: -------------------------------------------------------------------------------- 1 | # Base building challenge 2 | Create a base building prototype in Unity. 3 | 4 | Users should be able to build a base from rooms and corridors in a variety of layouts. Have a look at [base building in Subnautica](https://www.youtube.com/watch?v=Q-oa3bzir9A) as well as [base building in No Man's Sky](https://www.youtube.com/watch?v=hpsSJrh5WzY&t=165) for inspiration. 5 | 6 | The problem is open-ended by design. There is no right or wrong approach. Feel free to make technical and creative decisions that suit you. 7 | 8 | ## Gameplay 9 | The user is able to construct, deconstruct, enter, and walk around inside bases. 10 | 11 | ## Building blocks 12 | The user can choose between two basic building blocks. 13 | - Room with a door on each side 14 | - Corridor with a door on each end 15 | 16 | ## Construction process 17 | The user can place building blocks on the ground or attach them to an existing base. Pieces snap together when they connect. Connecting one piece to another removes any doors between them. 18 | 19 | It is impossible to place pieces so that they intersect each other. The user is given visual feedback on whether or not the placement of a piece in a particular position is allowed. 20 | 21 | # Submissions 22 | In order to participate, send me the link to the GitHub repository of your solution. By participating you agree to have your code reviewed by me in front of an audience. The goal is for all of us to learn something in the process. 23 | 24 | # Code reviews 25 | Code review sessions will be broadcast live in the [IGDShare Discord](https://discord.gg/FNkHgBN) and hopefully recorded on YouTube for future reference. 26 | 27 | # Translations 28 | - [台灣繁體中文 (zh-TW)](README-zh-TW.md) 29 | 30 | If you find this workshop useful and speak another language, I'd very much appreciate any help translating the chapters. Clone the repository, add a localized copy of the `README.md`, for example `README-pt-BR.md`, and send me a pull request. 31 | -------------------------------------------------------------------------------- /CleanCode/README.md: -------------------------------------------------------------------------------- 1 | # Clean code for Unity 2 | An attempt to list what works well and what doesn't. Mostly scars. 3 | 4 | # Platform specific code 5 | ```csharp 6 | #if UNITY_PS4 7 | ``` 8 | 9 | ```csharp 10 | if (Application.isConsolePlatform) 11 | ``` 12 | 13 | Avoid platform specific code. If you absolutely must have platform specific code, because you need call APIs only available on that platform, build a platform abstraction layer. Chances are other platforms will have a very similar feature under a slightly different API. Aim to expose a unified cross platform API that hides all the platform specific calls away. Use `asmdef`s instead of preprocessor directives. 14 | 15 | # Editor only code 16 | ```csharp 17 | #if UNITY_EDITOR 18 | ``` 19 | ```csharp 20 | if (Application.isEditor) 21 | ``` 22 | Avoid editor only code in runtime scripts. Put all your editor code in `Editor` folders so that it doesn't get compiled into the standalone player. No exceptions! 23 | 24 | # Play mode only code 25 | ```csharp 26 | if (Application.isPlaying) 27 | ``` 28 | Put runtime code in runtime scripts. Put editor code in `Editor` scripts. 29 | 30 | # Edit mode only code 31 | ```csharp 32 | if (!Application.isPlaying) 33 | ``` 34 | Put runtime code in runtime scripts. Put editor code in `Editor` scripts. Seriously. 35 | 36 | # Execute in edit mode 37 | Just no. Use `[ContextMenu]` if you need to run a runtime function in edit mode. But do you really? Think long and hard about moving the code into a proper editor window. 38 | 39 | # Mutable static data 40 | Debugging nightmare. Use `ScriptableObjects` instead. 41 | 42 | # Singletons 43 | Dependency hell. Use `ScriptableObjects` instead. 44 | 45 | # Formatting 46 | No tabs. Indent using four spaces. No trailing spaces. Unix style line endings. Use ASCII characters only. Use exactly one empty new line at the end of the file. All of this is to reduce noise when looking at diffs or resolving merge conflicts. 47 | 48 | # Canonical file format 49 | If you produce any kind of custom files where order is not important, sort the data before writing to disk. If it's case-insensitive strings, convert all of them to lower case before writing to disk. It will make diffs and merge conflicts much easier to deal with. 50 | 51 | # ignore.conf 52 | Keep it sorted alphabetically. See canonical file format. 53 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 785efb4c46cdd674393dfa6a56ce3442 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scenes/SampleScene.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 170076734} 41 | m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} 42 | m_UseRadianceAmbientProbe: 0 43 | --- !u!157 &3 44 | LightmapSettings: 45 | m_ObjectHideFlags: 0 46 | serializedVersion: 11 47 | m_GIWorkflowMode: 0 48 | m_GISettings: 49 | serializedVersion: 2 50 | m_BounceScale: 1 51 | m_IndirectOutputScale: 1 52 | m_AlbedoBoost: 1 53 | m_TemporalCoherenceThreshold: 1 54 | m_EnvironmentLightingMode: 0 55 | m_EnableBakedLightmaps: 1 56 | m_EnableRealtimeLightmaps: 0 57 | m_LightmapEditorSettings: 58 | serializedVersion: 10 59 | m_Resolution: 2 60 | m_BakeResolution: 10 61 | m_AtlasSize: 512 62 | m_AO: 0 63 | m_AOMaxDistance: 1 64 | m_CompAOExponent: 1 65 | m_CompAOExponentDirect: 0 66 | m_Padding: 2 67 | m_LightmapParameters: {fileID: 0} 68 | m_LightmapsBakeMode: 1 69 | m_TextureCompression: 1 70 | m_FinalGather: 0 71 | m_FinalGatherFiltering: 1 72 | m_FinalGatherRayCount: 256 73 | m_ReflectionCompression: 2 74 | m_MixedBakeMode: 2 75 | m_BakeBackend: 1 76 | m_PVRSampling: 1 77 | m_PVRDirectSampleCount: 32 78 | m_PVRSampleCount: 256 79 | m_PVRBounces: 2 80 | m_PVRFilterTypeDirect: 0 81 | m_PVRFilterTypeIndirect: 0 82 | m_PVRFilterTypeAO: 0 83 | m_PVRFilteringMode: 1 84 | m_PVRCulling: 1 85 | m_PVRFilteringGaussRadiusDirect: 1 86 | m_PVRFilteringGaussRadiusIndirect: 5 87 | m_PVRFilteringGaussRadiusAO: 2 88 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 89 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 90 | m_PVRFilteringAtrousPositionSigmaAO: 1 91 | m_ShowResolutionOverlay: 1 92 | m_LightingDataAsset: {fileID: 0} 93 | m_UseShadowmask: 1 94 | --- !u!196 &4 95 | NavMeshSettings: 96 | serializedVersion: 2 97 | m_ObjectHideFlags: 0 98 | m_BuildSettings: 99 | serializedVersion: 2 100 | agentTypeID: 0 101 | agentRadius: 0.5 102 | agentHeight: 2 103 | agentSlope: 45 104 | agentClimb: 0.4 105 | ledgeDropHeight: 0 106 | maxJumpAcrossDistance: 0 107 | minRegionArea: 2 108 | manualCellSize: 0 109 | cellSize: 0.16666667 110 | manualTileSize: 0 111 | tileSize: 256 112 | accuratePlacement: 0 113 | debug: 114 | m_Flags: 0 115 | m_NavMeshData: {fileID: 0} 116 | --- !u!1 &170076733 117 | GameObject: 118 | m_ObjectHideFlags: 0 119 | m_CorrespondingSourceObject: {fileID: 0} 120 | m_PrefabInternal: {fileID: 0} 121 | serializedVersion: 6 122 | m_Component: 123 | - component: {fileID: 170076735} 124 | - component: {fileID: 170076734} 125 | m_Layer: 0 126 | m_Name: Directional Light 127 | m_TagString: Untagged 128 | m_Icon: {fileID: 0} 129 | m_NavMeshLayer: 0 130 | m_StaticEditorFlags: 0 131 | m_IsActive: 1 132 | --- !u!108 &170076734 133 | Light: 134 | m_ObjectHideFlags: 0 135 | m_CorrespondingSourceObject: {fileID: 0} 136 | m_PrefabInternal: {fileID: 0} 137 | m_GameObject: {fileID: 170076733} 138 | m_Enabled: 1 139 | serializedVersion: 8 140 | m_Type: 1 141 | m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} 142 | m_Intensity: 1 143 | m_Range: 10 144 | m_SpotAngle: 30 145 | m_CookieSize: 10 146 | m_Shadows: 147 | m_Type: 2 148 | m_Resolution: -1 149 | m_CustomResolution: -1 150 | m_Strength: 1 151 | m_Bias: 0.05 152 | m_NormalBias: 0.4 153 | m_NearPlane: 0.2 154 | m_Cookie: {fileID: 0} 155 | m_DrawHalo: 0 156 | m_Flare: {fileID: 0} 157 | m_RenderMode: 0 158 | m_CullingMask: 159 | serializedVersion: 2 160 | m_Bits: 4294967295 161 | m_Lightmapping: 1 162 | m_LightShadowCasterMode: 0 163 | m_AreaSize: {x: 1, y: 1} 164 | m_BounceIntensity: 1 165 | m_ColorTemperature: 6570 166 | m_UseColorTemperature: 0 167 | m_ShadowRadius: 0 168 | m_ShadowAngle: 0 169 | --- !u!4 &170076735 170 | Transform: 171 | m_ObjectHideFlags: 0 172 | m_CorrespondingSourceObject: {fileID: 0} 173 | m_PrefabInternal: {fileID: 0} 174 | m_GameObject: {fileID: 170076733} 175 | m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} 176 | m_LocalPosition: {x: 0, y: 3, z: 0} 177 | m_LocalScale: {x: 1, y: 1, z: 1} 178 | m_Children: [] 179 | m_Father: {fileID: 0} 180 | m_RootOrder: 1 181 | m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} 182 | --- !u!1 &534669902 183 | GameObject: 184 | m_ObjectHideFlags: 0 185 | m_CorrespondingSourceObject: {fileID: 0} 186 | m_PrefabInternal: {fileID: 0} 187 | serializedVersion: 6 188 | m_Component: 189 | - component: {fileID: 534669905} 190 | - component: {fileID: 534669904} 191 | - component: {fileID: 534669903} 192 | m_Layer: 0 193 | m_Name: Main Camera 194 | m_TagString: MainCamera 195 | m_Icon: {fileID: 0} 196 | m_NavMeshLayer: 0 197 | m_StaticEditorFlags: 0 198 | m_IsActive: 1 199 | --- !u!81 &534669903 200 | AudioListener: 201 | m_ObjectHideFlags: 0 202 | m_CorrespondingSourceObject: {fileID: 0} 203 | m_PrefabInternal: {fileID: 0} 204 | m_GameObject: {fileID: 534669902} 205 | m_Enabled: 1 206 | --- !u!20 &534669904 207 | Camera: 208 | m_ObjectHideFlags: 0 209 | m_CorrespondingSourceObject: {fileID: 0} 210 | m_PrefabInternal: {fileID: 0} 211 | m_GameObject: {fileID: 534669902} 212 | m_Enabled: 1 213 | serializedVersion: 2 214 | m_ClearFlags: 1 215 | m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} 216 | m_projectionMatrixMode: 1 217 | m_SensorSize: {x: 36, y: 24} 218 | m_LensShift: {x: 0, y: 0} 219 | m_GateFitMode: 2 220 | m_FocalLength: 50 221 | m_NormalizedViewPortRect: 222 | serializedVersion: 2 223 | x: 0 224 | y: 0 225 | width: 1 226 | height: 1 227 | near clip plane: 0.3 228 | far clip plane: 1000 229 | field of view: 60 230 | orthographic: 0 231 | orthographic size: 5 232 | m_Depth: -1 233 | m_CullingMask: 234 | serializedVersion: 2 235 | m_Bits: 4294967295 236 | m_RenderingPath: -1 237 | m_TargetTexture: {fileID: 0} 238 | m_TargetDisplay: 0 239 | m_TargetEye: 3 240 | m_HDR: 1 241 | m_AllowMSAA: 1 242 | m_AllowDynamicResolution: 0 243 | m_ForceIntoRT: 0 244 | m_OcclusionCulling: 1 245 | m_StereoConvergence: 10 246 | m_StereoSeparation: 0.022 247 | --- !u!4 &534669905 248 | Transform: 249 | m_ObjectHideFlags: 0 250 | m_CorrespondingSourceObject: {fileID: 0} 251 | m_PrefabInternal: {fileID: 0} 252 | m_GameObject: {fileID: 534669902} 253 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 254 | m_LocalPosition: {x: 0, y: 1, z: -10} 255 | m_LocalScale: {x: 1, y: 1, z: 1} 256 | m_Children: [] 257 | m_Father: {fileID: 0} 258 | m_RootOrder: 0 259 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 260 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scenes/SampleScene.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bddea718f6e293540872131e368b2aa2 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f001f60b92f8a49418d6f952fe119866 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scripts/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2a15fbc39fcfba24c8021f44d01bd04a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scripts/Editor/UnitTests.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 68d0ad131de497542be6a2a8e5f87429 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scripts/Editor/UnitTests/TestAssetBundles.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | using UnityEditor; 4 | 5 | namespace Tests 6 | { 7 | [TestFixture] 8 | public sealed class TestAssetBundles 9 | { 10 | [Test] 11 | public void AssetDuplication() 12 | { 13 | // AssetDatabase.GetDependencies only works for saved assets 14 | AssetDatabase.SaveAssets(); 15 | 16 | // build map of explicitly assigned assets 17 | var assigned = new Dictionary(); 18 | foreach (var bundleName in AssetDatabase.GetAllAssetBundleNames()) 19 | { 20 | var assetPaths = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName); 21 | foreach (var assetPath in assetPaths) 22 | { 23 | assigned.Add(assetPath, bundleName); 24 | } 25 | } 26 | 27 | // build map of implicitly assigned assets 28 | var referenced = new Dictionary(); 29 | foreach (var bundleName in AssetDatabase.GetAllAssetBundleNames()) 30 | { 31 | var assetPaths = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName); 32 | var dependencies = AssetDatabase.GetDependencies(assetPaths, true); 33 | foreach (var assetPath in dependencies) 34 | { 35 | if (assigned.ContainsKey(assetPath)) 36 | { 37 | // dependency explicitly assigned to a bundle 38 | continue; 39 | } 40 | 41 | string otherBundleName; 42 | if (referenced.TryGetValue(assetPath, out otherBundleName)) 43 | { 44 | // dependency implicitly assigned to another bundle -> asset duplication 45 | Assert.AreEqual(otherBundleName, bundleName, "Asset '{0}' implicitly referenced by asset bundles '{1}' and '{2}'", assetPath, otherBundleName, bundleName); 46 | } 47 | else 48 | { 49 | referenced.Add(assetPath, bundleName); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scripts/Editor/UnitTests/TestAssetBundles.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5585587ac6f3a4a42ac1af1f47cc287e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scripts/Editor/UnitTests/TestShaders.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | using UnityEditor; 5 | using UnityEngine; 6 | using UnityEngine.TestTools; 7 | 8 | namespace Tests 9 | { 10 | public class TestShaders 11 | { 12 | [Test] 13 | public void ShaderHasErrors() 14 | { 15 | var infos = ShaderUtil.GetAllShaderInfo(); 16 | foreach (var info in infos) 17 | { 18 | Assert.IsFalse(info.hasErrors, "Shader '{0}' has errors.", info.name); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Scripts/Editor/UnitTests/TestShaders.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cfe3ffe2e2452814a81f22dd106c492b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 977f6bb6174ef5b46bc32fd3a1368d0d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Shaders/DemoShader.shader: -------------------------------------------------------------------------------- 1 | Shader "Custom/DemoShader" 2 | { 3 | Properties 4 | { 5 | _Color ("Color", Color) = (1,1,1,1) 6 | _MainTex ("Albedo (RGB)", 2D) = "white" {} 7 | _Glossiness ("Smoothness", Range(0,1)) = 0.5 8 | _Metallic ("Metallic", Range(0,1)) = 0.0 9 | } 10 | SubShader 11 | { 12 | Tags { "RenderType"="Opaque" } 13 | LOD 200 14 | 15 | CGPROGRAM 16 | // Physically based Standard lighting model, and enable shadows on all light types 17 | #pragma surface surf Standard fullforwardshadows 18 | 19 | // Use shader model 3.0 target, to get nicer looking lighting 20 | #pragma target 3.0 21 | 22 | sampler2D _MainTex; 23 | 24 | struct Input 25 | { 26 | float2 uv_MainTex; 27 | }; 28 | 29 | half _Glossiness; 30 | half _Metallic; 31 | fixed4 _Color; 32 | 33 | // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader. 34 | // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing. 35 | // #pragma instancing_options assumeuniformscaling 36 | UNITY_INSTANCING_BUFFER_START(Props) 37 | // put more per-instance properties here 38 | UNITY_INSTANCING_BUFFER_END(Props) 39 | 40 | void surf (Input IN, inout SurfaceOutputStandard o) 41 | { 42 | // Albedo comes from a texture tinted by color 43 | fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; 44 | //fixed4 c = 1; 45 | o.Albedo = c.rgb; 46 | // Metallic and smoothness come from slider variables 47 | o.Metallic = _Metallic; 48 | o.Smoothness = _Glossiness; 49 | o.Alpha = c.a; 50 | } 51 | ENDCG 52 | } 53 | FallBack "Diffuse" 54 | } 55 | -------------------------------------------------------------------------------- /ContinuousIntegration/Assets/Shaders/DemoShader.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4bc2bf56e090b2849853f00fdea40479 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /ContinuousIntegration/BuildScripts/Jenkins/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | environment { 4 | GIT = '"C:\\Program Files\\Git\\bin\\git.exe"' 5 | UNITY = '"C:\\Program Files\\Unity\\Hub\\Editor\\2018.4.14f1\\Editor\\Unity.exe"' 6 | PROJECT = 'ContinuousIntegration' 7 | PLATFORM = 'Win64' 8 | OUTPUT = 'Build/ContinuousIntegration.exe' 9 | STEAMCMD = '"W:\\Jenkins\\steamworks\\tools\\ContentBuilder\\builder\\steamcmd.exe"' 10 | STEAMUSERNAME = 'steam_username_here' 11 | STEAMPASSWORD = 'steam_password_here' 12 | STEAMSCRIPT = '"ContinuousIntegration\\BuildScripts\\Steam\\app_build.vdf"' 13 | } 14 | stages { 15 | stage('Clean Workspace') { 16 | steps { 17 | bat "$GIT reset --hard" 18 | bat "$GIT clean -x -f -d -e $PROJECT/Library" 19 | } 20 | } 21 | stage('Update Workspace') { 22 | steps { 23 | bat "$GIT checkout" 24 | bat "$GIT lfs checkout" 25 | } 26 | } 27 | stage('Import Assets') { 28 | steps { 29 | bat "$UNITY -batchmode -logFile - -projectPath $PROJECT -buildTarget $PLATFORM -quit -accept-apiupdate" 30 | } 31 | } 32 | stage('Run Unit Tests') { 33 | steps { 34 | bat "$UNITY -batchmode -logFile - -projectPath $PROJECT -buildTarget $PLATFORM -runEditorTests" 35 | } 36 | } 37 | stage('Build') { 38 | steps { 39 | bat "$UNITY -batchmode -logFile - -projectPath $PROJECT -buildTarget $PLATFORM -quit -buildWindows64Player $OUTPUT" 40 | } 41 | } 42 | stage('Upload Build') { 43 | steps { 44 | echo "$STEAMCMD +login $STEAMUSERNAME $STEAMPASSWORD +run_app_build $STEAMSCRIPT" 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ContinuousIntegration/BuildScripts/Steam/app_build.vdf: -------------------------------------------------------------------------------- 1 | "appbuild" 2 | { 3 | "appid" "1000" 4 | "desc" "Your build description here" // description for this build 5 | "buildoutput" "..\output\" // build output folder for .log, .csm & .csd files, relative to location of this file 6 | "contentroot" "..\content\" // root content folder, relative to location of this file 7 | "setlive" "" // branch to set live after successful build, non if empty 8 | "preview" "0" // to enable preview builds 9 | "local" "" // set to file path of local content server 10 | 11 | "depots" 12 | { 13 | "1001" "depot_build_1.vdf" 14 | "1002" "depot_build_2.vdf" 15 | } 16 | } -------------------------------------------------------------------------------- /ContinuousIntegration/BuildScripts/Steam/depot_build_1.vdf: -------------------------------------------------------------------------------- 1 | "DepotBuildConfig" 2 | { 3 | // Set your assigned depot ID here 4 | "DepotID" "1001" 5 | 6 | // Set a root for all content. 7 | // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) 8 | // will be resolved relative to this root. 9 | // If you don't define ContentRoot, then it will be assumed to be 10 | // the location of this script file, which probably isn't what you want 11 | "ContentRoot" "D:\MyGame\rel\master\" 12 | 13 | // include all files recursivley 14 | "FileMapping" 15 | { 16 | // This can be a full path, or a path relative to ContentRoot 17 | "LocalPath" "*" 18 | 19 | // This is a path relative to the install folder of your game 20 | "DepotPath" "." 21 | 22 | // If LocalPath contains wildcards, setting this means that all 23 | // matching files within subdirectories of LocalPath will also 24 | // be included. 25 | "recursive" "1" 26 | } 27 | 28 | // but exclude all symbol files 29 | // This can be a full path, or a path relative to ContentRoot 30 | "FileExclusion" "*.pdb" 31 | } 32 | -------------------------------------------------------------------------------- /ContinuousIntegration/BuildScripts/Steam/depot_build_2.vdf: -------------------------------------------------------------------------------- 1 | "DepotBuildConfig" 2 | { 3 | // Set your assigned depot ID here 4 | "DepotID" "1002" 5 | 6 | // Set a root for all content. 7 | // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) 8 | // will be resolved relative to this root. 9 | // If you don't define ContentRoot, then it will be assumed to be 10 | // the location of this script file, which probably isn't what you want 11 | "ContentRoot" "D:\MyGame\rel\master\" 12 | 13 | // include all files recursivley 14 | "FileMapping" 15 | { 16 | // This can be a full path, or a path relative to ContentRoot 17 | "LocalPath" "*" 18 | 19 | // This is a path relative to the install folder of your game 20 | "DepotPath" "." 21 | 22 | // If LocalPath contains wildcards, setting this means that all 23 | // matching files within subdirectories of LocalPath will also 24 | // be included. 25 | "recursive" "1" 26 | } 27 | 28 | // but exclude all symbol files 29 | // This can be a full path, or a path relative to ContentRoot 30 | "FileExclusion" "*.pdb" 31 | } 32 | -------------------------------------------------------------------------------- /ContinuousIntegration/Documentation/Jenkins1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/ContinuousIntegration/Documentation/Jenkins1.png -------------------------------------------------------------------------------- /ContinuousIntegration/Documentation/Jenkins2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/ContinuousIntegration/Documentation/Jenkins2.png -------------------------------------------------------------------------------- /ContinuousIntegration/Documentation/Pipeline1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/ContinuousIntegration/Documentation/Pipeline1.png -------------------------------------------------------------------------------- /ContinuousIntegration/Documentation/Pipeline2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/ContinuousIntegration/Documentation/Pipeline2.png -------------------------------------------------------------------------------- /ContinuousIntegration/Documentation/Pipeline3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/ContinuousIntegration/Documentation/Pipeline3.png -------------------------------------------------------------------------------- /ContinuousIntegration/Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.package-manager-ui": "2.0.8", 4 | "com.unity.modules.audio": "1.0.0", 5 | "com.unity.modules.imgui": "1.0.0", 6 | "com.unity.modules.physics": "1.0.0", 7 | "com.unity.modules.ui": "1.0.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!11 &1 4 | AudioManager: 5 | m_ObjectHideFlags: 0 6 | m_Volume: 1 7 | Rolloff Scale: 1 8 | Doppler Factor: 1 9 | Default Speaker Mode: 2 10 | m_SampleRate: 0 11 | m_DSPBufferSize: 1024 12 | m_VirtualVoiceCount: 512 13 | m_RealVoiceCount: 32 14 | m_SpatializerPlugin: 15 | m_AmbisonicDecoderPlugin: 16 | m_DisableAudio: 0 17 | m_VirtualizeEffects: 1 18 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!236 &1 4 | ClusterInputManager: 5 | m_ObjectHideFlags: 0 6 | m_Inputs: [] 7 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!55 &1 4 | PhysicsManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 8 7 | m_Gravity: {x: 0, y: -9.81, z: 0} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_BounceThreshold: 2 10 | m_SleepThreshold: 0.005 11 | m_DefaultContactOffset: 0.01 12 | m_DefaultSolverIterations: 6 13 | m_DefaultSolverVelocityIterations: 1 14 | m_QueriesHitBackfaces: 0 15 | m_QueriesHitTriggers: 1 16 | m_EnableAdaptiveForce: 0 17 | m_ClothInterCollisionDistance: 0 18 | m_ClothInterCollisionStiffness: 0 19 | m_ContactsGeneration: 1 20 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21 | m_AutoSimulation: 1 22 | m_AutoSyncTransforms: 0 23 | m_ReuseCollisionCallbacks: 1 24 | m_ClothInterCollisionSettingsToggle: 0 25 | m_ContactPairsMode: 0 26 | m_BroadphaseType: 0 27 | m_WorldBounds: 28 | m_Center: {x: 0, y: 0, z: 0} 29 | m_Extent: {x: 250, y: 250, z: 250} 30 | m_WorldSubdivisions: 8 31 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1045 &1 4 | EditorBuildSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Scenes: 8 | - enabled: 1 9 | path: Assets/Scenes/SampleScene.unity 10 | guid: bddea718f6e293540872131e368b2aa2 11 | m_configObjects: {} 12 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!159 &1 4 | EditorSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 7 7 | m_ExternalVersionControlSupport: Visible Meta Files 8 | m_SerializationMode: 2 9 | m_LineEndingsForNewScripts: 2 10 | m_DefaultBehaviorMode: 0 11 | m_SpritePackerMode: 0 12 | m_SpritePackerPaddingPower: 1 13 | m_EtcTextureCompressorBehavior: 1 14 | m_EtcTextureFastCompressor: 1 15 | m_EtcTextureNormalCompressor: 2 16 | m_EtcTextureBestCompressor: 4 17 | m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd 18 | m_ProjectGenerationRootNamespace: 19 | m_UserGeneratedProjectSuffix: 20 | m_CollabEditorSettings: 21 | inProgressEnabled: 1 22 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!30 &1 4 | GraphicsSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 12 7 | m_Deferred: 8 | m_Mode: 1 9 | m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} 10 | m_DeferredReflections: 11 | m_Mode: 1 12 | m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} 13 | m_ScreenSpaceShadows: 14 | m_Mode: 1 15 | m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} 16 | m_LegacyDeferred: 17 | m_Mode: 1 18 | m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} 19 | m_DepthNormals: 20 | m_Mode: 1 21 | m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} 22 | m_MotionVectors: 23 | m_Mode: 1 24 | m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} 25 | m_LightHalo: 26 | m_Mode: 1 27 | m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} 28 | m_LensFlare: 29 | m_Mode: 1 30 | m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} 31 | m_AlwaysIncludedShaders: 32 | - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} 33 | - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} 34 | - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} 35 | - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} 36 | - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} 37 | - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} 38 | - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} 39 | m_PreloadedShaders: [] 40 | m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, 41 | type: 0} 42 | m_CustomRenderPipeline: {fileID: 0} 43 | m_TransparencySortMode: 0 44 | m_TransparencySortAxis: {x: 0, y: 0, z: 1} 45 | m_DefaultRenderingPath: 1 46 | m_DefaultMobileRenderingPath: 1 47 | m_TierSettings: [] 48 | m_LightmapStripping: 0 49 | m_FogStripping: 0 50 | m_InstancingStripping: 0 51 | m_LightmapKeepPlain: 1 52 | m_LightmapKeepDirCombined: 1 53 | m_LightmapKeepDynamicPlain: 1 54 | m_LightmapKeepDynamicDirCombined: 1 55 | m_LightmapKeepShadowMask: 1 56 | m_LightmapKeepSubtractive: 1 57 | m_FogKeepLinear: 1 58 | m_FogKeepExp: 1 59 | m_FogKeepExp2: 1 60 | m_AlbedoSwatchInfos: [] 61 | m_LightsUseLinearIntensity: 0 62 | m_LightsUseColorTemperature: 0 63 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!13 &1 4 | InputManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Axes: 8 | - serializedVersion: 3 9 | m_Name: Horizontal 10 | descriptiveName: 11 | descriptiveNegativeName: 12 | negativeButton: left 13 | positiveButton: right 14 | altNegativeButton: a 15 | altPositiveButton: d 16 | gravity: 3 17 | dead: 0.001 18 | sensitivity: 3 19 | snap: 1 20 | invert: 0 21 | type: 0 22 | axis: 0 23 | joyNum: 0 24 | - serializedVersion: 3 25 | m_Name: Vertical 26 | descriptiveName: 27 | descriptiveNegativeName: 28 | negativeButton: down 29 | positiveButton: up 30 | altNegativeButton: s 31 | altPositiveButton: w 32 | gravity: 3 33 | dead: 0.001 34 | sensitivity: 3 35 | snap: 1 36 | invert: 0 37 | type: 0 38 | axis: 0 39 | joyNum: 0 40 | - serializedVersion: 3 41 | m_Name: Fire1 42 | descriptiveName: 43 | descriptiveNegativeName: 44 | negativeButton: 45 | positiveButton: left ctrl 46 | altNegativeButton: 47 | altPositiveButton: mouse 0 48 | gravity: 1000 49 | dead: 0.001 50 | sensitivity: 1000 51 | snap: 0 52 | invert: 0 53 | type: 0 54 | axis: 0 55 | joyNum: 0 56 | - serializedVersion: 3 57 | m_Name: Fire2 58 | descriptiveName: 59 | descriptiveNegativeName: 60 | negativeButton: 61 | positiveButton: left alt 62 | altNegativeButton: 63 | altPositiveButton: mouse 1 64 | gravity: 1000 65 | dead: 0.001 66 | sensitivity: 1000 67 | snap: 0 68 | invert: 0 69 | type: 0 70 | axis: 0 71 | joyNum: 0 72 | - serializedVersion: 3 73 | m_Name: Fire3 74 | descriptiveName: 75 | descriptiveNegativeName: 76 | negativeButton: 77 | positiveButton: left shift 78 | altNegativeButton: 79 | altPositiveButton: mouse 2 80 | gravity: 1000 81 | dead: 0.001 82 | sensitivity: 1000 83 | snap: 0 84 | invert: 0 85 | type: 0 86 | axis: 0 87 | joyNum: 0 88 | - serializedVersion: 3 89 | m_Name: Jump 90 | descriptiveName: 91 | descriptiveNegativeName: 92 | negativeButton: 93 | positiveButton: space 94 | altNegativeButton: 95 | altPositiveButton: 96 | gravity: 1000 97 | dead: 0.001 98 | sensitivity: 1000 99 | snap: 0 100 | invert: 0 101 | type: 0 102 | axis: 0 103 | joyNum: 0 104 | - serializedVersion: 3 105 | m_Name: Mouse X 106 | descriptiveName: 107 | descriptiveNegativeName: 108 | negativeButton: 109 | positiveButton: 110 | altNegativeButton: 111 | altPositiveButton: 112 | gravity: 0 113 | dead: 0 114 | sensitivity: 0.1 115 | snap: 0 116 | invert: 0 117 | type: 1 118 | axis: 0 119 | joyNum: 0 120 | - serializedVersion: 3 121 | m_Name: Mouse Y 122 | descriptiveName: 123 | descriptiveNegativeName: 124 | negativeButton: 125 | positiveButton: 126 | altNegativeButton: 127 | altPositiveButton: 128 | gravity: 0 129 | dead: 0 130 | sensitivity: 0.1 131 | snap: 0 132 | invert: 0 133 | type: 1 134 | axis: 1 135 | joyNum: 0 136 | - serializedVersion: 3 137 | m_Name: Mouse ScrollWheel 138 | descriptiveName: 139 | descriptiveNegativeName: 140 | negativeButton: 141 | positiveButton: 142 | altNegativeButton: 143 | altPositiveButton: 144 | gravity: 0 145 | dead: 0 146 | sensitivity: 0.1 147 | snap: 0 148 | invert: 0 149 | type: 1 150 | axis: 2 151 | joyNum: 0 152 | - serializedVersion: 3 153 | m_Name: Horizontal 154 | descriptiveName: 155 | descriptiveNegativeName: 156 | negativeButton: 157 | positiveButton: 158 | altNegativeButton: 159 | altPositiveButton: 160 | gravity: 0 161 | dead: 0.19 162 | sensitivity: 1 163 | snap: 0 164 | invert: 0 165 | type: 2 166 | axis: 0 167 | joyNum: 0 168 | - serializedVersion: 3 169 | m_Name: Vertical 170 | descriptiveName: 171 | descriptiveNegativeName: 172 | negativeButton: 173 | positiveButton: 174 | altNegativeButton: 175 | altPositiveButton: 176 | gravity: 0 177 | dead: 0.19 178 | sensitivity: 1 179 | snap: 0 180 | invert: 1 181 | type: 2 182 | axis: 1 183 | joyNum: 0 184 | - serializedVersion: 3 185 | m_Name: Fire1 186 | descriptiveName: 187 | descriptiveNegativeName: 188 | negativeButton: 189 | positiveButton: joystick button 0 190 | altNegativeButton: 191 | altPositiveButton: 192 | gravity: 1000 193 | dead: 0.001 194 | sensitivity: 1000 195 | snap: 0 196 | invert: 0 197 | type: 0 198 | axis: 0 199 | joyNum: 0 200 | - serializedVersion: 3 201 | m_Name: Fire2 202 | descriptiveName: 203 | descriptiveNegativeName: 204 | negativeButton: 205 | positiveButton: joystick button 1 206 | altNegativeButton: 207 | altPositiveButton: 208 | gravity: 1000 209 | dead: 0.001 210 | sensitivity: 1000 211 | snap: 0 212 | invert: 0 213 | type: 0 214 | axis: 0 215 | joyNum: 0 216 | - serializedVersion: 3 217 | m_Name: Fire3 218 | descriptiveName: 219 | descriptiveNegativeName: 220 | negativeButton: 221 | positiveButton: joystick button 2 222 | altNegativeButton: 223 | altPositiveButton: 224 | gravity: 1000 225 | dead: 0.001 226 | sensitivity: 1000 227 | snap: 0 228 | invert: 0 229 | type: 0 230 | axis: 0 231 | joyNum: 0 232 | - serializedVersion: 3 233 | m_Name: Jump 234 | descriptiveName: 235 | descriptiveNegativeName: 236 | negativeButton: 237 | positiveButton: joystick button 3 238 | altNegativeButton: 239 | altPositiveButton: 240 | gravity: 1000 241 | dead: 0.001 242 | sensitivity: 1000 243 | snap: 0 244 | invert: 0 245 | type: 0 246 | axis: 0 247 | joyNum: 0 248 | - serializedVersion: 3 249 | m_Name: Submit 250 | descriptiveName: 251 | descriptiveNegativeName: 252 | negativeButton: 253 | positiveButton: return 254 | altNegativeButton: 255 | altPositiveButton: joystick button 0 256 | gravity: 1000 257 | dead: 0.001 258 | sensitivity: 1000 259 | snap: 0 260 | invert: 0 261 | type: 0 262 | axis: 0 263 | joyNum: 0 264 | - serializedVersion: 3 265 | m_Name: Submit 266 | descriptiveName: 267 | descriptiveNegativeName: 268 | negativeButton: 269 | positiveButton: enter 270 | altNegativeButton: 271 | altPositiveButton: space 272 | gravity: 1000 273 | dead: 0.001 274 | sensitivity: 1000 275 | snap: 0 276 | invert: 0 277 | type: 0 278 | axis: 0 279 | joyNum: 0 280 | - serializedVersion: 3 281 | m_Name: Cancel 282 | descriptiveName: 283 | descriptiveNegativeName: 284 | negativeButton: 285 | positiveButton: escape 286 | altNegativeButton: 287 | altPositiveButton: joystick button 1 288 | gravity: 1000 289 | dead: 0.001 290 | sensitivity: 1000 291 | snap: 0 292 | invert: 0 293 | type: 0 294 | axis: 0 295 | joyNum: 0 296 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!126 &1 4 | NavMeshProjectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | areas: 8 | - name: Walkable 9 | cost: 1 10 | - name: Not Walkable 11 | cost: 1 12 | - name: Jump 13 | cost: 2 14 | - name: 15 | cost: 1 16 | - name: 17 | cost: 1 18 | - name: 19 | cost: 1 20 | - name: 21 | cost: 1 22 | - name: 23 | cost: 1 24 | - name: 25 | cost: 1 26 | - name: 27 | cost: 1 28 | - name: 29 | cost: 1 30 | - name: 31 | cost: 1 32 | - name: 33 | cost: 1 34 | - name: 35 | cost: 1 36 | - name: 37 | cost: 1 38 | - name: 39 | cost: 1 40 | - name: 41 | cost: 1 42 | - name: 43 | cost: 1 44 | - name: 45 | cost: 1 46 | - name: 47 | cost: 1 48 | - name: 49 | cost: 1 50 | - name: 51 | cost: 1 52 | - name: 53 | cost: 1 54 | - name: 55 | cost: 1 56 | - name: 57 | cost: 1 58 | - name: 59 | cost: 1 60 | - name: 61 | cost: 1 62 | - name: 63 | cost: 1 64 | - name: 65 | cost: 1 66 | - name: 67 | cost: 1 68 | - name: 69 | cost: 1 70 | - name: 71 | cost: 1 72 | m_LastAgentTypeID: -887442657 73 | m_Settings: 74 | - serializedVersion: 2 75 | agentTypeID: 0 76 | agentRadius: 0.5 77 | agentHeight: 2 78 | agentSlope: 45 79 | agentClimb: 0.75 80 | ledgeDropHeight: 0 81 | maxJumpAcrossDistance: 0 82 | minRegionArea: 2 83 | manualCellSize: 0 84 | cellSize: 0.16666667 85 | manualTileSize: 0 86 | tileSize: 256 87 | accuratePlacement: 0 88 | debug: 89 | m_Flags: 0 90 | m_SettingNames: 91 | - Humanoid 92 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!149 &1 4 | NetworkManager: 5 | m_ObjectHideFlags: 0 6 | m_DebugLevel: 0 7 | m_Sendrate: 15 8 | m_AssetToPrefab: {} 9 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!19 &1 4 | Physics2DSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 4 7 | m_Gravity: {x: 0, y: -9.81} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_VelocityIterations: 8 10 | m_PositionIterations: 3 11 | m_VelocityThreshold: 1 12 | m_MaxLinearCorrection: 0.2 13 | m_MaxAngularCorrection: 8 14 | m_MaxTranslationSpeed: 100 15 | m_MaxRotationSpeed: 360 16 | m_BaumgarteScale: 0.2 17 | m_BaumgarteTimeOfImpactScale: 0.75 18 | m_TimeToSleep: 0.5 19 | m_LinearSleepTolerance: 0.01 20 | m_AngularSleepTolerance: 2 21 | m_DefaultContactOffset: 0.01 22 | m_AutoSimulation: 1 23 | m_QueriesHitTriggers: 1 24 | m_QueriesStartInColliders: 1 25 | m_ChangeStopsCallbacks: 0 26 | m_CallbacksOnDisable: 1 27 | m_ReuseCollisionCallbacks: 1 28 | m_AutoSyncTransforms: 0 29 | m_AlwaysShowColliders: 0 30 | m_ShowColliderSleep: 1 31 | m_ShowColliderContacts: 0 32 | m_ShowColliderAABB: 0 33 | m_ContactArrowScale: 0.2 34 | m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} 35 | m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} 36 | m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} 37 | m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} 38 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 39 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1386491679 &1 4 | PresetManager: 5 | m_ObjectHideFlags: 0 6 | m_DefaultList: 7 | - type: 8 | m_NativeTypeID: 108 9 | m_ManagedTypePPtr: {fileID: 0} 10 | m_ManagedTypeFallback: 11 | defaultPresets: 12 | - m_Preset: {fileID: 2655988077585873504, guid: c1cf8506f04ef2c4a88b64b6c4202eea, 13 | type: 2} 14 | - type: 15 | m_NativeTypeID: 1020 16 | m_ManagedTypePPtr: {fileID: 0} 17 | m_ManagedTypeFallback: 18 | defaultPresets: 19 | - m_Preset: {fileID: 2655988077585873504, guid: 0cd792cc87e492d43b4e95b205fc5cc6, 20 | type: 2} 21 | - type: 22 | m_NativeTypeID: 1006 23 | m_ManagedTypePPtr: {fileID: 0} 24 | m_ManagedTypeFallback: 25 | defaultPresets: 26 | - m_Preset: {fileID: 2655988077585873504, guid: 7a99f8aa944efe94cb9bd74562b7d5f9, 27 | type: 2} 28 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2018.4.14f1 2 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!47 &1 4 | QualitySettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 5 7 | m_CurrentQuality: 4 8 | m_QualitySettings: 9 | - serializedVersion: 2 10 | name: Very Low 11 | pixelLightCount: 0 12 | shadows: 0 13 | shadowResolution: 0 14 | shadowProjection: 1 15 | shadowCascades: 1 16 | shadowDistance: 15 17 | shadowNearPlaneOffset: 3 18 | shadowCascade2Split: 0.33333334 19 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 20 | shadowmaskMode: 0 21 | blendWeights: 1 22 | textureQuality: 1 23 | anisotropicTextures: 0 24 | antiAliasing: 0 25 | softParticles: 0 26 | softVegetation: 0 27 | realtimeReflectionProbes: 0 28 | billboardsFaceCameraPosition: 0 29 | vSyncCount: 0 30 | lodBias: 0.3 31 | maximumLODLevel: 0 32 | particleRaycastBudget: 4 33 | asyncUploadTimeSlice: 2 34 | asyncUploadBufferSize: 16 35 | resolutionScalingFixedDPIFactor: 1 36 | excludedTargetPlatforms: [] 37 | - serializedVersion: 2 38 | name: Low 39 | pixelLightCount: 0 40 | shadows: 0 41 | shadowResolution: 0 42 | shadowProjection: 1 43 | shadowCascades: 1 44 | shadowDistance: 20 45 | shadowNearPlaneOffset: 3 46 | shadowCascade2Split: 0.33333334 47 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 48 | shadowmaskMode: 0 49 | blendWeights: 2 50 | textureQuality: 0 51 | anisotropicTextures: 0 52 | antiAliasing: 0 53 | softParticles: 0 54 | softVegetation: 0 55 | realtimeReflectionProbes: 0 56 | billboardsFaceCameraPosition: 0 57 | vSyncCount: 0 58 | lodBias: 0.4 59 | maximumLODLevel: 0 60 | particleRaycastBudget: 16 61 | asyncUploadTimeSlice: 2 62 | asyncUploadBufferSize: 16 63 | resolutionScalingFixedDPIFactor: 1 64 | excludedTargetPlatforms: [] 65 | - serializedVersion: 2 66 | name: Medium 67 | pixelLightCount: 1 68 | shadows: 1 69 | shadowResolution: 0 70 | shadowProjection: 1 71 | shadowCascades: 1 72 | shadowDistance: 20 73 | shadowNearPlaneOffset: 3 74 | shadowCascade2Split: 0.33333334 75 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 76 | shadowmaskMode: 0 77 | blendWeights: 2 78 | textureQuality: 0 79 | anisotropicTextures: 1 80 | antiAliasing: 0 81 | softParticles: 0 82 | softVegetation: 0 83 | realtimeReflectionProbes: 0 84 | billboardsFaceCameraPosition: 0 85 | vSyncCount: 1 86 | lodBias: 0.7 87 | maximumLODLevel: 0 88 | particleRaycastBudget: 64 89 | asyncUploadTimeSlice: 2 90 | asyncUploadBufferSize: 16 91 | resolutionScalingFixedDPIFactor: 1 92 | excludedTargetPlatforms: [] 93 | - serializedVersion: 2 94 | name: High 95 | pixelLightCount: 2 96 | shadows: 2 97 | shadowResolution: 1 98 | shadowProjection: 1 99 | shadowCascades: 2 100 | shadowDistance: 40 101 | shadowNearPlaneOffset: 3 102 | shadowCascade2Split: 0.33333334 103 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 104 | shadowmaskMode: 1 105 | blendWeights: 2 106 | textureQuality: 0 107 | anisotropicTextures: 1 108 | antiAliasing: 2 109 | softParticles: 0 110 | softVegetation: 1 111 | realtimeReflectionProbes: 1 112 | billboardsFaceCameraPosition: 1 113 | vSyncCount: 1 114 | lodBias: 1 115 | maximumLODLevel: 0 116 | particleRaycastBudget: 256 117 | asyncUploadTimeSlice: 2 118 | asyncUploadBufferSize: 16 119 | resolutionScalingFixedDPIFactor: 1 120 | excludedTargetPlatforms: [] 121 | - serializedVersion: 2 122 | name: Very High 123 | pixelLightCount: 3 124 | shadows: 2 125 | shadowResolution: 2 126 | shadowProjection: 1 127 | shadowCascades: 2 128 | shadowDistance: 40 129 | shadowNearPlaneOffset: 3 130 | shadowCascade2Split: 0.33333334 131 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 132 | shadowmaskMode: 1 133 | blendWeights: 4 134 | textureQuality: 0 135 | anisotropicTextures: 1 136 | antiAliasing: 4 137 | softParticles: 1 138 | softVegetation: 1 139 | realtimeReflectionProbes: 1 140 | billboardsFaceCameraPosition: 1 141 | vSyncCount: 1 142 | lodBias: 1.5 143 | maximumLODLevel: 0 144 | particleRaycastBudget: 1024 145 | asyncUploadTimeSlice: 2 146 | asyncUploadBufferSize: 16 147 | resolutionScalingFixedDPIFactor: 1 148 | excludedTargetPlatforms: [] 149 | - serializedVersion: 2 150 | name: Ultra 151 | pixelLightCount: 4 152 | shadows: 2 153 | shadowResolution: 2 154 | shadowProjection: 1 155 | shadowCascades: 4 156 | shadowDistance: 150 157 | shadowNearPlaneOffset: 3 158 | shadowCascade2Split: 0.33333334 159 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 160 | shadowmaskMode: 1 161 | blendWeights: 4 162 | textureQuality: 0 163 | anisotropicTextures: 1 164 | antiAliasing: 4 165 | softParticles: 1 166 | softVegetation: 1 167 | realtimeReflectionProbes: 1 168 | billboardsFaceCameraPosition: 1 169 | vSyncCount: 1 170 | lodBias: 2 171 | maximumLODLevel: 0 172 | particleRaycastBudget: 4096 173 | asyncUploadTimeSlice: 2 174 | asyncUploadBufferSize: 16 175 | resolutionScalingFixedDPIFactor: 1 176 | excludedTargetPlatforms: [] 177 | m_PerPlatformDefaultQuality: 178 | Android: 2 179 | Nintendo 3DS: 5 180 | Nintendo Switch: 5 181 | PS4: 5 182 | PSP2: 2 183 | Standalone: 5 184 | Tizen: 2 185 | WebGL: 3 186 | WiiU: 5 187 | Windows Store Apps: 5 188 | XboxOne: 5 189 | iPhone: 2 190 | tvOS: 2 191 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!78 &1 4 | TagManager: 5 | serializedVersion: 2 6 | tags: [] 7 | layers: 8 | - Default 9 | - TransparentFX 10 | - Ignore Raycast 11 | - 12 | - Water 13 | - UI 14 | - 15 | - 16 | - PostProcessing 17 | - 18 | - 19 | - 20 | - 21 | - 22 | - 23 | - 24 | - 25 | - 26 | - 27 | - 28 | - 29 | - 30 | - 31 | - 32 | - 33 | - 34 | - 35 | - 36 | - 37 | - 38 | - 39 | - 40 | m_SortingLayers: 41 | - name: Default 42 | uniqueID: 0 43 | locked: 0 44 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!5 &1 4 | TimeManager: 5 | m_ObjectHideFlags: 0 6 | Fixed Timestep: 0.02 7 | Maximum Allowed Timestep: 0.1 8 | m_TimeScale: 1 9 | Maximum Particle Timestep: 0.03 10 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!310 &1 4 | UnityConnectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 1 7 | m_Enabled: 0 8 | m_TestMode: 0 9 | m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events 10 | m_EventUrl: https://cdp.cloud.unity3d.com/v1/events 11 | m_ConfigUrl: https://config.uca.cloud.unity3d.com 12 | m_TestInitMode: 0 13 | CrashReportingSettings: 14 | m_EventUrl: https://perf-events.cloud.unity3d.com 15 | m_Enabled: 0 16 | m_LogBufferSize: 10 17 | m_CaptureEditorExceptions: 1 18 | UnityPurchasingSettings: 19 | m_Enabled: 0 20 | m_TestMode: 0 21 | UnityAnalyticsSettings: 22 | m_Enabled: 0 23 | m_TestMode: 0 24 | m_InitializeOnStartup: 1 25 | UnityAdsSettings: 26 | m_Enabled: 0 27 | m_InitializeOnStartup: 1 28 | m_TestMode: 0 29 | m_IosGameId: 30 | m_AndroidGameId: 31 | m_GameIds: {} 32 | m_GameId: 33 | PerformanceReportingSettings: 34 | m_Enabled: 0 35 | -------------------------------------------------------------------------------- /ContinuousIntegration/ProjectSettings/VFXManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!937362698 &1 4 | VFXManager: 5 | m_ObjectHideFlags: 0 6 | m_IndirectShader: {fileID: 0} 7 | m_CopyBufferShader: {fileID: 0} 8 | m_SortShader: {fileID: 0} 9 | m_RenderPipeSettingsPath: 10 | m_FixedTimeStep: 0.016666668 11 | m_MaxDeltaTime: 0.05 12 | -------------------------------------------------------------------------------- /ContinuousIntegration/README-zh-TW.md: -------------------------------------------------------------------------------- 1 | # 持續整合(Continuous Integration) 2 | 不斷做測試、穩定推版本、及早除臭蟲。 3 | 4 | # 問題 5 | 遊戲通常由橫跨多種專業並相互倚賴的素材所構成,專案很容易因為成員在沒有意識到關聯影響的情況下,就被改爛掉。某個問題如果需要越長的時間才浮現出來,通常也需要越長的時間才能找出到底是哪次修改改爛的。 6 | 7 | 不僅如此,通常在任一時間點,團隊內每個人電腦上的專案狀態都存在細微的差異,要用其他機器來重現問題時難度更上升。有些疑難雜症甚至只出現在目標平台上。 8 | 9 | ``` 10 | ¯\_(ツ)_/¯ 11 | 「我電腦上可以跑(攤手)」 12 | ``` 13 | 14 | # 解決方案 15 | 我們希望盡可能地降低從某次修改內容,到該次修改結果在目標平台上可以被呈現出來的時間差。我們想要能有某種檢查機制,來告訴我們有東西壞掉了,而且這個檢查機制應該每次修改後都要執行。這代表,必須把整個流程全部自動化。 16 | 17 | 持續整合與發佈將透過以下步驟來達成: 18 | 1. 維護單一的專案儲存庫(repository) 19 | 2. 把建置流程自動化 20 | 3. 把測試流程自動化 21 | 4. 把發佈流程自動化 22 | 23 | ## 維護單一的專案儲存庫 24 | 如果你已經在使用 [版本控制系統](https://en.wikipedia.org/wiki/Version_control) ,這段基本上可以跳過。繼續使用你已經順手的版控軟體即可。如果你還沒有用過版控,目前最好的選擇是 [GitHub 企業版](https://github.com/enterprise) 與 [Plastic SCM](https://www.plasticscm.com/) 。[兩者都要](https://github.com/pricing) [花錢](https://www.plasticscm.com/pricing) ,但不會太貴,絕對值回票價。對 [Unity Collaborate](https://unity.com/unity/features/collaborate) 敬而遠之,因為功能有限,而且用起來還跟預期的不太一樣。目前狀態下它大概在 game jam 時有點用,不過差不多也就這樣了。 25 | 26 | ## 建置流程自動化 27 | 如果你用的是 GitHub 企業版,可以看一看 [GitHub Actions](https://github.com/features/actions) 的用法。不要用 [Unity Cloud Build](https://unity3d.com/unity/features/cloud-build) 比較好,它的功能對於實戰需求來說還有點太基礎了。我們會使用 [Jenkins](https://jenkins.io/) 來做後續介紹,它能本地執行、開源,而且還免費。 28 | 29 | ### Jenkins 30 | 我們會把 Jenkins 連接到專案儲存庫,並在*每次*修改 commit 之後都叫它幫我們建置專案一次。當然這樣長久下來數量會很龐大,所以最好是將 [Jenkins 安裝](https://jenkins.io/download/thank-you-downloading-windows-installer-stable/) 在空機器上,這台機器也將會是我們的建置專用伺服器(build server)。 31 | 32 | 安裝好 Jenkins 之後,使用初始的 admin 密碼到 http://localhost:8080 登入伺服器,Jenkins 這時會問你要安裝哪些外掛模組,這邊我們先選預設建議就好(Install suggested plugins)。我們只會用到一小部分的外掛,但要一個一個手動挑蠻麻煩的。說到外掛,其實 Jenkins 裡的所有東西都是一種外掛,大部分是其他人自願貢獻的內容。實際上它們大部分都沒有到*商業上線專案可用(production ready)* 的品質,這也是為何常看到當中有各種重複功能的外掛,但卻都無法完全滿足我們需求的原因。 33 | 34 | ### 建置工作(Build job) 35 | 為了讓 Jenkins 建置我們的遊戲專案,需要將所有相關步驟包在一個 Jenkins job 裡面。這邊我們先只用 Pipeline 樣版,跳過所有的外掛,讓事情盡量單純一點。 36 | 37 | ![Jenkins 歡迎頁面](Documentation/Jenkins1.png "跑起來了!") 38 | ![Jenkins 工作建立頁面](Documentation/Jenkins2.png "給馬利歐用的水管線(Pipeline)...") 39 | ![Jenkins pipeline 設定](Documentation/Pipeline1.png) 40 | ![Jenkins pipeline 觸發條件](Documentation/Pipeline2.png) 41 | ![Jenkins pipeline 指令碼](Documentation/Pipeline3.png) 42 | 43 | 這邊 Jenkins job 要做的設定很少,只需要它去我們的專案儲存庫裡下載 [pipeline 指令碼](https://jenkins.io/doc/book/pipeline/jenkinsfile/) 檔案,並執行它就好了。下方指令碼中會有所有相關細節描述。 44 | 45 | 在 [我們的 pipeline 指令碼](BuildScripts/Jenkins/Jenkinsfile) 中,我們會: 46 | 1. 更新到專案儲存庫裡面的最新版本 47 | 2. 啟動 Unity 並讓它去載入元件 48 | 3. 執行單元測試 49 | 4. 建置專案 50 | 5. 最後,把建置完成的版本上傳 51 | 52 | ```groovy 53 | environment { 54 | GIT = '"C:\\Program Files\\Git\\bin\\git.exe"' 55 | UNITY = '"C:\\Program Files\\Unity\\Hub\\Editor\\2018.4.14f1\\Editor\\Unity.exe"' 56 | PROJECT = 'ContinuousIntegration' 57 | PLATFORM = 'Win64' 58 | OUTPUT = 'Build/ContinuousIntegration.exe' 59 | STEAMCMD = '"W:\\Jenkins\\steamworks\\tools\\ContentBuilder\\builder\\steamcmd.exe"' 60 | STEAMUSERNAME = 'steam_username_here' 61 | STEAMPASSWORD = 'steam_password_here' 62 | STEAMSCRIPT = '"ContinuousIntegration\\BuildScripts\\Steam\\app_build.vdf"' 63 | } 64 | stage('Import Assets') { 65 | steps { 66 | bat "$UNITY -batchmode -logFile - -projectPath $PROJECT -buildTarget $PLATFORM -quit -accept-apiupdate" 67 | } 68 | } 69 | stage('Run Unit Tests') { 70 | steps { 71 | bat "$UNITY -batchmode -logFile - -projectPath $PROJECT -buildTarget $PLATFORM -runEditorTests" 72 | } 73 | } 74 | stage('Build') { 75 | steps { 76 | bat "$UNITY -batchmode -logFile - -projectPath $PROJECT -buildTarget $PLATFORM -quit -buildWindows64Player $OUTPUT" 77 | } 78 | } 79 | ``` 80 | 81 | ## 測試流程自動化 82 | 既然我們已經在每次建置專案前加入單元測試,那最好就讓單元測試盡可能涵蓋各種潛在的問題。先從最基礎的檢查一致性開始。 83 | 84 | ```csharp 85 | [Test] 86 | public void ShaderHasErrors() 87 | { 88 | var infos = ShaderUtil.GetAllShaderInfo(); 89 | foreach (var info in infos) 90 | { 91 | Assert.IsFalse(info.hasErrors, "Shader '{0}' has errors.", info.name); 92 | } 93 | } 94 | ``` 95 | 96 | 要完善一整套測試系統需要投入很多時間。原則上來說,每次專案上出現沒碰過的問題時,大概就是新增一項測試的好時機。撰寫這項新測試的角度,當然就是希望可以防止同一個問題再次發生。 97 | 98 | ## 發佈流程自動化 99 | 現在這個建置版本已經通過了我們所設下的一些基礎檢查,但至此尚欠最關鍵的一步。我們需要*真人*來進行測試。 100 | 101 | ``` 102 | 「沒有真的發佈的東西,等於不存在。」── Jonas Bötel 103 | ``` 104 | 105 | 所幸,[上傳版本到 Steam](https://partner.steamgames.com/doc/sdk/uploading) 的流程還算直觀,而且也有足夠的文件可以查。 106 | 107 | ```groovy 108 | stage('Upload Build') { 109 | steps { 110 | echo "$STEAMCMD +login $STEAMUSERNAME $STEAMPASSWORD +run_app_build $STEAMSCRIPT" 111 | } 112 | } 113 | ``` 114 | 115 | Steam 的政策是在他們的伺服器上*永久*保留任何上傳過的版本,這在發生預期外問題並需要緊急回復舊版的時候非常有用。但話說回頭,我們也不希望冒著玩家每天都有機會玩到爛掉版本的風險。所以切記,這邊自動上傳的版本必須指定到 Steam 上的 *Beta* 分支上,可以透過 `app_build.vdf` 檔案裡的 `setlive` 屬性來進行設定。這樣一來,玩家就可以選擇他們是否真的心臟夠大顆,來試玩最新、~~熱到燙舌頭~~的版本。每隔幾週,當版本確定夠穩定之後,我們可以再透過 Steam 後台去發佈到公開分支去。 116 | 117 | # 延伸閱讀 118 | - [Continuous Integration](https://martinfowler.com/articles/continuousIntegration.html) by Martin Fowler 119 | - [Continuous integration and automated testing](http://itmattersgames.com/2019/02/18/continuous-integration-and-automated-testing/) by Michele Krüger 120 | - [Unite 2015 - Continuous Integration with Unity](https://www.youtube.com/watch?v=kSXomLkMR68) by Jonathan Peppers 121 | - [Setting Up a Build Server for Unity with Jenkins](https://www.youtube.com/watch?v=4J3SmhGxO1Y) by William Chyr 122 | 123 | # 翻譯 124 | 如果你覺得這個工作坊有其價值,並通曉另一個語言,我們非常歡迎任何幫助工作坊內容進行翻譯的協助。把本儲存庫內容 clone 下來後,增加一份特定語言在地化的 README.md,例如 README-pt-BR.md,並送 PR 給我們。 125 | -------------------------------------------------------------------------------- /ContinuousIntegration/README.md: -------------------------------------------------------------------------------- 1 | # Continuous Integration 2 | Keep testing. Keep shipping. Catch bugs early. 3 | 4 | # Problem 5 | Game assets tend to have lots of dependencies across various disciplines. Projects can easily break when people change things without realizing which other things depend on them. The longer it takes to notice the problem, the longer it takes to identify and fix the change that caused it. 6 | 7 | Furthermore, at any given time there's a slightly different work-in-progress version of the project on each developer's machine, making it difficult to reproduce issues seen on another machine. Some issues might even manifest only on the target platform. 8 | 9 | ``` 10 | ¯\_(ツ)_/¯ 11 | It works on 12 | my machine. 13 | ``` 14 | 15 | # Solution 16 | We want to reduce the time between making a change and seeing it on the target platform as much as possible. We want to have checks that tell us when something broke. We want to run those checks after every change. That means we have to automate the entire process. 17 | 18 | We achieve continuous integration and delivery by 19 | 1. maintaining a single repository, 20 | 2. automating the build process, 21 | 3. automating the testing process, and 22 | 4. automating the shipping process. 23 | 24 | ## Maintaining a single repository 25 | If you're already using [version control](https://en.wikipedia.org/wiki/Version_control) you can skip ahead. Keep using what works for you. If you're not using version control yet, currently your best options are [GitHub Enterprise](https://github.com/enterprise) and [Plastic SCM](https://www.plasticscm.com/). Both [cost](https://github.com/pricing) [money](https://www.plasticscm.com/pricing), but not too much. They are well worth it. Stay away from [Unity Collaborate](https://unity.com/unity/features/collaborate). It doesn't work properly and its feature set is severly limited. It might be useful for game jams, but that's about it. 26 | 27 | ## Automating the build process 28 | If you're using GitHub Enterprise, you might want to give [GitHub Actions](https://github.com/features/actions) a try. Don't use [Unity Cloud Build](https://unity3d.com/unity/features/cloud-build), it is too basic for anything serious. We'll be using [Jenkins](https://jenkins.io/) here, which is free, open source, and runs locally. 29 | 30 | ### Jenkins 31 | We connect Jenkins to the repository and tell it to build the project *every time* a change gets committed. That's a lot of builds, so it's best to [install Jenkins](https://jenkins.io/download/thank-you-downloading-windows-installer-stable/) on an unused machine, which we will also use as our build server. 32 | 33 | After installing Jenkins and logging in to http://localhost:8080 with the initial admin password, Jenkins will ask which plugins to install. We're going to "Install suggested plugins". We won't need most of them, but it's too tedious to pick plugins manually. Speaking of plugins, everything in Jenkins is a plugin, most of them contributed by volunteers. Most of them are not *production ready*, which is why there often are several plugins for the same task, but none of them does what we need. 34 | 35 | ### Build job 36 | To make Jenkins build the game, we need to wrap the build process in a Jenkins job. We're going to bypass all the plugins and keep things as simple as possible by using the "Pipeline" template. 37 | 38 | ![Jenkins welcome page](Documentation/Jenkins1.png "It works!") 39 | ![Jenkins create job page](Documentation/Jenkins2.png "Pipelines for Mario") 40 | ![Jenkins pipeline setup](Documentation/Pipeline1.png) 41 | ![Jenkins pipeline triggers](Documentation/Pipeline2.png) 42 | ![Jenkins pipeline script](Documentation/Pipeline3.png) 43 | 44 | The job configuration in Jenkins is fairly minimal. All we need Jenkins to do is to download the [pipeline script](https://jenkins.io/doc/book/pipeline/jenkinsfile/) from our repository and then execute it. We'll put all the details in the script. 45 | 46 | In [our pipeline script](BuildScripts/Jenkins/Jenkinsfile), we 47 | 1. update to the latest revision on the repository, 48 | 2. start Unity and let it import assets, 49 | 3. run the unit tests, 50 | 4. make a build, and finally 51 | 5. upload the build. 52 | 53 | ```groovy 54 | environment { 55 | GIT = '"C:\\Program Files\\Git\\bin\\git.exe"' 56 | UNITY = '"C:\\Program Files\\Unity\\Hub\\Editor\\2018.4.14f1\\Editor\\Unity.exe"' 57 | PROJECT = 'ContinuousIntegration' 58 | PLATFORM = 'Win64' 59 | OUTPUT = 'Build/ContinuousIntegration.exe' 60 | STEAMCMD = '"W:\\Jenkins\\steamworks\\tools\\ContentBuilder\\builder\\steamcmd.exe"' 61 | STEAMUSERNAME = 'steam_username_here' 62 | STEAMPASSWORD = 'steam_password_here' 63 | STEAMSCRIPT = '"ContinuousIntegration\\BuildScripts\\Steam\\app_build.vdf"' 64 | } 65 | stage('Import Assets') { 66 | steps { 67 | bat "$UNITY -batchmode -logFile - -projectPath $PROJECT -buildTarget $PLATFORM -quit -accept-apiupdate" 68 | } 69 | } 70 | stage('Run Unit Tests') { 71 | steps { 72 | bat "$UNITY -batchmode -logFile - -projectPath $PROJECT -buildTarget $PLATFORM -runEditorTests" 73 | } 74 | } 75 | stage('Build') { 76 | steps { 77 | bat "$UNITY -batchmode -logFile - -projectPath $PROJECT -buildTarget $PLATFORM -quit -buildWindows64Player $OUTPUT" 78 | } 79 | } 80 | ``` 81 | 82 | ## Automatic the testing process 83 | Now that we're running the unit tests before each build, it is a good idea to cover as many potential problems as possible using unit tests. We start with a basic set of consistency checks. 84 | 85 | ```csharp 86 | [Test] 87 | public void ShaderHasErrors() 88 | { 89 | var infos = ShaderUtil.GetAllShaderInfo(); 90 | foreach (var info in infos) 91 | { 92 | Assert.IsFalse(info.hasErrors, "Shader '{0}' has errors.", info.name); 93 | } 94 | } 95 | ``` 96 | 97 | It takes time to develop a comprehensive test suite. As a guideline, it's a good idea to write a test each time a new issue appears in the build. The goal is to write the new test in such a way that it prevents that issue from ever happening again. 98 | 99 | ## Automating the shipping process 100 | We now have a build that passed some basic checks, but that still doesn't mean much. What we need is *real people* testing it. 101 | 102 | ``` 103 | It does not exist until you ship it. --Jonas Bötel 104 | ``` 105 | 106 | The good news is that [uploading builds to Steam](https://partner.steamgames.com/doc/sdk/uploading) is reasonably straightforward and well documented. 107 | 108 | ```groovy 109 | stage('Upload Build') { 110 | steps { 111 | echo "$STEAMCMD +login $STEAMUSERNAME $STEAMPASSWORD +run_app_build $STEAMSCRIPT" 112 | } 113 | } 114 | ``` 115 | 116 | Steam keeps all uploaded builds around *forever*, which is super useful when something goes wrong and we have to revert to an earlier build. On the other hand, we don't really want to risk breaking the game every day. So let's make sure to automatically upload our builds to a *beta* branch on Steam, by configuring the `setlive` field in the `app_build.vdf`. That way our players can opt in to play the latest ~~and greatest~~ builds if they feel brave. Every few weeks, when the build is stable enough, we can release it to the default branch using Steam's backend. 117 | 118 | # Further reading 119 | - [Continuous Integration](https://martinfowler.com/articles/continuousIntegration.html) by Martin Fowler 120 | - [Continuous integration and automated testing](http://itmattersgames.com/2019/02/18/continuous-integration-and-automated-testing/) by Michele Krüger 121 | - [Unite 2015 - Continuous Integration with Unity](https://www.youtube.com/watch?v=kSXomLkMR68) by Jonathan Peppers 122 | - [Setting Up a Build Server for Unity with Jenkins](https://www.youtube.com/watch?v=4J3SmhGxO1Y) by William Chyr 123 | 124 | # Translations 125 | 126 | - [台灣繁體中文 (zh-TW)](README-zh-TW.md) 127 | 128 | If you find this workshop useful and speak another language, I'd very much appreciate any help translating the chapters. Clone the repository, add a localized copy of the README.md, for example README-pt-BR.md, and send me a pull request. 129 | -------------------------------------------------------------------------------- /Decoupling/Assets/Data.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c33b19a6eb99a94f872bd5d8dfe2752 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Data/PlayerHealth.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &11400000 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 11500000, guid: 02d9d0c655a44d443b038a45f66f7b90, type: 3} 13 | m_Name: PlayerHealth 14 | m_EditorClassIdentifier: 15 | maxValue: 80 16 | value: 70 17 | -------------------------------------------------------------------------------- /Decoupling/Assets/Data/PlayerHealth.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a3b5cacc10e52cb4dacf477ac19a07cb 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 11400000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Data/PlayerMana.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &11400000 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 11500000, guid: 02d9d0c655a44d443b038a45f66f7b90, type: 3} 13 | m_Name: PlayerMana 14 | m_EditorClassIdentifier: 15 | maxValue: 40 16 | value: 30 17 | -------------------------------------------------------------------------------- /Decoupling/Assets/Data/PlayerMana.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 560495f8111f5724da5599562e0eedec 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 11400000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 94c164f8206ef8f4a90a95eb938204b5 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Materials/Concrete.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: Concrete 11 | m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} 12 | m_ShaderKeywords: 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _MainTex: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MetallicGlossMap: 47 | m_Texture: {fileID: 0} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _OcclusionMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _ParallaxMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | m_Floats: 59 | - _BumpScale: 1 60 | - _Cutoff: 0.5 61 | - _DetailNormalMapScale: 1 62 | - _DstBlend: 0 63 | - _GlossMapScale: 1 64 | - _Glossiness: 0.2 65 | - _GlossyReflections: 1 66 | - _Metallic: 0.2 67 | - _Mode: 0 68 | - _OcclusionStrength: 1 69 | - _Parallax: 0.02 70 | - _SmoothnessTextureChannel: 0 71 | - _SpecularHighlights: 1 72 | - _SrcBlend: 1 73 | - _UVSec: 0 74 | - _ZWrite: 1 75 | m_Colors: 76 | - _Color: {r: 0.53333336, g: 0.53333336, b: 0.53333336, a: 1} 77 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 78 | -------------------------------------------------------------------------------- /Decoupling/Assets/Materials/Concrete.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 13c269584f0b28e4f9e7adf2b52ada58 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 2100000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Materials/Jeans.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: Jeans 11 | m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} 12 | m_ShaderKeywords: 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _MainTex: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MetallicGlossMap: 47 | m_Texture: {fileID: 0} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _OcclusionMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _ParallaxMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | m_Floats: 59 | - _BumpScale: 1 60 | - _Cutoff: 0.5 61 | - _DetailNormalMapScale: 1 62 | - _DstBlend: 0 63 | - _GlossMapScale: 1 64 | - _Glossiness: 0 65 | - _GlossyReflections: 1 66 | - _Metallic: 0 67 | - _Mode: 0 68 | - _OcclusionStrength: 1 69 | - _Parallax: 0.02 70 | - _SmoothnessTextureChannel: 0 71 | - _SpecularHighlights: 1 72 | - _SrcBlend: 1 73 | - _UVSec: 0 74 | - _ZWrite: 1 75 | m_Colors: 76 | - _Color: {r: 0.06666667, g: 0.26666668, b: 0.73333335, a: 1} 77 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 78 | -------------------------------------------------------------------------------- /Decoupling/Assets/Materials/Jeans.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 123bff0b006accf4cb2faf85eeee8cd0 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 2100000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Materials/Skin.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: Skin 11 | m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} 12 | m_ShaderKeywords: 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _MainTex: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MetallicGlossMap: 47 | m_Texture: {fileID: 0} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _OcclusionMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _ParallaxMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | m_Floats: 59 | - _BumpScale: 1 60 | - _Cutoff: 0.5 61 | - _DetailNormalMapScale: 1 62 | - _DstBlend: 0 63 | - _GlossMapScale: 1 64 | - _Glossiness: 0.4 65 | - _GlossyReflections: 1 66 | - _Metallic: 0.2 67 | - _Mode: 0 68 | - _OcclusionStrength: 1 69 | - _Parallax: 0.02 70 | - _SmoothnessTextureChannel: 0 71 | - _SpecularHighlights: 1 72 | - _SrcBlend: 1 73 | - _UVSec: 0 74 | - _ZWrite: 1 75 | m_Colors: 76 | - _Color: {r: 0.8666667, g: 0.6635184, b: 0.13333334, a: 1} 77 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 78 | -------------------------------------------------------------------------------- /Decoupling/Assets/Materials/Skin.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: baa77afc7a4a3954599b64b2349e5d26 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 2100000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Prefabs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e9f49eefc742774fbca97a351230239 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Prefabs/Dungeon.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &3532432759913047904 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 3602722377457682209} 12 | - component: {fileID: 1218563337582504828} 13 | - component: {fileID: 8275703702617193374} 14 | - component: {fileID: 3538887381787578262} 15 | m_Layer: 0 16 | m_Name: Floor 17 | m_TagString: Untagged 18 | m_Icon: {fileID: 0} 19 | m_NavMeshLayer: 0 20 | m_StaticEditorFlags: 0 21 | m_IsActive: 1 22 | --- !u!4 &3602722377457682209 23 | Transform: 24 | m_ObjectHideFlags: 0 25 | m_CorrespondingSourceObject: {fileID: 0} 26 | m_PrefabInstance: {fileID: 0} 27 | m_PrefabAsset: {fileID: 0} 28 | m_GameObject: {fileID: 3532432759913047904} 29 | m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} 30 | m_LocalPosition: {x: 0, y: 0, z: 0} 31 | m_LocalScale: {x: 10, y: 10, z: 1} 32 | m_Children: [] 33 | m_Father: {fileID: 1252267959797470240} 34 | m_RootOrder: 0 35 | m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} 36 | --- !u!33 &1218563337582504828 37 | MeshFilter: 38 | m_ObjectHideFlags: 0 39 | m_CorrespondingSourceObject: {fileID: 0} 40 | m_PrefabInstance: {fileID: 0} 41 | m_PrefabAsset: {fileID: 0} 42 | m_GameObject: {fileID: 3532432759913047904} 43 | m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} 44 | --- !u!23 &8275703702617193374 45 | MeshRenderer: 46 | m_ObjectHideFlags: 0 47 | m_CorrespondingSourceObject: {fileID: 0} 48 | m_PrefabInstance: {fileID: 0} 49 | m_PrefabAsset: {fileID: 0} 50 | m_GameObject: {fileID: 3532432759913047904} 51 | m_Enabled: 1 52 | m_CastShadows: 1 53 | m_ReceiveShadows: 1 54 | m_DynamicOccludee: 1 55 | m_MotionVectors: 1 56 | m_LightProbeUsage: 1 57 | m_ReflectionProbeUsage: 1 58 | m_RenderingLayerMask: 1 59 | m_RendererPriority: 0 60 | m_Materials: 61 | - {fileID: 2100000, guid: 13c269584f0b28e4f9e7adf2b52ada58, type: 2} 62 | m_StaticBatchInfo: 63 | firstSubMesh: 0 64 | subMeshCount: 0 65 | m_StaticBatchRoot: {fileID: 0} 66 | m_ProbeAnchor: {fileID: 0} 67 | m_LightProbeVolumeOverride: {fileID: 0} 68 | m_ScaleInLightmap: 1 69 | m_PreserveUVs: 0 70 | m_IgnoreNormalsForChartDetection: 0 71 | m_ImportantGI: 0 72 | m_StitchLightmapSeams: 0 73 | m_SelectedEditorRenderState: 3 74 | m_MinimumChartSize: 4 75 | m_AutoUVMaxDistance: 0.5 76 | m_AutoUVMaxAngle: 89 77 | m_LightmapParameters: {fileID: 0} 78 | m_SortingLayerID: 0 79 | m_SortingLayer: 0 80 | m_SortingOrder: 0 81 | --- !u!64 &3538887381787578262 82 | MeshCollider: 83 | m_ObjectHideFlags: 0 84 | m_CorrespondingSourceObject: {fileID: 0} 85 | m_PrefabInstance: {fileID: 0} 86 | m_PrefabAsset: {fileID: 0} 87 | m_GameObject: {fileID: 3532432759913047904} 88 | m_Material: {fileID: 0} 89 | m_IsTrigger: 0 90 | m_Enabled: 1 91 | serializedVersion: 3 92 | m_Convex: 0 93 | m_CookingOptions: -1 94 | m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} 95 | --- !u!1 &8762435044969880554 96 | GameObject: 97 | m_ObjectHideFlags: 0 98 | m_CorrespondingSourceObject: {fileID: 0} 99 | m_PrefabInstance: {fileID: 0} 100 | m_PrefabAsset: {fileID: 0} 101 | serializedVersion: 6 102 | m_Component: 103 | - component: {fileID: 1252267959797470240} 104 | m_Layer: 0 105 | m_Name: Dungeon 106 | m_TagString: Untagged 107 | m_Icon: {fileID: 0} 108 | m_NavMeshLayer: 0 109 | m_StaticEditorFlags: 0 110 | m_IsActive: 1 111 | --- !u!4 &1252267959797470240 112 | Transform: 113 | m_ObjectHideFlags: 0 114 | m_CorrespondingSourceObject: {fileID: 0} 115 | m_PrefabInstance: {fileID: 0} 116 | m_PrefabAsset: {fileID: 0} 117 | m_GameObject: {fileID: 8762435044969880554} 118 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 119 | m_LocalPosition: {x: 0, y: 0, z: 0} 120 | m_LocalScale: {x: 1, y: 1, z: 1} 121 | m_Children: 122 | - {fileID: 3602722377457682209} 123 | m_Father: {fileID: 0} 124 | m_RootOrder: 0 125 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 126 | -------------------------------------------------------------------------------- /Decoupling/Assets/Prefabs/Dungeon.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b4b9dda9117fe54aa82f756ba918230 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Decoupling/Assets/Prefabs/Player.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 53efc4fa4d90ed04fbef8595d4fd7693 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Decoupling/Assets/Prefabs/ProgressBar.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &3401348595165950329 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 3401348595165950330} 12 | m_Layer: 5 13 | m_Name: Fill Area 14 | m_TagString: Untagged 15 | m_Icon: {fileID: 0} 16 | m_NavMeshLayer: 0 17 | m_StaticEditorFlags: 0 18 | m_IsActive: 1 19 | --- !u!224 &3401348595165950330 20 | RectTransform: 21 | m_ObjectHideFlags: 0 22 | m_CorrespondingSourceObject: {fileID: 0} 23 | m_PrefabInstance: {fileID: 0} 24 | m_PrefabAsset: {fileID: 0} 25 | m_GameObject: {fileID: 3401348595165950329} 26 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 27 | m_LocalPosition: {x: 0, y: 0, z: 0} 28 | m_LocalScale: {x: 1, y: 1, z: 1} 29 | m_Children: 30 | - {fileID: 3401348595976271525} 31 | m_Father: {fileID: 3401348596094318139} 32 | m_RootOrder: 1 33 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 34 | m_AnchorMin: {x: 0, y: 0} 35 | m_AnchorMax: {x: 1, y: 1} 36 | m_AnchoredPosition: {x: 0, y: 0} 37 | m_SizeDelta: {x: 0, y: 0} 38 | m_Pivot: {x: 0.5, y: 0.5} 39 | --- !u!1 &3401348595410424767 40 | GameObject: 41 | m_ObjectHideFlags: 0 42 | m_CorrespondingSourceObject: {fileID: 0} 43 | m_PrefabInstance: {fileID: 0} 44 | m_PrefabAsset: {fileID: 0} 45 | serializedVersion: 6 46 | m_Component: 47 | - component: {fileID: 3401348595410424760} 48 | - component: {fileID: 3401348595410424762} 49 | - component: {fileID: 3401348595410424761} 50 | m_Layer: 5 51 | m_Name: Background 52 | m_TagString: Untagged 53 | m_Icon: {fileID: 0} 54 | m_NavMeshLayer: 0 55 | m_StaticEditorFlags: 0 56 | m_IsActive: 1 57 | --- !u!224 &3401348595410424760 58 | RectTransform: 59 | m_ObjectHideFlags: 0 60 | m_CorrespondingSourceObject: {fileID: 0} 61 | m_PrefabInstance: {fileID: 0} 62 | m_PrefabAsset: {fileID: 0} 63 | m_GameObject: {fileID: 3401348595410424767} 64 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 65 | m_LocalPosition: {x: 0, y: 0, z: 0} 66 | m_LocalScale: {x: 1, y: 1, z: 1} 67 | m_Children: [] 68 | m_Father: {fileID: 3401348596094318139} 69 | m_RootOrder: 0 70 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 71 | m_AnchorMin: {x: 0, y: 0} 72 | m_AnchorMax: {x: 1, y: 1} 73 | m_AnchoredPosition: {x: 0, y: 0} 74 | m_SizeDelta: {x: 0, y: 0} 75 | m_Pivot: {x: 0.5, y: 0.5} 76 | --- !u!222 &3401348595410424762 77 | CanvasRenderer: 78 | m_ObjectHideFlags: 0 79 | m_CorrespondingSourceObject: {fileID: 0} 80 | m_PrefabInstance: {fileID: 0} 81 | m_PrefabAsset: {fileID: 0} 82 | m_GameObject: {fileID: 3401348595410424767} 83 | m_CullTransparentMesh: 0 84 | --- !u!114 &3401348595410424761 85 | MonoBehaviour: 86 | m_ObjectHideFlags: 0 87 | m_CorrespondingSourceObject: {fileID: 0} 88 | m_PrefabInstance: {fileID: 0} 89 | m_PrefabAsset: {fileID: 0} 90 | m_GameObject: {fileID: 3401348595410424767} 91 | m_Enabled: 1 92 | m_EditorHideFlags: 0 93 | m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} 94 | m_Name: 95 | m_EditorClassIdentifier: 96 | m_Material: {fileID: 0} 97 | m_Color: {r: 0.133, g: 0.133, b: 0.133, a: 0.49803922} 98 | m_RaycastTarget: 1 99 | m_OnCullStateChanged: 100 | m_PersistentCalls: 101 | m_Calls: [] 102 | m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} 103 | m_Type: 1 104 | m_PreserveAspect: 0 105 | m_FillCenter: 1 106 | m_FillMethod: 4 107 | m_FillAmount: 1 108 | m_FillClockwise: 1 109 | m_FillOrigin: 0 110 | m_UseSpriteMesh: 0 111 | --- !u!1 &3401348595976271524 112 | GameObject: 113 | m_ObjectHideFlags: 0 114 | m_CorrespondingSourceObject: {fileID: 0} 115 | m_PrefabInstance: {fileID: 0} 116 | m_PrefabAsset: {fileID: 0} 117 | serializedVersion: 6 118 | m_Component: 119 | - component: {fileID: 3401348595976271525} 120 | - component: {fileID: 3401348595976271527} 121 | - component: {fileID: 3401348595976271526} 122 | m_Layer: 5 123 | m_Name: Fill 124 | m_TagString: Untagged 125 | m_Icon: {fileID: 0} 126 | m_NavMeshLayer: 0 127 | m_StaticEditorFlags: 0 128 | m_IsActive: 1 129 | --- !u!224 &3401348595976271525 130 | RectTransform: 131 | m_ObjectHideFlags: 0 132 | m_CorrespondingSourceObject: {fileID: 0} 133 | m_PrefabInstance: {fileID: 0} 134 | m_PrefabAsset: {fileID: 0} 135 | m_GameObject: {fileID: 3401348595976271524} 136 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 137 | m_LocalPosition: {x: 0, y: 0, z: 0} 138 | m_LocalScale: {x: 1, y: 1, z: 1} 139 | m_Children: [] 140 | m_Father: {fileID: 3401348595165950330} 141 | m_RootOrder: 0 142 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 143 | m_AnchorMin: {x: 0, y: 0} 144 | m_AnchorMax: {x: 0, y: 0} 145 | m_AnchoredPosition: {x: 0, y: 0} 146 | m_SizeDelta: {x: 0, y: 0} 147 | m_Pivot: {x: 0.5, y: 0.5} 148 | --- !u!222 &3401348595976271527 149 | CanvasRenderer: 150 | m_ObjectHideFlags: 0 151 | m_CorrespondingSourceObject: {fileID: 0} 152 | m_PrefabInstance: {fileID: 0} 153 | m_PrefabAsset: {fileID: 0} 154 | m_GameObject: {fileID: 3401348595976271524} 155 | m_CullTransparentMesh: 0 156 | --- !u!114 &3401348595976271526 157 | MonoBehaviour: 158 | m_ObjectHideFlags: 0 159 | m_CorrespondingSourceObject: {fileID: 0} 160 | m_PrefabInstance: {fileID: 0} 161 | m_PrefabAsset: {fileID: 0} 162 | m_GameObject: {fileID: 3401348595976271524} 163 | m_Enabled: 1 164 | m_EditorHideFlags: 0 165 | m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} 166 | m_Name: 167 | m_EditorClassIdentifier: 168 | m_Material: {fileID: 0} 169 | m_Color: {r: 1, g: 1, b: 1, a: 1} 170 | m_RaycastTarget: 1 171 | m_OnCullStateChanged: 172 | m_PersistentCalls: 173 | m_Calls: [] 174 | m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} 175 | m_Type: 1 176 | m_PreserveAspect: 0 177 | m_FillCenter: 1 178 | m_FillMethod: 4 179 | m_FillAmount: 1 180 | m_FillClockwise: 1 181 | m_FillOrigin: 0 182 | m_UseSpriteMesh: 0 183 | --- !u!1 &3401348596094318138 184 | GameObject: 185 | m_ObjectHideFlags: 0 186 | m_CorrespondingSourceObject: {fileID: 0} 187 | m_PrefabInstance: {fileID: 0} 188 | m_PrefabAsset: {fileID: 0} 189 | serializedVersion: 6 190 | m_Component: 191 | - component: {fileID: 3401348596094318139} 192 | - component: {fileID: 3401348596094318148} 193 | - component: {fileID: 3720783969492196593} 194 | m_Layer: 5 195 | m_Name: ProgressBar 196 | m_TagString: Untagged 197 | m_Icon: {fileID: 0} 198 | m_NavMeshLayer: 0 199 | m_StaticEditorFlags: 0 200 | m_IsActive: 1 201 | --- !u!224 &3401348596094318139 202 | RectTransform: 203 | m_ObjectHideFlags: 0 204 | m_CorrespondingSourceObject: {fileID: 0} 205 | m_PrefabInstance: {fileID: 0} 206 | m_PrefabAsset: {fileID: 0} 207 | m_GameObject: {fileID: 3401348596094318138} 208 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 209 | m_LocalPosition: {x: 0, y: 0, z: 0} 210 | m_LocalScale: {x: 1, y: 1, z: 1} 211 | m_Children: 212 | - {fileID: 3401348595410424760} 213 | - {fileID: 3401348595165950330} 214 | m_Father: {fileID: 0} 215 | m_RootOrder: 0 216 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 217 | m_AnchorMin: {x: 0, y: 0} 218 | m_AnchorMax: {x: 0, y: 0} 219 | m_AnchoredPosition: {x: 0, y: 0} 220 | m_SizeDelta: {x: 100, y: 100} 221 | m_Pivot: {x: 0, y: 0} 222 | --- !u!114 &3401348596094318148 223 | MonoBehaviour: 224 | m_ObjectHideFlags: 0 225 | m_CorrespondingSourceObject: {fileID: 0} 226 | m_PrefabInstance: {fileID: 0} 227 | m_PrefabAsset: {fileID: 0} 228 | m_GameObject: {fileID: 3401348596094318138} 229 | m_Enabled: 1 230 | m_EditorHideFlags: 0 231 | m_Script: {fileID: -113659843, guid: f70555f144d8491a825f0804e09c671c, type: 3} 232 | m_Name: 233 | m_EditorClassIdentifier: 234 | m_Navigation: 235 | m_Mode: 0 236 | m_SelectOnUp: {fileID: 0} 237 | m_SelectOnDown: {fileID: 0} 238 | m_SelectOnLeft: {fileID: 0} 239 | m_SelectOnRight: {fileID: 0} 240 | m_Transition: 0 241 | m_Colors: 242 | m_NormalColor: {r: 1, g: 1, b: 1, a: 1} 243 | m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 244 | m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} 245 | m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} 246 | m_ColorMultiplier: 1 247 | m_FadeDuration: 0.1 248 | m_SpriteState: 249 | m_HighlightedSprite: {fileID: 0} 250 | m_PressedSprite: {fileID: 0} 251 | m_DisabledSprite: {fileID: 0} 252 | m_AnimationTriggers: 253 | m_NormalTrigger: Normal 254 | m_HighlightedTrigger: Highlighted 255 | m_PressedTrigger: Pressed 256 | m_DisabledTrigger: Disabled 257 | m_Interactable: 0 258 | m_TargetGraphic: {fileID: 0} 259 | m_FillRect: {fileID: 3401348595976271525} 260 | m_HandleRect: {fileID: 0} 261 | m_Direction: 2 262 | m_MinValue: 0 263 | m_MaxValue: 1 264 | m_WholeNumbers: 0 265 | m_Value: 0.5 266 | m_OnValueChanged: 267 | m_PersistentCalls: 268 | m_Calls: [] 269 | --- !u!114 &3720783969492196593 270 | MonoBehaviour: 271 | m_ObjectHideFlags: 0 272 | m_CorrespondingSourceObject: {fileID: 0} 273 | m_PrefabInstance: {fileID: 0} 274 | m_PrefabAsset: {fileID: 0} 275 | m_GameObject: {fileID: 3401348596094318138} 276 | m_Enabled: 1 277 | m_EditorHideFlags: 0 278 | m_Script: {fileID: 11500000, guid: 8b6eb4e4f61e21a4c8facd7586530716, type: 3} 279 | m_Name: 280 | m_EditorClassIdentifier: 281 | slider: {fileID: 3401348596094318148} 282 | data: {fileID: 0} 283 | -------------------------------------------------------------------------------- /Decoupling/Assets/Prefabs/ProgressBar.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 61316d189acf74e4b8bf00aef51d499d 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Decoupling/Assets/Prefabs/UI.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e9e64589632d17245b61bfc66c2546bb 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Decoupling/Assets/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 67057b6d891904d44890faf1a3bfa29d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Scenes/Game.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9fc0d4010bbf28b4594072e72b8655ab 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Decoupling/Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 69aa69415ad4980428ca71a2450714da 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Scripts/BoundedFloat.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | [CreateAssetMenu] 4 | public sealed class BoundedFloat : ScriptableObject 5 | { 6 | public float maxValue; 7 | public float value; 8 | } 9 | -------------------------------------------------------------------------------- /Decoupling/Assets/Scripts/BoundedFloat.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 02d9d0c655a44d443b038a45f66f7b90 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Decoupling/Assets/Scripts/Player.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public sealed class Player : MonoBehaviour 4 | { 5 | [Header("Setup")] 6 | [SerializeField] private CharacterController controller; 7 | [SerializeField] private float speed; 8 | 9 | [Header("Stats")] 10 | [SerializeField] private BoundedFloat health; 11 | [SerializeField] private BoundedFloat mana; 12 | 13 | void Update() 14 | { 15 | var deltaX = Input.GetAxis("Horizontal"); 16 | var deltaZ = Input.GetAxis("Vertical"); 17 | var forward = new Vector3(deltaX, 0, deltaZ); 18 | 19 | if (forward.sqrMagnitude > 0.01f) 20 | { 21 | transform.localRotation = Quaternion.LookRotation(forward, Vector3.up); 22 | } 23 | 24 | controller.SimpleMove(forward * speed); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Decoupling/Assets/Scripts/Player.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 575110446718f4b419845f814e067c88 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Decoupling/Assets/Scripts/StatusBar.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | 4 | public sealed class StatusBar : MonoBehaviour 5 | { 6 | [SerializeField] private Slider slider; 7 | [SerializeField] private BoundedFloat data; 8 | 9 | void Update() 10 | { 11 | slider.maxValue = data.maxValue; 12 | slider.value = data.value; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Decoupling/Assets/Scripts/StatusBar.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8b6eb4e4f61e21a4c8facd7586530716 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Decoupling/Assets/csc.rsp: -------------------------------------------------------------------------------- 1 | -nowarn:649 2 | -warnaserror -------------------------------------------------------------------------------- /Decoupling/Assets/csc.rsp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ce0327f0cb4406945b970daf8149259c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Decoupling/Documentation/Decoupling.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | 3 | UI; 4 | Player; 5 | 6 | node [shape=Rect] 7 | PlayerHealth; 8 | PlayerMana; 9 | 10 | UI -> PlayerHealth; 11 | UI -> PlayerMana; 12 | Player -> PlayerHealth; 13 | Player -> PlayerMana; 14 | } 15 | -------------------------------------------------------------------------------- /Decoupling/Documentation/Decoupling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/Decoupling/Documentation/Decoupling.png -------------------------------------------------------------------------------- /Decoupling/Documentation/Dependencies.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | 3 | UI -> Player; 4 | UI -> Inventory; 5 | UI -> Equipment; 6 | UI -> TalentTree; 7 | UI -> CharacterStats; 8 | UI -> SpellBook; 9 | UI -> BuffManager; 10 | 11 | Player -> Inventory; 12 | Player -> Equipment; 13 | Player -> TalentTree; 14 | Player -> CharacterStats; 15 | Player -> SpellBook; 16 | Player -> BuffManager; 17 | 18 | Inventory -> Items; 19 | Inventory -> Equipment; 20 | 21 | Equipment -> Inventory; 22 | Equipment -> CharacterStats; 23 | Equipment -> SpellBook; 24 | Equipment -> BuffManager; 25 | 26 | TalentTree -> Player; 27 | TalentTree -> CharacterStats; 28 | TalentTree -> SpellBook; 29 | TalentTree -> BuffManager; 30 | 31 | SpellBook -> Inventory; 32 | SpellBook -> Spells; 33 | 34 | BuffManager -> CharacterStats; 35 | BuffManager -> EnemyManager; 36 | 37 | Spells -> BuffManager; 38 | Spells -> EnemyManager; 39 | Spells -> CharacterStats; 40 | 41 | Items -> Sockets; 42 | Items -> Gems; 43 | Sockets -> Gems; 44 | 45 | EnemyManager -> Player; 46 | } 47 | -------------------------------------------------------------------------------- /Decoupling/Documentation/Dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/Decoupling/Documentation/Dependencies.png -------------------------------------------------------------------------------- /Decoupling/Documentation/HealthBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/Decoupling/Documentation/HealthBar.png -------------------------------------------------------------------------------- /Decoupling/Documentation/HealthBar2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/Decoupling/Documentation/HealthBar2.png -------------------------------------------------------------------------------- /Decoupling/Documentation/ScriptableObjects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/Decoupling/Documentation/ScriptableObjects.png -------------------------------------------------------------------------------- /Decoupling/Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.package-manager-ui": "2.0.8", 4 | "com.unity.modules.audio": "1.0.0", 5 | "com.unity.modules.imgui": "1.0.0", 6 | "com.unity.modules.physics": "1.0.0", 7 | "com.unity.modules.ui": "1.0.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!11 &1 4 | AudioManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Volume: 1 8 | Rolloff Scale: 1 9 | Doppler Factor: 1 10 | Default Speaker Mode: 2 11 | m_SampleRate: 0 12 | m_DSPBufferSize: 1024 13 | m_VirtualVoiceCount: 512 14 | m_RealVoiceCount: 32 15 | m_SpatializerPlugin: 16 | m_AmbisonicDecoderPlugin: 17 | m_DisableAudio: 0 18 | m_VirtualizeEffects: 1 19 | m_RequestedDSPBufferSize: 1024 20 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!236 &1 4 | ClusterInputManager: 5 | m_ObjectHideFlags: 0 6 | m_Inputs: [] 7 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!55 &1 4 | PhysicsManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 11 7 | m_Gravity: {x: 0, y: -9.81, z: 0} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_BounceThreshold: 2 10 | m_SleepThreshold: 0.005 11 | m_DefaultContactOffset: 0.01 12 | m_DefaultSolverIterations: 6 13 | m_DefaultSolverVelocityIterations: 1 14 | m_QueriesHitBackfaces: 0 15 | m_QueriesHitTriggers: 1 16 | m_EnableAdaptiveForce: 0 17 | m_ClothInterCollisionDistance: 0 18 | m_ClothInterCollisionStiffness: 0 19 | m_ContactsGeneration: 1 20 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21 | m_AutoSimulation: 1 22 | m_AutoSyncTransforms: 0 23 | m_ReuseCollisionCallbacks: 1 24 | m_ClothInterCollisionSettingsToggle: 0 25 | m_ContactPairsMode: 0 26 | m_BroadphaseType: 0 27 | m_WorldBounds: 28 | m_Center: {x: 0, y: 0, z: 0} 29 | m_Extent: {x: 250, y: 250, z: 250} 30 | m_WorldSubdivisions: 8 31 | m_FrictionType: 0 32 | m_EnableEnhancedDeterminism: 0 33 | m_EnableUnifiedHeightmaps: 1 34 | m_DefaultMaxAngluarSpeed: 7 35 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1045 &1 4 | EditorBuildSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Scenes: 8 | - enabled: 1 9 | path: Assets/Scenes/Game.unity 10 | guid: 9fc0d4010bbf28b4594072e72b8655ab 11 | m_configObjects: {} 12 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!159 &1 4 | EditorSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 7 7 | m_ExternalVersionControlSupport: Visible Meta Files 8 | m_SerializationMode: 2 9 | m_LineEndingsForNewScripts: 2 10 | m_DefaultBehaviorMode: 0 11 | m_PrefabRegularEnvironment: {fileID: 0} 12 | m_PrefabUIEnvironment: {fileID: 0} 13 | m_SpritePackerMode: 0 14 | m_SpritePackerPaddingPower: 1 15 | m_EtcTextureCompressorBehavior: 1 16 | m_EtcTextureFastCompressor: 1 17 | m_EtcTextureNormalCompressor: 2 18 | m_EtcTextureBestCompressor: 4 19 | m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp 20 | m_ProjectGenerationRootNamespace: 21 | m_CollabEditorSettings: 22 | inProgressEnabled: 1 23 | m_EnableTextureStreamingInPlayMode: 1 24 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!30 &1 4 | GraphicsSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 12 7 | m_Deferred: 8 | m_Mode: 1 9 | m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} 10 | m_DeferredReflections: 11 | m_Mode: 1 12 | m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} 13 | m_ScreenSpaceShadows: 14 | m_Mode: 1 15 | m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} 16 | m_LegacyDeferred: 17 | m_Mode: 1 18 | m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} 19 | m_DepthNormals: 20 | m_Mode: 1 21 | m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} 22 | m_MotionVectors: 23 | m_Mode: 1 24 | m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} 25 | m_LightHalo: 26 | m_Mode: 1 27 | m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} 28 | m_LensFlare: 29 | m_Mode: 1 30 | m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} 31 | m_AlwaysIncludedShaders: 32 | - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} 33 | - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} 34 | - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} 35 | - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} 36 | - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} 37 | - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} 38 | m_PreloadedShaders: [] 39 | m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, 40 | type: 0} 41 | m_CustomRenderPipeline: {fileID: 0} 42 | m_TransparencySortMode: 0 43 | m_TransparencySortAxis: {x: 0, y: 0, z: 1} 44 | m_DefaultRenderingPath: 1 45 | m_DefaultMobileRenderingPath: 1 46 | m_TierSettings: [] 47 | m_LightmapStripping: 0 48 | m_FogStripping: 0 49 | m_InstancingStripping: 0 50 | m_LightmapKeepPlain: 1 51 | m_LightmapKeepDirCombined: 1 52 | m_LightmapKeepDynamicPlain: 1 53 | m_LightmapKeepDynamicDirCombined: 1 54 | m_LightmapKeepShadowMask: 1 55 | m_LightmapKeepSubtractive: 1 56 | m_FogKeepLinear: 1 57 | m_FogKeepExp: 1 58 | m_FogKeepExp2: 1 59 | m_AlbedoSwatchInfos: [] 60 | m_LightsUseLinearIntensity: 0 61 | m_LightsUseColorTemperature: 0 62 | m_LogWhenShaderIsCompiled: 0 63 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!13 &1 4 | InputManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Axes: 8 | - serializedVersion: 3 9 | m_Name: Horizontal 10 | descriptiveName: 11 | descriptiveNegativeName: 12 | negativeButton: left 13 | positiveButton: right 14 | altNegativeButton: a 15 | altPositiveButton: d 16 | gravity: 3 17 | dead: 0.001 18 | sensitivity: 3 19 | snap: 1 20 | invert: 0 21 | type: 0 22 | axis: 0 23 | joyNum: 0 24 | - serializedVersion: 3 25 | m_Name: Vertical 26 | descriptiveName: 27 | descriptiveNegativeName: 28 | negativeButton: down 29 | positiveButton: up 30 | altNegativeButton: s 31 | altPositiveButton: w 32 | gravity: 3 33 | dead: 0.001 34 | sensitivity: 3 35 | snap: 1 36 | invert: 0 37 | type: 0 38 | axis: 0 39 | joyNum: 0 40 | - serializedVersion: 3 41 | m_Name: Fire1 42 | descriptiveName: 43 | descriptiveNegativeName: 44 | negativeButton: 45 | positiveButton: left ctrl 46 | altNegativeButton: 47 | altPositiveButton: mouse 0 48 | gravity: 1000 49 | dead: 0.001 50 | sensitivity: 1000 51 | snap: 0 52 | invert: 0 53 | type: 0 54 | axis: 0 55 | joyNum: 0 56 | - serializedVersion: 3 57 | m_Name: Fire2 58 | descriptiveName: 59 | descriptiveNegativeName: 60 | negativeButton: 61 | positiveButton: left alt 62 | altNegativeButton: 63 | altPositiveButton: mouse 1 64 | gravity: 1000 65 | dead: 0.001 66 | sensitivity: 1000 67 | snap: 0 68 | invert: 0 69 | type: 0 70 | axis: 0 71 | joyNum: 0 72 | - serializedVersion: 3 73 | m_Name: Fire3 74 | descriptiveName: 75 | descriptiveNegativeName: 76 | negativeButton: 77 | positiveButton: left shift 78 | altNegativeButton: 79 | altPositiveButton: mouse 2 80 | gravity: 1000 81 | dead: 0.001 82 | sensitivity: 1000 83 | snap: 0 84 | invert: 0 85 | type: 0 86 | axis: 0 87 | joyNum: 0 88 | - serializedVersion: 3 89 | m_Name: Jump 90 | descriptiveName: 91 | descriptiveNegativeName: 92 | negativeButton: 93 | positiveButton: space 94 | altNegativeButton: 95 | altPositiveButton: 96 | gravity: 1000 97 | dead: 0.001 98 | sensitivity: 1000 99 | snap: 0 100 | invert: 0 101 | type: 0 102 | axis: 0 103 | joyNum: 0 104 | - serializedVersion: 3 105 | m_Name: Mouse X 106 | descriptiveName: 107 | descriptiveNegativeName: 108 | negativeButton: 109 | positiveButton: 110 | altNegativeButton: 111 | altPositiveButton: 112 | gravity: 0 113 | dead: 0 114 | sensitivity: 0.1 115 | snap: 0 116 | invert: 0 117 | type: 1 118 | axis: 0 119 | joyNum: 0 120 | - serializedVersion: 3 121 | m_Name: Mouse Y 122 | descriptiveName: 123 | descriptiveNegativeName: 124 | negativeButton: 125 | positiveButton: 126 | altNegativeButton: 127 | altPositiveButton: 128 | gravity: 0 129 | dead: 0 130 | sensitivity: 0.1 131 | snap: 0 132 | invert: 0 133 | type: 1 134 | axis: 1 135 | joyNum: 0 136 | - serializedVersion: 3 137 | m_Name: Mouse ScrollWheel 138 | descriptiveName: 139 | descriptiveNegativeName: 140 | negativeButton: 141 | positiveButton: 142 | altNegativeButton: 143 | altPositiveButton: 144 | gravity: 0 145 | dead: 0 146 | sensitivity: 0.1 147 | snap: 0 148 | invert: 0 149 | type: 1 150 | axis: 2 151 | joyNum: 0 152 | - serializedVersion: 3 153 | m_Name: Horizontal 154 | descriptiveName: 155 | descriptiveNegativeName: 156 | negativeButton: 157 | positiveButton: 158 | altNegativeButton: 159 | altPositiveButton: 160 | gravity: 0 161 | dead: 0.19 162 | sensitivity: 1 163 | snap: 0 164 | invert: 0 165 | type: 2 166 | axis: 0 167 | joyNum: 0 168 | - serializedVersion: 3 169 | m_Name: Vertical 170 | descriptiveName: 171 | descriptiveNegativeName: 172 | negativeButton: 173 | positiveButton: 174 | altNegativeButton: 175 | altPositiveButton: 176 | gravity: 0 177 | dead: 0.19 178 | sensitivity: 1 179 | snap: 0 180 | invert: 1 181 | type: 2 182 | axis: 1 183 | joyNum: 0 184 | - serializedVersion: 3 185 | m_Name: Fire1 186 | descriptiveName: 187 | descriptiveNegativeName: 188 | negativeButton: 189 | positiveButton: joystick button 0 190 | altNegativeButton: 191 | altPositiveButton: 192 | gravity: 1000 193 | dead: 0.001 194 | sensitivity: 1000 195 | snap: 0 196 | invert: 0 197 | type: 0 198 | axis: 0 199 | joyNum: 0 200 | - serializedVersion: 3 201 | m_Name: Fire2 202 | descriptiveName: 203 | descriptiveNegativeName: 204 | negativeButton: 205 | positiveButton: joystick button 1 206 | altNegativeButton: 207 | altPositiveButton: 208 | gravity: 1000 209 | dead: 0.001 210 | sensitivity: 1000 211 | snap: 0 212 | invert: 0 213 | type: 0 214 | axis: 0 215 | joyNum: 0 216 | - serializedVersion: 3 217 | m_Name: Fire3 218 | descriptiveName: 219 | descriptiveNegativeName: 220 | negativeButton: 221 | positiveButton: joystick button 2 222 | altNegativeButton: 223 | altPositiveButton: 224 | gravity: 1000 225 | dead: 0.001 226 | sensitivity: 1000 227 | snap: 0 228 | invert: 0 229 | type: 0 230 | axis: 0 231 | joyNum: 0 232 | - serializedVersion: 3 233 | m_Name: Jump 234 | descriptiveName: 235 | descriptiveNegativeName: 236 | negativeButton: 237 | positiveButton: joystick button 3 238 | altNegativeButton: 239 | altPositiveButton: 240 | gravity: 1000 241 | dead: 0.001 242 | sensitivity: 1000 243 | snap: 0 244 | invert: 0 245 | type: 0 246 | axis: 0 247 | joyNum: 0 248 | - serializedVersion: 3 249 | m_Name: Submit 250 | descriptiveName: 251 | descriptiveNegativeName: 252 | negativeButton: 253 | positiveButton: return 254 | altNegativeButton: 255 | altPositiveButton: joystick button 0 256 | gravity: 1000 257 | dead: 0.001 258 | sensitivity: 1000 259 | snap: 0 260 | invert: 0 261 | type: 0 262 | axis: 0 263 | joyNum: 0 264 | - serializedVersion: 3 265 | m_Name: Submit 266 | descriptiveName: 267 | descriptiveNegativeName: 268 | negativeButton: 269 | positiveButton: enter 270 | altNegativeButton: 271 | altPositiveButton: space 272 | gravity: 1000 273 | dead: 0.001 274 | sensitivity: 1000 275 | snap: 0 276 | invert: 0 277 | type: 0 278 | axis: 0 279 | joyNum: 0 280 | - serializedVersion: 3 281 | m_Name: Cancel 282 | descriptiveName: 283 | descriptiveNegativeName: 284 | negativeButton: 285 | positiveButton: escape 286 | altNegativeButton: 287 | altPositiveButton: joystick button 1 288 | gravity: 1000 289 | dead: 0.001 290 | sensitivity: 1000 291 | snap: 0 292 | invert: 0 293 | type: 0 294 | axis: 0 295 | joyNum: 0 296 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!126 &1 4 | NavMeshProjectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | areas: 8 | - name: Walkable 9 | cost: 1 10 | - name: Not Walkable 11 | cost: 1 12 | - name: Jump 13 | cost: 2 14 | - name: 15 | cost: 1 16 | - name: 17 | cost: 1 18 | - name: 19 | cost: 1 20 | - name: 21 | cost: 1 22 | - name: 23 | cost: 1 24 | - name: 25 | cost: 1 26 | - name: 27 | cost: 1 28 | - name: 29 | cost: 1 30 | - name: 31 | cost: 1 32 | - name: 33 | cost: 1 34 | - name: 35 | cost: 1 36 | - name: 37 | cost: 1 38 | - name: 39 | cost: 1 40 | - name: 41 | cost: 1 42 | - name: 43 | cost: 1 44 | - name: 45 | cost: 1 46 | - name: 47 | cost: 1 48 | - name: 49 | cost: 1 50 | - name: 51 | cost: 1 52 | - name: 53 | cost: 1 54 | - name: 55 | cost: 1 56 | - name: 57 | cost: 1 58 | - name: 59 | cost: 1 60 | - name: 61 | cost: 1 62 | - name: 63 | cost: 1 64 | - name: 65 | cost: 1 66 | - name: 67 | cost: 1 68 | - name: 69 | cost: 1 70 | - name: 71 | cost: 1 72 | m_LastAgentTypeID: -887442657 73 | m_Settings: 74 | - serializedVersion: 2 75 | agentTypeID: 0 76 | agentRadius: 0.5 77 | agentHeight: 2 78 | agentSlope: 45 79 | agentClimb: 0.75 80 | ledgeDropHeight: 0 81 | maxJumpAcrossDistance: 0 82 | minRegionArea: 2 83 | manualCellSize: 0 84 | cellSize: 0.16666667 85 | manualTileSize: 0 86 | tileSize: 256 87 | accuratePlacement: 0 88 | debug: 89 | m_Flags: 0 90 | m_SettingNames: 91 | - Humanoid 92 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!19 &1 4 | Physics2DSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 4 7 | m_Gravity: {x: 0, y: -9.81} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_VelocityIterations: 8 10 | m_PositionIterations: 3 11 | m_VelocityThreshold: 1 12 | m_MaxLinearCorrection: 0.2 13 | m_MaxAngularCorrection: 8 14 | m_MaxTranslationSpeed: 100 15 | m_MaxRotationSpeed: 360 16 | m_BaumgarteScale: 0.2 17 | m_BaumgarteTimeOfImpactScale: 0.75 18 | m_TimeToSleep: 0.5 19 | m_LinearSleepTolerance: 0.01 20 | m_AngularSleepTolerance: 2 21 | m_DefaultContactOffset: 0.01 22 | m_JobOptions: 23 | serializedVersion: 2 24 | useMultithreading: 0 25 | useConsistencySorting: 0 26 | m_InterpolationPosesPerJob: 100 27 | m_NewContactsPerJob: 30 28 | m_CollideContactsPerJob: 100 29 | m_ClearFlagsPerJob: 200 30 | m_ClearBodyForcesPerJob: 200 31 | m_SyncDiscreteFixturesPerJob: 50 32 | m_SyncContinuousFixturesPerJob: 50 33 | m_FindNearestContactsPerJob: 100 34 | m_UpdateTriggerContactsPerJob: 100 35 | m_IslandSolverCostThreshold: 100 36 | m_IslandSolverBodyCostScale: 1 37 | m_IslandSolverContactCostScale: 10 38 | m_IslandSolverJointCostScale: 10 39 | m_IslandSolverBodiesPerJob: 50 40 | m_IslandSolverContactsPerJob: 50 41 | m_AutoSimulation: 1 42 | m_QueriesHitTriggers: 1 43 | m_QueriesStartInColliders: 1 44 | m_CallbacksOnDisable: 1 45 | m_ReuseCollisionCallbacks: 1 46 | m_AutoSyncTransforms: 0 47 | m_AlwaysShowColliders: 0 48 | m_ShowColliderSleep: 1 49 | m_ShowColliderContacts: 0 50 | m_ShowColliderAABB: 0 51 | m_ContactArrowScale: 0.2 52 | m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} 53 | m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} 54 | m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} 55 | m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} 56 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 57 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1386491679 &1 4 | PresetManager: 5 | m_ObjectHideFlags: 0 6 | m_DefaultList: [] 7 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2018.4.11f1 2 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!47 &1 4 | QualitySettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 5 7 | m_CurrentQuality: 5 8 | m_QualitySettings: 9 | - serializedVersion: 2 10 | name: Very Low 11 | pixelLightCount: 0 12 | shadows: 0 13 | shadowResolution: 0 14 | shadowProjection: 1 15 | shadowCascades: 1 16 | shadowDistance: 15 17 | shadowNearPlaneOffset: 3 18 | shadowCascade2Split: 0.33333334 19 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 20 | shadowmaskMode: 0 21 | blendWeights: 1 22 | textureQuality: 1 23 | anisotropicTextures: 0 24 | antiAliasing: 0 25 | softParticles: 0 26 | softVegetation: 0 27 | realtimeReflectionProbes: 0 28 | billboardsFaceCameraPosition: 0 29 | vSyncCount: 0 30 | lodBias: 0.3 31 | maximumLODLevel: 0 32 | streamingMipmapsActive: 0 33 | streamingMipmapsAddAllCameras: 1 34 | streamingMipmapsMemoryBudget: 512 35 | streamingMipmapsRenderersPerFrame: 512 36 | streamingMipmapsMaxLevelReduction: 2 37 | streamingMipmapsMaxFileIORequests: 1024 38 | particleRaycastBudget: 4 39 | asyncUploadTimeSlice: 2 40 | asyncUploadBufferSize: 16 41 | asyncUploadPersistentBuffer: 1 42 | resolutionScalingFixedDPIFactor: 1 43 | excludedTargetPlatforms: [] 44 | - serializedVersion: 2 45 | name: Low 46 | pixelLightCount: 0 47 | shadows: 0 48 | shadowResolution: 0 49 | shadowProjection: 1 50 | shadowCascades: 1 51 | shadowDistance: 20 52 | shadowNearPlaneOffset: 3 53 | shadowCascade2Split: 0.33333334 54 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 55 | shadowmaskMode: 0 56 | blendWeights: 2 57 | textureQuality: 0 58 | anisotropicTextures: 0 59 | antiAliasing: 0 60 | softParticles: 0 61 | softVegetation: 0 62 | realtimeReflectionProbes: 0 63 | billboardsFaceCameraPosition: 0 64 | vSyncCount: 0 65 | lodBias: 0.4 66 | maximumLODLevel: 0 67 | streamingMipmapsActive: 0 68 | streamingMipmapsAddAllCameras: 1 69 | streamingMipmapsMemoryBudget: 512 70 | streamingMipmapsRenderersPerFrame: 512 71 | streamingMipmapsMaxLevelReduction: 2 72 | streamingMipmapsMaxFileIORequests: 1024 73 | particleRaycastBudget: 16 74 | asyncUploadTimeSlice: 2 75 | asyncUploadBufferSize: 16 76 | asyncUploadPersistentBuffer: 1 77 | resolutionScalingFixedDPIFactor: 1 78 | excludedTargetPlatforms: [] 79 | - serializedVersion: 2 80 | name: Medium 81 | pixelLightCount: 1 82 | shadows: 1 83 | shadowResolution: 0 84 | shadowProjection: 1 85 | shadowCascades: 1 86 | shadowDistance: 20 87 | shadowNearPlaneOffset: 3 88 | shadowCascade2Split: 0.33333334 89 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 90 | shadowmaskMode: 0 91 | blendWeights: 2 92 | textureQuality: 0 93 | anisotropicTextures: 1 94 | antiAliasing: 0 95 | softParticles: 0 96 | softVegetation: 0 97 | realtimeReflectionProbes: 0 98 | billboardsFaceCameraPosition: 0 99 | vSyncCount: 1 100 | lodBias: 0.7 101 | maximumLODLevel: 0 102 | streamingMipmapsActive: 0 103 | streamingMipmapsAddAllCameras: 1 104 | streamingMipmapsMemoryBudget: 512 105 | streamingMipmapsRenderersPerFrame: 512 106 | streamingMipmapsMaxLevelReduction: 2 107 | streamingMipmapsMaxFileIORequests: 1024 108 | particleRaycastBudget: 64 109 | asyncUploadTimeSlice: 2 110 | asyncUploadBufferSize: 16 111 | asyncUploadPersistentBuffer: 1 112 | resolutionScalingFixedDPIFactor: 1 113 | excludedTargetPlatforms: [] 114 | - serializedVersion: 2 115 | name: High 116 | pixelLightCount: 2 117 | shadows: 2 118 | shadowResolution: 1 119 | shadowProjection: 1 120 | shadowCascades: 2 121 | shadowDistance: 40 122 | shadowNearPlaneOffset: 3 123 | shadowCascade2Split: 0.33333334 124 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 125 | shadowmaskMode: 1 126 | blendWeights: 2 127 | textureQuality: 0 128 | anisotropicTextures: 1 129 | antiAliasing: 0 130 | softParticles: 0 131 | softVegetation: 1 132 | realtimeReflectionProbes: 1 133 | billboardsFaceCameraPosition: 1 134 | vSyncCount: 1 135 | lodBias: 1 136 | maximumLODLevel: 0 137 | streamingMipmapsActive: 0 138 | streamingMipmapsAddAllCameras: 1 139 | streamingMipmapsMemoryBudget: 512 140 | streamingMipmapsRenderersPerFrame: 512 141 | streamingMipmapsMaxLevelReduction: 2 142 | streamingMipmapsMaxFileIORequests: 1024 143 | particleRaycastBudget: 256 144 | asyncUploadTimeSlice: 2 145 | asyncUploadBufferSize: 16 146 | asyncUploadPersistentBuffer: 1 147 | resolutionScalingFixedDPIFactor: 1 148 | excludedTargetPlatforms: [] 149 | - serializedVersion: 2 150 | name: Very High 151 | pixelLightCount: 3 152 | shadows: 2 153 | shadowResolution: 2 154 | shadowProjection: 1 155 | shadowCascades: 2 156 | shadowDistance: 70 157 | shadowNearPlaneOffset: 3 158 | shadowCascade2Split: 0.33333334 159 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 160 | shadowmaskMode: 1 161 | blendWeights: 4 162 | textureQuality: 0 163 | anisotropicTextures: 2 164 | antiAliasing: 2 165 | softParticles: 1 166 | softVegetation: 1 167 | realtimeReflectionProbes: 1 168 | billboardsFaceCameraPosition: 1 169 | vSyncCount: 1 170 | lodBias: 1.5 171 | maximumLODLevel: 0 172 | streamingMipmapsActive: 0 173 | streamingMipmapsAddAllCameras: 1 174 | streamingMipmapsMemoryBudget: 512 175 | streamingMipmapsRenderersPerFrame: 512 176 | streamingMipmapsMaxLevelReduction: 2 177 | streamingMipmapsMaxFileIORequests: 1024 178 | particleRaycastBudget: 1024 179 | asyncUploadTimeSlice: 2 180 | asyncUploadBufferSize: 16 181 | asyncUploadPersistentBuffer: 1 182 | resolutionScalingFixedDPIFactor: 1 183 | excludedTargetPlatforms: [] 184 | - serializedVersion: 2 185 | name: Ultra 186 | pixelLightCount: 4 187 | shadows: 2 188 | shadowResolution: 2 189 | shadowProjection: 1 190 | shadowCascades: 4 191 | shadowDistance: 150 192 | shadowNearPlaneOffset: 3 193 | shadowCascade2Split: 0.33333334 194 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 195 | shadowmaskMode: 1 196 | blendWeights: 4 197 | textureQuality: 0 198 | anisotropicTextures: 2 199 | antiAliasing: 2 200 | softParticles: 1 201 | softVegetation: 1 202 | realtimeReflectionProbes: 1 203 | billboardsFaceCameraPosition: 1 204 | vSyncCount: 1 205 | lodBias: 2 206 | maximumLODLevel: 0 207 | streamingMipmapsActive: 0 208 | streamingMipmapsAddAllCameras: 1 209 | streamingMipmapsMemoryBudget: 512 210 | streamingMipmapsRenderersPerFrame: 512 211 | streamingMipmapsMaxLevelReduction: 2 212 | streamingMipmapsMaxFileIORequests: 1024 213 | particleRaycastBudget: 4096 214 | asyncUploadTimeSlice: 2 215 | asyncUploadBufferSize: 16 216 | asyncUploadPersistentBuffer: 1 217 | resolutionScalingFixedDPIFactor: 1 218 | excludedTargetPlatforms: [] 219 | m_PerPlatformDefaultQuality: 220 | Android: 2 221 | Lumin: 5 222 | Nintendo 3DS: 5 223 | Nintendo Switch: 5 224 | PS4: 5 225 | PSP2: 2 226 | Standalone: 5 227 | WebGL: 3 228 | Windows Store Apps: 5 229 | XboxOne: 5 230 | iPhone: 2 231 | tvOS: 2 232 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!78 &1 4 | TagManager: 5 | serializedVersion: 2 6 | tags: [] 7 | layers: 8 | - Default 9 | - TransparentFX 10 | - Ignore Raycast 11 | - 12 | - Water 13 | - UI 14 | - 15 | - 16 | - 17 | - 18 | - 19 | - 20 | - 21 | - 22 | - 23 | - 24 | - 25 | - 26 | - 27 | - 28 | - 29 | - 30 | - 31 | - 32 | - 33 | - 34 | - 35 | - 36 | - 37 | - 38 | - 39 | - 40 | m_SortingLayers: 41 | - name: Default 42 | uniqueID: 0 43 | locked: 0 44 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!5 &1 4 | TimeManager: 5 | m_ObjectHideFlags: 0 6 | Fixed Timestep: 0.02 7 | Maximum Allowed Timestep: 0.33333334 8 | m_TimeScale: 1 9 | Maximum Particle Timestep: 0.03 10 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!310 &1 4 | UnityConnectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 1 7 | m_Enabled: 0 8 | m_TestMode: 0 9 | m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events 10 | m_EventUrl: https://cdp.cloud.unity3d.com/v1/events 11 | m_ConfigUrl: https://config.uca.cloud.unity3d.com 12 | m_TestInitMode: 0 13 | CrashReportingSettings: 14 | m_EventUrl: https://perf-events.cloud.unity3d.com 15 | m_Enabled: 0 16 | m_LogBufferSize: 10 17 | m_CaptureEditorExceptions: 1 18 | UnityPurchasingSettings: 19 | m_Enabled: 0 20 | m_TestMode: 0 21 | UnityAnalyticsSettings: 22 | m_Enabled: 0 23 | m_TestMode: 0 24 | m_InitializeOnStartup: 1 25 | UnityAdsSettings: 26 | m_Enabled: 0 27 | m_InitializeOnStartup: 1 28 | m_TestMode: 0 29 | m_IosGameId: 30 | m_AndroidGameId: 31 | m_GameIds: {} 32 | m_GameId: 33 | PerformanceReportingSettings: 34 | m_Enabled: 0 35 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/VFXManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!937362698 &1 4 | VFXManager: 5 | m_ObjectHideFlags: 0 6 | m_IndirectShader: {fileID: 0} 7 | m_CopyBufferShader: {fileID: 0} 8 | m_SortShader: {fileID: 0} 9 | m_RenderPipeSettingsPath: 10 | m_FixedTimeStep: 0.016666668 11 | m_MaxDeltaTime: 0.05 12 | -------------------------------------------------------------------------------- /Decoupling/ProjectSettings/XRSettings.asset: -------------------------------------------------------------------------------- 1 | { 2 | "m_SettingKeys": [ 3 | "VR Device Disabled", 4 | "VR Device User Alert" 5 | ], 6 | "m_SettingValues": [ 7 | "False", 8 | "False" 9 | ] 10 | } -------------------------------------------------------------------------------- /Decoupling/README-zh-TW.md: -------------------------------------------------------------------------------- 1 | # 為何拆解耦合(decoupling) 2 | 3 | 需求會改變、專案會成長、軟體會演化。 4 | 5 | # 問題 6 | 7 | 在系統的各模組間有高度耦合的狀況代表: 8 | - 難以把每個組件的內容隔離開來描述 9 | - 組件難以單獨進行測試 10 | - 難以重複利用組件 11 | 12 | 以上種種都會讓我們難以看懂、維護並發展每個組件,以及整體的系統。 13 | 14 | ![打結的毛線球](https://cdn.pixabay.com/photo/2016/06/28/10/49/thread-1484387_640.jpg "能解得開算你厲害。") 15 | 16 | # 解方 17 | 18 | 我們必須讓組件盡量 [模組化](https://en.wikipedia.org/wiki/Modular_programming) 才能利於分別描述、測試與重複利用。模組化的組件能夠更容易的被修改或替換,讓我們的系統更具彈性,得以適應各種不同的需求修改。我們可以透過拆解耦合的相依組件,並定義最少介面的手段來達到模組化的目的。 19 | 20 | ![樂高磚塊](https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Lego_bricks.jpg/640px-Lego_bricks.jpg "以樂高為榜樣吧,朋友。") 21 | 22 | # 範例 23 | 24 | 舉例來說,假設我們要開發一款類似 [《暗黑破壞神》](https://en.wikipedia.org/wiki/Diablo_(video_game)) 的遊戲,在其中我們有一個玩家角色,還有一個顯示玩家現有血量跟法力的介面。我們要怎麼把血條顯示跟玩家的血量值做聯結?這兩個組件分別附著在完全不同的遊戲物件上! 25 | 26 | ## Singleton 模式 27 | 28 | 我們很快發現到,整個遊戲裡只有一個玩家角色,也只有一個介面。那不就正好能用上 [Singleton 模式](https://en.wikipedia.org/wiki/Singleton_pattern) 來存取其他組件了嗎?[太巧妙了!](https://youtu.be/wv4eTE0aUiQ) 29 | 30 | ```csharp 31 | public sealed class Player : MonoBehaviour 32 | { 33 | public static Player main { get; private set; } 34 | 35 | public float maxHealth; 36 | public float health; 37 | 38 | void Start() 39 | { 40 | main = this; 41 | } 42 | 43 | // ... 44 | } 45 | ``` 46 | 47 | ```csharp 48 | public sealed class HealthBar : MonoBehaviour 49 | { 50 | [SerializeField] private Slider slider; 51 | 52 | void Update() 53 | { 54 | var player = Player.main; 55 | slider.maxValue = player.maxHealth; 56 | slider.value = player.health; 57 | } 58 | } 59 | ``` 60 | 61 | ![血條](./Documentation/HealthBar.png "聰明!") 62 | 63 | 搞定。太輕鬆了。開始著手 [下個任務](https://www.reddit.com/r/restofthefuckingowl/) 吧。沒過多久,我們就寫好了物品欄、裝備、道具、寶石、鑲嵌凹槽、技能樹、角色數值、法術書、Buff,還有敵人系統到遊戲中。這些組件都需要互相溝通。那就加入更多 singleton 吧,易如反掌! 64 | 65 | ![組件相依圖](./Documentation/Dependencies.png "可以上架了!") 66 | 67 | 好等等,看起來沒那麼簡單了。就在這時,我們的遊戲企劃突然想到,如果能有個新法術,可以讓身上所有裝備的屬性加值效果加倍就太好了。哦,還有,順便增加個玩家必須每 20 秒殺死敵人才能維持的狂暴 buff。每當新增一個 singleton,程式設計師的心就酸了一下。如果再收到一個新的錯誤回報,說「為什麼在雙重 buff 時限結束後角色數值沒有正確重置啊!」的話,大家可能就會覺得乾脆辭職去賣雞排還比較快活吧。 68 | 69 | 還是別這樣做吧。如果我告訴你有個方法,讓每個組件都可以被隔離、模組化,而且還能和其他組件共享資訊呢?這就必須討論 [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) 了! 70 | 71 | ## Dependency injection(常譯「相依性注入」) 72 | 73 | 讓我們來仔細看看 [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection)、[separation of concerns (SoC,常譯「關注點分離」)](https://en.wikipedia.org/wiki/Separation_of_concerns),與 [inversion of control (IoC,常譯「控制反轉」)](https://en.wikipedia.org/wiki/Inversion_of_control) ,這些看起來都好像很高深、很複雜,然後可能還要下載額外的框架才能使用,還是得轉換到一個全新語言去比較快...(是嗎?) 74 | 75 | 此處關鍵是意識到,其實 [Unity 裡的 Inspector](https://docs.unity3d.com/Manual/UsingTheInspector.html) *就是* 一個 dependency injector(注入器)。事實上,我們已經使用了這個功能來注入相依組件。在 [前面範例](#singleton-模式) 的 `HealthBar` 當中對介面組件 `Slider` 的參考*就是*我們透過 Unity Inspector 來*注入*的相依性了。就跟指定一個欄位一樣簡單。很誇張嗎?但其實這差不多就是關於 dependency injection 你需要知道的全部了。 76 | 77 | ```csharp 78 | public sealed class Player : MonoBehaviour 79 | { 80 | public float maxHealth; 81 | public float health; 82 | 83 | // 娘子快來看,沒有 singleton! 84 | // ... 85 | } 86 | ``` 87 | 88 | ```csharp 89 | public sealed class HealthBar : MonoBehaviour 90 | { 91 | [SerializeField] private Slider slider; 92 | [SerializeField] private Player player; 93 | 94 | void Update() 95 | { 96 | slider.maxValue = player.maxHealth; 97 | slider.value = player.health; 98 | } 99 | } 100 | ``` 101 | 102 | ![血條第二版](./Documentation/HealthBar2.png "但我們真的有得到什麼好處嗎?") 103 | 104 | 好,所以我們把 singleton 模式換掉,改成使用注入的玩家組件參考,歡呼吧!不過...,我們到底從中獲得什麼好處了嗎?重新再來看一下我們原本遭遇的問題吧: 105 | 106 | - [x] 把每個組件的內容隔離開來描述 107 | - [ ] 組件可以單獨進行測試 108 | - [ ] 重複利用組件 109 | 110 | 其實真的還沒有多大改變。我們大概能在不去看 `Player` 定義的情況下,來猜測 `HealthBar` 到底在做什麼,但是我們還是無法開一個空場景,在不把 `Player` 也拉進來場景中的情況下去單獨測試它。我們也無法重複利用 `HealthBar` 的程式碼在 `ManaBar` 或其他類似的顯示上。我們得複製貼上一份相同的程式碼,然後把每個 `health` 字眼都替換成 `mana`。真令人失望。 111 | 112 | 之後,我們可能也會想進一步清理遊戲架構,把介面組件移到屬於它自己的場景裡並壓在遊戲場景上。但這樣我們的參考就爛了。這下我們得怎麼讓 `HealthBar` 得知 `Player` 呢?它們根本在不同場景中啊!於是乎,看起來 dependency injection 也不怎麼管用嘛,跟那些只有在理論上聽起來有用的想法沒兩樣。 113 | 114 | 修蛋幾勒! 115 | 116 | ## ScriptableObjects 117 | 118 | 今天要講的第二個關鍵是,我們可以不單純只注入指向其他組件的參考,也可以注入對 `ScriptableObject` 的參考。這是拿來 [存放資料,*且獨立於類別實體*的容器](https://docs.unity3d.com/Manual/class-ScriptableObject.html) 。難道說我們可以用它來存放玩家的血量值,而不用放在 `Player` 組件裡面嗎?答對了!現在馬上就來試試吧。 119 | 120 | ```csharp 121 | [CreateAssetMenu] 122 | public sealed class BoundedFloat : ScriptableObject 123 | { 124 | public float maxValue; 125 | public float value; 126 | } 127 | ``` 128 | 129 | ```csharp 130 | public sealed class StatusBar : MonoBehaviour 131 | { 132 | [SerializeField] private Slider slider; 133 | [SerializeField] private BoundedFloat data; 134 | 135 | void Update() 136 | { 137 | slider.maxValue = data.maxValue; 138 | slider.value = data.value; 139 | } 140 | } 141 | ``` 142 | 143 | ```csharp 144 | public sealed class Player : MonoBehaviour 145 | { 146 | [Header("Stats")] 147 | [SerializeField] private BoundedFloat health; 148 | [SerializeField] private BoundedFloat mana; 149 | 150 | // ... 151 | } 152 | ``` 153 | 154 | ![ScriptableObjects](./Documentation/ScriptableObjects.png "你可以同時開啟多個 Inspector 視窗。") 155 | 156 | 我們把所有 *資料* 都移到 *資料容器* 裡面,並在組件裡面去參考它們。`ScriptableObject` 存在於 Unity 的資源(asset)清單中,而且可以從任何 prefab、物件及場景中參考它們。我們也同時可以整合針對血條與法力條的程式碼,因為這兩個東西都同樣只關心一個最大值跟一個現在值。在我們的所有介面組件中,再也沒有指向 `Player` 的參考了,反之亦同。介面與玩家組件都只參考了 *資料*。它們甚至不需要去管是不是同一份資料。 157 | 158 | ![拆解耦合](./Documentation/Decoupling.png) 159 | 160 | 這樣終於完成了嗎?讓我們再回顧一次問題點: 161 | 162 | - [x] 把每個組件的內容隔離開來描述 163 | - [x] 組件可以單獨進行測試 164 | - [x] 重複利用組件 165 | 166 | 閱讀程式碼時,我們可以理解 `Player` 的行為,但不需要去管介面的 `StatusBar`,反之亦同。兩者之間不再存有相依性。我們可以把 `Player` 丟進一個空場景中,隨便指定一些 [測試用的血量與法力值](https://en.wikipedia.org/wiki/Mock_object) 就能執行場景,毫無問題。基礎型別資料(Plain Old Data, POD)非常容易複製。 167 | 168 | 我們也可以把介面丟到一個空場景中,同樣任意指定一些測試數值,就執行場景。我們甚至可以利用變動測試數值,看看血條跟法力條實際動起來的感覺。這下介面設計師可以瘋狂美化他們想要做到的視覺效果,而且根本不用執行實際完整的遊戲了。介面的設計迭代從來沒這麼方便過! 169 | 170 | 我們還沒開始實作任何其他的遊戲組件,就已經開始在重複利用程式碼了。針對兩個數值條,我們只須使用一份程式,而且這個 `BoundedFloat` 大概之後在很多類似數值需求的場合有機會派上用場。 171 | 172 | # 重構(Refactoring) 173 | 174 | Dependency injection 175 | 最美好的一個地方是,我們也可以把它用於既有專案上,並不需要從頭開始重寫。我們可以亦步亦趨把每個組件慢慢切出來,並將相依資料移到 176 | `ScriptableObject` 裡面,讓組件之間的直接聯結變成對資料的參考。一旦把一個組件完全解除耦合之後,我們就能輕易地單獨針對它做測試跟重構。 177 | 178 | # 延伸閱讀 179 | 180 | - [Unite Austin 2017 - Game Architecture with Scriptable Objects](https://youtu.be/raQ3iHhE_Kk) by [@roboryantron](https://github.com/roboryantron) 181 | - [Unite 2016 - Overthrowing the MonoBehaviour Tyranny in a Glorious Scriptable Object Revolution](https://youtu.be/6vmRwLYWNRo) by [@richard-fine](https://github.com/richard-fine) 182 | 183 | # 翻譯 184 | 如果你覺得這個工作坊有其價值,並通曉另一個語言,我們非常歡迎任何幫助工作坊內容進行翻譯的協助。把本儲存庫內容 clone 下來後,增加一份特定語言在地化的 README.md,例如 README-pt-BR.md,並送 PR 給我們。 185 | -------------------------------------------------------------------------------- /GameMaker/README-zh-TW.md: -------------------------------------------------------------------------------- 1 | # 遊戲編輯器實作挑戰 2 | 製作一個讓使用者可以開發並遊玩自己的遊戲的 Unity 原型。 3 | 4 | 在 _創作_ 模式下,使用者可以建立 prefab,並把他們實體化出來放置在場景內。在 _遊玩_ 模式下,使用者可以在建立好的場景中遊玩,並跟擺放好的物件互動。 5 | 6 | 這個問題的答案是開放式的,沒有絕對正確或錯誤的做法。技術或想法的運用請依照自己的認知來自由發揮。 7 | 8 | ## 創作模式 9 | 使用者可以選擇兩種不同的物件 10 | - 方塊 11 | - 球體 12 | 13 | 使用者可以增加行為到物件上。請提供這兩種行為: 14 | - 爆炸 15 | - 讓玩家加分 16 | 17 | 使用者可以將物件儲存為 prefab,並允許依此創建多個不同的 prefab。使用者能從特定 prefab 實體化出一個或多個物件到場景上。可將場景視為一個簡單的關卡。 18 | 19 | ## 遊玩模式 20 | 在 _遊玩_ 模式中,使用者可以透過點擊場景上的任何物件,來觸發物件所擁有的行為。 21 | 22 | # 挑戰提交方式 23 | 若想要參與本挑戰,請提供我你所實作的原型版本的 GitHub 連結。參與挑戰即代表同意讓程式碼在公開的場合被拿出來討論,本挑戰目的是希望所有人在過程中都能學到一些東西。 24 | 25 | # 解法程式討論 26 | 討論過程會在 [IGDShare Discord](https://discord.gg/FNkHgBN) 上直播進行,並預定錄影下來供未來參考。 27 | 28 | # 翻譯 29 | 如果你覺得這個工作坊有其價值,並通曉另一個語言,我們非常歡迎任何幫助工作坊內容進行翻譯的協助。把本儲存庫內容 clone 下來後,增加一份特定語言在地化的 README.md,例如 README-pt-BR.md,並送 PR 給我們。 -------------------------------------------------------------------------------- /GameMaker/README.md: -------------------------------------------------------------------------------- 1 | # Game maker challenge 2 | Create a Unity prototype where users can create and play their own games. 3 | 4 | In _Create_ mode users can create entity prefabs and place instances of them in the scene. In _Play_ mode users can play the created scene and interact with the placed entities. 5 | 6 | The problem is open-ended by design. There is no right or wrong approach. Feel free to make technical and creative decisions that suit you. 7 | 8 | ## Create mode 9 | The user can choose between two types of entities. 10 | - Cube 11 | - Sphere 12 | 13 | The user can add behaviors to the entity. Provide these two behaviors: 14 | - Explode 15 | - Give points 16 | 17 | The user can save an entity as a prefab. Multiple prefabs can be created this way. The user can place one or more instances of an entity prefab in the scene. Think of the scene as a simple level. 18 | 19 | ## Play mode 20 | In _Play_ mode, the user can click or tap on any instance of the entity in the scene to trigger the attached behaviors. 21 | 22 | # Submissions 23 | In order to participate, send me the link to the GitHub repository of your solution. By participating you agree to have your code reviewed by me in front of an audience. The goal is for all of us to learn something in the process. 24 | 25 | # Code reviews 26 | Code review sessions will be broadcast live in the [IGDShare Discord](https://discord.gg/FNkHgBN) and hopefully recorded on YouTube for future reference. 27 | 28 | # Translations 29 | - [台灣繁體中文 (zh-TW)](README-zh-TW.md) 30 | 31 | If you find this workshop useful and speak another language, I'd very much appreciate any help translating the chapters. Clone the repository, add a localized copy of the `README.md`, for example `README-pt-BR.md`, and send me a pull request. 32 | -------------------------------------------------------------------------------- /GitHubEnterprise/README.md: -------------------------------------------------------------------------------- 1 | # GitHub Enterprise 2 | 3 | GitHub Desktop 4 | Add AppData\Local\GitHubDesktop\app-2.2.4\resources\app\cmd to path 5 | Jenkins needs git.exe in non-user space 6 | https://stackoverflow.com/questions/37155321/error-error-cloning-remote-repo-origin/37541276 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jonas Boetel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-zh-TW.md: -------------------------------------------------------------------------------- 1 | # 健全碼農工作坊 2 | 3 | ## 簡介 4 | 5 | 這個遊戲開發技術工作坊的主要目的是希望幫助程式設計師自我學習、成長,並及早培養好習慣。這份問卷的目的,是為了想辦法走出每天問「為什麼這爛掉了!?」的日常,並透過較長期的培養與學習,致力於降低這種鳥事發生的*頻率*。 6 | 7 | 本工作坊由 [Jonas Bötel](https://twitter.com/codelumpn) 發起,並由 [IGDShare 的 Johnson Lin](https://twitter.com/igdshare) 協助中文翻譯。 8 | 9 | ## 章節 10 | 1. [拆解耦合](./Decoupling/README-zh-TW.md) 11 | 2. [持續整合](./ContinuousIntegration/README-zh-TW.md) 12 | 3. [狀態機](./StateMachines/README-zh-TW.md) 13 | 14 | ## 挑戰題目 15 | 1. [遊戲編輯器實作](./GameMaker/README-zh-TW.md) 16 | 2. [基地建造系統實作](./BaseBuilding/README-zh-TW.md) 17 | 18 | 如果你想到有趣的程式挑戰,而且難度與大小適中(大到可以凸顯出有趣的工程決策,但可以在大約一小時內看完程式碼),歡迎把你的想法告訴我,我會再加到列表上。 19 | 20 | ## Feedback form 21 | [Feedback form](https://docs.google.com/forms/d/e/1FAIpQLSd3JzkFGXOSWr9CyA7mSWUG8hzvDvHoThYw_CLs8pvduKAcNw/viewform) 22 | 23 | ## 翻譯 24 | 25 | 如果你覺得這個工作坊有其價值,並通曉另一個語言,我們非常歡迎任何幫助工作坊內容進行翻譯的協助。把 repository clone 下來後,增加一份特定語言在地化的 README.md,例如 README-pt-BR.md,並送 PR 給我們。 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Healthy Code Workshop 2 | 3 | ## Introduction 4 | The goal of this game development tech workshop is to help programmers learn, grow, and form good habits. We're here to look beyond the daily issues of "Why is this not working?!" and instead focus on reducing the *frequency* of those "Why?!" moments through education and long term growth. 5 | 6 | The organizers of the workshop are [Jonas Bötel](https://twitter.com/codelumpn) and [Johnson Lin](https://twitter.com/igdshare). The workshop will be held in both English and Traditional Chinese simultaneously. 7 | 8 | ## Chapters 9 | 1. [Decoupling](./Decoupling/) 10 | 2. [Continuous Integration](./ContinuousIntegration/) 11 | 3. [State Machines](./StateMachines/) 12 | 13 | If there is a particular topic that you would like me to address, please use the [feedback form](https://docs.google.com/forms/d/e/1FAIpQLSf6zky6QtlwzFEXGlWpW-WGGYXcCrcWLTk7e5Q2eeODSU_JiQ/viewform) to let me know. 14 | 15 | ## Challenges 16 | 1. [Game Maker](./GameMaker/) 17 | 2. [Base Building](./BaseBuilding/) 18 | 19 | If you have ideas for interesting coding challenges that are big enough to reveal engineering decisions and small enough to be reviewed in about one hour, please send them to me and I will add them to the list. 20 | 21 | ## Feedback form 22 | [Feedback form](https://docs.google.com/forms/d/e/1FAIpQLSf6zky6QtlwzFEXGlWpW-WGGYXcCrcWLTk7e5Q2eeODSU_JiQ/viewform) 23 | 24 | ## Translations 25 | - [台灣繁體中文 (zh-TW)](README-zh-TW.md) 26 | 27 | If you find this workshop useful and speak another language, I'd very much appreciate any help translating the chapters. Clone the repository, add a localized copy of the `README.md`, for example `README-pt-BR.md`, and send me a pull request. 28 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 05d9101729ec9fb42a726fe5bd0ce353 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/JumpController1.controller: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1101 &-7167521658628942765 4 | AnimatorStateTransition: 5 | m_ObjectHideFlags: 1 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_Name: 10 | m_Conditions: 11 | - m_ConditionMode: 1 12 | m_ConditionEvent: JumpButton 13 | m_EventTreshold: 0 14 | m_DstStateMachine: {fileID: 0} 15 | m_DstState: {fileID: -3405913857897670623} 16 | m_Solo: 0 17 | m_Mute: 0 18 | m_IsExit: 0 19 | serializedVersion: 3 20 | m_TransitionDuration: 0 21 | m_TransitionOffset: 0 22 | m_ExitTime: 0 23 | m_HasExitTime: 0 24 | m_HasFixedDuration: 1 25 | m_InterruptionSource: 0 26 | m_OrderedInterruption: 1 27 | m_CanTransitionToSelf: 1 28 | --- !u!1102 &-3405913857897670623 29 | AnimatorState: 30 | serializedVersion: 5 31 | m_ObjectHideFlags: 1 32 | m_CorrespondingSourceObject: {fileID: 0} 33 | m_PrefabInstance: {fileID: 0} 34 | m_PrefabAsset: {fileID: 0} 35 | m_Name: Start Jump 36 | m_Speed: 1 37 | m_CycleOffset: 0 38 | m_Transitions: 39 | - {fileID: 7373875079345159137} 40 | m_StateMachineBehaviours: 41 | - {fileID: 7990400404345941772} 42 | m_Position: {x: 50, y: 50, z: 0} 43 | m_IKOnFeet: 0 44 | m_WriteDefaultValues: 1 45 | m_Mirror: 0 46 | m_SpeedParameterActive: 0 47 | m_MirrorParameterActive: 0 48 | m_CycleOffsetParameterActive: 0 49 | m_TimeParameterActive: 0 50 | m_Motion: {fileID: 0} 51 | m_Tag: 52 | m_SpeedParameter: 53 | m_MirrorParameter: 54 | m_CycleOffsetParameter: 55 | m_TimeParameter: 56 | --- !u!1102 &-2522734117938477605 57 | AnimatorState: 58 | serializedVersion: 5 59 | m_ObjectHideFlags: 1 60 | m_CorrespondingSourceObject: {fileID: 0} 61 | m_PrefabInstance: {fileID: 0} 62 | m_PrefabAsset: {fileID: 0} 63 | m_Name: Falling 64 | m_Speed: 1 65 | m_CycleOffset: 0 66 | m_Transitions: 67 | - {fileID: -2299348655700377361} 68 | m_StateMachineBehaviours: [] 69 | m_Position: {x: 50, y: 50, z: 0} 70 | m_IKOnFeet: 0 71 | m_WriteDefaultValues: 1 72 | m_Mirror: 0 73 | m_SpeedParameterActive: 0 74 | m_MirrorParameterActive: 0 75 | m_CycleOffsetParameterActive: 0 76 | m_TimeParameterActive: 0 77 | m_Motion: {fileID: 0} 78 | m_Tag: 79 | m_SpeedParameter: 80 | m_MirrorParameter: 81 | m_CycleOffsetParameter: 82 | m_TimeParameter: 83 | --- !u!1101 &-2299348655700377361 84 | AnimatorStateTransition: 85 | m_ObjectHideFlags: 1 86 | m_CorrespondingSourceObject: {fileID: 0} 87 | m_PrefabInstance: {fileID: 0} 88 | m_PrefabAsset: {fileID: 0} 89 | m_Name: 90 | m_Conditions: 91 | - m_ConditionMode: 1 92 | m_ConditionEvent: OnGround 93 | m_EventTreshold: 0 94 | m_DstStateMachine: {fileID: 0} 95 | m_DstState: {fileID: 4455375708473501155} 96 | m_Solo: 0 97 | m_Mute: 0 98 | m_IsExit: 0 99 | serializedVersion: 3 100 | m_TransitionDuration: 0 101 | m_TransitionOffset: 0 102 | m_ExitTime: 0 103 | m_HasExitTime: 0 104 | m_HasFixedDuration: 1 105 | m_InterruptionSource: 0 106 | m_OrderedInterruption: 1 107 | m_CanTransitionToSelf: 1 108 | --- !u!1101 &-1600652835983460018 109 | AnimatorStateTransition: 110 | m_ObjectHideFlags: 1 111 | m_CorrespondingSourceObject: {fileID: 0} 112 | m_PrefabInstance: {fileID: 0} 113 | m_PrefabAsset: {fileID: 0} 114 | m_Name: 115 | m_Conditions: 116 | - m_ConditionMode: 2 117 | m_ConditionEvent: OnGround 118 | m_EventTreshold: 0 119 | m_DstStateMachine: {fileID: 0} 120 | m_DstState: {fileID: -2522734117938477605} 121 | m_Solo: 0 122 | m_Mute: 0 123 | m_IsExit: 0 124 | serializedVersion: 3 125 | m_TransitionDuration: 0 126 | m_TransitionOffset: 0 127 | m_ExitTime: 0 128 | m_HasExitTime: 0 129 | m_HasFixedDuration: 1 130 | m_InterruptionSource: 0 131 | m_OrderedInterruption: 1 132 | m_CanTransitionToSelf: 1 133 | --- !u!91 &9100000 134 | AnimatorController: 135 | m_ObjectHideFlags: 0 136 | m_CorrespondingSourceObject: {fileID: 0} 137 | m_PrefabInstance: {fileID: 0} 138 | m_PrefabAsset: {fileID: 0} 139 | m_Name: JumpController1 140 | serializedVersion: 5 141 | m_AnimatorParameters: 142 | - m_Name: OnGround 143 | m_Type: 4 144 | m_DefaultFloat: 0 145 | m_DefaultInt: 0 146 | m_DefaultBool: 0 147 | m_Controller: {fileID: 9100000} 148 | - m_Name: JumpButton 149 | m_Type: 4 150 | m_DefaultFloat: 0 151 | m_DefaultInt: 0 152 | m_DefaultBool: 0 153 | m_Controller: {fileID: 9100000} 154 | m_AnimatorLayers: 155 | - serializedVersion: 5 156 | m_Name: Base Layer 157 | m_StateMachine: {fileID: 1409161539223811584} 158 | m_Mask: {fileID: 0} 159 | m_Motions: [] 160 | m_Behaviours: [] 161 | m_BlendingMode: 0 162 | m_SyncedLayerIndex: -1 163 | m_DefaultWeight: 0 164 | m_IKPass: 0 165 | m_SyncedLayerAffectsTiming: 0 166 | m_Controller: {fileID: 9100000} 167 | --- !u!1107 &1409161539223811584 168 | AnimatorStateMachine: 169 | serializedVersion: 5 170 | m_ObjectHideFlags: 1 171 | m_CorrespondingSourceObject: {fileID: 0} 172 | m_PrefabInstance: {fileID: 0} 173 | m_PrefabAsset: {fileID: 0} 174 | m_Name: Base Layer 175 | m_ChildStates: 176 | - serializedVersion: 1 177 | m_State: {fileID: 4455375708473501155} 178 | m_Position: {x: 0, y: 200, z: 0} 179 | - serializedVersion: 1 180 | m_State: {fileID: -2522734117938477605} 181 | m_Position: {x: 300, y: 400, z: 0} 182 | - serializedVersion: 1 183 | m_State: {fileID: -3405913857897670623} 184 | m_Position: {x: 0, y: 400, z: 0} 185 | m_ChildStateMachines: [] 186 | m_AnyStateTransitions: [] 187 | m_EntryTransitions: [] 188 | m_StateMachineTransitions: {} 189 | m_StateMachineBehaviours: [] 190 | m_AnyStatePosition: {x: 20, y: 70, z: 0} 191 | m_EntryPosition: {x: 20, y: 120, z: 0} 192 | m_ExitPosition: {x: 800, y: 120, z: 0} 193 | m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} 194 | m_DefaultState: {fileID: 4455375708473501155} 195 | --- !u!1102 &4455375708473501155 196 | AnimatorState: 197 | serializedVersion: 5 198 | m_ObjectHideFlags: 1 199 | m_CorrespondingSourceObject: {fileID: 0} 200 | m_PrefabInstance: {fileID: 0} 201 | m_PrefabAsset: {fileID: 0} 202 | m_Name: On Ground 203 | m_Speed: 1 204 | m_CycleOffset: 0 205 | m_Transitions: 206 | - {fileID: -7167521658628942765} 207 | - {fileID: -1600652835983460018} 208 | m_StateMachineBehaviours: [] 209 | m_Position: {x: 50, y: 50, z: 0} 210 | m_IKOnFeet: 0 211 | m_WriteDefaultValues: 1 212 | m_Mirror: 0 213 | m_SpeedParameterActive: 0 214 | m_MirrorParameterActive: 0 215 | m_CycleOffsetParameterActive: 0 216 | m_TimeParameterActive: 0 217 | m_Motion: {fileID: 0} 218 | m_Tag: 219 | m_SpeedParameter: 220 | m_MirrorParameter: 221 | m_CycleOffsetParameter: 222 | m_TimeParameter: 223 | --- !u!1101 &7373875079345159137 224 | AnimatorStateTransition: 225 | m_ObjectHideFlags: 1 226 | m_CorrespondingSourceObject: {fileID: 0} 227 | m_PrefabInstance: {fileID: 0} 228 | m_PrefabAsset: {fileID: 0} 229 | m_Name: 230 | m_Conditions: [] 231 | m_DstStateMachine: {fileID: 0} 232 | m_DstState: {fileID: -2522734117938477605} 233 | m_Solo: 0 234 | m_Mute: 0 235 | m_IsExit: 0 236 | serializedVersion: 3 237 | m_TransitionDuration: 0 238 | m_TransitionOffset: 0 239 | m_ExitTime: 0 240 | m_HasExitTime: 1 241 | m_HasFixedDuration: 1 242 | m_InterruptionSource: 0 243 | m_OrderedInterruption: 1 244 | m_CanTransitionToSelf: 1 245 | --- !u!114 &7990400404345941772 246 | MonoBehaviour: 247 | m_ObjectHideFlags: 1 248 | m_CorrespondingSourceObject: {fileID: 0} 249 | m_PrefabInstance: {fileID: 0} 250 | m_PrefabAsset: {fileID: 0} 251 | m_GameObject: {fileID: 0} 252 | m_Enabled: 1 253 | m_EditorHideFlags: 0 254 | m_Script: {fileID: 11500000, guid: 616da9ba56f7d3943970abd298fa80ab, type: 3} 255 | m_Name: 256 | m_EditorClassIdentifier: 257 | onEnter: StartJump 258 | onExit: 259 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/JumpController1.controller.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 23c8a7eab10921f488032c4e2731fd42 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/JumpController2.controller.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 767f5cf168c7c4d41829400149e79458 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/JumpController3.controller.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 932636caddeee8c4f9ba9c4b5bd22a9e 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/JumpController4.controller.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dddea7b8ea1e923439a39a550f3c5284 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/JumpController5.controller.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 932636caddeee8c4f9ba9c4b5bd22a9e 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/Platform.anim: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!74 &7400000 4 | AnimationClip: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_Name: Platform 10 | serializedVersion: 6 11 | m_Legacy: 0 12 | m_Compressed: 0 13 | m_UseHighQualityCurve: 1 14 | m_RotationCurves: [] 15 | m_CompressedRotationCurves: [] 16 | m_EulerCurves: [] 17 | m_PositionCurves: 18 | - curve: 19 | serializedVersion: 2 20 | m_Curve: 21 | - serializedVersion: 3 22 | time: 0 23 | value: {x: 9, y: 3, z: 0} 24 | inSlope: {x: 0, y: 0, z: 0} 25 | outSlope: {x: 0, y: 0, z: 0} 26 | tangentMode: 0 27 | weightedMode: 0 28 | inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} 29 | outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} 30 | - serializedVersion: 3 31 | time: 2 32 | value: {x: 9, y: -1, z: 0} 33 | inSlope: {x: 0, y: 0, z: 0} 34 | outSlope: {x: 0, y: 0, z: 0} 35 | tangentMode: 0 36 | weightedMode: 0 37 | inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} 38 | outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} 39 | - serializedVersion: 3 40 | time: 4 41 | value: {x: 9, y: 3, z: 0} 42 | inSlope: {x: 0, y: 0, z: 0} 43 | outSlope: {x: 0, y: 0, z: 0} 44 | tangentMode: 0 45 | weightedMode: 0 46 | inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} 47 | outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} 48 | m_PreInfinity: 2 49 | m_PostInfinity: 2 50 | m_RotationOrder: 4 51 | path: 52 | m_ScaleCurves: [] 53 | m_FloatCurves: [] 54 | m_PPtrCurves: [] 55 | m_SampleRate: 60 56 | m_WrapMode: 0 57 | m_Bounds: 58 | m_Center: {x: 0, y: 0, z: 0} 59 | m_Extent: {x: 0, y: 0, z: 0} 60 | m_ClipBindingConstant: 61 | genericBindings: 62 | - serializedVersion: 2 63 | path: 0 64 | attribute: 1 65 | script: {fileID: 0} 66 | typeID: 4 67 | customType: 0 68 | isPPtrCurve: 0 69 | pptrCurveMapping: [] 70 | m_AnimationClipSettings: 71 | serializedVersion: 2 72 | m_AdditiveReferencePoseClip: {fileID: 0} 73 | m_AdditiveReferencePoseTime: 0 74 | m_StartTime: 0 75 | m_StopTime: 4 76 | m_OrientationOffsetY: 0 77 | m_Level: 0 78 | m_CycleOffset: 0 79 | m_HasAdditiveReferencePose: 0 80 | m_LoopTime: 1 81 | m_LoopBlend: 0 82 | m_LoopBlendOrientation: 0 83 | m_LoopBlendPositionY: 0 84 | m_LoopBlendPositionXZ: 0 85 | m_KeepOriginalOrientation: 0 86 | m_KeepOriginalPositionY: 1 87 | m_KeepOriginalPositionXZ: 0 88 | m_HeightFromFeet: 0 89 | m_Mirror: 0 90 | m_EditorCurves: 91 | - curve: 92 | serializedVersion: 2 93 | m_Curve: 94 | - serializedVersion: 3 95 | time: 0 96 | value: 3 97 | inSlope: 0 98 | outSlope: 0 99 | tangentMode: 136 100 | weightedMode: 0 101 | inWeight: 0.33333334 102 | outWeight: 0.33333334 103 | - serializedVersion: 3 104 | time: 2 105 | value: -1 106 | inSlope: 0 107 | outSlope: 0 108 | tangentMode: 136 109 | weightedMode: 0 110 | inWeight: 0.33333334 111 | outWeight: 0.33333334 112 | - serializedVersion: 3 113 | time: 4 114 | value: 3 115 | inSlope: 0 116 | outSlope: 0 117 | tangentMode: 136 118 | weightedMode: 0 119 | inWeight: 0.33333334 120 | outWeight: 0.33333334 121 | m_PreInfinity: 2 122 | m_PostInfinity: 2 123 | m_RotationOrder: 4 124 | attribute: m_LocalPosition.y 125 | path: 126 | classID: 4 127 | script: {fileID: 0} 128 | - curve: 129 | serializedVersion: 2 130 | m_Curve: 131 | - serializedVersion: 3 132 | time: 0 133 | value: 9 134 | inSlope: 0 135 | outSlope: 0 136 | tangentMode: 136 137 | weightedMode: 0 138 | inWeight: 0.33333334 139 | outWeight: 0.33333334 140 | - serializedVersion: 3 141 | time: 2 142 | value: 9 143 | inSlope: 0 144 | outSlope: 0 145 | tangentMode: 136 146 | weightedMode: 0 147 | inWeight: 0.33333334 148 | outWeight: 0.33333334 149 | - serializedVersion: 3 150 | time: 4 151 | value: 9 152 | inSlope: 0 153 | outSlope: 0 154 | tangentMode: 136 155 | weightedMode: 0 156 | inWeight: 0.33333334 157 | outWeight: 0.33333334 158 | m_PreInfinity: 2 159 | m_PostInfinity: 2 160 | m_RotationOrder: 4 161 | attribute: m_LocalPosition.x 162 | path: 163 | classID: 4 164 | script: {fileID: 0} 165 | - curve: 166 | serializedVersion: 2 167 | m_Curve: 168 | - serializedVersion: 3 169 | time: 0 170 | value: 0 171 | inSlope: 0 172 | outSlope: 0 173 | tangentMode: 136 174 | weightedMode: 0 175 | inWeight: 0.33333334 176 | outWeight: 0.33333334 177 | - serializedVersion: 3 178 | time: 2 179 | value: 0 180 | inSlope: 0 181 | outSlope: 0 182 | tangentMode: 136 183 | weightedMode: 0 184 | inWeight: 0.33333334 185 | outWeight: 0.33333334 186 | - serializedVersion: 3 187 | time: 4 188 | value: 0 189 | inSlope: 0 190 | outSlope: 0 191 | tangentMode: 136 192 | weightedMode: 0 193 | inWeight: 0.33333334 194 | outWeight: 0.33333334 195 | m_PreInfinity: 2 196 | m_PostInfinity: 2 197 | m_RotationOrder: 4 198 | attribute: m_LocalPosition.z 199 | path: 200 | classID: 4 201 | script: {fileID: 0} 202 | m_EulerEditorCurves: [] 203 | m_HasGenericRootTransform: 1 204 | m_HasMotionFloatCurves: 0 205 | m_Events: [] 206 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/Platform.anim.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b1697552a543b241bac176a12420e26 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/Platform.controller: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!91 &9100000 4 | AnimatorController: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_Name: Platform 10 | serializedVersion: 5 11 | m_AnimatorParameters: [] 12 | m_AnimatorLayers: 13 | - serializedVersion: 5 14 | m_Name: Base Layer 15 | m_StateMachine: {fileID: 5507408414913514037} 16 | m_Mask: {fileID: 0} 17 | m_Motions: [] 18 | m_Behaviours: [] 19 | m_BlendingMode: 0 20 | m_SyncedLayerIndex: -1 21 | m_DefaultWeight: 0 22 | m_IKPass: 0 23 | m_SyncedLayerAffectsTiming: 0 24 | m_Controller: {fileID: 9100000} 25 | --- !u!1107 &5507408414913514037 26 | AnimatorStateMachine: 27 | serializedVersion: 5 28 | m_ObjectHideFlags: 1 29 | m_CorrespondingSourceObject: {fileID: 0} 30 | m_PrefabInstance: {fileID: 0} 31 | m_PrefabAsset: {fileID: 0} 32 | m_Name: Base Layer 33 | m_ChildStates: 34 | - serializedVersion: 1 35 | m_State: {fileID: 8707971808241897047} 36 | m_Position: {x: 30, y: 200, z: 0} 37 | m_ChildStateMachines: [] 38 | m_AnyStateTransitions: [] 39 | m_EntryTransitions: [] 40 | m_StateMachineTransitions: {} 41 | m_StateMachineBehaviours: [] 42 | m_AnyStatePosition: {x: 50, y: 20, z: 0} 43 | m_EntryPosition: {x: 50, y: 120, z: 0} 44 | m_ExitPosition: {x: 800, y: 120, z: 0} 45 | m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} 46 | m_DefaultState: {fileID: 8707971808241897047} 47 | --- !u!1102 &8707971808241897047 48 | AnimatorState: 49 | serializedVersion: 5 50 | m_ObjectHideFlags: 1 51 | m_CorrespondingSourceObject: {fileID: 0} 52 | m_PrefabInstance: {fileID: 0} 53 | m_PrefabAsset: {fileID: 0} 54 | m_Name: Platform 55 | m_Speed: 1 56 | m_CycleOffset: 0 57 | m_Transitions: [] 58 | m_StateMachineBehaviours: [] 59 | m_Position: {x: 50, y: 50, z: 0} 60 | m_IKOnFeet: 0 61 | m_WriteDefaultValues: 1 62 | m_Mirror: 0 63 | m_SpeedParameterActive: 0 64 | m_MirrorParameterActive: 0 65 | m_CycleOffsetParameterActive: 0 66 | m_TimeParameterActive: 0 67 | m_Motion: {fileID: 7400000, guid: 5b1697552a543b241bac176a12420e26, type: 2} 68 | m_Tag: 69 | m_SpeedParameter: 70 | m_MirrorParameter: 71 | m_CycleOffsetParameter: 72 | m_TimeParameter: 73 | -------------------------------------------------------------------------------- /StateMachines/Assets/Data/Platform.controller.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1cb17c5dbb77475479b6ca282cddfa2d 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 0 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e412d1addcef53a408db94b1b12e2158 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ba1408b5afae9a744ac2fd238d2e4bb7 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scenes/SampleScene.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f9bfa677416da947a8966b1a50b4ba1 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8284898561414334aafebff741905faf 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scripts/JumpControllerMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public sealed class JumpControllerMessageHandler : MonoBehaviour 4 | { 5 | [SerializeField] private Rigidbody rb; 6 | [SerializeField] private float jumpVelocity; 7 | 8 | private bool startJump, stopJump; 9 | 10 | void StartJump() 11 | { 12 | startJump = true; 13 | } 14 | 15 | void StopJump() 16 | { 17 | stopJump = true; 18 | } 19 | 20 | void FixedUpdate() 21 | { 22 | if (startJump) 23 | { 24 | startJump = false; 25 | var downVelocity = Mathf.Min(rb.velocity.y, 0); 26 | var deltaVelocity = new Vector3(0, jumpVelocity - downVelocity, 0); 27 | rb.AddForce(deltaVelocity, ForceMode.VelocityChange); 28 | } 29 | if (stopJump) 30 | { 31 | stopJump = false; 32 | var upVelocity = Mathf.Max(rb.velocity.y, 0); 33 | var deltaVelocity = new Vector3(0, -upVelocity, 0); 34 | rb.AddForce(deltaVelocity, ForceMode.VelocityChange); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scripts/JumpControllerMessageHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d772aee82ef9d9640b272003e1851df1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scripts/JumpControllerParameterProvider.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public sealed class JumpControllerParameterProvider : MonoBehaviour 4 | { 5 | private static readonly int onGroundId = Animator.StringToHash("OnGround"); 6 | private static readonly int jumpButtonId = Animator.StringToHash("JumpButton"); 7 | 8 | private static readonly string[] groundLayers = { "Terrain" }; 9 | private const QueryTriggerInteraction groundTriggerInteraction = QueryTriggerInteraction.Ignore; 10 | 11 | [SerializeField] private Animator animator; 12 | [SerializeField] private Rigidbody rb; 13 | [SerializeField] private CapsuleCollider capsule; 14 | 15 | private bool onGround; 16 | private int groundLayerMask; 17 | 18 | void Start() 19 | { 20 | groundLayerMask = LayerMask.GetMask(groundLayers); 21 | } 22 | 23 | void Update() 24 | { 25 | animator.SetBool(onGroundId, onGround); 26 | animator.SetBool(jumpButtonId, Input.GetButton("Jump")); 27 | } 28 | 29 | void FixedUpdate() 30 | { 31 | RaycastHit hitInfo; 32 | var center = rb.position + capsule.transform.localPosition; 33 | var radius = capsule.radius; 34 | var direction = Vector3.down; 35 | var distance = capsule.height / 2 + 0.05f; 36 | onGround = Physics.SphereCast(center, radius, direction, out hitInfo, distance, groundLayerMask, groundTriggerInteraction); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scripts/JumpControllerParameterProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a56895805fb25aa4e9012e89c7bc8497 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scripts/Player.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public sealed class Player : MonoBehaviour 4 | { 5 | [SerializeField] private Rigidbody rb; 6 | [SerializeField] private float walkSpeed; 7 | 8 | private Vector3 targetVelocity; 9 | 10 | void Update() 11 | { 12 | float horizontal = Input.GetAxis("Horizontal"); 13 | targetVelocity = new Vector3(horizontal * walkSpeed, 0, 0); 14 | } 15 | 16 | void FixedUpdate() 17 | { 18 | var velocity = rb.velocity; 19 | var deltaVelocity = targetVelocity - velocity; 20 | deltaVelocity.y = 0; 21 | rb.AddForce(deltaVelocity, ForceMode.VelocityChange); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scripts/Player.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7425618305adfba4e8aea04f6f0264ba 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scripts/SendMessageState.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public sealed class SendMessageState : StateMachineBehaviour 4 | { 5 | [SerializeField] private string onEnter; 6 | [SerializeField] private string onExit; 7 | 8 | public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) 9 | { 10 | if (!string.IsNullOrEmpty(onEnter)) 11 | { 12 | animator.SendMessage(onEnter, SendMessageOptions.RequireReceiver); 13 | } 14 | } 15 | 16 | public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) 17 | { 18 | if (!string.IsNullOrEmpty(onExit)) 19 | { 20 | animator.SendMessage(onExit, SendMessageOptions.RequireReceiver); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /StateMachines/Assets/Scripts/SendMessageState.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 616da9ba56f7d3943970abd298fa80ab 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /StateMachines/Assets/csc.rsp: -------------------------------------------------------------------------------- 1 | -nowarn:649 2 | -warnaserror -------------------------------------------------------------------------------- /StateMachines/Assets/csc.rsp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 37171d99ca2581a4c9342b655a72f809 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /StateMachines/Documentation/CoyoteTime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/StateMachines/Documentation/CoyoteTime.png -------------------------------------------------------------------------------- /StateMachines/Documentation/DoubleJump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/StateMachines/Documentation/DoubleJump.png -------------------------------------------------------------------------------- /StateMachines/Documentation/JumpController.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/StateMachines/Documentation/JumpController.png -------------------------------------------------------------------------------- /StateMachines/Documentation/JumpController2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/StateMachines/Documentation/JumpController2.png -------------------------------------------------------------------------------- /StateMachines/Documentation/SendMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/StateMachines/Documentation/SendMessage.png -------------------------------------------------------------------------------- /StateMachines/Documentation/StateMachine.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | 3 | edge [dir=both] 4 | MovingForward [label="Moving\nForward"]; 5 | MovingBackward [label="Moving\nBackward"]; 6 | JumpingHoldingUp [label="Jumping\n(Holding Up)"] 7 | 8 | JumpingHoldingUp -> Jumping; 9 | Jumping -> Standing; 10 | Standing -> MovingBackward; 11 | Standing -> Ducking; 12 | Standing -> MovingForward; 13 | 14 | {rank=same;MovingForward;MovingBackward;Ducking} 15 | MovingBackward->Ducking->MovingForward [style=invis]; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /StateMachines/Documentation/StateMachine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumpn/gamedev-workshop/00fc539634b6099f44bb973d5fac68151ae05799/StateMachines/Documentation/StateMachine.png -------------------------------------------------------------------------------- /StateMachines/Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.test-framework": "1.1.14", 4 | "com.unity.timeline": "1.2.15", 5 | "com.unity.modules.animation": "1.0.0", 6 | "com.unity.modules.audio": "1.0.0", 7 | "com.unity.modules.director": "1.0.0", 8 | "com.unity.modules.imgui": "1.0.0", 9 | "com.unity.modules.jsonserialize": "1.0.0", 10 | "com.unity.modules.particlesystem": "1.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /StateMachines/Packages/packages-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.ext.nunit": { 4 | "version": "1.0.0", 5 | "depth": 1, 6 | "source": "registry", 7 | "dependencies": {}, 8 | "url": "https://packages.unity.com" 9 | }, 10 | "com.unity.test-framework": { 11 | "version": "1.1.14", 12 | "depth": 0, 13 | "source": "registry", 14 | "dependencies": { 15 | "com.unity.ext.nunit": "1.0.0", 16 | "com.unity.modules.imgui": "1.0.0", 17 | "com.unity.modules.jsonserialize": "1.0.0" 18 | }, 19 | "url": "https://packages.unity.com" 20 | }, 21 | "com.unity.timeline": { 22 | "version": "1.2.15", 23 | "depth": 0, 24 | "source": "registry", 25 | "dependencies": {}, 26 | "url": "https://packages.unity.com" 27 | }, 28 | "com.unity.modules.animation": { 29 | "version": "1.0.0", 30 | "depth": 0, 31 | "source": "builtin", 32 | "dependencies": {} 33 | }, 34 | "com.unity.modules.audio": { 35 | "version": "1.0.0", 36 | "depth": 0, 37 | "source": "builtin", 38 | "dependencies": {} 39 | }, 40 | "com.unity.modules.director": { 41 | "version": "1.0.0", 42 | "depth": 0, 43 | "source": "builtin", 44 | "dependencies": { 45 | "com.unity.modules.audio": "1.0.0", 46 | "com.unity.modules.animation": "1.0.0" 47 | } 48 | }, 49 | "com.unity.modules.imgui": { 50 | "version": "1.0.0", 51 | "depth": 0, 52 | "source": "builtin", 53 | "dependencies": {} 54 | }, 55 | "com.unity.modules.jsonserialize": { 56 | "version": "1.0.0", 57 | "depth": 0, 58 | "source": "builtin", 59 | "dependencies": {} 60 | }, 61 | "com.unity.modules.particlesystem": { 62 | "version": "1.0.0", 63 | "depth": 0, 64 | "source": "builtin", 65 | "dependencies": {} 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!11 &1 4 | AudioManager: 5 | m_ObjectHideFlags: 0 6 | m_Volume: 1 7 | Rolloff Scale: 1 8 | Doppler Factor: 1 9 | Default Speaker Mode: 2 10 | m_SampleRate: 0 11 | m_DSPBufferSize: 1024 12 | m_VirtualVoiceCount: 512 13 | m_RealVoiceCount: 32 14 | m_SpatializerPlugin: 15 | m_AmbisonicDecoderPlugin: 16 | m_DisableAudio: 0 17 | m_VirtualizeEffects: 1 18 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!236 &1 4 | ClusterInputManager: 5 | m_ObjectHideFlags: 0 6 | m_Inputs: [] 7 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!55 &1 4 | PhysicsManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 13 7 | m_Gravity: {x: 0, y: -19.81, z: 0} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_BounceThreshold: 2 10 | m_SleepThreshold: 0.005 11 | m_DefaultContactOffset: 0.01 12 | m_DefaultSolverIterations: 6 13 | m_DefaultSolverVelocityIterations: 1 14 | m_QueriesHitBackfaces: 0 15 | m_QueriesHitTriggers: 1 16 | m_EnableAdaptiveForce: 0 17 | m_ClothInterCollisionDistance: 0 18 | m_ClothInterCollisionStiffness: 0 19 | m_ContactsGeneration: 1 20 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21 | m_AutoSimulation: 1 22 | m_AutoSyncTransforms: 0 23 | m_ReuseCollisionCallbacks: 1 24 | m_ClothInterCollisionSettingsToggle: 0 25 | m_ClothGravity: {x: 0, y: -9.81, z: 0} 26 | m_ContactPairsMode: 0 27 | m_BroadphaseType: 0 28 | m_WorldBounds: 29 | m_Center: {x: 0, y: 0, z: 0} 30 | m_Extent: {x: 250, y: 250, z: 250} 31 | m_WorldSubdivisions: 8 32 | m_FrictionType: 0 33 | m_EnableEnhancedDeterminism: 0 34 | m_EnableUnifiedHeightmaps: 1 35 | m_SolverType: 0 36 | m_DefaultMaxAngularSpeed: 7 37 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1045 &1 4 | EditorBuildSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Scenes: 8 | - enabled: 1 9 | path: Assets/Scenes/SampleScene.unity 10 | guid: 08316ebcabc3d4941a86c1298333ac41 11 | m_configObjects: {} 12 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!159 &1 4 | EditorSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 9 7 | m_ExternalVersionControlSupport: Visible Meta Files 8 | m_SerializationMode: 2 9 | m_LineEndingsForNewScripts: 2 10 | m_DefaultBehaviorMode: 0 11 | m_PrefabRegularEnvironment: {fileID: 0} 12 | m_PrefabUIEnvironment: {fileID: 0} 13 | m_SpritePackerMode: 0 14 | m_SpritePackerPaddingPower: 1 15 | m_EtcTextureCompressorBehavior: 1 16 | m_EtcTextureFastCompressor: 1 17 | m_EtcTextureNormalCompressor: 2 18 | m_EtcTextureBestCompressor: 4 19 | m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmref 20 | m_ProjectGenerationRootNamespace: 21 | m_CollabEditorSettings: 22 | inProgressEnabled: 1 23 | m_EnableTextureStreamingInEditMode: 1 24 | m_EnableTextureStreamingInPlayMode: 1 25 | m_AsyncShaderCompilation: 1 26 | m_EnterPlayModeOptionsEnabled: 0 27 | m_EnterPlayModeOptions: 3 28 | m_ShowLightmapResolutionOverlay: 1 29 | m_UseLegacyProbeSampleCount: 1 30 | m_AssetPipelineMode: 1 31 | m_CacheServerMode: 0 32 | m_CacheServerEndpoint: 33 | m_CacheServerNamespacePrefix: default 34 | m_CacheServerEnableDownload: 1 35 | m_CacheServerEnableUpload: 1 36 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!30 &1 4 | GraphicsSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 12 7 | m_Deferred: 8 | m_Mode: 1 9 | m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} 10 | m_DeferredReflections: 11 | m_Mode: 1 12 | m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} 13 | m_ScreenSpaceShadows: 14 | m_Mode: 1 15 | m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} 16 | m_LegacyDeferred: 17 | m_Mode: 1 18 | m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} 19 | m_DepthNormals: 20 | m_Mode: 1 21 | m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} 22 | m_MotionVectors: 23 | m_Mode: 1 24 | m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} 25 | m_LightHalo: 26 | m_Mode: 1 27 | m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} 28 | m_LensFlare: 29 | m_Mode: 1 30 | m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} 31 | m_AlwaysIncludedShaders: 32 | - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} 33 | - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} 34 | - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} 35 | - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} 36 | - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} 37 | - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} 38 | - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} 39 | m_PreloadedShaders: [] 40 | m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, 41 | type: 0} 42 | m_CustomRenderPipeline: {fileID: 0} 43 | m_TransparencySortMode: 0 44 | m_TransparencySortAxis: {x: 0, y: 0, z: 1} 45 | m_DefaultRenderingPath: 1 46 | m_DefaultMobileRenderingPath: 1 47 | m_TierSettings: [] 48 | m_LightmapStripping: 0 49 | m_FogStripping: 0 50 | m_InstancingStripping: 0 51 | m_LightmapKeepPlain: 1 52 | m_LightmapKeepDirCombined: 1 53 | m_LightmapKeepDynamicPlain: 1 54 | m_LightmapKeepDynamicDirCombined: 1 55 | m_LightmapKeepShadowMask: 1 56 | m_LightmapKeepSubtractive: 1 57 | m_FogKeepLinear: 1 58 | m_FogKeepExp: 1 59 | m_FogKeepExp2: 1 60 | m_AlbedoSwatchInfos: [] 61 | m_LightsUseLinearIntensity: 0 62 | m_LightsUseColorTemperature: 0 63 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!13 &1 4 | InputManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Axes: 8 | - serializedVersion: 3 9 | m_Name: Horizontal 10 | descriptiveName: 11 | descriptiveNegativeName: 12 | negativeButton: left 13 | positiveButton: right 14 | altNegativeButton: a 15 | altPositiveButton: d 16 | gravity: 3 17 | dead: 0.001 18 | sensitivity: 3 19 | snap: 1 20 | invert: 0 21 | type: 0 22 | axis: 0 23 | joyNum: 0 24 | - serializedVersion: 3 25 | m_Name: Vertical 26 | descriptiveName: 27 | descriptiveNegativeName: 28 | negativeButton: down 29 | positiveButton: up 30 | altNegativeButton: s 31 | altPositiveButton: w 32 | gravity: 3 33 | dead: 0.001 34 | sensitivity: 3 35 | snap: 1 36 | invert: 0 37 | type: 0 38 | axis: 0 39 | joyNum: 0 40 | - serializedVersion: 3 41 | m_Name: Fire1 42 | descriptiveName: 43 | descriptiveNegativeName: 44 | negativeButton: 45 | positiveButton: left ctrl 46 | altNegativeButton: 47 | altPositiveButton: mouse 0 48 | gravity: 1000 49 | dead: 0.001 50 | sensitivity: 1000 51 | snap: 0 52 | invert: 0 53 | type: 0 54 | axis: 0 55 | joyNum: 0 56 | - serializedVersion: 3 57 | m_Name: Fire2 58 | descriptiveName: 59 | descriptiveNegativeName: 60 | negativeButton: 61 | positiveButton: left alt 62 | altNegativeButton: 63 | altPositiveButton: mouse 1 64 | gravity: 1000 65 | dead: 0.001 66 | sensitivity: 1000 67 | snap: 0 68 | invert: 0 69 | type: 0 70 | axis: 0 71 | joyNum: 0 72 | - serializedVersion: 3 73 | m_Name: Fire3 74 | descriptiveName: 75 | descriptiveNegativeName: 76 | negativeButton: 77 | positiveButton: left shift 78 | altNegativeButton: 79 | altPositiveButton: mouse 2 80 | gravity: 1000 81 | dead: 0.001 82 | sensitivity: 1000 83 | snap: 0 84 | invert: 0 85 | type: 0 86 | axis: 0 87 | joyNum: 0 88 | - serializedVersion: 3 89 | m_Name: Jump 90 | descriptiveName: 91 | descriptiveNegativeName: 92 | negativeButton: 93 | positiveButton: space 94 | altNegativeButton: 95 | altPositiveButton: 96 | gravity: 1000 97 | dead: 0.001 98 | sensitivity: 1000 99 | snap: 0 100 | invert: 0 101 | type: 0 102 | axis: 0 103 | joyNum: 0 104 | - serializedVersion: 3 105 | m_Name: Mouse X 106 | descriptiveName: 107 | descriptiveNegativeName: 108 | negativeButton: 109 | positiveButton: 110 | altNegativeButton: 111 | altPositiveButton: 112 | gravity: 0 113 | dead: 0 114 | sensitivity: 0.1 115 | snap: 0 116 | invert: 0 117 | type: 1 118 | axis: 0 119 | joyNum: 0 120 | - serializedVersion: 3 121 | m_Name: Mouse Y 122 | descriptiveName: 123 | descriptiveNegativeName: 124 | negativeButton: 125 | positiveButton: 126 | altNegativeButton: 127 | altPositiveButton: 128 | gravity: 0 129 | dead: 0 130 | sensitivity: 0.1 131 | snap: 0 132 | invert: 0 133 | type: 1 134 | axis: 1 135 | joyNum: 0 136 | - serializedVersion: 3 137 | m_Name: Mouse ScrollWheel 138 | descriptiveName: 139 | descriptiveNegativeName: 140 | negativeButton: 141 | positiveButton: 142 | altNegativeButton: 143 | altPositiveButton: 144 | gravity: 0 145 | dead: 0 146 | sensitivity: 0.1 147 | snap: 0 148 | invert: 0 149 | type: 1 150 | axis: 2 151 | joyNum: 0 152 | - serializedVersion: 3 153 | m_Name: Horizontal 154 | descriptiveName: 155 | descriptiveNegativeName: 156 | negativeButton: 157 | positiveButton: 158 | altNegativeButton: 159 | altPositiveButton: 160 | gravity: 0 161 | dead: 0.19 162 | sensitivity: 1 163 | snap: 0 164 | invert: 0 165 | type: 2 166 | axis: 0 167 | joyNum: 0 168 | - serializedVersion: 3 169 | m_Name: Vertical 170 | descriptiveName: 171 | descriptiveNegativeName: 172 | negativeButton: 173 | positiveButton: 174 | altNegativeButton: 175 | altPositiveButton: 176 | gravity: 0 177 | dead: 0.19 178 | sensitivity: 1 179 | snap: 0 180 | invert: 1 181 | type: 2 182 | axis: 1 183 | joyNum: 0 184 | - serializedVersion: 3 185 | m_Name: Fire1 186 | descriptiveName: 187 | descriptiveNegativeName: 188 | negativeButton: 189 | positiveButton: joystick button 0 190 | altNegativeButton: 191 | altPositiveButton: 192 | gravity: 1000 193 | dead: 0.001 194 | sensitivity: 1000 195 | snap: 0 196 | invert: 0 197 | type: 0 198 | axis: 0 199 | joyNum: 0 200 | - serializedVersion: 3 201 | m_Name: Fire2 202 | descriptiveName: 203 | descriptiveNegativeName: 204 | negativeButton: 205 | positiveButton: joystick button 1 206 | altNegativeButton: 207 | altPositiveButton: 208 | gravity: 1000 209 | dead: 0.001 210 | sensitivity: 1000 211 | snap: 0 212 | invert: 0 213 | type: 0 214 | axis: 0 215 | joyNum: 0 216 | - serializedVersion: 3 217 | m_Name: Fire3 218 | descriptiveName: 219 | descriptiveNegativeName: 220 | negativeButton: 221 | positiveButton: joystick button 2 222 | altNegativeButton: 223 | altPositiveButton: 224 | gravity: 1000 225 | dead: 0.001 226 | sensitivity: 1000 227 | snap: 0 228 | invert: 0 229 | type: 0 230 | axis: 0 231 | joyNum: 0 232 | - serializedVersion: 3 233 | m_Name: Jump 234 | descriptiveName: 235 | descriptiveNegativeName: 236 | negativeButton: 237 | positiveButton: joystick button 3 238 | altNegativeButton: 239 | altPositiveButton: 240 | gravity: 1000 241 | dead: 0.001 242 | sensitivity: 1000 243 | snap: 0 244 | invert: 0 245 | type: 0 246 | axis: 0 247 | joyNum: 0 248 | - serializedVersion: 3 249 | m_Name: Submit 250 | descriptiveName: 251 | descriptiveNegativeName: 252 | negativeButton: 253 | positiveButton: return 254 | altNegativeButton: 255 | altPositiveButton: joystick button 0 256 | gravity: 1000 257 | dead: 0.001 258 | sensitivity: 1000 259 | snap: 0 260 | invert: 0 261 | type: 0 262 | axis: 0 263 | joyNum: 0 264 | - serializedVersion: 3 265 | m_Name: Submit 266 | descriptiveName: 267 | descriptiveNegativeName: 268 | negativeButton: 269 | positiveButton: enter 270 | altNegativeButton: 271 | altPositiveButton: space 272 | gravity: 1000 273 | dead: 0.001 274 | sensitivity: 1000 275 | snap: 0 276 | invert: 0 277 | type: 0 278 | axis: 0 279 | joyNum: 0 280 | - serializedVersion: 3 281 | m_Name: Cancel 282 | descriptiveName: 283 | descriptiveNegativeName: 284 | negativeButton: 285 | positiveButton: escape 286 | altNegativeButton: 287 | altPositiveButton: joystick button 1 288 | gravity: 1000 289 | dead: 0.001 290 | sensitivity: 1000 291 | snap: 0 292 | invert: 0 293 | type: 0 294 | axis: 0 295 | joyNum: 0 296 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!126 &1 4 | NavMeshProjectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | areas: 8 | - name: Walkable 9 | cost: 1 10 | - name: Not Walkable 11 | cost: 1 12 | - name: Jump 13 | cost: 2 14 | - name: 15 | cost: 1 16 | - name: 17 | cost: 1 18 | - name: 19 | cost: 1 20 | - name: 21 | cost: 1 22 | - name: 23 | cost: 1 24 | - name: 25 | cost: 1 26 | - name: 27 | cost: 1 28 | - name: 29 | cost: 1 30 | - name: 31 | cost: 1 32 | - name: 33 | cost: 1 34 | - name: 35 | cost: 1 36 | - name: 37 | cost: 1 38 | - name: 39 | cost: 1 40 | - name: 41 | cost: 1 42 | - name: 43 | cost: 1 44 | - name: 45 | cost: 1 46 | - name: 47 | cost: 1 48 | - name: 49 | cost: 1 50 | - name: 51 | cost: 1 52 | - name: 53 | cost: 1 54 | - name: 55 | cost: 1 56 | - name: 57 | cost: 1 58 | - name: 59 | cost: 1 60 | - name: 61 | cost: 1 62 | - name: 63 | cost: 1 64 | - name: 65 | cost: 1 66 | - name: 67 | cost: 1 68 | - name: 69 | cost: 1 70 | - name: 71 | cost: 1 72 | m_LastAgentTypeID: -887442657 73 | m_Settings: 74 | - serializedVersion: 2 75 | agentTypeID: 0 76 | agentRadius: 0.5 77 | agentHeight: 2 78 | agentSlope: 45 79 | agentClimb: 0.75 80 | ledgeDropHeight: 0 81 | maxJumpAcrossDistance: 0 82 | minRegionArea: 2 83 | manualCellSize: 0 84 | cellSize: 0.16666667 85 | manualTileSize: 0 86 | tileSize: 256 87 | accuratePlacement: 0 88 | debug: 89 | m_Flags: 0 90 | m_SettingNames: 91 | - Humanoid 92 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!149 &1 4 | NetworkManager: 5 | m_ObjectHideFlags: 0 6 | m_DebugLevel: 0 7 | m_Sendrate: 15 8 | m_AssetToPrefab: {} 9 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!19 &1 4 | Physics2DSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 4 7 | m_Gravity: {x: 0, y: -9.81} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_VelocityIterations: 8 10 | m_PositionIterations: 3 11 | m_VelocityThreshold: 1 12 | m_MaxLinearCorrection: 0.2 13 | m_MaxAngularCorrection: 8 14 | m_MaxTranslationSpeed: 100 15 | m_MaxRotationSpeed: 360 16 | m_BaumgarteScale: 0.2 17 | m_BaumgarteTimeOfImpactScale: 0.75 18 | m_TimeToSleep: 0.5 19 | m_LinearSleepTolerance: 0.01 20 | m_AngularSleepTolerance: 2 21 | m_DefaultContactOffset: 0.01 22 | m_AutoSimulation: 1 23 | m_QueriesHitTriggers: 1 24 | m_QueriesStartInColliders: 1 25 | m_ChangeStopsCallbacks: 0 26 | m_CallbacksOnDisable: 1 27 | m_ReuseCollisionCallbacks: 1 28 | m_AutoSyncTransforms: 0 29 | m_AlwaysShowColliders: 0 30 | m_ShowColliderSleep: 1 31 | m_ShowColliderContacts: 0 32 | m_ShowColliderAABB: 0 33 | m_ContactArrowScale: 0.2 34 | m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} 35 | m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} 36 | m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} 37 | m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} 38 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 39 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1386491679 &1 4 | PresetManager: 5 | m_ObjectHideFlags: 0 6 | m_DefaultList: 7 | - type: 8 | m_NativeTypeID: 108 9 | m_ManagedTypePPtr: {fileID: 0} 10 | m_ManagedTypeFallback: 11 | defaultPresets: 12 | - m_Preset: {fileID: 2655988077585873504, guid: c1cf8506f04ef2c4a88b64b6c4202eea, 13 | type: 2} 14 | - type: 15 | m_NativeTypeID: 1020 16 | m_ManagedTypePPtr: {fileID: 0} 17 | m_ManagedTypeFallback: 18 | defaultPresets: 19 | - m_Preset: {fileID: 2655988077585873504, guid: 0cd792cc87e492d43b4e95b205fc5cc6, 20 | type: 2} 21 | - type: 22 | m_NativeTypeID: 1006 23 | m_ManagedTypePPtr: {fileID: 0} 24 | m_ManagedTypeFallback: 25 | defaultPresets: 26 | - m_Preset: {fileID: 2655988077585873504, guid: 7a99f8aa944efe94cb9bd74562b7d5f9, 27 | type: 2} 28 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2019.4.2f1 2 | m_EditorVersionWithRevision: 2019.4.2f1 (20b4642a3455) 3 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!47 &1 4 | QualitySettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 5 7 | m_CurrentQuality: 2 8 | m_QualitySettings: 9 | - serializedVersion: 2 10 | name: Low 11 | pixelLightCount: 0 12 | shadows: 0 13 | shadowResolution: 0 14 | shadowProjection: 1 15 | shadowCascades: 1 16 | shadowDistance: 20 17 | shadowNearPlaneOffset: 3 18 | shadowCascade2Split: 0.33333334 19 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 20 | shadowmaskMode: 0 21 | blendWeights: 2 22 | textureQuality: 0 23 | anisotropicTextures: 0 24 | antiAliasing: 0 25 | softParticles: 0 26 | softVegetation: 0 27 | realtimeReflectionProbes: 0 28 | billboardsFaceCameraPosition: 0 29 | vSyncCount: 0 30 | lodBias: 0.4 31 | maximumLODLevel: 0 32 | streamingMipmapsActive: 0 33 | streamingMipmapsAddAllCameras: 1 34 | streamingMipmapsMemoryBudget: 512 35 | streamingMipmapsRenderersPerFrame: 512 36 | streamingMipmapsMaxLevelReduction: 2 37 | streamingMipmapsMaxFileIORequests: 1024 38 | particleRaycastBudget: 16 39 | asyncUploadTimeSlice: 2 40 | asyncUploadBufferSize: 16 41 | asyncUploadPersistentBuffer: 1 42 | resolutionScalingFixedDPIFactor: 1 43 | excludedTargetPlatforms: [] 44 | - serializedVersion: 2 45 | name: Medium 46 | pixelLightCount: 1 47 | shadows: 1 48 | shadowResolution: 0 49 | shadowProjection: 1 50 | shadowCascades: 1 51 | shadowDistance: 20 52 | shadowNearPlaneOffset: 3 53 | shadowCascade2Split: 0.33333334 54 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 55 | shadowmaskMode: 0 56 | blendWeights: 2 57 | textureQuality: 0 58 | anisotropicTextures: 1 59 | antiAliasing: 0 60 | softParticles: 0 61 | softVegetation: 0 62 | realtimeReflectionProbes: 0 63 | billboardsFaceCameraPosition: 0 64 | vSyncCount: 1 65 | lodBias: 0.7 66 | maximumLODLevel: 0 67 | streamingMipmapsActive: 0 68 | streamingMipmapsAddAllCameras: 1 69 | streamingMipmapsMemoryBudget: 512 70 | streamingMipmapsRenderersPerFrame: 512 71 | streamingMipmapsMaxLevelReduction: 2 72 | streamingMipmapsMaxFileIORequests: 1024 73 | particleRaycastBudget: 64 74 | asyncUploadTimeSlice: 2 75 | asyncUploadBufferSize: 16 76 | asyncUploadPersistentBuffer: 1 77 | resolutionScalingFixedDPIFactor: 1 78 | excludedTargetPlatforms: [] 79 | - serializedVersion: 2 80 | name: High 81 | pixelLightCount: 2 82 | shadows: 2 83 | shadowResolution: 1 84 | shadowProjection: 1 85 | shadowCascades: 2 86 | shadowDistance: 40 87 | shadowNearPlaneOffset: 3 88 | shadowCascade2Split: 0.33333334 89 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 90 | shadowmaskMode: 1 91 | blendWeights: 2 92 | textureQuality: 0 93 | anisotropicTextures: 1 94 | antiAliasing: 2 95 | softParticles: 0 96 | softVegetation: 1 97 | realtimeReflectionProbes: 1 98 | billboardsFaceCameraPosition: 1 99 | vSyncCount: 1 100 | lodBias: 1 101 | maximumLODLevel: 0 102 | streamingMipmapsActive: 0 103 | streamingMipmapsAddAllCameras: 1 104 | streamingMipmapsMemoryBudget: 512 105 | streamingMipmapsRenderersPerFrame: 512 106 | streamingMipmapsMaxLevelReduction: 2 107 | streamingMipmapsMaxFileIORequests: 1024 108 | particleRaycastBudget: 256 109 | asyncUploadTimeSlice: 2 110 | asyncUploadBufferSize: 16 111 | asyncUploadPersistentBuffer: 1 112 | resolutionScalingFixedDPIFactor: 1 113 | excludedTargetPlatforms: [] 114 | m_PerPlatformDefaultQuality: 115 | Android: 1 116 | Nintendo 3DS: 2 117 | Nintendo Switch: 2 118 | PS4: 2 119 | PSP2: 1 120 | Standalone: 2 121 | Tizen: 1 122 | WebGL: 2 123 | WiiU: 2 124 | Windows Store Apps: 2 125 | XboxOne: 2 126 | iPhone: 1 127 | tvOS: 1 128 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!78 &1 4 | TagManager: 5 | serializedVersion: 2 6 | tags: [] 7 | layers: 8 | - Default 9 | - TransparentFX 10 | - Ignore Raycast 11 | - 12 | - Water 13 | - UI 14 | - 15 | - 16 | - PostProcessing 17 | - 18 | - Terrain 19 | - 20 | - 21 | - 22 | - 23 | - 24 | - 25 | - 26 | - 27 | - 28 | - 29 | - 30 | - 31 | - 32 | - 33 | - 34 | - 35 | - 36 | - 37 | - 38 | - 39 | - 40 | m_SortingLayers: 41 | - name: Default 42 | uniqueID: 0 43 | locked: 0 44 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!5 &1 4 | TimeManager: 5 | m_ObjectHideFlags: 0 6 | Fixed Timestep: 0.02 7 | Maximum Allowed Timestep: 0.1 8 | m_TimeScale: 1 9 | Maximum Particle Timestep: 0.03 10 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!310 &1 4 | UnityConnectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 1 7 | m_Enabled: 0 8 | m_TestMode: 0 9 | m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events 10 | m_EventUrl: https://cdp.cloud.unity3d.com/v1/events 11 | m_ConfigUrl: https://config.uca.cloud.unity3d.com 12 | m_TestInitMode: 0 13 | CrashReportingSettings: 14 | m_EventUrl: https://perf-events.cloud.unity3d.com 15 | m_Enabled: 0 16 | m_LogBufferSize: 10 17 | m_CaptureEditorExceptions: 1 18 | UnityPurchasingSettings: 19 | m_Enabled: 0 20 | m_TestMode: 0 21 | UnityAnalyticsSettings: 22 | m_Enabled: 0 23 | m_TestMode: 0 24 | m_InitializeOnStartup: 1 25 | UnityAdsSettings: 26 | m_Enabled: 0 27 | m_InitializeOnStartup: 1 28 | m_TestMode: 0 29 | m_IosGameId: 30 | m_AndroidGameId: 31 | m_GameIds: {} 32 | m_GameId: 33 | PerformanceReportingSettings: 34 | m_Enabled: 0 35 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/VFXManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!937362698 &1 4 | VFXManager: 5 | m_ObjectHideFlags: 0 6 | m_IndirectShader: {fileID: 0} 7 | m_CopyBufferShader: {fileID: 0} 8 | m_SortShader: {fileID: 0} 9 | m_RenderPipeSettingsPath: 10 | m_FixedTimeStep: 0.016666668 11 | m_MaxDeltaTime: 0.05 12 | -------------------------------------------------------------------------------- /StateMachines/ProjectSettings/XRSettings.asset: -------------------------------------------------------------------------------- 1 | { 2 | "m_SettingKeys": [ 3 | "VR Device Disabled", 4 | "VR Device User Alert" 5 | ], 6 | "m_SettingValues": [ 7 | "False", 8 | "False" 9 | ] 10 | } -------------------------------------------------------------------------------- /StateMachines/README-zh-TW.md: -------------------------------------------------------------------------------- 1 | # 為何使用「狀態機」 2 | 避免程式碼義大利麵化。所有事物都可視為一種狀態機,並避免狀態與邏輯的耦合。 3 | 4 | # 問題 5 | 當一個物件的內在狀態改變時,行為也會跟著改變。聽起來很抽象,但是這是在遊戲程式中非常常見的案例。考量以下狀況: 6 | 7 | 1. 輸入判定:在格鬥遊戲中,按下 *P鍵* 出拳,往往會依照角色目前正在前進、後退、直立、跳躍或蹲下等不同狀態,而出不同的拳。 8 | 2. 電腦操縱角色:機器人角色在看到玩家角色時,應該要考量角色的目前血量、武器種類、同伴數量等條件來做出不同的反應。 9 | 3. 遊戲流程控制:退出、暫停或儲存遊戲等功能的可執行與否,應該要依照目前遊戲是否正在載入、儲存、已經暫停、正在退出等狀態來決定。 10 | 11 | 上述這些常見情境的共通點,就是它們往往充滿各式各樣的特例。為了要實作相關邏輯來對應各種特例,你的程式碼很快就變成義大利麵了。 12 | 13 | ```csharp 14 | if (Input.GetButton("Punch")) { 15 | if (player.velocity.z > 0) { 16 | ForwardPunch(); 17 | } else if (player.velocity.z < 0) { 18 | Grapple(); 19 | } else if (player.isJumping) { 20 | if (Input.GetAxis("Vertical") > 0) { 21 | UpwardPunch(); 22 | } else { 23 | DownwardPunch(); 24 | } 25 | } else if (player.isDucking) { 26 | UpwardPunch(); 27 | } else { 28 | StandingPunch(); 29 | } 30 | } 31 | ``` 32 | 33 | 當你才剛寫完上述程式碼,遊戲企劃又突然冒出對你說,在蹲下兩秒集氣後起身瞬間按下 *P鍵* 要可以使出必殺技「升龍臂」哦!還有還有,在跳躍滯空高度上升期間,連按 *P鍵* 兩下要可以丟出火球。 34 | 35 | ``` 36 | (╯°□°)╯︵ ┻━┻ 37 | ``` 38 | 39 | # 解決方案 40 | 實際上,各種特例跟「狀態」是等價的。你程式碼中的每個條件分支,都是一個不同的狀態。在紙上先用筆把各種狀態畫下來,並用箭頭連接來代表狀態的改變。當你開始熟悉這樣的操作模式後,就會發現這種結構無所不在。 41 | 42 | ![狀態機範例圖](./Documentation/StateMachine.png "我在哪裡看過類似的結構?") 43 | 44 | 困難的地方在於,如何保證所有的狀態改變,都透過正確的條件、在正確的時機被觸發。但這件事跟實作 *P鍵* 要在各個狀態下執行什麼東西,是可以各自獨立的。我們切斷處理狀態改變部分,與執行行動部分的耦合,那就更容易在更乾淨的情境下來各別檢視他們。 45 | 46 | 現在我們知道問題的*輪廓*了,但要怎麼實作出來? 47 | 48 | ## Animators 49 | 上面那張圖,其實很像 [Unity 的 Animator 動畫控制系統](https://docs.unity3d.com/Manual/Animator.html)。而實際上,這就是我們現在要利用的,因為 Unity Animator 動畫控制器實際上能做到的事情遠遠不只單純播放動畫而已。讓我們來看看實例上怎麼運用吧。 50 | 51 | # 範例 52 | 假設我們要實作一個 platformer 遊戲的輸入處理系統。我們希望支援這類遊戲的[常見操作](https://celestegame.fandom.com/wiki/Moves),如: 53 | 1. 依據玩家按壓 *跳躍鍵* 的時間長短來決定跳躍高度。 54 | 2. 讓玩家角色在跑步離開平台地板後,有短暫的[起跳容錯時間](https://twitter.com/DavesInHisPants/status/1281189584462917632)。 55 | 3. 在空中能多跳一次的兩段跳能力。 56 | 57 | ## 跳躍控制器 58 | ![跳躍控制器範例圖](./Documentation/JumpController.png "跳躍吧時空少女!") 59 | 60 | 一開始我們先建立一個新的動畫控制器。帶入的兩個布林值參數代表 *跳躍鍵* 是否正被按住(JumpButton),以及玩家角色目前是否腳踏實地(OnGround)。 61 | 62 | 我們狀態機中的第一個狀態,是 `On Ground`,只有在這狀態下玩家才能進行跳躍。我們還需要一個相對的狀態 `Falling`,以及在後面談到「狀態轉換」時會用到的其他幾個狀態。 63 | 64 | ![跳躍控制器加上狀態轉換條件的範例圖](./Documentation/JumpController2.png "這邊有個臭蟲,有注意到嗎?") 65 | 66 | 先來看看 `On Ground` 狀態吧。當玩家按下 *跳躍鍵* 時,我們會開始一個跳躍的動作。當玩家放開 *跳躍鍵* 時,我們會依照一些遊戲邏輯設定來停止跳躍,讓玩家不會再上升太多高度。然後就會馬上轉換到 `Falling` 狀態,並不斷降落,直到玩家角色再次腳踏實地為止。 67 | 68 | 如果玩家落下瞬間,我們就馬上再從 `Falling` 轉換到 `On Ground` 狀態的話,便會發生玩家只要按住 *跳躍鍵*,就能像在彈簧墊上一樣不斷上下連續彈跳的情況,因為狀態機又會立刻轉換到 `Start Jump` 狀態。要把這個問題修掉,就得加入像 `On Ground (still holding Jump)` 這樣的中間狀態。 69 | 70 | 真沒想到,光處理跳躍就頗難的。我們的狀態機目前為止已經出現了不少邏輯,但也只能對應「可變跳躍高度」這項基本要素而已。可以想見這有多容易會產生義大利麵程式碼。 71 | 72 | ### 提供參數 73 | 要使我們的跳躍控制器能作動,還需要不斷傳入更新的參數值才行。幸好跟 Unity 動畫控制器的溝通還算簡單。 74 | ```csharp 75 | public class JumpControllerParameterProvider : MonoBehaviour 76 | { 77 | private bool onGround; 78 | 79 | void Update() 80 | { 81 | animator.SetBool("OnGround", onGround); 82 | animator.SetBool("JumpButton", Input.GetButton("Jump")); 83 | } 84 | 85 | void FixedUpdate() 86 | { 87 | onGround = Physics.SphereCast(rigidbody.position, radius, Vector3.down, 88 | out RaycastHit hitInfo, distance, groundLayerMask); 89 | } 90 | } 91 | ``` 92 | 93 | ### 傳送訊息 94 | 要讓這個跳躍控制器能真的對遊戲本體產生影響,我們得讓它可以和玩家角色相關程式溝通。可以透過繼承 `StateMachineBehaviour` 類別,來讓下面這段程式得以被加到狀態機中的任意狀態上。 95 | 96 | ![StateMachineBehaviour](./Documentation/SendMessage.png "TODO") 97 | 98 | 為了簡單起見,這邊我們使用 [Unity 的 SendMessage](https://docs.unity3d.com/ScriptReference/Component.SendMessage.html) 系統。 99 | 100 | ```csharp 101 | public class SendMessageState : StateMachineBehaviour 102 | { 103 | public string onEnter, onExit; 104 | 105 | public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) 106 | { 107 | if (!string.IsNullOrEmpty(onEnter)) animator.SendMessage(onEnter); 108 | } 109 | 110 | public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) 111 | { 112 | if (!string.IsNullOrEmpty(onExit)) animator.SendMessage(onExit); 113 | } 114 | } 115 | ``` 116 | 117 | ### 處理訊息 118 | 最後,我們需要另一段程式來接收動畫控制器所送的訊息,並帶入合適的遊戲邏輯。在目前的範例中,我們需要處理的是簡單的物理行為。 119 | 120 | ```csharp 121 | public class JumpControllerMessageHandler : MonoBehaviour 122 | { 123 | private bool startJump, stopJump; 124 | 125 | void StartJump() { startJump = true; } 126 | void StopJump() { stopJump = true; } 127 | 128 | void FixedUpdate() 129 | { 130 | if (startJump) 131 | { 132 | // 產生向上的力道來抵銷玩家角色任何向下的動量 133 | // 以讓玩家角色向上跳躍 134 | var downVelocity = Mathf.Min(rigidbody.velocity.y, 0); 135 | var deltaVelocity = new Vector3(0, jumpVelocity - downVelocity, 0); 136 | rigidbody.AddForce(deltaVelocity, ForceMode.VelocityChange); 137 | startJump = false; 138 | } 139 | if (stopJump) 140 | { 141 | // 產生剛好的力道來抵銷上升的動量 142 | var upVelocity = Mathf.Max(rigidbody.velocity.y, 0); 143 | var deltaVelocity = new Vector3(0, -upVelocity, 0); 144 | rigidbody.AddForce(deltaVelocity, ForceMode.VelocityChange); 145 | stopJump = false; 146 | } 147 | } 148 | } 149 | ``` 150 | 151 | ## 起跳容錯時間 152 | 幸運的是,把前面的架構建立起來就已經是最困難的部分了。要將[起跳容錯時間](https://celestegame.fandom.com/wiki/Moves#Coyote_Time)特性加入,只是很單純地新增一個狀態,並給予它非常短暫的 50 毫秒時限。這樣一來,就算玩家角色已經不是腳踏實地狀態,玩家還是可以在數個畫格時間內執行跳躍動作。這就是現在遊戲設計上常講的 [Game Feel](https://youtu.be/OfSpBoA6TWw?t=833)。 153 | 154 | ![起跳容錯時間](./Documentation/CoyoteTime.png "衝衝衝!") 155 | 156 | 最令人意外的地方大概是,我們根本不需要為此修改任何程式碼。所有在 `Start Jump` 與 `Stop Jump` 狀態時該發生的事,還是照常運作。 157 | 158 | ## 兩段跳 159 | 兩段跳就是指我們在玩家下墜過程中,給予第二次跳躍的能力。這個第二次跳躍到底該怎麼運作?其實就跟第一次跳躍差不多。所以我們可以把幾個有關的狀態複製一份,然後加在 `Falling` 狀態的右邊,然後把它們連起來就好了。整體問題的*輪廓*仍然保持一樣。 160 | 161 | 在這邊我們需要顧慮的就只是,確保在我們執行第二次跳躍之前,*跳躍鍵* 已經是在放開的狀態,要不然類似前面講到的彈簧墊狀況又會出現。而又跟之前一樣的,解決方法就是加入一個 `Falling (still holding Jump)` 這樣的中間狀態。 162 | 163 | ![兩段跳範例圖](./Documentation/DoubleJump.png "你跳一次不夠,那你有跳兩次嗎。") 164 | 165 | 從程式碼的角度來看,再一次地,我們什麼都不用改,一切照常運作。 166 | 167 | 現在我們可以真的體會到,將「狀態處理」與「跳躍物理實作」兩者耦合解開後的好處了。建立狀態機的結構是困難的部分,但好消息是,因為我們使用 Unity 動畫控制器來呈現狀態機,而動畫控制器非常容易除錯。只要按下播放鍵,Unity 就會顯示目前處於哪個狀態,然後跟遊戲內你看到的狀況一比較,通常馬上就可以看出錯誤出在哪裡。 168 | 169 | ## 參考資料 170 | 171 | - [Don’t Re-invent Finite State Machines: How to Repurpose Unity’s Animator](https://medium.com/the-unity-developers-handbook/dont-re-invent-finite-state-machines-how-to-repurpose-unity-s-animator-7c6c421e5785) by Darren Tsung 172 | - [Unite 2015 - Applied Mecanim : Character Animation and Combat State Machines](https://www.youtube.com/watch?v=Is9C4i4XyXk) by Aaron Horne 173 | - [Advanced AI in Unity (made easy) - State Machine Behaviors](https://www.youtube.com/watch?v=dYi-i83sq5g) by Noa Calice 174 | - [Game Programming Patterns - State](http://gameprogrammingpatterns.com/state.html) by Bob Nystrom 175 | - [Tips and Tricks for good platforming games](http://www.davetech.co.uk/gamedevplatformer) by David Strachan 176 | 177 | # 翻譯 178 | - [台灣繁體中文 (zh-TW)](README-zh-TW.md) 179 | 180 | 如果你覺得這個工作坊有其價值,並通曉另一個語言,我們非常歡迎任何幫助工作坊內容進行翻譯的協助。把本儲存庫內容 clone 下來後,增加一份特定語言在地化的 README.md,例如 README-pt-BR.md,並送 PR 給我們。 181 | --------------------------------------------------------------------------------