Problems With Collision Detection (OnCollisionEnter2D) - c#

I am starting to make a 2D platformer. I figured out how to make the character jump, but when I tried to add in Collision Detection to make it to where the player can only jump on the ground, it wouldn't work. Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
Vector2 newPosition = new Vector2(0, 7);
float movementSpeed = 5f;
float jumpForce = 10f;
public bool isGrounded;
// Start is called before the first frame update
void Start()
{
GetComponent<Rigidbody2D>().MovePosition(newPosition);
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.A))
{
Vector2 position = this.transform.position;
position.x -= movementSpeed * Time.deltaTime;
this.transform.position = position;
}
if (Input.GetKey(KeyCode.D))
{
Vector2 position = this.transform.position;
position.x += movementSpeed * Time.deltaTime;
this.transform.position = position;
}
if (Input.GetKey(KeyCode.Space) && isGrounded == true)
{
Vector2 position = this.transform.position;
position.y += jumpForce * Time.deltaTime;
this.transform.position = position;
isGrounded = false;
}
}
void OnCollisionEnter2D(Collision2D other)
{
isGrounded = true;
}
}

Sometimes this is problem of the collider that touches the ground. Try to make it higher, lower and try again. I had the same problem once and the solution was just adjusting the collider. Some times the collider is too low, so when you jump it touches the ground again so it becomes true and immediately stops jumping.

No that wont do it. Try adding a Debug.Log(isGrounded) inside OnCollisionEnter2D() so you can see when it happens exactly maybe that will help you. By the way you haven't put any conditions inside the OnCollisionEnter2D() which means anything that enters it's collider will make isGrounded = true; .

Related

In Unity player movement speed is not being added to the first bullet fired

