OverlapBox sometimes not working as expected, Unity 2d - c#

I am using an overlap box to detect whether an arrow hits the ground or an enemy, the arrow hitting the enemy works, it's that often the arrow will just pass through the ground, not sure if I am missing something.
Heres my code for detecting the ground or enemy.
private void FixedUpdate()
{
damageHit = Physics2D.OverlapBox(damagePosition.position, damageSize, whatIsEnemy);
groundHit = Physics2D.OverlapBox(damagePosition.position, damageSize, whatIsGround);
if (!hasHitGround)
{
if (damageHit)
{
Collider2D[] detectedObjects = Physics2D.OverlapBoxAll(damagePosition.position, damageSize, whatIsEnemy);
foreach (Collider2D collider in detectedObjects)
{
IDamageable damageable = collider.GetComponent<IDamageable>();
if (damageable != null)
{
damageable.Damage(weaponData.attackDamage);
Destroy(gameObject);
}
IKnockbackable knockbackable = collider.GetComponent<IKnockbackable>();
if (knockbackable != null)
{
knockbackable.KnockBack(weaponData.knockbackAngle, weaponData.knockbackStrength, (int)Mathf.Sign(transform.rotation.y));
}
}
}
if (groundHit)
{
rb.gravityScale = 0f;
rb.velocity = Vector2.zero;
hasHitGround = true;
Destroy(gameObject, 10f);
}
else if (Mathf.Abs(xStartPos - transform.position.x) >= travelDistance && !isGravityOn)
{
isGravityOn = true;
rb.gravityScale = gravity;
}
}
}
private void OnDrawGizmos()
{
Gizmos.DrawWireCube(damagePosition.position, damageSize) ;
}
I thought that the hitbox may have been to small and it would just pass through wall due to not hitting it on any frame. After making the hitbox larger it still did not help, here you can see that even though the overlap box is overlaping the collider the arrow will continue on.

Related

How to switch between the OnTriggerExit/Enter logic depending on the situation?

