Unity3D: Two objects with the same script - unity3d

I would like to associate the same script to different empty objects I just use as placeholders in the game. The aim is to exploit their positions so that when the user touch a point in the screen, close to one of these objects, a dedicate GUI appears. The problem is that though the two objects are different their scripts seem to influence each other so that when the game is running and I touch one of these two objects both the gui appears. What am I doing wrong?
....
private var check: boolean;
var topCamera : Camera;
var customSkin : GUISkin;
function Update () {
if (Input.GetMouseButtonDown(0)){
if(Input.mousePosition.x > this.transform.position.x - Screen.width*0.20 && Input.mousePosition.x < this.transform.position.x + Screen.width*20){
if(Input.mousePosition.y > this.transform.position.y - Screen.height*0.2 && Input.mousePosition.y < this.transform.position.y + Screen.height*0.2){
check = true;
}
}
}
if(check){
//the camera zooms forward
}else{
//the camera zooms backward
}
}
function OnGUI () {
if (this.check){
var w = Screen.width;
var h = Screen.height;
var bw = 0.083;
var bws = 0.001 *w;
GUI.skin = customSkin;
GUI.Box(new Rect(w*0.6,h*0.3,w*0.38,h*0.45), "Stuff");
customSkin.box.fontSize = 0.04*h;
customSkin.textField.fontSize = 0.08*h;
customSkin.button.fontSize = 0.04*h;
textFieldString = GUI.TextField (Rect (w*0.62, h*0.39, w*0.34, h*0.1), textFieldString);
if (GUI.Button (Rect (w*0.62,h*0.50, w*bw, h*0.1), "+")) {
if (this.check){
this.check=false;
}else{
this.check = true;
}
//...
}
//...
}

This is probably not working, because you are comparing apples with oranges in your Update() function. Input.mousePosition returns the the position in 2D pixel coordinates and transform.position returns the GameObject's position in 3D world coordinates.
To check if you clicked on an object, you need to attach a Collider to the game object in question and test for collisions using a Raycast in your script. Here is the relavant example from the documentation in JavaScript:
var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, 100)) {
print ("Hit something");
}
The cool thing about this approach is that we are checking for collisions between the Collider and the ray. If you only want to see if you clicked near the GameObject, just make the Collider larger than the GameObject. No need for messing around with inequalities!

If your objective is to click somewhere close to the object and not only at the object, then you have some configurations (positions of those objects in space) where there are space that are close enough to both objects for their GUI to appear and therefore you need some script to decide which one is closer.
I suggest you to implement a monobehaviour that is a singleton that would track those clicks and measure the distance of all objects, to get the closest.
Reading again your post, I think you want to get the GUI just when you click at the object, and when you do this you get both GUIs. I think that's happening because wrong calculation of the area that makes check to go true.
Can you give more details? Is there some space where there shouldn't have GUI messages when clicked, or is everything filled by those objects?

Related

how to make rigidbody slide on diagonal wall collision

I'm struggling a lot with moving Rigidbodies, currently I have this method to move player:
private void SimpleMove()
{
var scaledMovementSpeed = movementSpeed;
_isRunning = false;
if (Run)
{
scaledMovementSpeed *= runMultiplier;
_isRunning = true;
}
var trans = transform;
var forward = trans.forward;
var newDirection = Vector3.RotateTowards(forward, SimpleMoveVector, Time.fixedDeltaTime * rotationSpeed, 0.0f);
trans.rotation = Quaternion.LookRotation(newDirection, trans.up);
var velocity = _rigidbody.velocity;
var velocityChange = new Vector3(forward.x * scaledMovementSpeed, velocity.y, forward.z * scaledMovementSpeed) - velocity;
_rigidbody.AddForce(velocityChange, ForceMode.VelocityChange);
_isMoving = true;
}
Map is build out of simple cubes right now and the player has capsule collider on him with freeze rotation on all axes.
By tweaking force values a little bit I managed to get it to work properly without the need to check for collisions myself but
expected behavior of player going diagonally into wall is to slide perpendicular to its surface. When I get close to cube's corner character slides across but when I'm going diagonally into a wall character just stops. Haven't tried it with analog input yet.
Is there a way to properly make character slide (fe. put proper physics material on him, or the wall?) or do I need to explicitly check and manage these collisions myself for that?
I thought that my turn-and-then-go-forward method might be messing with proper colliding, but I made the turn instant and the behavior was exactly the same.

GameObjected "sucked" into another GameObject effect while dragging

