I am trying to achieve jumping on a 2d sprite in 3d world.
I can jump fine but I'm able to keep jumping in the air which I do not want.
I've been trying to find the problem for hours but still cannot come up with a solution.
{
public float jumpHeight = 5f;
public static bool isJumping = false;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && (isJumping == false))
{
gameObject.GetComponent<Rigidbody>().AddForce(new Vector2(0f, jumpHeight), ForceMode.Impulse);
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.collider.tag == "Ground")
{
isJumping = false;
}
}
private void OnCollisionExit(Collision collision)
{
if (collision.collider.tag == "Ground")
{
isJumping = true;
}
}
}
Am I missing something?
Put a collider as trigger on your 2D object and check if it is colliding with the terrain or ground. Only then you can jump. The same could be done with a Ray which collides with the ground. You dont need to use both just jump OnCollisionStay and thats it
Related
C# Character Controller
I'm new to using unity and I having difficulty with the force being applied to the sprite. The jump vector 2 variable is fine but I'm having issue with the horizontal movement. The code is not detecting if the key is held down,it will only add force to either side if you constantly tap the key, then it will move in a direction.
I'm not sure if I cant use the rigid body for horizontal movement or the vector is not written correctly. If you could please respond with the possible issues and or solution that would be helpful, thanks.
public float JumpForce;
public float HorizontalForce;
bool isGrounded = false;
Rigidbody2D RB;
void Start()
{
RB = GetComponent<Rigidbody2D>();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
if(isGrounded == true)
{
RB.AddForce(Vector2.up * JumpForce);
isGrounded = false;
}
}
if (Input.GetKeyDown(KeyCode.A))
{
RB.AddForce(Vector2.left * HorizontalForce);
}
if (Input.GetKeyDown(KeyCode.D))
{
RB.AddForce(Vector2.right * HorizontalForce);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("ground")) ;
{
if(isGrounded == false)
{
isGrounded = true;
}
}
}
}
You need GetButton() instead of GetButtonDown().
GetButtonDown is only true for a single frame, in which the button was pressed. GetButton returns true as long as you hold the button
How can I make my player die when he touches two different gameObjects with the different tags. The player should die when he touches "enemy" tag and "ground" tag. Also, I am not trying to use the same enemy tag for ground objects because I'm already using "enemy" tag for my other script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move2d : MonoBehaviour
{
public float playerSpeed; //allows us to be able to change speed in Unity
public Vector2 jumpHeight;
public bool isDead = false;
private Rigidbody2D rb2d;
private Score gm;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
gm = GameObject.FindGameObjectWithTag("gameMaster").GetComponent<Score>();
}
void Update()
{
if (isDead) { return; }
transform.Translate(playerSpeed * Time.deltaTime, 0f, 0f); //makes player run
if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)) //makes player jump
{
GetComponent<Rigidbody2D>().AddForce(jumpHeight, ForceMode2D.Impulse);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("ground")) // this will return true if the collision gameobject has ground tag on it.
{
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
}
}
void OnTriggerEnter2D(Collider2D col)
{
if( col.CompareTag("coin"))
{
Destroy(col.gameObject);
gm.score += 1;
}
}
}
I think you may have asked the question wrong, and that you mean the player will die if he hits to ground or an enemy, rather than both at the same time. An easy fix is doing as Nikola suggested with a minor change:
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("ground") || collision.gameObject.CompareTag("enemy"))
{
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
}
}
OnCollisionEnter will be individually called once for every object that starts touching your player during that frame.
You will need to create a way to track over multiple call to OnCollisionEnter the group of objects touching your player at that point. One way is to create a boolean for each type of tag you are checking:
private bool touchedGround = false;
private bool touchedEnemy = false;
private void LateUpdate() {
touchedGround = false;
touchedEnemy = false;
}
private void OnCollisionStay2D(Collision2D collision) {
if (collision.gameObject.CompareTag("ground")) {
touchedGround = true;
}
if (collision.gameObject.CompareTag("enemy")) {
touchedEnemy = true;
}
if (touchedGround && touchedEnemy) {
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
}
}
At the end of each OnCollisionEnter you are verifying whether or not both tags have been encountered. In LateUpdate you reset the flags to false, so that the next frame you recheck.
As a side note, this solution is not very extensible. You may wish to use layers (LayerMask) and a bit of boolean logic allowing you to compare multiple categories in one operation. But that would be a different question.
Hello why not just do this
private void OnCollisionEnter2D(Collision2D collision)
{
switch (collision.gameobject.tag)
case "tag_here"
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
break;
case "tag_here2"
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
break;
case "tag_here3"
isDead = true;
rb2d.velocity = Vector2.zero;
GameController.Instance.Die();
break;
//Copy paste as long as you need more
}
switch statements are like shorter version of if statements.
You could use the following approach, it's called when Enemy || Gound Tag enters && exits.
private bool enemy, ground;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
enemy = true;
}
if (collision.gameObject.CompareTag("Ground"))
{
ground = true;
}
if(enemy && ground)
{
Debug.Log("Enemy and Ground collision");
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
enemy= false;
}
if (collision.gameObject.CompareTag("Ground"))
{
ground = false;
}
}
My character can jump as much as you press ( W or space) I've read that you can prevent it with Raycast, but I don't understand how to do so. This is my character's code(this is a platformer game) :
private Rigidbody2D myRigidBody2D;
void Start () {
myRigidBody2D = GetComponent<Rigidbody2D>();
}
private void Update()
{
if (Input.GetButtonDown("Jump"))
{
myRigidBody2D.AddForce(new Vector2(0, jumpForce));
}
}
I've read that you can prevent it with Raycast
Yes, you can but you will likely run into problems which can still be fixed.
The best way to do this is to use a boolean variable to check if the character is touching the ground or not. You can set this variable to true or false in the OnCollisionEnter2D and OnCollisionExit2D functions.
Create a tag called "Ground". Change all your ground Gameobjects to this tag then the example below should prevent multiple jumping.
bool isGrounded = true;
private float jumpForce = 2f;
private Rigidbody2D myRigidBody2D;
void Start()
{
myRigidBody2D = GetComponent<Rigidbody2D>();
}
private void Update()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
myRigidBody2D.AddForce(new Vector2(0, jumpForce));
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = true;
}
}
void OnCollisionExit2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = false;
}
}
I'm still learning Unity and right now I'm trying to make my player able to jump. Of course I don't want my player to be able to jump on forever, so my idea was to only enable jumping when the player is in contact with a floor object. This is the code I have so far:
public class PlayerController : NetworkBehaviour
{
public float speed; // Player movement speed
private bool grounded = true; // Contact with floor
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Show a different color for local player to recognise its character
public override void OnStartLocalPlayer()
{
GetComponent<MeshRenderer>().material.color = Color.red;
}
// Detect collision with floor
void OnCollisionEnter(Collision hit)
{
if (hit.gameObject.tag == "Ground")
{
grounded = true;
}
}
// Detect collision exit with floor
void OnCollisionExit(Collision hit)
{
if (hit.gameObject.tag == "Ground")
{
grounded = false;
}
}
void FixedUpdate()
{
// Make sure only local player can control the character
if (!isLocalPlayer)
return;
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
rb.AddForce(movement * speed);
// Detect space key press and allow jump if collision with ground is true
if (Input.GetKey("space") && grounded == true)
{
rb.AddForce(new Vector3(0, 1.0f, 0), ForceMode.Impulse);
}
}
}
But it seems OnCollisionEnter and OnCollisionExit never trigger. So the player is still able to jump whenever he wants. Am I doing something wrong?
Edit: It seems OnCollisionEnter and OnCollisionExit are triggered perfectly fine. It's just the if statements returning false. I have no idea why though.
if (GameObject.Find("Ground") != null) returned true.
Edit 2: Strangely enough both of these return Untagged:
Debug.Log(hit.gameObject.tag);
Debug.Log(hit.collider.tag);
Please give us more information
Please tell me which version of unity you are using?
Have you updated the project to some other latest version of unity?
Also give a screen shot of your 'tag' array.
I'm trying to make a 2D Sidescroller in Unity right now and have some problems regarding the enemy.
In theory I want the enemy be destroyed if the player jumps on it's head(upper collider/trigger) and the player loses health if he hits the other collider/trigger.
Well the player loses health when hitting the enemy from the side but it also destroys the enemy and I don't know why.
It isn't even near the deathCollider.
Upper collider tag(isTrigger) : enemyHit
(this one is placed at the top of the enemy)
Bigger center collider tag(isTrigger) : enemy
(this one is under the other collider and encloses the rest of the body)
Sorry can't post an image :/
My onTrigger method in the playerScript looks like this
void OnTriggerEnter2D(Collider2D other){
if (other.tag == "spike") {
GotHit();
}
if (other.tag == "groundTrap") {
InstandDeath();
}
if (other.tag == "enemy") {
//hitEnemy = other.gameObject;
AudioSource.PlayClipAtPoint(hitSound, transform.position, 10.0f);
GotHit();
}
if (other.tag == "enemyHit") {
rigidbody2D.velocity = Vector2.zero;
rigidbody2D.AddForce (jumpVector, ForceMode2D.Force);
//switch animation to 'jump'
anim.SetInteger("AnimationStatePink", 2);
AudioSource.PlayClipAtPoint(jumpSound, transform.position, 10.0f);
Destroy(other.gameObject);
}
}
And the enemyScript only has it's movement
public float enemySpeed;
private Vector3 movement;
public static bool startEnemyMovement = true;
//set walking limits
public float leftLimit, rightLimit;
//adjust scaling
public float scaleX, scaleY;
public float rotateX, rotateY;
public void Start(){
movement = Vector3.left * enemySpeed;
}
public void Update() {
if (startEnemyMovement == true)
EnemyMovement ();
if (PinkPlayerControler.isPaused == true)
startEnemyMovement = false;
else
startEnemyMovement = true;
}
void EnemyMovement(){
if (this.transform.position.x > rightLimit) {
movement = Vector3.left * enemySpeed;
transform.localScale = new Vector3(-scaleX,scaleY,1);
} else if (this.transform.position.x < leftLimit) {
movement = Vector3.right * enemySpeed;
transform.localScale = new Vector3(scaleX,scaleY,1);
}
transform.Translate(movement);
}
Edit:
the GotHit() method looks like this
void GotHit (){
if(Health.gameLives > 1){
Health.gameLives--;
AudioSource.PlayClipAtPoint(hitSound, transform.position, 10.0f);
}
else {
youLose();
}
}
Thanks in advance :)
Chrizzly
I found the reason...you were right - I have a script that gives the player point for defeating an enemy and there was still some test code that destroyed the complete GameObject -.- Thanks for the help :) –