Unity speech recogniser, how to use in conjunction with an if statement - unity3d

I am creating a voice controlled game. One game I'm creating is a "shoot the targets" cannon game which will fire a cannon ball when the word "shoot" is said.
I am trying to use Unity's speech recogniser in conjunction with an if statement in order to move the Slider UI's fill area with my voice.
However, my initial method is drawing a few errors such as "Cannot convert Method group 'Up' to non-delegate type bool. Did you intent to invoke the method?"
P.S This is my first time using StackOverflow, please be nice.
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Windows.Speech;
using UnityEngine;
using UnityEngine.UI;
public class SliderControls : MonoBehaviour
{
//Voice Controls Setup
private KeywordRecognizer keywordRecognizer;
private Dictionary<string, Action> actions = new Dictionary<string, Action>();
float sliderValue;
// Start is called before the first frame update
void Start()
{
actions.Add("Up", Up);
actions.Add("Down", Down);
keywordRecognizer = new KeywordRecognizer(actions.Keys.ToArray());
keywordRecognizer.OnPhraseRecognized += Recognization;
keywordRecognizer.Start();
}
private void Recognization(PhraseRecognizedEventArgs speech)
{
Debug.Log(speech.text);
actions[speech.text].Invoke();
}
private void Up()
{
ControlGUI();
}
private void Down()
{
ControlGUI();
}
public void ControlGUI ()
{
if (Up)
{
sliderValue = sliderValue + 0.1F;
}
else if(Down)
{
sliderValue = sliderValue - 0.1F;
}
sliderValue = GUI.HorizontalSlider(new Rect(25F, 25F, 600F, 30F), sliderValue, 0.0F, 10.0F);
return;
}
}

As the error says, you trying to use method as a bool. You can't do that. Moreover, your logic in ControlGUI method is strange - where these Up and Down should come from?
I think you need to pass and argument to ControlGUI method, so your code will look like:
private void Up()
{
ControlGUI("Up");
}
private void Down()
{
ControlGUI("Down");
}
// You can also define enum Direction { Up, Down } or use int or any other approach
public void ControlGUI (string direction)
{
if (direction == "Up")
{
sliderValue = sliderValue + 0.1F;
}
else if(direction == "Down")
{
sliderValue = sliderValue - 0.1F;
}
sliderValue = GUI.HorizontalSlider(new Rect(25F, 25F, 600F, 30F), sliderValue, 0.0F, 10.0F);
return; // this return statement is redundant
}

Related

How to Jump by Button UI using Character Controller in Unity3D

I am calling JUMP function from a UI Button but unable to jump using Character Collider provided by Unity. Can someone please help me where am i going wrong?
PlayerMovement Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class PlayerControllerCC : MonoBehaviour
{
public FixedJoystick moveJoystick;
CharacterController _charController;
private Vector3 v_movement;
private Animator _animator;
public float moveSpeed = 0.1f;
public float gravity = 0.5f;
public float jumpForce = 0.5F;
private float originalstepOffset;
private float InputX;
private float InputZ;
// Start is called before the first frame update
void Start()
{
moveSpeed = 0.1f;
gravity = 0.5f;
jumpForce = 0.5F;
_charController = GetComponent<CharacterController>();
originalstepOffset = _charController.stepOffset;
_animator = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
InputX = moveJoystick.Horizontal;
InputZ = moveJoystick.Vertical;
isWalking();
}
private void FixedUpdate()
{
playerMovement();
}
void playerMovement()
{
//Check Gravity
if (_charController.isGrounded)
{
_charController.stepOffset = originalstepOffset;
v_movement.y = -0.5f;
}
else
{
_charController.stepOffset = 0;
v_movement.y -= gravity * Time.deltaTime;
}
//player movement
v_movement = new Vector3(InputX * moveSpeed, v_movement.y, InputZ * moveSpeed);
float magnitute = Mathf.Clamp01(v_movement.magnitude);
v_movement.Normalize();
_charController.Move(v_movement * magnitute);
Vector3 lookDir = new Vector3(v_movement.x, 0, v_movement.z);
//Set rotation facing after player movement
if ((lookDir.x != 0) && (lookDir.z != 0))
{
transform.rotation = Quaternion.LookRotation(lookDir);
}
}
void isWalking()
{
if (InputX == 0 && InputZ == 0)
{
_animator.SetBool("isWalking", false);
}
else
{
_animator.SetBool("isWalking", true);
}
}
public void Attack()
{
_animator.SetTrigger("isAttacking");
}
public void Jump()
{
if (_charController.isGrounded)
{
v_movement.y = jumpForce * Time.deltaTime;
}
}
}
When button is clicked in UI it called the JUMP function. I am not using keyboard but FixedJoyStick hence using button to jump.
My Game looks like this and the UI. The up arrow key is the jump button.
The Game button Up Arrow Jump
Calling Jump Function in Button
Your problem lays on the OnClick() function of the button, it is interacting with the script but the script itself is not attached to any character in this case. What you did works when the script works by itself no matter where is attached (e.g. when you call all the objects with a tag).
What you need to do is to link the object which has the script to the OnClick() in the editor, then select from the dropdown menu the script component and then call the function you want.
Like This.
Image link since I'm new
Notice how what is linked are the Game Objects and then the script is selected.

