Grab and drop object - c#

I'm trying new things and I'm stuck to find a way to drag and drop objects. My player is a square and he have a hand attached. Here is an example:
That red thing in the arm is the "hand", when I press shift it turns green. I made the detection like a ground check. Here is the code:
void Update () {
touch = Physics2D.OverlapCircle(touchDetect.position, 0.01f, objectLayer);
mao1.SetBool("Ligado", ligado);
if (Input.GetKey(KeyCode.LeftShift)) {
ligado = true;
} else {
ligado = false;
}
}
The touchDetect is working fine, because he turns to "true" when touches the box:
My question is: I don't know how to put in the script that I want him to grab the object and drop when I want to. If I need to use Raycast, how would the code looks like?

In order to "grab" an object in unity, simply set the transform.parent of the gameobject you wish to grab, to the transform of the gameobject you wish to grab it with.
For example, in your code you are using Physics2D.OverlapCircle which returns a Collider2D object. You can use that collider to grab on to your gameobject.
Collider2D touch = Physics2D.OverlapCircle(touchDetect.position, 0.01f, objectLayer);
if (Input.GetKey(KeyCode.LeftShift) && touch != null)
{
//grab on to the object
touch.gameObject.transform.parent = this.transform;
//if your box has a rigidbody on it,and you want to take direct control of it
//you will want to set the rigidbody iskinematic to true.
GetComponent<Rigidbody2D>().isKinematic = true;
}
else if( touch != null)
{
//let the object go
touch.gameObject.transform.parent = null;
//if your object has a rigidbody be sure to turn kinematic back to false
GetComponent<Rigidbody2D>().isKinematic = false;
}

Just place a GameObject with SprieRender component attach it to hand tip.
Put this script to that qube and follow what I commented
using UnityEngine;
using System.Collections;
public class collid : MonoBehaviour {
// Use this for initialization
public Sprite mySprite; // Put Here that Qube Sprite
public GameObject Cursorimg; // Put the gameobject here from hierarchy
void Start () {
mySprite = GetComponent<SpriteRenderer>().sprite;
}
// Update is called once per frame
void Update () {
if(Collide conditions) {
Debug.Log(" Collide ");
Cursorimg.GetComponent<SpriteRenderer>().sprite = mySprite;
Cursorimg.GetComponent<SpriteRenderer>().sortingOrder = 3;// U change based on your option
}
if(DropCondition)
{
Debug.Log("Drop");
Cursorimg.GetComponent<SpriteRenderer>().sprite =null;
}
}
If this not works Here is a script that gameobject with Sprite will follow mouse position,
Vector3 pos = Input.mousePosition;
pos.z = Cursorimg.transform.position.z - Camera.main.transform.position.z;
Cursorimg.transform.position = Camera.main.ScreenToWorldPoint(pos);
this code follows in update and Cursorimg is Gameobject
I think This would help you, Let me know further

Related

Changing Tag of Collided Object Through Collision

Either the script is outdated, or it's not what I need, but I cannot find an answer to this.
To start off, I'm making a pinball styled game, whenever the ball hits a piece, it changes color, but I have multiple colored balls, and I want to lock the color in place as to not have the other balls change them (to make the game a little bit easier). I've provided a script, which may be a little too complex for a simple solution. The problem area is at the bottom with void FixedUpdate.
(I just wanna change a tag ): )
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ColorBlue : MonoBehaviour
{
public Material mat;
public string ballTag;
public bool reset = false;
public bool found = false;
public void OnCollisionEnter (Collision collisionInfo)
{
if (collisionInfo.collider.tag == "Ball" )
{
gameObject.GetComponent<MeshRenderer>().material.color = Color.blue;
reset = true;
}
}
public void FixedUpdate ()
{
if(reset)
{
GameObject.FindWithTag("Ball");
} found = true;
if(found)
{
GameObject.FindWithTag("Ball").tag = "Untagged";
}
}
}
Instead of searching for the GameObject with the "Ball" Tag which you probably have multiple off in your scene. You can rather just change the tag directly when the collision happens.
Because in the OnCollisionEnter Function you have a reference to the gameObject already, you can just use that to change the tag with collisionInfo.gameObject.tag = "Untagged".
public void OnCollisionEnter (Collision collisionInfo) {
if (collisionInfo.gameObject.CompareTag("Ball") && gameObject.CompareTag("Ball")) {
// Get Mesh Renderer of the Colided Ball Component.
var meshRenderer = collsionInfo.gameObject
.GetComponent<MeshRenderer>();
// Change Color of collided ball to be the same as the ball it collided with.
meshRenderer.material.color = gameObject
.GetComponent<MeshRenderer>().material.color;
// Set Tag to "Untagged" to make sure ball won't change color anymore
colliderInfo.gameObject.tag = "Untagged";
}
}
You could also add additional Code to change the color depending on the current Color of the gameObject. Additionaly I would advise you to use CompareTag(), which checks if the tag even exists in your scene.
If you want to get the collided gameObject you can do that with collisionInfo.gameObject.tag

