Correct Use of the Timer - c#

pretty new here...
I am making a program that will allow a user to control a sprite (walking on a surface/jumping/falling - the usual... pretty basic i know)
To make the sprite jump in such a way, so that the human eye can actually see a rise and fall on the form, i need to slow the process by which the program translated the sprite upwards.
I decided to use a timer, not SLEEP because i don't want the whole program to freeze.
Here's what i came up with:
private void jump()
{
global.CharacterY = global.CharacterY - 1;
framer_Tick(null, new EventArgs()); //pause program without freezing
}
private void framer_Tick(object sender, EventArgs e)
{
sprite.Location = new Point(global.CharacterX, global.CharacterY);
}
Called by this:
private void Stage_KeyDown(object sender, KeyEventArgs e)
{
if (global.counter >= 1 & e.KeyCode.ToString() == "D")
{
global.CharacterX = global.CharacterX + 1;
jump();
}
if (e.KeyCode.ToString() == "W")
{
while (global.counter < 50)
{
jump();
global.counter = global.counter + 1;
}
global.counter = 0;
}
if (e.KeyCode.ToString() == "D")
{
global.CharacterX = global.CharacterX + 1;
sprite.Location =
new Point(global.CharacterX, global.CharacterY);
}
if (e.KeyCode.ToString() == "A")
{
global.CharacterX = global.CharacterX - 1;
sprite.Location =
new Point(global.CharacterX, global.CharacterY);
}
}
Now, the timer doesn't seem to have any effect. I assumed that placing the code to translate the sprite inside the timer would make it fire once every time the timer ticked.
- Unfortunately i don't have the experience make the timer pause the program (preferably 30 times a second, at an interval of 33(ish)) -

Simply changing the location of the sprite will not do anything. You have to call Invalidate() on whatever control the sprite is being drawn on to see the effect.
Also, you don't call framer_tick to get the process started. You have to call Start and Stop methods on the timer object. When you call Start, the tick handler will start getting called. When you call Stop, it will stop.
To make all of your animation smooth and logic less problematic your tick timer should be going off all the time because you should be redrawing the screen all the time. With the screen refreshing itself you just change the location of the sprite and the animation will behave as you expect it.

Simply changing the location of the sprite will not do anything. You have to call Invalidate() on whatever control the sprite is being drawn on to see the effect. Also, you don't call framer_tick to get the process started. You have to call start/stop on the timer object. When you call start, the tick handler will start getting called. When you call stop, it will stop. But I agree with #Chris. Your tick timer should be going off all the time because you should be redrawing the screen all the time. After that you just change the location of the sprite and everything will be fine. – Paul Sasik

Related

Why aren't my attempts to freeze time not working?

I want it so when the player moves his mouse on an object the time freezes and he can't move anymore. I tried different methods but it just doesn't freeze the time.
private void OnMouseEnter()
{
PlayRandomSound();
Time.timeScale = 0;
}

C# using a timer within another timer?

I'm programming a tetris game. So far I have implemented a timer with a very short interval of around 50 ms. Every tick the current falling tetromino gets moved one position in the game matrix downwards. Then the game checks if there is a collision, if so the tetromino gets marked as "landed" and new tetromino is spawned.
No I have the problem that I want to give the player the chance to move the tetromino to the left or right for let's say 0.5 seconds after it landed to have the possibility to move the tetromino underneath an overhanging already landed tetromino.
But with my current code I have the problem that as soon as there is a collision detected the timer continues and spawns the next tetromino. My idea now was to try to implement a second timer that gets activated for 0.5 seconds everytime a collision is detected to allow for additional keyboard input bevore the main timer continues. So basically pausing the main timer for 0.5 seconds and allow the program to detect keyboard input during that time and then continue with the main timer.
Everything I tried so far didn't really work. It seems like the first timer always continues running while I have a second timer active.
This is how my first timer looks like:
private void TimerBrick_Tick(object sender, EventArgs e)
{
ResetGameBoard(jaggedTetrisBricks[rnd-1][rotation], _brickX, _brickY, xLengthBrick, yLengthBrick);
_brickX++;
CollisionCheck(jaggedTetrisBricks[rnd - 1][rotation], _brickX, _brickY, xLengthBrick, yLengthBrick);
LineCheck();
Invalidate();
}
Now I somehow need to implement inside my CollisionCheck() method a way to wait for 0.5 seconds before the code continues with LineCheck().
Or is there another way that I'm not seeing right now?
In addition to my previous comment - here is a bit of pseudo-code :
enum GameState
{
Starting,
CreateBrick,
BrickFalling,
MoveSideways
}
private GameState curState = Starting;
private ushort ctSideways;
private void TimerBrick_Tick(object sender, EventArgs e)
{
switch (curState)
{
case Starting:
InitialiseGame();
curState = CreateBrick;
break;
case CreateBrick:
CreateNewBrick();
curState = BrickFalling;
break;
case BrickFalling:
.....
.....
if (CollisionCheck())
{
ctSideways = 0;
curState = MoveSideways;
}
break;
case MoveSideways:
.....
.....
ctSidways++;
if (ctSideways == 10)
curState = CreateBrick;
break;
}
}

