<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The Yandemil Chronicles]]></title><description><![CDATA[Game dev is full of thinking: What's the idea? How do we bring it to life? Here's my thought process—ideas, solutions, reflections. Welcome to the world behind ]]></description><link>https://yandemil.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 19 Sep 2026 18:50:19 GMT</lastBuildDate><atom:link href="https://yandemil.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Möbel Finder: Mastering 3D Drag-and-Drop in Unity]]></title><description><![CDATA[I. Introduction and Project Overview
1.1 Technical Specifications
Engine: UnityLanguage: C#Platform: PCTeam Size: Solo PlayDevelopment Time: 3 daysStatus: Published
1.2 Game Definition and Mechanics
M]]></description><link>https://yandemil.hashnode.dev/mobel-finder-mastering-3d-drag-and-drop-in-unity-part-1-2</link><guid isPermaLink="true">https://yandemil.hashnode.dev/mobel-finder-mastering-3d-drag-and-drop-in-unity-part-1-2</guid><category><![CDATA[gameplay]]></category><category><![CDATA[puzzle]]></category><category><![CDATA[indiedev]]></category><category><![CDATA[unity]]></category><category><![CDATA[Drag & Drop]]></category><category><![CDATA[GameDev]]></category><category><![CDATA[C#]]></category><category><![CDATA[Time management]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Yann K.]]></dc:creator><pubDate>Fri, 27 Jun 2025 15:14:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750796517548/e0e41fe5-cf64-4f3e-a41b-1bf2bdd748a2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>I. Introduction and Project Overview</h1>
<h2>1.1 Technical Specifications</h2>
<p><strong>Engine:</strong> Unity<br /><strong>Language:</strong> C#<br /><strong>Platform:</strong> PC<br /><strong>Team Size:</strong> Solo Play<br /><strong>Development Time:</strong> 3 days<br /><strong>Status:</strong> Published</p>
<h2>1.2 Game Definition and Mechanics</h2>
<p><strong>Möbel Finder</strong> (German for "Furniture Finder") combines the fun of a casual puzzle with the stress of time-management. It's a light but strategic game that tests players' accuracy and speed as they drag and drop furniture into the right places in a race against the clock.</p>
<p>Each level starts with the furniture in the same spot. As a player you get a time limit and a list of the furnitures to move. When you drag a piece of furniture from the list to its corresponding spot, it disappears from the list. If you put something in the wrong spot, it just goes back to its initial place.</p>
<hr />
<h2>II. Research Focus and Objectives</h2>
<h3>2.1 Primary Developments Goals</h3>
<p><strong>Focus of Development:</strong> Fundamental Drag-and-Drop System</p>
<p><strong>Main Goals</strong></p>
<ul>
<li><p>Make dragging and dropping items easy for gameplay.</p>
</li>
<li><p>Ensure accurate placement with clear visual feedback.</p>
</li>
<li><p>Add a timer to create some time pressure for success or failure.</p>
</li>
</ul>
<h3>2.2 Achievement Summary</h3>
<p><strong>What I Accomplished</strong></p>
<ul>
<li><p>Incorporated reliable 3D object selection and dragging</p>
</li>
<li><p>Created a plane-constrained mobility system</p>
</li>
<li><p>Solved cursor-speed drag interruption issues</p>
</li>
<li><p>Added visual highlighting for interactive objects</p>
</li>
<li><p>Drop zone validation (basic implementation complete)</p>
</li>
</ul>
<hr />
<h2><strong>III. Technical Implementation and Analysis</strong></h2>
<h3>3.1 Core Challenge Definition</h3>
<p>I had to try different methods to overcome some technical obstacles to create a responsive, smooth drag-and-drop mechanic in 3D space.</p>
<h3>3.2 Object Selection &amp; Hover Feedback System</h3>
<p><strong>3.2.1 Raycast Detection Implementation</strong></p>
<p><strong>Raycast Detection:</strong></p>
<pre><code class="language-csharp">// Detect furniture objects under cursor
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, _rayDistance, _furnitureMask)) 
{
    _selectedGO = hit.collider.gameObject;
    _renderer = _selectedGO.GetComponent&lt;MeshRenderer&gt;();
}
</code></pre>
<ul>
<li><strong>Purpose:</strong> It checks for furniture items under the cursor with a layer mask called <code>_furnitureMask</code>.</li>
</ul>
<p><strong>3.2.2 Visual Highlight System</strong></p>
<p>It changes the object (and its child objects) to red when you hover over it.</p>
<pre><code class="language-csharp">// Highlight selected object and children
_renderer.material.color = Color.red;
foreach (Transform child in selectedObject.transform)
{
    var childRenderer = child.GetComponent&lt;MeshRenderer&gt;();
    if (childRenderer != null)
        childRenderer.material.color = Color.red;
}
</code></pre>
<ul>
<li><strong>Note:</strong> It uses a list (<code>_listRenderer</code>) to cache child renderers for better performance.</li>
</ul>
<hr />
<h3>3.3 Drag Mechanics: Itereative Development Process</h3>
<p><strong>3.3.1 Approach 1: Dual-Raycast Drag-system</strong></p>
<p>In this implementation, two separate raycasts team up:</p>
<p><strong>Furniture Selection Raycast</strong></p>
<ul>
<li><p>Layer Mask: <code>_furnitureMask</code></p>
</li>
<li><p><strong>Purpose:</strong> It identifies which furniture object is being interacted with</p>
<pre><code class="language-csharp">if (Physics.Raycast(ray, out hit, _rayDistance, _furnitureMask)) {
    // Highlight and select furniture object
    _selectedGO = hit.collider.gameObject;
}
</code></pre>
</li>
</ul>
<p><strong>Ground Positioning Raycast</strong></p>
<ul>
<li><p>Layer Mask: <code>_groundMask</code></p>
</li>
<li><p><strong>Purpose:</strong> It determines where to place the selected furniture and updates position during drag</p>
<pre><code class="language-csharp">        _dragOffset = _selectedGO.transform.position - groundHit.point;
        _selectedGO.transform.position = hit.point + _dragOffset;
