Unity 2D drag and drop issue iDropHandler - unity3d

I have a draggable gameobject (brick) that implements IBeginDragHandler, IDragHandler, IEndDragHandler
I also have another gameobject (slot) to drop the brick into that implements IDropHandler
A quick look at the Bricks' OnBeginDrag method:
public static GameObject itemBeingDragged;
Vector3 startPosition;
Transform startParent;
public void OnBeginDrag(PointerEventData eventData)
{
itemBeingDragged = gameObject;
startPosition = transform.position;
startParent = transform.parent;
GetComponent<CanvasGroup>().blocksRaycasts = false;
GetComponent<BoxCollider2D>().enabled = false;
}
When i drop the brick into the slot, the brick assumes the slot as parent and also assumes the slots position, like so in the IDropHandler's OnDrop method code:
public void OnDrop(PointerEventData eventData)
{
DragHandler.itemBeingDragged.transform.SetParent(transform);
DragHandler.itemBeingDragged.transform.position = transform.position;
}
The problem with this is when i drag and drop the brick, i want there to be slight offset to the bricks position (e.g on a mobile phone, so that while dragging the brick, the brick is not visually hidden by my finger )
So in the Bricks OnDrag code, i have something like that to give a visual offset:
public void OnDrag(PointerEventData eventData)
{
Vector3 offset = new Vector3(0, 100, 0);
transform.position = Input.mousePosition + offset;
}
I know the above is for mouse position but ultimately i want it to be touch position.
This looks fine when dragging, however, when dropping it on the slot, it seems that the slot's OnDrop method is only called when the mouse pointer is above the slot, and not when the brick is above the slot. Meaning when i release the drag while the brick is above the slot, OnDrop doesn't get called. It is only called when I release the brick outside the slot in such a way that the mouse pointer is inside the slot. Make sense?
IS there a way to make OnDrop work with the bricks position rather than the mouse position?
Thanks
Kevin

This is ultimately a hack, and it's exactly the reason why I like to do my own drag/drop logic with the pointer down/up events, but anyway: Make the visual a child of the gameobject with the brick script attached, then move only the visual up when a drag starts. When the drag is complete, move the visual back down into place.
And btw, you're using SetParent(transform) on what looks like a RectTransform. You generally want to use SetParent(transform, false) with rect transforms, because otherwise you'll mess up the layout system and lose the benefits of rect transforms anyway.

Related

Teleporting in Unity3d

enter image description here I am instantiating prefabs and listing them on a scroll list. I am trying to teleport the player to instantiated prefab position when I click its reference on scroll list listing?
All suggestions are welcome.
From what I understand about your problem (explained in the comments rather than the question), you should be able to do this:
You can instantaneously move an object camera by setting its transform's position to the instantiated prefab's position when the click has occurred. For a camera, you probably have the camera view in X and Y dimension, so you want to move it to the new X and Y position but leave the Z-position as it is.
One potential solution: Add the following script to the GameObject with your Button component. Then add an event-listener on the Button component that points to the newly added script component and choose the MyTeleportingButton.OnClick as the target method. You also need to drag in the camera as a reference in the new script component.
public class MyTeleportingButton : MonoBehaviour
{
public GameObject camera;
public void OnClick()
{
// casting to Vector2 in order to move in 2D only
var currentPosition = camera.transform.position;
var newPosition = transform.position;
// set same depth as camera
newPosition.z = currentPosition.z;
camera.transform.position = newPosition;
}
}

Camera follow player around planet

