How to find all colliders below mouse? - unity3d

public static bool IsTopmost(GameObject go)
{
RaycastHit[] hits;
Vector3 wp = Camera.main.ScreenToWorldPoint(Input.mousePosition);
wp.z = Camera.main.transform.position.z;
hits = Physics.RaycastAll(wp, Vector3.forward, Vector3.Distance(Camera.main.transform.position, go.transform.position) * 2);
if (hits.Length == 0)
{
return false;
}
GameObject topMostSoFar = hits[0].collider.gameObject;
RaycastHit hit;
for (int i = 1; i < hits.Length; i++)
{
hit = hits[i];
if (Compare(topMostSoFar, hit.collider.gameObject) == -1)
{
topMostSoFar = hit.collider.gameObject;
}
}
Debug.Log("finishes method");
return topMostSoFar.name == go.name;
}
My camera position.z = -5, all sprites in the game position.z = 0. This method never finishes, it always enters the first if which says there are no colliders under the mouse position. What do I do wrong here?
EDIT: It turns out my mistake is very stupid, I'm using Physics instead of Physics2D for 2d colliders. How can I check same thing but with 2d raycasting?

The solution is to use the 2d methods and variable types everywhere and for the direction of the RaycastAll to use Vector2.zero, unless you put Vector2.zero it;s not going to work.

Related

How to make the enemy look at the player if the player is not hiding behind a wall?

The enemy must look at the player if the player comes into view. Below is the code how I did it. The problem is that the enemy sees the player through the wall. How can I fix the code so that the enemy does not see the player through obstacles?
I came up with this solution - to filter out all hits to the player, and then additionally filter out those that do not pass through the barrier. However, I don't know how to implement it.
RaycastHit[] hitsInfo = Physics.SphereCastAll(head.position, sightDistance, transform.forward, sightDistance);
for (int i = 0; i < hitsInfo.Length; i++)
if (hitsInfo[i].transform.gameObject.tag == "Player") {
transform.LookAt(hitsInfo[i].transform.position);
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
Debug.DrawLine(head.position, hitsInfo[i].transform.position, Color.red, 0.05f, true);
break;
}
Below is an illustration: the player is white, the enemy is blue, the red is a raycast visualization.
You can use math to check whether the player in sight or not and than use raycast to filter all obstacles.
Here is an example:
using UnityEngine;
public class Enemy : MonoBehaviour
{
[SerializeField] private Transform _objectOfInterest;
[SerializeField] private float _sightAngle = 60f;
[SerializeField] private float _maxDistance = 10f;
private void Update()
{
if (InSight(_objectOfInterest))
{
Debug.Log("InSight");
if (BehindObstacle((_objectOfInterest)))
{
Debug.Log("BehindObstacle");
}
}
}
private bool InSight(Transform objectOfInterest)
{
var forwardDirection = transform.forward;
var directionToTarget = objectOfInterest.position - transform.position;
var angle = Mathf.Acos(Vector3.Dot(forwardDirection.normalized, directionToTarget.normalized));
if (angle * 100 <= _sightAngle) return true;
return false;
}
private bool BehindObstacle(Transform objectOfInterest)
{
var direction = objectOfInterest.position - transform.position;
var raycastDistance = direction.magnitude < _maxDistance ? direction.magnitude : _maxDistance;
var ray = new Ray(transform.position, direction.normalized);
var hits = new RaycastHit[16];
var hitsAmount = Physics.RaycastNonAlloc(ray, hits, raycastDistance);
Debug.DrawRay(transform.position, direction.normalized * raycastDistance);
if (hitsAmount == 0) return true;
foreach (var hit in hits)
{
if (hit.transform.TryGetComponent<Player>(out Player player) == false) // Player is empty MonoBehaviour in my case
{
return true;
}
return false;
}
return false;
}
}
All the math are in InSight method. forwardDirection is the direction where an enemy is looking. When we use "targetposition" - "myposition" in directionToTarget we getting vector that pointing from our position to target. Than we use simple function to get the angle at which the player is in relation to the enemy. The function is - angle = Acos(Dot(A, B)) where A and B are normalized vectors.
In BehindObstacle we just making raycast to player position and if there are any obstacle returning true. You can set sight angle and max distance on your opinion.
And don't use this in Update method (i'm using it only for test) or very often, it can cause performance issues.