</code></pre>
</li>
</ul>
<p><strong>Why Two Separate Raycasts?</strong></p>
<ul>
<li><p>The furniture raycast handles <em><strong>selection</strong></em> (what you're dragging)</p>
</li>
<li><p>The ground raycast handles <em><strong>placement</strong></em> (where you're dragging it to)</p>
</li>
<li><p>The offset helps keep the object at the right height above the ground</p>
</li>
</ul>
<p><strong>3.3.2 Problem Identification: Drag Interruption</strong></p>
<p>When you are smoothly guiding your mouse across the screen, everything is great until you speed up a bit. The object that you were moving stops suddenly, even though you're still holding the mouse button down. Any idea why?</p>
<p><mark>It's the raycast</mark>. You see, when you drag slowly, the raycast keeps hitting the object, which is good. However, if you move too fast, the cursor flies off the object so fast that the system can’t keep up. The raycast loses track of it, and your drag stops, even though you're still holding down the mouse button.</p>
<pre><code class="language-plaintext">[MOUSE MOVEMENT]the selection depended on constantly detecting the hover, even when you were actively dragging
     ↓
[Furniture Raycast] → Selects object (what to move)
     ↓
[Ground Raycast]    → Finds placement point (where to move)
     ↓
[Offset Calculation] → Maintains consistent height
</code></pre>
<p><strong>3.3.3 Approach 2: Screen-to-World Conversion</strong></p>
<p>Here i try to fix the dragging issue by keeping the object's depth while tracking the mouse mouvement.</p>
<p><strong>The problem:</strong></p>
<p>To drag a 3D object, the mouse position must be changed from the 2D back into 3D space. The catch is that the mouse only shows X and Y coordinates, which means that there's nothing for depth.</p>
<p><strong>The solution:</strong></p>
<p>a. <em><mark>Get the Depth First:</mark></em> When selecting an object, it is checked how far the object is from the camera, mainly the Z-position.</p>
<p>b. <em><mark>Save the new position:</mark></em> A new position is created using the mouse's current X and Y coordinates along with the previous saved depth.</p>
<p>c. <em><mark>Conversion to 3D:</mark></em> The new position is converted back into actual 3D world coordinates. Finally the object can be moved.</p>
<pre><code class="language-csharp">Vector3 screenPosition = new Vector3(
    Input.mousePosition.x,      // Mouse X
    Input.mousePosition.y,      // Mouse Y
    Camera.main.WorldToScreenPoint(_selectedGO.transform.position).z // Object's current depth                                   
);
// Convert back to world space
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(screenPosition); 
_selectedGO.transform.position = worldPosition; // Move the object
</code></pre>
<p><strong>Limitation:</strong> Keeping the object's original depth on the screen helps it stay in the same place compared to the camera as it moves with your mouse.</p>
<p><strong>!!! This method works well, but it doesn't fit this game because players need to move the object along the ground, not through it.</strong></p>
<p>Right now, the code keeps the object at a set distance from the camera, making it move on an unseen plane that's parallel to the camera. In this game, though, the objects should slide along the ground surface and stay in contact with it instead of floating at a fixed distance.</p>
<p><strong>3.3.4 Final Solution: Plane-Constrained Dragging</strong></p>
<p><strong>The Breakthrough: Using</strong> <code>Plane.Raycast</code> <strong>for reliable, consistent movement</strong></p>
<p>Unity has a Plane class that defines a virtual, infinite plane used for calculations, as it exists only in code. This virtual plane allows for consistent object height without needing a physical ground, keeping the height consistent while preventing objects from dropping or moving unexpectedly. It ensures smooth and precise movement and also consistent control of objects, independent of cursor speed.</p>
<p>Being virtual, the plane can be defined directly in code. Since the ground has a given position, we can save this position and use it to position or align the plane in calculations. The plane's normal (<code>Vector3.up</code>) helps us determine if we're looking at a flat surface. The ray direction is calculated from the camera to the mouse, and a hit occurs only if these directions intersect properly.</p>
<pre><code class="language-csharp">public class FurnitureDragger : MonoBehaviour 
{    
    [SerializeField] private Transform _plane;   // Reference to the plane transform
    [SerializeField] private LayerMask _furnitureMask = 1;

    private Plane _dragPlane;                    // Virtual plane's reference for dragging calculations
    private GameObject _selectedGO;              // Reference to the selected GameObject
    private bool _select = false;                // Boolean to track selection state    
    private Camera _camera;                      // Reference to the camera
    
    void Start()
    {
        _camera = Camera.main;

        // Null checks
        if (_camera == null || _plane == null)
        {
            Debug.LogError("Missing required references!");
            enabled = false;
            return;
        }

        // Initialize the infinite virtual plane at the position of _plane       
        _dragPlane = new Plane(Vector3.up, _plane.transform.position);
    }
    
    void Update()
    {
        HandleDragAndDrop();
    }
    
    private void HandleDragAndDrop()
    {
        // Select object under cursor
        Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, _furnitureMask))
        {
            _selectedGO = hit.collider.gameObject;
            _select = true;
            Cursor.visible = false; // Hide cursor during drag
        }
        
        if (Input.GetMouseButton(0) &amp;&amp; _selectedGO != null)
        {
            Ray rayPlane = Camera.main.ScreenPointToRay(Input.mousePosition);
    
            // Check if the ray intersects with the virtual plane
            if (_dragPlane.Raycast(rayPlane, out float enter))
            {
                _selectedGO.transform.position = rayPlane.GetPoint(enter);
            }
        }
        
        if (Input.GetMouseButtonUp(0))
        {
            _select = false;
            _selectedGO = null;
            Cursor.visible = true;    // Clear selection
        }
    }
}
</code></pre>
<p><strong>Note:</strong> The normal determines which side of the plane is detectable by the raycast.</p>
<p><code>Vector3.up</code> makes the normal face up, so only rays coming from above will hit the plane. If a ray comes from below, it won't cross the plane. <strong>The plane's normal changes with rotation.</strong></p>
<ul>
<li><p><em><strong>Rotation (0, 0, 0)</strong></em> – Horizontal Plane - <em><strong>Nomal:</strong></em> <code>Vector3.up</code></p>
<pre><code class="language-csharp">_dragPlane = new Plane(Vector3.up, _plane.transform.position);
</code></pre>
</li>
<li><p><em><strong>Rotation (90, 0, 0)</strong></em> – Vertical Plane (Wall) - <em><strong>Nomal:</strong></em> <code>Vector3.forward</code></p>
<pre><code class="language-csharp">_dragPlane = new Plane(Vector3.forward, _plane.transform.position);
</code></pre>
</li>
</ul>
<hr />
<h2>IV. Performance Analysis and Optimization</h2>
<h3>4.1 Optimization Results</h3>
<ul>
<li><p><strong>Raycasting:</strong> I only check the furniture layer, which cuts down on extra calculations.</p>
</li>
<li><p><strong>Material Handling:</strong> I keep renderer references handy to skip repeated <code>GetComponent</code> checks.</p>
</li>
<li><p><strong>Update Frequency:</strong> I check for hover only when you’re not dragging anything.</p>
</li>
</ul>
<h3>4.2 Build Statitics and Metrics</h3>
<ul>
<li><p><strong>Target FPS:</strong> 60fps (consistently achieved)</p>
</li>
<li><p><strong>Build Size:</strong> Around 45MB</p>
</li>
<li><p><strong>Memory Usage:</strong> Less than 200MB peak during gameplay</p>
</li>
</ul>
<hr />
<h2>V. Visual Progress</h2>
<h3>Core Mechanics Demonstration</h3>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751036611516/45893b66-be50-49dd-a1d2-a53523e8682b.gif" alt="Moebel Finder - Drag-and-Drop GIF" style="display:block;margin:0 auto" />

