All posts in "C#"

SOLID Unity3D Code Architecture – The Open Closed Principal

Are you ready to start using the SOLID principals in your Unity3D Project?  Learn how the Open Closed Principal (the O in SOLID) can make your game easier to develop, extend, and maintain.  By using simple interfaces and separating out some code, we’ll convert a solution that’s destined for messiness into a clean and SOLID Unity3D project.

Video

What to Look Out For

  1. Adding new functionality requires you to modify your existing classes
  2. Your class is handling different inputs in different ways to the same method.
  3. You start to see if & else if statements cluttering your code.

Ways to Implement The Open Closed Principal in Unity

  • Have your classes act on interfaces, not discrete implementations
  • Use base classes and override their functionality
  • Add events to your class and have other components on the gameobject register for those events instead of the class calling them directly

 

Continue reading >
Share

Unity3D Machine Learning – Writing a custom Agent – Create your own AI Bot

I’ve been playing with the new Unity3D Machine Learning system for a few days now and made a little progress.  I wanted to share the steps I found to get a newly created agent setup and trained to complete a basic task.  In this post, you’ll see how to setup a basic agent with the goal of reaching a randomly chosen number using nothing but reinforced machine learning.  We’ll use the new Unity ML Agent system and tensorflow to create and train the agent to complete the task and discuss ways to extend this into a real game AI.

Setup Tensorflow and UnityML

If you don’t have tensorflow setup yet, you’ll need to follow the steps outlined here: https://unity3d.college/2017/10/25/machine-learning-in-unity3d-setting-up-the-environment-tensorflow-for-agentml-on-windows-10/

Video Version

Want to see this all in video?  Here ya go:

AgentML Scene Setup

Once you’ve gone through that process, open the Unity project and create a new scene.

The first thing we need is an academy.  Create a new gameobject, name it “NumberAcademy“.

Add the “TemplateAcademy” component to the “NumberAcademy“.  Our setup doesn’t need the academy to do anything special, so we can start with the basic blank academy provided in the template.

Under the Academy, create another child gameobject.  Name it “NumberBrain“.

Add a Brain component to it.

Set the State & Action size variables to 2.

Set the Action Space Type to Discrete.  We’ll be using 2 discrete actions (up or down) in our project.  We use discrete because these are represented as integers.

Set the State Space type to Continuous.  We’ll be tracking two floats, for state, so we use continuous.

Set the Brain Type to “Player

Add 2 actions.  Choose any 2 keys you want (I went with A & B), but set the Values to 0 and 1.  The key bound to value 0 decrements the #, the key bound to 1 will increment it.

The NumberDemoAgent Script

Create a new script named NumberDemoAgent.cs

Set the base class to Agent (replace the : MonoBehaviour with : Agent)

Add the following fields:

The currentNumber and targetNumber fields are the most important here.  Everything else is just for debugging and visualizing.

Our agent will pick a random targetNumber and try to get the currentNumber to our target using our increment and decrement commands.

Next we need to override the CollectState method like this:

Here, we’re returning our two floats for current and target number as the state of our agent.  Notice how this matches up with our 2 state variables on the brain and they’re floats which is why we have it set to continuous state instead of discrete.

For our agent to train, we need to select random target numbers.  To do that, we’ll override the AgentReset() method like this:

The final and most important part we need is the AgentStep() method.  This is where we take in actions (aka input), perform some tasks (respond to the actions), and reward our agent for successful choices.

The first thing you’ll see is our text update.  This is only for debugging / visualizing.  It allows us to see the current #, the target, and the # of times we’ve successfully solved the problem (reached the target number).

Next up is the switch where we look at the action and perform our task.  In this case, we either respond to action 0 by decrementing the current number, or to action 1 by incrementing it.  Any value out of that shouldn’t happen, but if we get one, we just ignore it and return.

Then we move our cube based on the currentNumber (using it for the x offset).  This cube again is only for visualizing, it has no impact on the actual logic or training.