I'm trying to make a 2D top-down shooter in Unity. I want it so when the you hold down the left mouse button the player fires a series of bullets until you run out of ammo. The player's movement speed is slowed while firing and the players movement speed should be added to the bullet's movement speed. For some reason the players movement speed is only applied to the bullets AFTER the first bullet is fired. The first bullet always seems to keep the slightly faster 'sprint' movement speed.
Weapon script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour
{
private GameObject bulletPrefab;
private Transform firePoint;
private PlayerControls player;
public float fireForce = 10f;
private bool cooldown = false;
private int bullets;
public int bulletDamage;
public int maxAmmo;
public float fireRate = 0.5f;
public float reloadRate = 2.5f;
public bool noAmmo = false;
public float walkSpeed = 2f;
private float timeSinceLastShot = 0f;
void Update()
{
// increase time since last shot
timeSinceLastShot += Time.deltaTime;
// if left-click is held down
if (Input.GetMouseButton(0))
{
// if enough time has passed since last shot
if (timeSinceLastShot >= fireRate && noAmmo == false)
{
player.moveSpeed = walkSpeed;
bullets -= 1;
cooldown = true;
if (bullets <= 0)
{
noAmmo = true;
player.moveSpeed = player.baseSpeed;
}
// instantiate a bullet
GameObject bullet = Instantiate(bulletPrefab, firePoint.transform.position, Quaternion.identity);
bullet.GetComponent<Bullet>().bulletDamage = bulletDamage;
// add player movement speed to bullet's speed
bullet.GetComponent<Rigidbody2D>().velocity = player.GetComponent<Rigidbody2D>().velocity;
bullet.GetComponent<Rigidbody2D>().AddForce(transform.up * fireForce, ForceMode2D.Impulse);
// reset time since last shot
timeSinceLastShot = 0f;
}
}
// if left-click is not held down
else
{
cooldown = false;
// restore player movement speed
player.moveSpeed = player.baseSpeed;
}
}
public void FillMag()
{
bullets = maxAmmo;
noAmmo = false;
}
}
PlayerControls:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControls : MonoBehaviour
{
public float moveSpeed = 5f;
public float baseSpeed = 5f;
public int health;
public Weapon weapon;
private Rigidbody2D rb;
Vector2 mousePosition;
Vector2 moveDirection;
public float walkSpeed = 2f;
void Update()
{
float moveX = Input.GetAxisRaw("Horizontal");
float moveY = Input.GetAxisRaw("Vertical");
moveDirection = new Vector2(moveX, moveY).normalized;
mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
rb.velocity = new Vector2(moveDirection.x * moveSpeed, moveDirection.y * moveSpeed);
Vector2 aimDirection = mousePosition - rb.position;
float aimAngle = Mathf.Atan2(aimDirection.y, aimDirection.x) * Mathf.Rad2Deg - 90f;
rb.rotation = aimAngle;
}
}
The player is moving from left to right.
Player firing
According to Unity's lifecycle, inputs are only calculated just before the Update method is called, but physics are applied during the FixedUpdate method. Is this what is causing my problems? I've tried moving some calculations into FixedUpdate and LateUpdate but nothing seems to make any difference.
Any help is appreciated. I've been banging my head against this for a few days now. I'm an amature, so feel free to explain like I'm 5.
So there are 2 problems with your code:
if you are not holding down left mouse, but multiple click instead:
the else statement in the Update() will immediately set it player back to base speed, which is not really matter if you intent to holding your mouse.
There are 2 concurrent things happen
first, you set player moveSpeed in Weapon Update()
at the same time, you calculate player velocity in Player Update()
But it TAKES TIME for player to update velocity before you get it right to your weapon.
Therefore, I recommend you to use IEnumerator to delay the fire action.
public class Weapon : MonoBehaviour
{
void FixedUpdate()
{
// increase time since last shot
timeSinceLastShot += Time.deltaTime;
// if left-click is held down
if (Input.GetMouseButton(0))
{
// if enough time has passed since last shot
if (timeSinceLastShot >= fireRate && noAmmo == false)
{
//player.moveSpeed = walkSpeed;
StopAllCoroutines();
StartCoroutine(SetSpeed());
bullets -= 1;
cooldown = true;
if (bullets <= 0)
{
noAmmo = true;
player.moveSpeed = player.baseSpeed;
}
// instantiate a bullet
StartCoroutine(FireBulelt());
// reset time since last shot
timeSinceLastShot = 0f;
}
}
// if left-click is not held down
}
IEnumerator SetSpeed()
{
player.moveSpeed = walkSpeed;
yield return new WaitForSeconds(0.5f);
cooldown = false;
// restore player movement speed
player.moveSpeed = player.baseSpeed;
yield return null;
}
IEnumerator FireBulelt()
{
yield return new WaitForSeconds(0.05f);
GameObject bullet = Instantiate(bulletPrefab, player.transform.position, Quaternion.identity);
print(" player.GetComponent<Rigidbody2D>().velocit " + player.GetComponent<Rigidbody2D>().velocity);
// add player movement speed to bullet's speed
Rigidbody2D bulletRB = bullet.GetComponent<Rigidbody2D>();
print(" bulletRB.velocit " + bulletRB.velocity);
bulletRB.velocity = player.GetComponent<Rigidbody2D>().velocity;
print(" after bulletRB.velocit " + bulletRB.velocity);
bulletRB.AddForce(transform.up * fireForce, ForceMode2D.Impulse);
}
}
And you should add print() like I did to keep track of code behavior when you're debugging.

How to make my character look in the direction of the camera in Unity 3d?

