Raycast how to deal with more than one object same layer - unity3d

I am using raycast to detect that the player is grounded after a jump, my problem is that some times the raycast is not detecting that the player is grounded and it is.
This is my code for the raycasting:
void IsGrounded()
{
RaycastHit hit;
int mask = 1 << LayerMask.NameToLayer("Floor");
if (Physics.Raycast(transform.position, Vector3.down, out hit, 0.1f, mask))
{
isGrounded = true;
checkGround = false;
Debug.Log("is grounded");
if (!myAudioSource.isPlaying)
{
myAudioSource.Play();
}
}
Debug.DrawLine(transform.position, hit.point, Color.red);
}
Here is my problem:
The character is in the ground but the ray is still not detecting it.
Sometimes this same location will detect and others it won't.

Related

Unity 2D. Why raycasting does not work properly?

With this code I am changing color of gun object if raycast can hit player. Color changes as soon as Player walks into raycast radius. However color won't switch back from red to green as soon as player either goes away (more than Range distance) or hide behind walls. It will eventually switch back to green but not right away. Can you please help me figure out what seems to be the problem?
void Update()
{
Vector2 targetPos = Target.position;
Direction = targetPos - (Vector2)transform.position;
RaycastHit2D rayInfo = Physics2D.Raycast(transform.position, Direction, Range, layerMask);
if (rayInfo)
{
if (rayInfo.collider.gameObject.tag == "Player")
{
if (Detected == false)
{
Detected = true;
Gun.GetComponent<SpriteRenderer>().color = Color.red;
}
}
else
{
if (Detected == true)
{
Detected = false;
Gun.GetComponent<SpriteRenderer>().color = Color.green;
}
}
}
}
When your raycasthit doesn't hit anything with specified LayerMask you don't change your gun's color maybe this is the issue. if your reset your gun color it can solve. Here is the example
//define gunColor variable it is default color of your gun
Color gunColor=Color.green;
void Update()
{
Vector2 targetPos = Target.position;
Direction = targetPos - (Vector2)transform.position;
RaycastHit2D rayInfo = Physics2D.Raycast(transform.position, Direction, Range, layerMask);
if (rayInfo)
{
gunColor=rayInfo.collider.gameObject.CompareTag("Player")?Color.red:Color.green,
}else{
//when raycasthit doesnt hit any thing set gun color to green;
gunColor=Color.green;
}
//update gun color
Gun.GetComponent<SpriteRenderer>().color = gunColor;
}

Jump functionality for a 2D Top Down Unity game?

I am struggling to find an efficient way to let my player jump in a 2D Top Down world. I can see a lot of tutorials about platformer views where the camera is oriented at the side of the player, but nothing really working for a top down view like startdew Valley.
I am not using physics, so I move the character on the tilemap using a Couroutine which moves the player to the next position on grid, here it is my Update and DoMove methods:
private void Update()
{
if (!isMoving)
{
input.x = Input.GetAxisRaw("Horizontal");
input.y = Input.GetAxisRaw("Vertical");
if (input.x != 0)
input.y = 0;
if (input != Vector2.zero)
{
animator.SetFloat("Horizontal", input.x);
animator.SetFloat("Vertical", input.y);
var targetPos = transform.position + new Vector3(input.x, input.y, 0f);
// obstacle detection
Vector3Int obstaclesMapTile = obstacles.WorldToCell(targetPos - new Vector3(0, .5f, 0));
if (obstacles.GetTile(obstaclesMapTile) == null)
{
StartCoroutine(DoMove(targetPos));
}
}
animator.SetFloat("Speed", input.sqrMagnitude);
}
}
private IEnumerator DoMove(Vector3 newPos)
{
isMoving = true;
while ((newPos - transform.position).sqrMagnitude > Mathf.Epsilon)
{
transform.position = Vector3.MoveTowards(transform.position, newPos, moveSpeed * Time.fixedDeltaTime);
yield return null;
}
transform.position = newPos;
isMoving = false;
}
Is there anybody which could give me an hint on how to add a jumping feature? ( ideally with animation support?) I am kind of running out of ideas.
Thanks in advance.
Just think of it as animation only. Since it is 2D top down, it's more about it looking like it jumps, and then if it has to go over something while in the jump animation, test for just that.
For example; if over hole and jump animation is playing, then allow movement over the whole, otherwise fall. So if the player presses the button for jump, the animation would play, and there should be some variable storing what animation the player is currently in.

Unity - How to jump using a NavMeshAgent and click to move logic

