Resetting X position of image - c#

I've written code to reset the x position of the ufo when it moves off the screen. I have taken into consideration everything that could happen with the ufo.
Below is the code which says if the ufo travels off the screen, then the x position of the ufo is set back to 0, and it is killed off by means of false. I don't know what more i could add. The ufo travels off the screen, not to be seen ever again (poor thing) :( Any help?
if (ufo.alive == false)
{
Random random = new Random();
int randomNumber = random.Next(0, 100);
{
if (randomNumber == 1)
{
ufo.alive = true;
}
}
{
if (ufo.XPos > 1000)
{
// kill the ufo if it goes off th
ufo.alive = false;
ufo.XPos = 0;
}
}
//make a new one
// here you want to do it randomly .
// so
//int random = random number (you have to do some code to make a random number google it.
//if (random number == 1)
// ufo = new ufo();
// so if you tell it to make a random number between 1 and 1000, then every now and then, 1 will be the number it makes
// fo when it amkes one, and randomnumber is equal to 1, it will make a new ufo.
// i will let you figure out how to do the random bit.
// i guess haha
}
//if ufo is alive
// check for collision
if (ufo.alive == true)
{
// also, we need to make it move
ufo.XPos = ufo.XPos + 1;
if (MissileFired != null)
{
// if you miss, and the ufo carries on, it will go forever.
//so
Rectangle rectMissile = new Rectangle((int)MissileFired.GetPosition().X, (int)MissileFired.GetPosition().Y, MissileImg.Width, MissileImg.Height);
Rectangle rectUFO = new Rectangle(ufo.XPos, 30, UFOImage.Width, UFOImage.Height);
if (rectMissile.Intersects(rectUFO))
{
PlayerScore = PlayerScore + 1000;
// we needed to kill the missile, other wise it gives you a point for every time it goes through.
MissileFired = null;
//now only 1000 points for winning
ExplosionSoundInstance.Play();
ufo.alive = false;
}
}
}
}
EDITED CODE: above

the error you have is easy to debug and fix, you should learn to use a debugger, it is very useful.
here is a debugging tutorial

Basically the check to see if the position of the UFO > 1000 is never being run when the UFO is alive because it's inside the scope of the first IF statement.
if (ufo.alive == false)
{
if (ufo.XPos > 1000)
{
}
}
if the UFO is moving shouldn't you be checking for the position while it's alive?

I'm not sure that your brackets are well-formed. The open bracket { on line 5 doesn't make any sense there. Plus you dont' have a closing bracket at the very end.
Maybe the code is dying not the UFO?

Related

Making a for loop wait for user Input

I am programming a task in VR where the user hears an audio file with a certain amount of numbers and needs to press a key after that. If he did it right then he should press "r" and an audio file that is one number longer will be played. If he did it wrong he should press "w" and if it was his first wrong answer after a right answer an audio file will be played that has the same amount of numbers than before. If he did it wrong and already did it wrong right before that the amount of numbers is reduced by one.
In total there will be 10 audio files which is why I decided to use a for loop.
But now I don't know how I can make the for loop wait for the user input. I've also read that it is in general not god to use a for loop when you have to wait for a user input but I don't know what else I can do.
Here is my code so far:
IEnumerator playDSBtask()
{
bool secondWrong = false;
fillList();
for (int i = 0; i < 10; i = i + 1)
{
List<AudioSource> x = chooseList(score);
AudioSource myAudio = x[0];
float duration = myAudio.clip.length;
myAudio.GetComponent<AudioSource>();
myAudio.Play();
yield return new WaitForSeconds(duration + 5);
if (Input.GetKeyDown("r"))
{
score++;
secondWrong = false;
}
else if (Input.GetKeyDown("f") && secondWrong == false)
{
secondWrong = true;
}
else if (Input.GetKeyDown("f") && secondWrong == true)
{
score--;
}
x.Remove(myAudio);
}
}
But this won't work since the for loop will just continue if none of the if or else if statements are true and therefor executed.
Using a for loop in your case is totally fine since it is in a Coroutine where you yield inside of it so there will be no endless loop and therefore freeze of the main thread.
You could probably use WaitUntil something like
...
yield return new WaitForSeconds(duration + 5);
// Wait until any o the expected inputs is pressed
yield return WaitUntil(() => Input.GetKeyDown("r") || Input.GetKeyDown("f"));
// then check which one exactly
if (Input.GetKeyDown("r"))
{
...
alternatively this could also be done in a more generic way but also more error prone
...
yield return new WaitForSeconds(duration + 5);
// wait until something breaks out of the loop
while(true)
{
// check if any key was pressed
if(Input.anyKeyDown)
{
// handle r key
if (Input.GetKeyDown("r"))
{
score++;
secondWrong = false;
// important!
break;
}
else if (Input.GetKeyDown("f"))
{
if(!secondWrong)
{
secondWrong = true;
}
else
{
score--;
}
// Important!
break;
}
}
// if nothing called break wait a frame and check again
yield return null;
}
x.Remove(myAudio);
...
In general you might want to use KeyCode.F and KeyCode.R instead of strings.
From a UX perpective though you would either want to reduce the 5 seconds wait or/and show some kind of UI feedback for when user input is handled/awaited.

Unity: flash at frequency with variable on/off ratio

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);
}
}

