UnityLearn – Beginner Programming – Working with Classes – Pt. 03

October 22, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Working with Classes

The Four Pillars of OOP
  • 1. Encapsulation: grouping of data and methods into a cohesive object
  • 2. Abstraction: process of exposing only those features of an object necessary for interactions
  • 3. Inheritance: creating a new class based on and extending another
  • 4. Polymorphism: ability of an object of function to take on a different form

The PlayerController class created for this section of the tutorials dervied from the Shape class, which allowed it to inherit the SetColor method and change the player to yellow. It also extended the class by creating its own method, MovePlayer. I am trying to keep track of this to ensure I keep all the terminology straight.

There was an interesting approach to using WaitForSeconds in the enemy spawning method. Instead of directly using new WaitForSeconds directly in the yield return statement of the coroutine, they actually created a WaitForSeconds variable reference named wait. They then just used wait in the yield return statement in place of all the WaitForSeconds syntax. This is nice to keep in mind as another way to organize coroutines, especially those that use similar values for multiple yield statements.

Inheritance and Polymorphism

Inheritance was demonstrated by creating protected variables within the base class that could be used by all of the derived classes. The examples here were halfHeight and halfWidth, which assumed the values of the bounds.extents of the SpriteRenderer at Start. This was done in the Start method of the base class, so the derived classes simply had to call base.Start() to have those values individually set for all of them inheriting from Shape class.

It is important to note for this to work they made the Start method in the base class a virtual protected method. This allowed the derived classes to override the Start method to add functionality, while also using the base.Start() method still to assume the base class’s Start method functionality. This started to get into polymorphism.

virtual: this keyword can be used to modify a method, property, indexer, or event declaration and allow for it to be overridden in a derived class

IMPORTANT: This can be used in conjunction with the protected access modifier to allow for a base class’s Start method to be useable within the Start method of derived classes. By creating a protected virtual void Start method in the base class, the derived classes can have their own modified Start methods by using a protected override void Start method and calling the base.Start() method from within.

Unity Procedural Landmass Generation by Sebastian Lague

October 21, 2019

Procedural Terrain Generation

Tutorial

Procedural Landmass Generation (E01: Introduction)

Tutorial #1 – Link

By: Sebastian Lague


This is the beginning of a procedural landmass tutorial by Sebastian Lague. This appears to get move involved in setting up noise with more controlled variability along with shading and other interesting tricks. This will compliment a procedural terrain tutorial I followed by Brackeys since this gets much more involved and gives many more designer options.

Unity Card Game Tutorials – Drag and Drop

October 15, 2019

Unity Card Game Tutorials

Drag and Drop

Youtube – Unity Tutorial – Drag & Drop Tutorial #1 [RPGs, Card Games, uGUI]

Tutorial #1 – Link

By: quill18creates


I was interested at looking into how card games are created in Unity as I was interested in possibly making that type of game and also thought it would be something good to look into for my current research on learning how to effectively create my own class systems that utilize interfaces and inheritance well. Cards are a very obvious type of object that will use a similar setup every single time, so they are a nice simple base to focus on creating a nice class that contains everything you should need for all of your cards. Interfaces could then be a good way to give these elements their interactive components (such as dragging them around or sending them to their proper locations during gameplay).

This video is actually the first in a series of three. They mostly cover interactivity in a card game, with a focus on dragging and dropping, with some visual clarity effects (like moving the cards around for you). There is still some coverage on the general “card” class as well.

HFFWS Fixing and Debugging the Pulley Generator

October 14, 2019

Pulley System Generator

Fixing

Notes:

– Deselecting “Fix Start” and “Fix End” on the Rope component allows for the creation of a simple rope that appears to operate normally, but it is not connected to anything
– “Fix Start” and “Fix End” appear necessary to connect the rope to physics objects

TESTING

Issue 1:

Instantiated Rope is a completely broken mess of physics masses jumping around.

Test 1:

Changing objects at ends to TestBox and TestSphere just to simplify the physics objects involved to see if that makes diagnosing the problems easier

Results:

This did result in a more stable rope. It was still not in the correct location, but the rope was no longer just a jumbled bumbling mess of masses.

