Unity Using Events and Building Effective UI Systems

September 7, 2021

Unity Events

UI Systems


Title:
C# Events in Unity! – Intermediate Scripting Tutorial

By:
Unity


Youtube Link – Tutorial #1

Description:
Brief introduction to using Events in Unity.


Title:
How To Build An Event System in Unity

By:
Game Dev Guide


Youtube Link – Tutorial #2

Description:
Quick showing of implementing a basic Event system in Unity for gameplay reasons.


Title:
Delegates, Events, Actions and Funcs – The Observer Pattern (Unity & C#)

By:
One Wheel Studio


Youtube Link – Tutorial #3

Description:
Covers Events, as well as Delegates, Actions, and Funcs specifically through the Observer Pattern in Unity.


Title:
Game Architecture Tips – Event Driven UI – Unity

By:
Dapper Dino


Youtube Link – Tutorial #4

Description:
An Event system built in Unity specifically with a focus on UI.


Title:
Building Unity UI that scales for a real game – Prefabs/Scenes?

By:
Jason Weimann


Youtube Link – Tutorial #5

Description:
General coverage of building effective UI systems in Unity.


Overview

I wanted to delve further into using Event systems in Unity specifically when building UI systems, so I collected a few resources I thought would help with researching that. These tutorials cover everything from the basic foundations of Events themselves to fully fledged UI system tutorials near the end implementing those tools and concepts covered in the earlier tutorials.

I think Event heavy systems are very effective ways to build out UI in games, so I want to get a better grasp of setting that up. I especially think the final tutorial by Jason Weimann will help bring those topics together as well as cover other important factors for building larger scale UI systems.

via Blogger http://stevelilleyschool.blogspot.com/2021/09/unity-using-events-and-building.html

Pokemon Randomizers – Universal Pokemon Randomizer, UPR ZX, and pk3ds – Github Links and Intros

August 23, 2021

Pokemon Randomizers

Resources


Title:
Universal Pokemon Randomizer

By:
Dabomstew


Github – Repo #1

Description:
Original top level pokemon randomizer for generations 1 through 5.


Title:
Universal Pokemon Randomizer ZX

By:
Ajarmar


Github – Repo #2

Description:
Extended work from original universal pokemon randomizer that works for all original handheld generations 1 through 7.


Title:
pk3ds

By:
kwsch


Github – Repo #3

Description:
Randomizer for the 3DS pokemon games, which were generations 6 and 7.


Overview

I have always been a huge fan of pokemon randomizers for their ability to add replayability to games I am always looking to replay with different variations. I wanted to collect and archive some of the best current tools for doing so in one place to make them easy to reference, especially through their github repos since I am interested in seeing if I can make any contributions to add fun and exciting randomization settings.

Universal Pokemon Randomizer

This is the original randomizer tool that became the main standard for randomizing most pokemon games. It has a lot of very nice options and has just existed for a while now so its errors are not found as often and are generally more minor.

This however is limited in that in only works for pokemon generations 1 – 5, leaving a few on the 3DS unable to be randomized. The project was put on stop and archived as good enough, but it was recently continued with the ZX version to add further generation support. I will also note it is written almost exclusively in Java.

Universal Pokemon Randomizer ZX

This is an extension of the original Universal Pokemon Randomizer that adds support for generations 6 and 7 on top of the original 1 – 5. This appears to be relatively active and growing (with changes submitted between 2 months and 4 hours ago as of the time of collecting this information).

With how great the original tool is, I would love to explore this option with the newer generations. I have not personally tested this randomizer tool since it is on the newer side, but I’ve seen some cases for it working which look promising. As a continuation of the original Universal Pokemon Randomizer, this is also written primarily in Java.

pk3ds

This tool was created to fill the gap the original Universal Pokemon Randomizer had, as this tool was made specifically to randomize the 3DS pokemon games which cover generations 6 and 7.

With less background and time to mature, I remember it having a bit less flexibility in its options compared to the Universal Pokemon Randomizer, but it worked well for the primary need of shuffling the major content of the game. Being its own separate tool, this one is actually written majorly in C#, which fits directly in my wheel house.

via Blogger http://stevelilleyschool.blogspot.com/2021/08/pokemon-randomizers-universal-pokemon.html

Modding Monster Train – Patch to Allow for Adding Custom Units with Custom Syntheses

July 28, 2021

Modding with Harmony

Monster Train


Title:
My Monster Train Custom Unit Synthesis Patch

By:
Steven lilley (Me)


Github – Link

Description:
Link to my github page leading to my patch for adding custom units and syntheses to Monster Train.


Overview

Monster Train modding suffered from a lot of issues when the latest large DLC was made, the Last Divinity. Trainworks, a modding API created to help with modding Monster Train, was created before this DLC, and its addition broke many of its features.

One of the large additions from the DLC itself was unit synthesis, which let you break a character down into a unique upgrade for another character. This however was impossible to implement with Trainworks since it encountered new bugs because of unforeseen interactions with the new content. Originally, it would not even run if using new content, but a few updates to Trainworks at least made using them possible. However, unit syntheses continued to be an issue that did not work.

The existing custom units did not have any essences as they were created before the DLC. Trying to synthesize them into another unit would then create a softlock in the game. Being such a crucial aspect of the game, I investigated the error in an effort to remove it either on my own or through additional information to improve future iterations of Trainworks.

Issue and Monster Train’s UnitSynthesisMapping Class

CollectMappingData Method

The new DLC added a class within Monster Train’s base code named UnitSynthesisMapping which is responsible for creating the list of unit syntheses and holding that data for use in game. It has a method named CollectMappingData, which searches through the AllGameData for a list of all the CharacterData and all the CardUpgradeData in the game. It creates an initial list from every CharacterData in the game (to make sure every character has one, and only one upgrade).

While this appeared to be a perfect point to approach this issue, it was a bit indirect. This class was contained within a Unity Scriptable object, and most of the properties and methods contained within were private and very encapsulated. My best guess is that this Scriptable Object exists for the Monster Train team within the Unity Inspector and they actually call the CollectMappingData method with a button in the Inspector. This is because that method specifically has a Unity attribute called Context Menu which lets it be called from within the Inspector, outside of running the game. This threw me off for a while since I was trying to track when this method was called, and it turns out it never gets called naturally during the running of the game.

CollectMappingData Code from Monster Train

[ContextMenu("Collect Mapping Data")]
	private void CollectMappingData()
	{
		this.editorMappings.Clear();
		foreach (CardData cardData in this.allGameData.GetAllCardData())
		{
			if (!cardData.IsArchived && cardData.IsSpawnerCard() && !cardData.GetSpawnCharacterData().IsChampion())
			{
				this.editorMappings.Add(new UnitSynthesisMapping.MappingEntry(cardData.GetSpawnCharacterData(), null));
			}
		}
		this.editorMappings.Sort((UnitSynthesisMapping.MappingEntry a, UnitSynthesisMapping.MappingEntry b) => a.character.name.CompareTo(b.character.name));
		foreach (CardUpgradeData cardUpgradeData in this.allGameData.GetAllCardUpgradeData())
		{
			if (cardUpgradeData.IsUnitSynthesisUpgrade())
			{
				foreach (UnitSynthesisMapping.MappingEntry mappingEntry in this.editorMappings)
				{
					if (mappingEntry.character == cardUpgradeData.GetSourceSynthesisUnit())
					{
						mappingEntry.cardUpgrade = cardUpgradeData;
					}
				}
				cardUpgradeData.InternalSetLinkedPactDuplicateRarity(CollectableRarity.Rare);
			}
		}
	}

Calling Private Methods with Harmony and Reverse Patch

I covered this in a previous blog post, but it did turn out that this method worked well for accessing a calling a private method from within the base game’s code when I wanted to. Since I just wanted to forcibly call that private method, CollectMappingData, I did not need any other extra logic to run at the time. This makes the code for it very tight as follows:

[HarmonyPatch(typeof(UnitSynthesisMapping), "CollectMappingData", new Type[] { })]
    public class RecallingCollectMappingData
    {
        [HarmonyReversePatch]
        public static void MyTest(object instance)
        {
            // It's a stub so it has no initial content
        }
    }

The hardest part of implementing this was that it would work easiest with the single existing instance of the UnitSynthesisMapping class during the runtime of the game. We could instantiate our own instance to supply to this method, but without the other data it did not work at all. I was finally able to find a route to getting a reference to the actual existing instance through Trainworks actually.

Trainworks has a way to access the AllGameData class instance in the game, which gave me an entry point into a lot of data in the game. Through this, I was able to access the BalanceData class, which then gave me direct access to the existing UnitSynthesisMapping instance. This could then be passed into my Reverse Patch to call the method just as if it had been called from within that specific instance, which has all the access to the game’s data and existing lists if needed. This example path is shown below:

public static void FindUnitSynthesisMappingInstanceToStub()
        {
            // Gets a reference to AllGameData with Trainworks
            AllGameData testData = ProviderManager.SaveManager.GetAllGameData();
            CustomUnitSynthesisPatcher.Log("Got reference to AllGameData: " + testData.name);

            // Use AllGameData to get access to BalanceData
            BalanceData balanceData = testData.GetBalanceData();
            CustomUnitSynthesisPatcher.Log("Got reference to BalanceData: " + balanceData.name);

            // Use BalanceData to get access to the current instance of the UnitSynthesisMapping
            UnitSynthesisMapping mappingInstance = balanceData.SynthesisMapping;
            if (mappingInstance == null)
            {
                CustomUnitSynthesisPatcher.Log("Failed to find a mapping instance.");
            }
            else
            {
                CustomUnitSynthesisPatcher.Log("Able to find mapping instance: " + mappingInstance.GetID()); // Test to see if this is a different instance
            }

            // Calls CollectMappingData method
            RecallingCollectMappingData.MyTest(mappingInstance);
            CustomUnitSynthesisPatcher.Log("Called CollectMappingData.");
        }

Key String Tests for this Approach: CardEffectData and CardUpgradeData

Renaming the EffectStateName of the CardEffectData to Override Trainworks Default

It turns out two of the main hiccups I ran into with this approach were simply because of string checks not finding exactly what they wanted (or accidentally finding too many strings that were exactly the same). The first issue was in the CollectMappingData method’s first foreach loop, which includes in its if statement that cardData.IsSpawnerCard(). Somewhere along the lines it checks that the card has a CardEffectData specifically named “CardEffectSpawnMonster”. This was not being picked up initially however because Trainworks sets that property of your card to a very long string that also included “CardEffectSpawnMonster”, but all that information kept that part of the check from returning true. So simply making sure to rename that specific CardEffectData object’s EffectStateName to “CardEffectSpawnMonster” after setting EffectStateType (since this includes methods for creating its own EffectStateName) gets through this issue.

Setting the UpgradeTitleKey of your Synthesis CardUpgradeData to Override Trainworks Default

It just so happens that Trainworks default string options for some of these do not match the exact needs of this new Monster Train class (as they were made before these existed), and this was another case of that (although it made more sense). In the synthesis you build, if an UpgradeTitleKey is not set, a fairly empty default one is made for your CardUpgradeData. If you do not set this for several CardUpgradeData objects you make, then they will all have this SAME default UpgradeTitleKey. As a duplication check however, when adding a new CardUpgradeData to the overall list in the game, Trainworks checks by UpgradeTitleKey whether that name already exists or not, and if it does, it removes it from the list and adds the new one. As one would expect, if you have a bunch of objects with the same UpgradeTitleKey then, they all continually get replaced until only the last one remains (as it never gets replaced).

This is easy enough to solve by simply making sure to set a unique UpgradeTitleKey for each of your unit synthesis CardUpgardeDataBuilder objects. This is all that is required to make sure they do not keep deleting themselves in the current iteration of Trainworks (and is just a good practice in general).

via Blogger http://stevelilleyschool.blogspot.com/2021/07/modding-monster-train-patch-to-allow.html

Modding Monster Train – Harmony Reverse Patches

July 14, 2021

Modding with Harmony

Monster Train


Title:
Harmony ParDeike – Reverse Patch

By:
Harmony


Harmony – Documentation

Description:
Harmony documentation on patching with the Reverse Patch.


Overview

I was investigating ways to use Harmony to access private methods and fields from classes within the Unity target game itself, Monster Train in this case. One of the first direct options I came across was using reverse patches.

Basics of the Reverse Patch

“A reverse patch is a stub method in your own code that ‘becomes’ the original or part of the original method so you can use that yourself.”

    Typical use cases:

  • easily call private methods
  • no reflection, no delegate, native performance
  • you can cherry-pick part of the original method by using a transpiler
  • can give you the unmodified original
  • will freeze the implementation to the time you reverse patch

Implementation

After seeing its first use case being calling private methods, I thought this would be a perfect tool to use. As far as I have found, it appears to need a reference to an instance of the object that the private method belongs to. This has proved rather troublesome. While the setting up of the reverse patch has appeared to work and run, it is running into other issues that require further research.

The class I am trying to access is a Unity Scriptable Object, and has no easy way to directly access the existing instance (that I have found so far). I tried circumventing this by creating my own instance to work from, but that has its own issues. The new instance, if it is even being generated properly, is not intialized in any way and is missing integral references, such as to the AllGameData, which are also difficult to inject into the instance. This is again because it is a Unity Scriptable Object, so most fields are private but serialized, so they are very protected and only exposed in the Unity Inspector to make the necessary references I am missing in a newly generated instance.

via Blogger http://stevelilleyschool.blogspot.com/2021/07/modding-monster-train-harmony-reverse.html

Modding Monster Train – Using Harmony to Track Methods and Log

July 1, 2021

Modding with Harmony

Monster Train


Title:
Getting Started with Trainworks Modding Tools

By:
KittenAqua


Github – Wiki

Description:
More detailed and in-depth documentation on properly setting up a C# project for modding Monster Train as well as examples for modding various content.


Title:
Harmony Patching Article

By:
Harmony


Harmony – Article

Description:
General documentation on Harmony.


Overview

As of the writing of this blog, the state of the Trainworks modding API with Monster Train after its DLC release is a bit rough. Creating custom cards was a core feature of Trainworks, but the DLC update has broken a lot of the original system. I have been investigating these issues to see if I can identify the problem(s) and potentially fix them. This has led me to learning a lot about using Harmony, a tool for modding Unity games, especially in a way to create logs to help me track bugs and problems when integrated with BepInEx.

Harmony Basics with Trainworks

The ‘Getting Started with Trainworks Modding Tools’ link above was very helpful in covering the basics of using Harmony with Monster Train. The most common methods used with these patches are ‘Postfix’ and ‘Prefix’.

Each patch is its own class, which contains methods performing the patching. It must also use attributes supplied by the Harmony API to indicate that it is a Harmony patch to be picked up when running the mod. This attributes for these cases generally contain information such as: the method name, the parameters of the method (if an overloaded method), and the class name containing said method.

Prefix and Postfix Arguments

The list of arguments covered by the ‘Getting Started…’ link above is:

  • Any arguments the original method took, with the same type and name
  • The instance of the class the method has been called on: ClassName __instance (two underscores)
  • Any fields of the instance, whether public or private. They should have the same name and type as the original, but with three underscores before: FieldType ___fieldName
  • The return value of the method: __result (two underscores)
  • A couple other things that aren’t used as often. Reference the official Harmony documentation for details.

The ‘ref’ keyword is used with the Postfix and Prefix methods often. This is because it is common that you will want to take in a variable from the existing methods and actively change the value of said argument.

Postfix

Postfix patches are used to perform actions after a method in the established project (Monster Train in this case) is called. This means it also has access to the result of said method to read from it or even alter it.

Prefix

Opposite of the Postfix, Prefix methods are those which are executed before the original method. As such, they are uniquely situated to prevent the original method from even executing if wanted (although this is something that is advised to do with care as it can easily break systems relying on it running).

From the official Harmony documentation, these are commonly used to:

  • access and edit the arguments of the original method
  • set the result of the original method
  • skip the original method and prefixes that alter its input/result
  • set custom state that can be recalled in the postfix

Example Logging Monster Train CardUpgradeData Setup Method

While investigating an issue with unit synthesis in the game, I encountered an error that led me to explore the Setup method in the CardUpgradeData class of Monster Train, as this appears to be a core part of the synthesis process. I decided to setup a Postfix method to return some information from this method when it was run (as well as to just help determine when this method was being run).

In this investigation I found that selecting existing units (which clearly have all the assets necessary) in the unit synthesis temple would call this method, along with a specialized unit synthesis upgrade. However, selecting a custom unit did not call this method at all. While this normally would make sense obviously since they do not have a unit synthesis upgrade made for them, the Trainworks tool is supposed to make a replacement synthesis upgrade in this case, which I thought may be picked up by this system.

Class for Logging

[HarmonyPatch(typeof(CardUpgradeState))]
    [HarmonyPatch("Setup")]
    [HarmonyPatch(new Type[] { typeof(CardUpgradeData), typeof(bool)})]
    public static class LogSynthesis
    {
        private static void Postfix(ref CardUpgradeState __instance)
        {
            //string cardUpgradeDataId = __instance.GetCardUpgradeDataId();
            string upgradeDescriptionKey = __instance.GetUpgradeDescriptionKey();
            string assetName = __instance.GetAssetName();

            if(upgradeDescriptionKey == null || assetName == null)
            {
                NyanCat.LogSomething("LogSynthesis hit a null string");
            }
            else
            {
                NyanCat.LogSomething(
                    "CardUpgradeState Setup method detected with data: \n" +
                    $"\tUpgrade Description Key: \n" +
                    $"\t\t{upgradeDescriptionKey}\n" +
                    $"\tAsset Name: \n" +
                    $"\t\t{assetName}");
            }
        }
    }

Fig. 1: Screen with Game, my Patch, and Log Console Output Example

via Blogger http://stevelilleyschool.blogspot.com/2021/07/modding-monster-train-using-harmony-to.html

Modding Monster Train – Basics and Introduction

June 21, 2021

Modding

Monster Train


Title:
Monster Train – Shiny Mark mades mods

By:
Good Shepherd Entertainment


Youtube – Tutorial #1

Description:
Extremely basic introduction to setting up a C# project for modding Monster Train (slightly outdated).


Title:
Monster Train Modding Card Creation Tutorial Mod

By:
Good Shepherd Entertainment


Youtube – Tutorial #2

Description:
Introduction to using the Trainworks API, a modding tool for Monster Train geared towards adding new content.


Title:
Getting Started with Trainworks Modding Tools

By:
KittenAqua


Github – Wiki

Description:
More detailed and indepth documentation on properly setting up a C# project for modding Monster Train as well as examples for modding various content.


Overview

Monster Train is a game I have played a lot and really enjoyed, and it recently put out some modding support in the last year. Being a Unity-based game, I figured I could use some of my Unity and C# knowledge to take a crack at modding in some content of my own to play around with and possibly add to the community.

It is not the most straight forward process, and there was recently a large DLC added to the game that makes parts of the old modding tutorials different or completely obsolete, but I still think there enough to sift through to make a base for starting a mod going. I think there is also a decently strong community that would appreciate some mod content since it is rather lacking, so it could also just be a nice place to implement some game design practices and create some new stuff for others to play with.

via Blogger http://stevelilleyschool.blogspot.com/2021/06/modding-monster-train-basics-and.html

Accessing Variables in Other Scripts in Unity

May 26, 2021

Accessing Variables

Unity


Title:
How To Access Variables From Another Script In Unity

By:
Infallible Code


Youtube – Tutorial #1

Description:
Goes over many programming techniques within Unity to access variables in one script from another.


Key Concepts

Scripts

C# classes that inherit from MonoBehaviour.

Collaborators

Scripts that require a reference to at least one other script in order to function.

Dependencies

Objects that collaborators need in order to function.

Categories of Resolution

Where the burden of dependency resolution lies.

External Resolution

The responsibility of dependency resolution falls somewhere else.

Public Properties

Expose dependency of a public field or property


Simple and requires no overhead, but can easily make bugs that are also hard to debug. This is because it can be unclear where the dependencies lie.

Editor Properties

Expose serializable dependencies in the Unity editor.
Use SerializeField on attribute private properties.


Similar to public properties, but are much safer.

Unity Event Methods

Functions that are called when specific events occur at runtime.


This specific example covers methods like OnCollisionEnter(). These are functions that are called when specific events occur, and they help resolve dependencies when those events are triggered.

Internal Resolution

The script itself is responsible for resolving its own dependencies.

FindObjectByType() Method

Search for dependencies in the scene based on their type.
FindObjectsByType() returns a list of matching objects.

This can be tricky to use sometimes since you may not be sure of the state of your scene when this is called. It is also relatively expensive. Most consistent to use to find manager scripts, since they should confidently be active in the scene when called.

Static Instances

Expose dependency on a public static property.
Commonly used in the Singleton pattern.

Dependency Injections

A pattern for automating dependency resolution based on a configuration.

It allows you to configure all your dependencies in one place and have them resolve automatically based on that configuration.

Summary

I have used all these techniques before, but it is always good to find condensed lists of them all together to really help organize them. Determining the cleanest and safest way to pass around variables through scripts is something I work on a lot, so laying out these options to go through in an effort to determine the best approach is always helpful. This also led me to some other resources on the topic that I am looking forward to investigating.

via Blogger http://stevelilleyschool.blogspot.com/2021/05/accessing-variables-in-other-scripts-in.html

Singletons in Unity and Many of Their Downfalls by Infallible Code

May 26, 2021

Singletons

Unity


Title:
Everything You Need to Know About Singletons in Unity

By:
Infallible Code


Youtube – Tutorial #1

Description:
Goes over the true properties of singletons in code and why they should be used sparingly in Unity game development.


Intro to Singletons

Singleton Pattern: class that is both globally accessible and can only be instantiated once

Common Example:

Audio Manager – because it is something many scripts will want access to, and you specifically want only one in your game at a time

Singleton Requirement #1: Global Access Specification

Singletons sound promising because they are an easy solution to “how do you access other scripts” or “how do you fill your class dependencies”. Using Singletons for access can hide the fact that a class depends on another, which can lead to difficult to track bugs and difficulties. This may lead to using the singleton even more than necessary because of ease of access, which creates more and more hidden dependencies.

Example #1

Non-Singleton Version

public class GameManager : MonoBehaviour
{
	[SerializeField] private Player player;
	
	privat void OnPlayerDied()
	{
		print($"Final Score: {player.Score}");
	}
}

Singleton Version

public class GameManager : MonoBehaviour
{
	privat void OnPlayerDied()
	{
		print($"Final Score: {Player.Instance.Score}");
	}
}

Because the Singleton pattern accesses a specific class directly, additions of derived classes will require more updates that may also be hard to find because of the hidden dependencies. Again, doing something similar to the Non-Singleton version of Example #1 makes it so that an additionally created derived class could fill the Player dependency with little to no edits.

Singleton Requirement #2: Restricted to a Single Instance

This is generally accomplised by creating a private constructor for the class, and then instantiating a single instance from the getter. This is already difficult in Unity as many classes you create in Unity will be MonoBehaviours, which cannot be instantiated in code with the new keyword. This is because Unity hides their constructor and allows anything to instantiate them through object.Instantiate().

So to help satisfy this, you need an extra check if another instance of the class already exists in the scene in case it was added directly in the Editor or through other object.Instantiate() calls. Once these are located, they may also require being destroyed to make sure there only exists one instance at any given time. This can also lead to strange results, as sometimes destruction prevention is used in Unity, such as with the DontDestroyOnLoad() method. Sometimes you may also get rid of and keep the “wrong” singleton if multiple somehow run into each other.

Singleton Criteria:

1) Absolutely required to exist only once
2) Must be accessible from every part of your code
3) Controls concurrent access to a resource