<p>Easy dragging of furniture with flat limit rule</p>
<h3>Highlighting System</h3>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751036246483/b8a90c8b-169b-4546-8eef-51be9dbbb94a.gif" alt="Moebel Finder - Highlighting System GIF" style="display:block;margin:0 auto" />

<p>When you hover over it, the furniture turns red.</p>
<h3>UI Integration</h3>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751036082441/9dd38b04-d6bd-4001-a33a-9d9e45b8b1fe.gif" alt="Moebel Finder - UI Integration GIF" style="display:block;margin:0 auto" />

<p>Shows time and score when you match furniture.</p>
<hr />
<h2>VI. Challenges &amp; Solutions Summary</h2>
<h3>6.1 Challenge 1: Drag Interruption</h3>
<ul>
<li><p><strong>Problem:</strong> Quick mouse moves mess with drag tracking</p>
</li>
<li><p><strong>Solution:</strong> A system where selections don't rely on hovering</p>
</li>
<li><p><strong>Result:</strong> Drag works well no matter how fast the mouse is moved</p>
</li>
</ul>
<h3><strong>6.2 Challenge 2: Inconsistent Object Depth</strong></h3>
<ul>
<li><p><strong>Problem:</strong> Items moved at any Z-depths without rules</p>
</li>
<li><p><strong>Solution:</strong> System that keeps movement on a set plane</p>
</li>
<li><p><strong>Result:</strong> Things stay where they should on tables or floors</p>
</li>
</ul>
<h3><strong>6.3 Challenge 3: Performance with Multiple Objects</strong></h3>
<ul>
<li><p><strong>Problem:</strong> Non-stop raycasting for every item</p>
</li>
<li><p><strong>Solution:</strong> Use of layers and chosen detection</p>
</li>
<li><p><strong>Result:</strong> Smooth work even with more than 20 items to interact with</p>
</li>
</ul>
<hr />
<h2>VII. Knowledge Synthesis and Lessons Learned</h2>
<h3>7.1 Technical Insights</h3>
<ol>
<li><p><strong>Plane.Raycast</strong> is good for flat areas and can be use this to note where you touch or click on large flat zones like ground or walls. It’s fast, good, quick, right on point and doesn't make the system slow.</p>
</li>
<li><p><strong>Tracking the Drag State</strong> is the Key for good drag-and-drop as it helps to watch closely if you are hovering, holding, or let go for a sharp drag-and-drop feel.</p>
</li>
<li><p><strong>Using a Layermask</strong> boost the performance as it helps by cutting down on the things Unity has to check, making it run better.</p>
</li>
<li><p><strong>Visual feedback</strong> (highlighting) makes it better for players. When items light up as you interact, it makes the feel of the game much better.</p>
</li>
</ol>
<h3>7.2 Development Process</h3>
<ul>
<li><p><strong>Iterative problem-solving</strong> led to better answers than the first try.</p>
</li>
<li><p><strong>Player feedback</strong> provided valuable information for improvements.</p>
</li>
<li><p><strong>Early performance testing</strong> helped prevent problems that might have required optimization later</p>
</li>
</ul>
<hr />
<h2>VIII. Next Steps : Improving the experience</h2>
<h3>Gameplay Polish</h3>
<ul>
<li><p>Set up a system to check drop zone.</p>
</li>
<li><p>Add some visual cues to show when placements are good.</p>
</li>
<li><p>Create a way for the game to get harder as players progress.</p>
</li>
<li><p>Adjust the difficulty based on what player’s feedback.</p>
</li>
</ul>
<h3>Progression &amp; Replayability</h3>
<ul>
<li>Adjusting difficulty based on what players say</li>
</ul>
<hr />
<h2>IX. Technical Specifications</h2>
<h3>9.1 Development Environment</h3>
<ul>
<li><p><strong>Unity Version:</strong> 2022.3.62f1 LTS</p>
</li>
<li><p><strong>Target Platform:</strong> Windows PC</p>
</li>
<li><p><strong>Physics:</strong> Unity 3D Physics</p>
</li>
</ul>
<h3>9.2 Architecture</h3>
<ul>
<li><p><strong>Design Pattern:</strong> Component-based architecture</p>
</li>
<li><p><strong>Code Organization:</strong> Single-responsibility classes</p>
</li>
<li><p><strong>Asset Management:</strong> Prefab-based furniture system</p>
</li>
</ul>
<hr />
<h2>X. Accessibility and Community Engagement</h2>
<h3><strong>10.1 Distribution Channels</strong></h3>
<p>Try It Yourself</p>
<ul>
<li><p><strong>Itch.io:</strong> <a href="https://yandemil.itch.io/moebel-finder">https://yandemil.itch.io/moebel-finder</a></p>
</li>
<li><p><strong>Source Code:</strong> [Not available at the moment]</p>
</li>
<li><p><strong>Development Log:</strong> [Full Documentation]</p>
</li>
</ul>
<h3>10.2 Feedback Welcome</h3>
<p>I'd love to hear your thoughts on the drag-and-drop mechanics! You can:</p>
<ul>
<li><p>Comment below with your experience</p>
</li>
<li><p>Share suggestions for improvements</p>
</li>
<li><p>Report any bugs or issues encountered</p>
</li>
</ul>
<hr />
<h2>XI. Development Stats</h2>
<ul>
<li><p><strong>Total Development Time:</strong> 7 days</p>
</li>
<li><p><strong>Lines of Code:</strong> ~500</p>
</li>
<li><p><strong>Major Features:</strong> 3 core systems</p>
</li>
<li><p><strong>Bugs Fixed:</strong> 12 during development</p>
</li>
<li><p><strong>Performance Target:</strong> 60fps (achieved)</p>
</li>
</ul>
<h2>Tags</h2>
<p><code>#gamedev</code> <code>#unity</code> <code>#indiedev</code> <code>#dragdrop</code> <code>#puzzle</code> <code>#timemanagement</code> <code>#c#</code> <code>#gameplay</code></p>
<hr />
<p><em><strong>Next week:</strong></em> <em>Implementing advanced drop zone validation and visual feedback systems</em></p>
]]></content:encoded></item></channel></rss>