I have a "datapack" hidden in each of my levels, the system remembers per level if the artifact has been taken and if so it disables the artifact when a player replays it but i also want to keep track of the total amount taken, the system almost works there's just 1 small issue:
When i finish the level with the first datapack, in the menu it says: 1, this is correct.
but when i do it with the 2nd, it says 3, with the 3rd it says 4, 4th says 5 etc
i don't understand why and thus can't fix it
here's the code:
function OnTriggerEnter(other : Collider) {
if(other.CompareTag("DataPacket")){
pickedUpDataPacket = 1;
Destroy(other.gameObject);
gameObject.Find("DatapackFound").guiText.enabled = true;
yield WaitForSeconds (1.5);
gameObject.Find("DatapackFound").guiText.enabled = false;
}
if(other.CompareTag("Finish") && pickedUpDataPacket == 1){
PlayerPrefs.SetInt("DataPackLevel" + levelindex.ToString(), 1);
//if(!PlayerPrefs.HasKey("totalDatapacks")){
//PlayerPrefs.SetInt("totalDatapacks", 1);
//} else {
PlayerPrefs.SetInt("totalDatapacks", (PlayerPrefs.GetInt("totalDatapacks")+1));
}
}
//}
i already commented a part out, i believe this was also part of the issue.
and part of a 2nd script:
if(datapacktotal){
if(PlayerPrefs.GetInt("totalDatapacks") > 0){
findText.text = "Collected:" + PlayerPrefs.GetInt ("totalDatapacks");
}
Thanks in advance :)
From the described behaviour it seems that, in some occasions, the collision is triggered multiple times, before the datapack is being destroied. As in level 2, where the counter passes from 1 to 3.
My though is that your collider has multiple contact points, so that - if you touches n of them - the collision is triggered n times.
I would try a simple experiment, just use a flag to determine if that's the first time you "touch" the collider; then, you'll update the PlayerPref just in that case:
function OnTriggerEnter (other : Colliderstrong text){
if (collisionAlreadyConsidered) return;
collisionAlreadyConsidered = true;
// your code here...
}
function Update(){
collisionAlreadyConsidered = false;
}
, where collisionAlreadyConsidered is a global variable.
Related
I have a joystick display picture for my game. Currently, when the player touches the screen the image disappears and when the player is not touching the screen, it reappears. I wrote that using an if else statement.
if (indicator.inputIndicator.x != 0)
{
joystick.SetActive(false);
}
else
{
joystick.SetActive(true);
}
The problem is, I want the image to reappear after some time like 2 seconds. I want to delay the "else", but I do not want to use a coroutine. I want "else" to work after 2 seconds since the player takes his hand off the screen but I couldn't figure out how to do it. any help will be great.
Setting a timer is a pretty common problem you have to solve in Unity. One basic approach is to have a variable that you add Time.deltaTime every update. That way you can tell how long it has been since some condition was met.
Every Update iteration that meets the condition, add Time.deltaTime to the variable. If at some point the condition fails, reset the variable to 0. Then you can just base your joystick.SetActive() call on the value of your variable.
For example, your script might become:
float thresholdTimeToShowPrompt = 2;
// By starting at the threshold, the image is hidden at the start until a touch
float timeSincePlayerTouch = 2;
void Update()
{
// Rather than calling SetActive directly, just update the timer
if (indicator.inputIndicator.x != 0)
{
timeSincePlayerTouch = 0;
}
else
{
timeSincePlayerTouch += Time.deltaTime;
}
// Now we can base visibility on the time since the last user touch
bool shouldShowIcon = timeSincePlayerTouch >= thresholdTimeToShowPrompt;
// Only call SetActive when needed, in case of overhead
if (shouldShowIcon && !joystick.activeSelf)
{
joystick.SetActive(true);
}
else if (!shouldShowIcon && joystick.activeSelf)
{
joystick.SetActive(false);
}
}
I want to be able to flash stuff at a certain frequency. For an example, let's say 2Hz. I also want to be able to specify a ratio, where I can have the thing displayed for let's say 2/3 of the cycle and have it hidden for 1/3, so the ratio would be 2:1. It's a wild bunch of flashing, so I Need to stay flexible in the way I do it. There might be some flashing with a ratio of 3:5 and a frequency of 2Hz, and some other flashing at 4Hz with ratio 1:1, and so on.
Also, I need to be able to flash in sync. So if one object is flashing already and I start flashing another one, they need to be in sync (or rather their cycles need to be in sync, the flashing may vary as the ratio may be different). But if at the same frequency, they need to "turn on" at the same time, even if their ratios are different. Also, they all need to turn on at the same time the slowest turns on.
My current approach: I have a GameObject FlashCycle, that essentially in it's update method calculates a progress for the 3 frequency's I have (2Hz, 4Hz and 8Hz).
float time = Time.time;
twoHerzProgress = (time % twoHerzInSeconds) / twoHerzInSeconds;
fourHerzProgress = (time % fourHerzInSeconds) / fourHerzInSeconds;
eightHerzProgress = (time % eightHerzInSeconds) / eightHerzInSeconds;
I have tried different times, but that didn't really matter so let's just stick to that one if you don't think it's a bad idea!
Now, whenever I want to flash an object, in it's own Update() I do this:
switch (flashRate.herz)
{
case FlashRateInterval.twoHerz:
show = flashCycle.oneHerzProgress <= onTimePercentage;
case FlashRateInterval.fourHerz:
show =flashCycle.twoHerzProgress <= onTimePercentage;
case FlashRateInterval.eightHerz:
show =flashCycle.fourHerzProgress <= onTimePercentage;
default:
show =true;
}
and then just continue and have the object displayed if show == true.
Unfortunately this doesn't flash the objects at a nice smooth and regular interval. I measured the 2Hz interval and got differences in the ratio of up to 48ms, and though it seems like not much it really makes a difference on the screen.
So the question boils down to: How can I get quick, reqular flashes while maintaining the flexibility (ratio and frequency wise) and have a syncronized flash?
Thanks for your help!
You could use Coroutines and WaitForSeconds to achieve that
// onRatio and offRatio are "optional" parameters
// If not provided, they will simply have their default value 1
IEnumerator Flash(float frequency ,float onRatio = 1, float offRatio = 1)
{
float cycleDuration = 1.0f / frequency;
float onDuration = (onRatio/ (onRatio + offRatio)) * cycleDuration;
float offDuration = (offRatio/ (onRatio + offRatio)) * cycleDuration;
while(true)
{
show = true;
yield return new WatForSeconds(onDuration);
show = false;
yield return new WatForSeconds(offDuration);
}
}
so you can call it either with a frequency e.g. 8Hz
StartCoroutine(Flash(8.0f));
this is actually equal to any call where you set onRatio = offRatio e.g.
StartCoroutine(Flash(8.0f, onRatio = 1, offRatio = 1));
StartCoroutine(Flash(8.0f, onRatio = 2, offRatio = 2));
....
or with a frequency and ratios e.g. 1(on):2(off) with 8Hz
StartCoroutine(Flash(8.0f, onRatio = 1, offRatio = 2));
With this setup the Coroutine runs "forever" in the while(true)-loop. So, don't forget before you start a new Coroutine with different parameters to first stop all routines with
StopAllCoroutines();
Now if you want to change that dynamically in an Update method, you would have to add some controll flags and additional variables in roder to make sure a new Coroutine is only called when something changed:
FlashRateInterval currentInterval;
float currentOnRatio = -1;
float currentOffRatio = -1;
void Update()
{
// if nothing changed do nothing
if(flashRate.herz == currentInterval
//todo && Mathf.Approximately(<yourOnRatio>, currentOnRatio)
//todo && Mathf.Approximately(<yourOffRatio>, currentOffRatio)
) return;
StopAllCoroutines();
currentInterval = flashRate.herz;
//todo currentOnRatio = <yourOnRatio>;
//todo currentOffRatio = <yourOffRatio>;
switch (flashRate.herz)
{
case FlashRateInterval.twoHerz:
StartCoroutine(2.0f);
//todo StartCoroutine(2.0f, onRatio = <yournRatio>, offRatio = <yourOffRatio>);
case FlashRateInterval.fourHerz:
StartCoroutine(4.0f);
//todo StartCoroutine(4.0f, onRatio = <yournRatio>, offRatio = <yourOffRatio>);
case FlashRateInterval.eightHerz:
StartCoroutine(8.0f);
//todo StartCoroutine(8.0f, onRatio = <yournRatio>, offRatio = <yourOffRatio>);
default:
show =true;
}
}
Notes:
I dont know your FlashRateInterval but if you need to use it for some reason you could make it like
public enum FlashRateInterval
{
AllwaysOn,
twoHerz = 2,
fourHerz = 4,
eightHerz = 8
}
in order to directly use the correct values.
I would call a frequency variable flashRate.herz. You also wouldn't call a size value cube.meters. I'ld recommend to rename it to flashRate.frequency.
To archieve that syncing you would somehow need access to all Behaviours and compare their values (so I'ld say some static List<YourBehavior>) and than e.g. in the Coroutine wait until all bools are e.g. set to true before continuing with your own one. For that you would need an additional bool since it is possible that show is true permanently on one component.
public bool isBlinking;
IEnumerator Flash(float frequency ,float onRatio = 1, float offRatio = 1)
{
//todo: You'll have to set this false when not blinking -> in Update
isBlinking = true;
float cycleDuration = 1.0f / frequency;
float onDuration = (onRatio/ (onRatio + offRatio)) * cycleDuration;
float offDuration = (offRatio/ (onRatio + offRatio)) * cycleDuration;
// SYNC AT START
show = false;
// wait until all show get false
foreach(var component in FindObjectsOfType<YOUR_COMPONENT>())
{
// skip checking this component
if(component == this) continue;
// if the component is not running a coroutine skip
if(!component.isBlinking) continue;
// Now wait until show gets false
while(component.show)
{
// WaitUntilEndOfFrame makes it possible
// for us to check the value again already before
// the next frame
yield return new WaitForEndOfFrame;
}
}
// => this line is reached when all show are false
// Now lets just do the same but this time wating for true
// wait until all show get false
foreach(var component in FindObjectsOfType<YOUR_COMPONENT>())
{
// skip checking this component
if(component == this) continue;
// if the component is not running a coroutine skip
if(!component.isBlinking) continue;
// Now wait until show gets false
while(!component.show)
{
// WaitUntilEndOfFrame makes it possible
// for us to check the value again already before
// the next frame
yield return new WaitForEndOfFrame;
}
}
// this line is reached when all show are getting true again => begin of loop
while(true)
{
.........
Instead of using FindObjectsOfType<YOUR_COMPONENT>() which is kind of slow you could also do something like
public static List<YOUR_COMPONENT> Components = new List<YOUR_COMPONENT>();
private void Awake()
{
if(!Components.Contains(this)){
Components.Add(this);
}
}
so you also get currently disabled components and objects
You got some diferences because you are doing everything in an Update() cycle with <= condition. On slower/faster machines you will have more/less differences because the frame's duration will never be equal to your frequency.
Try doing everything in a Corotine: unity coroutine docs
//bad code below but i think its more understandable like this
IEnumerator Flash()
{
while(true)
{
BlinkOn();
Sync();//sync here another cicle if you want to sync when on starts
yield return new WaitForSeconds(yourDuration);// yourDuration*multiplier/something+0.5f....ecc
BlinkOff()
Sync();//sync here another cicle if you want to sync when of starts
yield return new WaitForSeconds(yourDuration);
}
}
I'm having trouble counting the points of my AI Paddle Player and the user Paddle Player each time they miss colliding the ball. I tried using a boolean method and loops to count points, but to no avail as the program keeps on stating that the label in which my points are to be displayed, is "stack overflowed" due to an infinite loop which I am unable to recognize. So can you please help me.
Here is my code:
private void resetShiruken()
{
if ((reset == false) && (AIPoints < MAXPoints))
{
picShiruken.Location = new Point(ClientSize.Width / 2 - picShiruken.Width / 2, ClientSize.Height / 2 - picShiruken.Height / 2); //puts the picShiruken Picturebox in the middle anytime the picPlayerPaddle or picAIPaddle miss and is useful for counting the points of the computer and the player.
reset = true;
}
TheWinner();
}
private void TheWinner()
{
while (reset == true)
{
AIPoints += 1;
reset = false;
}
if (AIPoints >= AIPoints)
{
lblAIPoints.Text = AIPoints.ToString();
}
resetShiruken();
}
. resetShiruken() is a method that resets the ball to the middle each time any player misses.
. TheWinner() is a method that determines the winner of the game after either one of the players reach 5 points.
Thank You very Much,
Kai
As comments says:
you have recursive call of methods which lasts forever. resetShiruken calls TheWinner which calls resetShiruken and so on....
private void resetShiruken()
{
if ((reset == false) && (AIPoints < MAXPoints))
{
picShiruken.Location = new Point(ClientSize.Width / 2 - picShiruken.Width / 2, ClientSize.Height / 2 - picShiruken.Height / 2); //puts the picShiruken Picturebox in the middle anytime the picPlayerPaddle or picAIPaddle miss and is useful for counting the points of the computer and the player.
reset = true;
}
TheWinner();
}
private void TheWinner()
{
while (reset == true)
{
AIPoints += 1;
reset = false;
}
if (AIPoints >= AIPoints)
{
lblAIPoints.Text = AIPoints.ToString();
}
//resetShiruken(); <--- if you get rid of that call, should be fine.
}
Please note if you need to do reset after show the Winner, you should add additional parameter to reset method, or create separated method for post-winning reset.
Regular answer:
Your functions keep calling each other. Thats probably where the overflow is coming from.
From the functions given I'm unable to deduce exactly what your program flow would look like so I can't provide you with a fix which will do what you expect it to do.
To get rid of the overflow however simply remove either the call to "TheWinner" from "resetShiruken" or remove the call to "resetShiruken" from "TheWineer".
I suspect you should remove the latter.
Code style:
You're mixing upercase and lowercase function names which is a bad habit.
The default case for method names is "PascalCase" in c# (as depicted by the documentation).
I have a multiple choice quiz game, and i want to make score. however i want to connect the score with my time renaming therefore if user chooses the right answer he get's 10 points but i also want to multiply that number with the time remaining. so if the time reaches 0 the game ends if he finishes fast he get's higher score, and if he finishes so slow he will lose score. to to summarize i want the time remaining and the points to be connected together.
What i have tried is, on each correct answer i pass in a point and i made it score and also i have a high score, what i can't figure out is how to connect it
to the time remaining?
Here's what i have
public void AnswerButtonClick(bool isCorrect)
{
if (isCorrect)
{
Debug.Log("I'm Correct");
theAnswerIsCorrect = true;
playerScore += currentRoundData.pointAddedForCorrectAnswer;
scoreDisplayText.text = "Score: " + playerScore.ToString();
}
else
theAnswerIsCorrect = false;
// Do we still have questions?
if (questionPool.Length > questionIndex + 1)
{
//questionIndex++;
UpdateQuestionIndex();
StartCoroutine(DelayTime(3));
// ShowQuestion();
}
else
{
EndRound();
}
}
This just add's point if i get the correct answer, what i need is how do also calculate the time remaining with my points.
Here's the time remaining
// Update is called once per frame
void Update ()
{
if (isRoundActive)
{
timerRemaing -= Time.deltaTime;
UpdateTimeRemainingDisplay();
if (timerRemaing <= 0)
EndRound();
}
}
so basically everytime my time is getting lower i lose more score.
Thank you
I think it would be best to start a timer at the beginning of each question, and then create some algorithm for the points. I hope I understood your question right
Why you don't multiply the variable timerRemaing with a Question Score variable and add it to the Player Score variable?
That's what u want right?
Ive been having some issues programming in unity with c#. I am trying to request an interstitial AD when i start the game so i can show it when the player has died 5 times. The issue is that when i get to the 5th death the ad won't show. and when I try requesting the ad when the level starts it gets laggy and still doesn't show.
This is my code. It seems right.
void Update(){
if (clicks >= 5) {
RequestInterstitial();
Debug.Log("Got 5 clicks");
if (interstitial.IsLoaded()){
Debug.Log("interstitial loaded");
interstitial.Show();
clicks = 0;
}
}
}
EDIT: After modifying my code, I now get the error:
NullReferenceException: Object reference not set to an instance of an object ADS.ADMobsGameplay.Update () (at Assets/Scripts/ADS/ADMobsGameplay.cs:28)
Line 28 corresponds to if (interstitial.IsLoaded()){ in the following code:
void Update(){
if (clicks >= 5) {
Debug.Log("Got 5 clicks");
if (interstitial.IsLoaded()){
Debug.Log("interstitial loaded");
interstitial.Show();
clicks = 0;
}else{
RequestInterstitial();
}
}
}
You are Requesting the Interstitial over and over again. Just run the RequestInterstitial method at the beginning of the program (and make sure it only runs the method once).
Then, in whatever method you increment clicks, simply add something like this to the end:
int clicksRequired = 5;
int currentClicksRequired = 5;
if(clicks > currentClicksRequired){
Debug.Log("Got 5 Clicks");
if (interstitial.IsLoaded()){
Debug.Log("interstitial loaded");
interstitial.Show();
RequestInterstitial();
clicks = 0;
currentClicksRequired = clicksRequired;
}else{
Debug.Log("interstitial not loaded, skipping to next click");
currentClickRequired ++;
}
}
Once clicks reaches 5, it will check if the interstitial has been loaded. If so, it shows the interstitial, requests another interstitial, resets clicks at 0, and then moves on. If the interstitial is not loaded yet, it makes you wait till the 6th click, or the 7th, etc, until the interstitial is loaded.
First of all, you shown NullReferenceException error in yours comment: this is first issue that can provide lags. Second - it is really weird to put some counting logic into Update function - update works every frame - so it`s second possible lag issue. Third Debug.Log function is not a lightweight, into Update function it will be third possible lag issue