<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Giorgi Eliozashvili</title>
    <description>The latest articles on DEV Community by Giorgi Eliozashvili (@eliozashvili).</description>
    <link>https://dev.to/eliozashvili</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F849591%2Fe35b6b29-ac91-4cda-b010-feb98077d080.jpg</url>
      <title>DEV Community: Giorgi Eliozashvili</title>
      <link>https://dev.to/eliozashvili</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eliozashvili"/>
    <language>en</language>
    <item>
      <title>Unity ScriptableObject</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:47:25 +0000</pubDate>
      <link>https://dev.to/eliozashvili/unity-scriptableobject-3lpj</link>
      <guid>https://dev.to/eliozashvili/unity-scriptableobject-3lpj</guid>
      <description>&lt;p&gt;When you first make a weapon in a shooter game, you usually give it the standard &lt;em&gt;MonoBehaviour&lt;/em&gt; script and write everything there: damage, fire rate, ammo, shooting logic, etc. But when you start adding new weapons, lots of data duplication appears. Every prefab holds a copy of the same data. To solve this, Unity has a feature called &lt;em&gt;ScriptableObjects&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;According to Unity documentation, this is the official use case of &lt;em&gt;ScriptableObjects&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Saving and storing data during an Editor session&lt;br&gt;
Saving data as an asset in your project to use at runtime&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;ScriptableObject&lt;/em&gt; is an independent container for saving data. Its main difference from the usual &lt;em&gt;MonoBehaviour&lt;/em&gt; is that it does not need to be attached to a &lt;em&gt;GameObject&lt;/em&gt; in the scene.&lt;/p&gt;

&lt;p&gt;Let’s see the example of my basic weapon &lt;em&gt;ScriptableObject&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using UnityEngine;

[CreateAssetMenu(fileName = "WeaponSO", menuName = "Scriptable Objects/WeaponsSO")]
public class WeaponSO : ScriptableObject
{
    public Weapon WeaponPrefab;
    public ParticleSystem HitVFX;

    public int Damage;
    public float FireRate;
    public bool IsAutomatic;
    public bool CanZoom;
    public float ZoomAmount;
    public float ZoomRotationSpeed;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;ScriptableObject&lt;/em&gt;-s are inheriting from &lt;em&gt;ScriptableObject&lt;/em&gt; instead of &lt;em&gt;MonoBehaviour&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let’s break this example down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[CreateAssetMenu]&lt;/strong&gt; allows us to create a &lt;em&gt;ScriptableObject&lt;/em&gt; file in our project's folder.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside the class, there are no &lt;em&gt;Start()&lt;/em&gt; nor &lt;em&gt;Update()&lt;/em&gt; methods. Just pure data that you can manipulate through &lt;strong&gt;Unity Inspector&lt;/strong&gt;. And this is how it looks in &lt;strong&gt;Inspector&lt;/strong&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbihj1il8d4klxupxg5n6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbihj1il8d4klxupxg5n6.webp" alt="ScriptableObject in inspector" width="430" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So you can have separate weapon &lt;em&gt;ScriptableObjects&lt;/em&gt; in your project and manipulate them more clearly and easily.&lt;/p&gt;




&lt;p&gt;In summary, &lt;em&gt;ScriptableObject&lt;/em&gt; is a perfect tool for data management, separating data &lt;em&gt;(ScriptableObject)&lt;/em&gt; and logic &lt;em&gt;(MonoBehaviour)&lt;/em&gt;, making your project cleaner and easily manageable.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Simple Unity NavMesh</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:43:42 +0000</pubDate>
      <link>https://dev.to/eliozashvili/simple-unity-navmesh-o48</link>
      <guid>https://dev.to/eliozashvili/simple-unity-navmesh-o48</guid>
      <description>&lt;p&gt;Let’s build a simple AI follower using &lt;strong&gt;Unity&lt;/strong&gt;’s built-in component &lt;em&gt;NavMesh&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We will use &lt;em&gt;NavMesh Agent&lt;/em&gt; and &lt;em&gt;NavMesh Surface&lt;/em&gt; for this simple example.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NavMesh Surface&lt;/em&gt; — is a component that scans the surface and “bakes” it in a way that our Agent can walk on it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NavMesh Agent&lt;/em&gt; — is a component that you give to your “follower” (any kind of follower, can be an enemy or just a cute minion that follows you around). It knows its height and radius and can go around obstacles.&lt;/p&gt;

&lt;p&gt;With our Agent and Surface, we need to make a short script that tells our Agent to move and follow our target, in this case, the Player.&lt;/p&gt;




&lt;p&gt;But before that, let’s dive into some NavMesh settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feemasw7g2zf9a2ofzgqr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feemasw7g2zf9a2ofzgqr.webp" alt="nav mesh surface parameters" width="438" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this &lt;em&gt;NavMesh Surface&lt;/em&gt;, we are interested in two parameters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent Type&lt;/strong&gt; — is Robot, because it knows what kind of agent will walk around on the surface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include Layers&lt;/strong&gt; — is where you specify which &lt;em&gt;GameObject&lt;/em&gt; layers the &lt;em&gt;NavMesh Surface&lt;/em&gt; will include during the baking process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu00b6kg6ukrxrebjqjgj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu00b6kg6ukrxrebjqjgj.webp" alt="nav mesh agent parameters" width="438" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is &lt;em&gt;NavMesh Agent&lt;/em&gt; settings, where you can choose &lt;em&gt;Agent Type&lt;/em&gt; and give it some basic and pretty straightforward options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46q2get47vmzvt7ozcda.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46q2get47vmzvt7ozcda.webp" alt="navigation parameters" width="319" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is &lt;em&gt;Navigation of the Agent&lt;/em&gt;. Let’s find out what those options are because I find it very interesting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Radius&lt;/strong&gt; — defines agent’s &lt;em&gt;drum roll&lt;/em&gt; radius! We determine how wide our agent is. When baking the mesh, Unity will offset all walls and corners by this amount to prevent your agent from rubbing its sides against textures when walking or getting stuck in narrow passages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Height&lt;/strong&gt; — defines agent’s height. Allows Unity to calculate whether the agent can fit under low obstacles, bridges, or ceilings. If the clearance height is less than 1, the mesh underneath it will not be baked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step Height&lt;/strong&gt; — determines the maximum height of a stair or threshold of an obstacle that the agent can climb. We want to make sure that this option is always lower than Height.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Max Slope&lt;/strong&gt; — the maximum degree of surface slope that the agent is physically capable of walking up or down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drop Height&lt;/strong&gt; — determines the maximum height from which agent can jump. In our case 9. If the actual height of an object is more than 9 then agent will not drop down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jump Distance&lt;/strong&gt; — is the maximum horizontal distance that agent can jump.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Now let’s check the code out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using UnityEngine;
using UnityEngine.AI;
using StarterAssets;

public class Robot : MonoBehaviour
{
    private FirstPersonController _player;
    private NavMeshAgent _agent;