Physics2D.OverLapBox detects objects that should be ignored

I'm using a script that looks at the tiles around my GameObject to see if it can detect a collider, through Physics2D.OverlapBox. My problem is that my Player should be ignored by the OverLapBox, as I've set it to look at layers it isn't in, but it is detected everytime.
My Player is in the "Default" layer.
private void SpawnBasicWalls()
{
int layersToScan = LayerMask.GetMask("Floor", "Wall");
//for each tile around this tile
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
Vector2 targetPos = new Vector2(transform.position.x + x, transform.position.y + y);
Collider2D hit = Physics2D.OverlapBox(targetPos, Vector2.one * 0.8f, layersToScan);
//if there isn't a tile around
if (!hit)
{
//Add a wall in that empty adjacent tile.
GameObject goWall = Instantiate(dungMan.wallPrefab, targetPos, Quaternion.identity) as GameObject;
goWall.name = dungMan.wallPrefab.name;
goWall.transform.SetParent(dungMan.transform);
}
else
{
Debug.Log(hit);
}
}
}
//Once it's done, the gameobject is useless and thus is destroyed
Destroy(gameObject);
}
If anyone can tell me what I'm doing wrong, I'd be very grateful.
Physics2D.OverlapBox(targetPos, Vector2.one * 0.8f, layersToScan) is basically calling Physics2D.OverlapBox(point, size, angle) - which means you are sending the layers (casted to int) as an angle.
You need to use one of the overloads that receive a layermask, and make sure you pass it in the right parameter.

Camera not displaying anything when clamped used to set restrictions in Unity

Good day
I am trying to restrict my camera movement using a function that I am using to restrict other elements in a 2D game. When I call this function using my camera, it does something strange.
For some reason it does not display anything as soon as I called the function. I have tested the camera's position using Debug.log, and it seems to be in exactly the same place. The constraints also seem to work, but that is useless if nothing displays.
I am using a Mathf.Clamp function to try and constrain the map. I know that there are many tutorials showing how to constrain map movement, and honestly my approach seems similar.
I want to know why this function is failing. I am trying to keep things generic, and am already using this function to restrict the movement of other game elements.
My code looks like this:
int cameraSpeed = 10;
GameObject camera;
int maxX = 20;
int minX = -20;
int maxY = 20;
int minY = -20;
// Use this for initialization
public void constrain(GameObject obj)
{
Vector2 pos = obj.transform.position;
pos.x = Mathf.Clamp(pos.x, -maxX, maxX);
pos.y = Mathf.Clamp(pos.y, -maxY, maxY);
obj.transform.position = pos;
}
void Start () {
camera = GameObject.Find("Main Camera");
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.RightArrow))
{
camera.transform.Translate(new Vector2(cameraSpeed * Time.deltaTime, 0));
}
if (Input.GetKey(KeyCode.LeftArrow))
{
camera.transform.Translate(new Vector2(-cameraSpeed * Time.deltaTime, 0));
}
if (Input.GetKey(KeyCode.DownArrow))
{
camera.transform.Translate(new Vector2(0, -cameraSpeed * Time.deltaTime));
}
if (Input.GetKey(KeyCode.UpArrow))
{
camera.transform.Translate(new Vector2(0, cameraSpeed * Time.deltaTime));
}
Debug.Log(camera.transform.position.x);
Debug.Log(camera.transform.position.y);
constrain(camera);
}
Screenshot of game without constraints:
Screenshot of game with constraints:
I am new to Unity and am trying to understand it thoroughly. Any advice would be greatly appropriated.
The Z-position of your camera is being set to 0. All of your other objects have the same Z-position, so the camera won't render them.
Change constrain() to:
public float zPos = -10
public void constrain(GameObject obj)
{
Vector3 pos = obj.transform.position;
pos.x = Mathf.Clamp(pos.x, -maxX, maxX);
pos.y = Mathf.Clamp(pos.y, -maxY, maxY);
pos.z = zPos
obj.transform.position = pos;
}
and that should fix it.
EDIT: The reason that this is happening is because you were using a Vector2 for your camera position. In Unity, a Vector2 is a Vector3 with a z-value of 0.
I have managed to find the bug with #DerwB's assistance. I now see that the camera needs to be on a lower z index than the objects it is displaying. DerwB's answer is close to a solution, but it does not allow the function to be reused to constrain other objects.
I have thus modified the function as below:
public void constrain(GameObject obj, bool isCamera = false)
{
Vector3 pos = obj.transform.position;
pos.x = Mathf.Clamp(pos.x, -maxX, maxX);
pos.y = Mathf.Clamp(pos.y, -maxY, maxY);
if(isCamera)
pos.z = 0;
else
pos.z = 1;
obj.transform.position = pos;
}
You then call the function as follows for the camera:
GameObject camera = GameObject.Find("Main Camera");
constrain(camera.gameObject,true); //if you are constraining the camera object
And like this if you are referencing other game objects:
GameObject player= GameObject.Find("Player");
constrain(player.gameObject,true); //skip the optional parameter if not referencing the camera