Issue 2:

Rope is instantiated in a strange position away from most other objects.

– This appears to be because of the ropeStart and ropeEnd positions. The rope is still following their positions, and they are far away.
– The ropeStart and ropeEnd positions however do have the same Y and Z values, suggesting that they are following the logic to some degree, but not as intended.

Test 1:

Change instantiation process so that pulley end objects are instantiated as children of the overall pulley prefab.

– The idea is that this will help with relative positioning
– I am not sure if I can use this method with a prefab that has not been instantiated yet (seems strange using the transform of an object that does not exist yet)

Results:

– This did properly situate the rope end physics objects at the ends of the rope, however the rope was still spawning in a location that was unintended, below everything else.
– The rope also does not appear to actually be bound by the end objects either.

Test 2:

Debug.Log all the positions to see if the positions occurring at run time are matching those in the script.

Results:

– The script dictates that the start physics object is positioned at (-9.5, 0, 0) – (1.0, 0, 0), so (-10.5, 0, 0) relative to its starting position, and the end physics object is the reversse (+9.5 and +1.0). This indicates that there should be 21 units between them. This did happen, but the values are very different looking.
– The start instantiated at position: (-2.5. -14, 35.5)
– The end instantiated at position: (18.5, -14, 35.5)
– This indicates that the parent position they are referencing is: (8.0, -14, 35.5)
– Both RopeStart and RopeEnd child objects’ positions matchup with the weird physics object positions as well, so it’s before them.

SOLVED:

Turns out the overall prefab had the exact transform position (8.0, -14, 35.5) so those values were getting passed into everything as the starting parent transform value

Issue 3:

Rope is not actually connected to the physics objects at the ends.

– This is accomplished by setting the connectedBody reference of the RopeStart and RopeEnd objects to the rigid body components of the start physics object and end phsyics object respectively.
– This is happening as seen in the references at run time, which makes me expect that this connection is still somehow ‘ being made before the instantiation of the full pulley setup.

Test 1:

Run everything in Awake instead of Start

– I thought running everything sooner may help set the connection up earlier.

Results:

– There were no noticeable differences.

NEXT STEPS

Figure out how to actually connect the objects to the ends of the rope.

HFFWS Rope Pulley System Issues and Strange Audio Instantiation

October 9, 2019

HFFWS Thesis Project

Creating Pulley System

Creating Pulley in HFFWS

Working with the Rope Script

The Rope script is the central focus for the pulley system, but the Fixed Joint that goes with the rigid body of the ropeStart and ropeEnd also play a crucial role which makes getting everything to work together nicely a bit of a challenge. I want the setup to be able to take in a prefab for each end of the pulley and instantiate those objects in the proper position, and then become connectedBody for each of these Fixed Joints. Similar to other issues I have had before with instantiating objects, there seems to be a critical timing factor needed on top of the fact that the objects just physically behave very weirdly when created.

I think currently there is some collision issue that is occurring because the rope ends up spawning very far away from where it is intended to spawn and also waves around violently. Meanwhile, the objects that are supposed to be at the rope ends are spawning in the correct position. The demolition ball just falls to the ground while the hinged platform moves back and forth some.

The connectedBody reference on the Fixed Joints is something that needs to be done on initialization to work properly, so I have been trying to instantiate the rope end objects first, then setting the prefab’s Fixed Joint connectedBody’s to these objects’ rigidbody’s, and then instantiating that prefab, but this is clearly leading to some significant errors. I will have to try some other approaches where I change the order of events to see if I can get more desireable results, or just understand where the errors are occurring more accurately.

Weird Audio Issue

Weird Siege(Clone) Issue There was a Siege(Clone) object being instantiated at game start. This had lots of audio files. I tracked this to the Level gameObject which has a SoundManager component. This had a Stored State of Siege which I removed. This stopped Siege from instantiating anymore. I am not sure how this got set in the first place (may happen when loading other scenes or checking the prefab scenes). The SoundLibrary component also has a Siege reference in its path: Path = 6Siege. However this does not appear to be doing anything currently.

UnityLearn – Beginner Programming – Pt. 02