Trying to set gravity modifier with script

I want to set the gravity modifier of an object to 2 when the mouse is clicked down, then go back down to .3 when it is released.
I think its just a simple stupid error.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move : MonoBehaviour
{
public Rigidbody2D rb;
public float idle = .3f;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
rb.gravityScale = 2f;
}
else
{
rb.gravityScale = .3f;
}
}
}
GetMouseButtonDown will only return true on the exact frame that the mouse button was pressed down. This means that if you hold it down, your else block will run every frame other than the one it was pressed. You can instead check for the mouse being released with GetMouseButtonUp and change it back when that happens:
if (Input.GetMouseButtonDown(0))
{
rb.gravityScale = 2f;
}
if (Input.GetMouseButtonUp(0))
{
rb.gravityScale = .3f;
}
or you can be more specific with your first if and then use an empty else:
if (Input.GetMouseButtonDown(0) || Input.GetMouseButton(0))
{
rb.gravityScale = 2f;
}
else
{
rb.gravityScale = .3f;
}
GetMouseButton will return true during any frame it's held.

Unity3D VR app: fade to another scene after a certain time

I'm trying to develop a VR app where you can move around and interact with music (moving the audio sources, and so on). So, you can't actually die in this app, which is why a fade to black on trigger wouldn't help me. I'm trying to get a fade to black and show the credits after a certain amount of time (specifically, when the music is over). Maybe even a fade out (black) and a fade in (to another scene containing the credits) would do the trick. I know very little about programming, so I could really use some help.
I'm using Unity3D 2018.2.9f1
The app is for the Samsung's Gear VR
Timer
For any stuff using timers or wait I find Coroutines most of the time the best options.
You could either get the audio clip length and than wait for it
public AudioClip clip;
private void Start()
{
StartCoroutine(Wait(clip.length));
}
private IEnumerator Wait(float seconds)
{
yield return new WaitForSeconds(seconds);
// start fadeout here
}
or you could wait until it finishes playing
public AudioSource source;
// or wherever you start the music adioclip
private void Start()
{
source.Play ();
yield return new WaitUntil(()=> !source.isPlaying);
// start fadeout here
}
Fading
Here it depends a lot ... do you really switch Scenes by using LoadScene etc or do you just want to enable/disable certain content within one Scene?
And do you have 3D objects with materials and renderers or only 2D content using Unity.UI components?
The simplest solution for sure would be to place one overlay Canvas with the desired color as Image and simply fade it in and out. This is actually already available in the AssetStore and basically does
Uses an Overlay Canvas on top of everything with a certain color and animates it's alpha value (again using a Coroutine)
Makes sure that canvas is DontDestroyOnLoad so it isn't removed when we switch scenes
FadeIn the Canvas/Image => Fades out the current Scene
Load the other scene
FadeOut the Canvas/Image => Fades in the new Scene
Here is the source code (just cleaned it up a little)
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Fader : MonoBehaviour
{
private string _fadeScene;
private float _alpha;
private CanvasGroup _myCanvas;
private Image _bg;
private float _lastTime;
private bool _startedLoading;
private float _fadeInDuration;
//Set callback
private void OnEnable()
{
SceneManager.sceneLoaded -= OnLevelFinishedLoading;
SceneManager.sceneLoaded += OnLevelFinishedLoading;
}
//Remove callback
private void OnDisable()
{
SceneManager.sceneLoaded -= OnLevelFinishedLoading;
}
public void InitiateFader(CanvasGroup canvasGroup, Image image, string scene, Color fadeColor, float fadeInDuration, float fadeOutDuration)
{
DontDestroyOnLoad(gameObject);
_fadeInDuration = fadeInDuration;
_fadeScene = scene;
//Getting the visual elements
_myCanvas = canvasGroup;
_bg = image;
_bg.color = fadeColor;
//Checking and starting the coroutine
_myCanvas.alpha = 0.0f;
StartCoroutine(FadeIt(FadeDirection.Out, fadeOutDuration));
}
private enum FadeDirection
{
In,
Out
}
private IEnumerator FadeIt(FadeDirection fadeDirection, float fadeDuration)
{
var timePassed = 0.0f;
switch (fadeDirection)
{
case FadeDirection.Out:
do
{
_alpha = Mathf.Lerp(0, 1, timePassed / fadeDuration);
_myCanvas.alpha = _alpha;
timePassed += Time.deltaTime;
yield return null;
} while (timePassed < fadeDuration);
_alpha = 1;
SceneManager.LoadSceneAsync(_fadeScene);
break;
case FadeDirection.In:
do
{
_alpha = Mathf.Lerp(1, 0, timePassed / fadeDuration);
_myCanvas.alpha = _alpha;
timePassed += Time.deltaTime;
yield return null;
} while (timePassed < fadeDuration);
_alpha = 0;
Initiate.DoneFading();
Debug.Log("Your scene has been loaded , and fading in has just ended");
Destroy(gameObject);
break;
}
}
private void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
{
//We can now fade in
StartCoroutine(FadeIt(FadeDirection.In, _fadeInDuration));
}
}
and
public static class Initiate
{
private static bool areWeFading;
//Create Fader object and assing the fade scripts and assign all the variables
public static void Fade(string scene, Color col, float fadeOutDuration, float fadeInDuration)
{
if (areWeFading)
{
Debug.Log("Already Fading");
return;
}
var init = new GameObject("Fader", typeof(Canvas), typeof(CanvasGroup), typeof(Image), typeof(Fader));
init.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
var fader = init.GetComponent<Fader>();
areWeFading = true;
fader.InitiateFader(init.GetComponent<CanvasGroup>(), init.GetComponent<Image>(), scene, col, fadeOutDuration, fadeInDuration);
}
public static void DoneFading()
{
areWeFading = false;
}
}
than You call this in a dedicated component like in order to be able to do it e.g. in a button's onClick
public class DemoScript : MonoBehaviour
{
//name of the scene you want to load
public string TargetSceneName;
public Color LoadToColor = Color.black;
public float FadeInDuration = 1.0f;
public float FadeOutDuration = 1.0f;
public void GoFade()
{
Initiate.Fade(TargetSceneName, LoadToColor, FadeOutDuration, FadeInDuration);
}
}
or since it is static simply use
Initiate.Fade(TargetSceneName, LoadToColor, FadeOutDuration, FadeInDuration);
from anywhere.
Instead of the LoadSceneAsync you could also do your enabling and disabling stuff if you prefer to do it in only one scene.
However in VR it is actually a bad idea to fade to completely black and let the user see nothing. It might lead to disorientation and cybersickness ...

