Character Stuck On Walls - unity3d

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 ();
}

Related

Is there a way to have an object with rigidbody tilt but not rotate (cube)?

I am working on a project where a player can control a cube and move it around. I love having physics on the cube, but when manuevering, it is very difficult to time a jump when the cube is spinning all over the place. I would like the player to be able to move the cube along the ground, and the cube would gradually tilt towards the direction they are moving it, but not flip entirely. I don't know if it would even work conceptually, but if someone could help me figure out how to alter my code to keep it grounded but still tilt. Thank you.
{
public GameObject Camera;
Collider coll;
private bool isGrounded;
Rigidbody rb;
Vector3 velocity;
public float speed = 12f;
public float gravity = -9.8f;
public float jumpStrength = 1000f;
// Start is called before the first frame update
void Start()
{
Camera = GameObject.Find("Main Camera");
rb = GetComponent<Rigidbody>();
}
void OnCollisionEnter(UnityEngine.Collision collision)
{
if (collision.gameObject.tag == "Floor")
{
isGrounded = true;
Debug.Log("Is Grounded");
}
}
void OnCollisionExit(UnityEngine.Collision collision)
{
if (collision.gameObject.tag == "Floor")
{
isGrounded = false;
Debug.Log("Is Not Grounded");
}
}
// Update is called once per frame
void Update()
{
Camera.transform.position = new Vector3(transform.position.x, transform.position.y + 2.38f, transform.position.z - 3.45f);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float mH = Input.GetAxis("Horizontal");
float mV = Input.GetAxis("Vertical");
rb.velocity = new Vector3(mH * speed, rb.velocity.y, mV * speed);
if(isGrounded && Input.GetButtonDown("Jump"))
{
velocity.y = Mathf.Sqrt(3 * -2 * gravity);
}
velocity.y += gravity * Time.deltaTime;
if(Input.GetButtonDown("Jump") && isGrounded)
{
rb.AddForce(0f, jumpStrength, 0f);
Debug.Log("Jump");
}
}
}

Why my character won't jump with my code?

