I have an object in my game that is like a power object: when my player enters the power it should activate a panel that indicates that the power was grabbed, and past 3 seconds that panel should disappear. At the moment my panel appears when I hit the power, but it doesn't disappear. I am using a Coroutine like this:
using UnityEngine;
using System.Collections;
public class shrink : MonoBehaviour {
public float value = 0.1f; //1 by default in inspector
private bool colided = false;
private float speed;
Manager gameManager;
public GameObject panel;
// Update is called once per frame
void Start(){
speed = 3.4f;
gameManager = GameObject.Find ("GameController").GetComponent<Manager> ();
}
void OnTriggerEnter(Collider c)
{
if (c.gameObject.tag == "Player") {
colided = true;
gameManager.powerUp1 = true;
StartCoroutine(menuOp());
}
}
//This method is executed every frame
void Update(){
if (colided) {
Vector3 temp = transform.localScale;
//We change the values for this saved variable (not actual transform scale)
temp.x -= value * Time.time;
temp.y -= value * Time.time;
if (temp.x > 0) {
speed += 0.02f;
transform.Rotate (0f, 0f, Time.deltaTime * 90 * speed);
transform.localScale = temp;
} else {
Object.Destroy (this.gameObject);
}
}
}
IEnumerator menuOp(){
panel.SetActive (true);
yield return new WaitForSeconds (3f);
panel.SetActive (false);
}
}
Ps: what is inside the update is independent from what i need to do, so i think it doesn't interfer with my needs.
I think you might be destroying the GameObject before the WaitForSeconds(3f) has ended, which will prevent panel.SetActive(false) from executing.
In your Update-method's "else statement to if (temp.x > 0)" you're destroying the GameObject that tries to show/hide the panel.
If you absolutely need to Destroy this gameobject at that time you should break out your IEnumerator to another script and call it from this (shrink.cs) script.
Destroying the object before 3 seconds might be the issue. You can check by putting two logs:
1- After "Object.Destroy" line
2- After "WaitForSeconds"
maybe you should check if your gameobject(shrink's) is unactive or destroyed.
coroutine does not work with unactive gameobject.
Related
I am making my first foray into AI, specifically AI that will follow the player.
I am using the A* Path finding project scripts, but used the Brackeys tutorial for the code
https://www.youtube.com/watch?v=jvtFUfJ6CP8
Source in case needed.
Here's the code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class EnemyAI : MonoBehaviour
{
public Transform Target;
public float speed = 200f;
public float NextWayPointDistance = 3f;
Path path;
int currentWaypoint = 0;
bool ReachedEndOfpath = false;
Seeker seeker;
Rigidbody2D rb;
public Transform EnemyGraphics;
// Start is called before the first frame update
void Start()
{
seeker = GetComponent<Seeker>();
rb = GetComponent<Rigidbody2D>();
InvokeRepeating("UpdatePath", 0f, 1f);
}
void UpdatePath()
{
if (seeker.IsDone())
seeker.StartPath(rb.position, Target.position, OnPathComplete);
}
void OnPathComplete(Path p)
{
if (!p.error)
{
path = p;
currentWaypoint = 0;
}
}
// Update is called once per frame
void fixedUpdate()
{
if(path == null)
return;
if(currentWaypoint >= path.vectorPath.Count)
{
ReachedEndOfpath = true;
return;
}
else
{
ReachedEndOfpath = false;
}
Vector2 Direction = ((Vector2)path.vectorPath[currentWaypoint] - rb.position).normalized;
Vector2 Force = Direction * speed * Time.fixedDeltaTime;
rb.AddForce(Force);
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if(distance < NextWayPointDistance)
{
currentWaypoint++;
}
if(rb.velocity.x >= 0.01f)
{
EnemyGraphics.localScale = new Vector3(-1f, 1f, 1f);
}else if(rb.velocity.x <= 0.01f)
{
EnemyGraphics.localScale = new Vector3(1f, 1f, 1f);
}
}
}
How I tried to fix the issue:
I thought it could've been a problem with the speed so I increased it to 10000000, and still nothing
Next I thought it was a problem with the Rigidbody2d so I check there, found the gravity scale was set at 0, so I increased it to 1. It made my enemy fall to the ground but still no movement.
I thought it could've been a problem with the mass and drag, so I set Linear drag and Angular drag to 0, and also set mass to 1. Still nothing.
I set the body type to kinematic, pressed run, nothing. Set the body type to static, pressed run, nothing. Set the body type to Dynamic, pressed run, still nothing.
I tried to make a new target for the enemy to follow, dragged the empty game object i nto the target, pressed run and still didn't move.
I am at a loss on how to fix this.
Please help?
Looks like maybe a typo? You have:
// Update is called once per frame
void fixedUpdate()
{
but the method is called FixedUpdate(), with a big F in front. You have fixedUpdate, which is NOT the same.
(I'm a beginner so please have patience with me).
How it needs to happend:
I have a player that can jump and hit a tile, on collision the tile with move a fixed distance up and come back down with a smooth transition.
What I have so far:
I am detecting the collision now there's only the matter of moving the tile up and down.
The catch
The tiles are suspended in air so basically they either don't have a RigidBody2D or they have one with gravity scale 0
What I've tried
Basically I've tried 2 solutions:
(I'm not limited to these 2, I want to implement the solution that is correct so I am open to other ideas)
I was thinking to simply take the current position, calculate another vector with another position, and lerp to the new position and then lerp back to the initial one.
Vector2 initialPosition = transform.position;
Vector2 targetPosition = new Vector2(initialPosition.x + 4f, initialPosition.y + 4f);
print("Initial position: " + initialPosition);
print("Target position: " + targetPosition);
Vector2.Lerp(initialPosition, targetPosition, 1f);
I tried adding a rigidbody with scale 0
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground")
{
Jumping = false;
anim.SetInteger("State", 0);
}
// print("Colision layer: " + collision.collider.gameObject.layer);
if (collision.collider.gameObject.layer == Mathf.Log(layerMask.value, 2))
{
GameObject Tile = collision.gameObject;
Rigidbody2D rigidBody = Tile.GetComponent<Rigidbody2D>();
rigidBody.gravityScale = 1;
rigidBody.AddForce(new Vector2(0, 10f), ForceMode2D.Force);
StartCoroutine(MoveTileWithForce(rigidBody));
}
}
IEnumerator MoveTileWithForce(Rigidbody2D TileRigidBody)
{
yield return new WaitForSeconds(1);
TileRigidBody.AddForce(new Vector2(0, -5f), ForceMode2D.Force);
TileRigidBody.gravityScale = 0;
print("END MY COROUTINE, gravityScale: " + TileRigidBody.gravityScale);
}
I think the best way you can achieve this is in this simple way:
1
Make sure you have a box collider for your character and a Rigidbody and the same for the block you wanna move. For both of the colliders set on trigger. If you want to have another collider for the player in order to make him touch the ground or hit enemies you can add another box collider, but make sure you have one on trigger on his head
2
Add a script to the block you wanna move and also a tag to the player, for example "player"
3
Inside of this new script check if the block is triggering the player:
void OnTriggerEnter2D(Collision other)
{
if(other.compareTag("player"))
{ StartCoroutine(MovingBlock(0.5f, transform.position, upperPosition));}
|
It will enter inside the if when the player touches the block, it will start a coroutine that I will now write so you will understand the 0.5f and the other variables
IEnumerator MovingBlock(float time, Vector2 startpos, Vector2 endpos)
{
float elapsedTime = 0;
while (elapsedTime < time)
{
transform.position= Vector2.Lerp(startpos, endpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
elapsedTime = 0f;
while (elapsedTime < time)
{
transform.position= Vector2.Lerp(endpos, startpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
}
Basically, the Coroutine will move the object from startpos to endpos and then back again in the amount of time that you decide (in this case 0.5 seconds). I have used the Vector2 variable upperposition and it's just the height you want to reach with the platform.
If you want, you can also add inside the coroutine some yield return new WaitForSeconds(timeToWait) and make the platform wait in a certain position the amount of seconds you want (timeToWait)
Declare Vector2 initialPosition at the beginning of your class.
On Start() of the tile object you should get the initialPosition = transform.position.
And in the OnCollisionEnter2D(Collision2D collision) you could start a coroutine to bring the tile back down.
So for example:
Player Script:
public class Player : MonoBehaviour
{
public Rigidbody2D rb;
//Initialize the rigidbody on the editor.
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.AddForce(new Vector2(0f, 1000f));
}
}
}
And the tile script:
public class MyTile : MonoBehaviour
{
Vector2 initialPosition;
float speed = 2f; //the speed the tile will go down.
private void Start()
{
initialPosition = transform.position;
}
private void OnCollisionEnter2D(Collision2D collision)
{
//The amount you want to go up on the Y component, for this example I used 2.
transform.position = new Vector2(transform.position.x, transform.position.y + 2f);
StartCoroutine(MoveTileDown());
}
IEnumerator MoveTileDown()
{
while (transform.position.y > initialPosition.y)
{
transform.position = Vector2.Lerp(transform.position, initialPosition, Time.deltaTime * speed);
yield return null; //Make it run every frame, just like Update()
}
StopCoroutine(MoveTileDown());
}
}
There are a lot of ways you can achieve the same result, this is just one of them.
Earlier I was facing problem regarding the unity camera problem it always stuck on 0,0,0.08 and also find a solution so I first create an empty gameobject and then drag the camera in that empty gameobject but after doing this the scripts which I applied to the gameobject is working fine but the script which I place in camera is not working at all
Camera Script
public float MovementAmplitude = 0.1f;
public float MovementFrequency = 2.25f;
void Update()
{
transform.position = new Vector3(
transform.position.x,
Mathf.Cos(transform.position.z * MovementFrequency) * MovementAmplitude,
transform.position.z
);
}
Player Script
public float speed = 4.5f;
public float JumpingForcec = 450f;
void Update()
{
transform.position += speed * Vector3.forward * Time.deltaTime;
if (Input.GetKeyDown("space"))
{
Debug.Log("SPace is pressed");
Debug.Log(GetComponent<Rigidbody>());
GetComponent<Rigidbody>().AddForce(Vector3.up * JumpingForcec);
}
}
First of all when dealing with a Rigidbody (or the Physics in general) you shouldn't set a position directly through the Transform component but rather use Rigidbody.position or in your case for a smooth movement even rather Rigidbody.MovePosition, both in FixedUpdate.
In general anything related to the Physics (so also everything using Rigidbody) should be done in FixedUpdate while the check for GetKeyDown has to be done in Update.
PlayerScript
public class PlayerScript : MonoBehaviour
{
public float speed = 4.5f;
public float JumpingForcec = 450f;
// If possible reference this in the Inspector already
[SerializeField] private Rigidbody rigidBody;
private bool jumpWasPressed;
private void Awake()
{
if (!rigidBody) rigidBody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
rigidBody.MovePosition(transform.position + speed * Vector3.forward * Time.deltaTime);
if (!jumpWasPressed) return;
Debug.Log("SPace was pressed");
rigidBody.AddForce(Vector3.up * JumpingForcec);
jumpWasPressed = false;
}
private void Update()
{
// Note that currently you can multijump .. later you will want to add
// an additional check if you already jump again
if (Input.GetKeyDown(KeyCode.Space)) jumpWasPressed = true;
}
}
Make sure that Is Kinematic is disabled in the Rigidbody component! Otherwise AddForce is not processed.
If isKinematic is enabled, Forces, collisions or joints will not affect the rigidbody anymore.
The camera movement I would move to LateUpdate in order to make sure it is the last thing calculated after the other Update calls have finished. Especially after all user input has been processed (in your case maybe not that relevant since movement is processed in FixedUpdate but in general).
Second problem: Here you are not taking the changed Y position by jumping into account so rather add the "wobbling" effect to the player's transform.position.y and rather use the localPosition for the Camera:
public class CameraScript : MonoBehaviour
{
public float MovementAmplitude = 0.1f;
public float MovementFrequency = 2.25f;
// reference the player object here
public Transform playerTransform;
private float originalLocalPosY;
private void Start()
{
if(!playerTransform) playerTransform = transform.parent;
originalLocalPosY = transform.localPosition.y;
}
private void LateUpdate()
{
transform.localPosition = Vector3.up * (originalLocalPosY + Mathf.Cos(playerTransform.position.z * MovementFrequency) * MovementAmplitude);
}
}
Maybe you want to disable the wobbling effect during a jump later, though ;)
Try to put all the update stuff in the same method, it should work both (theorically, not tested) so you have to fix your code in order to get what you want:
void Update() {
// Camera update
transform.position = new Vector3(
transform.position.x,
Mathf.Cos(transform.position.z * MovementFrequency) * MovementAmplitude,
transform.position.z
);
// Player update
transform.position += speed * Vector3.forward * Time.deltaTime;
if (Input.GetKeyDown("space"))
{
Debug.Log("SPace is pressed");
Debug.Log(GetComponent<Rigidbody>());
GetComponent<Rigidbody>().AddForce(Vector3.up * JumpingForcec);
}
}
Hope this helps you, cheers!
I'm making a non random generated runner game and I'm trying to code the boss entry and fight. So the game consist of an astronaut (player) that stays on screen all the time (so him, background and camera never move, well the player can move but it's clamped.
All the hazards come towards the player and he has to avoid them or defeat them until the last hazard which is the boss. The boss is at the end of the line and goes along the z-Axis as well towards the player and I want it to stop when it collides with a Quad, so the boss is static and they can fight.
After that I want the boss to move up and down and shoot the player with Lerp functions.
The code looks like this:
public class BossController : MonoBehaviour {
public float speed;
public float health;
public Animator anim;
public Transform startMarker;
public Transform endMarker;
private Rigidbody rb;
private HUDController hud;
private bool startIntro = false;
private float startTime;
private float journeyLength;
void Start () {
startTime = Time.time;
journeyLength = Vector3.Distance (startMarker.position, endMarker.position);
anim = GetComponent<Animator> ();
rb = GetComponent<Rigidbody> ();
rb.velocity = transform.forward * -speed;
}
void Update(){
//transform.position += transform.forward * -speed * Time.deltaTime;
if (startIntro) {
rb.velocity = new Vector3(0,0,0);
Fight ();
}
}
void Fight(){
float distCovered = (Time.time - startTime) * speed;
float fracJourney = distCovered / journeyLength;
while(true)
{
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fracJourney);
transform.position = Vector3.Lerp(endMarker.position, startMarker.position, fracJourney);
}
}
void OnTriggerEnter(Collider other){
if (other.gameObject.CompareTag ("bossEntry")) {
startIntro = true;
//anim.SetTrigger ("quad");
}
}
}
I tried first with the rigidbody and when it collides, the rigidbody's velocity is 0 and that works fine AS LONG AS the Fight() method is commented. So maybe it's because of the lerp functions?
When it;s not commented when the boss hits the collider everything freezes and I cannot use Unity anymore and I have to restart the programm.
Please help!
Edit: I removed the while(true) loop and now the boss falls straight down when it collides with the quad instead of lerping. The Boss is under an empty object and both start and end markers are under the empty object as well. Every component is attached to the empty object (parent).
try and track (print) the values of your veriables.
I'd guess you have a divisor of 0 here -
float fracJourney = distCovered / journeyLength;
if not, its most likely you have an endless loop, or a null pointer which is vital.
Sorry, I just noticed this now.
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fracJourney);
transform.position = Vector3.Lerp(endMarker.position, startMarker.position, fracJourney);
The second Lerp overrides the value of first one immediately. If you want something like a ping pong effect, make a flag to determine the current state you want.
bool toEndMarker = true;
void Fight() {
//Some code here
if(toEndMarker)
{
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fracJourney);
if(//DETERMINE if transform.position is already at end marker)
{
toEndMarker = false;
}
}
else
{
transform.position = Vector3.Lerp(endMarker.position, startMarker.position, fracJourney);
if(//DETERMINE if transform.position is already at start marker)
{
toEndMarker = true;
}
}
}
I'm using this:
void Update(){
if(leftJoystick){
rb.AddRelativeForce(0, 0, 400f, ForceMode.Impulse);
}
}
to move my character forward in the air (using jetpack). When I let go of the joystick my character stops immediately. What I want is that the character keeps moving forward after joystick is released and with time slow down and gradually stop. It looks stupid that my character stops like it hit a wall. What kind of force/velocity/transform I should use so that physics slow down my character after I let go of the joystick?
My rigidbody settings:
Mass: 100
Drag: 0
Angular drag: 0.05
Uses gravity
Not kinematic
Interpolate: interpolate
Collision detection: Discrete
Freeze position: none
Freeze rotation: all
Character also has a capsule collider that is NOT trigger and has no physics material.
EDIT: here is the full code so you can see if there is any conflicts in my code
using UnityEngine;
using System.Collections;
using UnityStandardAssets.CrossPlatformInput;
public class JetpackController : MonoBehaviour{
Rigidbody rb;
// controller axes
[SerializeField]
public string speedAxis = "Vertical";
public string rotateAxis = "HorizontalRotate";
[SerializeField]
public float forwardSpeed;
public float upSpeed;
public float rotateSpeed;
public float rotationIgnoreZone;
public float MAX_HEIGHT;
float speedInput;
float rotateInput;
public bool leftJoystick;
public bool rightJoystick;
ParticleSystem jetpackFire1;
Vector3 originalGravity;
// access Joystick script in Standard assets to detect when right joystick is held down
Joystick joystick_left;
Joystick joystick_right;
void Start () {
if (GetComponent<Rigidbody> ()) {
rb = GetComponent<Rigidbody> ();
} else {
Debug.LogError ("Player needs to have a rigidbody.");
}
// get Joysticks
if (GameObject.Find ("MobileJoystick_left").GetComponent<Joystick> ()) {
joystick_left = GameObject.Find ("MobileJoystick_left").GetComponent<Joystick> ();
} else {
Debug.LogError ("Either gameobject with name 'MobileJoystick_right' does not exist in the scene or it does not have script called Joystick.cs attached to it.");
}
if (GameObject.Find ("MobileJoystick_right").GetComponent<Joystick> ()) {
joystick_right = GameObject.Find ("MobileJoystick_right").GetComponent<Joystick> ();
} else {
Debug.LogError ("Either gameobject with name 'MobileJoystick_right' does not exist in the scene or it does not have script called Joystick.cs attached to it.");
}
jetpackFire1 = GameObject.Find("JetpackFire1").GetComponent<ParticleSystem> ();
jetpackFire1.playbackSpeed = 5.0f;
originalGravity = Physics.gravity;
}
void FixedUpdate () {
GetInput ();
// move forward and backward according to left joysticks input
if(leftJoystick){
// left joystick held down
rb.AddRelativeForce(0, 0, forwardSpeed*speedInput, ForceMode.Impulse);
}
// if right joystick is used
if (rightJoystick) {
jetpackFire1.Play ();
if (transform.position.y < MAX_HEIGHT) {
// allow going up
//rb.AddRelativeForce (0, upSpeed, 0, ForceMode.Impulse);
rb.AddForce(transform.forward * forwardSpeed);
} else if (transform.position.y >= MAX_HEIGHT) {
// prevent player to go any further up and also falling
Physics.gravity = Vector3.zero; // no gravity to prevent player from falling
rb.velocity = Vector3.zero;
}
// rotate
// always keep rotating (player can go past look at joystick dir)
if (rotateInput >= rotationIgnoreZone || rotateInput <= -rotationIgnoreZone) {
transform.Rotate(Vector3.up * rotateInput, Time.deltaTime * rotateSpeed);
}
} else {
jetpackFire1.Stop ();
// if right joystick is released
Physics.gravity = originalGravity; // normal gravity -> player can fall
}
}
public void GetInput(){
speedInput = CrossPlatformInputManager.GetAxis (speedAxis);
rotateInput = CrossPlatformInputManager.GetAxis (rotateAxis);
// access Joystick.cs script to grab wether right joystick is pressed or not
leftJoystick = joystick_left.leftJoystickPressed;
rightJoystick = joystick_right.rightJoystickPressed;
}
}
Can't comment yet
Like J.Small said, you could multiply the velocity with a number around 0.95 to slowly slow the rigidbody down. If you put the multiplication in the Update method I suggest you to multiply the ~0.95 number with Time.deltaTime, so the velocity decrease will be proportional to the passed time.
void Update(){
if(leftJoystick){
rb.AddRelativeForce(0, 0, 400f, ForceMode.Impulse);
}
if(noJoystick){
rb.velocity = rb.velocity * 0.95 * Time.deltaTime;
//Or: rb.velocity.x
}
}
You could also apply a small amount of force instead of multiplying the velocity if you want to slow down the rigidbody in a more configurable way (the amount should be based on Time.deltaTime if you put it into Update - which gets called once a frame), but the method above is easier.
I'm not a pro at coding, so I'm not sure if this'll work for everyone, but I tried something like this to fix a similar issue.
void FixedUpdate()
{
playerRigidbody.AddRelativeForce(Vector2.up * forwardInput * playerSpeed * Time.fixedDeltaTime);
if (forwardInput <= 0)
{
playerRigidbody.velocity = playerRigidbody.velocity / 1.005f; // <-- This will gradually slow down the player when they're idle.
}
}
You could do a simple linear slow down.
void Update(){
if(leftJoystick){
rb.AddRelativeForce(0, 0, 400f, ForceMode.Impulse);
}
if(noJoystick){
rb.velocity = rb.velocity * 0.9;
}
}
Note that the update function gets called on frame update, so I would suggest putting this in a FixedUpdate() function that way the slow rate won't depend on the frame rate.