I am building a game where the player can be controlled using the mouse input, using a click to move logic via a navmesh agent.
In order to let the player jump, I started using a CharacterController as well which should help managing the player. My issue is that I can't figure out where to put the jump logic. All references I found are related using the character controller without the navmesh agent.
I can get rid of the CharacterController if needed, but the NavMeshAgent has to stay.
Here it is a working code which allows to walk. Can you please help me with the jumping logic?
private NavMeshAgent _agent;
private CharacterController _characterController;
private Vector3 _desVelocity;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hitInfo))
{
_agent.destination = hitInfo.point;
}
}
var currMovementDirection = _desVelocity.normalized * currentSpeed;
if (_agent.remainingDistance > _agent.stoppingDistance)
{
_desVelocity = _agent.desiredVelocity;
_characterController.Move(currMovementDirection * Time.deltaTime);
}
}
You can achieve this using a Rigidbody instead of a CharacterController. The trick is that you need to disable the NavMeshAgent in order to jump.
Optionally, you set the destination to where you are at the time of the jump, so that the agent doesn't continue the simulation while the jump is happening.
Using collision detection, you turn the NavMeshAgent back on again once you land.
public class PlayerMovement : MonoBehaviour
{
private Camera cam;
private NavMeshAgent agent;
private Rigidbody rigidbody;
public bool grounded = true;
void Start()
{
cam = Camera.main;
agent = GetComponent<NavMeshAgent>();
rigidbody = GetComponent<Rigidbody>();
}
void Update()
{
// clicking on the nav mesh, sets the destination of the agent and off he goes
if (Input.GetMouseButtonDown(0) && (!agent.isStopped))
{
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
agent.SetDestination(hit.point);
}
}
// when you want to jump
if (Input.GetKeyDown(KeyCode.Space) && grounded)
{
grounded = false;
if (agent.enabled)
{
// set the agents target to where you are before the jump
// this stops her before she jumps. Alternatively, you could
// cache this value, and set it again once the jump is complete
// to continue the original move
agent.SetDestination(transform.position);
// disable the agent
agent.updatePosition = false;
agent.updateRotation = false;
agent.isStopped = true;
}
// make the jump
rigidbody.isKinematic = false;
rigidbody.useGravity = true;
rigidbody.AddRelativeForce(new Vector3(0, 5f, 0), ForceMode.Impulse);
}
}
/// <summary>
/// Check for collision back to the ground, and re-enable the NavMeshAgent
/// </summary>
private void OnCollisionEnter(Collision collision)
{
if (collision.collider != null && collision.collider.tag == "Ground")
{
if (!grounded)
{
if (agent.enabled)
{
agent.updatePosition = true;
agent.updateRotation = true;
agent.isStopped = false;
}
rigidbody.isKinematic = true;
rigidbody.useGravity = false;
grounded = true;
}
}
}
}
The jump logic should be inside the Update() method since we want the height to be calculated every frame.
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hitInfo))
{
_agent.destination = hitInfo.point;
}
}
var currMovementDirection = _desVelocity.normalized * currentSpeed;
groundedPlayer = _characterController.isGrounded;
if (groundedPlayer && currMovementDirection.y < 0)
{
currMovementDirection.y = 0f;
}
// Changes the height position of the player..
if (Input.GetButtonDown("Jump") && groundedPlayer)
{
currMovementDirection.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
}
currMovementDirection.y += gravityValue * Time.deltaTime;
if (_agent.remainingDistance > _agent.stoppingDistance)
{
_desVelocity = _agent.desiredVelocity;
_characterController.Move(currMovementDirection * Time.deltaTime);
}
}
Please see the official docs here

buttons only react to clicks/touches when clicking/touching to the right of them

I'm making a 2D game in Unity3D for android. Right now I'm making buttons. And this buttons does not react clicks/touched properly. I've got same issue with mouse clicks and touches both. Every button has trigger boxcollider with a same size as an object. BUT buttons react only when I click on area, that is right from a button. I don't understand why is it so. What should I do? Here is my code:
if (Input.GetMouseButtonDown(0)) {
Vector3 i = Camera.main.ScreenToWorldPoint (new Vector3 (Input.mousePosition.x, Input.mousePosition.y, 1));
RaycastHit2D hit = Physics2D.Raycast (i, i);
if (hit.transform != null) {
if (hit.transform.tag == "button") {
hit.transform.gameObject.SetActive(false);
}
}
}
Also, I've instantiated an object on mouse click on "i" position to check does it convert screen position to world correctly, and it works fine.
the first parameter in Physics2D.Raycast is the origin and the second one is direction so you should make the raycast from your ray.origin in the direction of ray.direction
void Update () {
if (Input.GetMouseButtonDown(0)) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast (ray.origin, ray.direction, Mathf.Infinity);
if (hit) {
if(hit.collider.gameObject.tag=="button"){
//do something
}
}
}
}
Try to handle it by this way:
if (Input.GetMouseButtonDown(0)) {
Vector3 pos = Camera.main.ScreenToWorldPoint (Input.mousePosition);
Vector2 touchPos = new Vector2(pos.x, pos.y);
Collider2D hit = Physics2D.OverlapPoint(touchPos);
if (hit) {
Debug.Log(hit.transform.gameObject.name);
if (hit.transform.tag == "button") {
hit.transform.gameObject.SetActive(false);
}
}
}