Placing Furniture on drag in AR Core

I am new to ARCore in Unity. My colleague has already made this app on iOS. But I am having some problems dragging the object around and set it on a position. Also you can rotate it with 2 fingers. Here I am giving the ARKit equivalent code of m dragging. Can anyone help me with doing the same on AR Core
switch (Input.touchCount) {
case 1:
if (touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved) {
var screenPosition = Camera.main.ScreenToViewportPoint(touch.position);
ARPoint point = new ARPoint {
x = screenPosition.x,
y = screenPosition.y
};
// prioritize reults types
ARHitTestResultType[] resultTypes = {
//ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent,
// if you want to use infinite planes use this:
ARHitTestResultType.ARHitTestResultTypeExistingPlane,
ARHitTestResultType.ARHitTestResultTypeHorizontalPlane,
ARHitTestResultType.ARHitTestResultTypeFeaturePoint
};
foreach (ARHitTestResultType resultType in resultTypes) {
if (HitTestWithResultType(point, resultType)) {
return;
}
}
}
break;
case 2:
if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Ended) {
Vector2 direction = touch.deltaPosition;
float yAngleIncrementVal = touch.deltaPosition.magnitude;
Vector3 currentRotation = transform.rotation.eulerAngles;
if (direction.x > 0) {
currentRotation.y += (yAngleIncrementVal * rotationScale);
}
else {
currentRotation.y -= (yAngleIncrementVal * rotationScale);
}
transform.rotation = Quaternion.Euler(currentRotation);
}
break;
}
}
This script will attached to the gameobject which will move across the plane. How can I drag a gameobject and update the anchors so that it stays on the plane. Thank You
I am pretty much doing the same thing, and this is a simplified version of my own code:
case 1:
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
touchOffset = ARCamera.WorldToScreenPoint(gameObject.position) - new Vector3(Input.GetTouch(0).position.x, Input.GetTouch(0).position.y, 0);
}
RaycastHit hit;
LayerMask layers = (1 << LayerMask.NameToLayer(layersOfGroundObjects))
Ray ray = ARCamera.ScreenPointToRay(Input.GetTouch(0).position + new Vector2(mouseOffset.x, mouseOffset.y));
if (!ClickedOnUI && Physics.Raycast(ray, out hit, Mathf.Infinity, layers))
{
if(Input.GetTouch(0).phase == TouchPhase.Began)
{
touchOffset = hit.point - gameObject.position;
}
if (Input.GetTouch(0).phase == TouchPhase.Moved)
{
gameObject.position = hit.point - touchOffset ;
}
}
case 2:
float currentTouchDistance = Vector2.Distance(touches[0].position, touches[1].position);
float diff_y = touches[0].position.y - touches[1].position.y;
float diff_x = touches[0].position.x - touches[1].position.x;
float currentTouchAngle = Mathf.Atan2(diff_y, diff_x) * Mathf.Rad2Deg;
if (isFirstFrameWithTwoTouches)
{
cachedTouchDistance = currentTouchDistance;
cachedTouchAngle = currentTouchAngle;
isFirstFrameWithTwoTouches = false;
}
float angleDelta = currentTouchAngle - cachedTouchAngle;
float scaleMultiplier = (currentTouchDistance / cachedTouchDistance);
float scaleAmount = cachedAugmentationScale * scaleMultiplier;
float scaleAmountClamped = Mathf.Clamp(scaleAmount, scaleRangeMin, scaleRangeMax);
if (enableRotation)
{
gameObject.localEulerAngles = cachedAugmentationRotation - new Vector3(0, angleDelta * 3f, 0);
}
if (enableRotation && enableScaling)
{
gameObject.localScale = new Vector3(scaleAmountClamped, scaleAmountClamped, scaleAmountClamped);
}
I also use a touchOffset which is the distance away from the object's pivot (position) that the user has clicked to start dragging. I apply that offset to the final position every time it is dragged. It makes dragging feels much more natural. I also use it for a lot more in my own code, therefore it was quite important for me, but that is irrelevant for this question so I removed the rest of the code.
Moreover, I use many layers for what is considered ground, because I can put my furniture on more surfaces other than the ARCore tracked plane. If you only care about the tracked plane then either assign a layer to it, or use ARCore's raycast instead of Unity's one. Or just use my Eazy ARCore Interface plugin that can do both (uses layers for tracked planes and makes things easier with raycasting for multiple surfaces)