    private void Awake()
    {
        _agent = GetComponent&amp;lt;NavMeshAgent&amp;gt;();
    }

    private void Start()
    {
        _player = FindAnyObjectByType&amp;lt;FirstPersonController&amp;gt;();
    }

    private void Update()
    {
        _agent.SetDestination(_player.transform.position);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use &lt;em&gt;NavMesh&lt;/em&gt; we need to import its namespace, which is &lt;em&gt;UnityEngine.AI&lt;/em&gt;. I also use &lt;em&gt;StarterAssets&lt;/em&gt; for this project for a premade first-person controller from &lt;strong&gt;Asset’s Store&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;First, we need to declare and cache our variables. We retrieve the _&lt;em&gt;agent&lt;/em&gt; component in &lt;em&gt;Awake()&lt;/em&gt; since this script is attached directly to the Robot game object. Then, we find the _&lt;em&gt;player&lt;/em&gt; in &lt;em&gt;Start()&lt;/em&gt;. In the &lt;em&gt;Update()&lt;/em&gt;, we call the built-in &lt;em&gt;SetDestination()&lt;/em&gt; method, which takes player's Vector3 position. Once the game starts, the agent will dynamically pursue the player wherever they go.&lt;/p&gt;

&lt;p&gt;And that’s it. This is how to make a simple AI follower in &lt;strong&gt;Unity&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Mathf.Lerp() Making dynamic camera zoom in Unity using Coroutines</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:37:42 +0000</pubDate>
      <link>https://dev.to/eliozashvili/mathflerp-making-dynamic-camera-zoom-in-unity-using-coroutines-73d</link>
      <guid>https://dev.to/eliozashvili/mathflerp-making-dynamic-camera-zoom-in-unity-using-coroutines-73d</guid>
      <description>&lt;p&gt;The easiest way to show Player how fast his character is moving is by using camera zoom. For example, if Player is going very fast, camera is zoomed out, FOV is increased, and a feeling of swiftness appears.&lt;/p&gt;

&lt;p&gt;We want the camera zoom to be smooth, not instantaneous. For this &lt;em&gt;Mathf.Lerp()&lt;/em&gt;, Coroutine and while loop come in handy.&lt;/p&gt;




&lt;p&gt;The &lt;em&gt;Mathf.Lerp(A, B, t)&lt;/em&gt; function stands for linear interpolation. It takes a starting value - A, a final target - B, and returns a point between them based on the t parameter (a percentage of the path from 0 to 1).&lt;/p&gt;

&lt;p&gt;If we want the FOV change to take exactly the time we specify (for example, half a second), we can’t simply pass random numbers. We need a strict timer.&lt;/p&gt;

&lt;p&gt;To achieve this, a &lt;em&gt;while loop&lt;/em&gt; is created inside the &lt;em&gt;Coroutine&lt;/em&gt;, which calculates what fraction of the total animation time has elapsed each frame.&lt;/p&gt;




&lt;p&gt;Let’s check the realisation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Unity.Cinemachine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CameraController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;minFOV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;40f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;maxFOV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;80f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;zoomDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.5f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;zoomSpeedModifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CinemachineCamera&lt;/span&gt; &lt;span class="n"&gt;_camera&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Coroutine&lt;/span&gt; &lt;span class="n"&gt;_zoomCoroutine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Awake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_camera&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CinemachineCamera&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Called externally to trigger the camera zoom effect&lt;/span&gt;
    &lt;span class="c1"&gt;// In this project, it's invoked within the level generation script&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ChangeCameraFOV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;speedAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_zoomCoroutine&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;StopCoroutine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_zoomCoroutine&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;_zoomCoroutine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;StartCoroutine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;CameraFOVChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speedAmount&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IEnumerator&lt;/span&gt; &lt;span class="nf"&gt;CameraFOVChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;speedAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Store the initial FOV before starting the interpolation&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;startFOV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FieldOfView&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// // Calculate target FOV and clamp it to prevent visual artifacts or unexpected behavior&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;targetFOV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mathf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startFOV&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;speedAmount&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;zoomSpeedModifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minFOV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxFOV&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;elapsedTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsedTime&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;zoomDuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;elapsedTime&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Calculate the normalized time (0 to 1) &lt;/span&gt;
            &lt;span class="c1"&gt;// by dividing elapsed time by total duration&lt;/span&gt;
            &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;elapsedTime&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;zoomDuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;_camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FieldOfView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mathf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Lerp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startFOV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetFOV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;// Pause execution and resume on the next Update cycle&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Ensure the camera snaps precisely to the target FOV&lt;/span&gt;
        &lt;span class="c1"&gt;// avoiding float inaccuracies&lt;/span&gt;
        &lt;span class="n"&gt;_camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FieldOfView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;targetFOV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_zoomCoroutine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;The &lt;em&gt;Coroutine + While Loop + Mathf.Lerp&lt;/em&gt; combination is a great pattern for implementing any smooth time change in &lt;strong&gt;Unity&lt;/strong&gt;. Once you understand how the time step t is calculated, you can use this pattern for anything from smooth platform movement to dissolving objects upon destruction.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Unity Physics Material</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:34:56 +0000</pubDate>
      <link>https://dev.to/eliozashvili/unity-physics-material-5ab4</link>
      <guid>https://dev.to/eliozashvili/unity-physics-material-5ab4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The Physics Material is a material asset that you can place on a GameObject. The material defines properties on the collider’s surface, such as friction and bounciness.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To create &lt;em&gt;Physics Material&lt;/em&gt;, simply right-click in &lt;em&gt;Project&lt;/em&gt; folder, hover over &lt;em&gt;Create&lt;/em&gt;, and at the bottom, you will see &lt;em&gt;Physics Material&lt;/em&gt;. After creating it, you need to drag and drop the material to &lt;em&gt;GameObject&lt;/em&gt; of your choice that has a collider on it.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: If collider has no &lt;strong&gt;Physics Material&lt;/strong&gt; on it &lt;strong&gt;(None (Physics Material))&lt;/strong&gt; then it has &lt;strong&gt;default **&lt;/strong&gt;Physics Material** properties.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flf8piez5p5hlpmo8cm01.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flf8piez5p5hlpmo8cm01.webp" alt="physical material properties" width="390" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Friction&lt;/strong&gt;: works when an object is already in motion on a surface and determines how fast it will stop sliding. Value of 0 will make an object act like a hockey puck, and value of 1 will make an object stop sliding almost immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static Friction&lt;/strong&gt;: is the frictional force that must be overcome to move a stationary object. Imagine a wardrobe that you must move. It is always harder to move a heavy wardrobe from its place (high static friction) than to push it further once it has already moved (dynamic friction). Value of 0 means that any smallest push will make an object move.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bounciness&lt;/strong&gt;: is pretty straightforward. This option determines how well an object will bounce. Value of 0 means an object has no bouncing properties, imagine you drop a block of wet cheese, while value of 1 is a perfect bouncing ball.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Friction Combine&lt;/strong&gt;: determines the friction between two touching surfaces.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Average&lt;/strong&gt;: arithmetic mean between two objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimum&lt;/strong&gt;: picks the lowest value of two objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maximum&lt;/strong&gt;: pick the highest value of two objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiply&lt;/strong&gt;: values are multiplied by each other.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bounce Combine&lt;/strong&gt;: works the same way as Friction Combine, but with bounciness and has the same dropdown options.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Quaternion.LookRotation &amp; Vector subtraction</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:29:51 +0000</pubDate>
      <link>https://dev.to/eliozashvili/quaternionlookrotation-vector-subtraction-4l4i</link>
      <guid>https://dev.to/eliozashvili/quaternionlookrotation-vector-subtraction-4l4i</guid>
      <description>&lt;p&gt;Today, we talk about two very interesting features, vector subtraction and &lt;em&gt;Quaternion.LookRotation&lt;/em&gt; on a missile example.&lt;/p&gt;

&lt;p&gt;First, we need to find a direction from point A to point B, and we do this by subtracting two Vectors:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Direction = Target position — Missile position&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, we translate the direction into rotation. In &lt;strong&gt;Unity&lt;/strong&gt; we use &lt;em&gt;Quaternions&lt;/em&gt; to handle rotations.&lt;/p&gt;

&lt;p&gt;There is a good method that Unity offers named &lt;em&gt;Quaternion.LookRotation()&lt;/em&gt;. It takes direction and calculates rotation around Y axis (which is sky by default).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AimMissile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;missile&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;missiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Distance/Direction between missile and target&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;shootingDistance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;missile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Quaternion.LookRotation takes shootingDistance Vector3&lt;/span&gt;
        &lt;span class="c1"&gt;// to rotate missile in target's direction&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;missileRotationToTarget&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Quaternion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LookRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shootingDistance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// And finally update missile transform&lt;/span&gt;
        &lt;span class="n"&gt;missile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;missileRotationToTarget&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, every frame, this method executes a loop throughout missiles array. Every missile individually calculates its own and target coordinates. &lt;em&gt;LookRotation&lt;/em&gt; understands the angle at which to rotate each missile. As a result, even if the target shifts to the side, all missiles synchronously adjust their course in flight.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Mathf.Exp()</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:27:22 +0000</pubDate>
      <link>https://dev.to/eliozashvili/mathfexp-5b5e</link>
      <guid>https://dev.to/eliozashvili/mathfexp-5b5e</guid>
      <description>&lt;p&gt;I am making a rail game where you pilot a cosmic ship and shoot down enemies, and I wanted to share what I used for smooth rotation.&lt;/p&gt;

&lt;p&gt;I want to tell how &lt;em&gt;Mathf.Exp()&lt;/em&gt; works in Unity. To make my ship tilt left or right smoothly, there are many options, but I stopped at &lt;em&gt;Mathf.Exp()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;First of all, what is &lt;em&gt;Mathf.Exp()&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;It is a mathematical exponent, an &lt;em&gt;Euler number (e)&lt;/em&gt;, which ≈ 2.713. The &lt;em&gt;Mathf.Exp()&lt;/em&gt; method takes a power in parentheses and raises Euler’s constant to that same power.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ProcessRotation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pitch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;controlRotationFactor&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;_movement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;controlRotationFactor&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;_movement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;targetRotation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Quaternion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Euler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localRotation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Quaternion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Lerp&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localRotation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;targetRotation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="m"&gt;1f&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Mathf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exp&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="n"&gt;rotationSpeed&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the code that calculates tilts. Let’s dive into it a little.&lt;/p&gt;

&lt;p&gt;For example, we have a powerful PC which generates high FPS, because of that Time.deltaTime will be a very small number. Let’s use some numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rotation speed = 20&lt;/li&gt;
&lt;li&gt;Time.deltaTime = 0.02 (roughly 50 FPS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Mathf.Exp(-20 * 0.02) = -0.4&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This means &lt;em&gt;Euler’s number (e)&lt;/em&gt; is raised to a negative exponent. Raising e to a negative power produces a small value. In this case, &lt;em&gt;Mathf.Exp(-0.4)&lt;/em&gt; returns a small number, which is then subtracted from &lt;em&gt;1f&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now, how does this work in Update method:&lt;/p&gt;




&lt;p&gt;Before I tell you this, a few words about &lt;em&gt;Lerp&lt;/em&gt; method. Since we use &lt;em&gt;Quaternion.Lerp&lt;/em&gt;, I will talk about that one exactly. So &lt;em&gt;Lerp&lt;/em&gt; receives 3 arguments: start unit, end unit and interpolation ratio. Imagine this interpolation ratio as a loading bar where 0 is literally 0%, and 1 is 100%&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a - Start unit quaternion value, returned when t = 0&lt;br&gt;
b - End unit quaternion value, returned when t = 1&lt;br&gt;
t - Interpolation ratio. The value is clamped to the range [0, 1]&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;In a first frame, when you press button to tilt your ship, for example, to the left, &lt;em&gt;Mathf.Exp&lt;/em&gt; returns a value (0.67 in our case) which is subtracted from &lt;em&gt;1f&lt;/em&gt;, and we get &lt;em&gt;0.33&lt;/em&gt; as a result. This means that in one frame, the ship is tilted by 33% already, because first result is the highest. On the next frame, we take the same 33% but from the remainder, here is a breakdown:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1f&lt;br&gt;
1f — 0.33 = 0.67&lt;br&gt;
Now we take 33% of 0.67, which is 0.22&lt;br&gt;
0.67 — 0.22 = 0.45&lt;br&gt;
etc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With every new frame we receive a smaller number, and our eye sees this like this: ship is tilted on the left and slows down the tilt speed when it reaches the target angle. In math, this is an infinite process, but in &lt;strong&gt;Unity&lt;/strong&gt; numbers are eventually rounded to almost 0.&lt;/p&gt;

&lt;p&gt;Using &lt;em&gt;1f&lt;/em&gt; — &lt;em&gt;Mathf.Exp(-speedRotation * Time.deltaTime)&lt;/em&gt; inside &lt;em&gt;Quaternion.Lerp&lt;/em&gt;, we get the ideal wing pitch. The higher the player’s FPS, the smaller the steps, but the resulting smoothness and turn time will be the same whether on a low-end laptop or a powerful gaming PC.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Oscillation, Mathf.PingPong, Vector3.Lerp</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:17:46 +0000</pubDate>
      <link>https://dev.to/eliozashvili/oscillation-mathfpingpong-vector3lerp-38g2</link>
      <guid>https://dev.to/eliozashvili/oscillation-mathfpingpong-vector3lerp-38g2</guid>
      <description>&lt;p&gt;When you develop a game at the beginning of your journey, you quickly notice that the environment is very static. To change that, let’s create some oscillations and move our platforms.&lt;/p&gt;

&lt;p&gt;We will use methods like &lt;em&gt;Vector3.Lerp&lt;/em&gt; and &lt;em&gt;Mathf.PingPong&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Oscillate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Vector3&lt;/span&gt; &lt;span class="n"&gt;movementVector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Vector3&lt;/span&gt; &lt;span class="n"&gt;_startPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Vector3&lt;/span&gt; &lt;span class="n"&gt;_endPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;_movementFactor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_startPosition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_endPosition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;movementVector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_movementFactor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mathf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PingPong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Lerp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_startPosition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_endPosition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_movementFactor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we add &lt;em&gt;movementVector&lt;/em&gt; and speed to inspector. &lt;em&gt;movementVector&lt;/em&gt; receives the direction, and we tell it how far object must move. &lt;em&gt;speed&lt;/em&gt; defines how fast objects move&lt;/p&gt;

&lt;p&gt;_&lt;em&gt;startPosition&lt;/em&gt; simply gets the starting coordinates in &lt;em&gt;Start()&lt;/em&gt; with &lt;em&gt;transform.position&lt;/em&gt;, and _&lt;em&gt;endPosition&lt;/em&gt; is the sum of the _&lt;em&gt;startPosition&lt;/em&gt; and &lt;em&gt;movementVector&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now _&lt;em&gt;movementFactor&lt;/em&gt; is different. It defines the progress of the movement. Imagine it as a loading bar from 1% to 100%. In &lt;em&gt;Update()&lt;/em&gt; method, we constantly calculate it every frame using &lt;em&gt;Mathf.PingPong&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;_movementFactor = Mathf.PingPong(Time.time * speed, 1f);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So what is going on here, &lt;em&gt;Mathf.PingPong&lt;/em&gt; does what you would imagine. Value goes back and forth. &lt;em&gt;1f&lt;/em&gt; is our length, so in &lt;em&gt;Update()&lt;/em&gt; every frame _&lt;em&gt;movementFactor&lt;/em&gt; is getting updated from 0.0, 0.1, 0.2… up to 1.0, when the value is 1.0, it goes back to 0.0 in the same way. And since it’s in &lt;em&gt;Update()&lt;/em&gt; this is an endless cycle.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;transform.position = Vector3.Lerp(_startPosition, _endPosition, _movementFactor);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now this is where magic happens. &lt;em&gt;Lerp (Linear Interpolation)&lt;/em&gt; takes start position, end position and a “loading” bar. Why we did exactly &lt;em&gt;1f&lt;/em&gt; in PingPong is perfectly described in the official documentation:&lt;/p&gt;

&lt;p&gt;a — _&lt;em&gt;startPosition&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;b — _&lt;em&gt;endPosition&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Value used to interpolate between a and b. Values greater than one are clamped to 1. Values less than zero are clamped to 0.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In short, &lt;em&gt;Lerp&lt;/em&gt; is teleporting an object by a very short amount, and to our eyes, it looks like smooth movement.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>How to add Sounds and Particles in Unity</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:11:41 +0000</pubDate>
      <link>https://dev.to/eliozashvili/how-to-add-sounds-and-particles-in-unity-3ngk</link>
      <guid>https://dev.to/eliozashvili/how-to-add-sounds-and-particles-in-unity-3ngk</guid>
      <description>&lt;p&gt;There is no game without sounds and particle effects, this is what gives life, along with other details, to our game.&lt;/p&gt;




&lt;p&gt;How to add Sounds to our project:&lt;/p&gt;

&lt;p&gt;First of all, &lt;em&gt;Audio Source&lt;/em&gt; component has to be added to &lt;em&gt;GameObject&lt;/em&gt;, then we create a folder where my sounds will be placed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq4mzici3hbszbx5g6dtm.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq4mzici3hbszbx5g6dtm.webp" alt=" " width="686" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Afterwards, we declare &lt;strong&gt;[SerializeField]&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[SerializeField] private AudioClip engineThrustSfx;
[SerializeField] private AudioClip crashSfx;
[SerializeField] private AudioClip successSfx;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we drag and drop our sounds to newly created &lt;strong&gt;[SerializeField]&lt;/strong&gt; in &lt;strong&gt;Unity Inspector&lt;/strong&gt;, which were declared in &lt;em&gt;Movement.cs&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8399dnp6zy3xoa6ce24a.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8399dnp6zy3xoa6ce24a.webp" alt=" " width="474" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We write code where we do Caching, so we receive Audio Source reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private AudioSource _audioSource;

private void Start()
    {
        _audioSource =  GetComponent&amp;lt;AudioSource&amp;gt;();
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we are good to go. From now on, we can easily implement our sound effects using logic. For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;_audioSource.PlayOneShot(audioClip)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We call &lt;em&gt;PlayOneShot&lt;/em&gt; (this method plays sound once) method on _&lt;em&gt;audioSource&lt;/em&gt; and pass &lt;em&gt;audioClip&lt;/em&gt; custom argument, which contains the sound. Or this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void OnCollisionEnter(Collision collision)
    {
        switch (collision.gameObject.tag)
        {
            case "Friendly":
                break;
            case "Finish":
                StartCoroutine(HandleAfterDelay(isNext: true, reload: false, audioClip: successSfx));
                successParticle.Play();
                break;
            default:
                StartCoroutine(HandleAfterDelay(isNext: false, reload: true, audioClip: crashSfx));
                crashParticle.Play();
                break;
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down case &lt;strong&gt;“Finish”&lt;/strong&gt;. When a collision happens, and &lt;em&gt;collision.gameObject.tag&lt;/em&gt; is &lt;strong&gt;“Finish”&lt;/strong&gt; this means in my game Player landed on the landing pad, and it’s time to load next scene, which happens with a coroutine. We pass a bunch of arguments and pass sound (which is declared above). Then we handle it in &lt;em&gt;IEnumerator&lt;/em&gt; function below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private IEnumerator HandleSceneAfterDelay(bool isNext, bool reload, AudioClip audioClip)
    {
        // disabling Movement.cs forces to execute OnDisable method
        _movementScript.enabled = false;
        _rb.isKinematic = true;

        if (audioClip)
            _audioSource.PlayOneShot(audioClip);

        yield return _delay;
        Helpers.HandleScene(isNext, reload);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So if audioClip is not &lt;em&gt;null&lt;/em&gt;, play the sound when the scene is handled.&lt;/p&gt;




&lt;p&gt;This process is similar to adding Sounds. Here is a screenshot of my Player Object:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0zrrw95f349hmma04uxo.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0zrrw95f349hmma04uxo.webp" alt=" " width="181" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rick’s Weird Space Rocket&lt;/strong&gt; was provided to me from Udemy course I follow. So here we have 5 particles. Explosion executes when Player hits Obstacle, Success when we safely land on the landing pad, and the rest are thrusters. All of those particles have this Component named &lt;strong&gt;Particle System&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzg4v2bcl5qscrnn3fbkj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzg4v2bcl5qscrnn3fbkj.webp" alt=" " width="457" height="827"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is quite large system that allows you to create custom particles right in &lt;strong&gt;Unity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rest of the implementation is same as in Sounds. You declare variables, drag and drop them, and use them in your logic. They even share same methods like &lt;em&gt;Play()&lt;/em&gt; or &lt;em&gt;Stop()&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Quaternion Rotation</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 09:04:25 +0000</pubDate>
      <link>https://dev.to/eliozashvili/quaternion-rotation-oi4</link>
      <guid>https://dev.to/eliozashvili/quaternion-rotation-oi4</guid>
      <description>&lt;p&gt;When you work with physics in &lt;strong&gt;Unity&lt;/strong&gt; you will eventually stumble upon &lt;em&gt;Quaternion&lt;/em&gt; class. Official Unity documentation states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quaternions are used to represent rotations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But behind those simple words lies a complex mathematical structure based on numbers and four-dimensional space (X, Y, Z, W).&lt;/p&gt;




&lt;p&gt;First, let me tell you how I found out about Quaternions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5c5wv9lvg4i5m1tq8vuc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5c5wv9lvg4i5m1tq8vuc.webp" alt=" " width="443" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those orange objects are spinning. When spinners are hitting player (red ball), most of the time player goes through objects, which is not what I wanted. The reason was that spinners had no &lt;em&gt;Rigidbody&lt;/em&gt;, hence no physics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void Update()
    {
        transform.Rotate(spinX, spinY, spinZ);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had this simple &lt;em&gt;transform.Rotate&lt;/em&gt; line. I found out that my issue is called &lt;strong&gt;Tunneling&lt;/strong&gt;. Spinner was rotating too fast and since Unity is not checking collision every second, but every frame, the following bug happened:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First frame: Spinner was in front of the ball&lt;/li&gt;
&lt;li&gt;Second frame: Spinner was behind the ball&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To fix this issue, I was introduced to Quaternions.&lt;/p&gt;




&lt;p&gt;Now back to &lt;em&gt;Quaternions&lt;/em&gt;. Humans easily understand &lt;em&gt;Euler Angles&lt;/em&gt;, that is, our beloved X, Y and Z. We can see them in &lt;strong&gt;Unity Inspector&lt;/strong&gt;. But &lt;em&gt;Euler Angles&lt;/em&gt; can have a bug called &lt;strong&gt;Gimbal Lock&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Imagine you have three rings nested inside each other like a gyroscope:&lt;/p&gt;

&lt;p&gt;The outer ring rotates the object left and right (Y axis).&lt;/p&gt;

&lt;p&gt;The middle ring rotates the object up and down (X axis).&lt;/p&gt;

&lt;p&gt;The inner ring tilts the object sideways (Z axis).&lt;/p&gt;

&lt;p&gt;They work in turns, dependent on each other. If you rotate the middle ring exactly 90 degrees, the inner ring will rotate and align itself with the outer ring.&lt;/p&gt;

&lt;p&gt;At this point, the Z and Y axes are aligned. Now, whether you rotate the outer ring or the inner ring, the object performs exactly the same motion. However, it can no longer physically rotate in the third direction, it’s stuck. To regain the lost axis, you’ll first have to rotate the middle ring back.&lt;/p&gt;

&lt;p&gt;For a human in real life, this isn’t a problem, but for &lt;strong&gt;Unity&lt;/strong&gt;’s Euler angle math, it’s a disaster, physics breaks and loses control of one of the axes, and the object starts to jerk wildly or rotate in the wrong direction.&lt;/p&gt;

&lt;p&gt;To resolve this issue, &lt;em&gt;Quaternions&lt;/em&gt; were introduced. A &lt;em&gt;Quaternion&lt;/em&gt; describes a rotation not as a sequence of three separate rotations, but as a simultaneous calculation of a direction vector and the angle of rotation around it using four components: X, Y, Z, and W.&lt;/p&gt;

&lt;p&gt;Because of a four-dimensional structure, Quaternions have three advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No Gimbal Lock&lt;/strong&gt; — object will rotate freely and correctly in any direction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effective Interpolation&lt;/strong&gt; — methods like Quaternion.Slerp allows calculating smooth transitions from one rotation to another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; — &lt;em&gt;Quaternions&lt;/em&gt; allow the physics engine to work faster and better.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;To fix the issue I wrote this line of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rigidbody rigidBody;

    void Awake()
    {
        rigidBody = GetComponent&amp;lt;Rigidbody&amp;gt;();
    }

    void FixedUpdate()
    {
        Quaternion deltaRotation = Quaternion.Euler(
            new Vector3(rotateX, rotateY, rotateZ) * Time.fixedDeltaTime
        );
        rigidBody.MoveRotation(rigidBody.rotation * deltaRotation);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we work with physics, we use &lt;em&gt;FixedUpdate&lt;/em&gt; and &lt;em&gt;Time.fixedDeltaTime&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There we call &lt;em&gt;Quaternion&lt;/em&gt; class and use its static method to pass three dimentional vector, and multiply it by &lt;em&gt;Time.fixedDeltaTime&lt;/em&gt;. Eventually, we call &lt;em&gt;MoveRotation&lt;/em&gt; on our &lt;em&gt;Rigidbody&lt;/em&gt; and rotate our object.&lt;/p&gt;

&lt;p&gt;You might ask what’s the difference between &lt;em&gt;Time.deltaTime&lt;/em&gt; and &lt;em&gt;Time.fixedDeltaTime&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Time.deltaTime&lt;/em&gt; — is the difference between normal frames in Update function. It always changes based on PC stats. Stronger the PC, less time there is between frames.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Time.fixedDeltaTime&lt;/em&gt; — its fixed time between frames in &lt;em&gt;FixedUpdate&lt;/em&gt; function. In &lt;strong&gt;Unity&lt;/strong&gt; it is always the same, 0.02 seconds.&lt;/p&gt;

&lt;p&gt;In a nutshell, &lt;em&gt;deltaTime&lt;/em&gt; is dependent on game lag and FPS, while &lt;em&gt;fixedDeltaTime&lt;/em&gt; is a stable, perfectly smooth metronome for the physics engine.&lt;/p&gt;




&lt;p&gt;So let's just not manually intervene with &lt;em&gt;Quaternions&lt;/em&gt; and just use given static methods of &lt;em&gt;Quaternion&lt;/em&gt; class that are basically our translators.&lt;/p&gt;




&lt;p&gt;To sum up, &lt;em&gt;Quaternions&lt;/em&gt; in &lt;strong&gt;Unity&lt;/strong&gt; are not a replacement for Euler angles, but rather a safe internal implementation of them. We write code and configure the inspector in degrees, but the engine converts them into &lt;em&gt;Quaternions&lt;/em&gt; behind the scenes, so that collisions, triggers, etc., are processed smoothly, quickly, and without errors.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Data transfer between script files</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 08:54:28 +0000</pubDate>
      <link>https://dev.to/eliozashvili/data-transfer-between-script-files-3lp5</link>
      <guid>https://dev.to/eliozashvili/data-transfer-between-script-files-3lp5</guid>
      <description>&lt;p&gt;I learned quite a lot today to compensate for the days that I had to drop. The most important piece of knowledge that I have gathered today is how to transfer data between script files. Let’s get to it:&lt;/p&gt;

&lt;p&gt;So the problem was that I needed to catch the Player’s position when he set foot on a trigger that activates a small trap, which shoots hazardous balls at the Player’s coordinates.&lt;/p&gt;

&lt;p&gt;I have two script files. One for a trigger object and the other for flying projectiles.&lt;/p&gt;

&lt;p&gt;So at first, in a script file (file name: &lt;strong&gt;ProjectileTowardsPlayer.cs&lt;/strong&gt;) for flying projectiles, I made a public function that receives the Player's position when the trigger is activated (which happens in &lt;strong&gt;TriggerProjectiles.cs&lt;/strong&gt;) and assigns its value to an already declared variable &lt;em&gt;playerPosition&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PlayerPosAtTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Vector3&lt;/span&gt; &lt;span class="n"&gt;playerPos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;playerPosition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;playerPos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, how does this function’s parameter catches players position, you may ask? And I shall grant you an answer! :D&lt;/p&gt;

&lt;p&gt;Here is my piece of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnTriggerEnter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Collider&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompareTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Player"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;projectiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectiles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;projectileScript&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;projectiles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProjectileTowardsPlayer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;projectileScript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PlayerPosAtTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                    &lt;span class="n"&gt;projectiles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;SetActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, don’t think much about the logic of this. In short, it loops through projectiles, checks if the array value is &lt;em&gt;null&lt;/em&gt; or not, and sets them active (because at the start, they are inactive). Now, &lt;em&gt;projectileScript&lt;/em&gt; variable is where magic happens. When Player triggers the trap, we iterate through projectiles and set them active, and catch Player’s coordinates for &lt;strong&gt;TriggerProjectiles.cs&lt;/strong&gt; using:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;var projectileScript = projectiles[i].GetComponent&amp;lt;ProjectileTowardsPlayer&amp;gt;();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and then calling a &lt;em&gt;PlayerPosAtTrigger()&lt;/em&gt; function and passing an argument to it&lt;/p&gt;

&lt;p&gt;&lt;code&gt;projectileScript.PlayerPosAtTrigger(other.transform.position);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;so &lt;strong&gt;TriggerProjectiles.cs&lt;/strong&gt; will know Player’s coordinates, and trap will shoot at the correct location.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Unity Time.deltaTime</title>
      <dc:creator>Giorgi Eliozashvili</dc:creator>
      <pubDate>Sun, 19 Jul 2026 08:47:08 +0000</pubDate>
      <link>https://dev.to/eliozashvili/unity-timedeltatime-2cj4</link>
      <guid>https://dev.to/eliozashvili/unity-timedeltatime-2cj4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The interval in seconds from the last frame to the current one&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's break it down a little:&lt;/p&gt;

&lt;p&gt;You and your friend want to play a racing game, your PC is powerful and has 100 FPS in that said racing game, meanwhile, your friend has a slow PC, and it can only generate 10 FPS. What does this mean? This means that if a car is moving 1 meter per frame, your car will end up being way further, covering 100 meters per second because you have 100 FPS, while your friend's car will only cover 10 meters in a second because his PC is slower and generates only 10 FPS. &lt;strong&gt;!!! UNFAIR !!!&lt;/strong&gt; You say, and you will be right. To make cars travel the same distance on both machines, you need &lt;em&gt;Time.deltaTime&lt;/em&gt;. Now, how does it work? &lt;em&gt;Time.deltaTime&lt;/em&gt; measures exactly how much time passed since the very last frame. On your PC, the time between frames is very short, 0.01 seconds, on your friend's PC, the time between frames is longer, 0.1 seconds. When you multiply your speed by &lt;em&gt;Time.deltaTime&lt;/em&gt; &lt;strong&gt;Unity&lt;/strong&gt; adjusts the movement to that time gap. So speaking easily, your computer does 100 tiny steps while your friend's computer does 10 bigger steps, but eventually you both move with the same speed.&lt;/p&gt;

&lt;p&gt;How &lt;em&gt;Time.deltaTime&lt;/em&gt; is calculated:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Time.deltaTime = Timestamp of Current Frame - Timestamp of Last Frame.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Explanation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frame 1 finished:&lt;/strong&gt; &lt;strong&gt;Unity&lt;/strong&gt; records time (e.g. 10.00 sec)&lt;br&gt;
&lt;strong&gt;Frame 2 finished:&lt;/strong&gt; &lt;strong&gt;Unity&lt;/strong&gt; records time again (e.g. 10.03 sec)&lt;br&gt;
&lt;strong&gt;Calculation:&lt;/strong&gt; &lt;strong&gt;Unity&lt;/strong&gt; subtracts 10.00 from 10.03 and gets 0.03, and assigns that value to &lt;em&gt;Time.deltaTime&lt;/em&gt; &lt;em&gt;(Time.deltaTime = 10.03 - 10.00)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;The Update:&lt;/strong&gt; **Unity **opens your &lt;em&gt;Update()&lt;/em&gt; method and passes that 0.03 value into your movement calculation&lt;/p&gt;

&lt;p&gt;Here is a small piece of code as an example of how to use &lt;em&gt;Time.deltaTime&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAxis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Horizontal"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deltaTime&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;moveSpeed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we do here is we declare a floating variable, and we assign X coordinates. To make our character move, we need to multiply X by moveSpeed and &lt;em&gt;Time.deltaTime&lt;/em&gt;. So now our character moves on the X coordinate with the same speed on every machine.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