My character has a chinemachine camera attached to it and it is moving perfectly fine. But when I move my mouse to change the camera direction it is not looking in that direction. And I can't figure out how to make it look in that direction.
I have a reference to the original camera at the top of the script by the name cam.
The script has different functions for movement, rotation, animation, etc.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//so that unity recognises the callbacks ctx passed to the movementinput
using UnityEngine.InputSystem;
public class AnimationAndMovController : MonoBehaviour
{
public Transform cam;
public float speed;
//here we are creadting three vars for the animation
//vector2 currentMovementInput stores the input axis of the player
//vector3 store the current position of the player
PlayerInput playerInput;
CharacterController characterController;
Animator animator;
Vector2 currentMovementInput;
Vector3 currentMovement;
Vector3 currentRunMovement;
Vector3 moveDir;
bool isMovementPressed;
bool isRunPressed;
float rotationFactor = 15.0f;
// float turnSmoothtime = 0.1f;
// float turnSmoothVelocity ;
float runMultiplier = 4.0f;
int walkHash ;
int runHash ;
//runs before start function
void Awake(){
playerInput = new PlayerInput();
characterController = GetComponent<CharacterController>();
animator = GetComponent<Animator>();
// walkHash = Animator.StringToHash("Walking");
// runHash = Animator.StringToHash("run");
//now instead of writing the logic three times we pass the callback ctx to the movementInput() function
playerInput.CharacterControls.Move.started += movementInput;
playerInput.CharacterControls.Move.canceled += movementInput;
playerInput.CharacterControls.Move.performed += movementInput;
playerInput.CharacterControls.Run.started += handleRun;
playerInput.CharacterControls.Run.canceled += handleRun;
}
void handleRun(InputAction.CallbackContext ctx){
isRunPressed = ctx.ReadValueAsButton();
}
//we are going to handle rotations with quaternions
void handleRotation(){
Vector3 positionToLookAt;
positionToLookAt.x = currentMovement.x;
positionToLookAt.y = 0.0f ;
positionToLookAt.z = currentMovement.z;
Vector3 direction = new Vector3(positionToLookAt.x, 0.0f, positionToLookAt.z).normalized;
Quaternion currentRotation = transform.rotation;
//we take the current rotation and the target rotation and slerp them *FYI : Im still not sure how slerp works
if(isMovementPressed){
Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt);
transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactor * Time.deltaTime);
}
}
//we are passing the callback ctx to this function so that we dont have to call the function everytime we start, cancel or perform the movement
void movementInput(InputAction.CallbackContext ctx){
//we are setting the movement input to the axis of the player
currentMovementInput = ctx.ReadValue<Vector2>();
currentMovement.x = currentMovementInput.x;
//we are setting the z axis to the y axis of the player because we move y axis on keyboard or joystick but in game we move in z axis
currentMovement.z = currentMovementInput.y;
//now we are setting the run movement to the current movement
currentRunMovement.x = currentMovementInput.x * runMultiplier;
currentRunMovement.z = currentMovementInput.y * runMultiplier;
isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;
}
void handleAnimation(){
bool walk = animator.GetBool("walking");
bool run = animator.GetBool("run");
if(isMovementPressed && !walk){
animator.SetBool("walking", true);
}
else if(!isMovementPressed && walk){
animator.SetBool("walking", false);
}
if((isMovementPressed && isRunPressed) && !run){
animator.SetBool("run", true);
}
else if((!isMovementPressed || !isRunPressed)&& run){
animator.SetBool("run", false);
}
}
void handleGravity(){
//we are setting the gravity to -9.8f because we are moving in y axis
if(characterController.isGrounded){
float groundGravity = -0.05f;
currentMovement.y = groundGravity;
currentRunMovement.y = groundGravity;
}
else{
float gravity = -9.8f;
currentMovement.y += gravity * Time.deltaTime;
currentRunMovement.y += gravity * Time.deltaTime;
}
}
// Update is called once per frame
void Update()
{
handleAnimation();
handleRotation();
handleGravity();
if(isRunPressed){
characterController.Move(currentRunMovement * Time.deltaTime);
}
else{
characterController.Move(currentMovement * Time.deltaTime);
}
}
//we are checking if the player script gets enabled or disabled and accordingly we are enabling or disabling the player input
void OnEnable(){
playerInput.CharacterControls.Enable();
}
void OnDisable(){
playerInput.CharacterControls.Disable();
}
}
That quite a lot of code to dive into.
I'd try:
Quaternion cameraRot = Camera.Main.transform.rotation;
transform.rotation = cameraRot;
Or if you have a target Quaternion.LookRotation:
Vector3 relativePos = target.position - transform.position;
// the second argument, upwards, defaults to Vector3.up
Quaternion rotation = Quaternion.LookRotation(relativePos, Vector3.up);
transform.rotation = rotation;
Hope that helps
Use Transform.LookAt, which points a Game Object's rotation towards a target's position.
In your case, this would be
transform.LookAt(cam);
here is the code that I use..
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveCamera : MonoBehaviour
{
public float sensitivity = 1000f;
public float xRotation = 0f;
public Transform playerBody;
public Quaternion localRotate;
// Start is called before the first frame update
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update()
{
float MX = Input.GetAxis("Mouse X")*sensitivity*Time.deltaTime;
float MY = Input.GetAxis("Mouse Y")*sensitivity*Time.deltaTime;
xRotation -= MY;
xRotation = Mathf.Clamp(xRotation,-90f,90f);
transform.localRotation = Quaternion.Euler(xRotation,0f,0f);
localRotate = transform.localRotation;
playerBody.Rotate(Vector3.up * MX);
}
}
the player body you see is the player object to you character...
note that the camera is use is not In. cinema chine and is inside of the player...