The script is attached to two gameobjects.
One it's colliding area the collider is big enough so when the game start the player is already inside the collider area and then when he exit the area everything is working fine.
The problem is when I attached the object to another gameobject with collider but this time the collider s smaller and he is inside the bigger collider so now the player is entering the smaller collider and not first time exiting. but I want in both case to make the same effect.
If the player exit the collider he slow down wait then turn around and move inside back.
The same I want to make when the player getting closer to the fire flames slow down wait turn around and move back in it's just with the flames the player entering the collider area and then exit while in the bigger collider he first exit then enter.
Screenshot of the two colliders areas. The small one on the left is the one the player entering first and not exiting. The game start when the player is in the bigger collider area.
And the script :
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.Characters.ThirdPerson;
public class DistanceCheck : MonoBehaviour
{
public Transform targetToRotateTowards;
public Transform colliderArea;
public float lerpDuration;
public float rotationSpeed;
[TextArea(1, 2)]
public string textToShow;
public GameObject descriptionTextImage;
public TextMeshProUGUI text;
public ThirdPersonUserControl thirdPersonUserControl;
private Animator anim;
private float timeElapsed = 0;
private float startValue = 1;
private float endValue = 0;
private float valueToLerp = 0;
private bool startRotating = false;
private bool slowOnBack = true;
private bool exited = false;
private Vector3 exitPosition;
private float distance;
void Start()
{
anim = transform.GetComponent<Animator>();
}
private void FixedUpdate()
{
if (startRotating)
{
transform.rotation = Quaternion.RotateTowards(transform.rotation,
Quaternion.LookRotation(targetToRotateTowards.position - transform.position),
rotationSpeed * Time.deltaTime);
}
if (exitPosition != new Vector3(0, 0, 0) && slowOnBack)
{
distance = Vector3.Distance(transform.position, exitPosition);
}
if (distance > 5 && slowOnBack)
{
slowOnBack = false;
StartCoroutine(SlowDown());
}
}
private void OnTriggerExit(Collider other)
{
if (other.name == colliderArea.name)
{
exited = true;
slowOnBack = true;
exitPosition = transform.position;
thirdPersonUserControl.enabled = false;
descriptionTextImage.SetActive(true);
text.text = textToShow;
StartCoroutine(SlowDown());
}
}
private void OnTriggerEnter(Collider other)
{
if (other.name == colliderArea.name)
{
exited = false;
startRotating = false;
text.text = "";
descriptionTextImage.SetActive(false);
}
}
IEnumerator SlowDown()
{
timeElapsed = 0;
while (timeElapsed < lerpDuration)
{
valueToLerp = Mathf.Lerp(startValue, endValue, timeElapsed / lerpDuration);
anim.SetFloat("Forward", valueToLerp);
timeElapsed += Time.deltaTime;
yield return null;
}
if (exited)
{
yield return new WaitForSeconds(3f);
startRotating = true;
StartCoroutine(SpeedUp());
}
if (slowOnBack == false)
{
thirdPersonUserControl.enabled = true;
}
}
IEnumerator SpeedUp()
{
timeElapsed = 0;
while (timeElapsed < lerpDuration)
{
valueToLerp = Mathf.Lerp(endValue, startValue, timeElapsed / lerpDuration);
anim.SetFloat("Forward", valueToLerp);
timeElapsed += Time.deltaTime;
yield return null;
}
}
}
Two problems I'm facing right now :
The smaller collider the player enter it then exit and the bigger collider the player exit it then enter so I need to change somehow the OnTriggerExit/Enter behavior logic in the script. but how to make the logic ?
Maybe it's better to make the script to be on the player object only and make it some how generic so I can drag to it many colliders and to make the effect in each one of the colliders the problem is how to make a text field for each collider area ? Now because I attach the script to each collider object I have one colliderArea variable but if I want to make the script only attached to the player I need to change the colliderArea variable to a List<Transform> collidersAreas and then how to create a text field/area to each collider area in the List ?
I think I would do this by creating two tags, NoExit and NoEntry. Once the tags are created you can set the tag on the GameObjects that hold your colliders. Then you can check for tags in the OnTriggerEnter and OnTriggerExit and act accordingly.
I can't tell for sure which functionality you've got written is for which case, or if it's both - everything looks muddled. Ultimately what you should be going for is a function like RepositionPlayer, that moves them back to before they're violating the no entry or no exit rules, and then some kind of OnPlayerRepositioned event that restores their control.
I'll leave it up to you to split out your functionality, but in general I'd look to do something like the following:
private void OnTriggerExit(Collider other)
{
if (other.tag == "NoExit")
{
RepositionPlayer();
}
else if(other.tag == "NoEntry")
{
OnPlayerRepositioned();
}
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "NoExit")
{
OnPlayerRepositioned();
}
else if(other.tag == "NoEntry")
{
RepositionPlayer();
}
}
And again here it's not clear to me what you're trying to do to reposition the player, but it looks like it's something like:
private void RepositionPlayer()
{
// Stuff that needs to happen to reposition the player
exited = true;
slowOnBack = true;
exitPosition = transform.position;
thirdPersonUserControl.enabled = false;
descriptionTextImage.SetActive(true);
text.text = textToShow;
StartCoroutine(SlowDown());
}
private void OnPlayerRepositioned()
{
// stuff you need to do to clear the "repositioning" status
exited = false;
startRotating = false;
text.text = "";
descriptionTextImage.SetActive(false);
}
Splitting up the logic like this makes it easier to both read and maintain.

Make Character Able To Turn Back and Fourth in Unity