I have this problem with my 2D game project,I pasted and modified it a little bit from the code can be found in this video:https://www.youtube.com/watch?v=dwcT-Dch0bA
But the problem is it won't jump I added Debug.Log to see if the input is really the reason why my character won't jump,I tested it and it won't jump but it still shows me that the "q" button is pressed.I tried with another way is that changing input in unity but it just doesn't work.I used the same code in Brackeys's project and it works like normal.
Here is the code:
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController2D controller;
public float runSpeed = 40f;
float horizontalMove = 0f;
bool jump = false;
bool crouch = false;
void Update()
{
horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
if (Input.GetKeyDown("q"))
{
Debug.Log("Jump button works");
jump = true;
}
if (Input.GetButtonDown("Crouch"))
{
crouch = true;
}
else if (Input.GetButtonUp("Crouch"))
{
crouch = false;
}
}
void FixedUpdate()
{
controller.Move(horizontalMove * Time.fixedDeltaTime, crouch, jump);
jump = true;
}
}
CharacterController2D:
public class CharacterController2D : MonoBehaviour
{
[SerializeField] private float m_JumpForce = 400f; // Amount of force added when the player jumps.
[Range(0, 1)] [SerializeField] private float m_CrouchSpeed = .36f; // Amount of maxSpeed applied to crouching movement. 1 = 100%
[Range(0, .3f)] [SerializeField] private float m_MovementSmoothing = .05f; // How much to smooth out the movement
[SerializeField] private bool m_AirControl = false; // Whether or not a player can steer while jumping;
[SerializeField] private LayerMask m_WhatIsGround; // A mask determining what is ground to the character
[SerializeField] private Transform m_GroundCheck; // A position marking where to check if the player is grounded.
[SerializeField] private Transform m_CeilingCheck; // A position marking where to check for ceilings
[SerializeField] private Collider2D m_CrouchDisableCollider; // A collider that will be disabled when crouching
const float k_GroundedRadius = .2f; // Radius of the overlap circle to determine if grounded
private bool m_Grounded; // Whether or not the player is grounded.
const float k_CeilingRadius = .2f; // Radius of the overlap circle to determine if the player can stand up
private Rigidbody2D m_Rigidbody2D;
private bool m_FacingRight = true; // For determining which way the player is currently facing.
private Vector3 velocity = Vector3.zero;
private void Awake()
{
m_Rigidbody2D = GetComponent<Rigidbody2D>();
}
private void FixedUpdate()
{
m_Grounded = false;
// The player is grounded if a circlecast to the groundcheck position hits anything designated as ground
// This can be done using layers instead but Sample Assets will not overwrite your project settings.
Collider2D[] colliders = Physics2D.OverlapCircleAll(m_GroundCheck.position, k_GroundedRadius, m_WhatIsGround);
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i].gameObject != gameObject)
m_Grounded = true;
}
}
public void Move(float move, bool crouch, bool jump)
{
// If crouching, check to see if the character can stand up
if (!crouch)
{
// If the character has a ceiling preventing them from standing up, keep them crouching
if (Physics2D.OverlapCircle(m_CeilingCheck.position, k_CeilingRadius, m_WhatIsGround))
{
crouch = true;
}
}
//only control the player if grounded or airControl is turned on
if (m_Grounded || m_AirControl)
{
// If crouching
if (crouch)
{
// Reduce the speed by the crouchSpeed multiplier
move *= m_CrouchSpeed;
// Disable one of the colliders when crouching
if (m_CrouchDisableCollider != null)
m_CrouchDisableCollider.enabled = false;
}
else
{
// Enable the collider when not crouching
if (m_CrouchDisableCollider != null)
m_CrouchDisableCollider.enabled = true;
}
// Move the character by finding the target velocity
Vector3 targetVelocity = new Vector2(move * 10f, m_Rigidbody2D.velocity.y);
// And then smoothing it out and applying it to the character
m_Rigidbody2D.velocity = Vector3.SmoothDamp(m_Rigidbody2D.velocity, targetVelocity, ref velocity, m_MovementSmoothing);
// If the input is moving the player right and the player is facing left...
if (move > 0 && !m_FacingRight)
{
// ... flip the player.
Flip();
}
// Otherwise if the input is moving the player left and the player is facing right...
else if (move < 0 && m_FacingRight)
{
// ... flip the player.
Flip();
}
}
// If the player should jump...
if (m_Grounded && jump)
{
// Add a vertical force to the player.
m_Grounded = false;
m_Rigidbody2D.AddForce(new Vector2(0f, m_JumpForce));
}
}
private void Flip()
{
// Switch the way the player is labelled as facing.
m_FacingRight = !m_FacingRight;
// Multiply the player's x local scale by -1.
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
First of all, your code is not very optimized.
You call Physics2D.OverlapCircleAll () every frame and it's not very good for performance.
As an alternative you can use Physics2D.OverlapCircleNonAlloc(), which will save some memory, or even better, use OnCollisionEnter2D () and OnCollisionExit2D () to understand when it collides with something.
Speaking of the scripts, they seem to have to work. Try adding a print(m_Grounded + “ " + jump) and say what appears in the console.
When you press q both should be true, but apparently one of them isn't, and with that print() we'll find out what it is and fix the problem.
public void Move(float move, bool crouch, bool jump)
{
// If crouching, check to see if the character can stand up
if (!crouch)
{
// If the character has a ceiling preventing them from standing up, keep them crouching
if (Physics2D.OverlapCircle(m_CeilingCheck.position, k_CeilingRadius, m_WhatIsGround))
{
crouch = true;
}
}
//only control the player if grounded or airControl is turned on
if (m_Grounded || m_AirControl)
{
// If crouching
if (crouch)
{
// Reduce the speed by the crouchSpeed multiplier
move *= m_CrouchSpeed;
// Disable one of the colliders when crouching
if (m_CrouchDisableCollider != null)
m_CrouchDisableCollider.enabled = false;
}
else
{
// Enable the collider when not crouching
if (m_CrouchDisableCollider != null)
m_CrouchDisableCollider.enabled = true;
}
// Move the character by finding the target velocity
Vector3 targetVelocity = new Vector2(move * 10f, m_Rigidbody2D.velocity.y);
// And then smoothing it out and applying it to the character
m_Rigidbody2D.velocity = Vector3.SmoothDamp(m_Rigidbody2D.velocity, targetVelocity, ref velocity, m_MovementSmoothing);
// If the input is moving the player right and the player is facing left...
if (move > 0 && !m_FacingRight)
{
// ... flip the player.
Flip();
}
// Otherwise if the input is moving the player left and the player is facing right...
else if (move < 0 && m_FacingRight)
{
// ... flip the player.
Flip();
}
}
// If the player should jump…
print(“m_Grounded = " + m_Grounded + " && jump = " + jump);
if (m_Grounded && jump)
{
// Add a vertical force to the player.
m_Grounded = false;
m_Rigidbody2D.AddForce(new Vector2(0f, m_JumpForce));
}
}
if you think my answer helped you, you can mark it as accepted and vote positively. I would very much appreciate it :)

How to jump when player is upside down in a spherical world, using FauxGravity?

What I try to do
I'm trying to get jumping working all over the sphere and not just at the top while using FauxGravity.
How it works currently
My character jumps correctly when he is on top but when he is in the bottom of the sphere the jump doesn't take place.
FauxGravityAttractor
[SerializeField] private float gravity = -9.81f;
public void Attract(Rigidbody body) {
Vector3 gravityUp = (body.position - transform.position).normalized;
Vector3 localUp = body.transform.up;
// Apply downwards gravity to body
body.AddForce(gravityUp * gravity);
// Align bodies up axis with the centre of planet
body.rotation = Quaternion.FromToRotation(localUp,gravityUp) * body.rotation;
}
FauxGravityBody
FauxGravityAttractor planet;
new Rigidbody rigidbody;
void Awake()
{
planet = GameObject.FindGameObjectWithTag("Planet").GetComponent<FauxGravityAttractor>();
rigidbody = GetComponent<Rigidbody>();
// Disable rigidbody gravity and rotation as this is simulated in GravityAttractor script
rigidbody.useGravity = false;
rigidbody.constraints = RigidbodyConstraints.FreezeRotation;
}
void FixedUpdate()
{
// Allow this body to be influenced by planet's gravity
planet.Attract(rigidbody);
}
Sample Jumping
void Jump()
{
if(Input.GetKeyDown(KeyCode.Space) && isOnGround)
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
isOnGround = false;
}
}
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.CompareTag("Planet"))
{
isOnGround = true;
}
}
FauxGravity jumping problem solved
All you need to do is AddRelativeForce to your rigidbody
Read more about RelativeForce
// JUMPING
private void Jump()
{
if (Input.GetKeyDown(KeyCode.Space) && onGround)
{
rb.AddRelativeForce(0, forceConst, 0, ForceMode.Impulse);
onGround = false;
}
}

