I'm using the event OnTriggerStay2D to destroy an object, the reason i'm using this instead of OnTriggerEnter2D is because i'm dragging the object using the touchscreen, and i want to destroy it after it is released, the problem i have is that OntriggerStay2D is not always called, so sometimes the object is not destroyed after it is released and it has to be moved again to work, i've read the docummentation from Unity
OnTriggerStay is called almost all the frames for every Collider other that is touching the trigger.
public void OnTriggerStay2D(Collider2D other)
{
if (gameObject.tag == other.tag) {
Destroy (other.gameObject);
}
}
I would like to know if there's any way to call OntriggerStay2D everytime i release the object.
Thanks.
Edit
Dragging code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Drag : MonoBehaviour {
private bool draggingItem = false;
private GameObject draggedObject;
private Vector2 touchOffset;
void Update ()
{
if (HasInput)
{
DragOrPickUp();
}
else
{
if (draggingItem)
DropItem();
}
}
Vector2 CurrentTouchPosition
{
get
{
Vector2 inputPos;
inputPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
return inputPos;
}
}
private void DragOrPickUp()
{
var inputPosition = CurrentTouchPosition;
if (draggingItem)
{
draggedObject.transform.position = inputPosition + touchOffset;
}
else
{
RaycastHit2D[] touches = Physics2D.RaycastAll(inputPosition, inputPosition, 0.5f);
if (touches.Length > 0)
{
var hit = touches[0];
if (hit.transform != null && hit.rigidbody != null)
{
draggingItem = true;
draggedObject = hit.transform.gameObject;
touchOffset = (Vector2)hit.transform.position - inputPosition;
}
}
}
}
private bool HasInput
{
get
{
return Input.GetMouseButton(0);
}
}
public void DropItem()
{
draggingItem = false;
}
}
Avoid using OnTriggerStay2D for this. You can use a boolean variable that you set to true and false in the OnTriggerEnter2D and OnTriggerExit2D function.
bool isTouching = false;
void OnTriggerEnter2D(Collider2D collision)
{
Debug.Log("Entered");
if (collision.gameObject.CompareTag("YourOtherObject"))
{
isTouching = true;
}
}
void OnTriggerExit2D(Collider2D collision)
{
Debug.Log("Exited");
if (collision.gameObject.CompareTag("YourOtherObject"))
{
isTouching = false;
}
}
You can now check the isTouching variable when the object is released.
if(isTouching){
....
}
Note that I suggest you abandon your current code that uses Raycast and Input.GetMouseButton(0); since you are using this on mobile devices too. You should be using Unity's new EventSystem for this since it is made to be mobile friendly too.
Since you are using 2D collider, see #7 from this answer.
Here is a complete example of how to drag a Sprite with the new EventSystem. Combine that with the answer above and you get a much more better solution.
Related
I want to place an object in-game. I have a UI button to make the object appear, but as Unity only runs the function triggered by the button one time, the engine doesn't continuously move the object to the position of the mouse, which is what I want it to do. How do I fix it?
This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlaceObjectsWithUI : MonoBehaviour
{
[SerializeField]
private GameObject placeableObjectPrefab;
private GameObject currentPlaceableObject;
public void PlaceHouse()
{
if (currentPlaceableObject == null)
{
currentPlaceableObject = Instantiate(placeableObjectPrefab);
}
else
{
Destroy(currentPlaceableObject);
}
if (currentPlaceableObject != null)
{
MoveCurrentPlaceableObjectToMouse(); //This is the function I want to be repeated
ReleaseIfClicked();
}
}
private void MoveCurrentPlaceableObjectToMouse()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo))
{
currentPlaceableObject.transform.position = hitInfo.point;
currentPlaceableObject.transform.rotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
}
}
private void ReleaseIfClicked()
{
if (Input.GetMouseButtonDown(0))
{
currentPlaceableObject = null;
}
}
}
I tried a while-loop to make Unity run the code until the mouse is clicked, but then Unity freezes. I think it gets stuck in the while-loop.
I think what you're after is the Update method? If you place the final if statement found within PlaceHouse() into the Update method, then it will constantly be checked.
Try adding this:
private void Update()
{
if (currentPlaceableObject != null)
{
MoveCurrentPlaceableObjectToMouse();
ReleaseIfClicked();
}
}
i am trying to make ragdolls turn on when clicked, but i dont know how to do this. I have ragdoll already but script for turning on ragdoll doesnt works.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RagdollEnemy : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
setRigidbodyState(true);
setColliderState(false);
GetComponent<Animator>().enabled = true;
}
public void die()
{
GetComponent<Animator>().enabled = false;
setRigidbodyState(false);
setColliderState(true);
if (gameObject != null)
{
Destroy(gameObject, 3f);
}
}
void setRigidbodyState(bool state)
{
Rigidbody[] rigidbodies = GetComponentsInChildren<Rigidbody>();
foreach (Rigidbody rigidbody in rigidbodies)
{
rigidbody.isKinematic = state;
}
GetComponent<Rigidbody>().isKinematic = !state;
}
void setColliderState(bool state)
{
Collider[] colliders = GetComponentsInChildren<Collider>();
foreach (Collider collider in colliders)
{
collider.enabled = state;
}
GetComponent<Collider>().enabled = !state;
}
}
It may be not proper way but, you can use ragdoll gameobject as another gameobject that set active when you die and set position to player position and set active false your player.I use it like that in my game for my character and also for enemy characters.
so I’m trying to create a respawn system as a beginner, and everything seems to be working the first time but the second time it seems to be unbehaving. If anyone knows how to help me, I would appreciate it
LevelControl:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelControl : MonoBehaviour
{
public int index;
public string levelName;
public GameObject GameOverPanel;
public static LevelControl instance;
private void Awake()
{
if (instance == null)
{
DontDestroyOnLoad(gameObject);
instance = GetComponent<LevelControl>();
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
//Loading level with build index
SceneManager.LoadScene(index);
//Loading level with scene name
SceneManager.LoadScene(levelName);
//Restart level
//SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
public void LoadLevel1()
{
SceneManager.LoadScene("Game");
}
public GameObject GetGameOverScreen()
{
return GameOverPanel;
}
}
PlayerMovement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController2D controller;
public float runSpeed = 40f;
float horizontalMove = 0f;
bool jump = false;
bool crouch = false;
// Update is called once per frame
void Update()
{
horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
if (Input.GetButtonDown("Jump"))
{
jump = true;
}
if (Input.GetButtonDown("Crouch"))
{
crouch = true;
}
else if (Input.GetButtonUp("Crouch"))
{
crouch = false;
}
}
void FixedUpdate()
{
// Move our character
controller.Move(horizontalMove * Time.fixedDeltaTime, crouch, jump);
jump = false;
//FindObjectOfType<GameManager>().EndGame();
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Enemy")
{
//Destroy(FindObjectOfType<CharacterController2D>().gameObject);
GameObject.Find("Player").SetActive(false);
LevelControl.instance.GetGameOverScreen().SetActive(true);
}
}
}
Error In Unity Video: https://imgur.com/a/Sr0YCWk
If your wondering what I'm trying to do, well, when the 2 colliders of the Player and Enemy collide, I want a restart button to pop up, and the Character to get destroyed, and after that restart the level as is.
You didn't provide much but I try to work with what we have.
In LevelController you have
private void Awake()
{
if (instance == null)
{
DontDestroyOnLoad(gameObject);
instance = GetComponent<LevelControl>();
}
}
First of all just use
instance = this;
;)
Then you are doing
LevelControl.instance.GetGameOverScreenn().SetActive(true);
I don't see your setup but probably GetGameOverScreenn might not exist anymore after the Scene reload while the instance still does due to the DontDestroyOnLoad.
Actually, why even use a Singleton here? If you reload the entire scene anyway you could just setup the references once via the Inspector and wouldn't have to worry about them later after scene changes...
Also
GameObject.Find("Player").SetActive(false);
seems odd .. isn't your PlayerController attached to the Player object anyway? You could just use
gameObject.SetActive(false);
Ok, Normally it would be easier just to do this:
if (collision.gameObject.tag == "Enemy")
{
//Destroy(FindObjectOfType<CharacterController2D>().gameObject);
gameObject.SetActive(false);
LevelControl.instance.GetGameOverScreen().SetActive(true);
But this will NOT work if you want to attach the script to any other gameobjects for any reason. If you are then first make a game object variable containing the player, like this:
public GameObject Player = GameObject.Find("Player");
Then say
Player.SetActive(false);
This creates a player gameobject which you can access by calling the variable.
In a game I am working on, the player uses a cube to open doors. When the player interacts with the cube while near the door, the door slides open. I am now trying to make it so that while it slides open, it makes a sound as doors should but I kinda ran into some issues. When I tried to add in the sound for the door, it ignored the part where the door should open altogether.
Here is what I did:
I added an AudioSource to the Cube's child object, CORE because the Cube object already contains an AudioSource which will play at the same time as this sound and assigned it in my Cube's script...
public AudioSource Woosh;
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
Here's the interactive part of the script, it runs a check but for some reason, it is pretending CanBeKey is false...
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
Now here is the IEnumerator part of the script, PlayDaWoosh()
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
I am aware that the last code snippet is a bit messy but it was the best thing I can think of.
Here is the full script in case you are that curious.....
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CUBE_CORE : MonoBehaviour
{
public AudioSource Hm; // HM!!!
private bool HasPlayed = false;
public float PlayTimeOut = 0.0f;
public static bool CanBeKey = false;
public Animator anim;
public static bool HasPlayedFirstTime = false;
public static AudioSource Door;
public AudioSource Woosh;
// Start is called before the first frame update
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if(HasPlayed == true)
{
if (PlayTimeOut < 0.0f)
{
HasPlayed = false;
}
PlayTimeOut -= Time.smoothDeltaTime;
}
}
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name == "SecondDoor")
{
anim = GameObject.Find("DoorSecond").GetComponent<Animator>();
}
if(other.gameObject.name == "ThirdDoor")
{
anim = GameObject.Find("TheThirdDoor").GetComponent<Animator>();
}
if(other.gameObject.name == "FourthDoor")
{
anim = GameObject.Find("TheFourthDoor").GetComponent<Animator>();
}
}
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
}
Expected result: Door opening and sound playing
Actual result: Neither happening unless I remove anything that has to do with the Woosh
I apologize ahead of time if I wasn't specific enough or if the question was answered somewhere else. I am relatively new here.
I have basic snapping of objects in place in my test scene, but when I go to unsnap the objects the snapped object will resnap unless I move it away very quickly.
For context of the scene setup, I want to implement snapping objects in VR so I have a smaller parent object inside the object I'm moving to represent the hand the object would be parented to in VR. Also I have the snapping script (first one below) attached to the 'hand' object, and am using trigger colliders on the hand & the square obj it attaches to.
Any clue how I can fix this so I don't need to pull quickly to decouple the object?
Video of the bug happening
Below are the scripts I'm using to implement this
public class snap : MonoBehaviour, collider_helper.collider_help_reciever
{
public Transform snapObj;
public Transform SnapTarget;
bool snapped = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void OnTriggerEnter (Collider other)
{
Debug.Log("enter");
if (snapped == false && other.transform.GetInstanceID() == SnapTarget.GetInstanceID())
{
snapObj.position = SnapTarget.position;
snapObj.rotation = SnapTarget.rotation;
snapObj.parent = SnapTarget;
snapped = true;
}
}
public void OnTriggerExit(Collider other)
{
Debug.Log("exit");
if (snapped == true && other.transform.GetInstanceID() == SnapTarget.GetInstanceID())
{
snapObj.position = transform.position;
snapObj.rotation = transform.rotation;
snapObj.parent = transform;
snapped = false;
StartCoroutine(noSnap);
}
}
}
public class collider_helper : MonoBehaviour {
public GameObject recieving;
collider_help_reciever reciever;
// Use this for initialization
void Start () {
reciever = recieving.GetComponent<collider_help_reciever>();
}
// Update is called once per frame
void Update () {
}
void OnTriggerStay (Collider other)
{
reciever.OnTriggerEnter(other);
}
public interface collider_help_reciever
{
void OnTriggerEnter(Collider other);
}
}
Well the answer turned out to be adding a float (lastSnapTime) to store the last time the objects decoupled, and only allowing snapping to happen if some time had passed since they last decoupled:
public void OnTriggerEnter(Collider other)
{
Debug.Log("enter");
if (other.transform.GetInstanceID() == SnapTarget.GetInstanceID())
{
if (lastSnapTime + 0.1f < Time.time)
{
snapObj.position = SnapTarget.position;
snapObj.rotation = SnapTarget.rotation;
snapObj.parent = SnapTarget;
}
}
}
public void OnTriggerExit(Collider other)
{
Debug.Log("exit");
if (other.transform.GetInstanceID() == SnapTarget.GetInstanceID())
{
lastSnapTime = Time.time;
snapObj.position = transform.position;
snapObj.rotation = transform.rotation;
snapObj.parent = transform;
//StartCoroutine(noSnap);
}
}