I am building a chemistry game where the user can drag around different atoms and connect them together to build molecules. I am able to get the atoms to "stick" to one another when they collide. However, the spheres are stuck just on the outside of each other. I want to get them to overlap slightly like the picture below.
Basically, I want the user to click and drag the mouse to drag atom spheres within the radius of other atom spheres. When that collision happens, I want the atom to be "sucked" into the other atom. However, the problem I am facing is that because the user is holding on to the atom with the mouse, any sort of movement is negated. I've tried each of the three lines of code below but none of them work in getting that atom to suck into the other one while the user is holding the mouse.
atom_entering.transform.position = Vector3.MoveTowards(atom_entering.transform.position, this_atom.transform.position, 5*Time.deltaTime);
atom_entering.GetComponent<Rigidbody>().MovePosition(this_atom.transform.position + (atom_entering.transform.position - this_atom.transform.position));
atom_entering.GetComponent<Rigidbody>().AddForce(this_atom.transform.position, ForceMode.Acceleration);
If I try to decrease the radius of each atom, it can work but then the atom starts clipping any other collisions like the table itself.
Edit: Asked to post complete code:
void OnCollisionEnter(Collision collision)
{
GameObject atom_entering = collision.gameObject;
GameObject this_atom = this.gameObject;
if(atom_entering.name == "HydrogenPrefab(Clone)" && num_hydrogens < max_hydrogens && !hydrogen_connected)
{
// Code to make the "suction" effect here
// One of the three lines above went here but none of them worked.
// First, make the hydrogen a child of this oxygen atom.
// Next, create a Fixed Joint component on the hydrogen and stick it to the oxygen
atom_entering.transform.parent = this_atom.transform;
atom_entering.AddComponent<FixedJoint>();
atom_entering.GetComponent<FixedJoint>().connectedBody = this_atom.GetComponent<Rigidbody>();
atom_entering.GetComponent<HydrogenCollider>().setConnectedStatus(true);
// Increment the number of hydrogens connected to the oxygen
num_hydrogens += 1;
}
}
In a separate C# script:
void OnMouseDrag()
{
this.GetComponent<Rigidbody>().isKinematic = true;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, Camera.main.farClipPlane))
{
float oldY = this.transform.position.y;
this.transform.position = new Vector3(hit.point.x, oldY, hit.point.z);
}
this.GetComponent<Rigidbody>().isKinematic = false;
}
Edit: Here is a video of how it is currently behaving. https://vimeo.com/185896832
I want that same behavior expect I just want to get it "sucked into" the parent atom. If I do anything with the radius, the atoms sink into the floor a bit which is not what I want. It doesn't make sense to have an object with a normal radius when it interacts with the floor and a different radius when it interacts with an atom.
Well here's how I would do it :
I would create prefabs of my atoms with empty GameObjects as children serving as references (example : OxygenAtom => HydrogenPos1, HydrogenPos2, HydrogenPos3 and HydrogenPos4)
On atoms I would have a script managing the available position and occupied ones
When the atom you're carrying (let's say Atom2) collides with another atom (Atom1), you check the available positions for one that fits the Atom2 type.
If it fits, you tag the available position as occupied and parent Atom2 as child of Atom1 and finally place him at the position you just checked as occupied.
EDIT : here's a sample of code to bind atoms together :
protected void OnCollisionEnter(Collision collision)
{
if(gameObject.name.Equals("Oxygen") && collision.gameObject.name.Equals("Hydrogen"))
{
Debug.Log("Binding...");
StartCoroutine(BindAtomAfterFixedUpdate(collision.gameObject));
}
}
private IEnumerator BindAtomAfterFixedUpdate(GameObject atomToBind)
{
GetComponent<SphereCollider>().isTrigger = true;
// or GetComponent<SphereCollider>().enabled = false;
yield return new WaitForFixedUpdate();
atomToBind.transform.position = transform.FindChild("HydrogenSpot").position;
gameObject.AddComponent<FixedJoint>().connectedBody = atomToBind.GetComponent<Rigidbody>();
GetComponent<SphereCollider>().isTrigger = false;
// or GetComponent<SphereCollider>().enabled = true;
}

Unity3D Raycasting with Oculus Rift Integration / Measuring the distance

