selecting blocks on mouse click - c#

I have a 2.5d type game, with falling blocks (like tetris) and orthographic projection setup (I've setup my game as "3D" type).
I've defined a block like this:
public class Block{
public Block () {
this.gameObj = GameObject.CreatePrimitive (PrimitiveType.Cube);
}
public GameObject gameObj;
}
I have a BoardMgr (GameObj + script only component), where I spawn these blocks and store them in an array:
public class BoardMgr : MonoBehaviour {
protected Block[] blocks;
protected Block[,] board;
}
In BoardMgr::update(), blocks are falling down one after the other (like tetris). Now, I'd like to figure out when I click on a block, which block object is it. This is the click detection code:
if (Input.GetMouseButtonDown(0)) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast (ray, out hit)) {
Debug.Log ("Ray hit block");
// How do I find which block got hit here ?
} else {
Debug.Log ("Ray missed block");
}
}
When I click on a block, I do see the ray hit block on console, but then, how do I access which "Block" object got hit ? From RayCastHit, hit object, how do I decode which Block does it reference ?
I'm new to unity (2 days old) but not new to gamedev. Trying to find my way through unity here. I'd appreciate if someone can point me in the right direction here.

//Check the GameObject by name
if (hit.collider.name == "brainydexter")
{
Debug.Log("Hit: "+hit.collider.name);
}
//Check the GameObject by tag
if (hit.collider.CompareTag("brainydexterTag"))
{
}
//Check the GameObject by GameObject instance
GameObject otherGameObject = gameObject;
if (hit.collider.gameObject == otherGameObject)
{
}
EDIT: This is what you need
Use array to loop through the blocks then compare the gameobject instance.
for (int i = 0; i < blocks.Length; i++)
{
if (hit.collider.gameObject == blocks[i].gameObj)
{
Debug.Log("Block hit is " + blocks[i].gameObj);
break;
}
}

In your if block use the hit parameter to detect which object was hit , because it has the information what the ray collided with , example hit.collider.tag == "myBlock"

I like your question. And I think I got the key of what make the above 2 answers be not you want.
Usually, unity developers want to make this kind of block, they will create a cube and add script as element to it to make a prefab.
However, What you do to define a block is more programmer, creating a C# class with a primitive cube.
I think one way is to check the block's position.x and z with the position of each block in the array .because the blocks are dropping, then just y is changing, to check x and z is a solution.
Hope help.

Related

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.

Unity Raycast not working from close distances

I'm trying to have various interactions take place when looking at a game object, but it doesn't seem to work when I'm too close. I'm using Unity's first person controller and the script it attached to the camera.
void Update () {
RaycastHit hit;
Vector3 forward = transform.TransformDirection(Vector3.forward) * 10;
if(Physics.Raycast(transform.position,(forward), out hit) ){
GameObject lookingAt = hit.collider.gameObject;
if (lookingAt.layer == 9)
{
Debug.Log("This doesn't always show up.");
}
}
}
I put
public LayerMask interactionLayers = ~0;
And selected the elements that should be detected in the inspector. That seemed to fix the issue.
Thanks.

Concentric Box colliders in Unity

I have two concentric Box Colliders on same GameObject. Outer Box Collider rotates the object (on swiping the screen) and inner Box Collider plays animation when we touch the screen.
But when ray goes from my mobile screen to outer Collider it destroys and does not pass through that collider.
Is there any way to do it?
You could make the outer one a Trigger Collider by checking Is Trigger in the inspector:
and have the inner collider as just a collider. Then you can check the outer one with OnTriggerEnter and the inner one with OnCollisionEnter.
Alternatively you could give them different Tags and check for each Ray hit by checking for the tag (use OnTriggerEnter for this).
void OnTriggerEnter (Collider other)
{
if (other.gameObject.tag == "Inner Cube")
{
// We have hit the inner cube
}
else if (other.gameObject.tag == "Outer Cube")
{
// We have hit the outer cube
}
}
Looking at what you want to do in your question though, using 2 concentric colliders is not as good as simply detecting which input action the user performs (tap or swipe) and acting according to that.
Take a look at Physics.RayCastAll. With RayCastAll your Ray won't return just the first collision but an array of all collisions.
RayCastHit[] hits;
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
hits = Physics.RaycastAll(ray);
// If you have objects behind your object then sort the hit array by distance:
// hits = hits.OrderBy(l => l.distance).ToArray();
for(int i = 0; i < hits.Length; i++)
{
RaycastHit hit = hits[i];
// From here you can use a tag approach similar to what Flaming Zombie posted to check whether the collision object supports swipes or touches.
// Once you find the first valid object then you can perform your action and break out of this loop.
}
However, there are a couple of things to note with this approach:
You will need to loop through each collision.
You will need to determine whether or not the user swiped or touched (you probably already have this down)
Colliders behind the GameObject will also return collisions.
If there is not a specific reason you are using two colliders then I would do what Flaming Zombie suggested and use a single collider. Here is a simple implementation:
Component on collider:
public class ExampleComponent : MonoBehaviour
{
public void OnInteract(bool isSwipe)
{
if (isSwipe)
{
//Rotate
}
else
{
//Animate
}
}
}
Raycast logic:
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
ExampleComponent c = hit.transform.GetComponent<ExampleComponent>()
if(c != null)
{
c.OnInteract(isSwipe); // You'll need to implement isSwipe
}
}

Update() + Raycast + tag is causing incorrect identification of gameobject when clicking the mouse

I am debugging this, but so far I am not really getting anywhere.
My setup: I have prefabs spawned at runtime; they all bear the tag "npc_entity" to identify them.
When I click on one of them, I want to get the data which is in one of the components attached to the gameobject (my custom entity class). I achieve this with raycasting in update function, getting the gameobject at hit point.
Now here is the weirdness: I use raycast to get the click on a specific entity in the game window; and read the parameters. IF there is only one entity instantiated, all is OK; but if there are 2 or more, the update is called multiple times, which result in the data being overwritten every time.
I have a script with the update function that does raycasting on the prefab itself; this is the code:
void Update()
{
// using this to select entities
if (Input.GetMouseButtonDown(0) && Camera.main.name == "top_Camera")
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, raycast_length) && hit.collider.gameObject.tag == "npc_entity")
{
selected_target = gameObject;
Debug.Log(selected_target.name + " " + selected_target.GetComponent<AIPlayer>().connected_player.playername);
UpdateNPCUI();
}
}
}
void UpdateNPCUI()
{
ui_manager.NPCpanel.GetComponentInChildren<CanvasGrou>().alpha = 1;
NPC_name.text = selected_target.GetComponent<AIPlayer>().connected_player.playername);
}
What I do not understand is why do I get the behavior, where all the gameobjects instantiated are basically processed. I click once on the gameobject, and the raycast show that the position is consistent with where the gameobject is; the others are nowhere near it.
Any suggestion would be appreciated; even doing step by step debugging; I can't see the issue.
So, each time the data is getting overwritten because you have the script on each prefab.
You don't need this script to be attached to each instance, this code can be on any other single instance game object and would fire just once. Put this script on a GameManager gameObject or something that only has one instance of.
To get the current gameobject only, and not all of them:
// using this to select entities
if (Input.GetMouseButtonDown(0) && Camera.main.name == "top_Camera")
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, raycast_length) && hit.collider.gameObject.tag == "npc_entity")
{
selected_target = hit.collider.gameObject;
Debug.Log(selected_target.name + " " + selected_target.GetComponent<AIPlayer>().connected_player.playername);
UpdateNPCUI();
}
}

Grab and drop object

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

Categories

Resources