how can I make my door open from a specific distance c#

I want to add doors to my Unity project and so far I have gotten the code to work with my door but I can open it from any distance in the scene. I want to be able to only open it from a small distance away but I cannot figure out how.
public class Door : MonoBehaviour
{
public bool doorIsOpen;
public Transform closedPos;
public Transform openPos;
public float openSpeed;
public float openRadius;
void Update()
{
if(Physics.CheckSphere(gameObject.transform.position, openRadius))
{
if(Input.GetKeyDown(KeyCode.E))
{
doorIsOpen = !doorIsOpen;
}
}
if(doorIsOpen == true)
{
OpenDoor();
}
if(doorIsOpen == false)
{
CloseDoor();
}
}
void OpenDoor()
{
doorIsOpen = true;
gameObject.transform.position = openPos.position;
gameObject.GetComponent<Animator>().SetTrigger("OpenDoor");
gameObject.GetComponent<Animator>().SetBool("DoorIsClosed", false);
gameObject.GetComponent<Animator>().SetBool("DoorIsOpen", true);
}
void CloseDoor()
{
doorIsOpen = false;
gameObject.transform.position = closedPos.position;
gameObject.GetComponent<Animator>().SetTrigger("CloseDoor");
gameObject.GetComponent<Animator>().SetBool("DoorIsOpen", false);
gameObject.GetComponent<Animator>().SetBool("DoorIsClosed", true);
}
}
Prerequisites
Add a sphere with a collision body around the door instance in Unity. This sphere functions as the radius which will trigger the ChangeDoorState method.
Change the update method
The update will look at any collisions happening in the specified sphere. If there is at least one object in range (the collision sphere) of the door, then it opens or closes the door instance. Source: https://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html
void Update()
{
Collider[] hitColliders = Physics.OverlapSphere(center, radius);
if (hitColliders.Length > 0)
{
ChangeDoorState();
}
}
Merged the OpenDoor and CloseDoor methods
void ChangeDoorState()
{
doorClosureState = doorClosure ? true : false;
gameObject.transform.position = doorClosureState ? closedPos.position : openPos.postition;
gameObject.GetComponent<Animator>().SetTrigger("DoorClosure");
gameObject.GetComponent<Animator>().SetBool("DoorIsOpen", doorClosureState);
gameObject.GetComponent<Animator>().SetBool("DoorIsClosed", !doorClosureState);
}
You can increase the value of 'openRadius', the game is creating a sphere at gameObject.transform.position with a radius of 'openRadius' and checking if there is any colliders overlapping the sphere.
//...
if(Physics.CheckSphere(gameObject.transform.position, openRadius))
//...
One issue is that you permanently set the triggers in every frame. You would only want to do so when you hit the key.
Then also Physics.CheckSphere checks whether there is any collider within the range. This could be any collider, not only the player. To make sure it only works if the player is the one in range you should definitely use Layers and give the player its own layer e.g. "Player". Then you can pass the layerMask parameter to make sure the doors only check for the player's layer.
so I would simply use
// if possible already reference this in the Inspector
[SerializeField] private Animator _animator;
[Tooltip("Here select only the Layer(s) which you assigned to your Player object(s)")]
[SerializeField] LayerMask _playerLayer;
private void Awake()
{
// as fallback get it ONCE on runtime
if(!_animator) _animator = GetComponent<Animator>();
}
private void Update()
{
// Afaik the Key is way cheaper to check so do it first
// and use physics only if actually necessary
if(Input.GetKeyDown(KeyCode.E))
{
// Only check for the Player's Layer and ignore other colliders
if(Physics.CheckSphere(transform.position, openRadius, _playerLayer.value))
{
ToggleDoorState();
}
}
}
// Since manually changing the flag in the Inspector will not have
// any effect anymore in order to be able to test it you can use
// the context menu of this component in the Inspector
[ContextMenu(nameof(ToggleDoorState))]
private void ToggleDoorState()
{
// invert the flag
doorIsOpen = !doorIsOpen;
// use the flag in ternary expressions
transform.position = doorIsOpen ? openPos.position : closedPos.position;
_animator.SetTrigger(doorIsOpen ? "OpenDoor" : "CloseDoor");
// use the value of the flag diectly
_animator.SetBool("DoorIsClosed", !doorIsOpen);
_animator.SetBool("DoorIsOpen", doorIsOpen);
}
It is seems a bit though as if you have a bit of redundancy in your animator. I would either use the Bools in order to trigger a transition or use the Triggers, to have both seems odd.