Why is there a constant stutter when i move in Unity 2d?

So i have a simple scene in unity, a player with a parallax background and a Tilemap as a ground, as well as some very simple post processing. I know this isn't a The minute i move, there is a consistent stutter just under ever second. I'm not sure whether it's to do with my player movement code, camera movement or anything else. I'm also using a Cinemachine virtual camera. My rigidbody interpolation is set to interpolate and collision detection set to continuous. Here's my player movement if this helps. Here is a sample of what it looks like, if you look at the background or the tilemap it's quite noticeable. https://youtu.be/h2rSheZWtKs
[SerializeField] private LayerMask groundLayerMask;
public float speed;
public float Jump;
public sword swordScript;
public GameObject swordSprite;
private float move;
private Rigidbody2D rb;
private BoxCollider2D boxCollider2d;
private bool facingRight;
public SpriteRenderer spr;
public Animator PlayerAnims;
public bool movementAllowed;
void Awake()
{
Application.targetFrameRate = 60;
Application.targetFrameRate = Screen.currentResolution.refreshRate;
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void FixedUpdate()
{
if(movementAllowed == true)
{
rb.velocity = new Vector2(move * speed, rb.velocity.y);
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(new Vector2(rb.velocity.x, Jump));
}
}
}
void Update()
{
move = Input.GetAxis("Horizontal");
rb.velocity = new Vector2(move * speed, rb.velocity.y);
if (movementAllowed == true)
{
Flip(move);
if (move == 0)
{
PlayerAnims.SetBool("isRunning", false);
}
else
{
PlayerAnims.SetBool("isRunning", true);
}
}
}
private bool isGrounded()
{
float extraHeightText = .1f;
RaycastHit2D raycasthit2d = Physics2D.BoxCast(boxCollider2d.bounds.center, boxCollider2d.bounds.size, 0f, Vector2.down, extraHeightText, groundLayerMask);
Color rayColour;
if (raycasthit2d.collider != null)
{
rayColour = Color.green;
PlayerAnims.SetBool("isJumping", false);
}
else
{
rayColour = Color.red;
PlayerAnims.SetBool("isJumping", true);
}
Debug.DrawRay(boxCollider2d.bounds.center + new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, boxCollider2d.bounds.extents.y + extraHeightText), Vector2.right * (boxCollider2d.bounds.extents.x), rayColour);
return raycasthit2d.collider != null;
}
private void Flip(float move)
{
if (move > 0 && !facingRight || move < 0 && facingRight)
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
if (swordScript.isFollowing == true)
{
Vector3 swordScale = swordSprite.transform.localScale;
swordScale.x *= -1;
swordSprite.transform.localScale = swordScale;
}
}
}
You are setting rb.velocity in both the Update() and FixedUpdate() methods. I would try only using one of those.
On top of that, your jump check also re-applies the X velocity along with the upward force. So if you're jumping the player will be pushed forward at double speed.
You also have an error being outputted in the console about an Index being out of range... I would look into that.
Can you also post your code for the parallax background?
FixedUpdate is a method where you want to do everything physics, player-controller and simulation related.
Update is just for rendering-related fluff i.e. code of no real consequence other than making things look correctly.
Hence:
Move all your player/physics controller code to FixedUpdate.
Move bg parallax code to Update.
Use Time.deltaTime (in Update) and Time.fixedDeltaTime (in FixedUpdate) time steps when animating or interpolating between values.
Ad #3.: Although - as #Menyus noted (below) - Time.deltaTime is all you really need.
Time.fixedDeltaTime is for that extra intent explicitness (but was necessary in earlier unity versions).

