- Home /
My script for grabbing and carrying objects in 2D doesn't work properly.
For the most part, I can pick up, carry and drop one object with no problems. However when I add another object with the same tag. It picks up the previous object.
I wish I could show a video, however the file size is too big.
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ActionScript : MonoBehaviour
{
private bool canHold;
[SerializeField]
private string grabbableTag;
[SerializeField]
private float distance;
private GameObject items;
public GameObject tempParent;
public GameObject placePos;
public bool isHolding;
public LayerMask whatIsGrabbable;
public Transform player;
public Transform holdPos;
public Transform rayPos2;
void Start()
{
items = GameObject.FindWithTag("Grabbable");
items.GetComponent<Rigidbody2D>();
}
void Update()
{
PickAndDrop();
}
private void PickAndDrop()
{
{
Vector2 checkPos = rayPos2.position;
Physics2D.queriesStartInColliders = false;
RaycastHit2D hit = Physics2D.Raycast(checkPos, transform.right * transform.localScale.x, distance, whatIsGrabbable);
{
if (hit.collider != null && items.tag == "Grabbable" && Input.GetKeyDown(KeyCode.E))
{
canHold = true;
if (canHold)
{
isHolding = true;
items.GetComponent<Rigidbody2D>().isKinematic = true;
items.transform.eulerAngles = new Vector3(0, 0, 0);
items.transform.SetParent(holdPos);
items.transform.position = tempParent.transform.position;
}
}
else if (isHolding && Input.GetKeyDown(KeyCode.E))
{
canHold = true;
isHolding = false;
items.GetComponent<Rigidbody2D>().isKinematic = false;
items.transform.eulerAngles = new Vector3(0, 0, 0);
items.transform.SetParent(null, true);
items.transform.position = placePos.transform.position;
}
}
}
}
private void OnDrawGizmos()
{
Vector2 checkPos = rayPos2.position;
Gizmos.color = Color.green;
Gizmos.DrawLine(checkPos, (Vector2)checkPos + (Vector2)transform.right * transform.localScale.x * distance);
}
}
Answer by Llama_w_2Ls · Dec 30, 2020 at 08:14 PM
This line in your start method items = GameObject.FindWithTag("Grabbable");
gets the first object with that tag in the scene, and forgets about the rest. You dont even need a reference to these, since your hit.collisionInfo from your raycast should handle this for you:
In here:
items.GetComponent<Rigidbody2D>().isKinematic = true;
items.transform.eulerAngles = new Vector3(0, 0, 0);
items.transform.SetParent(holdPos);
items.transform.position = tempParent.transform.position;
And here:
items.GetComponent<Rigidbody2D>().isKinematic = false;
items.transform.eulerAngles = new Vector3(0, 0, 0);
items.transform.SetParent(null, true);
items.transform.position = placePos.transform.position;
You can replace items
with hit.collider.gameObject
and put it in an if statement, so that it only works if the hit.collider.gameObject.tag == "Grabbable";
@AmateurProgramming
Answer by AmateurPrograming · Dec 30, 2020 at 09:31 PM
@Llama_w_2Ls I tried that and I got an error.
NullReferenceException: Object reference not set to an instance of an object
New Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ActionScript : MonoBehaviour
{
private bool canHold;
[SerializeField]
private string grabbableTag;
[SerializeField]
private float distance;
private GameObject items;
Vector3 itemPos;
public GameObject tempParent;
public GameObject placePos;
public bool isHolding;
public LayerMask whatIsGrabbable;
[SerializeField]
private float checkHoldRadius;
public Transform player;
public Transform holdPos;
public Transform rayPos2;
void Start()
{
}
void Update()
{
PickAndDrop();
}
private void PickAndDrop()
{
{
Vector2 checkPos = rayPos2.position;
Physics2D.queriesStartInColliders = false;
RaycastHit2D hit = Physics2D.Raycast(checkPos, transform.right * transform.localScale.x, distance, whatIsGrabbable);
{
if (hit.collider.gameObject.tag == "Grabbable" && Input.GetKeyDown(KeyCode.E))
{
canHold = true;
if (canHold)
{
isHolding = true;
hit.collider.gameObject.GetComponent<Rigidbody2D>().isKinematic = true;
hit.collider.gameObject.transform.eulerAngles = new Vector3(0, 0, 0);
hit.collider.gameObject.transform.SetParent(holdPos);
hit.collider.gameObject.transform.position = tempParent.transform.position;
}
}
else if (isHolding && Input.GetKeyDown(KeyCode.E))
{
canHold = true;
isHolding = false;
hit.collider.gameObject.GetComponent<Rigidbody2D>().isKinematic = false;
hit.collider.gameObject.transform.eulerAngles = new Vector3(0, 0, 0);
hit.collider.gameObject.transform.SetParent(null, true);
hit.collider.gameObject.transform.position = placePos.transform.position;
}
}
}
}
private void OnDrawGizmos()
{
Vector2 checkPos = rayPos2.position;
Gizmos.color = Color.green;
Gizmos.DrawLine(checkPos, (Vector2)checkPos + (Vector2)transform.right * transform.localScale.x * distance);
}
}
New code that kind of works, however I can't drop the item anymore. I am still getting the error.
New Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ActionScript : $$anonymous$$onoBehaviour
{
private bool canHold;
[SerializeField]
private string grabbableTag;
[SerializeField]
private float distance;
private GameObject items;
Vector3 itemPos;
public GameObject tempParent;
public GameObject placePos;
public bool isHolding;
public Layer$$anonymous$$ask whatIsGrabbable;
public Transform player;
public Transform holdPos;
public Transform rayPos2;
void Start()
{
}
void Update()
{
PickAndDrop();
}
private void PickAndDrop()
{
{
Vector2 checkPos = rayPos2.position;
Physics2D.queriesStartInColliders = false;
RaycastHit2D hit = Physics2D.Raycast(checkPos, transform.right * transform.localScale.x, distance, whatIsGrabbable);
{
if (Input.GetKeyDown(KeyCode.E))
{
canHold = true;
if (canHold)
{
isHolding = true;
hit.collider.gameObject.GetComponent<Rigidbody2D>().isKinematic = true;
hit.collider.gameObject.transform.eulerAngles = new Vector3(0, 0, 0);
hit.collider.gameObject.transform.SetParent(holdPos);
hit.collider.gameObject.transform.position = tempParent.transform.position;
}
}
else if (isHolding && Input.GetKeyDown(KeyCode.E))
{
canHold = true;
isHolding = false;
hit.collider.gameObject.GetComponent<Rigidbody2D>().isKinematic = false;
hit.collider.gameObject.transform.eulerAngles = new Vector3(0, 0, 0);
items.transform.SetParent(null, true);
hit.collider.gameObject.transform.position = placePos.transform.position;
}
}
}
}
private void OnDrawGizmos()
{
Vector2 checkPos = rayPos2.position;
Gizmos.color = Color.green;
Gizmos.DrawLine(checkPos, (Vector2)checkPos + (Vector2)transform.right * transform.localScale.x * distance);
}
}
I believe that you need to do a check to see if the raycast actually hit a collider, before you use hit.collider.gameObject. For example:
if (Input.GetKeyDown(KeyCode.E) && hit.collider != null)
{
canHold = true;
if (canHold)
{
isHolding = true;
GameObject grabbedObject = hit.collider.gameObject;
grabbedObject.GetComponent<Rigidbody2D>().isKinematic = true;
grabbedObject.transform.eulerAngles = new Vector3(0, 0, 0);
grabbedObject.transform.SetParent(holdPos);
grabbedObject.transform.position = tempParent.transform.position;
}
}
If this still causes an error, the grabbed object might not have a rigidbody2D, and so you might need to do another check as well (only if you have grabbable objects without a rigidbody2D).
Also, in the else-if, which detects if you dropped an item, you don't need to do a raycast, and therefore, using hit.collider.gameObject doesn't make sense. ($$anonymous$$y bad). You should have a private GameObject at the top of the script called CurrentlyGrabbedObject, or something, and set it to the grabbed object, and then set it to null when dropping it. Hope that makes sense. @AmateurProgra$$anonymous$$g