using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rolling : MonoBehaviour
{
public NaviManager naviManager;
public float speed;
public float timeToStopRolling;
public string onCollisionState;
private Rigidbody rb;
private bool isTouching = false;
private bool stopRollingCoroutine = false;
private bool stopRolling = false;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
if (naviManager.crateOpenOnce)
{
if (isTouching)
{
if(stopRollingCoroutine == false)
{
StartCoroutine(WaitBeforeStopRolling());
stopRollingCoroutine = true;
}
if (stopRolling == false)
{
rb.AddForceAtPosition(Vector3.right * speed * Time.deltaTime, transform.position);
}
}
else
{
rb.velocity = rb.velocity * 0.95f * Time.deltaTime;
}
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Crate_0_0")
{
onCollisionState = "Touching !!!";
isTouching = true;
}
if(collision.gameObject.name == "Stair")
{
speed = 100;
}
}
IEnumerator WaitBeforeStopRolling()
{
yield return new WaitForSeconds(timeToStopRolling);
stopRolling = true;
}
}
I want to stop the transform from rolling at once or slowly but the goal is to make it stop rolling after X seconds.
After for example 3 seconds I'm setting the flag stopRolling to true and I used a breakpoint and it is true but the object is kept rolling.
Should I remove force somehow to make it stop?
The object that is rolling has a Rigidbody Use Gravity set to true. and a Sphere Collider.
I didn't do anything to make it start rolling. The object starts rolling once he touches the "Crate_0_0" once the flag isTouching is true.
I also tried to set both velocity and angularVelocity to Vector3.zero but it didn't stop it either.
IEnumerator WaitBeforeStopRolling()
{
yield return new WaitForSeconds(timeToStopRolling);
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
stopRolling = true;
}
Setting the Rigidbody isKinematic to true stop the object but that stop it at once. What if I want to stop the object slowly smooth instead of using isKinematic?
I am working on solo project that is horizontal 2D infinite runner. I have a problem in jumping mechanics where player can hold the jump button and jump as soon as he touches the ground. I want to force player to release the button to be able to jump again. I want to make same mechanics when he is floating(at the end of the jump when player starts falling down its y velocity is being reduced for few bits of second). I am following single responsibility principle so jumping and floating are 2 seperate scripts.
I have tried to implement a timer that will count while player is touching the ground and after some time the player would be able to jump again, but didn't manage to get asked result because you can hold the jump button all the time and after determined time spent on ground player would just jump again without releasing the button.
public class Jumping : MonoBehaviour
{
public bool isJumping;
public bool isGrounded;
public float speedUp;
public float verticalAxis;
public float timePassed;
public float jumpLimit;
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0 && timePassed == 0)
{
isJumping = true;
}
}
private void FixedUpdate()
{
if (isJumping)
{
Jump();
}
}
private void OnCollisionStay2D(Collision2D collision)
{
if (collision.collider.tag == "Ground")
{
isGrounded = true;
timePassed = 0f;
}
}
private void Jump()
{
isGrounded = false;
//Modifying y component of players rigidbody(jumping)
Vector2 velocity = Vector2.up * speedUp * verticalAxis;
rb.velocity = velocity;
//Counting time when jumping
timePassed += Time.deltaTime;
if (timePassed >= jumpLimit)
{
isJumping = false;
}
}
}
P.S. I have tried to shrink the code in this question as stackoverflow suggests but I didn't know what to cut out because according to me everything is relevant and crucial to solving the problem. Thanks!
without knowing the rest of your code I would use an additional flag and obviously something for getting the cool down delay like
public float jumpCoolDownTime;
private float coolDownTimer;
private bool canJump;
void Update()
{
// if no can jump you can't jump again
if(!canJump)
{
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0)
{
canJump = false;
isJumping = true;
coolDownTimer = 0;
}
}
else
{
// run timer to enable jump again
if(isGrounded) coolDownTimer += Time.deltaTime;
if(coolDownTimer >= jumpCoolDownTime)
{
canJump = true;
}
}
}
You could however simplify the code a bit using Coroutines
public bool isJumping;
public float speedUp;
public float verticalAxis;
public float jumpLimit;
public float coolDownTime;
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
}
void Update()
{
// do nothing is already jumping
if(isJumping) return;
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0)
{
// start jumping
StartCoroutine(Jump());
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag != "Ground") return;
isGrounded = true;
StartCoroutine(CoolDown());
}
private IEnumerator Jump()
{
isJumping = true;
isGrounded = false;
coolDownTimer = 0;
var timePassed = 0f;
while(timePassed < jumpLimit)
{
// wait for the FixedUpdate call
yield return new WaitForFixedUpdate();
Vector2 velocity = Vector2.up * speedUp * verticalAxis;
rb.velocity = velocity;
}
}
private IEnumerator CoolDown()
{
yield return new WaitForSeoncds(coolDownTime);
isJumping = false;
}
If you are using the axis to get the jump, you can see if the player have released the button by:
if (GetAxis("Vertical") == 0.0) { //Button up
// do stuff
}
if(GetAxis("Vertical") > 0)
{
//Button down, is jumping
}
In Unity axis aren't booleans, they are coefficients, if you play with a controller you can set different behaviors for different intensities.
I am creating a reload script for my PlayerController. I am following Brackey's guide: https://www.youtube.com/watch?v=kAx5g9V5bcM . His method uses IEnumerators and WaitForSeconds to add reload time. Putting it basically, I am logging "reloading" before the yield, and logging "reloaded" after the yield. But, I get "reloading" and "reloaded" at the same time.
I've tried increasing reloading time, in case the time was too short, but nothing has worked.
Here is the inside of my class. I am getting no errors.
public float speed;
private Rigidbody2D rb;
private Vector2 moveVelocity;
public GameObject bullet;
public GameObject shootPoint;
//public float ammo = 6;
//[HideInInspector] public bool reloading = false;
//[SerializeField] private float reloadTime = 3;
//private float reloadState;
public int maxAmmo = 10;
private int currentAmmo;
public float reloadTime = 100f;
private bool isReloading = false;
public float health = 50f;
void Start()
{
rb = GetComponent<Rigidbody2D>();
health = 50;
currentAmmo = maxAmmo;
}
void Update()
{
Vector2 moveInput = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
moveVelocity = moveInput * speed;
if (isReloading) {
return;
}
if (currentAmmo <= 0) {
StartCoroutine(Reload());
return;
}
if (Input.GetButtonUp("Shoot")) {
Instantiate(bullet, shootPoint.transform.position, Quaternion.identity);
currentAmmo--;
}
}
void FixedUpdate()
{
rb.MovePosition(rb.position + moveVelocity * Time.fixedDeltaTime);
}
void OnTriggerEnter2D(Collider2D other) {
if(other.gameObject.tag == "Bullet") {
health--;
//Debug.Log("Player 1 Health: " + health);
Destroy(other.gameObject);
}
}
IEnumerator Reload()
{
Debug.Log("reloading");
isReloading = true;
yield return new WaitForSeconds(reloadTime);
currentAmmo = maxAmmo;
isReloading = false;
Debug.Log("reloaded");
}
As I said, I am not getting any errors. I am expecting me to not be able to shoot for one second, but I can shoot and reloading does not take one second.
You have declared
public float reloadTime = 100f;
a public field gets automatically serialized ("stored") by the Unity Inspector.
My guess would be that at first you declared it like
public float reloadTime;
so the Unity Inspector stored it with the default value 0. Then later you tried to increase that value from within the script.
Any later changes to the initial value within the script won't have any effect. The Unity Inspector will allways overwrite the value with the serialized one.
In order to change your value from now on you always either have to make it using the Inspector, make the field not serialized (e.g. by making it private) or set it in the Awake or Start method which again overwrites the Inspector value.
I'm having difficulty getting my gold pickups to respawn after they've been destroyed on death. The idea is, if the player fails to pick up the 5 gold bars, activates a checkpoint, and dies, the current gold is destroyed and it resets once the screen has faded from black.
I currently have a Coroutine in my Health Manager that runs correctly if the player dies and resets them. I have a Gold Pickup script that destroys the gold if they haven't been picked up. I just can't seem to get them to re-instantiate. I've tried adding the instantiate code within the Health Manager's coroutine and within the Gold Pickup script. Nothing seems to work. If I'm not getting errors saying 'Array index is out of range' it's 'object reference not set to an instance of an object' etc.
public class GoldPickup : MonoBehaviour{
public int value;
public GameObject pickupEffect;
public GameObject[] goldBarArray;
public HealthManager healthManager;
public Checkpoint checkpoint;
private Vector3 goldRespawnPoint;
private Quaternion goldStartPosition;
void Start()
{
//To destroy multiple objects at once, use FindGameObjectsWithTag.
//GetComponent is considered more efficient than FindObjectOfType, but the latter avoids any errors saying an object reference hasn't been set.
goldBarArray = GameObject.FindGameObjectsWithTag("Gold");
healthManager = FindObjectOfType<HealthManager>();
//FindObjectOfType<Checkpoint>();
checkpoint = FindObjectOfType<Checkpoint>();
goldRespawnPoint = transform.position;
goldStartPosition = transform.rotation;
}
public void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
FindObjectOfType<GameManager>().AddGold(value);
Instantiate(pickupEffect, transform.position, transform.rotation);
Destroy(gameObject);
}
}
public void DestroyGold()
{
//For Statics, an object reference isn't necessary. Use the FindObjectOfType to find the appropriate script and reference the Type, such as HealthManager.
if (checkpoint.checkpoint1On == false)
{
foreach (GameObject Gold in goldBarArray)
{
Destroy(Gold);
Instantiate(goldBarArray[5], goldRespawnPoint, goldStartPosition);
goldRespawnPoint = transform.position;
goldStartPosition = transform.rotation;
//healthManager.RespawnCo();
}
}
}
/*public void GoldReset()
{
if (healthManager.isRespawning == true)
{
if (checkpoint.checkpoint1On == false)
{
StartCoroutine("GoldRespawnCo");
}
}
else if (_respawnCoroutine != null)
{
StopCoroutine(_respawnCoroutine);
_respawnCoroutine = StartCoroutine("GoldRespawnCo");
}*/
/*public IEnumerator GoldRespawnCo()
{
if (checkpoint.checkpoint1On == false)
{
Instantiate(goldPrefab, goldRespawnPoint, goldStartPosition);
transform.position = goldRespawnPoint;
transform.rotation = goldStartPosition;
}
else
{
yield return null;
}
}*/
/*if (thePlayer.gameObject.activeInHierarchy == false)
{
Destroy(gameObject);
Instantiate(goldBar, transform.position, transform.rotation);
}
else
{
if (thePlayer.gameObject.activeInHierarchy == true)
{
transform.position = respawnPoint;
transform.rotation = startPosition;
}
}*/
}
public class HealthManager : MonoBehaviour
//The counters will count down and will keep counting down based on the length variables
public int maxHealth;
public int currentHealth;
public PlayerController thePlayer;
//public GoldPickup goldPickup;
//public GoldPickup[] goldPickup;
public float invincibilityLength;
public Renderer playerRenderer;
public float flashLength;
public float respawnLength;
public GameObject deathEffect;
public Image blackScreen;
public float fadeSpeed;
public float waitForFade;
public bool isRespawning;
//public GameObject goldBar;
//To reference another script's function, such as in the DeathTrigger script, make a public DeathTrigger, give it a reference name, and put it into the Start function. Use the reference name and assign it using GetComponent. Call another script's method by using the reference name, followed by a dot and the name of the method. Eg: deathTrigger.DestroyGold().
private Quaternion startPosition;
//private Quaternion goldPosition;
private float flashCounter;
private float invincibilityCounter;
private Vector3 respawnPoint;
//private Vector3 goldRespawnPoint;
private bool isFadetoBlack;
private bool isFadefromBlack;
//private Coroutine _respawnCoroutine;
//private Vector3 goldRespawnPoint;
//private Quaternion goldStartPosition;
void Start()
{
currentHealth = maxHealth;
respawnPoint = thePlayer.transform.position;
startPosition = thePlayer.transform.rotation;
//goldPickup = GetComponent<GoldPickup>();
//goldRespawnPoint = goldBar.transform.position;
//goldStartPosition = goldBar.transform.rotation;
//goldRespawnPoint = transform.position;
//goldStartPosition = transform.rotation;
//goldPickup = FindObjectOfType<GoldPickup>();
//goldRespawnPoint = goldBar.transform.position;
//goldPosition = goldBar.transform.rotation;
}
void Update()
{
//These functions are checked every frame until the player takes damage
if (invincibilityCounter > 0)
{
invincibilityCounter -= Time.deltaTime;
flashCounter -= Time.deltaTime;
if (flashCounter <= 0)
//The Flash Counter is currently set at 0.1 and will be within the 0 region as it counts down. During this period, the playerRenderer will alternate between on and off
{
playerRenderer.enabled = !playerRenderer.enabled;
//The Flash Counter will keep counting down and reloop depending on the Flash Length time
flashCounter = flashLength;
}
//This makes sure after the flashing and invincibility has worn off that the player renderer is always turned back on so you can see the player
if (invincibilityCounter <= 0)
{
playerRenderer.enabled = true;
}
}
if (isFadetoBlack)
{
blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 1f, fadeSpeed * Time.deltaTime));
if (blackScreen.color.a == 1f)
{
isFadetoBlack = false;
}
}
if (isFadefromBlack)
{
blackScreen.color = new Color(blackScreen.color.r, blackScreen.color.g, blackScreen.color.b, Mathf.MoveTowards(blackScreen.color.a, 0f, fadeSpeed * Time.deltaTime));
if (blackScreen.color.a == 0f)
{
isFadefromBlack = false;
}
}
}
public void HurtPlayer(int damage, Vector3 direction)
{
//If the invincibility countdown reaches zero it stops, making you no longer invincible and prone to taking damage again
if (invincibilityCounter <= 0)
{
currentHealth -= damage;
if (currentHealth <= 0)
{
Respawn();
}
else
{
thePlayer.Knockback(direction);
invincibilityCounter = invincibilityLength;
playerRenderer.enabled = false;
flashCounter = flashLength;
}
}
}
public void Respawn()
{
//A StartCoroutine must be set up before the IEnumerator can begin
if (!isRespawning)
{
StartCoroutine("RespawnCo");
}
}
//IEnumerators or Coroutines will execute the code separately at specified times while the rest of the code in a codeblock will carry on executing as normal.
//To prevent an error appearing below the name of the Coroutine, be sure to place a yield return somewhere within the code block. Either yield return null or a new WaitForSeconds.
public IEnumerator RespawnCo()
{
if (GameManager.currentGold < 5)
{
isRespawning = true;
thePlayer.gameObject.SetActive(false);
Instantiate(deathEffect, respawnPoint, startPosition);
yield return new WaitForSeconds(respawnLength);
isFadetoBlack = true;
yield return new WaitForSeconds(waitForFade);
//To reference another script's function quickly and just the once, use the FindObjectOfType function. This is considered to be slow however.
FindObjectOfType<GoldPickup>().DestroyGold();
//GetComponent<GoldPickup>().DestroyGold();
//Instantiate(goldBar, goldRespawnPoint, Quaternion.identity);
isFadefromBlack = true;
//goldRespawnPoint = goldBar.transform.position;
//goldStartPosition = goldBar.transform.rotation;
isRespawning = false;
thePlayer.gameObject.SetActive(true);
thePlayer.transform.position = respawnPoint;
thePlayer.transform.rotation = startPosition;
currentHealth = maxHealth;
invincibilityCounter = invincibilityLength;
playerRenderer.enabled = false;
flashCounter = flashLength;
GameManager.currentGold = 0;
GetComponent<GameManager>().SetCountText();
StopCoroutine("RespawnCo");
/*isRespawning = true;
thePlayer.gameObject.SetActive(false);
yield return new WaitForSeconds(respawnLength);
isFadetoBlack = true;
yield return new WaitForSeconds(waitForFade);
isFadefromBlack = true;
invincibilityCounter = invincibilityLength;
playerRenderer.enabled = false;
flashCounter = flashLength;
SceneManager.LoadScene("Level 1");
GameManager.currentGold = 0;*/
}
else if(GameManager.currentGold >= 5)
{
isRespawning = true;
thePlayer.gameObject.SetActive(false);
Instantiate(deathEffect, respawnPoint, startPosition);
yield return new WaitForSeconds(respawnLength);
isFadetoBlack = true;
yield return new WaitForSeconds(waitForFade);
isFadefromBlack = true;
isRespawning = false;
thePlayer.gameObject.SetActive(true);
thePlayer.transform.position = respawnPoint;
thePlayer.transform.rotation = startPosition;
currentHealth = maxHealth;
invincibilityCounter = invincibilityLength;
playerRenderer.enabled = false;
flashCounter = flashLength;
}
}
/*public void HealPlayer(int healAmount)
{
currentHealth += healAmount;
if(currentHealth > maxHealth)
{
currentHealth = maxHealth;
}
}*/
public void SetSpawnPoint(Vector3 newPosition)
{
respawnPoint = newPosition;
}
public class Checkpoint : MonoBehaviour
public HealthManager theHealthManager;
public Renderer cpRenderer;
public Renderer postRenderer;
public SpriteRenderer pcRenderer;
public Material cpOff;
public Material cpOn;
public Material postOff;
public Material postOn;
public GameObject[] infoPanels;
public bool checkpoint1On;
//Make sure to assign a value to a bool with '=' and in an 'if' statement somewhere in the code to prevent warnings.
//private bool checkpoint1IsActivated;
private bool infoPanel1Activated;
void Start()
{
theHealthManager = FindObjectOfType<HealthManager>();
}
void Update()
//Key presses are better handled in the Update function and will recognise keys being pressed once every frame.
{
if (checkpoint1On == true)
{
if (infoPanel1Activated == false)
{
if (Input.GetKeyDown(KeyCode.Space))
{
infoPanels[0].SetActive(true);
infoPanel1Activated = true;
}
}
else
{
if (infoPanel1Activated == true)
{
if (Input.GetKeyDown(KeyCode.Space))
{
infoPanels[0].SetActive(false);
infoPanel1Activated = false;
}
}
}
}
}
public void Checkpoint1On()
{
cpRenderer.material = cpOn;
postRenderer.material = postOn;
pcRenderer.color = new Color(1f, 1f, 1f, 1f);
checkpoint1On = true;
}
//[] makes a variable an Array (a list). The 'foreach' loop will check through all the Checkpoint objects
//Checkpoint[] checkpoints = FindObjectsOfType<Checkpoint>();
//For each Checkpoint Array called 'checkpoints', look for 'cp' and turn the others in the list off
/*foreach (Checkpoint cp in checkpoints)
{
cp.CheckpointOff();
}
theRenderer.material = cpOn;*/
public void Checkpoint1Off()
{
cpRenderer.material = cpOff;
postRenderer.material = postOff;
pcRenderer.color = new Color(1f, 1f, 1f, 5f);
checkpoint1On = false;
}
public void OnTriggerStay(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if (GameManager.currentGold >= 5)
{
if (Input.GetKeyDown(KeyCode.Return))
{
theHealthManager.SetSpawnPoint(transform.position);
Checkpoint1On();
checkpoint1On = true;
}
}
else if (GameManager.currentGold <= 5)
{
checkpoint1On = false;
}
}
}
In your DestroyGold() function, you instantiate the gold like this:
foreach (GameObject Gold in goldBarArray)
{
Destroy(Gold);
Instantiate(goldBarArray[5], goldRespawnPoint, goldStartPosition);
goldRespawnPoint = transform.position;
goldStartPosition = transform.rotation;
//healthManager.RespawnCo();
}
But transform.position and transform.rotation only get the position and rotation of the current object (i.e. whatever your script is attached to). So not only are you spawning all the gold in the same spot, it's spawning the gold at the location of the object that holds your script, not where you actually want it to go!
Without knowing much about the objects in your scene, here's what I can tell you: try creating a Transform[] to store the locations where you want to respawn the gold. Also, make sure you assign the goldRespawnPoint and goldStartPosition BEFORE you call Instantiate() in your foreach loop. Finally, just a general tip: you should never use variable == true or variable == false in an if statement. You can just use if(variable) or if(!variable), respectively. It will work just the same while being more readable and reducing the amount of code you need to write.
EDIT 1: In response to comments, I've added specific code examples for implementing these suggestions.
To start, you're probably getting the out of range error because of goldBarArray[5]. Since arrays start at index 0, you can only access up to element n-1 in a size n array. More on how to fix this in the next step.
Now for the Transform array. In the area where you declare your public variables (at the top of the script), add the line
public Transform[] spawnPoints;
Then, back in Unity you will be able to assign those spawn points in the Inspector.
EDIT 2: Additionally, in the foreach loop you're trying to instantiate one of the gold bars from the scene, but those are getting deleted with the Destroy(Gold); statement. Instead, you should be instantiating from the prefab which won't get destroyed. To do this, add
public GameObject goldPrefab;
up with the rest of your public variables. Then, in the Editor create a prefab by dragging one of the gold bars from the Hierarchy into your Assets folder. Finally, set that prefab to be the value of goldPrefab in the Inspector.
Now, you actually can clean up your foreach loop a little bit. You can get rid of the goldRespawnPoint and goldStartPosition lines because the respawn locations will be contained in the Transform array we just created. Again, without knowing how your scene is structured I've needed to just make an educated guess about what will work. Give this loop a try:
int spawnPointCounter = 0;
foreach(GameObject Gold in goldBarArray){
Destroy(Gold);
Transform currentSP = spawnPoints[spawnPointCounter];
Instantiate(goldPrefab, currentSP.position, currentSP.rotation);
spawnPointCounter++;
}
Basically I want to make a player that can transform into demon at will whenever the user press the power-up button however I want the transformation to end after 60 seconds (when the transformation ends I want the player revert back to his original state). I also want the transformation to end if the player gets hit by an enemy. So far I've made this code and it works but I'm having trouble resetting the yield wait for seconds back to 60 seconds when if the player gets hit by an enemy and if the user decided to press the button to transform the player back into a demon. Can anyone help me with this problem?
In my hierarchy I have my player as the parent and my demon player as the child. A playermovement script attached to the player as well as the transformation script below:
public GameObject demon;
public BoxCollider2D col;
public Renderer rend;
public ParticleSystem par1;
public static Vector3 target;
void Start () {
target = transform.position;
}
void Update () {
target.z = transform.position.z;
}
public void DemonCharacter() {
StartCoroutine (PowerUpCoroutine ());
}
private IEnumerator PowerUpCoroutine() {
yield return new WaitForSeconds (0.3f);
par1.Play (); // particle system animation to cover transformation happening
par1.transform.position = target;
yield return new WaitForSeconds (0.2f);
demon.SetActive (true); // activates demon gameobject
rend.enabled = false; // deactivate players spriterenderer
col.enabled = false;
yield return new WaitForSeconds (60f);
demon.SetActive (false); // deactivates demon gameobject
rend.enabled = true; // activate players spriterenderer
col.enabled = true;
par1.Stop ();
}
And on my demon player, I attached this script;
I works but when the user clicks on the button to transform into a demon the yield waitforseconds doesn't stop, so when the player transform into a demon seconds later the demon player transforms back into the player rather than resetting the yield wait for seconds.
public BoxCollider2D Playercol;
public Renderer PlayerRend;
void Start()
{
}
void Update ()
{
}
void OnTriggerEnter2D(Collider2D col) {
if (col.tag == "enemy") {
demon.SetActive (false);
PlayerRend.enabled = true;
Playercol.enabled = true;
}
}
Another way than what #m.rogalski suggested would be to use a simple float variable as timer:
public GameObject demon;
public BoxCollider2D col;
public Renderer rend;
public ParticleSystem par1;
public static Vector3 target;
private float demonTimer;
void Start()
{
target = transform.position;
demonTimer = 0.0f;
}
void Update()
{
target.z = transform.position.z;
if (demonTimer > 0.0f)
{
demonTimer -= Time.deltaTime;
if (demonTimer <= 0.0f)
{
demon.SetActive(false);
rend.enabled = true;
col.enabled = true;
}
}
}
public void DemonCharacter()
{
par1.Play();
par1.transform.position = target;
demon.SetActive(true);
rend.enabled = false;
col.enabled = false;
demonTimer = 60.0f;
}
public void CancelDemon()
{
demonTimer = 0.0f;
}
Hope this helps,
My suggestion would be for you to modify your Coroutine to not use WaitForSeconds but use it's own timing calculations.
// create the flag indicating interruption
bool _interrupt = false;
// create your coroutine
IEnumerator PowerUpCoroutine()
{
// set the time you want to hold transformation
const float TRANSFORMATION_INTERVAL = 60.0f;
// currently elapsed time
float currentlyElapsed = 0.0f;
// add your logic for pre-transformation
while ( currentlyElapsed < TRANSFORMATION_INTERVAL && !_interrupt )
{
yield return null;
currentlyElapsed += Time.deltaTime;
}
// add post-transformation logic
// revert transformation process
_interrupt = false;
}
Now if you run this coroutine calling StartCoroutine(PowerUpCoroutine()); you can interrupt it setting _interrupt flag to true. eg :
public void Interrupt()
{
_interrupt = true;
}
// in some update :
if ( gotHitThisFrame == true )
Interrupt();