I was trying to code a system where a 2D character's sprite and collider turn to face the direction being input (i.e. pressing the left key makes the character face left, pressing the right key makes the character turn right). I had this script and it seemed to work for a while:
public bool turnedAround = false; //Says if you are facing to the left (true) or right (false)
[SerializeField]
private float xVelocityTolerance = .6f; //The slowest the player's X can move at to have the running animation show
[SerializeField]
private float groundDetectionOffset = .3f; //How far the raycast origin is offset
private Animator anim;
private Rigidbody2D rb;
private PlayerController playerController;
public float HalfPlayerHeight
{
get
{
return playerController.halfPlayerHeight;
}
}
private bool IsOnGround() //Checks if there is ground below the outside to the left, middle, and outside to the right
{
return Physics2D.Raycast(transform.position - new Vector3(groundDetectionOffset, 0f, 0f), -transform.up, HalfPlayerHeight) || Physics2D.Raycast(transform.position, -transform.up, HalfPlayerHeight) ||
Physics2D.Raycast(transform.position + new Vector3(groundDetectionOffset, 0f, 0f), -transform.up, HalfPlayerHeight); //Uses an artificial line break
}
void Start()
{
anim = GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
playerController = GetComponent<PlayerController>();
}
// Update is called once per frame
void Update()
{
//NOTE ABOUT WHOLE SECTION OF IS ELSE: The if checks if the player's facing right and moving left. If it is then it turns left and changes turnedAround to be true which means it is facing left. The else if is the opposite
if (rb.velocity.x < -xVelocityTolerance && !turnedAround)
{
transform.Rotate(0, 180, 0);
turnedAround = true;
}
else if (rb.velocity.x > xVelocityTolerance && turnedAround)
{
transform.Rotate(0, -180, 0);
turnedAround = false;
}
/*NOTE ABOUT WHOLE SECTION OF IS ELSE: The if checks if the player is moving either left and right fatser than the VelocityTolerance and if the player isn't jumping/falling.
If true it will tell the animator by setting IsRunningNotJumping to true. If it it false it will be set to false.*/
if ((rb.velocity.x > xVelocityTolerance || rb.velocity.x < -xVelocityTolerance) && IsOnGround() && !anim.GetBool("IsRunningNotJumping"))
{
anim.SetBool("IsRunningNotJumping", true);
}
else if (anim.GetBool("IsRunningNotJumping"))
{
anim.SetBool("IsRunningNotJumping", false);
}
//NOTE ABOUT WHOLE SECTION OF IS ELSE: The if statement checks if the player is jumping/falling. If they are than it will tell the animator by setting IsJumpingOrFallling to true and vice versa with the else.
if (!IsOnGround() && !anim.GetBool("IsJumpingOrFalling"))
{
anim.SetBool("IsJumpingOrFalling", true);
}
else if (IsOnGround() && anim.GetBool("IsJumpingOrFalling"))
{
anim.SetBool("IsJumpingOrFalling", false);
}
if (Fireballs.HasFireballs != anim.GetBool("HasFireballs"))
{
anim.SetBool("HasFireballs", Fireballs.HasFireballs); //Sets the HasFireballs bool in the animator equal to the HasFireball bool in the Fireballs script
}
if (BonusLife.hasBonusLife != anim.GetBool("HasBonusLife"))
{
anim.SetBool("HasBonusLife", BonusLife.hasBonusLife); //Sets the HasBonusLife bool in the animator equal to the HasBonusLife bool in the BonusLife script
}
if (SpawnFireballs.isThrowing != anim.GetBool("IsThrowing"))
{
anim.SetBool("IsThrowing", SpawnFireballs.isThrowing); //Sets the IsThrowing bool in the animator equal to the IsThrowing bool in the SpawnFireballs script
}
}
}
(rb = RigidBody2d)
However, for some reason now when I try to walk in one direction the TurnedAroud bool would constantly switch on and off and the player would be unable to move except in weird and unexpected ways. How can I fix this? I am open to different ways of making the Player face the correct direction.
I am coding in c# in Unity 2020.3.24f1 using visual studio. I am new to stack exchange so if I asked this question wrong I am sorry.

OnCollisionExit is called, but behaves strangely