Summary

While I already knew that singletons should be used sparingly, having a better understanding of them fundamentally really helps put that into perspective just how sparingly they should be used. It also helps confirm that it can generally be a bad way of passing around data in Unity, and with a need that comes up so often and has so many options to be accomplished, it is good to generally remove an option from that list. It is also important to acknnowledge that in Unity specifically they are extra difficult to implement when it involves MonoBehaviours, as this again helps narrow down the process of passing around data.

via Blogger http://stevelilleyschool.blogspot.com/2021/05/singletons-in-unity-and-many-of-their.html

Game Project: Flying Game – Part 3 – Camera Controller and Zoom Control

May 25, 2021

Flying Game

Camera Controller


Overview

After getting the basics of the camera controller down, I wanted to explore ways to make it feel more immersive. One of the ways I’ve seen racing or other fast moving games handle this was to have a zoom that mimicked the acceleration to make that feel all the more impactful. Following this, I wanted to look into connecting the player’s speed with the amount the camera zooms in on the player.

Setting the Camera Zoom Suggested by Player Speed

Since the game has a speed boost option, I wanted the impact of the speed boost to be larger than any normal speed the player can reach. With this in mind, I was looking at an approach where normally their max speed gets the camera up to around 80% of the maximum zoom, and activating a boost is the only way to hit the 100% zoom mark (creating a significant difference in zoom specifically when boosting).

