iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0
Software Development

Vibe Unity - AI時代的遊戲開發工作流系列 第 12

Day 12 - 實現可以拖拉道具出來的 UI 欄位

  • 分享至 

  • xImage
  •  

image.png

還記得我們的下方有一個可以拖拉道具出來的 UI 欄位嗎 ?

這一章, 我們來實作拖拉道具出來的效果:

Prompt:

實作代碼: 下方有一個 UI Scroll View 欄位, 可以拖拽食物和玩具給北極熊玩
1. Scroll View Content 裡面有兩個道具 - Fish, Soap 
2. Content 裡面的道具目前是以 Image 的方式擺放的
3. 滑鼠點擊 Image 的道具的時候, 可以拖拽出來變成 Sprite Renderer 放到場景
4. 拖拽的時候, Scroll View 的 UI 會半透明, 放開拖拽的時候恢復

Code:

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

/// <summary>
/// 掛在 Scroll View Content 內每個道具 Image 上:
/// - PointerDown 開始拖拽:在場景生成對應的 Prefab(含 SpriteRenderer 與相應 Item 腳本,如 FishItem/SoapItem)。
/// - 拖拽期間使指定的 Scroll Rect CanvasGroup 半透明;結束時恢復。
/// - 支援將新生成物件以指定 Z 放到場景,並由 DraggableItem 接手跟隨滑鼠。
/// 設計:使用 EventSystem 事件,避免每幀輪詢;僅在拖拽期間有狀態。
/// </summary>
public class UIDragSpawnItem : MonoBehaviour, IPointerDownHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
	[Header("Spawn Settings")]
	[Tooltip("要生成到場景的 Prefab,需包含 SpriteRenderer 與 DraggableItem 派生類(如 FishItem/SoapItem)")]
	[SerializeField] private GameObject spawnPrefab;
	[Tooltip("生成時放置到此父物件(可為場景中的容器),留空則放在場景根層級")]
	[SerializeField] private Transform worldParent;
	[Tooltip("拖拽時使用的世界 Z 軸(決定渲染排序深度)")]
	[SerializeField] private float dragWorldZ = 0f;
	[Tooltip("將滑鼠轉世界座標使用的相機,若留空將使用 Camera.main")]
	[SerializeField] private Camera targetCamera;

	[Header("UI Fade During Drag")]
	[Tooltip("拖拽時變半透明的 CanvasGroup(建議掛在 Scroll View 或其父物件)")]
	[SerializeField] private CanvasGroup fadeCanvasGroup;
	[SerializeField] [Range(0f, 1f)] private float draggingAlpha = 0.5f;

	private GameObject spawned;
	private DraggableItem spawnedDraggable;
	private float originalAlpha = 1f;
	private bool dragging;

	private void Awake()
	{
		if (targetCamera == null)
		{
			targetCamera = Camera.main;
		}
		if (fadeCanvasGroup != null)
		{
			originalAlpha = fadeCanvasGroup.alpha;
		}
	}

	/// <summary>
	/// 使用者在 UI Image 上按下,標記可能開始拖拽。
	/// </summary>
	public void OnPointerDown(PointerEventData eventData)
	{
		// 不生成於 PointerDown,等 BeginDrag 確認拖拽再生成,避免誤觸。
	}

	/// <summary>
	/// 真正開始拖拽時才生成場景物件並交給 DraggableItem 接管移動。
	/// </summary>
	public void OnBeginDrag(PointerEventData eventData)
	{
		if (dragging) return;
		if (spawnPrefab == null) return;

		spawned = Object.Instantiate(spawnPrefab, worldParent);
		spawnedDraggable = spawned != null ? spawned.GetComponent<DraggableItem>() : null;
		if (spawnedDraggable != null)
		{
			spawnedDraggable.BeginDragFromUI(targetCamera, dragWorldZ, true);
			dragging = true;
			SetUIFade(true);
		}
		else
		{
			// 若 Prefab 缺少 DraggableItem,直接取消拖拽
			CleanupSpawned();
		}
	}

	/// <summary>
	/// 拖拽過程不需要額外處理,由 DraggableItem 自動跟隨滑鼠。
	/// 但為了確保事件持續,保留空實作。
	/// </summary>
	public void OnDrag(PointerEventData eventData)
	{
		// no-op
	}

	/// <summary>
	/// 放開拖拽:恢復 UI 透明度,並通知 DraggableItem 結束拖拽(模擬滑鼠放開)。
	/// </summary>
	public void OnEndDrag(PointerEventData eventData)
	{
		if (!dragging)
		{
			SetUIFade(false);
			return;
		}

		// DraggableItem 會在 Update 偵測到 MouseUp 自行結束;這裡只負責 UI 狀態恢復。
		SetUIFade(false);
		dragging = false;
		spawned = null;
		spawnedDraggable = null;
	}

	private void SetUIFade(bool isDragging)
	{
		if (fadeCanvasGroup == null) return;
		fadeCanvasGroup.alpha = isDragging ? draggingAlpha : originalAlpha;
	}

	private void CleanupSpawned()
	{
		if (spawned != null)
		{
			Object.Destroy(spawned);
			spawned = null;
		}
		spawnedDraggable = null;
		dragging = false;
		SetUIFade(false);
	}
}

一開始, 我們可以先把場景中的道具先製作成 Prefabs
變成 Prefabs 後的道具, 就可以不斷的重複使用生成, 而且會保留原本的設定值
在 Resources 中新增一個資料夾, 就叫 Prefabs
把場景中的道具拉下來, 就會變成 Prefab
接著把 Canvas 底下, Scroll View / Viewport / Content 裡的圖片道具
全部加上 UI Drag Spawn Item 的代碼, 再把對應的道具 Prefab拉上去

image.png

接下來播放遊戲進行測試
你就可以把 UI 的道具直接拖到場景中啦


下一章, 我們來實現最後的小功能 - 北極熊的移動 !


上一篇
Day 11 - 北極熊吃飯和洗澡功能的實現方法
下一篇
Day 13 - 北極熊走來走去, 發呆, 睡覺的功能
系列文
Vibe Unity - AI時代的遊戲開發工作流13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言