Zoom/unzoom and points decimation

Situation:
I have one Chart and three ChartArea that are aligned in view, zoom, cursor through the AxisViewChanged method that act in this way:
if (e.Axis == ax1)
{
ax2.ScaleView.Size = ax1.ScaleView.Size;
ax2.ScaleView.Position = ax1.ScaleView.Position;
ax3.ScaleView.Size = ax1.ScaleView.Size;
ax3.ScaleView.Position = ax1.ScaleView.Position;
min = (int)ax1.ScaleView.ViewMinimum;
max = (int)ax1.ScaleView.ViewMaximum;
}
if (e.Axis == ax2)
{
....
And it works very well in both cases: when I zoom in/out or scroll.
Problem:
The problem is that my graph source is made by a lot of points, in the worst case we talk about 3'600'000 samples. With this amount of samples, when I move around points with cursor and try to show a tooltip with the values, the interaction quality collapse and becomes unusables (even having set Fast Line).
So I tried to implement a simple decimation algorithm to reducethe number of showed points:
void draw_graph(int start, int end)
{
double fract_value = 0;
int int_value = 0;
int num_saples_selected = end - start;
if(num_saples_selected <= MAX_GRAPH_NUM_SAMPLES)
fill_graphs_point(0, end, 1);
else
{
fract_value = ((double)num_saples_selected) / ((double)MAX_GRAPH_NUM_SAMPLES);
int_value = (int)fract_value;
if (fract_value > int_value)
int_value++;
fill_graphs_point(0, end, int_value);
}
}
void fill_graphs_point(int start, int end, int step)
{
int i = 0;
for (i = start; i < end; i=i+step)
{
dlChart.Series[SERIES_VOLTAGE].Points.AddXY(timestamps_array[i], voltage_array[i]);
dlChart.Series[SERIES_CURRENT].Points.AddXY(timestamps_array[i], current_array[i]);
dlChart.Series[SERIES_ENERGY].Points.AddXY(timestamps_array[i], energy_array[i]);
// I will use this to came back to real position of the initial array
decimation_positions.Add(i);
}
}
Assuminig I had a good idea with this method to reduce the points number, I do not know where to put the call to the function "draw_graph". If I put it in the AxisViewChanged method it will call my method also when I scroll (horizontally) my graph and this is not what I want. I want to call my method only on zoom and unzoom event.
Expected behavior: in the first view (without any zoom) the graph has to show an "idea" of the trend of the graph. Then for every selection/(un)zoom I want to call my function to check if the points number of selected portion will fit in my window size that is MAX_GRAPH_NUM_SAMPLES(=10000).
Hope someone can help me. Whatever kind of suggestion will be appreciated. Thanks in advance.
I forgot to say that strangely the problem appeared when I zoomed in more than one time. At some point also the PC fan started. I resolved disabling the library zoom and implement my self a sort of zoom (a little bit more simple). The solution is in this method and the method I write in the question:
private void dlChart_SelectionRangeChange(object sender, CursorEventArgs e)
{
double startSelection = e.NewSelectionStart;
double endSelection = e.NewSelectionEnd;
// Allow the selection to disappear
reset_selection();
try
{
// before convert point from "showed point" to "real point" I check the limits
if (
(startSelection >= 0) && (startSelection <= decimation_positions.Count()) &&
(endSelection >= 0) && (endSelection <= decimation_positions.Count()) &&
(endSelection != startSelection)
)
{
// convert
startSelection = decimation_positions[(int)startSelection];
endSelection = decimation_positions[(int)endSelection];
// check for reverse zoom (from right to left)
if (startSelection > endSelection)
{
double tmp = startSelection;
startSelection = endSelection;
endSelection = tmp;
}
// clean the series
this.reset_series();
// draw the selected range
draw_graph((int)startSelection, (int)endSelection);
}
}catch(ArgumentOutOfRangeException ex)
{
// todo
}
//System.Console.WriteLine("SelSTART = "+e.NewSelectionStart+" SelEND = "+e.NewSelectionEnd);
}

How to count and display points in a pong game within C#?

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).

issue with Playerprefs

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.

Categories