I'm making an Asteroid Destroyer game in which you get score as time passes by (the game becomes progressively more difficult), but also when you hit an asteroid. I have written a method inside the Game1 class to calculate the amount of score that should be added by the end of the Update method.
The code is as follows:
private float CountScore(GameTime gameTime, List<Asteroid> asteroids)
{
float addScore = 0;
foreach (Asteroid asteroid in asteroids)
{
if (asteroid.Killed)
{
addScore += 100;
}
}
if (spaceship1.Alive)
{
addScore += 80 * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
return addScore;
}
The method is called like this in the Update method:
score += CountScore(gameTime, asteroids);
The part of the code that adds score as time passes works fine. The problem is that when I destroy an asteroid, instead of getting 100 more score points, sometimes I get 200, sometimes I get 2000 and sometimes I get 5000. I don't understand what is happening, since only asteroids that are killed by the player get the property Killed = true;, so I don't really think it is adding 100 per asteroid on screen.
Does anybody know what could cause this? If it's needed I can post more code, but I didn't want to write too much here for the sake of readability.
Edit:
Adding asteroid.Killed = false; just after adding the score worked. However I still don't understand what was the issue, since it looks like it was looping through the same asteroid more than once.
I would recommend that the logic for adding to the score when an asteroid is killed, is done at the same time as the logic for killing an asteroid.
It is likely that you are iterating through the list of asteroids already when processing the hit detection, and thus it would improve performance if the logic was done at the same time, instead of having another foreach loop, as demonstrated in your code.
Further more your addScore += 80 * (float)gameTime.ElapsedGameTime.TotalSeconds; is unreliable, a player with a higher FPS will have an unfair advantage. I recommend using the deltaTime when adding to the total score during the games progression.
Related
After I added this one for statement, Unity crashes, I've reopened and reran it several times, but it still crashes. Here is the for statement:
for (i = 0; i < 20; i++)
{
player.transform.position += Vector3.right * -1;
i = 0;
}
Your code results in an infinite loop. If you want to have a repeating function with a delay, you can use Coroutine. The YieldInstruction (yield return new WaitForSeconds(waitTime) in the unity document example) determine the amount of time you wish to wait. The sample code in the document prints infinitely with a 2 second delay; however, rewriting the condition of the while loop is one of the ways that helps you determine when a coroutine ends.
Another way to acheive it is using async/await. However, the instructions you can use for await are not as rich as yield instructions. Moreover, the last time I checked, a while true code with a print statment, keeps printing even when unity is not in play mode anymore.
Your loop is running infinitely. On each iteration you set your counter variable back to 0, hence it never stops and keeps decrementing your player position. Have you tried forcing the loop to quit at one point to see if your program still crashes?
Well, of course you program crashes, you have an infinite loop due to always resetting
i = 0;
which basically makes it equivalent to
while(true)
{
// move
}
since you never exit this loop you never allow Unity to move on in its execution -> The app and the entire Unity editor which runs in the same single Unity main thread completely freezes.
Since you say you want this to happen infinitely anyway, what you rather want is move this into Update which is called once every frame and then use Tim.deltaTime to convert your fixed value per frame into a value per second
// Speed in Units per second
// adjust this via the Inspector
[SerializeField] private float speed = 1;
private void Update()
{
player.transform.position += Vector3.left * speed * Time.deltaTime;
}
I'm learning C# and unity, one of my recent issue is having my OnTriggerEnter2D() function work. After a lot of test i managed to resolve my issues but i'm not sure at 100% if i understood.
So basically i'm checking if an object touch another one and if this is not the case i rotate the object :
for (var i = 0; i < 4; i++)
{
if (!door.connected)
{
go.transform.Rotate(0f, 0f, 90f, Space.Self);
}
}
This one didnt work and after a lot of debug i decided to use a coroutine with a 0.5s delay right after the go.transform.Rotate(0f, 0f, 90f, Space.Self); and this work.
If i'm right this work because the delay let enought time for my OnTriggerEnter2D() function to detect the collision or this have nothing to do with the OnTriggerEnter2D() ? If this is the case, everytime i'm going to check for collision i'll need to use a coroutine ?
Edit 1 : I have this code in another script
public void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Door"))
{
this.connected = true;
}
}
So the previous loop will indeed make my object rotate but never set my public variable "connected" to true, like the collision never occur, except for the one spawning at the right spot. But when i use a coroutine it does work, why ?
You definitely don't need a coroutine to rotate an object in OnTriggerEnter2D.
I imagine what's happening, based on the code snippet you've shown us, is that it's almost instantly rotating the object 90 degrees 4 times (that's a full 360 degree rotation) and it therefore looks like it's not moving at all.
Edit (in response to Edit 1):
The reason your code works when you add a coroutine is because the four 90 degree rotations don't all happen in a single frame then. What happens is:
It rotates the door 90 degrees then kicks off a coroutine which last a few frames.
Since the door has rotated between frames, the other script gets the chance to detect any collisions
If it detects one with the door, it can set the connected flag to
true which prevents go.transform.Rotate from being called in any subsequent
frames.
Compare this to the sequence without the coroutine which would be:
It rotates the door 90 degrees, then it does it again, then again, then again... then it moves on to the next frame.
Since, as far as all other GameObjects are concerned, the door has
NOT moved between frames no new collisions are detected and your
connected flag remains false.
The solution to your problem isn't using a coroutine, though. The solution is to remove your for loop. In the Update method, if the door is not connected rotate it a single degree step. On the next update, if the door still isn't connected, do it again. Eventually, within 4 updates, it will have become connected and it will no longer rotate every frame.
If i'm right this work because the delay let enought time for my OnTriggerEnter2D() function to detect the collision or this have nothing to do with the OnTriggerEnter2D() ?
No it has nothing to do with OnTriggerEnter2D. As you can see your collision is working and triggered.
What happens is that you rotate 4 times about 90° which sums up to 360° within one frame => You will note no difference.
A Coroutine in Unity is basically an IEnumerator. Every IEnumerator at some point uses the yield keyword. So what a Coroutine in Unity does is basically:
Each frame call MoveNext on the given IEnumerator until it either terminates or reaches the next yield statement. (There are some special cases like e.g. WaitUntilEndOfFrame...)
So translated you could say the yield means something like
"Interrupt" this routine
Render the current frame
Continue the routine from here in the next frame
Btw. what you are doing via e.g.
yield return new WaitForseconds(3f);
basically equals doing something like
var timePassed = 0f;
while(timePassed < 3f)
{
yield return null;
timePassed += Time.deltaTime;
}
So: No for Collision detection you do not need to use a Coroutine; BUT whenever you want to "stretch" some codes execution over multiple frames you should use one.
What Unity does internally when you declare OnTriggerEnter2D as an IEnumerator basically equals something like
private void OnTriggerEnter2D(Collider2D other)
{
StartCoroutine(RotationAnimation());
}
private IEnumerator RotationAnimation()
{
// ...
}
so you still have to be very careful that you don't get concurrent routines!
If what you want would actually be a smooth rotation you would e.g. use
private bool alreadyRotating;
// adjust this via the Inspector
[SerializeField] float rotationAnglePerSecond = 90f;
private void OnTriggerEnter2D(Collider2D other)
{
// avoid concurrent routines
if(!alreadyRotating) StartCoroutine(RotationAnimation());
}
private IEnumerator RotationAnimation()
{
// block concurrent routine
if(alreadyRotating) yield break;
alreadyRotating = true;
var initialOrientation = go.transform.rotation;;
// rotate until you reach 360°
var rotated = 0f;
while(rotated < 360f)
{
// get amount of rotation within this frame
var rotate = Time.deltaTime * rotationAnglePerSecond;
go.transform.Rotate(0f, 0f, 90f);
rotated += rotate;
yield return null;
}
// reset to initial orientation
go.transform.rotation = initialOrientation;
// when done allow the next routine
alreadyRotating = false;
}
My main problem is, I'm coding a movement simulaltor for a game,
What I want to do is, to check if the character got stuck and its position is not changing.
So What I thought is something like this:
If Me.Location3D is the same for X Amount of time -> Me.SetLocation(Location3D.X = Location3D.X +1, Location3D.Y = Location3D.Y +1)
public void Pulse(float dt)
{
Skandia.Update();
Me = Skandia.Me;
if (Me == null)
{
return;
}
//This void repeats every frame of the game
//functions should take place below
}
If anyone got an idea how to check the elapsed time, that'd be great.
Sadly I havent found such a specific thing on stackoverflow yet, just saying cause the topic sound pretty general.
greetings
If you are working a game, you should use a main loop.
The solution is pretty simple.
Each time you get Inside this method, check the time elapsed between your actions.
You should read this link: https://msdn.microsoft.com/en-us/library/bb203873.aspx
Variable-Step Game Loops
A variable-step game calls its Update and Draw methods in a continuous loop without regard to the TargetElapsedTime. Setting Game.IsFixedTimeStep to false causes a Game to use a variable-step game loop.
Animation and Timing
For operations that require precise timing, such as animation, the type of game loop your game uses (fixed-step or variable-step) is important.
Using a fixed step allows game logic to use the TargetElapsedTime as its basic unit of time and assume that Update will be called at that interval. Using a variable step requires the game logic and animation code to be based on ElapsedGameTime to ensure smooth gameplay. Because the Update method is called immediately after the previous frame is drawn, the time between calls to Update can vary. Without taking the time between calls into account, the game would seem to speed up and slow down. The time elapsed between calls to the Update method is available in the Update method's gameTime parameter. You can reset the elapsed times by calling ResetElapsedTime.
When using a variable-step game loop, you should express rates—such as the distance a sprite moves—in game units per millisecond (ms). The amount a sprite moves in any given update can then be calculated as the rate of the sprite times the elapsed time. Using this approach to calculate the distance the sprite moved ensures that the sprite will move consistently if the speed of the game or computer varies.
I would usually comment with a short answer like this but I'm short 3 reputation.
My solution is just track it with a variable and reset when needed, in your case when character moves.
Double elapsedTimeInMS = 0;
private void YourTimer(object sender, EventArgs e)
{
//Every 10 ms
elapsedTimeInMS += 10;
}
private void btnClick(object sender, EventArgs e)
{
//In your case the character moves
MessageBox.Show(elapsedTimeInMS + "");
elapsedTimeInMS = 0;
}
After edit
You would need to implement a check in your timer to see if elapsed time isn't getting too high.
I have made some collision if statements, but they didn't work.
birdbox3.X += 5;
birdbox3.Y -= 5;
if (birdbox3.Intersects(Banner1)) {
birdbox3.Y += 10;
}
else if (birdbox3.Intersects(Banner2)) {
birdbox3.Y = -birdbox3.Y; }
So if we take the first statement, the box is initially on the left down corner. According to my code, it would ideally be going right+up in my game, once it hits the banner on the extreme top, it should go down+right, thus both the X and the Y positions will increase. But what happens is that is starts bouncing really fast, after debugging I realised it was forever stuck in the first if, it's almost as if it collides 2 times, reverting Y to its initial movement, colliding a 3rd time, and doing the process over and over.
Which brings us to the question, how can I make the Update code run a tad bit slower, or after half a second or such passes, so when it runs, it doesn't misinterpret collisions? Thanks a lot!
If you look at your Update method definition (If you have a regular GameComponent or DrawableGameComponent) , you'll notice you have access to the GameTime:
public override void Update(GameTime gameTime).
You can use that variable to trigger the collision only after a set amount of time. To do that, surround everything by a big if which checks the time elapsed:
private int elapsedTime = 0; //Declared at class level
public override void Update(GameTime gameTime)
{
// We add the time spent since the last time Update was run
elapsedTime += gameTime.ElapsedGameTime.TotalMilliseconds;
if (elapsedTime > 500) // If last time is more than 500ms ago
{
elapsedTime = 0;
birdbox3.X += 5;
birdbox3.Y -= 5;
if (birdbox3.Intersects(Banner1))
{
birdbox3.Y += 10;
}
else if (birdbox3.Intersects(Banner2))
{
birdbox3.Y = -birdbox3.Y;
}
}
}
In that case, the collision code is run only once every 500 milliseconds.
We need to add the elapsed time each time Update is run. Then, when we exceed the threshold, we run the collision bit and reset the timer to 0.
We need the local variable because ElapsedGameTime only knows how much time was spent since the last Update. To carry that number on multiple updates, we need to store it somewhere.
I also encourage you to take a look at MSDN's Starter Kit: Platformer. You can find the download links at the bottom. There a lot of info that can be take from that, like effective screen/input management or a basic physics engine. In my opinion, it's the best thing from the official documentation.
I am working on a system, which is distributing Commands from a HashSet to a Player. I want to distribute a Command to the Player, who is closest to the Command.
void AssignCommand(Player player, HashSet<Command> commandList) {
//Player assigned;
float min = float.MaxValue;
float dist;
foreach(Command command in commandList) {
dist = Vector3.Distance(command.Position, player.Position);
if(dist < min) {
//check if command already assigned to another player
assigned = command.assigned;
if(assigned != null) {
//reassign when distance is smaller
if(dist < command.Distance(assigned)) {
//mark previously assigned command as unassigned
if(player.activeCommand != null) player.activeCommand.assigned = null;
player.activeCommand = command;
command.assigned = player;
min = dist;
assigned.activeCommand = null;
AssignCommand(assigned, commandList);
}
}
else {
if(player.activeCommand != null) player.activeCommand.assigned = null;
player.activeCommand = command;
command.assigned = player;
min = dist;
}
}
}
}
My problem with this code is that if there are a lot of commands in the HashSet it takes quite a while and the framerate drops from ~60 to about ~30 fps on my machine. This is no surprise, because the Vector3.Distance method is just called for (every player) * (every command), which is way too much. I am looking now for a way to reduce the number of calls somehow to improve the performance. Any ideas here?
I also tried running this code in a different Thread, but I gave up, because this is changing and using too many Thread Unsafe values. My latest try brought me to the check if assigned != null throwing an error for comparing.
I would be very grateful for any hints either improving the overall speed of this code or how to run this in a ThreadPool. If required I can also post my JobClass I created for the Thread attempt.
All the threading solutions and optimizations are fine, but the biggest thing you want to keep in mind (for this and for the future) is: Do not use Vector3.Distance or Vector3.magnitude for this, ever. They are inefficient.
Instead, use Vector3.sqrMagnitude, which is the same thing (for distance comparison), without the sqrt (the most expensive part).
Another optimization is to write your own (square) distance calculation, throwing out the y value if you know you don't care about vertical distances. My distance comparison code was slow, so I tested this pretty carefully and found this is the fastest way (especially if you don't care about vertical positions): (EDIT: this was fastest in 2015. Test yourself for the fastest code on modern Unity.)
tempPosition = enemy.transform.position; // declared outside the loop, but AFAIK that shouldn't make any difference
float xD = targetPosition.x - tempPosition.x;
float yD = targetPosition.y - tempPosition.y; // optional
float zD = targetPosition.z - tempPosition.z;
float dist2 = xD*xD + yD*yD + zD*zD; // or xD*xD + zD*zD
Edit: Another optimization (that you're likely already doing) is to only recalculate when a player has moved. I like this one because it doesn't compromise the data at all.
I wrote my own version of System.Threading.Tasks for unity and added something like this for ordering workload based on distance from the camera.
Essentially whenever a task (or in your case command) was needed it passed off a position and the task to a TaskManager that would then each frame sort the items it had and run through them.
You could probably do the same / similar but instead of passing the command to some sort of CommmandManager like I did with a TaskManager do a lookup on creation and pass the command to the player closest to the point in question.
Most people these days are pulling their scene graphs in to something like a quad tree which should make finding the right player fairly fast then each player would be responsible for executing its own commands.
Okay after hours working on the issue I finally found a solution to improve the performance on the one hand and make it threadable on the other hand. I had problems with the threading, because player is a Unity object.. Instead of using the player object within the task I only get its position in the Start() method. Thus I managed to make it threadable, although it seemed strange to me, that I represented the players by its positions using Vector3 now.
Improving the performance did happen by adding another Dictionary storing the already calculated distances for each player. So when reassigning a player the distance did not need to be recalculated again... I am not sure how much of performance this brought, because I tested it together with the Thread, but at least I got rid of some calls of Distance and am back at stabel 60 fps!
Furthermore I messed something up with the recursions, so that I got up to 100.000 recursions for each player.. This should NOT happen o.O. The fix was easy enough. Simply added a minCommand command, which I set during the foreach and only assign commands and touch the Sets AFTER the foreach.. I bet the code would now run like sugar even without threading...