I have been working on a Unity2d project for a while now and I recently implemented a method of which my "Enemy" housed in the GreyGuardController script would fire a bullet in the direction of which it would be facing, using the animator and
otherAnimator = otherObject.GetComponent<Animator>();
for this.
Every time I now run my code, my game freezes but doesn't crash (I think it is stuck in a loop but there are no real loops in my program). It isn't my instantiation of the bullet before anyone starts accusing that as the loop of it freezing as I have commented this out and changed things around over and over again.
public class HurtPlayer : MonoBehaviour {
public float timeToShoot;
private float timeToShootCounter;
private bool shot;
private Vector3 moveDirection;
public float timeBetweenShot;
public float timeBetweenShotCounter;
public Transform firePoint;
public GameObject Bullet;
// Use this for initialization
void Start()
{
shot = false;
timeToShootCounter = timeToShoot;
timeBetweenShotCounter = timeBetweenShot;
}
IEnumerator ExecuteAfterTime(float time)
{
yield return new WaitForSeconds(time);
}
// Update is called once per frame
void Update () {
if (shot == true)
{
timeBetweenShot -= Time.deltaTime;
timeToShoot -= Time.deltaTime;
if (timeBetweenShot <= 0f)
{
shot = false;
timeToShoot = timeToShootCounter;
timeBetweenShot = timeBetweenShotCounter;
}
}
}
void OnTriggerStay2D(Collider2D other)
{
if (other.gameObject.tag == "player")
{
if(shot == false)
{
if (timeToShoot >= 0f)
{
shot = true;
while(shot == true)
{
Instantiate(Bullet, firePoint.position, firePoint.rotation);
if (timeBetweenShot <= 0f)
{
shot = false;
timeToShoot = timeToShootCounter;
timeBetweenShot = timeBetweenShotCounter;
}
}
}
}
}
}
This here is my code attached to my guard which instantiates a bullet as well as trying to use variables "TimeToShoot" as a counter for how long the enemy has left to shoot for and "TimeBetweenShoot" as a counter for how long the enemy has in between shots.
This doesn't work and neither does the Enumerator delay.
As an amateur I am obviously doing something clearly wrong but I have no idea what I'm doing wrong or where and would greatly appreciate your help.
while(shot == true)
this will freeze the editor if the body is never left
if (timeBetweenShot <= 0f)
{
shot = false;
if this is not executed in the first iteration, it won't be in the second because you never change the value of "timeBetweenShot" in that loop
It is important to understand that your update function needs to terminate for the game frame to continue with the next game object's update etc, until the end of the frame is reached, before the next update is called on this game object in the next game frame, so
void Update () {
if (shot == true)
{
timeBetweenShot -= Time.deltaTime;
is never executed while your while loop goes rogue
EDIT:
try something like:
// Update is called once per frame
void Update()
{
if (timeToShoot >= 0f) // if we are shooting at all
{
timeToShootCounter -= Time.deltaTime; // make sure we'll stop shooting after the given amount of time (decrease countdown until negative
timeBetweenShotCounter -= Time.deltaTime; // decrease countdown until next shot (after some intervall)
if (timeBetweenShotCounter <= 0f) // if intervall since last shot expired
{
Instantiate(Bullet, firePoint.position, firePoint.rotation); // shoot
timeBetweenShotCounter += timeBetweenShot; // add the time to wait for the next shot to the intervall (so that we don't shoot anymore in the following frames, until this time expired again (by becoming negative))
}
}
}
void OnTriggerStay2D(Collider2D other)
{
if (other.gameObject.tag == "player")
{
timeToShootCounter = -1f; // initialise as expired (start shooting immediately)
timeBetweenShotCounter = timeBetweenShot; // shoot in intervalls for this amount of time
}
}
this way, when the collision event is triggered, you reset the counters to their initial values. After that the update function makes sure to fire after intervals of time, and stops once the overall shooting time is over.
Of course you can further improve this code to handle edge cases (if overall time expires and intervals time also expires at the same time, do we shoot a last bullet or not? etc)
hope this helps.
If you appreciate my efforts, please give them a thumbs up =)
Here is your loop: you increase timeBetweenShot on each frame but your while have to be executed in one frame, this means your code executes this while until you enter the if statement but you will never do since timeBetweenShot will change value only in next frame
while(shot == true)// here is your loop budy
{
// shot = false; // try this row here you will problably stop having the trouble
Instantiate(Bullet, firePoint.position, firePoint.rotation);
if (timeBetweenShot <= 0f)//
{
shot = false;
timeToShoot = timeToShootCounter;
timeBetweenShot = timeBetweenShotCounter;
}
}
Related
I got Spikes and the bool onSpikes, which tells if the player is onSpikes or not.
Currently the health goes Down constantly, if the player is onSpikes.
if(onSpikes)
{
health -= 0.01f;
}
But I think this it isn't a good solution, cause I'm in void Update(), and I anyways wanted the Health to drop in bigger pieces ( like -0.2f each second).
I've already tried building an Method and use Invoke, but like this my player didn't lost health at all.
Code:
private void Update(){
Debug.Log("Player on Spikes: "+SpikeDMG.onSpikes);
bar.localScale = new Vector3(health,1f);
if(SpikeDMG.onSpikes)
{
Invoke("doDmg",1);
}
//Wenn leben unter 0 fällt ==> starte szene neu
if(health <=0){
int scene = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(scene, LoadSceneMode.Single);
Time.timeScale = 1;
SpikeDMG.onSpikes = false;
}
}
void doDmg(){
health -= 10/100;
}
}
Thanks to everyone who can help!
What you want to do is use some kind of time stamp, when your next action should be executed. It is also described here and here.
private float spikeDamageAt= 0.0f;
void update() {
if(onSpikes && Time.time >= spikeDamageAt) {
health -= 0.01f;
spikeDamageAt= Time.time + 1.0f; // for 1 seconds
}
}
I'm trying to get the player to continue on after he jumps (see code), but what he does is return back to the jumpPosition transform without continuing on to the next point.
Here is what it is doing in Unity:
...
Here is my code for playerMovment:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class MovementController : MonoBehaviour
{
// public GameObject playerToMove; // not sure why I need this
public float moveSpeed; // move speed of the player going from player postion to current point, possible to use somewhere else
private Transform currentPoint; // used to determine where the next point the player has to move by cycling through 'points' array
public Transform jumpPoint; // used as a location trigger to tell the player when to jump -- will be attempting to make into an array
public Transform crouchPoint; // used as a location trigger to tell the player when to crounch -- will be attempting to make into an array
public Transform[] points; // an array of location for the 'currentPoint' to cycle through
public float maxPause = 100; // used to determine the length of time between when the player arrives at the 'currentPoint' and when to leave said point; default = 100
public float reducedPause = 2; // used to set 'maxPause' to a smaller number so that player won't keep jumping/crouching
public TestCharacterController2D controller; // acceses the TestCharacterController2D script (I didnt write this script but plan to modiify) used for basic move, jump, and crouch funtions
public Animator animator; // my attempt to find the player's animator
public bool isRight; // used to to determine which way the character is facing -- I think this can be accesed through the 'controller' variable (TestCharacterController2D script)
private bool jump; // to tell the 'controller' when to jump
private bool crouch; // to tell the 'controller' when to crouch
private bool pause = false; // used to determine when the player arrives at the 'currentPoint' and the 'maxPause' countdown begins
public int pointsSelection; // used to cycle the 'points' array when maxPause cycle is over and player is at current point
// public float jumpHeight = 100f; // not sure why used
void Start() // looking into 'onAwake' maybe? (or others)
{
currentPoint = points[pointsSelection]; // sets currentPoint to default location ('pointSelection' is 'publc' so can be modified in Unity
isRight = true; // player starts facing right -- as per character animations
}
void Update() // not sure if should have more in 'FixedUpdate' or others (maybe?)
{
jump = false;
if (Vector2.Distance(transform.position, currentPoint.position) < 0.05f)
// checks to see if player is at 'currentPoint'
{
pause = true; // starts the pause sequenece
Debug.Log("Pause = " + pause);
if (pause) // when the movement is pause do the the following
{
moveSpeed = 0;
animator.SetFloat("Speed", 0); // player stops moving -- works!
if (maxPause <= 100) // checks to see if still paused
{
Debug.Log("this is maxPause: " + maxPause);
if (maxPause < 0) // found 'maxPause' was going to far below zero
maxPause = 0;
maxPause--; // reduce pause amount (working way out of loop)
}
if (maxPause == 0) // when 'maxPause' timer has finished
{
pointsSelection++; // move to next point
maxPause = 100; // reset 'maxPause' timer
pause = false; // resume 'transform.position == currentPoint.position' process
}
}
if (pointsSelection == points.Length) // makes sure 'pointsSelection' doesn't go out of bounds
{
Debug.Log("at end of array");
pointsSelection = 0; // start the player's movement process over again
}
}
else // not sure if requried
{
Debug.Log("pause = false");
Debug.Log("this is the moveSpeed " + moveSpeed);
Debug.Log("pointsSelection: " + pointsSelection);
}
if (Vector2.Distance(transform.position, jumpPoint.position) < 0.05f && jumpPoint == currentPoint) // conditions for the jump action (automatic) -- I fell the whole thing needs to be more elaborate ** WORK IN PROGRESS **
{
jump = true;
}
else
jump = false;
currentPoint = points[pointsSelection]; // moved to line 130 -- not sure if better here
}
void FixedUpdate()
{
if (isRight && transform.position.x > currentPoint.position.x) // flipping the character -- I'm pretty sure I can use TestCharacterController2D to do this for me, this is comparing the player's 'transform'
{
moveSpeed = -0.25f; // tells controller to head in the left direction
isRight = false; // no longer facing right
}
if (!isRight && transform.position.x < currentPoint.position.x) // reverse of above
{
moveSpeed = 0.25f; // tells controller to head in the right direction
isRight = true; // no longer facing left
}
if (moveSpeed > 0 || moveSpeed < 0)
animator.SetFloat("Speed", 1); // player starts PlayerRun animation -- works!
// Move our character
controller.Move(moveSpeed, crouch, jump); // draws from the TestCharacterController2D script
}
public void OnLanding()
{
animator.SetBool("Jumped", false);
}
}
This has been asked before, but none of the answers solved my problem because the problem was always slightly different.
I have a character that jumps on spacebar down. I want it to make higher jumps when the spacebar is pressed longer, with a maximum of twice the normal jump height.
Here's what I came up with so far:
void FixedUpdate () {
if (Input.GetKeyDown(KeyCode.Space) && myRB.velocity.y == 0)
{
timeHeld = 1;
}
if (Input.GetKey(KeyCode.Space) && myRB.velocity.y == 0)
{
myRB.AddForce(Vector3.up * 20,ForceMode2D.Impulse);
timeHeld += Time.deltaTime;
myRB.mass = myRB.mass - timeHeld/10;
}
if (Input.GetKeyUp(KeyCode.Space))
{
myRB.mass = 1;
}
}
Since the jump has to happen on keydown (not keyup), I can't increase the force added on jump, because the force is already applied on keydown. Therefore, I was thinking of lowering the mass of my rigidbody.
The above code "works" if I keep holding spacebar: the character jumps (minimum height), lands, jumps again but now a bit higher, etc. But that's not what I'm looking for, obviously.
This can easily be done by making a Time frame in which Jumping occurs;
Within this time frame u allow upward forces to be applied by holding space bar down. As soon as you reach either the end of the time frame or the end of the button press; you restrict upward velocity until the character has landed.
Let gravity take its own course after you end the jump.
As for the coding part i suggest you start a co routine when the jump happens.
here is some code to u help you get started;
IEnumerator Jumper()
{
if (maxjumpduration!=jumpduration)
{
jumpduration = jumpduration + 0.1f;
yield return new WaitForSeconds(0.1f);
}
else
{
jumpduration = 0;
StopCoroutine(Jumper());
}
}
As for the fact you want have a regular jump as well; make another time frame in which is decided if the press of space bar is considered either a regular jump or a controlled jump; During that initial time frame in which you are still deciding what kind of jump it is; you execute a short part of the jump until you reach a conclusion and then you send it to the correct method.
I added Some ground work for u but try to finish the code yourself;
{
private bool shortjump = false;
private bool checkphase = false;
private bool hitground = false;
private Rigidbody player = new Rigidbody();
private bool jumping = false;
Vector3 sharedjumpforce = new Vector3();
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Initialjump();
}
if (Input.GetKeyUp(KeyCode.Space))
{
if (jumping)
{
shortjump = true;
}
}
if (jumping)
{
if (hitground)
{
jumping = false;
}
}
}
void Initialjump()
{
if (jumping = false)
{
checkphase = true;
jumping = true;
player.AddForce(sharedjumpforce);
Invoke("Standardorcontrolledjump", 0.2f);
}
}
void Standardorcontrolledjump()
{
checkphase = false;
if (shortjump)
{
}
else
{
}
}
}
I don't understand why are you putting
&& myRB.velocity.y == 0
You want it to reduce its mass while in air don't you. Assuming that reducing the mass after the initial force has been applied does allow the Rigidbody to go higher, (force of gravity weakens, im not sure if that happens in Unity)
Why don't you try this
void FixedUpdate () {
// apply force the instant button is pressed. That is only once
if (Input.GetKeyDown(KeyCode.Space) && myRB.velocity.y == 0)
{
myRB.AddForce(Vector3.up * 20,ForceMode2D.Impulse);
timeHeld = 1;
}
// keep subtracting from mass while its held
if (Input.GetKey(KeyCode.Space))
{
timeHeld += Time.deltaTime;
myRB.mass = myRB.mass - timeHeld/10;
}
if (Input.GetKeyUp(KeyCode.Space))
{
myRB.mass = 1;
}
}
First off, I am quite new to scripting so there's probably going to be a few flaws in my script.
So basically, I've made a script for the power up, but once my shot or the player touches the power up coin the fire rate does increase however it won't go back to the normal fire rate after 5 seconds... I have no idea what might be the cause, any advice would be helpful!
using UnityEngine;
using System.Collections;
public class FireRatePowerUp : MonoBehaviour {
private bool isPowerUp = false;
private float powerUpTime = 5.0f;
private PlayerShoot playerShoot;
private void Start()
{
playerShoot = PlayerShoot.FindObjectOfType<PlayerShoot>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Player" || collision.gameObject.tag == "Projectile")
{
StartCoroutine(PowerUpTime());
isPowerUp = true;
Destroy(gameObject);
if (collision.gameObject.tag == "Projectile")
{
Destroy(collision.gameObject);
}
}
}
IEnumerator PowerUpTime()
{
playerShoot.fireRate -= 0.13f;
yield return new WaitForSeconds(powerUpTime);
playerShoot.fireRate += 0.13f;
}
}
I think the issue here is that you're destroying the gameobject this script is attached to (the coin) and by so doing, the script itself is destroyed, therefor its code, coroutine or otherwise won't execute.
StartCoroutine(PowerUpTime());
isPowerUp = true;
Destroy(gameObject); //oops, our script has been destroyed :(
You would have to do this very differently, basically moving the bulk of the code to the PlayerShoot class.
Something like this (this being in PlayerShoot.cs)
public void ActivatePowerupFireRate(float time, float amt) {
StartCoroutine(DoActivatePowerupFireRate(time, amt));
}
public IEnumerator ActivatePowerupFireRate(float time, float amt) {
fireRate -= amt;
yield return WaitForSeconds(time);
fireRate += amt;
}
IEumerator is definately one of the ways you can solve this issue.
However I'm not a fan of them here's my solution if you have a timer in game.
public int timePassed = 0;
public int gotPowerUp = 0;
void Start(){
InvokeRepeating("Timer", 0f, 1.0f);
//Starting at 0 seconds, every second call Timer function.
}
void Timer(){
timePassed++; // +1 second.
}
That way when you obtained the powerup you can set gotPowerUp = timePassed. So you have the exact time when powerup is activated.
then you do something like
if( (gotPowerUp + 5) == timePassed ){
//5 seconds have passed.
//deactivate powerup here
}
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.