I have a planet and a player moving on it using gravity. I would like to have a camera to follow the player around it. Using the Parent Constraint component works perfectly, but I want to delay the rotation follow, so I have to use a script. I just cannot figure out how to make it follow it around the globe. I either have a camera that completely freaks out, or a camera that sort of follows the player but doesn't move over the planet and always stays in front of it. And often the position following works, but as soon as I add something that changes rotation it only does that. I've tried many different scripts but nothing works. I'm grateful for any help.
EDIT
I'm sorry for not adding an example. At the moment I've tried this script attached to the camera:
public class CameraFollow : MonoBehaviour
{
public GameObject player;
private Vector3 offset;
void Start()
{
offset = transform.position - player.transform.position;
}
void LateUpdate()
{
transform.rotation = Quaternion.Slerp(transform.rotation, player.transform.rotation, 50f * Time.deltaTime);
transform.position = player.transform.position + offset;
}
The camera does mimic the rotation of the player, but the position isn't being follow correctly anymore. It seems mostly stuck in place, moving only very slightly.
In order to have a camera following a GameObject, you need to go to the camera you want following the GameObject, select Add component, write FollowPlayer, press New script, and then press select and add. Edit the script so it contains the following:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FollowPlayer : MonoBehaviour
{
public Transform player;
public Vector3 offset;
// Update is called once per frame
void Update()
{
transform.position = player.position + offset;
}
}
Then, you will need to drag and drop the GameObject you want the camera to follow, in the "Player" box.
Define the offset of the camera from the GameObject, and your'e good to go.

My game object move as soon as it gets some input

I'm trying to learn Unity by myself. I'm recreating pong in 3d with Unity objects. I started a few minutes ago and every time I throw any input into the pad its y coordinate shifts to 2.6, I have no idea why. Can someone help?
public class PadMovement : MonoBehaviour {
private CharacterController pad;
private Vector3 direction;
private Vector3 movement;
[SerializeField] private float speed = 50.0f;
// Use this for initialization
void Start() {
pad = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update() {
direction = new Vector3(Input.GetAxis("Horizontal"), 0, 0);
movement = direction * speed;
pad.Move(movement * Time.deltaTime);
}
}
SOLVED: there was a capsule collider out of place!
Afternoon, I recently copied your code into a 'Unity3d', ".cs", File and actually I created a cube and placed my game into a 2d mode, After this I named my file "PadMovement", after that I dragged it onto my newly created cube, Once I had done that I tried to click play and noticed that my cube didn't have a "CharacterController", attached to my cube, Once I had done that and clicked play I was eligible to have my "paddle", smoothly move around the screen # 50.0f along the X axis.
Knowingly, My Input came from the "Character Controller", My Speed came from the Serial field you had for speed!
Do you by any chance have a CapsuleCollider Component or some other collider on the same GameObject that you have this PadMovement Component on? That sounds like a height where it might just be trying to pop the object out of ground collision.
It should be harmless if that's all it is. If you really want an object to be at y of 0 you can attach it to a parent object and have that parent stay at 0.

Physics2D.OverlapPoint () return always null

I'm trying to detect mouse click on 2D sprite on a 3D scene.
All my Sprite have a Box Collider 2D (well placed) and a script on it but hit is null all the time. I Also tried to put the Update() function on a script on GameEngine GameObject, but I got the same result.
void Update () {
if (Input.GetMouseButtonDown(0)) {
Vector2 mouse_position = Camera.main.ScreenToWorldPoint (Input.mousePosition);
Collider2D hit = Physics2D.OverlapPoint (mouse_position);
if (hit) {
Debug.Log ("Hit" + hit.transform.name);
} else {
Debug.Log (hit);
}
}
}
void OnMouseDown() {
Debug.Log ("Hit " + this.name);
}
No need to do what you're doing. The new Canvas UI systems has a sophisticated event system built-in.
If you look at your "Image" component, it has a "Raycast Target" basically turns on or off the event system handlers for that component.
You can listen for clicks/drags and other events on canvas elements using the UnityEngine.EventSystems namespace.
Here's an example for you:
using UnityEngine;
using UnityEngine.EventSystems;
class BuildingUI : Monobehaviour, IPointerDownHandler, IPointerUpHandler {
void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Pointer Down " + eventData.selectedObject.name);
}
void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Pointer Up " + eventData.selectedObject.name);
}
}
There are loads of interfaces you can implement, I recommend you checkout the Manual.
IBeginDragHandler
ICancelHandler
IDeselectHandler
IDragHandler
IDropHandler
IEndDragHandler
IInitializePotentialDragHandler
IMoveHandler
IPointerClickHandler
IPointerDownHandler
IPointerEnterHandler
IPointerExitHandler
IPointerUpHandler
IScrollHandler
ISelectHandler
ISubmitHandler
IUpdateSelectedHandler
I'm pretty sure that the root cause of your problem is that your "Building" objects are UI objects, being under a canvas. There's a number of things that this could lead to, but what I believe is causing your problem is the issue of world and screen space.
You are converting the mouse location from a screen point to world space, when your "Building" objects are under a canvas that does not appear to be using world space for its location. To confirm this, I suggest that you do a Debug.Log for the original mouse position, the converted mouse position, and the actual position of your "Building" objects. If you find that the unconverted mouse position lines up more realistically with your object positions, I suggest removing the conversion.
You may have to do additional work (ie doing some math on the mouse or object positions and/or changing the anchors of your objects) to get it to work perfectly, but this should allow your mouse position and objects to be working with the same units in terms of position.

