You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity3d+C#开发智龙迷城复刻版:符文拖拽交换功能实现求助

How to Implement Rune Drag & Swap in Unity (Puzzle & Dragons Style)

Hey there! I’ve built similar match-3 drag mechanics in Unity before, so let’s walk through practical steps to get your rune swap working smoothly.

1. Core Setup: Rune Script with Drag Interfaces

First, attach a script to your rune prefab that uses Unity’s event interfaces to handle drag start, movement, and end. This works for both 2D and 3D runes—just adjust the raycasting type later.

using UnityEngine;
using UnityEngine.EventSystems;

public class Rune : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    private Vector3 _startPosition;
    private Transform _originalParent;
    private Rune _targetRune;
    private CanvasGroup _canvasGroup;

    void Awake()
    {
        _canvasGroup = GetComponent<CanvasGroup>();
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        // Store initial state and move rune to overlay for proper rendering
        _startPosition = transform.position;
        _originalParent = transform.parent;
        transform.SetParent(FindObjectOfType<Canvas>().transform);
        _canvasGroup.blocksRaycasts = false; // Prevent blocking other raycasts while dragging
    }

    public void OnDrag(PointerEventData eventData)
    {
        // Follow mouse position during drag
        transform.position = Input.mousePosition;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        // Reset parent and raycast blocking
        transform.SetParent(_originalParent);
        _canvasGroup.blocksRaycasts = true;

        // Raycast to find the target rune we're dropping onto
        if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
        {
            _targetRune = hit.collider.GetComponent<Rune>();
            if (_targetRune != null && IsAdjacent(_targetRune))
            {
                // Swap positions with the adjacent rune
                StartCoroutine(AnimateSwap(_targetRune));
                
                // Check if swap creates a match (implement your match logic here)
                if (!CheckForMatches())
                {
                    // No match? Swap back immediately
                    StartCoroutine(AnimateSwap(_targetRune));
                }
            }
            else
            {
                // No valid target, snap back to original position
                transform.position = _startPosition;
            }
        }
        else
        {
            transform.position = _startPosition;
        }
    }

    // Check if the target rune is adjacent on the grid
    private bool IsAdjacent(Rune otherRune)
    {
        // Adjust the 1.1f value to match your grid cell size
        Vector3 positionDiff = transform.position - otherRune.transform.position;
        return (Mathf.Abs(positionDiff.x) < 1.1f && Mathf.Abs(positionDiff.y) < 0.1f) ||
               (Mathf.Abs(positionDiff.y) < 1.1f && Mathf.Abs(positionDiff.x) < 0.1f);
    }

    // Smooth animation for swap movement
    private IEnumerator AnimateSwap(Rune otherRune)
    {
        Vector3 myStartPos = transform.position;
        Vector3 targetStartPos = otherRune.transform.position;
        float duration = 0.15f;
        float elapsedTime = 0f;

        while (elapsedTime < duration)
        {
            elapsedTime += Time.deltaTime;
            float t = elapsedTime / duration;
            transform.position = Vector3.Lerp(myStartPos, targetStartPos, t);
            otherRune.transform.position = Vector3.Lerp(targetStartPos, myStartPos, t);
            yield return null;
        }

        // Ensure positions are exact after animation
        transform.position = targetStartPos;
        otherRune.transform.position = myStartPos;
    }

    // Replace this with your actual match detection logic
    private bool CheckForMatches()
    {
        // Example: Check 3+ runes in a row/column
        // Return true if matches are found, false otherwise
        return false;
    }
}

2. Scene Configuration

  • Add a CanvasGroup component to your rune prefab (set Interactable to true and Blocks Raycasts to true initially).
  • Ensure your runes have a collider (like BoxCollider or SphereCollider) so raycasts can detect them.
  • Verify your EventSystem has a PhysicsRaycaster (for 3D) or GraphicRaycaster (for UI) component—this is critical for detecting drag events.

For Puzzle & Dragons’ strict grid, assign each rune a grid coordinate (e.g., (x, y)). This makes adjacency checks and match detection way easier. Add a GridManager script to track runes:

public class GridManager : MonoBehaviour
{
    public int gridWidth = 5;
    public int gridHeight = 6;
    public float cellSize = 1f;
    private Rune[,] _grid;

    void Start()
    {
        _grid = new Rune[gridWidth, gridHeight];
        // Populate the grid with your spawned runes here
    }

    public Rune GetRuneAt(int x, int y)
    {
        if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight)
            return _grid[x, y];
        return null;
    }

    public void UpdateGridPosition(Rune rune, int oldX, int oldY, int newX, int newY)
    {
        _grid[oldX, oldY] = null;
        _grid[newX, newY] = rune;
    }
}

Modify your Rune script to track its (x, y) grid position and use GridManager for adjacency checks instead of world positions.

Troubleshooting Quick Fixes

  • Drag not registering? Double-check colliders, raycasters, and CanvasGroup settings.
  • Runes not snapping to grid? Verify your grid coordinate calculation logic.
  • Swap not rolling back? Test your CheckForMatches function with hardcoded values first to ensure it works.

Let me know if you need help refining any part of this—match-3 mechanics can be tricky, but this foundation should get you over the hump!

内容的提问来源于stack exchange,提问作者Arcosha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:02:06