After learning the basics of C#, many beginners run into the same problem: they understand the syntax, but they don’t understand how to actually use it when creating a game.
At first glance, it feels like two completely different worlds. Code is text and numbers, while a game is visuals, movement, and sound. They seem like unrelated pieces.
In this lesson, we’ll explore how these two worlds connect and build the bridge between code and the objects on your scene.
There’s no magic in Unity. There are connections. And from those connections, action is born.
Every object in a Unity scene is a GameObject. It doesn’t matter whether you can see it or not. The player, NPCs, environment props, UI elements, the camera — all of them are GameObjects.
They may serve different purposes, but they all share one thing — they are participants in the scene.
That’s why every GameObject has a Transform component. It defines the object’s position in the game world: where it is, how it is rotated, and what its scale is.
It’s important to understand this: a GameObject is just a container. By itself, it does nothing. Its behavior is entirely defined by the components attached to it.
Earlier, you saw the Camera object. In addition to the required Transform component, it also had two more: Camera and Audio Listener.
The Camera component allows the object to render the scene for us, while the Audio Listener lets it receive sounds produced by other objects.
A GameObject is a container. Its “abilities” come from its components. The rule is simple: show me your components, and I’ll tell you what you do 🙂
Let’s add a new GameObject to the scene. For example, a simple square sprite. Unity will automatically create a GameObject with the required Transform component and a Sprite Renderer component, which is responsible for displaying the sprite.
Select this object in the Hierarchy and look at its Inspector. In front of you are all the components that currently “build” this object.
If a GameObject is the actor, then the Inspector is both its dressing room and its control panel. Everything that defines its behavior is visible here.
And here’s the interesting part — you can change parameters while the game is running.
You’re not writing code. You’re simply changing values. But the object already reacts.
A small secret: when you modify values in the Inspector, Unity does under the hood exactly what a programmer would do through code. You’re already controlling the engine — just without typing.
A script you write in C# can also be a component. As long as it simply sits in the Assets folder, it does nothing. It’s just text.
But once you attach it to a GameObject, it becomes part of that object. Now Unity will execute the logic you described in it. Well… at least the logic you actually wrote 🙂
Scripts that work with Unity are usually created as MonoBehaviour. So a new script typically looks like this:
using UnityEngine;
public class TheBestScript : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
When you run the game, Unity first executes the code inside Start(), and then calls Update() every frame.
Now attach this script to the Square object. At the moment, it doesn’t do anything — but the object now has a new component: TheBestScript.
Now we arrive at the most important part. Earlier, we changed component parameters through the Inspector and immediately saw the result.
While learning C#, you’ve already worked with variables, data types, and values. Now it’s time to connect these two worlds: make the variables in your script modify component properties.
To do that, a script needs access to the component it wants to
interact with. This is done using the method
GetComponent<ComponentName>().
Let’s repeat what we did in the Inspector — but this time through code.
using UnityEngine;
public class TheBestScript : MonoBehaviour
{
// Variable to store a reference to SpriteRenderer
private SpriteRenderer sr;
void Start()
{
// Get a reference to the SpriteRenderer component
sr = GetComponent<SpriteRenderer>();
// Change the color to red
sr.color = Color.red;
}
void Update()
{
// Rotate around the Z axis
transform.Rotate(0f, 0f, 90f * Time.deltaTime);
}
}
Now the object changes its color when the game starts and rotates every frame. What you previously did manually is now handled by the script.
Notice that the Transform component exists on every
GameObject by default, so you can access it directly via
transform without calling GetComponent.
Also note that component property names often match how you access them in code. At first, you may need to check the documentation — or ask an AI assistant 🙂 — but over time this becomes second nature.
Logic errors and NullReferenceException are the most common issues you’ll encounter while creating your own games.
When your project is small, you might feel confident that the required component is definitely attached to the object. But as the project grows, it’s better to add a safety check to avoid runtime errors.
A more careful version of the code would look like this:
using UnityEngine;
public class TheBestScript : MonoBehaviour
{
// Variable to store a reference to SpriteRenderer
private SpriteRenderer sr;
void Start()
{
// Get a reference to the SpriteRenderer component
sr = GetComponent<SpriteRenderer>();
// Check whether the component was found
if (sr != null)
{
// If found — change the color to red
sr.color = Color.red;
}
else
{
// If the component is missing — log a warning
Debug.LogWarning("SpriteRenderer was not found on this object!");
}
}
void Update()
{
// Rotate around the Z axis at 90 degrees per second
transform.Rotate(0f, 0f, 90f * Time.deltaTime);
}
}
We simply check whether the reference to the component was successfully obtained. If the component doesn’t exist, the code won’t try to access it.
A NullReferenceException doesn’t mean your game is “broken” — it means there’s a missing connection. The script is trying to access something that isn’t there.
If the error does appear, don’t panic. Read the message in the Console — Unity usually points directly to the line where the problem occurred. Double-clicking the error will take you to the exact place in your code.
Until now, we’ve worked with components that are attached to the same object as the script. But in a real game, you often need to control other GameObjects.
To do that, you first need to get a reference to the target object,
and then use the familiar
GetComponent<>() method or access its Transform.
Common ways to get a reference to an object:
GameObject.FindWithTag() or
GameObject.Find()
(convenient, but slower).
Instantiate().
For this example, let’s control the Square object from a different GameObject.
Add the Player tag to Square. Then create an empty object and attach the following script to it:
using UnityEngine;
using UnityEngine.InputSystem;
public class SquareMoving : MonoBehaviour
{
// Public reference to another GameObject
public GameObject otherGameObject;
void Start()
{
// If the reference was not set in the Inspector
if (otherGameObject == null)
{
// Search for the object by tag
otherGameObject = GameObject.FindWithTag("Player");
}
// If the object was found — try to get its SpriteRenderer
if (otherGameObject != null)
{
SpriteRenderer sr = otherGameObject.GetComponent<SpriteRenderer>();
// If the object has a SpriteRenderer — change its color to blue
if (sr != null)
{
sr.color = Color.blue;
}
}
}
void Update()
{
// If the object is still not found — stop execution
if (otherGameObject == null) return;
float x = 0f;
// Check if a keyboard is available
if (Keyboard.current != null)
{
if (Keyboard.current.aKey.isPressed)
x -= 1f;
if (Keyboard.current.dKey.isPressed)
x += 1f;
}
// Create a movement direction
Vector3 direction = new Vector3(x, 0f, 0f);
// Move the other object
otherGameObject.transform.Translate(direction * 5f * Time.deltaTime);
}
}
Now you can either manually drag Square into the otherGameObject field in the Inspector, or leave it empty — in that case, the script will find the object by its tag.
Run the game — and you’ll see that Square moves, even though the movement script is attached to a different object.
When the game starts, the square may turn blue. However, we now have two scripts modifying the same property — the object’s color. Whether it ends up red or blue depends on the script execution order.
This is how logical errors appear: the code runs, there are no Console errors, but the result isn’t what you expected. We’ll leave this as a demonstrative example.
A script doesn’t have to control only its “own” object. The key is
getting the reference. Now you understand how
GetComponent<>() allows you to work with components
on another object.
We started by changing parameters manually through the Inspector. Then we repeated the same actions using a script. And now, we’ve learned how to control even another object.
Color, movement, rotation — all of these are simply component properties that you can access through a reference.
In Unity, everything is built on connections: get a reference → get a component → change its state.