With what I should replace dirX for my bullets to shoot right and left

I am doing a 2d game and I want to know how I can make the bullets to shoot right and left . At this moment the bullets go just to the left , even if my player moves right. How I can make them shoot both sides or shoot just when they find an object tagged " enemy "
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
public class Character : MonoBehaviour
{
Rigidbody2D rb;
float dirX;
[SerializeField]
float moveSpeed = 5f, jumpForce = 400f, bulletSpeed = 500f;
Vector3 localScale;
public Transform barrel;
public Rigidbody2D bullet;
// Use this for initialization
void Start()
{
localScale = transform.localScale;
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
dirX = CrossPlatformInputManager.GetAxis("Horizontal");
if (CrossPlatformInputManager.GetButtonDown("Jump"))
Jump();
if (CrossPlatformInputManager.GetButtonDown("Fire1"))
Fire();
}
void FixedUpdate()
{
rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);
}
void Jump()
{
if (rb.velocity.y == 0)
rb.AddForce(Vector2.up * jumpForce);
}
void Fire()
{
var firedBullet = Instantiate(bullet, barrel.position, barrel.rotation);
firedBullet.AddForce(barrel.up * bulletSpeed);
}
}
You're already accounting for the direction of the barrel. You just need to change the direction of the barrel when you move.
One way to do that is to just set that directly:
// Update is called once per frame
void Update()
{
dirX = CrossPlatformInputManager.GetAxis("Horizontal");
if (dirX !=0)
{
barrel.up = Vector3.right * Mathf.Sign(dirX);
}
if (CrossPlatformInputManager.GetButtonDown("Jump"))
Jump();
if (CrossPlatformInputManager.GetButtonDown("Fire1"))
Fire();
}
If the barrel is a child of the player object, then changing the character's rotation so that the barrel's direction points in the correct direction will also work. There's not enough information in the question to know for sure but maybe using Quaternion.LookRotation to set the character's rotation like so would work:
// Update is called once per frame
void Update()
{
dirX = CrossPlatformInputManager.GetAxis("Horizontal");
if (dirX !=0)
{
Vector3 newPlayerForward = Vector3.forward * Mathf.Sign(dirX);
transform.rotation = Quaternion.LookRotation(newPlayerForward, Vector3.up);
}
if (CrossPlatformInputManager.GetButtonDown("Jump"))
Jump();
if (CrossPlatformInputManager.GetButtonDown("Fire1"))
Fire();
}

My sprite only flips when it's close to the player

