I have two cheesbar in the game... once finished the other appears...and there is "eat button". when a mouse approaches to chees that button is being activated and make continueeating bool active. And I press and hold the button , cheese is being eaten and when cheese finished , second game object appears which means second cheese bar, somewhere, but start as a half cheesebar ...so even if it has SetActive(false); in the beginning , it is decreasing as if it was activated... how can I correct this.
thanks in advance.
void Start ()
{
eatingcheese = maxcheese;
eatingcheese2 = maxcheese2;
}
void Update ()
{
cheesebar.fillAmount = eatingcheese / maxcheese;
cheesebar2.fillAmount = eatingcheese2 / maxcheese2;
if (continueeating)
{
eatingcheese--;
eatingcheese2--;
hungerscript.gettinghungery+=1.5f;
}
if (eatingcheese <=0)
{
cheesebar.gameObject.SetActive(false);
cheesebar2.gameObject.SetActive(true);
}
}
public void eating()
{
continueeating = true;
}
public void stopeating()
{
continueeating = false;
}
I notice that in the code
if (continueeating)
{
eatingcheese--;
eatingcheese2--;
hungerscript.gettinghungery+=1.5f;
}
You decrement eatingcheese2 whether or not cheesebar2 is active. You could check to see if the cheese is ready to be eaten and only proceed with decrementing eatingcheese2 if it is. You could use an integer to track which cheese you are currently eating to do that, and a switch statement to decrement either value based on that state. Or if you are sure you'll only ever have two cheeses, you can use a bool and an if statement.
Note that Update() is framerate dependent and you are dealing with eatingcheese numbers that will be very large (since they decrement every update while being eaten). I would suggest using a float from 1 to 0 representing whole and fully eaten cheese, and I would also recommend multiplying the amount eaten each step by Time.deltaTime so avoid the issue of scaling too fast and unpredictably.
Related
I've been trying to give to the NPCs of a game I'm working on the ability to "sense" when the Player is near. I've made this script and, for reasons unknown, the bool "found" stays false and, when I automatically set it to true, it reverts back to false (it still sends the player position to the goTo script, so at least that works). Does anyone know how to resolve this?
public class NPCLookForPlayerScript : MonoBehaviour {
public bool found; //player found
public float awareness; //how large is the circlecast
public int keepLooking; //for how much time, after losing sight of the player, he tries to keep on looking for him
public GameObject player; //variable to lock on to the player
int timer; //variable to decrement while player isn't in line of sight
NPCGoToScript goTo;
// Use this for initialization
void Start () {
goTo = GetComponent<NPCGoToScript>();
}
// Update is called once per frame
void Update () {
//he's always looking for the player
Collider2D coll = Physics2D.OverlapCircle((Vector2)transform.position, awareness);
//if the player is found, keep looking for him
if (coll.gameObject == player)
{
found = true;
timer = keepLooking;
}
//if the player was found,
if (found)
{
timer = timer - 1; //less time to look for the player
goTo.newPosition(player.transform.position);
}
//if the player is out of sight for too much time, stop looking for him
if (timer <= 0)
{
found = false;
}
}
void OnDrawGizmos()
{
Gizmos.color = Color.green;
Gizmos.DrawWireSphere((Vector2)transform.position, awareness);
}
}
Since someone has made me notice that the values of the variables aren't written in the code, let me be clear: I'm working on unity, this script has public values that can be modified by the inspector, which is very usefull since this script has to be used for different kinds of NPCs. So the values are not zero. awareness = 5f and keepLooking = 20. The player field does have the player GameObject.
You are never setting initial values for any of your fields :).
timer is declared but never set to anything so essentially it equals 0. Also awareness (your radius) is never set either so is 0.0f by default.
So your code...
Executes the Physics2D.OverlapCircle with a radius of zero (i.e. no circle)
Skips the logic in the coll == player statement cause there is no circle to collide with the player.
Skips the logic in the found statement cause found of false
Then executes the logic in the timer <= 0 statement cause the timer is 0 (was never defined)
Continues to set found to false every frame, forever and ever and ever, into the long good night
The player getting the the transform data should only happen whe you manually set found to true in the inspector and then only for one frame cause it reverts back to false in the very next statement.
You need to give your fields value greater than 0.
I want to make countdown timer that will return value of bool when he is active , so I can check on other script if is active give double points if is not then you need to give normal points..
I want to make it more complicated and I want to add time on timer if the timer is active, if is not then we use default time on countdown...
I don't know how to use courutine specially when I need to add time if the timer is not over..
Lets say like a example:
I pick up power up and timer starts for 5 seconds counting to 0.
If i pick up again powerup and timer is on lets say 3 , Power up need to have now 8 seconds. When powerup is over he must go from 5 seconds when player pick up new one..
Here is my code that doesn't work how I want also my code doesn't have a function to add time to power up when power up is active.. In other words I don't know how i can check if powerup is active and if yes just to add to counter 5 more seconds..
Here is code that doesn't contain adding time it only contains working counter..
void startDoublePoints()
{
StartCoroutine("doublePoints");
Time.timeScale = 1;
}
//Simple courutine
IEnumerator doublePoints()
{
while (true)
{
yield return new WaitForSeconds(1);
timeLeft--;
}
}
I hope someone will explain me more about how I can achieve my goal.. I hope I explained what I need to achieve.. If you do not understand something please ask on comment and I will try to explain it again..
Thank you so much community I don't know how would I learn anything without this great place :)
float powerUpTimer;
bool isDoublePoints = false;
void Update()
{
// Check timer only when Power up time
if(isDoublePoints)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
if(powerUpTimer <= 0)
{
// End of power up time
isDoublePoints = false;
powerUpTimer = 0;
}
}
}
// Add any time player picks to timer
public void OnPickPowerUp(float buffTime)
{
isDoublePoints = true;
powerUpTimer += buffTime;
}
I need to keep up to an X (changeable amount) instances of countdown timers, each being added by the user, once a single one of them reaches 0 it calls for a function while the other counters keep counting.
for that purpose i tried to create a queue of them, but as it turns out, I can't change the value of an iteration variable in foreach:
public struct Debuff
{
public float Timer;
public int Stack;
public int MaxStack;
...
}
public Debuff Poisoned;
public void CalcDamage(...)
{
...
if (Poisoned.Stack < Poisoned.MaxStack)
{
Poisoned.Stack++;
PoisonStacksTimer.Enqueue(Poisoned.Timer);
InvokeRepeating("Poisoning", 0.1F, 1F);
}
else
{
PoisonStacksTimer.Dequeue();
PoisonStacksTimer.Enqueue(Poisoned.Timer);
}
}
public void Poisoning()
{
foreach(float PTimer in PoisonStacksTimer)
{
TakeDamage(Poisoned.DmgTranslate);
PTimer -= Time.deltaTime; // here lies at least one of the problems
if (PTimer <= 0)
{
Poisoned.Stack--;
PoisonStacksTimer.Dequeue();
CancelInvoke("Poisoning");
}
}
}
is there any other way to use countdown timers in queue? or maybe a better way to achieve my objective?
this is my first time asking a question here so I hope I explained my problem clearly.
Off the top my head, I'd use coroutines.
Every time you need to start a timer, you start an associated coroutine, so if you, at a specific moment, have for example 4 active timers, there'll be 4 coroutines.
In order to keep a reference to every single coroutine, use a List<IEnumerator>: every time a timer needs to be started, add the new IEnumerator reference and start the coroutine, so when a coroutine ends naturally or needs to be stopped, you can remove that reference from the list while keeping the other alive coroutines' references in the list.
I'm working on a project in the Unity engine and I've come across an issue that I cannot understand. I have an ammo counter that decreases the amount of ammo available by 1, each time the player clicks the left mouse button. Instead of doing this though, it decreases by half (rounded down). I'm not entirely sure why this happens as the only decremental value is " ammoCount -= 1; " My code for the script is below:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ShootGun : MonoBehaviour {
public Text Ammo;
public int ammoCount = 30;
void FixedUpdate ()
{
if(Input.GetButtonDown("Fire1"))
{
for(int i = 1; i <= ammoCount; i++)
{
ammoCount -= 1;
Debug.Log ("Ammo remaining: " + ammoCount);
Ammo.text = " " + ammoCount;
Vector3 gunRayOrigin = transform.position;
float gunRayDistance = 50f;
Ray gunRay = new Ray ();
gunRay.origin = gunRayOrigin;
gunRay.direction = Vector3.down;
if(Physics.Raycast(gunRayOrigin, gunRay.direction, gunRayDistance)) {
Debug.Log("Bullet Hit");
}
}
}
}
}
Any help would be greatly appreciated, as I'm still getting the hang of C#.
You are decrementing it inside a for loop.
If we simplify down to this:
for(int i = 1; i <= ammoCount; i++)
{
ammoCount -= 1;
}
and take an initial ammoCount of 6, the first time you enter the loop you have:
i = 1, ammoCount = 6
But then you subtract one from ammoCount, and the loop adds one to i. So the next loop you have
i = 2; ammoCount = 5
But then you subtract one from ammoCount, and the loop adds one to i. So the next loop you have
i = 3; ammoCount = 4
But then you subtract one from ammoCount, and the loop adds one to i. So the next loop you have
i = 4; ammoCount = 3
And the loop completes (because now i <= ammoCount is no longer true). And ammoCount is half what it was.
It looks like Input.GetButtonDown("Fire1") only returns true when the button has been pressed and released, so this is a "push the button and fire all ammo" scenario, rather than "fire until out of ammo or the key is released".
So you are wanting it to keep firing while there is any ammo left (hint hint).
You're decrementing the counter in a for loop. By decreasing the loop's upper bound and increasing the iteration variable i you basically halve the upper bound (ammoCount).
In this case, I see no reason why a loop would be necessary.
FixedUpdate is an event that happens in a fixed interval and should be used mainly for scenarios when you're dealing with physics. I don't think this is the case (some may argue that you're using raycast, but I'm not sure personally if that counts). You want to react on the users button press, so in my opinion you should use Update event instead.
GetButtonDown() returns true if the user pressed and released the button. What you should be doing instead is to use GetButton() to know if the button is held down. If yes, keep firing, if not, do nothing. Otherwise you have to press the button repeatedly, which again is probably not the desired behavior.
Let's now look at what happens when you press the button. Every time the user does the action, you start to decrease the ammo count until it reaches zero. Not sure what kind of game you're creating, but that doesn't sound right. What I'd expect instead would be to decrease the count while the user keeps the button pressed. In the Update event just check if ammo count is higher than zero and if yes, do the raycast check if it hit.
It would look like this:
void Update()
{
if(Input.GetButton("Fire1"))
{
if (ammoCount > 0)
{
ammoCount--;
// do raycast and so on
}
}
}
Hi I am new to Unity game engine and I am creating a 3d shooting game. In the level 1 I want to shoot 5 enemies in certain amount of time let's say 30sec. After completing level 1, I want to go to level 2 where my total enemies are 10 and want to kill it in 60sec and if fail there will be game over. I wrote some script for it, it works a little bit but it is not perfect because after starting level 2 the game become slow and after game over the level 2 restart again but not with the default value of 10 enemies, rather it starts from the no. which reaches at the time of game over. needs some idea and good logic and script for my game. here is my code.
public class Status : MonoBehaviour
{
public static int TotalZombies=5;
public static float timeLeft=25.0f;
// destry this game object.
Destroy (this.gameObject);
TotalZombies--;
}
and here is my other script where I am handling my levels and time etc.
using UnityEngine;
using System.Collections;
public class Generate : MonoBehaviour {
public GUIText Zombiesobject;
public string zombiesscore;
public GUIText countdown;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
zombiesscore = "Zombies Left: " + Status.TotalZombies.ToString ();
Zombiesobject.text = (zombiesscore);
Status.timeLeft -= Time.deltaTime;
if (Status.timeLeft <= 0.0f && Status.TotalZombies > 0)
{
countdown.text = "Game Over";
Application.LoadLevel(Application.loadedLevel);
}
else if (Status.timeLeft <= 10.0f && Status.TotalZombies > 0)
{
countdown.text = "Time left = " + (int)Status.timeLeft + " seconds" + " \n You are running out of time!";
}
else if (Status.timeLeft > 0.0f && Status.TotalZombies <= 0) {
countdown.text = "You win!";
Application.LoadLevel("level 2");
Status.TotalZombies=10;
Status.timeLeft=59.0f;
}
else
{
countdown.text = "Time left = " + (int)Status.timeLeft + " seconds";
}
}
}
Your problem is that you use static variables. static's are exactly like global variables. The only difference is that you need to access those through a Class name.
So what does that exactly mean? When you load your game, and when the class itself gets loaded the static variables are created and initialised. In your case it is TotalZombies set to 5 and timeLeft to 25f.
But those variables persist and never gets re-initialized as long as your game runs. Even if you do a Application.LoadLevel these variables and their values persists.
That means if you change those variables, and reload your level TotalZombies and timeLeft still have their last values.
Because of this i encourage to never use static variables. They easily introduce hard to spot bugs. Let's for assume a simple fix to your code.
You additionally add an initialization to your Start() Method. For example in your Status class you add.
void Start() {
TotalZombies = 5;
timeLeft = 25.0f;
}
In your case it could solve the problem completely, but you also could say this is just by accident or luck.
In Unity there don't exists an order in which Start() is called. It could for example still happen that the Start method in your Generate class is called first on loading a Scene. If you used Status.TotalZombies or Status.timeleft in Start to initialize something in Generate you still have your bug that your initializiation is wrong because it uses the variables from the previous run. The problem is Unity could sometimes first execute Status.Start() before Generate.Start() sometimes the other way around. That would lead to a bug that just sometimes occur and is extremely hard to debug.
If you knew the above you could also put your initialization in the Awake method. Because Awake methods will be called before any Start method. So this will be a better fix.
But there exists other problems. For example lets look into your Generate.Update() method. You for example directly do a Status.timeLeft -= Time.deltaTime; in your Update method. But when you for example have multiple GameObjects in your game that has the Generate Component it means timeLeft will be decreased multiple times in a single frame. If you have two Generate Components it means your time will run out twice as fast.
So even putting an initialization into Start or Awake can fix some bugs, but you still have different problems with statics
That is a reason why i encourage not to use static at all. So how do you fix also this problem? Instead of having static you should create Attributes of a class. And on top of that you should make all your attributes only set-able only from your own class. That also has an impact on other code. For example you could not reduce the timeLeft attribute anymore from Generate. That sounds like a disadvantage, but it forces to think you about how to change timeLeft correctly. In your case you do not really want that any class from everywhere can change timeLeft. It is a time that should be constantly reduced and it is just an error to reduce it multiple time. The result of that is. Your Status class should only change the timeLeft in the Update. The same goes for TotalZombies. It would be better to just have a method like IncrementTotalZombies and DecrementTotalZombies instead of doing Status.TotalZombies++ and so on. For example your Status class should now look like
public class Status : MonoBehaviour {
public int TotalZombies { get; private set; }
public float TimeLeft { get; private set; }
void Awake() {
this.TotalZombies = 5;
this.TimeLeft = 25f;
}
void Update() {
this.TimeLeft -= Time.deltaTime;
}
public void IncreaseTotalZombies() {
this.TotalZombies++;
}
public void DecreaseTotalZombies() {
if ( this.TotalZombies <= 0 ) {
throw new ApplicationException("Cannot decrease TotalZombies. Already 0. Possible Bug in your code.");
}
this.TotalZombies--;
}
}
Now the IncreaseTotalZombies or DecreaseTotalZombies sounds like overhead, but you can do a lot of extra checking here. For example check if the counter never gets smaller than zero. Because when it does, you have a bug somewhere in your code. For example Increasing your TotalZombies accidentally by two, or somewhere else decreasing it by two and so on. You also could implement a MaxTotalZombies attribute that ensures that you never get more Zombies as defined. And if that happens it will throw an Exception pointing it to your code directly where it happened.
It is also easier to identify bugs. Because Increasing it twice in a row looks wrong.
status.IncreaTotalZombies();
status.IncreaTotalZombies();
where following code can just look right
Status.TotalZombies += 2;
But if you do the above changes you will see your current Status.TotalZombies will not work anymore. You also have to change how to get an instance of your Status class. For this lets assume you create a GameObject in Unity named Status. Then in your Generate class you should add the following.
private Status status;
void Awake() {
this.status = GameObject.Find("Status").GetComponent<Status>();
}
Now you can replace the Status.TotalZombies++ and so on with status.IncreaseTotalZombies(). If you just want to get the values, you still just can write status.TimeLeft but setting the value status.TimeLeft -= Time.deltaTime will now throw an error. And you don't need to set it anymore because that is a behaviour that the Status class already handle in his Update Method.
Now additonally in your Generate class you had code like this.
Application.LoadLevel("level 2");
Status.TotalZombies=10;
Status.timeLeft=59.0f;
This didn't work as expected. Because when you call Application.LoadLevel() your new Scene gets called and the lines behind it was was never called. You could fix this be changing the order.
Status.TotalZombies=10;
Status.timeLeft=59.0f;
Application.LoadLevel("level 2");
Because your Status where static the value persists through loading. But the whole approach is still not really good. The Problem is you hardcode values in your code. And it seems you want different amount of Zombies and Time for every level. If you want that you can just add Attributes to your Status class that initialize your variables, And those variables are set-able through your Unity IDE. For example add the following attributes to your Status class.
public int _StartZombies = 5;
public float _StartTime = 25f;
If you add this to your Status class now in your IDE two TextBoxes will appear named Start Zombies and Start Time. In this boxes you can now enter how many Zombies or how much Start time your Level should have. The default values are 5 and 25 for those values. But those values didn't get applied on loading your level. To also apply those values when your level gets loaded change your Awake method to.
void Awake() {
this.TotalZombies = this._StartZombies;
this.TimeLeft = this._StartTime;
}
Now this.TotalZombies and this.TimeLeft always get the values that you have configured in your IDE. The only thing you now need to do is to write.
Application.LoadLevel("SomeLevel");
And you just can configure the amount of Zombies and time through your IDE! It also means you now have reusable Components. And you configure things where it belongs!
You also described that you want different conditions to loading a new Level. For example if a user is able to kill all the Zombies in a specific amount of time he directly jumps to Level 3 instead of Level 2 and so on. So how can you add this, without creating a lot of special classes?
At first you need a class on its own that just hold data. In your case you want a specific time and a definition which level gets loaded. So you could write something like this.
[System.Serializable]
public class LoadLevelData {
public float TimeLeft;
public string LoadLevel;
}
But in my opinion that logic belongs to the Status class, so what you now do is add the following to this class.
public LoadLevelData[] _NextLevels;
As soon as you add that to your code. In the Unity IDE you will see "Next Levels" with a "Cursor". You now can expand this cursor and a Size field will appear. You now can for example write 2 into it and it gives you Element 0 and Element 1. So Unity gives you the ability to create an Array of objects and you can create as many entries as you want from the IDE with any values that you want!
Now you could write a LoadNextLevel Method in such a way.
public void LoadNextLevel() {
foreach ( var level in this._NextLevels ) {
if ( level.TimeLeft > this.TimeLeft ) {
Application.LoadLevel(level.LoadLevel);
}
}
}
Now you can configure in the Unity IDE
Element 0:
Time Left -> 20
Next Level -> "Level 3"
Element 1:
Time Left -> 10
Next Level -> "Level 2"
You only need to call status.LoadNextLevel() when your game finished. And you can configure everything from the IDE. Also note. The order in which you fill your _NextLevel Array is important. In this case "Time Left" -> 20 must be before "10".