Flicker in C# WinForms program on screen clear

Alright, so I've done some research into this topic and most of the solutions I've found claim to fix the problem but I am finding that they aren't quite working right. I'm in the early stages of implementing just a simple little particle engine, nothing crazy I'm just doing it out of boredom. I have not done anything like this with WinForms before, I have certainly with C/C++ but this is a new thing for me. The following is the code I am using to draw the particles to the screen, the boiler plate code for the particles is not relevant as it works fine, I am more curious about my actual game loop.
Here is the main code for updates and redraws
public MainWindow()
{
this.DoubleBuffered = true;
InitializeComponent();
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle(object sender, EventArgs e)
{
Graphics g = CreateGraphics();
while (IsApplicationIdle())
{
UpdateParticles();
RenderParticles(g);
g.Dispose();
}
}
//Variables for drawing the particle
Pen pen = new Pen(Color.Black, 5);
Brush brush = new SolidBrush(Color.Blue);
public bool emmiter = false;
private void EmitterBtn_Click(object sender, EventArgs e)
{
//Determine which emitter to use
if (emmiter == true)
{
//Creates a new particle
Particle particle = new Particle(EmitterOne.Left, EmitterOne.Top, .5f, .5f, 20, 20);
emmiter = false;
}
else if(emmiter == false)
{
Particle particle = new Particle(EmitterTwo.Left, EmitterTwo.Top, -.5f, .5f, 20, 20);
emmiter = true;
}
}
public void RenderParticles(Graphics renderer)
{
Invalidate();
Thread.Sleep(0);
//Iterate though the static list of particles
for (int i = 0; i < Particle.activeParticles.Count; i++)
{
//Draw Particles
renderer.DrawRectangle(pen, Particle.activeParticles[i].x,
Particle.activeParticles[i].y,
Particle.activeParticles[i].w,
Particle.activeParticles[i].h);
}
}
public void UpdateParticles()
{
for (int i = 0; i < Particle.activeParticles.Count; i++)
{
//Move particles
Particle.activeParticles[i].MoveParticle();
}
}
The issue I am running into is that anytime the screen is getting cleared and updated, it gets this awful flickering, and not only that but it sometimes won't whenever I emit a particle.
The form is basically just using labels as invisible locations on the screen to say where to render each particle.
Anyway, I've seen this topic before but nothing has fixed anything, the current implementation is the least flickery/laggy but is not solving the issue.
Any help is appreciated, thanks!
EDIT* I realized I was never deallocating the graphics object each loop so I did that and there is no more delay whenever I click the emitter button, however the flicker is still there, I updated the code accordingly.
Getting rid of the visible paint artifacts requires double-buffering. In other words, render the scene into a back-buffer that, when ready, gets quickly blitted to the screen surface in a single step. That's a built-in feature in Winforms, simply set the DoubleBuffered property to true in the form constructor. You must use the Paint event to take advantage of that. Override OnPaint() and call RenderParticles(e.Graphics).
You need to take care of timing, right now your UI thread is burning 100% core and animation speed completely depends on the number of particles and the speed of the machine. Instead of Application.Idle, drop a Timer from the toolbox onto your form. In the Tick event handler, call UpdateParticles() and this.Invalidate() to get the Paint event to fire again. The timer's Interval property value is critical, you get the most reproducible update rate by picking 15 or 31 msec (64 or 32 FPS).
You are not always going to get the desired FPS rate, the timer will simply delay or skip a Tick event if the machine gets busy or is too slow or other code on the UI thread needs to run. To make sure that doesn't affect the animation, you must measure actual elapsed time instead of moving the particles by a fixed amount. Either Environment.TickCount, DateTime.UtcNow or Stopwatch are suitable ways to measure true elapsed time.

Event delayed when in a loop

I use the global mouse hooks from this page in a project. I want the mouse to move a specified path when it is clicked, but interupted if I let go of the mouse button, but for some reason the HookManager_MouseUp won't fire until round about 175 loops in my for-loop (forloop is for moving the mouse). (That should be about 0.4 seconds). No matter how long I press the mousebutton it will only stop the loop after those 0.4 seconds. If I wait longer than that it will stop the loop immediately when I let go of the mousebutton.
public void MoveMouse()
{
btm = new Bitmap(2000, 2000);
for (int i = 0; i < BézierPointList.Count; i++)
{
if (!run) { break; }
this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point((int)BézierPointList[i].X(), (int)BézierPointList[i].Y());
Graphics g = Graphics.FromImage(btm);
g.DrawRectangle(new Pen(Color.Red), (int)BézierPointList[i].X(), (int)BézierPointList[i].Y(), 1, 1);
Application.DoEvents();
System.Threading.Thread.Sleep(2);
}
CreateGraphics().DrawImage(btm, 0, 0);
}
(The graphics are for debugging).
Is there some way to update the events, or something else I can do to fix this? Thanks! (C#)
Edit:
My Form1.Designer.cs contains this code (among other)
Gma.UserActivityMonitor.HookManager.MouseDown += HookManager_MouseDown;
Gma.UserActivityMonitor.HookManager.MouseUp += HookManager_MouseUp;
And the methods HookManager_MouseUp and HookManager_MouseDown enters MoveMouse() and brakes it. The delay happens before HookManager_MouseUp (HookManager_MouseUp breaks the loop pretty much instantaneosly).

Thread for my user control

I have a user control that shows a speed in a dial format (An image).
It has a single method: SetSpeed(int speed);
It then makes the dial move to the desired speed, from the last set speed. It does then in incriments. That is, it moves to the new speed.
Problem is, if the car is going 10km/h, and then goes (very quickly) to 100km/h, it takes the dial maybe 2 seconds to reach that speed... (It's not 100% real time - which is realistic .. it moves to the new speed).
But, if, before the dial gets to 100km/h, the person slows to 50km/h, I need to interupt the movement to 100, and start moving to 50. Regardless of the current position (not speed) of the dial, I need to change the 'target speed'.
My control is using a BackgroundWorker to handle the refreshing, and not lock the UI.
So, my call is very basic from the UI:
dial1.SetSpeed(int.Parse(value));
And then the user control does this:
BackgroundWorker bw = new BackgroundWorker();
public void SetSpeed(int speed)
{
while(bw.IsBusy)
{
Thread.Sleep(50);
}
bw.RunWorkerAsync(speed);
}
And the method that does the work:
private void UpdateSpeed(object sender, DoWorkEventArgs e)
{
var oldSpeed = airSpeed;
var newSpeed = (int) e.Argument;
if(oldSpeed <= newSpeed)
{
for (int i = oldSpeed; i < newSpeed; i++)
{
airSpeed++;
this.Invoke(new MethodInvoker(Refresh));
}
}
else
{
for (int i = oldSpeed; i > newSpeed; i--)
{
airSpeed--;
this.Invoke(new MethodInvoker(Refresh));
}
}
airSpeed = newSpeed;
}
It locks when I send it two values in quick succession...
How do I interrupt the thread, if it's running, and change the value?
(Also, I think my code to change the speed is bad - can that be made neater?)
You do not. You handle it in a proper way, with locks, and checking whether the value needs changing. You do NOT interrupt it.
Basically, you need a lock area between SetSpeed and the Refresh method, so that one blocks the other. Then, when you set speed and the thread is currently in a critical area, it simply waits until the update is finished.
And your UpdateSpeed makes no sense - the change (airspeed-- and airspeed++) should be timer driven... you currently change them in a "arbitrary" speed, depending on processor speed. No timing.

Categories

Resources