Unity - Raycast hit, wrong collider - unity3d

Here is my code:
using UnityEngine;
public class InputController : MonoBehaviour {
void Update() {
if (Input.GetMouseButtonUp(0)) {
var hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if (hit.collider != null) {
var e = hit.collider.gameObject;
Debug.Log(e.transform.position.x + ":" + e.transform.position.y);
}
}
}
}
When I click/touch a cell, sometimes the hit is good and the cell is revealed, sometimes the hit is always false (it's like I touch the wrong cell) and sometimes it depends of the position of my click (see images below).
I click the left part of the cell, the console says I click the cell on the left.
I click the right part of the cell, the console says I click the right cell (and reveals it). In this case it's left / right but it can be top / bottom, a corner...
I don't know if my problem is very well explained sorry, and my English is not perfect... Don't hesitate to ask me more details!

Ok I find the problem.
The left cell had a scale of 2 so it overlaps the right cell.

I Guess Here is problem with collider overlaping. you have to just reset all the collider and check it, is it overlaping on any object or not?
you can check colider on gamemode , turn on "Gizmo". so you will see all the collider and check it again is it overlaping on there or not ?

Related

Unity - How do I click on a specific object instead of just the whole screen in a different object?

I'm coding a top down game, with point and click movement. Currently you are able to click on the map, but you can also click outside the map to move there. I added colliders to the walls, but you still try and go outside. Code example:
if (Input.GetMouseButtonDown(1)) {'move'}
But what I want is something like this:
if (Input.GetMouseButtonDown(1) on MAP) //map is the object
So I want to be able to only click on the map, and if you click outside the map, it won't do anything. Thanks!
My script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed = 10f;
Vector2 lastClickedPos;
//Rect inRect = new Rect(82.80f, -83.20f, 164.90f, 163.29f);
bool moving;
private void Update()
{
if (Input.GetMouseButtonDown(1) && GameObject.CompareTag("clickedOn")){ // && inRect.Contains(Input.mousePosition)
lastClickedPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
moving = true;
}
if (moving && (Vector2)transform.position != lastClickedPos)
{
float step = speed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, lastClickedPos, step);
}
else
{
moving = false;
}
}
}
I think you can resolve your issue using tags or layers. I'll just list how to setup tags since it has a lot less setup vs. doing layers.
First off you'll need to create a tag and since Unity has good documentation on stuff like this I'll just link it here: https://docs.unity3d.com/Manual/Tags.html
Once you created your tag and tagged the ground/environment/area you want to have be clickable with the tag then you just need to find the object you want to collided with and use CompareTag so to put that in an example here is what your if statement could look like:
if (Input.GetMouseButtonDown(1) && collidedObject.CompareTag("TagNameGoesHere"))
{
//Movement goes here
}
Just to note I've named the gameObject that was found to collidedObject but you can name it whatever you want.
Hopefully this helps, let me know if I need to clarify something, it has been awhile since I've done a stackoverflow answer so I may have left something out.
Edit: Alright so adding onto this, you'll need to also look into how to do raycasting to check what object you click on so you can determine if it's a spot you can move to.
I've just tested this in a project just to make sure I understand it (I've used raycast a lot but never really done point to click movement before).
Essentially I've broken down things into 3 statements, which you can add together into one if statement but it's more so I can explain everything in detail:
if (Input.GetMouseButtonDown(1))
What you use currently, we want to make sure we only do the next few checks when we click
if (Physics.Raycast(playerCamera.ScreenPointToRay(Input.mousePosition), out hit, Mathf.Infinity))
So this here, is essentially drawing a line from a position, in this bit we are using the player camera which is just a regular camera reference and converting a point on screen into a ray which we then set the length to be Mathf.Infinity (this can be whatever float, I've just used this for an example) and then we output the hit object to hit which is a RaycastHit struct.
if (hit.collider != null && hit.collider.CompareTag("TagNameGoesHere"))
Now we finally check to see if the collider is not null (in case we hit the sky or something, which shouldn't happen for you in a top down game) and also that the object has right tag. Again you need to setup the tags which I've listed above and make sure you set the correct game objects in scene to have the correct tag. After which you should be able to move to the position (using the position you've setup and such is fine)
So it should look something like this:
if (Input.GetMouseButtonDown(1))
{
if (Physics.Raycast(playerCamera.ScreenPointToRay(Input.mousePosition), out rayCastHit, Mathf.Infinity))
{
if (hit.collider != null && hit.collider.CompareTag("TagNameGoesHere"))
{
//Movement goes here
}
}
}
Make sure to also put a reference to the camera and RayCastHit objects (these are the playerCamera and rayCastHit variables in the above). For my example script that I created I made them global variables.

rigid body moves by addForce but the object itself doesn't

I'm following this video https://www.youtube.com/watch?v=THnivyG0Mvo and This is my shoot function
void Shoot()
{
muzzleFlash.Play();
shootingSound.Play();
RaycastHit hit;
if( Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
{
//Debug.Log(hit.transform.name);
Enemy1 target = hit.transform.GetComponent<Enemy1>();
if (target != null)
{
target.TakeDamage(damage);
}
if(hit.rigidbody != null)
{
Debug.Log("******************");
hit.rigidbody.AddForce( - hit.normal * impactForce);
}
GameObject impactGo = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(impactGo, 0.3f);
}
}
Rigidbody added to target:
Barrel component:
enter image description here
This function is in my Rifle.cs script which is being added to a rifle object. Everything works fine. But, when I hit an object which has a Rigidbody, it doesn't move but I can see in the scene that the Rigidbody is moving when I hit it many times. The Rigidbody of the target is set to 'Use Gravity' and 'Is Kinematic' is not checked. What am I doing wrong ?
Probably the force you are adding is too small, so it needs a lot of shots to make some effect, as #Horothenic says, try to increase the value of the impactForce Variable. Look if the rigidbody, the mesh renderer, and the colliders are attached to the same object in the scene. The title of your question suggests your rigidbody is moving but your render doesn't change.
Give the force a ForceMode. Since you want the object to look shot, I would recommend using impulse and make sure the force is bigger than the objects mass by a big factor.
if(hit.rigidbody != null)
{
Debug.Log("******************");
hit.rigidbody.AddForce((-hit.normal * impactForce), ForceMode.Impulse);
}
See this page for more info. Force Modes in Unity
Try removing the Animator completely, the animations have an option to write defaults, if the movement was edited on animation it misbehaves because it wants to set the default.
After I have done a lot of google search. This answered my question https://forum.unity.com/threads/mesh-renderer-does-not-move-with-parent-rigid-body.204700/. So What I needed to do it to turn off (unchecked) static in the top right of the inspector for the object.
enter image description here

Terminate drag while mouse is still down

Imagine some standard Unity UI ScrollView. You grab it with your mouse and start dragging it so it is scrolling with your mouse. How do I programmatically make it stop scrolling while mouse button is still down? Is there any way to make ScrollRect think that user is released mouse button?
Quite old question, but I found exactly this same problem and no clean solution. My problem was to have scrollRect with items which I can scroll left/right but when user select one item and drag it up then scrollRect should cancel scrolling and allow user to drag item up. I achieved this by temporary setting ScrollRect.enable to false, and after few ms set it back to true.
Here is part of mu code which do this. Maybe somebody find this useful.
void Update()
{
if (state == DragState.Started) {
dragStart = Input.mousePosition;
state = DragState.Continued;
}
if (state == DragState.Continued) {
Vector3 pos = Input.mousePosition;
if (pos.y - dragStart.y > 80) {
scrollRect.enabled = false;
state = DragState.None;
Invoke("ReenableScroll", 0.01f);
}
}
}
private void ReenableScroll() {
scrollRect.enabled = true;
}
Its not easy to make the EventSystem think something has happend while it hasn't. I believe its much easier to implement your own ScrollRect logic and parse the mouse input yourself
You can call ScrollRect's OnEndDrag() method with artificial PointerEventData.
var forceEndDragData = new PointerEventData(EventSystem.current)
{
button = PointerEventData.InputButton.Left
};
_scrollRect.OnEndDrag(forceEndDragData);

Unity 5: How to know if finger is on joystick even if it is at horizontal & vertical zero

I'm creating a jetpack (mobile) controller where left joystick is used to control forward and backward movements and right joystick is used to control rotation and upward movements. What I want is player to go upwards whenever user is touching the right joystick even if horizontal && vertical axes both return zero. So if there is a finger on the right joystick player goes up similar to GetButton or GetKey(some keycode).
Hope this helps somebody in the future:
I found out there is OnPointerUp and OnPointerDown methods that can be used to check if joystick is pressed or not. Easiest way for me to use those were to change a few things in Standard Assets > Utility > Joystick.cs. This is how those methods look like after my modifications:
public void OnPointerUp(PointerEventData data)
{
transform.position = m_StartPos;
UpdateVirtualAxes(m_StartPos);
if (data.rawPointerPress.name == "MobileJoystick_right") {
rightJoystickPressed = false;
}
}
public void OnPointerDown(PointerEventData data) {
if (data.pointerEnter.name == "MobileJoystick_right") {
rightJoystickPressed = true;
}
}
So basically I just added the If-statements. Now I can access the rightJoystickPressed boolean from any other script to check if joystick is being pressed even if it is not moved.

Unity 3D - Matching Pairs (2D) Game

I am currently working on a game in Unity3D where you must click on colour pairs to match them and then they disappear. I am using 2D sprites to do this but I am struggling in terms of the logic to erase the pair when both is clicked via mouse.
Click the yellow then click yellow again to make both disappear. (Until board is cleared or colours.)
If clicked on yellow then anything other than yellow do nothing.
Thanks in advance.
Here is what the layout of the sprites looks like:
Would it be best to give every colour a tag?
Here is what I want to happen: When the game starts it picks 3 colours from an array of 6 then randomly places them (2 of each colour) on the screen. You then have to click the colour for example green (it will highlight) then click on the other green, and they will both disappear. If you were to, say, click on the green first then yellow, the game will just end.
This is the code that I have implemented at the moment:
// [...]
if (Input.GetMouseButtonDown(0))
{
CastRay();
}
}
function CastRay() {
var ray: Ray = Camera.main.ScreenPointToRay(Input.mousePosition);
var hit: RaycastHit2D = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if(hit.collider != null)
{
// Number is the amount of objects on the screen at one time.(6)
number --;
//Test to see if a mouse click interacts with the 2D Sprite.(Then destroys it)
Debug.Log ("Target Position: " + hit.collider.gameObject.transform.position + gameObject.tag);
Destroy(hit.collider.gameObject);
}
// This when the number hits 0 the level restarts (To check random elements)
if (number == 0)
{
Application.LoadLevel (0);
}
}
You need to have two variables storing the type of clicked cards and boolean variable to store info if you are clicking first or second card (false for . And then on click event you need to check few things:
1. Need to check if you are clicking on first or second card. If it is first card, check the boolean variable if it is false. If yes: change it to true and show the card. Store the card type in the firsClicked variable.
2. If it is second click your check for boolean should be true. In that case you should check if the type of second card is same as first card. if true - Profit. If false, turn the cards back. Thats all the logic here.
EDIT
Ok, here it is step by step.
You need to have a GameObject variable to store the circle that is chosen in the first click. Lets say private GameObject firstCircle = null; . Put it outside your click method so it is not initialized on every click.
Every of your circle objects have to have some fields that store their colour. I do not know how do you set them, I guess that there is a tag? I guess that they have tags like "green", "red" and so on?
In your click event you have to have if-else. Something like that (pseudocode only):
if(firstCircle == null)
{
firstCircle = hit.collider.gameobject; // this will store the first clicked circle for later comparison
}else{
firstCircle = null;
if(firstCircle.tag == hit.collider.gameObject.tag)
{
//here you can destroy both objects or add points or whatever like
Destroy(firstCircle);
Destroy(hit.collider.gameObject);
}else{
// here you do what you want when circles are not the same
}
}
Plese not this is just pseudocode and I could not test it, but I hope you get the idea behind this. Generally you need to store the first circle after the first clik to compare it with the circle after the second click. Please have in mind that you have to check if user do not click the same circle twice (I did not include this here)