Character Stuck On Walls

I recently started using Unity2D and put together a simple character controller with a rigidbody and colliders. The way it is set up now is a circle collider for the feet and a box collider for the rest of the body. The reason I did this is a box collider on the feet causes the character to sometimes get stuck moving across tiles. The problem I am trying to fix happens when the character is pressed against a wall. While moving horizontally into a wall gravity and jumping seem to have no affect. He gets stuck in the wall. I believe it has something to do with friction because when I set the block material to 0 friction he no longer has this problem. However, when I do this he becomes slippery enough to slide off the edge of blocks because of the circle collider on his feet. Any suggestions/fixes would be much appreciated.
public class SpriteController : MonoBehaviour {
public float maxSpeed = 2f;
bool facingRight = true;
Animator anim;
bool grounded = false;
bool swimming = false;
public Transform groundCheck;
float groundRadius = 0.05f;
public LayerMask whatIsGround;
public float jumpForce = 700f;
public float swimForce = 10f;
public PhysicsMaterial2D myMaterial;
void Start ()
{
anim = GetComponent<Animator> ();
}
void FixedUpdate ()
{
grounded = Physics2D.OverlapCircle (groundCheck.position, groundRadius, whatIsGround);
anim.SetBool ("Ground", grounded);
anim.SetFloat ("vSpeed", rigidbody2D.velocity.y);
float move = Input.GetAxis ("Horizontal");
anim.SetFloat ("Speed", Mathf.Abs (move));
rigidbody2D.velocity = new Vector2 (move * maxSpeed, rigidbody2D.velocity.y);
if (move > 0 && !facingRight)
Flip ();
else if (move < 0 && facingRight)
Flip ();
}
void Update()
{
Spin();
if (grounded && Input.GetKeyDown (KeyCode.Space))
{
anim.SetBool ("Ground", false);
rigidbody2D.AddForce(new Vector2(0, jumpForce));
}
else if(swimming && Input.GetKey (KeyCode.Space)) rigidbody2D.AddForce(new Vector2(0, swimForce));
}
void Spin()
{
if(Input.GetKeyDown ("f")) anim.SetBool("Spin", true);
if(Input.GetKeyUp ("f")) anim.SetBool("Spin", false);
}
void Flip()
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
void OnTriggerEnter2D(Collider2D collider)
{
if (collider.gameObject.tag == "Water") {
rigidbody2D.drag = 15;
swimming = true;
grounded = false;
}
}
void OnTriggerExit2D(Collider2D collider)
{
if (collider.gameObject.tag == "Water"){
rigidbody2D.drag = 0;
swimming = false;
}
}
}
Friction being set to 0 should be fine. How far away from the edge of the block does the character start to slip? You could try shrinking the size of the circle collider relative to the character (particularly width). If all your other physics work OK it might be something to do with the positioning or size of the circle collider.
Here, my fix which works quite well for me.
I've just checked whether the player is colliding with something which is not the ground. In this particular case, I disable the Player (user input) movement :
void FixedUpdate ()
{
grounded = Physics2D.OverlapCircle (groundCheck.position, groundRadius, whatIsGround);
anim.SetBool ("Ground", grounded);
anim.SetFloat ("vSpeed", rigidbody2D.velocity.y);
// -- JUST ADD THIS --
if (!grounded && rigidbody2D.IsTouchingLayers ()) {
return;
}
// -- END --
float move = Input.GetAxis ("Horizontal");
anim.SetFloat ("Speed", Mathf.Abs (move));
rigidbody2D.velocity = new Vector2 (move * maxSpeed, rigidbody2D.velocity.y);
if (move > 0 && !facingRight)
Flip ();
else if (move < 0 && facingRight)
Flip ();
}