October 8, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Understanding Types

What is a Type

Overview: Types of types, “var” keyword, enumerations, generics
Data Type: data type or simply type is an attribute of data which tells the compiler or interpreter how programmer intends to use the data.

Types of types: Value types and Reference types
  • Value Types: Memory is allocated directly and inline on the stack
  • Examples: int, byte
  • Reference Types: Memory is allocated randomly on the managed heap
  • Examples: Classes, delegates, interfaces
Memory Allocation by Type
  • There are two main memory storage locations we are concerned with, the stack and the heap.
  • Value types and pointers to reference types are located in stack (pointers not directly used in C#)
  • Reference types are located in heap
Stack vs. Heap
  • Stack
    • Allocated when compiled
    • Data are stored sequentially
    • Variable size must be known
    • Not subject to garbage collection
    • Very fast
  • Heap
    • Allocated during runtime
    • Data are stored randomly
    • Variable size can be unknown
    • Subject to garbage collection
    • Slower

The var Keyword and Anonymous Type

The var keyword allows for implicit typing but must be used within method scope. C# can use the value of a var variable it is initialized to to determine what the type should be. This is implicit typing. This also means we cannot declare a var variable without intializing it. Type inference occurs at the compiler level, so it does not effect performance.

Anonymous Type
  • An unnamed container for properties
  • properties are read only
  • no type name available to source code
  • type of each property is inferred

Enumerations

Enumeration: value type that represents a set of related named integral constants; declared using enum keyword
The underlying type of each element of an enum is int. These default to having values of 0, 1, 2, etc. The underlying int values can be set for each element individually however if wanted.
Benefits of Enumerations:

  • value types
  • express and limit available options for a variable’s value
  • named values provide readability
  • leverage Intellisense

Generic Types

Generics allow you to defer type declaration until use.
Benefits of Generics:

  • flexibility
  • code reuse
  • performance
  • type safety

Generics are used with the syntax of , where T is a type parameter. An example is shown below:
private void Evaluate(T suppliedValue)
{
Debug.LogFormat(“the Type of {0} is {1}”, suppliedValue, typeof(T));
}

Generics can be used in similar scenarios where overloaded methods would make sense, but would clutter things because there are many different types you would like a similar method to be able to use. Since you are also using a single method for vaarious types, it will also be performing the same actions on those types, so overloaded methods may be better if you want different actions for different types.

Working with Groups of Types

Module Introduction and Setup

Overview: arrays, generic lists, dictionaries, Queues & Stacks

Arrays

There was nothing new here.

Generic Lists

Lists are part of the System.Collections.Generic namespace. Lists are a generic class that use the type T type parameter.

Dictionaries

Dictionary: in C#, it is a collection type defined in System.Collections.Generic namespace that can store any data types in the form of keys and values.
Similar to other type parameters, they are declared between angle brackets for dictionaries. In arrays and lists, the elements are referenced by an integer index. A dictionary allows you to use a different type of index to reference your collection elements.

Queues and Stacks

Queues and stacks are used for more transient data, as opposed to arrays, lists, and dictionaries which are for more persistent data. The main difference between queues and stacks is the order in which data is removed. Queue is FIFO (First in, First out), where stack is LIFO (Last in, First out).

Coroutines

Introducing Coroutines

Coroutine: In Unity, a function declared with a return type of IEnumerator that allows execution to be suspended and resumed using the yield keyword.
Coroutines allow execution to be suspended and resumed using the “yield” keyword. The execution will run until it hits this keyword, then suspend this operation based on the value returned. This lets Unity continue any other operations going on while retaining its place in the coroutine. The value returned determines how long execution will be suspended. For example, returning null simply suspends execution until the beginning of the next frame. This ability to suspend and resume coroutines is what makes them so useful. The fact that coroutines must be of the return type IEnumerator dictates what values it can return, which means it dictates what values can be used to determine the time its routine execution can be suspended.

Delaying Execution

Coroutine Wait Types:

  • WaitForSeconds(): waits using scaled time
  • WaitUntil(): suspends execution until supplied delgate evaluates to true
  • WaitWhile(): suspends execution until supplied delgate evaluates to false
  • WaitForEndOfFrame(): waits until the end of the current frame
  • WaitForFixedUpdate(): waits until the next fixed frame update
  • WaitForSecondsRealtime(): waits for some time, but uses unscaled time
Time.timeScale

This can be used for a slow motion effect, or even pausing the game in Unity. Setting this to 0 is a common technique for pausing a game. Since WaitForSeconds uses scaled time, this will also stop any coroutines using that as the return type. However, if the coroutine uses WaitForSecondsRealtime(), which uses unscaled time, it will continue to run even at a Time.timeScale of 0. This difference can be useful if you want certain UI elements to perform some action for example while your main game is paused.

IMPORTANT: Yield statement does not return out of the function, it simply suspends execution. When it resumes, it picks up right where it left off.

Running in Parallel

Coroutines can be used for parallel processing. Coroutines allow processes to be split across multiple frames. This can also be used to let them be run in parallel with other game logic to minimize impact on frame rate.

Module Summary

Stopping a Coroutine:

  • StopAllCoroutines(): stops all coroutines running on a MonoBehaviour
  • StopCoroutine(): stops a specific coroutine running on a MonoBehaviour

The way coroutines are stopped depends on how they were started. If started with a string, they must be ended with a string. If started with a reference to the routine, it must be stopped using that reference.

Coroutine Caveats

  • can result in unexpected behavior (it is very easy to accidentally run a coroutine more than once)
  • can make debugging more difficult, since one or more coroutines can be running at a time along with update
  • can be complex and difficult to manage

How to Spawn Objects anywhere in Unity3D from Unity3D College

October 7, 2019

Spawn Objects in Unity

Basics and Addressables

How to Spawn Objects anywhere in Unity3D (and a bit of addressables)

Youtube – Link

By: Unity3d College


I wanted to note this as a possible useful source since my thesis work will involve spawning a lot of objects, and in varied and rather precise positions. With how important of a focus it will be, I figure more sources on the generals of spawning objects would be good to know to give me as many tools to use as possible.

Pulley Wheel Positioning System – Weighted Ranges for Positioning

October 6, 2019

Pulley Wheel Positioning System

Video Demo of Wheel Positioning System

Demo – Link

By: me


General System Needs

My pulley system needed to create a rope with objects attached to it, as well as objects which act as the pulley wheels which will support the rope. The positioning of these wheel objects must be in line with the rope, but below it. Using the HFF system, the rope gets generated and then falls, draping itself over any objects below it.

Keeping those requirements in mind, I also wanted the system to be able to generate a varied amount of these pulley wheels and place them in varied positions below the generated rope. Having the option for multiple wheels became more important when I noticed that sometimes ropes with heavy objects attached can break through rigid body wheel support objects, but having more (therefore more support) can sometimes alleviate this issue.

So to summarize, I wanted a system that could choose a varied amount of positions along a randomly determined line, with extra rules dictating how far these positions should be from the ends of that line, and how much of a space buffer should be given to each position (so when an object is instantiated at one of these positions, it does not collide with another instantiated object).

Starting Off

To keep the system simpler for now, since the system needs to fall into place anyway, I was specifically randomly generating the rope on the xz plane at some height y. The wheel positions are then generated on basically the same line, but some distance below that line (to ensure the wheels are within the rope line, but below for the rope to fall on them).

Adaptive Range System

The difficult thing with this position range system is that choosing a position for an object removes a specific range around that position to account for the space needed to instantiate an object at that position. Because of this, I needed a system that could update the range options everytime a position was chosen. I came up with a system that could determine how to update the ranges everytime a position was chosen, and could also determine if a range needed split into multiple.

The first range is the easiest to determine. It is just the full range of values between the beginning and the start of the wheel positions, with a buffer at each end factoring in the value for distanceFromEnds that we want to give to keep objects from instantiating too close to the ends.

With this first range created, we choose a random position within this single range. After that is when the adaptive range system comes in. Depending on the position selected, there are several possible options to account for the options that have been removed from the range:

  • If the position is sufficiently far away from any other range bounds, a range is effectively split in two.
  • If the position is very close to both the min and max bounds of the range, that range is fully removed.
  • If the position is very close to either the min or the max bound of the range, that range can just be modified.

Weighting the Ranges

One issue with a system that creates multiple ranges is that these ranges can be different sizes. So if at some point in the process I am randomly selected a range first, to then select values from, each range will inherently have the same chance of being selected, regardless of its size. To remedy this, I used a weighting system.

Each range has a weight in the range (0, 1] (The minimum bound is exclusive, as nothing should ever have a weight of 0, but the maximum bound is inclusive since if there is only 1 range it is the only option) representing how much its individual range covers amongst the total coverage of all the ranges within a list of ranges. This is easiest to explain with an example:
We have 3 ranges: (0, 2] (2, 5] (5, 10]
The total range coverage is: (2 – 0) + (5 – 2) + (10 – 5) = 10
The individual weights for these ranges are then:
Range 1 (0, 2]: Weight = (2 – 0) / 10 = 0.2
Range 2 (2, 5]: Weight = (5 – 2) / 10 = 0.3
Range 3 (5, 10]: Weight = (10 – 5) / 10 = 0.5

So when we go to select a range, we randomly roll a number in the range of (0, 1] and use this to determine which range to pull a value from. This works by checking if the rolled value is less than or equal to the first range’s weight. If this is satisfied, that is the range to choose from. If not, it moves to check the next range’s weight. To fit this within this (0, 1] range, each time a range is not used, its weight is added to a pool to add to the next range’s weight. This helps move the weight check along the range of (0, 1] with many values significantly below 1.

To follow the previous example to show this in action:
Our randomly rolled value to select a range is: 0.75
General Formula:
Check range n: Is rolledValue <= (rangenWeight + weightCheckPool)? No, add rangenWeight to weightCheckPool
Check range 1: Is 0.75 <= (0.2 + 0)? No, add 0.2 to weight check pool and move to next range
Check range 2: Is 0.75 <= (0.3 + 0.2)? No, add 0.3 to weight check pool and move to next range
Check range 3: Is 0.75 <= (0.5 + 0.5)? Yes, use this range to select a value from

HFFWS Framework for Pulley Puzzle Generator

Pulley Puzzle

Thesis

– Basic setup
– Objects attached to both ends of a rope
– Rope hangs over something

– Setting Up First Basic Instance
– Ropes
– these are very touchy in HFF
– they usually use a setup where there is a start and end transform position, and several
rope segments get instantiated between these two points to produce a “soft body” object
– need to make sure neither of these points is deeply within another rigid body object,
or physical interacting object as this can lead to a constant state of colliding
– constant colliding causes the rope system to act erratically on its own
– Attaching a Physics object to both ends of a rope
– the original rope objects use a physics object at one end, and a fully fixed positional
object at the other end
– we need both ends to use a physics object
– This is done by:
– Making sure both objects are rigid body objects
– In the Rope script:
– Make sure to assign “Start Body” as one object, and “End Body” as the
other object
– Using “Copy_YellowMetalMechMediumCradledPlatform2” as other physics object
– this prefab initially has a hinge joint attached to it
– there is something about this that fixes the entire platform in place
– removing this allowed it to work as intended
– I dragged the reference of this main object in as the “Connected Body” for the Fixed Joint
component on the RopeStart gameObject in the pulley setup
– Issues
– I was using “Copy_YellowMetalMechSmallWinchSpool1” as the central wheel for the pulley setup, but sometimes the rope would break through it
– Appeared that such massive objects can cause the rope to clip through this sometimes
– I alleviated this by having the rope go over two winch spools (similar to the setup found
in the Demolition level)
– this also broke through one of them eventually while I was increasing the mass of the ball in Play mode to see if I could raise the platform

– Constructing “Create Pulley” script
– Needs:
– Rope
– Physics object at each end
– Total: 2
– Pulley wheel object
– Total: >= 1
– Parameters (words)
– Rope
– Length
– Start Point
– End point
– Physics objects attached
– Type of objects
– Mass
– Start point
– End point
– Wheels
– Quantity
– Position
– Important Relationships:
– Wheel(s) must start below the rope
– Must be on a similar plane, but lower on Y position
– End position must be “length” away from start position
– Length = End Position – Start Position
– This can be done with using Random.OnUnitSphere to pick a random
direction in 3D space

UnityLearn – Beginner Programming – Pt. 01

October 2, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Module Introduction

This has basic coding principles, like formatting code files, naming conventions, namespaces, and effectively using comments. These are all concepts I know about, but only have some experience with so seeing another view should be beneficial. One of their main principles is “Consistency trumps style”.

Formatting your Code Files

The main points for this section are: value of conventions, anatomy of a code file, and formatting guidelines and tips.

Conventions provide a consistent look, make your code more accessible, makes it easier to maintain and extend, and shows your understanding.

Starting basic script structure: At the top there are using directives for namespaces. This is followed by the definition of the class. This defaults to deriving from Monobehaviour, and creating methods for Start and Update.

Editing Visual Studio Code Formatting Preferences

You can change your code formatting preferences in Visual Studio on Windows by going to Tools -> Options. Here, you can find “Text Editor” and find a bunch of formatting options, some for general purposes and then others for specific languages. Under C# for example, it breaks down even further into “Code Style” and more formatting options. These can be used to change how Visual Studio does default spacing for you, bracket placement, etc.

What’s in a Name?

The values of conventions (again) are: provide consistent look to your code, makes your code more accessible, makes it easier to maintain and extend, and show your understanding.

The benefits of naming conventions are: tell us what something represents, tell us what something is, leverage capabilities of modern IDEs, and (although less common currently) can indicate scope.

As usual, they say make your names descriptive (without being too wordy) and be consistent.

C# Naming Conventions in Unity

The capitalization techniques are: Pascal Case and Camel Case. Pascal Case capitalizes the first letter of every word, and single word identifiers are capitalized. Camel Case starts lower case, and only capitalizes every word after the first. For single word identifiers, camel case leaves it lower case.

General Naming Guidelines:

  • use descriptive names
  • use easily readable names
  • avoid abbreviations
  • do not use underscores between words
  • be consistent

Use Pascal Case:

  • namespaces
  • classes
  • structs
  • methods

Use Camel Case:

  • fields
  • parameters (arguments)
  • local variables

There was an older convention to prefix field names with an underscore, an m, or both. This is not done as much anymore, but it is good to mention since you will come across it often. These were originally done to show a difference in scope between variables (i.e. local and non-local variables), but it has been deprecated with the advancement of modern IDEs. They will show you the scope of a variable just by hovering it.

Namespaces and Regions

Namespaces

These are spaces or containers for names. The benefits are modularity and avoiding naming collisions.

Modularity is accomplished by helping group types, classes, delegates, interfaces, etc. logically under a specific namespace. For example, the UnityEngine namespace contains a lot of commonly used tools such as Monobehaviour or Debug.

Fully qualifying the reference: This is when you precede something with its namespace to directly specify which reference you are using.

Including the namespaces used in the using directives at the top of your code file tells you and others that see it what types of objects they can expect to see in the file. Namespaces can even include other namespaces.

Naming Collisions

This occurs when the same name is used to reference different variables representing different things. Modern IDEs help minimize this issue, but it can still happen.

It is not mandatory to put your own code in namespaces, but it is strongly recommended to use in larger projects with project specific features to help keep your code organized and make it easier to maintain.

Regions

Regions are technically preprocessor directives, but they are used often in Unity for their visual purposes. They provide collapsable sections of code to help focus on what is necessary.

Regions are declared with the word region along with a “#” and the name of the region. Everything within the region is contained within a set of brackets. Finally, you end the region with the keyword “#endregion”.

FINAL NOTES

As expected, a lot of this is covering topics I already know, but it is good to confirm some of the practices I use and go over the terminology again. For example, I do still see a lot of underscores used for indicating scope of fields but it is not something I particularly use so it was nice to confirm that its less standard practice now with modern IDEs. These tutorials consistently use all the proper programming vernacular, which is a very nice plus over a lot of other learning sources I use that just want to get across the end game result as quickly as possible.