First of all, please don't get too technical: I've only been studying Unity coding for two months and I'm not a programmer in general! I'm a total beginner! :-)
So, I'm building a game where you control a ball on a treadmill avoiding endless spawning obstacles by shifting left-right and jumping.
I set up a restart function after game over with SceneManager.LoadScene method, it works, but after reloading the jumping function gets compromised: it looks like there's an invisible wall above the player blocking its jumps. And it also looks like it's an "additive" issue: if I die and restart again, the player jumps even less, until it just doesn't jump at all. The other functions seem fine.
Any idea why that happens?
public class PlayerController : MonoBehaviour
//Jump code:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && isOnLane)
{
Jump();
}
}
void Jump()
{
playerRb.AddForce(Vector3.up * forceMult, ForceMode.Impulse);
isOnLane = false;
}
private void OnCollisionEnter(Collision other)
{
//checks if player is touching the ground
if (other.gameObject.CompareTag("Lane"))
{
isOnLane = true;
}
}
Restart code:
public class GameManager : MonoBehaviour
//restart game by clicking Restart Button
public void RestartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
Thanks!
The problem is that additive scene loading is being used when there is only a single scene. It would be good to break these up into 2 scenes:
A scene for GameManager & the Player object
A scene for the level itself which is being reloaded
This way the GameManager is not being reloaded and the player is not being reloaded. Upon reload you need to "reset" variables for the player back to 0 and move him to a starting position.
RestartGame needs some logic to reset the player and his physics.
public void RestartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
playerController.playerRb.velocity = Vector3.zero;
playerController.playerRb.angularVelocity = Vector3.zero;
playerTransform.position = spawnLocation.tranform.position;
}
You probably want to unload the scene, wait for 1 frame, and then load the scene. This way variables in the scene get reset properly.
public void RestartGame()
{
SceneManager.UnloadScene(scene);
playerController.playerRb.isKinematic = true; // Try setting kinematic between scenes
StartCoroutine(RestartLoad());
}
private IEnumerator RestartLoad()
{
yield return new WaitForEndOfFrame();
// Reset the player here
SceneManager.LoadScene(scene);
playerController.playerRb.isKinematic = false; // Player can use physics again
}
Related
I'm trying to "animate" (yes, I'm using the animate capabilities of Unity but I'm only changing the sprite from one still frame to another still frame) my character jumping in Unity. According to my code, everything should be running properly but for some reason, nothing happens in-game when I hit the Jump button (player is jumping but the sprite isn't changing). I'm following along with this tutorial and I've tried several others explaining how to use Unity's Animator and Animation windows but nothing seems to be working to change the sprite.
My code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField]
private float moveForce = 10f;
[SerializeField]
private float jumpForce = 11f;
private float movementX;
private float movementY;
private Rigidbody2D myBody;
private SpriteRenderer sr;
private Animator anim;
private string WALK_ANIMATION = "Walk";
private bool isGrounded = true;
private string GROUND_TAG = "Ground";
private string JUMP_ANIMATION = "Jump";
private void Awake()
{
myBody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
PlayerMoveKeyboard();
AnimatePlayer();
PlayerJump();
}
void FixedUpdate()
{
}
void PlayerMoveKeyboard()
{
movementX = Input.GetAxisRaw("Horizontal");
movementY = Input.GetAxisRaw("Vertical");
transform.position += new Vector3(movementX, 0f, 0f) * Time.deltaTime * moveForce;
}
void AnimatePlayer()
{
if (movementX > 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = false;
}
else if (movementX < 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = true;
}
else
{
anim.SetBool(WALK_ANIMATION, false);
}
}
void PlayerJump()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
isGrounded = false;
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
anim.SetBool(JUMP_ANIMATION, true);
Debug.Log("You should be jumping.");
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.CompareTag(GROUND_TAG))
{
isGrounded = true;
anim.SetBool(JUMP_ANIMATION, false);
}
}
}
My animator states currently:
I'm not sure if this is enough information or if I've included screenshots of everything so please feel free to ask for more input.
I have tried:
Changing the Transitions from (currently) Walking to Jumping to (previously) Idle to Jumping.
Removing the Walking animation entirely from the code
Removing the Transitions from Walking to Idle so that there were only transitions from Walking to Jumping
Checking "Has Exit Time" and unchecking "Has Exit Time"
Extending the length of the Jumping animation
Changing the speed of the jump from 1 to .5 and from 1 to 2
Using an if/else statement in the Animate Player function where the bool of JUMPING_ANIMATION only becomes true if the Y value of the player is higher than the base position when they're standing on the ground
(Trying to; not sure if I coded this correctly because I'm shaky on using triggers instead of bools) Use a trigger instead of a bool for initializing the jump animation
I'm absolutely positive I've just missed something in the tutorial or something else fairly simple and dumb but I cannot for the life of me figure out what it is I'm missing. I've searched the other jump animation issues here on SO, too, and none of them seem to be quite what I'm missing. As it stands, my code is telling me that the Jump parameter is becoming true properly based on the Console Log but nothing about the visual sprite changes for the character.
As I can see in your Animator, you need to set your "StateMachine Default State" to one of your used states (I suppose for your case, the Idle State).
You can do that with a right click on the Entry State.
I am a beginner in Unity and I am currently making a simple game. I have a problem where the instantiated Button is not destroyed.
The process of the script is to instantiate a button and destroy it upon collision exit. But when I move out of the object, the object stays and it is not destroyed. i don't know if there is something wrong with the Destroy code of line.
Here is the script for the collision:
using UnityEngine;
using UnityEngine.UI;
public class InteractButtonPosition : MonoBehaviour
{
public Button UI;
private Button uiUse;
private Vector3 offset = new Vector3(0, 0.5f, 0);
// Start is called before the first frame update
void Start()
{
//uiUse = Instantiate(UI, FindObjectOfType<Canvas>().transform).GetComponent<Button>();
}
// Update is called once per frame
void Update()
{
//uiUse.transform.position = Camera.main.WorldToScreenPoint(this.transform.position + offset);
}
private void OnCollisionEnter(Collision collisionInfo)
{
if(collisionInfo.collider.name == "Player")
{
uiUse = Instantiate(UI, FindObjectOfType<Canvas>().transform).GetComponent<Button>();
uiUse.gameObject.SetActive(true);
uiUse.transform.position = Camera.main.WorldToScreenPoint(this.transform.position + offset);
}
}
private void OnCollisionExit(Collision collisionInfo)
{
if(collisionInfo.collider.name == "Player")
{
Destroy(uiUse);
}
}
}
The code does exactly what you have written, which is destroying uiUse which is a Button component. If you go and inspect your game object in the scene, you will see, that indeed, the button component has been destroyed.
What you want to do is to destroy the GameObject the button component is attached to, like: Destroy(uiUse.gameObject)
I've done it so far that when my player dies the level is restarted and I've written everything in the script but when I try it and the scene is reloaded everything is somehow extremely dark compared to before, although all settings from the camera etc. are the same and I don't know if I'm doing something wrong or if it's a bug
my Script:
public class GameManager : MonoBehaviour
{
public GameObject enemyPrefab;
// Start is called before the first frame update
void Start()
{
GameObject player = GameObject.Find("Player");
SpawnEnemy();
}
// Update is called once per frame
void Update()
{
RestartGame();
}
void RestartGame()
{
if(GameObject.Find("Player").GetComponent<PlayerMovement>().playerHealth <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
You need to bake your lighting inside your scene. Here are instructions on how:
Head to Window > Rendering > Lighting
Press Generate Lighting (Make sure Auto Generate is unselected)
Source
Your code has some trouble. But I'm not going to fix rotation issues. But here your working sample:
public class GameManager : MonoBehaviour {
public GameObject enemyPrefab;
// Start is called before the first frame update
void Start()
{
GameObject player = GameObject.Find("Player");
SpawnEnemy();
}
// Update is called once per frame
void Update()
{
RestartGame();
}
bool navigated = false;
void RestartGame()
{
if (!navigated)
{
if(GameObject.Find("Player").GetComponent<PlayerMovement>().playerHealth <= 0)
{
navigated = true;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
}
}
I wanted to create an collider on my players sword, that if he attacks that he would detect and via an animation event turn off/on an gameobject called (Damage Point) who was an script attached that would subtract the enemys health.
But somewhere it does not detect correctly. I tried to add the OnDrawGizmos function to see my sphere collider but even after making it bigger it does not detect.
The most strange thing about my issue is that im using the same code for my monster chest and my fantasy player, but for the chest it works but for the player it does not.
I created a class called PlayerDamage that is attached on an empty gameobject to the tip of my sword.
{
public class PlayerDamage : MonoBehaviour
public int damageAmount = 2;
public LayerMask enemyLayer;
void Update()
{
Collider[] hits = Physics.OverlapSphere(transform.position, 1.7f, enemyLayer);
if (hits.Length > 0)
{
if (hits[0].gameObject.tag == MyTags.ENEMY_TAG)
{
print("COLLIDED WITH ENEMY");
}
}
}
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, 1.7f);
}
In another script called PlayerScript that is attached directly to the player I have a function called Attack:
void Attack()
{
if (Input.GetKeyDown(KeyCode.K))
{
if (!anim.GetCurrentAnimatorStateInfo(0).IsName(MyTags.ATTACK_ANIMATION) || !anim.GetCurrentAnimatorStateInfo(0).IsName(MyTags.RUN_ATTACK_ANIMATION))
{
anim.SetTrigger(MyTags.ATTACK_TRIGGER);
}
}
}
Also in the PlayerScript class there are two functions called ActivateDamagePoint and DeactivateDamagePoint, these are assigned to animation events for the attack animations.
void ActivateDamagePoint()
{
damagePoint.SetActive(true);
}
void DeactivateDamagePoint()
{
damagePoint.SetActive(false);
}
I double checked that everything is on his layer and that the tags are okay, but that did not solve my problem.
Like I said before for the chest I use the same code and it works, but unfortunately it does not work with my player. I also have the Activate and Deactivate DamagePoint functions in there and these are also called by the animation event for my chest attack animation.
Okay after 1hour of debugging I finnaly found the solution:
It was in the MyTags class (a helper class for all the string variables), the error was:
public static string ENEMY_TAG = "Enemey";
instead of:
public static string ENEMY_TAG = "Enemy";
My goal is to have a platform destroy if it is landed on. Landing on the platform also bounces me up (it is an endless jumper).
The problem is the destroying of the layers isn't consistent. Sometimes the bounce and destroy work as intended, sometimes the platform won't get destroyed (if I bounce on it again it will destroy), other times the platform will destroy and the bounce won't initiate. I am not sure how to make it work every time and why it isn't working properly.
This is the code I am using for the bounce:
public bool platformTouch; //true or false if you are grounded
public Transform groundCheck; //Object which will check if we are grounded
public bool groundedTouch;
public LayerMask ground; //Decide which layers count as grounded.
float groundRadius = .2f; //Radius around ground check object will check if grounded
public LayerMask platform; //Decide which layers count as grounded.
public Rigidbody2D Player;
void FixedUpdate()
{
platformTouch = Physics2D.OverlapCircle(groundCheck.position, groundRadius, platform);
groundedTouch = Physics2D.OverlapCircle(groundCheck.position, groundRadius, ground);
}
Here is the destroy code I am using. If the player's velocity is less than 0 (falling) it should trigger the destroy. There is also a delay in the destroy, so that the bounce triggers.
public Rigidbody2D Player;
void Update()
{
Player = GameObject.FindGameObjectWithTag("Player").GetComponent<Rigidbody2D>();
}
void OnTriggerEnter2D(Collider2D collider)
{
if (collider.gameObject.tag == "Destroyer")
{
if (Player.velocity.y <= 0)
{
StartCoroutine(DestroyPlatforms());
}
}
}
IEnumerator DestroyPlatforms()
{
yield return new WaitForSeconds(.1f); //waits .1 seconds
Destroy(gameObject); //this will work after .1 seconds.
//play sound
}
I recommend that you increase your collider on the platform and use OnTriggerExit with Destroy(this,0.1) , again I can be wrong. But mb it will help
Physics engines are not deterministic, and it's usually not a good practice to have 2 objects checking for the same collision (one object could trigger and not the other in the same frame) I think that's the problem here.
The collision should only be handled by the platform and call a bounce method on the player in the OnTriggerEnter2D right before calling the StartCoroutine(DestroyPlatforms());