We then check the currentNumber against some known limits.  Since we choose a random number between -1 & 1, if we reach -1.2 or +1.2, we can consider it a failure as it’s definitely going in the wrong direction.  In that case, we set the reward to -1 to denote a failure, then mark done as true so the agent can reset and try again.

Then finally, we check to see if the currentNumber is within 0.01 of the target.  If so, we consider that a match, set the reward to 1.0 for a success, and mark it as done.  We also increment the solved counter for debugging purposes (it’s nice to see how many times it’s been successful).

Here’s the complete script:

Setup The Agent

With the script ready, we need to create a new gameobject and name it “NumberDemoAgent”.

Attach the NumberDemoAgent script to it and assign the brain.

Next create a Text object and place it where you can see it (ideally big in the middle of the screen).

Assign the text object to the NumberDemoAgent.

Create a Cube and a Sphere and assign them to the NumberDemoAgent as well (these will help you see what’s going on, much easier than reading #s).

Testing in Player Mode

Now press play.  You should be able to move the cube left and right with your two hotkeys (remember I went with A & B for the hotkeys).

When you get the box to the sphere, it should increment the solved count and reset.  If you go too far the wrong way it should also reset (remember that 1.2 limit).

Training

Once it works in player mode, select the brain and change the “Brain Type” to “External

Save your scene and build an executable where the scene is the only thing included (with debug mode enabled).

For your output folder, choose the python subdirectory of your ml-agents project (included when you downloaded or cloned the source project).  For example, mine is located here: C:\ml-agents\python.

Remember the name you give it, you’ll need that in just a minute.

Anaconda / Jupyter

Launch an anaconda prompt.

Change directory to the python folder you just built into.  ex. “cd c:\ml-agents\python”

Enter the command “jupyter notebook” (you may need to hit enter a 2nd time btw)

You should be prompted with a web interface shortly after that looks like this:

Change the highlighted parts to match.  On the env_name, don’t just put in “numberdemo”, use the name that you built your executable with.  Buffer_size and batch_size you can copy though (it’s important to note that these #’s were only found by testing/trying, even after getting it working, I still barely understand what’s going on with them).

Once you’re done editing the hyperparameters, run the steps in order.

Start with step 1 & 2.  (the * disappears and a # appears in the [*] when it’s done.

When you run step 3, you should see a window appear for your game (a small window though).  The first time, you’ll probably also get a windows permissions dialog, make sure to allow it.

Once you start step 4… WAIT.. and watch the results come in (first one may take a minute so be patient)

Once it’s saved a couple times, hit the stop button.  Then move on to step 5 and run it.  This will export your training data to a .bytes file in the “python/models/ppo” subfolder.

Copy the .bytes file (again it’ll be named to match your executable name) and place it in your Unity project somewhere.

Select the brain and set the “Brain Type” to “Internal”.

Assign the .bytes file to the “Graph Model” field.

Save and press play!

Conclusions

This is a pretty simple sample, meant to just help get a basic understanding how how this system works.  I’m excited to see where it goes though and build bigger more interesting projects to control game AI and make interesting gameplay / bots.

 

Reference

Unity ML Agents GitHub – https://github.com/Unity-Technologies/ml-agents

HyperParameters Doc – https://github.com/Unity-Technologies/ml-agents/blob/master/docs/best-practices-ppo.md

Machine Learning Playlist –https://www.youtube.com/watch?v=qxicgknzUG8&list=PLB5_EOMkLx_Ub1A4iHoDUx7vg37sVoL-E

 

Continue reading >
Share

Simple System for Passing Balls in Unity

By Jason Weimann / October 12, 2017

Ball Passing System for Unity

This is a simple system I’ve used to pass soccer balls, hockey pucks, and anything else you can imagine in a sports game from one player to another.  The passing script contains everything needed to make something like this work and the logic for calculating the closest angle to your aim is pretty straightforward (and included in the project source below)

 

Video

Project Download

https://unity3dcollege.blob.core.windows.net/site/Downloads/Passing%20Balls.zip

Continue reading >
Share

VR Dash movement / locomotion with SteamVR and Unity3D

Ready to add dash locomotion to your game?  It’s pretty simple to setup, but there are some important things to consider before you start. In this guide, I’ll show you how to set it up, what to watch out for, and a trick to minimize motion sickness.  All you need is SteamVR and Unity3D to get started.

Before you get started, you’ll want to have some sort of environment setup.  Ideally you want something where it’s obvious that you’re moving.  The locomotion is FAST, and if you just have a flat white area or single repeated texture, it’s gonna be a whole lot harder to tell what’s going on.

Here’s what my test scene looks like.

SteamVR / Oculus SDK

Once you have an environment setup, you’ll need the SteamVR plugin.  The code here is setup for SteamVR but could definitely be ported to Oculus with minimal effort, all we really care about in code is the button click.

After importing SteamVR, drop the [CameraRig] prefab into the middle of your scene.

Select the right controller and add the SteamVR_LaserPointer component to it.

We’re only using the laser pointer to visualize where we’ll dash, there’s no requirement to use the pointer for things to work, you can use it if you like or choose any other visualization of your choice.

Set the color of the pointer to Green.

DashController

It’s time to add a little code.  This is the only code you’ll need for dashing to work, so take a good look at it.

The Code

RequireComponent- The first thing to notice is we’re requiring a SteamVR_TrackedController component to be on the GameObject.  We’ll be attaching this component to the “Controller (right)” and the tracked controller will get added automatically.

Serialized Fields – We have 4 fields that are editable in the inspector.

  • MinDashRange & MaxDashRange – We have a  minimum and maximum dash range so we can prevent people from dashing across the entire map or ‘dashing in place’.
  • DashTime – This is the amount of time it will take the player to move from their position to the end point.  In this setup it’s always the same time, no matter how far you go.
  • MaskAnimator – Here we’re storing a reference to an animator.. we’ll be creating this animator soon.  It’s used to add an effect that will minimize motion sickness.

Private Fields

  • trackedController – We’ll be using the tracked controller reference in a few places, so we’re caching it here to avoid calling GetComponent more than once.
  • cameraRigRoot – The same situation applies here, we’ll use the cameraRigRoot to move the player around, and we don’t want to call GetComponent multiple times.

Start() – Here we do the caching of our private fields, and we register for the PadClicked event on the controllers.  If you wanted to use a different button, you’d register for some other event, like GripClicked or TriggerClicked.

TryDash() – This is what we call when the player clicks the trackpad.  The method performs a raycast from the controller’s position aimed in it’s forward direction.  This is exactly what the laserpointer we added earlier does, so if the laser pointer hits something, we should hit the same thing.  And our RaycastHit named “hit” will have a reference to the thing they both hit.  If it does hit something, and the distance of that hit is within our allowed ranges, we start a coroutine called DoDash().

DoDash() – The DoDash method does the actual work here.  First, we set a bool on our maskAnimator named “Mask” to true.  Then we give it 1/10th of a second for the animation to play.  Next, we take note of the cameraRig’s current position and save it off in startPoint.  We then go into a while loop.  This loop will execute for the amount of time specified in our ‘dashTime’ field above (editable in the inspector).  In the loop, we use Vector3.Lerp to move the cameraRig from it’s startPoint to the endPoint (where the user aimed the laser).

Vector3.Lerp returns a vector at a point between two other vector3’s.  The 3rd value (elapsedPct) determines how far between them it should be.  A value of 0 is right at the start point, a value of 1 is at the end, and a value of 0.5 is right in the middle.

Once we leave the loop, we set the “Mask” parameter on our animator back to false and we’re done.

Back to the Inspector

Attach the DashController to the Controller (right)

Play & Test

You should be able to play now and dash around.  Put on your headset, aim the controller and click the trackpad!

The Mask – Reducing motion sickness

Dashing around usually feels okay for most people, but motion sickness is still possible.

To help remove some of the sickness, we can add a cool masking effect.  And if you have some artistic skill, you can make it look much better.

Download this DashMask.png image

Select the Camera (eye).  Right click and add a Quad.

Drag the DashMask.png image onto the quad, a new material will be created automatically.

Select the quad and change the material’s render mode to “Fade”

Move and resize the quad to match this.  We need it close to our face as it’s going to be used to mask out the world.

Animating the Mask

If you play now, you’ll have a mask sitting in your face (or not depending on where you drag the alpha of the albedo color on that material)

What we want though is for the code above to be able to toggle the mask, with a smooth animated fade that we can customize later.

To do this, let’s create a simple animation for the alpha channel of our quad.

The Animation

Select the Quad

Open the Animation window.

Click create animation, name the animation FadeMask

Click record.

Open the Color picker for the Albedo of the material.

Slide the alpha around and settle it at 0.

Drag the Animator red line to the 1:00 mark.

Open the color picker again.

Slide the alpha to the far right.

The Animator

The animator will control our animations.  It handles the state of our animations and does the transitions between them.

Create a new animator for the quad.

The Mask Parameter

In the parameters section, add a new Boolean parameter and name it “Mask

Adding the Animations

Drag the FadeMask animation you just made onto it.

Do it again, but for the second FadeMask, rename it to “FadeMask Reverse“.

Add transitions between the two and use the “Maskparameter to control them.

Set the FadeMask speed to 5.

Select the FadeMask reverse and change the speed to -1.

We’re playing the animation in reverse here to fade the mask back out.  We could have a separate animation for this, but we don’t need it when we can just set the speed to -1.

Attaching the Animator

Add an animator to the Quad.

Assign the new Animator Controller to your animator.

The last thing we need to do is assign the MaskAnimator field of the Dash Controller.

Select the controller, drag the quad onto it, and you’re done..

 

The Result

Continue reading >
Share

Build a Unity multiplayer drawing game using UNET

In this article you’ll learn how to build the foundation for a multiplayer drawing game.  We’ll start with drawing on a texture and add some Unity UNET network components to make it multiplayer.  By the end, you’ll see other players drawing in real time and have a good foundation to build your own drawing game in Unity3D.

Requirements

To start, we’ll need a couple pieces of art.

The Canvas

First is our default/blank canvas.

For this example, I’m using a solid white texture that I put a little border around.

Download this canvas or create your own

Color Wheel

The other thing we need is a color wheel.

Download this wheel or find your own

Add both of those textures (or alternate ones you choose) to your project in a Textures folder.

Setup the Canvas

Create a new Quad.

Resize it so it looks similar to this.

Drag the canvas texture you’ve chosen onto the Quad.

Change the material shader on the quad to Unlit/Texture.

Make sure you select a resolution and don’t use free aspect while you’re setting this up.  I”m using 1920×1080.

Name it “Paint Canvas”.

Create a script named PaintCanvas.cs and replace the contents with this.

PaintCanvas Code

This script is used for two things.  First, it makes a copy of the texture in memory so we can easily read and write to it.  This texture is created in PrepareTemporaryTexture and assigned to the static property “Texture”.

The second thing it does is set the texture data from a byte array.  This is so we can ‘load’ a texture from the host when we join the game as a client.  You’ll see this later when we get into the networking code.

Add the new PaintCanvas component to  the “Paint Canvas” Quad.

Back to the Inspector

Your PaintCanvas should look similar to this.

Color Picker

It’s time to setup our color picker so the player can choose different colors easily.

Create a Quad

Name it “Color Picker”

Drag the ColorWheel texture onto the Quad.

Change the Shader to “Unlit/Transparent Cutout”

Create a new script named “ColorPicker.cs” and replace the contents with this.

Add the ColorPicker component to your “Color Picker” object in the inspector.

ColorPicker Code

This script starts off with a static “SelectedColor” property.  This is where we’ll store the users color selection when they click on the colorpicker.

But the work here is done in Update().  We check to see if the user has clicked this frame, and if so, we do a raycast from the click point into the scene).

If the ray intersects with something (line 17) and that something happens to be the color picker (line 20), then we pull the texture coordinate from the RayCastHit multiply it by the height and width of the texture, and get the actual pixel on the texture that we clicked.

With that pixel, we use the Texture2D method GetPixel to get its color and store it in that “SelectedColor” property (line 32).

Finally, we set the material color of our “preview” object to the selected color (line 34).  This is where the user will see the color they’ve picked and have “selected”.

Back to the Inspector

Your color picker should look like this.

Move the wheel so it looks like this.

Selected Color Preview

If you look at the Color Picker code or inspector, you may notice that we have a “Selected Color Preview” field.

Create another Quad and position it just below the wheel like this.

Create a new Material and name it “SelectedColor

Assign the material to the “Selected Color Preview” Renderer

Change the Shader to “Unlit/Color

We don’t want light having any impact on how our drawing appears.  Unlit/Color makes that not an issue.

It should look like this in the inspector.

Brush Size Slider

Let’s also add a brush size slider.

Create a new Slider from the GameObject->UI menu.

Move it so it looks like this.  (I also added a text object at the top, feel free to do that too if you want)

Create a new script named “BrushSizeSlider.cs” and replace the contents with this.

BrushSizeSlider Code

This class is really just wrapping the slider value so we don’t have to reference it anywhere in the editor.  Because our player is network instantiated, and we don’t have any objects in the scene that would make sense referencing this slider, we’re just putting the BrushSize into a static global int.  This isn’t the best architecture for something like this, but we’re not building out a full game or editor so it’s a quick enough way to get things working.

Add the BrushSizeSlider component to your slider GameObject.

Your slider should look like this.

Save your work to a scene!

Player & Brush Time

Now that we have the board setup, it’s time to create the player & brush.

Create a new script and name it “PlayerBrush.cs“.

Replace the contents with this.

PlayerBrush Code

This is the most important part of the project.  Here, we’re doing the drawing and networking.  (ideally we’d split this into 2 classes soon)

The Start method is only called on the server/host because of the [Server] tag.  That’s because the Start method is getting the texture data and sending it to the client.  This may be a little confusing, but remember that the “Player” will be spawned on the server, and this method is being called on the player gameobject but only on the host/server.

RpcSendFullTexture is the opposite.  It uses the [ClientRpc] tag because we only want that method being called on the clients (it’s also required to send the command to the client).  This method calls the SetAllTextureData method we covered earlier, setting the data of the client to match the server.

.Compress() & .Decompress() – These methods are extensions that you’ll see in a while.  They’re used to compress the texture data into something much smaller for network transmission.  The compression here is extremely important, without it, this wouldn’t work.

When you look at the Update method, it should feel familiar.  Like with the color picker, we’re checking for left mouse down, doing a raycast, and checking to see if the click was on what we wanted.  In this case, we’re looking to see if they have the mouse down on the PaintCanvas.

If they do, we get the texture coordinate (like before), get the pixel x & y, then we send a command to the server using CmdBrushAreaWithColorOnServer.  We also call BrushAreaWithColor on the client (if we don’t do this, the pixel won’t change until the server gets the message, handles it, and sends one back.  this would feel laggy and bad, so we need to call the method on the client immediately).

CmdBrushAreaWithColorOnServer doesn’t really do any work itself.  It’s a [Command] so clients can call it on the server, but all it really does is tell the clients to brush the area using an Rpc.  It also tells the server/host to draw using BrushAreaWithColor.

BrushAreaWithColor is responsible for changing the pixels.  It does looping over the brush size to get all the pixels surrounding the clicked one.  It does this in a simple square pattern.  If you wanted to change the brush to be circular or some other shape, this is the method you’d modify.

Back to the Inspector

Create a new “Empty GameObject“.

Name it “Player

Add a NetworkIdentity component to it & check the “Local Player Authority” box.

Add the “PlayerBrushcomponent.

Your Player object should look like this.

Drag the Player object into a folder named Prefabs.  This will make your player a prefab so we can instantiate it for network play.

Error! – An Extension Method!

The playerbrush script references some compression methods that we’ll use to sync data to the clients when they first connect.

To add these compression methods, let’s create a new script for our extension methods.  If you don’t know what extension methods are, you can read more about them here: Unity Extension Methods

Create a new script named “ByteArrayExtensions.cs” & replace the contents with this.

ByteArrayExtensions Code

I won’t go into the details on how this compression works, but this is some standard c# code for doing a quick in memory compression of data.  These are extension methods that will ‘extend’ the byte arrays.  It’s essentially just a cleaner way so we can write somearray.Compress() instead of ByteArrayExtensions.Compress(somearray).

The Networkmanager

The last thing we need is a networkmanager.

Create a new gameobject and name it “[NetworkManager]“.

Add the NetworkManager component to it.

Add the NetworkManagerHUD component to it.

Expand the NetworkManager component.

Drag the “Player” prefab from your Prefabs folder (NOT THE SCENE VIEW ONE).. onto the “Player Prefab” area of the component.

The work is done, Save your Scene!

Testing the game

Now that everything’s setup, it’s time to test.

To do this, create an executable build (make sure to add your scene to the build settings).

In one instance, start a LAN host, then the other can join.

Download

If you want to download the full working project sample, it’s available here: https://unity3dcollege.blob.core.windows.net/site/Downloads/MPPainting.zip

Online Multiplayer

If you want to go beyond LAN mode, you’ll need to enable Multiplayer in your Services window.  It will take you to the Unity webpage where you’ll need to set a maximum room size.  For my test I went with 8 players.

Conclusion & Notes

This project is meant only to be an example for UNET & drawing.  To keep things simple, I used quite a few static fields, which I’d highly recommend against in a real project of any size.

 

 

Continue reading >
Share

VR Movement – Ironman / Jetpack Flying with SteamVR

There are a lot of interesting movement systems for VR.  Arm swinging, dashing, teleportation, trackpad, and more.  In this guide, I’ll show you another one I experimented with previously and really enjoyed.  We’ll go over how to make a player fly like Ironman or at least like a person holding some jet packs..

Prerequisites

This article assumes you have SteamVR in your project, though converting it to OVR native should be a simple task.

Rigidbody on [CameraRig]

Start by adding the [CameraRig] prefab from the SteamVR package to your scene.

Add a Rigidbody component to the camerarig.

Freeze the rotation values.

Locking rotation is very important, if you don’t, your player will end up spinning around, flipping over, and getting sick

Now, add a box collider to the rig so you can land (this could be a capsule or any other collider that works well for your game)

Controller Setup

Expand the CameraRig and select both controllers.

Add the SteamVR_TrackedController component to them both.

JetpackBasic Script

Create a new script, name it “JetpackBasic”

Replace the contents with this.

Your controller should look similar to this: (the sphere collider and audio source are optional and beyond the scope of this article)

Press Play

Aim the controllers where you want to go.

Pull the trigger.

Fly!

Continue reading >
Share

Using Vector3.Reflect to cheat ball bouncing physics in Unity3D

Games based on balls can be a frustrating to build.  I’ve done a number of them myself and used a variety of different techniques.  The hardest part tends to be getting the physics right.  Some games require a ton of tweaking, adjusting rigidbodies, colliders, physics materials and scripts.  But in some other games, cheating is the way to go..  Cheating by not using the physics system for bouncing..

Cheating How?

Instead of allowing the physics system to control our ball bounces, we’ll create a simple script to do it instead.  Instead of adjusting the mass, bounciness, & friction our script will have a single float for the minimum bounce velocity.  This will keep our ball bouncing forever, and always at about the same speed.

What’s it look like?

The Code

Review

There are a couple things to cover here, we’ll go over them in order.

To start we have a Vector3 for an initial velocity.  Like the tooltip says, this is just for the example / debugging.  Our OnEnable method will set the initial velocity to this value so the ball starts moving.

The minVelocity field is used to control how slow the ball can go.  Every bounce will be at this velocity (or higher).

OnEnable() – Here, we’re caching the rigidbody reference and setting that initial velocity.

Update() – This one’s important.  Because we’re going to override the collision behavior, we need to keep track of the objects velocity each frame.  When the collisions happen, this velocity is going to change, but for our calculations, we want the velocity before the collision.  Saving it off here in Update is an easy solution.

OnCollisionEnter() / Bounce() – This is where we do the work.. even though it’s not much work.  Calculating direction is done using Vector3.Reflect.  We pass in the last frame velocity (that we cached in Update), along with the collision’s normal.  Then we multiply the result by the current velocity OR the minVelocity, whichever is greater.

Bouncing toward the player

In games where you have the player hitting a ball against a surface, you’ll often want to send the ball back toward the player.  This might seem ‘not realistic’, but it’s usually way more fun.  And it’s pretty easy to do.

The Code

The only real difference is when you’re setting the return velocity, we’ll lerp between the direction to the player and the reflect value.

In the editor, we expose a bias field that allows easy tuning of just how ‘toward the player’ the ball goes.  The right value for the bias will depend on your game and the play area size.

Example Download

If you want to try this out and don’t want to setup a couple cubes, you can download the example project here: https://unity3dcollege.blob.core.windows.net/site/Downloads/Ball%20Bounce.zip

Conclusions

This functionality isn’t appropriate for every game, or every interaction in every game.. but there are plenty where it works better than using the default physics setup.  Especially the bouncing towards a player.. I’ve used that more than once and found it always made the games more fun and engaging.  It’s also worth noting that you can use Vector3.Reflect for all kinds of other functionality, reflecting bullets, beams, or anything else.. so keep it in mind when coming up with new game mechanics 🙂

Continue reading >
Share

LINQ for Unity Developers

LINQ (Language Integrated Query) is a great feature available to C# Unity developers.  Many developers don’t know it exists or how to use it though, and lose out on the great time & code savings it can provide.  LINQ in Unity has a variety of great uses, and a couple pitfalls you’ll want to avoid.

Getting Started

To use LINQ in a C# script, you need to add a using statement for the namespace like this.

What Can I do with LINQ?

The most common uses for LINQ statements tend to be sorting, searching, and filtering (though there’s plenty more you can do).

For example, let’s take a scenario where we want to find the closest game object to the player.

Compare that to the LINQ version

As you can see, LINQ has an OrderBy extension method for collections.  Because we have a List of gameobjects, and List implements IEnumerable, we can use OrderBy to easily sort objects by distance, then take the first from the collection (since they’re in ascending order).

What the hell is that t => ????

You’ll see this a lot in LINQ statements, it’s called a lambda operator.

In the case of this OrderBy statement, think of the “t” as a reference to each object in the collection.  When it orders them by distance, it’s passing in our local objects position, and the position of “t”.

Does it have to be “t”?

That “t” is just a variable name.  It could just as easily be named “a”, “thingToCompare”, or any other variable name.  “t” is just a common standard in lambda examples.  Often, I’ll use a variable name that’s more descriptive, especially when building more complex LINQ statements.

What’s FirstOrDefault?

The FirstOrDefault() call makes our LINQ statement return the first object from the collection… OR whatever the objects default value is.  Default is just the value you’d have if you never assigned a value.. for objects that’s null.

There’s also a First() method, but that will throw an exception if there’s nothing in the collection.  Sometimes you want this, but I find myself almost never using it.

What about Performance?

Of course with anything, you’ll need to consider the performance of operations like this.  In the majority of cases using LINQ won’t hurt you at all.  It may add a couple nanoseconds here or there, but it can also shave some time off if your custom code isn’t completely optimized.

The one thing you definitely do need to look out for though is Garbage Collection.  LINQ statements will generate a little garbage, so avoid using them in something that’s going to be called every frame (don’t put them in your Update() calls).

For other events though, LINQ can be a huge time saver, make your code easier to read, and having less code always reduces the chance for bugs.

Multiple Lines or One Line?

When you look at LINQ statements, sometimes they’re written as a single long line.  Sometimes there’s a line per method..

Functionally, it doesn’t make any difference.  Personally, I prefer a line per method because it makes the call easier to read at a quick glance.  So while there’s no set rule on it, I recommend you split your LINQ statements with a new line before each method call (with the period on the newline).

Take()

The Take method can be used to “Take” a subset of a collection and put them into another collection.

As an example, imagine a case where you want to find the 4 lowest health enemies.  The Take method makes that easy.

Sorting by multiple things

You saw the OrderBy() method above, which is great for sorting, but sometimes you’ll need to sort by more than one thing..  For example imagine you have a scene full of Dogs… and you want to sort the dogs by Color and Size.

Switching The Order

If you want to sort in the opposite direction, you can use OrderByDescending() to reverse the order.

Deferred Execution

Often when you see a LINQ statement acting on a collection, you’ll see it end with ToList();

There’s a very important reason this is done.. and that reason is called deffered execution.

When you use a LINQ statement, the execution of the statement doesn’t happen until it’s needed.

Take a look at this example:

The ordering and distance checking of coins is only done if the player presses ‘A’.  And even then, it’s not until the delay has passed and we’ve reached the foreach statement.  If that’s never reached, the deferred call isn’t needed so it’s never run.

The downside to this is we have a bit less control over execution time.  Sometimes that’s fine, other times we want to enforce execution immediately.

And to force that execution, we can call ToList() or ToArray().

What about the other syntax?

It’s important to note that there are two different types of LINQ syntax.  There’s the one I’ve shown you so far, and another that looks a bit more like SQL.  For Unity developers, I’d recommend staying with the fluent syntax you see here and avoid the SQL one.  I’ve found developers who don’t do much SQL work tend to get a bit more confused by the other syntax and confusion causes bugs.

What other operators are there?

There are a TON of them.  I’ve covered a couple of the most common ones, but I recommend you view the larger list here just to know what’s available:
https://www.tutorialspoint.com/linq/linq_query_operators.htm

Here are some of the ones I find myself using more often

  • GroupBy() – groups things as you’d expect.. often end up using this with ToDictionary
  • ToDictionary() – yep it builds a dictionary with the keys/values you want from any other collection(s).
  • Any() – tell you if any object in the collection meets a criteria (returns true or false)
  • Skip() – great for paging, often used with Take
  • Contains() – easy way to check if a collection contains a specific object

Conclusions

LINQ is amazingly powerful, and with just a little time learning the syntax, it can be a huge time saver.  It’s also important to be able to read in other people code..  Outside gaming, in other C# projects, you’ll see LINQ everywhere.  As I mentioned above though, garbage collection and performance are extremely important for games, so you still need to think and profile when using it in your projects.  That’s true for everything though, so don’t let it discourage you from taking advantage of this amazing language feature.

For more examples and LINQ statements, check out this site: http://linqsamples.com/

Continue reading >
Share

Unity Slider Label Text

Need a slider in your game or app and want text on the handle?

The setup for this is pretty simple, you’ll need a slider, a label and a script..

The Slider

Create a slider.. then select a minimum and maximum value.  For the slider above, I’ve also checked “whole numbers”.

The Label

Once you’ve created the slider, find the Handle child.

Create a new Text (or ideally a TextMeshPro – Text) object under the Handle.

Center the text object, and set the color to something that works well with your handle.

And finally, add this script to the text (or textmeshpro text) object..

Code

Note: If you don’t use TextMeshPro (which you should), you’ll need to change the references to just be “Text” & remove the top using statement.

Formatting

The default format for the script above shows a degrees symbol.  You can modify that by editing the FormatText field.  The code uses string.Format, and the {0} is replace by the actual value.

 

Continue reading >
Share
Page 3 of 6