One immediate issue with this approach is that any zoom that is directly tied to player speed has a jarring jump in zoom position when the player boosts, since in a single frame it jumps from rather low zoom (because charing boost goes with slowing down) to maximum zoom. So while this was a decent initial approach, I knew I would want a smoother transition system to especially deal with this common extreme case.

The approach I thought of was that the camera zoom system would read the player’s speed and suggest a position for the camera to move to. If that new position is substantially far away from the current position, it would only move a portion of the way there instead of directly there. This would help me spread out extremely large zoom changes over several frames, similar to how a Lerp function may handle this.

This ended up working rather well, and can be seen in action in the video I have linked. Another hopeful benefit of this system was that I was hoping I could remove the small zoom jitter when the player’s speed is not changing any appreciable amount. This is an effect that happens often when the player is consistently maintaing the normal top speed. I was hoping to use the opposite distance check of not changing the zoom at all at very small distance changes, but as can be seen in the video as well this does not particularly fix it currently.

Camera Controller – Zoom Focus from Steve Lilley on Vimeo.

Video: Zoom Based on Speed System for Camera Controller

via Blogger http://stevelilleyschool.blogspot.com/2021/05/game-project-flying-game-part-3-camera.html

Coroutine Fundamentals in Unity

May 20, 2021

Coroutines