Oculus Go: Picking up/Moving object

I am very new to unity and am building a VR app for Oculus Go. I want to pick and move the object by pointing the ray from the controller on the object and then picking or releasing it by pressing the trigger button. I want the object to stay fixed at the end of the ray's position rather than coming suddenly onto the controller. I have used this script to create a ray and basically allow the controller to pick it up but this script shits the object to the controller's position and as a result I can only move object in a circle(in 360 degrees). It also does not drop the object correctly, as the objects continue to float.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerPointer : MonoBehaviour {
//Returns whatever object is infrount of the controller
private GameObject pointerOver;
[SerializeField]
//Is the object that is currently intractable
private PropBase selectedObject;
//Is the object currently stored in hand, ready to throw.
[SerializeField]
private PickUp inHand;
//This is a refrance to the object we want the pointer to be cast from.
[SerializeField]
public Transform controllerRef;
//This is where we want object we are holding to appear
[SerializeField]
private Transform holdingRef;
//The amount of force we want to throw objects from our hand with.
[SerializeField]
[Range(2,12)]
private float throwForce = 10;
//The script that handles the visuals to show what object is selected
[SerializeField]
private HighlightObject selectVisual;
private LineRenderer line;
void Start () {
line = GetComponent<LineRenderer> ();
}
void Update () {
//If a object is currently being held I don't want to select another
object until it is thrown.
if (inHand == null) {
WorldPointer ();
} else {
line.SetPosition (0, controllerRef.position);
line.SetPosition (1, controllerRef.position);
pointerOver = null;
}
//This function handles how you intract with selected objects
Intract ();
}
//This function handles shooting a raycast into the world from the
controller to see what can be intracted with.
void WorldPointer(){
//We set the line visual to start from the controller.
line.SetPosition (0, controllerRef.position);
RaycastHit hit;
//We reset the pointer so things don't stay selected when we are
pointing at nothing.
pointerOver = null;
//This sends a line from the controller directly ahead of it, it returns
true if it hits something. Using the RaycastHit we can then get information
back.
if (Physics.Raycast (controllerRef.position, controllerRef.forward, out
hit)) {
//Beacuse raycast is true only when it hits anything, we don't need
to check if hit is null
//We set pointerOver to whatever object the raycast hit.
pointerOver = hit.collider.gameObject;
//We set the line visual to stop and the point the raycast hit the
object.
line.SetPosition (1, hit.point);
//Here we check if the object we hit has the PropBase component, or
a child class of its.
if (pointerOver.GetComponent<PropBase> ()) {
//We set the object to be highlighted
selectVisual.NewObject (pointerOver);
} else {
selectVisual.ClearObject ();
}
} else {
//If the raycast hits nothing we set the line visual to stop a
little bit infrount of the controller.
line.SetPosition (1, controllerRef.position + controllerRef.forward
* 10);
selectVisual.ClearObject ();
}
Debug.DrawRay(controllerRef.position , controllerRef.forward *
10,Color.grey);
}
void Intract(){
//We set up the input "OculusTouchpad" in the Input manager
if (Input.GetButtonDown ("Jump") || OVRInput.GetDown
(OVRInput.Button.PrimaryTouchpad)) {
selectVisual.ClearObject ();
//Check if you are holding something you can throw first
if (inHand != null) {
inHand.Release (controllerRef.forward, throwForce);
inHand = null;
//We do this check here to prevent Errors if you have nothing
selected
} else if (selectedObject != null) {
//Check if you can pick up the selected object second
if (selectedObject.GetComponent<PickUp> ()) {
//Beacuse PickUp is a child of PropBase, we can ask InHand
to store selectedObject as PickUp, rather than use GetComponent
inHand = selectedObject as PickUp;
inHand.Store (holdingRef);
//If non of the above were valid then simple call the
trigger function of the selected object
} else {
selectedObject.Trigger ();
}
}
//If you have a object that you need to hold down a button to
intract with
} else if (Input.GetButton ("Jump") && selectedObject != null ||
OVRInput.Get (OVRInput.Button.PrimaryTouchpad) && selectedObject != null) {
selectedObject.Pulse ();
//When you are not pressing down the touchpad button, the selected
object can be updated
} else if (pointerOver != null) {
if (pointerOver.GetComponent<PropBase> ()) {
selectedObject = pointerOver.GetComponent<PropBase> ();
} else {
selectedObject = null;
}
} else {
selectedObject = null;
}
}
}
And i have attached this script to the objects I want to pick:
public class PickUp : PropBase
{
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
public virtual void Store(Transform NewParent)
{
//The following stops the object being effected by physics while it's in
the players hand
rb.isKinematic = true;
//And fixes it to the new parent it is given by the player script to
follow.
transform.parent = NewParent;
//It then resets it's position and rotation to match it's new parent
object
transform.localRotation = Quaternion.identity;
transform.localPosition = Vector3.zero;
}
public virtual void Release(Vector3 ThrowDir, float ThrowForce)
{
//On Release the object is made to be effected by physics again.
rb.isKinematic = false;
//Free itself from following it's parent object
transform.parent = null;
//And applies a burst of force for one frame to propel itself away from
the player.
rb.AddForce(ThrowDir * ThrowForce, ForceMode.Impulse);
}
}
What i'd like to see is have the position of the sphere change according to wherever the end of the ray is cast.
I have also attached this script to the player contoller, which allows it to move to a point by pointing to it and pressing the touchpad button.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClickToMove : MonoBehaviour
{
private Vector3 targetPos; //This Vector3 will store the position where we
click to move.
private bool Moving = false; /*This bool keeps track of whether we are in
the process of moving or not.*/
private GameObject targetInstance;
/*The variables we want to customize. Added info headers to these for the
Unity Editor.*/
[Header("Our Go controller object")]
public GameObject goController;
[Header("Movement Speed")]
public float speed = 1;
[Header("Stop When This Far Away From Target")]
public float haltDistance = 0;
[Header("Optional Target Object")]
public GameObject targetObj;
void Update()
{
MoveToTarget(); /*Here we simply run our MoveToTarget method in the
Update method.*/
//That way we don't clutter up the Update method with too much code.
}
void MoveToTarget() //Here we do the cluttering instead.
{
var ray = new Ray(goController.transform.position,
goController.transform.forward); /*Create a ray going from the goController
position and in the Forward direction of the goController.*/
RaycastHit hitInfo; //Store info about what the ray hits.
Physics.Raycast(ray, out hitInfo, 100);
if (OVRInput.GetUp(OVRInput.Button.PrimaryTouchpad)) /*If we release the
trigger..*/
{
targetPos = hitInfo.point; /*Make our targetPos assume the
positional value of the hit point.*/
if (targetObj) /*If we have specified a Target Object to mark where
we click*/
//If we didn't, then we don't want to try to instantiate it.
{
if (targetInstance) /*If there is already a Target Object in the
scene.*/
{
Destroy(targetInstance); //Destroy it.
}
targetInstance = Instantiate(targetObj, targetPos,
transform.rotation); //Create our Target object at the position we clicked.
}
Moving = true; //And finally we set Moving to True.
}
if (Moving == true) //Since Moving is now true
{
transform.position = Vector3.MoveTowards(transform.position, new
Vector3(targetPos.x, transform.position.y, targetPos.z), speed *
Time.deltaTime); /*Transform our x and z position to move towards the
targetPos.*/
/*Note that our y position is kept at default transform position
since we only want to move along the ground plane.*/
}
if (Vector3.Distance(transform.position, targetPos) <= haltDistance + 1)
/*Check proximity to targetPos. Mainly useful to keep your player from
setting a target position right next to say a building and then end up
clipping through half of it.*/
{
if (targetInstance) //If we created a Target Object..
{
Destroy(targetInstance); //Then we want to destroy it when we
reach it.
}
Moving = false; //Since we have now arrived at our target
//destination.
}
}
}
If anyone could point me in a right direction or help me with this, I would greatly appreciate it!
Thanks in advance.
Okay, with your updated question its now possible to try and answer.
First off - have you tried not resetting your BaseProp localPosition to the controller's?
Try commenting the line that says
transform.localPosition = Vector3.zero;
This wil still orient the object and parent it to the controller but will lock it in a position relative to the moment of parenting.
You currently use "holdingRef" object as a place where the object appears. You may want to use "controllerRef" instead.
To vary distance at which the object appears you can set the object position to:
controllerRef.position+ distance*controllerRef.forward
As this is the direction in which you fire your raycasts. You can get the hit distance by querying hit.distance.
If for any reason that doesn't work out for you, the very point of the raycast hitting the collider is available within HitInfo, so with hit.point you can extract the hit position and position the object relative to that point. Another very useful attribute of hitinfo is .normal, which enables you to get direction at which the hit happened.
You can pass that info along with your Store method.

