using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move : MonoBehaviour {
enum MoveProperties
{
DirectionStart,
DirectionEnd
};
public float spinSpeed = 2.0f;
private bool rotate = false;
private bool exited = false;
private List<GameObject> prefabs;
private void Start()
{
InstantiateObjects gos = GetComponent<InstantiateObjects>();
prefabs = new List<GameObject>();
prefabs = gos.PrefabsList();
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
Debug.Log("Player entered the hole");
rotate = true;
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
Debug.Log("Player exited the hole");
rotate = false;
exited = true;
}
}
void Rotate()
{
if (rotate)
{
transform.Rotate(Vector3.up, spinSpeed * Time.deltaTime);
spinSpeed += 1f;
}
if (rotate == false && exited == true)
{
transform.Rotate(Vector3.up, spinSpeed * Time.deltaTime);
if (spinSpeed > 0.0f)
spinSpeed -= 1f;
}
}
private void Update()
{
Rotate();
}
}
There are two triggers here:
OnTriggerEnter and OnTriggerExit.
But this triggers will work if the character first went out and then went in again. Or went out. But i need to detect while the character is already inside.
Without first going out and getting in again.
The situation is that the character is first in one place(hole inside a object) and then i change the character position to another object with hole. After changing the position i need to detect somehow that the character is inside the second hole.
But this triggers will work if the character first went out and then
went in again. Or went out. But i need to detect while the character
is already inside. Without first going out and getting in again.
You are looking for the OnTriggerStay function. As long as both Colliders are inside one another, OnTriggerStay will always be called every frame.
void OnTriggerStay(Collider other)
{
}
There is also OnCollisionStay if you decide to use Collision instead of a trigger.
void OnCollisionStay(Collision collisionInfo)
{
}
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
I had my code working perfectly the character was moving side to side and jumping but I tried to get him to stop jumping twice and he just stopped moving entirely. Can you help me figure out what I did wrong? I have looked this question up but yet to find an answer that makes sense to me (I'm relatively new) so if some can work through this with me I would appreciate it greatly so I can get back to work learning.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
bool jumpKeyWasPressed;
float horizontalInput;
Rigidbody rigidBodyComponent;
bool isGrounded;
// Start is called before the first frame update
void Start()
{
rigidBodyComponent = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
//Space key input
if (Input.GetKeyDown(KeyCode.Space))
{
jumpKeyWasPressed = true;
}
horizontalInput = Input.GetAxis("Horizontal");
}
//main problem I think
void FixedUpdate ()
{
if (!isGrounded)
{
return;
}
if (jumpKeyWasPressed)
{
rigidBodyComponent.AddForce(Vector3.up * 5, ForceMode.VelocityChange);
jumpKeyWasPressed = false;
}
rigidBodyComponent.velocity = new Vector3(horizontalInput,rigidBodyComponent.velocity.y, 0);
}
void OnCollisionEnter(Collision collision)
{
isGrounded = true;
}
void OnCollisionExit(Collision collision)
{
isGrounded = false;
}
}
If isGrounded is false, you return right out of the function. The last line is also skipped this way. To fix this, you could simply check if isGrounded is true, and only execute the jumping code then (forget return).
if (isGrounded) {
if (jumpKeyWasPressed) {
// ...
}
}
I'm new at Unity and C#. I started this 2D game few weeks ago.
So basically what I want to do is when the player is hit by "FrostBullet", it should deal the damage and slow that player over time. In my script the player is hit by FrostBullet, it deals the damage and the slow is constant, not over time.
I want it to be like, when the player is hit by FrostBullet, the player should be slowed for 2 sec and than his movement speed is back to normal.
So this is how my code look like:
public class FrostHit : MonoBehaviour
{
float maxS = 40f;
float slowSpeed = 20f;
public Rigidbody2D rb;
int damage = -5;
int timeOfReducedSpeed = 2;
bool isHit = false;
void Start()
{
rb.velocity = transform.right * slowSpeed;
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Player_Njinja")
{
HealthNjinja healthNjinja = other.gameObject.GetComponent<HealthNjinja>();
healthNjinja.ModifyHealth(damage);
PlayerMovementNjinja reduceMoveSpeed = other.gameObject.GetComponent<PlayerMovementNjinja>();
reduceMoveSpeed.runSpeed = slowSpeed;
Destroy(gameObject);
}
PlayerMovementKnight maxSpeed = other.gameObject.GetComponent<PlayerMovementKnight>();
maxSpeed.runSpeed = maxS;
HealthKnight healthKnight = other.gameObject.GetComponent<HealthKnight>();
if (other.gameObject.tag == "Player_Knight")
{
isHit = true;
healthKnight.ModifyHealth(damage);
StartCoroutine(speedTime());
Destroy(gameObject);
}
IEnumerator speedTime()
{
while (isHit == true)
{
slowPlayer();
yield return new WaitForSeconds(timeOfReducedSpeed);
revertSpeed();
}
}
void slowPlayer()
{
maxSpeed.runSpeed = slowSpeed;
}
void revertSpeed()
{
maxSpeed.runSpeed = maxS;
}
}
}
Also my PlayerMovement code:
public class PlayerMovementKnight : MonoBehaviour
{
public CharacterController2D controller;
public Animator animator;
public float runSpeed = 40f;
float horizontalMove = 0f;
bool jump = false;
bool crouch = false;
public WeaponKnight Bullet;
public bool grounded;
void Update()
{
horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
animator.SetFloat("Speed", Mathf.Abs(horizontalMove));
if (Input.GetButtonDown("Jump"))
{
jump = true;
animator.SetBool("IsJumping", true);
}
if (Input.GetButtonDown("Crouch"))
{
crouch = true;
}
else if (Input.GetButtonUp("Crouch"))
{
crouch = false;
}
if (grounded && GetComponent<Bullet>().knockBack == false)
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, 0);
}
}
public void OnLanding()
{
animator.SetBool("IsJumping", false);
}
public void OnCrouching(bool isCrouching)
{
animator.SetBool("IsCrouching", isCrouching);
}
void FixedUpdate()
{
controller.Move(horizontalMove * Time.fixedDeltaTime, crouch, jump);
jump = false;
}
}
The issue seems to be that your bool isHit, that is set to true before calling the Coroutine, is never reset to false.
So the loop:
while (isHit == true)
{
slowPlayer();
yield return new WaitForSeconds(timeOfReducedSpeed);
revertSpeed();
}
is really an infinite loop.
You might want to reset the bool to false somewhere.
Edit:
Following to comments, I see now that your script is attached to the bullet GameObject. After starting the Coroutine, and slowing down the player, you Destroy(gameObject);
After that, the script gets destroyed too, and the Coroutine is therefore stopped...and nothing remains to restore player’s speed back to normal.
You should move the Coroutine to the player’s script, and start it when needed. This way, it would still exist after you destroy the bullet.
Your bool isHit is never set to false. Resulting in the Player being slowed and then being reset forever after he get's hit once.
As well as creating isHit in the first place isn't needed. Why would you need to check that the Player has been hit when you enter the IEnumerator only when the Player_Knight has been hit anyway.
Code Example:
public class FrostHit : MonoBehaviour
{
float maxS = 40f;
float slowSpeed = 20f;
public Rigidbody2D rb;
int damage = -5;
int timeOfReducedSpeed = 2;
void Start()
{
rb.velocity = transform.right * slowSpeed;
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Player_Njinja")
{
HealthNjinja healthNjinja = other.gameObject.GetComponent<HealthNjinja>();
healthNjinja.ModifyHealth(damage);
PlayerMovementNjinja mSpeedNinja = other.gameObject.GetComponent<PlayerMovementNjinja>();
StartCoroutine(speedTimeNinja());
}
else if (other.gameObject.tag == "Player_Knight")
{
HealthKnight healthKnight = other.gameObject.GetComponent<HealthKnight>();
healthKnight.ModifyHealth(damage);
PlayerMovementKnight mSpeedKnight = other.gameObject.GetComponent<PlayerMovementKnight>();
StartCoroutine(speedTimeKnight());
}
// Destroy Bullet
Destroy(gameObject);
}
IEnumerator speedTimeKnight()
{
mSpeedKnight.runSpeed = slowSpeed;
yield return new WaitForSeconds(timeOfReducedSpeed);
mSpeedKnight.runSpeed = maxS;
}
IEnumerator speedTimeNinja()
{
mSpeedNinja.runSpeed = slowSpeed;
yield return new WaitForSeconds(timeOfReducedSpeed);
mSpeedNinja.runSpeed = maxS;
}
}
Things I've changed and the reason:
Removed bool as well as while loop, didn't solve any real purpose
Seperated IEnumerator and 2 Functions from inside OnTriggerEnter2D()
Added Destroy(gameObject); to the end, because the bullets always gets destroyed anyway
Removed maxSpeed.runSpeed = maxS;, because the speed get's set/reset already anyway. There is no reason to reset the speed after we collided with the bullet.
Put GetComponent for Player_Knight into the else if, there is no reason to execute the GetComponent even if the Player_Njinja gets hit.
Removed reduceMoveSpeed.runSpeed = slowSpeed; the Player_Njinja only gets slowed and never regains his speed. Instead use the IEnumerator again
Added another IEnumerator so that both Player_Njinja and Player_Knight get slowed and unslowed correctly (it would now be even possible to create different slow times depending on which player gets hit)
Removed functions, it's not really worth it to write a function for one line of code. Especially when you can just set it in IEnumerator without needing more lines of code
I am developing a platform game, where a ball should be able to move left and right and to jump. I could make the character jump or move successfully, but when i tried to check if it was on the ground, by creating an element as child of the character, with a trigger collider 2D, and wrote the code using a variable that was supposed to be true when the player was touching the ground, and false when it wasn't, it just did not activate.
Here is the code for the main movement script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Grounded : MonoBehaviour
{
GameObject Player;
// Start is called before the first frame update
void Start()
{
Player = gameObject.transform.parent.gameObject;
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (GetComponent<Collider2D>().tag == "Ground")
{
Player.GetComponent<Move2D>().isGrounded = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (GetComponent<Collider2D>().tag == "Ground")
{
Player.GetComponent<Move2D>().isGrounded = true;
}
}
}
And this is the Grounded script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move2D : MonoBehaviour
{
public float moveSpeed = 5f;
public bool isGrounded = false;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Jump();
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
transform.position += movement * Time.deltaTime * moveSpeed;
}
void Jump()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector3(0f, 5f), ForceMode2D.Impulse);
}
}
}
Any help or information is really appreciated.
In
GetComponent<Collider2D>().tag == "Ground"
you are checking the tag of this GameObject's (child of Player) collider itself!
You probably rather wanted to check the tag of the thing you collided with.
Also avoid repeated GetComponent calls .. rather do it only once.
(Thanks #Jichael) You should also rather use CompareTag instead of using tag == "XY". It is more efficient and also actually checks if the given compare string exists as tag. If not an error is thrown while using == simply returns false which makes it hard to find evtl. typos.
// Would be better even to already reference this via Inspector
[SerializeField] private Move2D Player;
private void Awake()
{
if(!Player) Player = GetComponentInParent<Move2D>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Ground"))
{
Player.isGrounded = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.CompareTag("Ground"))
{
Player.isGrounded = true;
}
}
The same also in Move2D
[SerializeField] private Rigidbody2D rigidbody;
private void Awake()
{
if(!rigidbody) rigidbody = GetComppnent<Rigidbody2D>();
}
void Jump()
{
// This is very small but it is slightly cheaper to check the
// isGrounded value so this order is minimal faster if isGrounded is false
if (isGrounded && Input.GetButtonDown("Jump"))
{
rigidbody.AddForce(new Vector3(0f, 5f), ForceMode2D.Impulse);
}
}
Also note: Whenever a Rigidbody is involved you should not control the position via the Transform component but rather use Rigidbody2D.MovePosition to keep the Physics intact!
This has to be done however in FixedUpdate so your code would become something like
private Vector3 movement;
void Update()
{
Jump();
movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
}
private void FixedUpdate ()
{
rigidbody.MovePosition(rigidbody.position += movement * Time.deltaTime * moveSpeed);
}
void Jump()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
rigidbody.AddForce(new Vector3(0f, 5f), ForceMode2D.Impulse);
}
}
Whether both objects require a Rigidbody or only the player depends on the ground objects (afaik):
ground objects are static → one (player) Rigidbody is enough
ground objects move → both need a rigidBody (the ground e.g. a kinematic)
Could you check both ball and ground has a rigidbody? which is required to trigger a trigger.
*Note: Trigger events are only sent if one of the Colliders also has a Rigidbody attached. *
Also could you change your code
private void OnTriggerEnter2D(Collider2D collision)
{
if (GetComponent<Collider2D>().tag == "Ground")
{
Player.GetComponent<Move2D>().isGrounded = true;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Ground")
{
Player.GetComponent<Move2D>().isGrounded = true;
}
}
Also I also recommend to use "layer" instead of using tag
I got a Cube with a box collider and a trigger on it. When the player stands on it, it falls down.
I want the platform to destroy itself after colliding with something and before that, instantiate itself at its starting position.
So my code looks this:
void OnTriggerEnter(Collider col)
{
if (col.CompareTag("Player"))
isFalling = true;
}
void OnCollisionEnter(Collision col)
{
if (!col.gameObject.CompareTag("Player"))
{
Instantiate(gameObject, startPosition, startRotation);
Destroy(gameObject);
}
}
void Update()
{
if (isFalling)
{
fallingSpeed += Time.deltaTime / 20;
transform.position = new Vector3(transform.position.x, transform.position.y - fallingSpeed, transform.position.z);
}
}
Well when my platform crashes down, it just passes through the ground. There is even no collision detected.
Does someone got a hint for me?
So I just got my mistake.
The platform got no rigidbody attached. Therefore it was not able to collide with the ground.
This is my new code:
private void Start()
{
data.PlatformRigid.useGravity = false; // Disable the gravity to make it stay in the air
}
private void OnTriggerEnter(Collider col)
{
if (!data.Activated) // just do this 1 time
{
if (col.CompareTag("Player")) // just start executing the following code if the colliding object is the player
{
data.Activated = true; // don't execute this code a second time
data.PlatformRigid.useGravity = true; // start falling
}
}
}
private void OnCollisionEnter(Collision col)
{
if (!col.gameObject.CompareTag("Player"))
{
Instantiate(gameObject, data.StartPosition, data.StartRotation); // Create itself at default
Destroy(gameObject); // Destroy itself
}
}
I don't need to calculate the fallspeed in the update anymore. I just disable the gravity and enable it, when the player hits the platform.
If your collider is set to trigger, it won't fire the OnCollisionEnter event. Instead, put your code in the OnTriggerEnter like this:
void OnTriggerEnter(Collider col)
{
if (col.CompareTag("Player")) {
isFalling = true;
}
else
{
Instantiate(gameObject, startPosition, startRotation);
Destroy(gameObject);
}
}