I'm currently hard at work on a little platformer in the Unity3D engine, utilizing its 2D capabilities.
Now I have been having a problem where eventhough an object is clearly grounded it somehow doesn't register as such on every frame and thus the jump function, which only runs when the object is grounded, won't work. This so far only seems to happen whilst the player is on a platform that is currently moving downwards.
Now I'm not the best at 2D physics in Unity, so as such I decided to see what everyone here thinks might be the problem.
For illustration purposes here is what it looks like in the editor:
The white block is the player character, the blue block is the platform (currently moving down) and the red line is a raycast that checks wether or not the player character is grounded.
I hope you guys can figure out what's wrong. All I have so far is that it seems 50/50 wether or not the player is grounded during a frame on the platform, whilst said platform is moving down.
Hereby also my code for the player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterMovement : MonoBehaviour {
private bool left = false;
private bool right = false;
float velX = 0;
float velY = 0;
[SerializeField]
private float gravity;
[SerializeField]
private float speed;
[SerializeField]
private float maxSpeed;
[SerializeField]
private float jumpForce;
[SerializeField]
private float jumpSpeed;
bool isGrounded = false;
float groundedTime = 0;
float distanceToGround = 0;
float distanceSide = 0;
bool plug = false;
int plugCounter = 0;
void Start () {
distanceToGround = this.GetComponent<Collider2D>().bounds.extents.y;
distanceSide = this.GetComponent<Collider2D>().bounds.extents.x;
}
void Update () {
Grounded();
WalkingInput();
Walking();
Jumping();
Debug.Log(isGrounded);
this.GetComponent<Rigidbody2D>().velocity = new Vector2(velX, this.GetComponent<Rigidbody2D>().velocity.y);
//Debug.Log(this.GetComponent<Rigidbody2D>().velocity.x + " " + this.GetComponent<Rigidbody2D>().velocity.y);
}
void Grounded()
{
LayerMask mask = (1 << 8);
mask = ~mask;
if (Physics2D.Raycast(new Vector3(this.transform.position.x - distanceSide, this.transform.position.y - distanceToGround - 0.04f, this.transform.position.z),
Vector3.right,
distanceSide * 2,
mask))
{
isGrounded = true;
groundedTime += Time.deltaTime;
}
else
{
isGrounded = false;
groundedTime = 0;
}
//debugging ray
Debug.DrawLine(new Vector3(this.transform.position.x - distanceSide, this.transform.position.y - distanceToGround - 0.04f, this.transform.position.z),
new Vector3(this.transform.position.x - distanceSide, this.transform.position.y - distanceToGround - 0.04f, this.transform.position.z) + new Vector3(distanceSide * 2, 0, 0),
Color.red);
}
void WalkingInput()
{
if (Input.GetButtonDown("Left"))
{
left = true;
}
if (Input.GetButtonUp("Left"))
{
left = false;
}
if (Input.GetButtonDown("Right"))
{
right = true;
}
if (Input.GetButtonUp("Right"))
{
right = false;
}
}
void Walking()
{
if (left == true && velX >= -maxSpeed)
{
if(velX > 0)
{
velX = 0;
}
velX -= speed * Time.deltaTime;
if (isGrounded)
{
if (velX < -maxSpeed)
{
velX = -maxSpeed;
}
} else
{
if (velX < -jumpSpeed)
{
velX = -jumpSpeed;
}
}
}
if (right == true && velX <= maxSpeed)
{
if (velX < 0)
{
velX = 0;
}
velX += speed * Time.deltaTime;
if (isGrounded)
{
if (velX > maxSpeed)
{
velX = maxSpeed;
}
}
else
{
if (velX > jumpSpeed)
{
velX = jumpSpeed;
}
}
}
if(left == false && right == false || left == true && right == true)
{
velX = 0;
}
}
void Jumping()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
Debug.Log("ActuallyJumped");
this.GetComponent<Rigidbody2D>().AddForce(new Vector2(0, jumpForce));
}
}
}
and the code for the platform:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovingPlatform : MonoBehaviour {
[SerializeField]
Transform pointA;
[SerializeField]
Transform pointB;
[SerializeField]
float platformSpeed;
private float wayThere = 0;
bool going = true; //true = going to b, false = going to a
void FixedUpdate()
{
if (going == true)
{
Vector2 offset = DetermineOffset(pointA.transform.position, pointB.transform.position);
this.GetComponent<Rigidbody2D>().velocity = new Vector2(offset.x * platformSpeed * Time.deltaTime, offset.y * platformSpeed * Time.fixedDeltaTime);
if (Vector2.Distance(this.transform.position, pointB.transform.position) < 0.2f)
{
going = false;
}
}
else
{
Vector2 offset = DetermineOffset(pointB.transform.position, pointA.transform.position);
this.GetComponent<Rigidbody2D>().velocity = new Vector2(offset.x * platformSpeed * Time.deltaTime, offset.y * platformSpeed * Time.fixedDeltaTime);
if (Vector2.Distance(this.transform.position, pointA.transform.position) < 0.2f)
{
going = true;
}
}
}
private Vector2 DetermineOffset(Vector2 PointA, Vector2 PointB)
{
int x = 0;
int y = 0;
if(PointB.x > PointA.x)
{
x = 1;
}
else if (PointB.x < PointA.x)
{
x = -1;
}
else
{
x = 0;
}
if(PointB.y > PointA.y)
{
y = 1;
}
else if (PointB.y < PointA.y)
{
y = -1;
}
else
{
y = 0;
}
return (new Vector2(x, y));
}
}
Also the editor setups for both the character and the platform in that order:
All in all I hope one of you good people can help me out on this one and thanks already!
EDIT: Changed the code for the platform to use velocity instead of rigidbody.moveposition. This works better. The bug is now non-existant on low speeds, it is still visible at high speeds. I think it might have to do with how Unity calculates rigidbody gravity because it becomes 0 when a rigidbody is grounded which means that currently the rigidbody is grounded, then it falls, then is grounded, then it falls which would explain the 50/50 ratio as well. I do however find it strange that this would work fine at a lower speed though.
If someone somehow can still manage to find out how to do this at faster speeds I'd be more than happy!
EDIT2: Answered my own question in the end. Check the answers to see how I fixed it.