- Home /
How to make a jumping mechanic for a rolling ball
I'm making a 3D game where you control a rolling marble. It can move in all 4 directions and you can jump. Im also using a camera that follows you and that you can orbit around the player. The current script I'm using for movement isn't perfect and I'm having alot of trouble with checking if the player is grounded. I can't just test if the player is colliding with an object because that cant tell the difference between standing on a closet or next to a closet so you can wall jump on stuff which I don't want. I also can't use a raycast becuase then I wouldnt be able to jump on slopes because the bottem isnt touching the ground then. Here is the code I used (Don't mind the horrible way of deciding which way the player should move relative to the camera):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControl : MonoBehaviour
{
public Rigidbody rb;
public Transform camRotation;
public float groundSpeed, airSpeed;
float speed;
float rotation = 0f;
int jumpDelay;
bool isGrounded = false;
RaycastHit hit;
float distance = 0.51f;
Vector3 dir = new Vector3(0, -1);
void OnCollisionEnter(Collision collision) {
if (collision.gameObject.tag == "Jumpable") {
isGrounded = true;
} else {
isGrounded = false;
}
}
void OnCollisionStay(Collision collision) {
if (collision.gameObject.tag == "Jumpable") {
isGrounded = true;
} else {
isGrounded = false;
}
}
void OnCollisionExit(Collision collision) {
if (collision.gameObject.tag == "Jumpable") {
isGrounded = false;
}
}
void FixedUpdate() {
if (isGrounded) {
speed = groundSpeed;
} else {
speed = airSpeed;
}
rotation = Mathf.Deg2Rad * camRotation.rotation.eulerAngles.y;
if (Input.GetKey(KeyCode.W)) {
rb.AddForce(0, 0, (speed * Mathf.Sin(rotation + (Mathf.PI / 2))) * Time.deltaTime, ForceMode.VelocityChange);
rb.AddForce((speed * Mathf.Sin(rotation)) * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
if (Input.GetKey(KeyCode.A)) {
rb.AddForce(0, 0, (speed * Mathf.Sin(rotation)) * Time.deltaTime, ForceMode.VelocityChange);
rb.AddForce((-speed * Mathf.Sin(rotation + (Mathf.PI / 2))) * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
if (Input.GetKey(KeyCode.S)) {
rb.AddForce(0, 0, (-speed * Mathf.Sin(rotation + (Mathf.PI / 2))) * Time.deltaTime, ForceMode.VelocityChange);
rb.AddForce((-speed * Mathf.Sin(rotation)) * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
if (Input.GetKey(KeyCode.D)) {
rb.AddForce(0, 0, (-speed * Mathf.Sin(rotation)) * Time.deltaTime, ForceMode.VelocityChange);
rb.AddForce((speed * Mathf.Sin(rotation + (Mathf.PI / 2))) * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
if (Input.GetKey(KeyCode.Space) && jumpDelay > 5 && isGrounded && Physics.Raycast(transform.position, dir, out hit, distance)) {
jumpDelay = 0;
rb.AddForce(0, 250 * Time.deltaTime, 0, ForceMode.Impulse);
isGrounded = false;
}
jumpDelay++;
}
}
If I need to clarify something just tell me!
Answer by DrawMen1999 · Sep 12, 2019 at 05:21 PM
To handle the slopes you can use Physics.Spherecast (https://docs.unity3d.com/ScriptReference/Physics.SphereCast.html). It's basically a very large raycast, if you configure it correctely you can check a zone underneath the marble and detect slopes. Just do the same as with a regular raycast and make the sphere check slightly lower than the marble's collider.
I don't exactly know how raycasting and stuff works, could you make line of code that would work for me?
Sure, try this:
private void CheckGround(){
RaycastHit hitInfo;
if(Physics.SphereCast(transform.position + Vector3.up * groundCheckOffset,
groundCheckRadius, -Vector3.up, out hitInfo, groundCheckDistance, groundLayer)){
grounded = true;
}
else{
grounded = false;
}
}
$$anonymous$$ake sure grounded is a global variable and this method is called in FixedUpdate. The groundcheckOffset is the distance from the center of the marble your cast starts (for you 0 should work), the groundCheckRadius should be slightly larger than the marble and the groundCheckDistance should be around 0.05. To visualize add this method
private void OnDrawGizmos(){
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(transform.position + Vector3.up * (groundCheckOffset -
groundCheckDistance), groundCheckRadius);
}
Answer by slythethieve · Sep 12, 2019 at 11:48 AM
Have you considered using submeshes for different parts of the level like ground, walls and so forth? Than if you touch a different submesh you have a different kind of movement.
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Renderer on object disabled after level reload 1 Answer
GameObject.Find not working 4 Answers