Character is not colliding with other other objects in Unity3D

I'm working on a game right now (first 3D styled game), and I have a problem with my character colliding. I have a Player object, which has another object(s) as the real moveable characters (now I have only one). I have rigidbody and box collider too attached to my character. I have made a level (with hand-placed platforms), and I would like avoid my character of falling of the platforms, while the user control it. I have tried to place a cube on the side of the platform(s), with box collider attached to it, but the character not detect the colliding for some reasons. I would like my character to be stopped by the collider "cube", but it doesn't happen.
I use this script to move my object (attached to my character Player object) :
public class Bounce : MonoBehaviour {
float lerpTime;
float currentLerpTime;
float perc = 1;
Vector3 startPos;
Vector3 endPos;
bool firstInput;
public bool justJump;
public GameObject player;
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("up") || Input.GetButtonDown("down") || Input.GetButtonDown("left") || Input.GetButtonDown("right")) {
if (perc == 1) {
lerpTime = 1;
currentLerpTime = 0;
firstInput = true;
justJump = true;
}
}
startPos = gameObject.transform.position;
if (Input.GetButtonDown("up") && gameObject.transform.position == endPos) {
endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, 1f));
}
if (Input.GetButtonDown("down") && gameObject.transform.position == endPos) {
endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, -1f));
}
if (firstInput == true) {
currentLerpTime += Time.deltaTime * 5;
perc = currentLerpTime / lerpTime;
gameObject.transform.position = Vector3.Lerp(startPos, endPos, perc);
if (perc > 0.8f) {
perc = 1;
}
if (Mathf.Round(perc) == 1) {
justJump = false;
}
}
}
void OnCollisionEnter(Collision collision) {
Debug.Log("!!!!!!!");
}
}
And I use this script on the character itself: (to rotate and animate it)
Code (csharp):
public class AnimationController : MonoBehaviour {
Animator anim;
public GameObject thePlayer;
// Use this for initialization
void Start () {
anim = gameObject.GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
Bounce bounceScript = thePlayer.GetComponent<Bounce>();
if (bounceScript.justJump == true) {
anim.SetBool("Jump", true);
}
else {
anim.SetBool("Jump", false);
}
if (Input.GetButtonDown("right")) {
//transform.rotation *= Quaternion.Euler(0,30,0);
transform.RotateAround(transform.position, Vector3.up, 90);
}
if (Input.GetButtonDown("left")) {
transform.Rotate (0, -90, 0, 0);
}
}
}
It's only colliding when the cube not isKinematic, but it doesn't stop my player from getting through the cube for some reason.
I have read some sites about problems like this, but nothing helped yet.
It would be great if you can give me any code improvements :)
EDIT1:
Sorry, for answering so late, I couldn't do anything with my game in the last few days. Today I have tried to do something with raycasts, you can see my code, for my first try just in the update method:
void Update () {
if (Input.GetButtonDown("up") || Input.GetButtonDown("down") || Input.GetButtonDown("left") || Input.GetButtonDown("right")) {
if (perc == 1) {
lerpTime = 1;
currentLerpTime = 0;
firstInput = true;
justJump = true;
}
}
startPos = gameObject.transform.position;
/* if (Input.GetButtonDown("right") && gameObject.transform.position == endPos) {
//endPos = new Vector3(transform.position.x + 0.5f, transform.position.y, transform.position.z);
}
if (Input.GetButtonDown("left") && gameObject.transform.position == endPos) {
endPos = new Vector3(transform.position.x - 0.5f, transform.position.y, transform.position.z);
}*/
Vector3 fwd = player.transform.TransformDirection(Vector3.forward);
RaycastHit objectHit;
if (Physics.Raycast(player.transform.position, fwd, out objectHit, 2)) {
if (objectHit.collider.tag == "Wall") {
Debug.Log("WALL RAYCAST HIT!");
}
}
if (Input.GetButtonDown("up") && gameObject.transform.position == endPos) {
endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, 1f));
}
if (Input.GetButtonDown("down") && gameObject.transform.position == endPos) {
//endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, -1f));
}
if (firstInput == true) {
currentLerpTime += Time.deltaTime * 5;
perc = currentLerpTime / lerpTime;
gameObject.transform.position = Vector3.Lerp(startPos, endPos, perc);
if (perc > 0.8f) {
perc = 1;
}
if (Mathf.Round(perc) == 1) {
justJump = false;
}
}
}
With this I can get the Debug Log WALL RAYCAST HIT 2-3 times at a time, but only once. As I move the character it won't appear again, for some reason (I think it should because the update method is called in every frame).
Although when I place it in the Input.GetButtonDown("up") method, it won't log anything:
if (Input.GetButtonDown("up") && gameObject.transform.position == endPos) {
fwd = player.transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(player.transform.position, fwd, out objectHit, 2)) {
if (objectHit.collider.tag == "Wall") {
Debug.Log("WALL RAYCAST HIT!");
}
}
endPos = transform.position + player.transform.rotation * (new Vector3(0, 0, 1f));
}
When you update the transform.position you actually "teleport" yout object to the new position. Even when you do this smoothly, using something like the Lerp function, as you do on your move script, Unity understands that you are teleporting your object to a new coordinate.
This is not wrong, All games work like that. Functions like Lerp just create the ilusion of movement, so the player fell like the character actually moved from one point to another.
What is happening is that your script keeps telling the character to move to a new position, despite any objects on its path. The collision happens, but your Update function still places the character on ints new position.
I can think of two possible ways for you to solve this:
Create an OnCollisionEnter() function for your Move script that somehow stops your movement. By creating a flag like you did with the firstInput you could achieve this.
Instead of updating your transform.position you could use a rigidbody to handle the movement and collisions for your. This way, everytime you need to move your character you will need to add some velocity to the character rigidbody, instead of directly updating the transform.position.
Since your character doesn't move in a constant way, but in a "one step at a time" way, I think the first solution is a better suitted for you.
The code will be something like this
void OnCollisionenter(Collision collision)
{
collided = true;
}
and the end of your Move script should be updated to
if(!collided)
{
if (firstInput == true) {
currentLerpTime += Time.deltaTime * 5;
perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(startPos, endPos, perc);
if (perc > 0.8f) {
perc = 1;
}
if (Mathf.Round(perc) == 1) {
justJump = false;
}
}
}
else
{
// Remember to reset this when you stop your character
endPos = transform.position;
collided = false;
}
This time I couldn't test the code before writting it here, so you may need to do some changes before it works.
ps: I don't think you need your firstInput variable. Once it is set to true, you never set it to false again. Also, the Input.GetButtonDown() function will only return true on the first frame a button is pressed wich is what I guess you intended when you created firstInput.
ps2: My solution added a collided boolean attribute to your class, so don't forget to add a line private bool collided; with your other class attributes. Also, read that chapter about the state machine pattern I recommended you on your other question. Creating this kind of control attribute suach as the collided boolean is not a good practice and if you had implemented a state machine this could be avoided.
Hey I had a similar problem i was using an animated model from Mixamo.com and I notices that for some strange reason the character didn't collide with boundaries despite having collider, for some reason it didn't work until i added a CharacterController to my model instead of a collider just search for the CharacterController and add it as you would add any script or rigid body to your game object.
Hope it Works XD Greetings