Unity


Title:
Introduction to Game Development (E21: coroutines)

By:
Sebastian Lague


Youtube – Tutorial #1

Description:
Covers the basics of coroutines in Unity.


Title:
Coroutines In Unity – What Are Coroutines And How To Use Them – Coroutines Unity Tutorial

By:
Awesome Tuts


Youtube – Tutorial #2

Description:
Covers some intermediate complexity uses of coroutines as well as tying them in with Invoke calls.


Title:
Coroutines – Unity Official Tutorials

By:
Unity


Youtube – Tutorial #3

Description:
Old official Unity tutorial on the fundamentals of coroutines in their system.


Overview

Coroutines appear to be a strong tool within Unity that I still am looking for ways to implement better in my systems. While understanding the basic uses for them with timing related events, I feel like they offer a lot more that I am not utilizing, so I wanted to further my understanding of them by following up on one of my older tutorial blogs and going through the tutorials it offers.

Tutorial #1 Notes

Coroutines are part of the Unity engine, and are not native to C#. They also belong to the Monobehaviour base class, so they class they are used in must inherit from Monobehaviour.

The coroutine return type is IEnumerator. Because of this, coroutines can also be stored in IEnumerator variables. This also allows them to be passed in as a paramter for the yield return statement within a Coroutine.