Mute button does not work when I switch the scenes

AudioManager and sound effects are working just fine at the beginning of scene, however it does not work when i switch the scenes. Or even with the same scene.
I would be grateful if you could help with that and PlayerPrefs issue. I have been searching all around the forum about PlayerPrefs but i could not be sure about what to type.
Thank you very much.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using System;
public class AudioManager : MonoBehaviour {
bool mutebutton = false;
public Sound[] sounds;
public static AudioManager instance;
// Use this for initialization
void Awake () {
if (instance == null)
instance = this;
else
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
foreach (Sound s in sounds)
{
s.source = gameObject.AddComponent<AudioSource>();
s.source.clip = s.clip;
s.source.volume = s.volume;
}
}
void Start()
{
Play("Theme");
PlayerPrefs.SetFloat("volume", AudioListener.volume);
PlayerPrefs.Save();
}
// Update is called once per frame
public void Play (string name)
{
Sound s = Array.Find(sounds, sound => sound.name == name);
s.source.Play();
}
public void Mute()
{
if (!mutebutton)
{
mutebutton = true;
AudioListener.volume = 0;
}
else
{
mutebutton = false;
AudioListener.volume = 1;
}
}
}
If you only miss "Theme" sound on scene load:
Start() method won't be called again on scene load.
You have to actively call Play("Theme") after loading a scene.