A better way to detect the cursor?

Goal: Detect when the cursor has entered a defined radius of the player.
Hello, I am in the process of trying to replicate the combat system from a game called CrossCode. The idea is that when the cursor is within a certain radius of the player, I will be able to switch to melee combat, and back to ranged once the cursor leaves this radius.
I have implemented one way I thought it could be done, however it feels slow or unreliable and I just wanted to know if there are any other methods I could possibly look into to achieve a smoother result.
Here is what I've done
attached to the player
void Update()
{
attackStyleSwitchRadius = colRef.radius;
playerCenter = colRef.transform.position;
if(Physics2D.OverlapCircle(playerCenter, attackStyleSwitchRadius, cursor))
{
meleeMode = true;
rangeMode = false;
}
else
{
meleeMode = false;
rangeMode = true;
}
}
And on a small 2D object I have this script so that it follows the cursor position.
void Update()
{
pos = Input.mousePosition;
gameObject.transform.position = Camera.main.ScreenToWorldPoint(pos);
}
when the small object enters the overlap circle it changes the bools around.
You can remove the collision detection overhead by doing something like this instead;
void Update ()
{
attackStyleSwitchRadius = colRef.radius;
playerCenter = colRef.transform.position;
var mouse = Input.mousePosition;
mouse.z = Vector3.Distance(Camera.main.transform.position, playerCenter);
var range = Vector2.Distance(Camera.main.ScreenToWorldPoint(mouse), playerCenter);
var inside = range < attackStyleSwitchRadius;
meleeMode = inside;
rangeMode = !inside;
}
Cursor
Make an object, name it cursor.
Add a small collider to the cursor object.
Add a script to the cursor object so its always at the mouses location.
Melee range zone
Add a GameObject as child of the player, name it MeleeRangeZone.
Add a collider to it, set it to be a Trigger. The size of this collider will be the players melee range,
Add a rigidbody to it so that collisions can be detected, but set the rigidbody to not rotate or change its position.
Add a script to the object and use the OnTriggerEnter and OnTriggerExit methods to detect whether or not the cursor has entered your melee zone.
You can now use the OnTriggerEnter and OnTriggerExit methods to switch between the players attack modes, meaning that when the cursor enters it changes to melee and when it exit it changes to ranged.
You can fire the ray to detect the location the cursor should have like this:
public LayerMask RaycastCollidableLayers;
public RaycastHit Hit;
public float CheckDistance = 200f;
public Transform Cursor;
void PerformRaycast(){
//Set up ray
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//Fire ray
Physics.Raycast(ray, out Hit, CheckDistance + 0.1f, RaycastCollidableLayers);
if (Hit.collider == null)
{
//Debug.Log("Raycast hit nothing");
return;
}
else //Ray hit something
{
//Move cursor to Hit.point;
Cursor.position = Hit.point;
}
}

