Hi. I’m trying to understand on how to drag ui element to 3D field like comparing a tag and instantiate a a prefab on that spot. I didn’t find anything about it for UI Toolkit.
This is my previous setup when I was using uGUI button.
for (int i = 0; i < objects.Count; i++)
{
if (objects[i].isUnlocked && objects[i].isSelected && !objects[i].isPayed)
{
GameObject newButton = Instantiate(buttonConf, actionMenu.transform);
button = newButton.GetComponent<Button>();
int selectedTower = i;
newButton.GetComponent<SetConfigurationButton>().SetupTurretButton(objects[i]);
EventTrigger trigger = newButton.AddComponent<EventTrigger>();
var pointerDown = new EventTrigger.Entry();
pointerDown.eventID = EventTriggerType.PointerDown;
pointerDown.callback.AddListener((e) => SelectLevel(selectedTower));
trigger.triggers.Add(pointerDown);
}
else if (objects[i].isUnlocked && objects[i].isSelected && objects[i].isPayed)
{
GameObject newButton = Instantiate(buttonConf, actionMenu.transform);
int selectedTower = i;
newButton.GetComponent<SetConfigurationButton>().SetupTurretButton(objects[i]);
EventTrigger trigger = newButton.AddComponent<EventTrigger>();
var pointerDown = new EventTrigger.Entry();
pointerDown.eventID = EventTriggerType.PointerDown;
pointerDown.callback.AddListener((e) => SelectLevel(selectedTower));
trigger.triggers.Add(pointerDown);
}
}
I’m not sure how to do it with UI Elements after a deep research many people says that UI Toolkit doesn’t have any of these function
I tried this myself but unfortunately no luck it doesn’t work at all
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class TowerDeckProvider : MonoBehaviour
{
public TowerInventoryDataConfig towerDatas;
public List<ConfigurationTurret> towers;
public UIDocument battleUI;
public VisualTreeAsset cardSpawn;
private VisualElement rootUI;
private VisualElement cardContainer;
public CellCreatorGrid currentTower;
public Camera mainCamera;
public GameObject towerPrefab;
private Vector2 originalCardPosition;
private bool isDragging = false;
private void Awake()
{
mainCamera = Camera.main;
}
private void OnEnable()
{
rootUI = battleUI.rootVisualElement;
cardContainer = rootUI.Q("CardsContainer");
for (int i = 0; i < towers.Count; i++)
{
if (towers[i].deckIndex == towerDatas.currentTabIndexSelected)
{
var cardBattle = new CardTowerAsset(cardSpawn);
cardBattle.SetBattleCard(towers[i]);
cardBattle.userData = new Vector2(cardBattle.worldBound.position.x, cardBattle.worldBound.position.y);
cardBattle.RegisterCallback<PointerDownEvent>(evt => StartDrag(evt, cardBattle));
cardBattle.RegisterCallback<PointerMoveEvent>(evt => Drag(evt, cardBattle));
cardBattle.RegisterCallback<PointerUpEvent>(evt => Drop(evt, cardBattle));
cardContainer.Add(cardBattle);
}
}
}
private void StartDrag(PointerDownEvent evt, CardTowerAsset setTower)
{
isDragging = true;
originalCardPosition = setTower.resolvedStyle.position;
Debug.Log($"Start Dragging. Initial Position: {originalCardPosition}");
setTower.style.position = Position.Absolute;
}
private void Drag(PointerMoveEvent evt, CardTowerAsset setTower)
{
if (!isDragging) return;
Vector2 localMousePosition = evt.position;
setTower.style.left = localMousePosition.x - (setTower.resolvedStyle.width / 2);
setTower.style.top = localMousePosition.y - (setTower.resolvedStyle.height / 2);
Debug.Log($"Dragging. Current Position: {localMousePosition}");
}
private void Drop(PointerUpEvent evt, CardTowerAsset setTower)
{
isDragging = false;
Ray ray = mainCamera.ScreenPointToRay(evt.position);
if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity))
{
if (hit.collider.CompareTag("grounded"))
{
Debug.Log("We touching grounds");
Instantiate(towerPrefab, hit.point, Quaternion.identity);
}
}
setTower.style.left = originalCardPosition.x;
setTower.style.top = originalCardPosition.y;
Debug.Log($"Reset Position to: {originalCardPosition}");
}
}
Is it real that UI Toolkit can’t do that?
Because after many tries and research I still didn’t achieve any results. I try to add as Manipulator still no luck. I’m really disappointed in UI Toolkit
“Unity intends for UI Toolkit to become the recommended UI system for new UI development projects, but it is still missing some features found in Unity UI (uGUI) and IMGUI.”
It’s coming along but it is still far from anything our team considers ready for production, especially when UGUI works quite well thank you very much.
I already know how to drag and drop on UI Elements it’s just enough to create a fake uss as a draggable to add into a class in runtime so you can achieve this but this is not what I’m trying to do.
My question was how to make UI Elements interact with 3D space. For example drag a card into the battlefield in 3D so typically in world space to instantiate a prefab in that spot where the card was dragged from the UI
I mean UI Toolkit currently doesn’t have world space support. If you required that, you should have checked that before migrating over to it.
Otherwise if you just need UI to render over a particular world space position, you can just write some code to position the visual elements based on the world space coordinates they’re meant to be situated at.
And there’s no reason why you can’t have a mix of UI Toolkit and uGUI in the same project, either.
where you drag card into ground where you do a raycast check if it hits ground so I can instantiate a prefab in that spot.
I do know that this game is in 2D space so it’s easy to drag into another visual element container. But my game is in 3D space…
I thought that UI Toolkit couldn’t only display UI in world space but I never tought it couldn’t detect as an event or action that we are hitting something in the world space while we drag the element
I would avoid it… But as far as I know I need to revert back into uGUI because many things won’t work together since my scripts depends a lot on UI’s
I mean in your example, if the UI only ever needs to draw on top of game objects, then that’s entirely possible. As mention, you will just have to write some code that positions then manually.
This is easier if your camera doesn’t move. If it does, then you will need to manually update the visual element’s positions as you move.
This concept applies to both 2d and 3d space. In both cases you’d probably want a fullscreen visual element that these overlayed elements get placed in.
As your project is in 3d, if you want game objects to be able to go in front of your UI, then I’m not entirely sure that’s possible yet.
I don’t see why it can’t. Is the PointerUpEvent not firing? Looking at your code, I think you’re registering it to the wrong element. You would want to handle pointer-up events at a higher level visual element. Likely the same full-screen one I mentioned before.
This is exactly what I do. Once I start to drag viusal element (CardBattle which act as a card) I prevent camera from moving until I call dropEnd.
Yes this is something that I was trying to do by myself and I’m not entirly sure if that was correct then I found this Drag Manipulator so I used DropEvent here’s a refactored code:
private void OnEnable()
{
rootUI = battleUI.rootVisualElement;
cardContainer = rootUI.Q("CardsContainer");
for (int i = 0; i < towers.Count; i++)
{
if (towers[i].deckIndex == towerDatas.currentTabIndexSelected)
{
var cardBattle = new CardTowerAsset(cardSpawn);
cardBattle.SetBattleCard(towers[i]);
cardBattle.AddManipulator(new DragManipulator());
cardBattle.RegisterCallback<DropEvent>(evt => currentTower.SelectLevel(towers[i]));
cardContainer.Add(cardBattle);
}
}
}
but the problem is that DropEvent never calls currentTower.SelectLevel(towers[i]) because it expect that you place your Visual Element into another visualElement container to trigger this event. So it doesn’t react at all when you try to compare Tag to ground it just doesn’t trigger it. Instead when I start to drag in theory it should instantiate a prefab at the current position of the mouse where the evt.mousePosition is triggered by DropEvent at the card spot like gameObject should follow the card at that point only if we detect that we are currently hitting a ground in the world space.
On uGUI it’s simple once we trigger OnPointerDown I can instantiate a prefab at that spot to start dragging it around the map that is based on the ground checks
Which is a touch disappointing, but we can certainly emulate this behaviour with PointerUpEvent’s. We just need to bear in mind its particular behaviour: Unity - Manual: Pointer events
So if you’re starting from one of your card visual elements, but dragging your pointer out of it, then you won’t receive a PointerUpEvent on that element. You want the destination where the visual element would end up to have this callback registered.
Return type has nothing to do with this. Visual element callbacks can only registered to visual elements…
Like I said, have a full-screen visual element that you register this event to, so you can always recieve it no matter where its dragged to on the screen. This is a very easily solvable problem.
The problem can arise once you check if your current mouse is over UI elements I think making it full screen will return this always to true while visual Element still need receive input I can’t disable it nor set position to ignore right?
I mean there’s plenty of API methods to see what is underneath your pointer. Or you can just have this visual element sized to your ‘game-play’ space if you have a UI overlay taking up space around the screen. Again, easily solvable issues.
The reason why I need to check if the mouse is over ui elements is to stop camera from moving but in this case it will always return true since we cover the whole screen and I think I can’t cover some space of the gameplay because players still can move the camera so it will change the position. I’m not really familiar with those pointers.
And I still think it won’t work because players also can pan the camera and also zoom it so the gameplay space needs to be empty or at least not getting any other inputs
This seems unrelated to what we’re talking about. I’m only talking about having an element with which you register your PointerUpEvent so you know where something has been dropped-off at.
Everything else you’re worried about seems beside the point.
I’m really not sure what you’re worried about. Yes you’ll recieve the callback, but from there its entirely up to you to determine what to do with the callback.
Yes you have project specific considerations but I guarantee they’re all solvable.
In fact as I said it does only drag the visual element but it still doesn’t trigger PointerMove nor PointerUp the card return back to it’s original position where it was dragged before. It doesn’t instantiate a prefab at that spot unfortunately