I'm developing a 3d FPS game in Unity. At the moment I'm implementing a wall-running mechanic. The logic behind this is very simple - if player is pushing forward, and not grounded, and touching a wall, I constrain the Y/Z direction (but player can still run forward as I ignore the X direction though) and turn off gravity. It seems to work fine, a little bit clumsy but ok for me. Except, when the wall is left behind player is still able to run in mid-air until he runs out of inertia (here's the example: https://imgur.com/a/LtbWs9J). Here's the code:
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public class WallRunning : MonoBehaviour
{
public AudioClip audioClip;
private CharacterMovement cm;
private Rigidbody rb;
private bool isJumping;
public bool isWall;
private bool playAudio;
private AudioSource audioSource;
public float energyLimit = 3.5f;
private void Start()
{
//Get attached components so we can interact with them in our script.
cm = GetComponent<CharacterMovement>();
rb = GetComponent<Rigidbody>();
audioSource = GetComponent<AudioSource>();
}
private void FixedUpdate()
{
bool jumpPressed = Input.GetButtonDown("Jump");
float verticalAxis = Input.GetAxis("Vertical");
//Check if the controller is grounded.
if (cm.Grounded)
{
isJumping = false;
isWall = false;
}
//Has the jump button been pressed.
if (jumpPressed)
{
StartCoroutine(Jumping());
}
//If we are pushing forward, and not grounded, and touching a wall.
if (verticalAxis > 0 && isJumping && isWall)
{
StartCoroutine(Energy());
//We constrain the Y/Z direction to defy gravity and move off the wall.
//But we can still run forward as we ignore the X direction.
rb.useGravity = false;
rb.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezeRotation;
//We also telegraph to the player by playing a sound effect on contact.
if (audioClip != null && playAudio == true)
{
audioSource.PlayOneShot(audioClip);
//We block more audio being played while we are on the wall.
playAudio = false;
}
}
else
{
StopCoroutine(Energy());
//We need to make sure we can play audio again when touching the wall.
playAudio = true;
rb.useGravity = true;
rb.constraints = RigidbodyConstraints.FreezeRotation;
}
}
void OnCollisionEnter(Collision other)
{
//Are we touching a wall object?
if (other.gameObject.CompareTag("Walls"))
{
isWall = true;
}
}
void OnCollisionExit(Collision other)
{
//Did we stop touching the wall object?
if (!other.gameObject.CompareTag("Walls"))
{
isWall = false;
}
}
IEnumerator Jumping()
{
//Check for 5 frames after the jump button is pressed.
int frameCount = 0;
while (frameCount < 5)
{
frameCount++;
//Are we airbourne in those 5 frames?
if (!cm.Grounded)
{
isJumping = true;
}
yield return null;
}
}
IEnumerator Energy()
{
yield return new WaitForSeconds(energyLimit);
isWall = false;
}
}
Notice: walls have box colliders on them ("Is Trigger" checkbox is unchecked), and player has non-kinematic rigidbody and capsule collider attached. Walls aren't marked as "static" and assigned to Default layer, while player is assigned to the Player layer.
What am I doing wrong? I'm sure I screwed up with the code, but can't figure out the problem.
Replace
void OnCollisionExit(Collision other)
{
//Did we stop touching the wall object?
if (!other.gameObject.CompareTag("Walls"))
{
isWall = false;
}
}
With
void OnCollisionExit(Collision other)
{
//Did we stop touching the wall object?
if (other.gameObject.CompareTag("Walls"))
{
isWall = false;
}
}

How can i make the player idle when not moving the mouse?

When i'm moving the mouse around the player is walking facing to the mouse cursor.
Now i want to make that if i'm not moving the mouse to make the player idle.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WorldInteraction : MonoBehaviour
{
public int speed = 5; // Determines how quickly object moves towards position
public float rotationSpeed = 5f;
private Animator _animator;
private bool toMove = true;
private Vector3 tmpMousePosition;
UnityEngine.AI.NavMeshAgent playerAgent;
private void Start()
{
tmpMousePosition = Input.mousePosition;
_animator = GetComponent<Animator>();
_animator.CrossFade("Idle", 0);
playerAgent = GetComponent<UnityEngine.AI.NavMeshAgent>();
}
private void Update()
{
if (Input.GetMouseButtonDown(0) && toMove == false &&
!UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
{
toMove = true;
}
if (Input.GetMouseButtonDown(1) && toMove == true)
{
toMove = false;
}
if (toMove == true)
{
GetInteraction();
}
}
void GetInteraction()
{
if (tmpMousePosition != Input.mousePosition)
{
tmpMousePosition = Input.mousePosition;
_animator.CrossFade("Walk", 0);
Ray interactionRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit interactionInfo;
if (Physics.Raycast(interactionRay, out interactionInfo, Mathf.Infinity))
{
GameObject interactedObject = interactionInfo.collider.gameObject;
if (interactedObject.tag == "Interactable Object")
{
interactedObject.GetComponent<Interactable>().MoveToInteraction(playerAgent);
}
else
{
playerAgent.destination = interactionInfo.point;
}
}
}
else
{
_animator.CrossFade("Idle", 0);
}
}
}
I'm using the variable tmpMousePosition to check if the mouse is in a move or not. The problem is when it's on a move and the player is in "Walk" mode the player is stuttering each a second or so.
The idea is when the mouse is moving then move the player when the mouse is not moving make the player in Idle.
In the Update function i'm using a bool to stop/continue the interaction like a switch with the mouse left/right buttons. But now i want to use the mouse movement to Walk/Idle the player.
Just get the movement through Input.GetAxis("Mouse X") and if it's not moving , play Idle

Unity Cant jump and move on the same time

Hello and thanks for reading this.
I made a little game in Unity and I finally got the movement controls with touch input to work.
But right now I face a little problem with combining the movement and jumping part. I cant jump if I'm moving BUT I can move if I'm jumping.
Each of my arrow keys contain a script and then later calls the "RobotController" script to start the movement.
ArrowRight and ArrowLeft Script. They look very much alike so I'll only post 1:
private RobotController PlayermoveRight;
// Use this for initialization
void Start () {
PlayermoveRight = GameObject.Find("Player").GetComponent<RobotController>();
}
void OnMouseOver()
{
if(Input.touchCount >= 1)
{
var touchr = Input.touches[0];
if(touchr.phase != TouchPhase.Ended && touchr.phase != TouchPhase.Canceled)
{
PlayermoveRight.MoveRight();
}
else
{
}
}
}
The ArrowUp script:
void OnMouseOver()
{
GameObject Go = GameObject.Find("Player");
if ((Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began) || (Input.GetMouseButtonDown(0)))
{
Go.GetComponent<RobotController>().Jump();
}
}
And the RobotController script:
public double moveTime = 0.1;
public double moveTimeR = 0.1;
private double lastPressedTime = 0.0;
private double PressRight = 0.0;
public void MoveLeft() // If ArrowLeft is clicked or Pressed
{
lastPressedTime = Time.timeSinceLevelLoad;
}
public void MoveRight() // If ArrowRight is clicked or Pressed
{
PressRight = Time.timeSinceLevelLoad;
}
void FixedUpdate () {
if (PressRight + moveTimeR > Time.timeSinceLevelLoad)
{
rigidbody2D.velocity = new Vector2 (maxSpeed, rigidbody2D.velocity.y);
}
else if (lastPressedTime + moveTime > Time.timeSinceLevelLoad)
{
rigidbody2D.velocity = new Vector2 (maxSpeed - maxSpeed - maxSpeed, rigidbody2D.velocity.y);
}
else
{
rigidbody2D.velocity = new Vector2(0.0f, rigidbody2D.velocity.y);
}
}
public void Jump()
{
if (isOnGround == true) {
anim.SetBool("Ground",false);
rigidbody2D.AddForce (new Vector2 (0, jumpForce));
}
}
How can I do so I can jump and move on the same time.?
from the up arrow code:
(Input.GetTouch(0).phase == TouchPhase.Began) || (Input.GetMouseButtonDown(0))
you're checking if the first touch has just started, if you are holding down a move arrow and you tap up to jump the "jump touch" isn't the first touch and the first touch (for the movement) isn't in it's began phase.
The reason it works with hitting jump and then moving is because the first touch in that case is the jump touch (by coincidence rather than by code).
You don't want to check against the first touch here, you want to check against the touch that is over the up arrow.
(not sure how you'd actually do that, but I can't comment until I get 50 rep :( )
I am also a newbie and was facing this problem few hours ago but I fixed it as I was told to do in an video tutorial so the fix was: Add some drag to your player, in the inspector there is a "Linear Drag" option in your rigidBody component increase it by 0.3(0 is the default value) this really fixed my problem I hope it will also help you(I know it is really late but I just found your question while googling my problem).

Categories