I want to measure the distance between two points in Unity3D Game Engine using the Oculus Rift. The points are targeted by the user by looking at point A, pressing alpha1 on the keyboard and B, pressing alpha2 on the keyboard. I got this far:
#pragma strict
private var measuring = false;
private var startPoint : Vector3;
private var dist;
function Update() {
var hit : RaycastHit;
if (Input.GetKeyDown(KeyCode.Alpha1)) {
dist = 0.0f;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), hit)) {
// if (Physics.Raycast(transform.position, transform.forward, hit, 10)) {
measuring = true;
startPoint = hit.point;
}
}
if (measuring && Input.GetKeyDown(KeyCode.Alpha2)) {
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), hit)) {
// if (Physics.Raycast(transform.position, transform.forward, hit, 10)) {
dist = Vector3.Distance(startPoint, hit.point);
}
}
}
function OnGUI() {
if (measuring) {
GUI.Label(Rect(50,50,180,50), "Distance: " + dist.ToString());
}
}
My problem is, that this code only works with the standard main camera object, but I want to use the Oculus integrated OVRCameraRig. I get the following exception message:
NullReferenceException: Object reference not set to an instance of an object
MeasureInGame.Update () (at Assets/MeasureInGame.js:11)
I found a solution on this site: https://kaharri.com/unity-gaming-shootingaiming-part3-oculus/ I created a ShotSpawner object as a child of OVRCameraRig (this should act like a gun in front of the camera) and changed the Raycast to
Physics.Raycast(transform.position, transform.forward, hit, 10)
to get the users view. But it also doesn't seem to work.
How can I get the aiming done with the Oculus a Main Camera. And is it correct that I strictly need to have a collider on my objects to be measured or is there a solution without collider?
Greetings
First of all - are you really using the mouse with Oculus? Of course you can, but the standard way is to look at the selected object (cursor is in the center of viewport). Cast the ray from the "middle" eye in the oculus integration - it's the object that is the parent to the left and right eyes. Use this ray instead of the one from Camera.main.ScreenPointToRay:
// add a reference to the middleEyeGameobject
// in your class and link it in the inspector
var ray=new Ray(middleEyeGameobject.transform.position, middleEyeGameobject.transform.forward);
Also, you don't have to specify the distance with hit raycast (remove the fourth parameter to Physics.Raycast).
And for the additional question yes, everything has to have a collider with Physics.Raycast. And yes, there are other ways to do this, but the built in ones (2) still require colliders (or similar), although they can be virtual, not attached to objects.
It's best to use colliders, perhaps on their own layer.
The NullReferenceException you are getting is because you are using Camera.main.ScreenPointToRay(Input.mousePosition) , camera.main uses the MainCamera tag which is not set for your OVRCameraRig. You can set tag of this camera as mainCamera if OVRCameraRig is supposed to be your mainCamera or otherwise you can take reference of this camera (OVRCameraRig) and use cameraRef.ScreenPointToRay(Input.mousePosition). :)

Check Collision between tags from a 3rd object

I have run into a bit of an issue while trying to get a scoring system into my game. I have a script on the camera to display the score via a GUI box. I want to check for collision of two other objects and then increment/decrement my score based off of the collision. I have "notes" falling down from the top of the screen and if they go off the bottom and hit the box tagged "miss" then it would decrease the score by some amount.
Script on Camera:
#pragma strict
var score: int = 0;
var customSkin : GUISkin;
function OnGUI()
{
GUI.skin = customSkin;
//Sets the background color of GUI objects to clear
GUI.backgroundColor = Color.clear;
GUI.Box(new Rect(770,25,150,60), "Score: " + score.ToString("0"));
}
Create script, attach it to your notes. Inside this script create OnCollisionEnter function and write your code to decrease score.
For example:
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.CompareTag("miss"))
{
ScoreManager.Instance.Score -= 10;
}
}
All "notes" and "miss" objects must have Collider component. And one of those (all "notes" or just "miss"-object) must have Rigidbody component.
Helpful link: Colliders and collisions

Only select the first sprite in a pile of sprites (Unity3D 2D-game)

I have a number of sprites on top of each-other on the game board. When i use the mouse and select the sprite on top, all sprites under the mouse position is selected. My question is how to only select the sprite that I click on and not catch the ones below?
Here is the code i am using for my tests, attached to the sprites:
function Update () {
if(Input.GetMouseButtonDown(0)) {
var theActualMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
// print("ALL MousePosition: " + theActualMousePosition);
if(collider2D.OverlapPoint(theActualMousePosition)) {
// print("HIT theActualMousePosition: " + theActualMousePosition);
print(collider2D.gameObject.tag);
}
}
}
The results you are getting are completely expected as your code is placed on the GameObjects, what you should do is to push your script out of those objects or use another function than OverlapPoint (because overlap point does not check for collision it simply checks if you are in the bounds of an object, which means it is valid for all object)
Some ideas :
Using OnMouseDown should provide you with an event only for the first collider encountered
Using A raycast from the camera : Camera.ScreenPointToRay should be able to be used for a raycast and then to check only the first collider encountered
Using Layers depending on layer collision Order.
EDIT :
Or you could also cast the ray the other way around :
function Update () {
if(Input.GetMouseButtonDown(0)) {
var theActualMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 direction = (transform.Position-theActualMousePosition).normalized;
Ray ray = Ray(transform.Position,direction)
if(Physics.Raycast(ray) == null) {
//Then there is nothing between the object and the camera then the object is the first one
print(collider2D.gameObject.tag);
}
}
}