Drag and Drop between 2 gameObjects

I have 2 Spheres in my scene. I want to be able to drag and drop my mouse from one sphere to the other, and have an indicator (a straight line for example) while dragging. After releasing the mouse button, I want to store in the first sphere the other sphere (as GameObject).
I need this in UnityScript, but I can accept C# ideas.
So far I have thought about onMouseDown event on the first Sphere, and then onMouseEnter to the other sphere, I'll store the data in some global variable (which I donno how to do yet) and in case of onMouseExit I'll just put the global variable as null.
Then onMouseUp on the first Sphere I'll store the global variable in the pressed object (the sphere).
Any tips and tricks on how to do it?
Assumptions/Setup
You're working in a 2D worldspace; the logic for dragging the object for this part of the solution would need changing.
Setting parent after click drag, referred to setting the object you didn't click to be the child of the parent you did click on.
The camera should be an orthographic camera; using a perspective camera will cause the dragging to not align with where you think it should be.
In order to make the dragging work, I created a quad that was used as the 'background' to the 2D scene, and put that on a new layer called 'background'. Then setup the layermask field to only use the background layer.
Create and setup a material for the line renderer, (I'm using a Particles/Additive shader for the above example), and for the parameters of the line renderer I'm using Start Width: 0.75, End Width: 0, and make sure Use World Space is ticked.
Explanation
Firstly setup the otherSphere field to be pointing to the other sphere in the scene. OnMouseDown and OnMouseUp toggle the mouseDown bool, that is used to determine if the line renderer should be updated. These are also used to turn on/off the line renderer, and set/remove the parent transform of the two spheres.
To get the position to be dragged to, I'm getting a ray based off of the mouse position on screen, using the method ScreenPointToRay. If you wanted to create add smoothing to this drag functionality, you should enable the if (...) else statement at the bottom, and set the lerpTime to a value (0.25 worked well for me).
Note; when you release the mouse, the parent is immediately set, as such both objects end up getting dragged, but the child will return to it's former position anyway.
[RequireComponent(typeof(LineRenderer))]
public class ConnectedSphere : MonoBehaviour
{
[SerializeField]
private Transform m_otherSphere;
[SerializeField]
private float m_lerpTime;
[SerializeField]
private LayerMask m_layerMask;
private LineRenderer m_lineRenderer;
private bool m_mouseDown;
private Vector3 m_position;
private RaycastHit m_hit;
public void Start()
{
m_lineRenderer = GetComponent<LineRenderer>();
m_lineRenderer.enabled = false;
m_position = transform.position;
}
public void OnMouseDown()
{
//Un-parent objects
transform.parent = null;
m_otherSphere.parent = null;
m_mouseDown = true;
m_lineRenderer.enabled = true;
}
public void OnMouseUp()
{
//Parent other object
m_otherSphere.parent = transform;
m_mouseDown = false;
m_lineRenderer.enabled = false;
}
public void Update()
{
//Update line renderer and target position whilst mouse down
if (m_mouseDown)
{
//Set line renderer
m_lineRenderer.SetPosition(0, transform.position);
m_lineRenderer.SetPosition(1, m_otherSphere.position);
//Get mouse world position
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out m_hit, m_layerMask))
{
m_position.x = m_hit.point.x;
m_position.y = m_hit.point.y;
}
}
//Set position (2D world space)
//if (m_lerpTime == 0f)
transform.position = m_position;
//else
//transform.position = Vector3.Lerp(transform.position, m_position, Time.deltaTime / m_lerpTime);
}
}
Hope this helped someone.