Trying to create a nice character controller?

I'm kind of new in the 2D environment of Unity.
I'm trying to create a platformer. For now, I have a simple map and my player.
My simple map and my player
My player have one script attached :
public class Player : MonoBehaviour
{
public float speed;
public float jump;
public GameObject raycastPoint; // Positioned at 0.01 pixel below the player
private SpriteRenderer spriteRenderer;
private Rigidbody2D body; // Gravity Scale of the Rigidbody2D = 50
private Animator animator;
private void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>();
body = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
}
private void Update()
{
float horizontal = Input.GetAxisRaw("Horizontal");
if (horizontal == 1 && spriteRenderer.flipX)
{
spriteRenderer.flipX = false;
}
else if (horizontal == -1 && !spriteRenderer.flipX)
{
spriteRenderer.flipX = true;
}
body.velocity = new Vector2(horizontal * speed, body.velocity.y);
animator.SetFloat("Speed", Mathf.Abs(horizontal));
float vertical = Input.GetAxisRaw("Vertical");
if (vertical == 1)
{
RaycastHit2D hit = Physics2D.Raycast(raycastPoint.transform.position, Vector2.down, 0.01f);
if (hit.collider != null)
{
body.AddForce(new Vector2(0f, jump));
}
}
}
}
For now I have achieved the right and left movements.
For the jump, I have a child gameobject just under the player and I'm firing a raycast to the bottom so I can know if my player is grounded or not.
I have two problems.
PROBLEM NUMBER ONE.
Sometimes I feel like my "AddForce" line is executed multiple times my player is jumping really high
Problem number one image
PROBLEM NUMBER TWO.
When I'm jumping to the left or right wall, if I keep pressing the left or right key my player is not falling anymore and stay against the wall.
Problem number two image
I tried to put my code into the FixedUpdate method (I know it's better) but I had the same results.
And I tried to set the Collision Detection on Continuous but I had the same results.
Try this code for your first problem :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(BoxCollider2D))]
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Animator))]
public class Player_Controller : MonoBehaviour {
private Rigidbody2D body;
private bool canJump, facingRight;
private Animator anim;
[SerializeField]
private float moveSpeed, jumpForce;
void Start ()
{
SetStartValues();
}
void FixedUpdate ()
{
float horizontal = Input.GetAxis("Horizontal");
animator.SetFloat("Speed", Mathf.Abs(horizontal));
Flip(horizontal);
Move(horizontal);
Jump();
}
private void SetStartValues()
{
body = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
canJump = true;
facingRight = true;
}
private void Jump()
{
if (Input.GetKeyDown(KeyCode.Space) && canJump)
{
body.AddForce(new Vector2(0, jumpForce));
canJump = false;
}
}
private void Move(float x)
{
body.velocity = new Vector2(x * moveSpeed * Time.deltaTime, body.velocity.y);
}
private void Flip(float x)
{
if (x > 0 && !facingRight|| x < 0 && facingRight)
{
facingRight = !facingRight;
transform.localScale = new Vector2(transform.localScale.x * -1, transform.localScale.y) ;
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground")
{
canJump = true;
}
}
}
And don't forget to put the "Ground" tag on your ground object.