I'm making an enemy in my 2D Unity game, and I was trying to flip the sprite when it faces the opposite direction, the thing is, the sprite would only flip when the player was very close to the enemy. This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMovement : MonoBehaviour
{
public float moveSpeed;
private PlayerMovement player;
public float playerRange;
public LayerMask playerLayer;
public bool playerinRange;
private bool Right = false;
private void Start()
{
player = FindObjectOfType<PlayerMovement>();
}
private void Update()
{
playerinRange = Physics2D.OverlapCircle(transform.position, playerRange, playerLayer);
if (playerinRange)
{
transform.position = Vector3.MoveTowards(transform.position, player.transform.position, moveSpeed * Time.deltaTime);
}
Flip();
}
private void OnDrawGizmosSelected()
{
Gizmos.DrawSphere(transform.position, playerRange);
}
private void Flip()
{
if (transform.position.x > 0 && !Right || transform.position.x < 0 && Right)
{
Right = !Right;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
}
Looking at your code, what's happening is that the direction the enemy is facing is dependent of the position of the enemy in the world (since you are using the x component of transform.position). If your enemy x position is positive, the Right boolean will be true, otherwise it will be false (also the 0 case isn't accounted for).
Since the only thing that can move the enemy in this code snippet is the Vector3.MoveToward when the player is in range, I suspect what's happening is that as your player gets close, the enemy moves and the x position value goes from negative to positive (or the opposite), flipping the sprite.
Now, linking the sprite direction to the enemy's position isn't something you want to do. Instead, you want the enemy to face the direction it is moving toward. This can be done by having a velocity parameter that you use to move your enemy and which will tell you in which direction he is moving (and at which speed) for instance. If you want to keep the move toward, then you need to figure out in which direction it is making you move and flip the sprite accordingly. Here is a code example base on your code:
private void Update()
{
playerinRange = Physics2D.OverlapCircle(transform.position, playerRange, playerLayer);
if (playerinRange)
{
transform.position = Vector3.MoveTowards(transform.position, player.transform.position, moveSpeed * Time.deltaTime);
Flip(player.transform.position.x - transform.position.x);
}
}
private void Flip(float direction)
{
Right = (direction >= 0);
Vector3 theScale = transform.localScale;
theScale.x = Mathf.Abs(theScale.x) * Mathf.Sign(direction);
transform.localScale = theScale;
}

Character won't jump in Unity2D but entered the jump statement

I have a little problem with my player control script (C#) in the unity enigne. I worked out the following script with the basic movement of the player. The problem is that the player can enter the jump statement (the debug log printed it out)
Debug Log
but it will not work. The character is still on the ground.
The jump function will be enabled when the player is on the ground (grounded) and did not a double jump.
So my question is are there any "code mistakes" or maybe some configuration problems which I do not see?
Thank you for your help in advance!
using UnityEngine;
using System.Collections;
public class PlayerControl : MonoBehaviour
{
// public variables
public float speed = 3f;
public float jumpHeight = 5f;
// private variables
Vector3 movement;
Animator anim;
Rigidbody2D playerRigidbody;
// variables for the ground check
public Transform groundCheck;
public float groundCheckRadius;
public LayerMask whatIsGround;
private bool grounded;
private bool doubleJump;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
// Proves if the player is on the ground and activate the double jump function
if (grounded)
{
doubleJump = false;
}
// First line of proving the jump
if (Input.GetMouseButtonDown(0) && grounded)
{
Debug.Log("Jump if entered");
Jump();
}
if (Input.GetMouseButtonDown(0) && !doubleJump && !grounded)
{
Debug.Log("double Jump");
Jump();
doubleJump = true;
}
// Flipping the Player when he runs back
if (Input.GetAxis("Horizontal") < 0)
{
playerRigidbody.transform.localScale = new Vector2(-1.7f, 1.7f);
}
else
{
playerRigidbody.transform.localScale = new Vector2(1.7f, 1.7f);
}
}
void Awake()
{
// References setting up
playerRigidbody = this.GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
void FixedUpdate()
{
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
// simple Movement without a speed control
Move(horizontal, vertical);
Animating(horizontal, vertical);
// Section for ground detection
grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
// Set the parameter for the jump animation false or true
anim.SetBool("Grounded", grounded);
}
void Move(float horizontal, float vertical)
{
movement.Set(horizontal, 0f, vertical);
movement = movement.normalized * speed * Time.deltaTime;
playerRigidbody.MovePosition(transform.position + movement);
}
void Jump()
{
playerRigidbody.AddForce(Vector3.up * jumpHeight);
// playerRigidbody.AddForce(new Vector2(0f, jumpHeight), ForceMode2D.Impulse);
Debug.Log("Jump function");
}
void Animating(float h, float v)
{
bool walking = h != 0f || v != 0f;
anim.SetBool("IsWalking", walking);
}
}
Just guessing here, but maybe Vector3.up does not work for 2D physics? I'm not really into 2D, but you could try
playerRigidbody.AddForce(transform.up * jumpHeight);
instead.
Also, have you tried different values for jumpHeight? 5 might be way to small depending on the mass you set for your rigidbody.
And make sure you haven't restricted any axes in the inspector.
// Note: If you want the object to move in a reliable predictable way but still allow physics interactions, use MovePosition (set the object to kinematic if you want it to be unaffected by physics but still be able to affect other things, and uncheck kinematic if you want both objects to be able to be acted on by physics.
If you want to move your object but let physics handle the finer details, add a force.
playerRigidbody.rigidbody2D.AddForce(Vector3.up * 10 * Time.deltaTime);
use Vector.up, Vector.down, vector.right, Vectore.left along with time.deltaTime to have smooth movement along the frame.

Categories