Deleting previous added Component of a GameObject

I have this script where it would add the SelectMove script at runtime when a character was touched by the user.
public GameObject target;
void Start () {
}
// Update is called once per frame
void Update () {
if(Input.touchCount > 0 || Input.GetTouch(0).phase == TouchPhase.Began){
ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
Debug.DrawRay(ray.origin,ray.direction * 20,Color.red);
if(Physics.Raycast(ray, out hit,Mathf.Infinity)){
Debug.Log(hit.transform.gameObject.name);
target = hit.transform.gameObject;
//Destroy(hit.transform.gameObject);
selectedPlayer();
}
}
}
void selectedPlayer(){
target.AddComponent(Type.GetType("SelectMove"));
}
In the code above, if the user will click Player A, Player A will move using accelerometer. What I need to do is if I click another character, say Player B, I need Player A to stop moving and it is now Player B's time to move. But I seemed not to get what I want. I tried destroying the script by using Destroy(this) or by this code:
if (target != null)
{
var sphereMesh = target.GetComponent<SelectMove>();
Destroy(sphereMesh);
}
But it is still not working.
If I don't destroy the previous added script, if the user clicks for another character, the previous selected player still moves along with the new one.
Is there another way that I could achieve what I needed to do?
public GameObject target = null;
void Update ()
{
if(Input.touchCount > 0 || Input.GetTouch(0).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
Debug.DrawRay(ray.origin,ray.direction * 20,Color.red);
RaycastHit hit;
if(Physics.Raycast(ray, out hit,Mathf.Infinity))
{
Debug.Log(hit.transform.gameObject.name);
if(this.target != null){
SelectMove sm = this.target.GetComponent<SelectMove>()
if(sm != null){ sm.enabled = false; }
}
target = hit.transform.gameObject;
//Destroy(hit.transform.gameObject);
selectedPlayer();
}
}
}
void selectedPlayer(){
SelectMove sm = this.target.GetComponent<SelectMove>();
if(sm == null){
target.AddComponent<SelectMove>();
}
sm.enabled = true;
}
The code reuses your base, the new part is that you use the previous selected object to disable the SelectMove script (I assume it is the one moving things). Instead of destroying it, it enables/disables so you save that memory consumption.
I do not think Adding and destroying scripts is the convenient method here.
Try this:
1. Add the SelectMove script to all movable players.
2. Add a public property bool IsMoving = false; to the SelectMove script and make an if statement to move if IsMoving is true.
3. Change selectedPlayer() method to instead change the target player IsMoving to true, and set the previous one (or simply all other players) to false.

Categories

Resources