Use a reference to properly control the starting and stopping of a specific coroutine instance.

Tutorial #2 Notes

You can call a Coroutine from within itself (the same Coroutine). This is one way to cycle a Coroutine continously.

WaitForSecondsRealtime is similar to WaitForSeconds, except that WaitForSecondsRealtime is independent of Unity’s timescale. WaitForSeconds however is affected by the timescale. WaitUntil suspends the coroutine until the given delegate returns true.

Invoke Notes

  • Invoke – call a method after a given amount of time
  • InvokeRepeating – call a method every given amount of time, after a starting given time
  • CancleInvoke – stops an InvokeRepeating call

Tutorial #3 Notes

Coroutines are functions which execute in intervals. They work with special yield statements which return the code execution out of the function. Then, when the function continues, execution begins from where it left off.

Combining coroutines with properties can allow for efficient code which produces execution only when a value is changed, without the need to check for variable changes every frame with the Update() method or something similar.

Coroutine Return Statements

A substantial part of using and controlling coroutines is the various return statements you can use within them to determine when they should run and when they should pause execution. As such I listed several of these options and how they work within the coroutines.

yield return null

waits a frame before progressing to the next step

seems similar to the rate of the Update() method

yield return new WaitForSeconds(float time)

waits the given amount of seconds between each run of the process

yield return WaitForSecondsRealtime(float time)

waits the given amount of seconds, regardless of Unity’s timescale

yield return StartCoroutine(DoSomething())

waits until the given Coroutine “DoSomething” is finished running

yield return new WaitUntil(bool test)

suspends coroutine until the given parameter is true (opposite of WaitWhile)

yield return new WaitWhile(bool test)

suspends coroutine until the given paramter is false (opposite of WaitUntil)

Summary

Coroutines are starting to become a bit more clear to me, and this helped me learn a few more ways to incorporate them into my projects. Just seeing that they are specifically a Unity native concept was helpful to me as I did not think about that before, and that makes their somewhat strange nature make more sense in my head. This made it more clear that Unity is doing a lot of work behind the scenes to accomplish their main goals of determining when to start, pause, resume execution based on their yield values. Also the Unity note on tying them in with properties is very interesting and something I want to experiment with more myself for more efficient code.

via Blogger http://stevelilleyschool.blogspot.com/2021/05/coroutine-fundamentals-in-unity.html