Drag and Drop In ScrollRect (ScrollView) in Unity3D

I want to implement drag and drop on contents of scroll view.
The problem is when you try drag items in scroll view you can't scroll the view.
First, I've tried to implement drag and drop by IDragHandler, IBeginDragHandler, IEndDragHandle and IDropHandler interfaces. In a first sight, It worked pretty good but the problem was you can't scroll the ScrollRect.
I think the problem is because of overriding, When I use event triggers that the same as scroll rect like drag, the parent one don't work properly.
So after that, I've thought by myself and implement it by IPointerDown, IPointerUp interfaces and specific time for holding drag-gable UI in ScrollRect and if you don't hold it in specific time the scrolling work well.
But the problem is by enabling DragHandler script that I wrote before the OnDrag, OnBeginDrag and OnEndDrag functions doesn't work when time of holding ended.
First I want to know there is any way to call these functions ?
Second is there any way to implement drag and drop UI without using drag interfaces ?
DragHandler :
using System;
using UnityEngine;
using UnityEngine.EventSystems;
public class DragHandler : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{
public static GameObject itemBeingDragged;
private Vector3 startPos;
private Transform startParent;
DragHandler dragHandler;
public void Awake()
{
dragHandler = GetComponent<DragHandler>();
}
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("Begin");
itemBeingDragged = gameObject;
startPos = transform.position;
startParent = transform.parent;
}
public void OnDrag(PointerEventData eventData)
{
Debug.Log("Drag");
transform.position = Input.mousePosition;
}
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("End");
itemBeingDragged = null;
if (transform.parent == startParent)
{
dragHandler.enabled = false;
transform.SetParent(startParent);
transform.position = startPos;
}
}
}
ScrollRectController:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class ScrollRectController : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public float holdTime;
public float maxVelocity;
private Transform scrollRectParent;
private DragHandler dragHandler;
private ScrollRect scrollRect;
private float timer;
private bool isHolding;
void Awake()
{
scrollRectParent = GameObject.FindGameObjectWithTag("rec_dlg").transform;
dragHandler = GetComponent<DragHandler>();
dragHandler.enabled = false;
}
// Use this for initialization
void Start()
{
timer = holdTime;
}
// Update is called once per frame
void Update()
{
}
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Down");
scrollRect = scrollRectParent.GetComponent<ScrollRect>();
isHolding = true;
StartCoroutine(Holding());
}
public void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Up");
isHolding = false;
}
IEnumerator Holding()
{
while (timer > 0)
{
//if (scrollRect.velocity.x >= maxVelocity)
//{
// isHolding = false;
//}
if (!isHolding)
{
timer = holdTime;
yield break;
}
timer -= Time.deltaTime;
Debug.Log(timer);
yield return null;
}
dragHandler.enabled = true;
//dragHandler.OnBeginDrag();
}
}
Slot:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Slot : MonoBehaviour, IDropHandler
{
public void OnDrop(PointerEventData eventData)
{
DragHandler.itemBeingDragged.transform.SetParent(transform);
}
}
Answer:
I wrote some codes that handle drag and drop in scrollRect(scrollView) without using DragHandler interfaces.
DragHandler:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class DragHandler : MonoBehaviour, IPointerExitHandler
{
public static GameObject itemBeingDragged;
public static bool isCustomerDragged;
public Transform customerScrollRect;
public Transform dragParent;
public float holdTime;
public float maxScrollVelocityInDrag;
private Transform startParent;
private ScrollRect scrollRect;
private float timer;
private bool isHolding;
private bool canDrag;
private bool isPointerOverGameObject;
private CanvasGroup canvasGroup;
private Vector3 startPos;
public Transform StartParent
{
get { return startParent; }
}
public Vector3 StartPos
{
get { return startPos; }
}
void Awake()
{
canvasGroup = GetComponent<CanvasGroup>();
}
// Use this for initialization
void Start()
{
timer = holdTime;
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (EventSystem.current.currentSelectedGameObject == gameObject)
{
//Debug.Log("Mouse Button Down");
scrollRect = customerScrollRect.GetComponent<ScrollRect>();
isPointerOverGameObject = true;
isHolding = true;
StartCoroutine(Holding());
}
}
if (Input.GetMouseButtonUp(0))
{
if (EventSystem.current.currentSelectedGameObject == gameObject)
{
//Debug.Log("Mouse Button Up");
isHolding = false;
if (canDrag)
{
itemBeingDragged = null;
isCustomerDragged = false;
if (transform.parent == dragParent)
{
canvasGroup.blocksRaycasts = true;
transform.SetParent(startParent);
transform.localPosition = startPos;
}
canDrag = false;
timer = holdTime;
}
}
}
if (Input.GetMouseButton(0))
{
if (EventSystem.current.currentSelectedGameObject == gameObject)
{
if (canDrag)
{
//Debug.Log("Mouse Button");
transform.position = Input.mousePosition;
}
else
{
if (!isPointerOverGameObject)
{
isHolding = false;
}
}
}
}
}
public void OnPointerExit(PointerEventData eventData)
{
isPointerOverGameObject = false;
}
IEnumerator Holding()
{
while (timer > 0)
{
if (scrollRect.velocity.x >= maxScrollVelocityInDrag)
{
isHolding = false;
}
if (!isHolding)
{
timer = holdTime;
yield break;
}
timer -= Time.deltaTime;
//Debug.Log("Time : " + timer);
yield return null;
}
isCustomerDragged = true;
itemBeingDragged = gameObject;
startPos = transform.localPosition;
startParent = transform.parent;
canDrag = true;
canvasGroup.blocksRaycasts = false;
transform.SetParent(dragParent);
}
public void Reset()
{
isHolding = false;
canDrag = false;
isPointerOverGameObject = false;
}
}
Some explanation for this piece of code :
Your draggable UI element need intractable option, for me, I used button.
You need to attach this script to your draggable item.
Also you need add Canvas Group component.
customerScrollRect is a ScrollRect parent of your items.
dragParent can be a empty GameObject which is used because of mask of view port.
Slot:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Slot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
bool isEntered;
public void OnPointerEnter(PointerEventData eventData)
{
isEntered = true;
}
public void OnPointerExit(PointerEventData eventData)
{
isEntered = false;
}
void Update()
{
if (Input.GetMouseButtonUp(0))
{
if (isEntered)
{
if (DragHandler.itemBeingDragged)
{
GameObject draggedItem = DragHandler.itemBeingDragged;
DragHandler dragHandler = draggedItem.GetComponent<DragHandler>();
Vector3 childPos = draggedItem.transform.position;
//Debug.Log("On Pointer Enter");
draggedItem.transform.SetParent(dragHandler.StartParent);
draggedItem.transform.localPosition = dragHandler.StartPos;
draggedItem.transform.parent.SetParent(transform);
draggedItem.transform.parent.position = childPos;
isEntered = false;
}
}
}
}
}
Some explanation for this script:
1.Attach the script to the dropped item.
The easiest solution for this problem is actually to manually call the ScrollRect's events IF the user hasn't pressed long enough using ExecuteEvents.Execute. This solution has the least amount of additional code.
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class DragAndDropHandler : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IDragHandler, IBeginDragHandler, IEndDragHandler
{
// Don't forget to set this to TRUE or expose it to the Inspector else it will always be false and the script will not work
public bool Draggable { get; set; }
private bool draggingSlot;
[SerializeField] private ScrollRect scrollRect;
public void OnPointerDown(PointerEventData eventData)
{
if (!Draggable)
{
return;
}
StartCoroutine(StartTimer());
}
public void OnPointerExit(PointerEventData eventData)
{
StopAllCoroutines();
}
public void OnPointerUp(PointerEventData eventData)
{
StopAllCoroutines();
}
private IEnumerator StartTimer()
{
yield return new WaitForSeconds(0.5f);
draggingSlot = true;
}
public void OnBeginDrag(PointerEventData eventData)
{
ExecuteEvents.Execute(scrollRect.gameObject, eventData, ExecuteEvents.beginDragHandler);
}
public void OnDrag(PointerEventData eventData)
{
if (draggingSlot)
{
//DO YOUR DRAGGING HERE
} else
{
//OR DO THE SCROLLRECT'S
ExecuteEvents.Execute(scrollRect.gameObject, eventData, ExecuteEvents.dragHandler);
}
}
public void OnEndDrag(PointerEventData eventData)
{
ExecuteEvents.Execute(scrollRect.gameObject, eventData, ExecuteEvents.endDragHandler);
if (draggingSlot)
{
//END YOUR DRAGGING HERE
draggingSlot = false;
}
}
}
I've managed to find an easier solution.
Maybe it must be customized by special needs, but if you put this script on your items and make the setup, it should work.
It's not easy from a prefab, but I let a controller make this.
I find all UIElementDragger inside a specified GameObject and add the needed GO instances programmatically.
But can use this code out of the box if you don't use prefabs.
using UnityEngine;
using UnityEngine.EventSystems;
public class UIElementDragger : MonoBehaviour, IPointerUpHandler, IPointerDownHandler
{
/// <summary>
/// Offset in pixels horizontally (positive to right, negative to left)
/// </summary>
[Range(40, 100)]
public float offsetX = 40;
/// <summary>
/// Offset in pixels vertically (positive to right, negative to left)
/// </summary>
[Range(40, 100)]
public float offsetY = 40;
/// <summary>
/// The Panel where the item will set as Child to during drag
/// </summary>
public Transform parentRect;
/// <summary>
/// The GameObject where the item is at start
/// </summary>
public Transform homeWrapper;
/// <summary>
/// The Object where the mouse must be when pointer is up, to put it in this panel
/// </summary>
public Transform targetRect;
/// <summary>
/// The GameObject where the item should live after dropping
/// </summary>
public Transform targetWrapper;
private int siblingIndex;
private bool dragging;
private void Start()
{
siblingIndex = transform.GetSiblingIndex();
}
private void Update()
{
if (dragging)
{
transform.position = new Vector2(Input.mousePosition.x + offsetX, Input.mousePosition.y + offsetY);
}
}
public void OnPointerDown(PointerEventData eventData)
{
transform.parent = parentRect;
dragging = true;
}
public void OnPointerUp(PointerEventData eventData)
{
if (eventData.pointerCurrentRaycast.gameObject.transform.IsChildOf(targetRect))
{
transform.parent = targetWrapper;
}
else
{
transform.parent = homeWrapper;
transform.SetSiblingIndex(siblingIndex);
}
dragging = false;
}
}
Here my solution, thank all.
I have to use a fake instance to keep the object over even if you move it down the list. I post a vertical and a horizontal solution. I hope it help
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class DragController : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
{
private RectTransform currentTransform;
private GameObject instancePrefab;
private GameObject mainContent;
private Vector3 initialePosition;
private int totalChild;
void Awake()
{
currentTransform = this.GetComponent<RectTransform>();
mainContent = currentTransform.parent.gameObject;
}
public void OnPointerDown(PointerEventData eventData)
{
initialePosition = currentTransform.position;
totalChild = mainContent.transform.childCount;
if (instancePrefab == null)
{
instancePrefab = Instantiate(this.gameObject, mainContent.transform.parent.transform);
instancePrefab.GetComponent<Image>().enabled = false;
}
}
public void OnDrag(PointerEventData eventData)
{
currentTransform.position = new Vector3(eventData.position.x, currentTransform.position.y, currentTransform.position.z);
if (instancePrefab != null)
{
instancePrefab.GetComponent<Image>().enabled = true;
currentTransform.GetComponent<Image>().enabled = false;
instancePrefab.transform.position = currentTransform.position;
}
for (int i = 0; i < totalChild; i++)
{
if (i != currentTransform.GetSiblingIndex())
{
Transform otherTransform = mainContent.transform.GetChild(i);
int distance = (int)Vector3.Distance(currentTransform.position, otherTransform.position);
if (distance <= 20)
{
Vector3 otherTransformOldPosition = otherTransform.position;
// Vertical
/*otherTransform.position = new Vector3(otherTransform.position.x, initialePosition.y,
otherTransform.position.z);
currentTransform.position = new Vector3(currentTransform.position.x, otherTransformOldPosition.y,
currentTransform.position.z);*/
// Horizontal
otherTransform.position = new Vector3(initialePosition.x, otherTransform.position.y,
otherTransform.position.z);
currentTransform.position = new Vector3(otherTransformOldPosition.x, currentTransform.position.y,
currentTransform.position.z);
currentTransform.SetSiblingIndex(otherTransform.GetSiblingIndex());
initialePosition = currentTransform.position;
}
}
}
}
public void OnPointerUp(PointerEventData eventData)
{
currentTransform.position = initialePosition;
instancePrefab.GetComponent<Image>().enabled = false;
currentTransform.GetComponent<Image>().enabled = true;
Destroy(instancePrefab);
instancePrefab = null;
}
}
You can just manually call ScrollRect events, because they are public. Smthing like this:
public void OnDrag(PointerEventData eventData)
{
_scrollRect.OnDrag(eventData);
}