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.
Related
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;
}
}
Object is forced up. After 10 seconds, object will be forced back down. Alternatively, object can be clicked and thereby forced back down before 10 seconds has elapsed.
What is currently happening: If object is forced down, GAME OVER.
What I am trying to do instead: If object is forced down before you are able to click it, GAME OVER.
In other words, the GAME OVER is being shown in 10 seconds, regardless of whether or not the object was clicked.
I'm having a really difficult time with this logic and I am hoping a fresh perspective might be able to help.
public void FixedUpdate() {
// Increase the kick timer
kickTimer += Time.fixedDeltaTime;
// If the next kick time has came
if (nextKick < kickTimer) {
// Schedule the kick back corresponding to the current kick
nextKickBacks.Enqueue (nextKick + 10f);
// Apply the kick force
rb.AddForce (transform.up * thrust, ForceMode.Impulse);
// Plan the next kick
nextKick = kickTimer + Random.Range (MinKickTime, MaxKickTime);
}
// If there are more kick backs to go, and the time of the closest one has came
if (0 < nextKickBacks.Count) {
if (nextKickBacks.Peek () < kickTimer) {
// Apply the kick back force
rb.AddForce (-transform.up * thrust, ForceMode.Impulse);
// Show the GAME OVER gameObject
GameObject.Find ("icetextureONfile").transform.localScale = new Vector3 (0.02f, 0.02f, 0.02f);
// Dequeue the used kick back time
nextKickBacks.Dequeue ();
}
}
}
void OnMouseDown()
{
rb.AddForce(-transform.up * thrust,ForceMode.Impulse);
}
}
You need to add the logic for what happens when you click on the object. Right now you are applying force, but there is nothing in the code changing the state of the Update loop.
I don't know what type of game you are making. So I am not sure what you want to do when the click is executed, but I am guessing you want to reset the kickbacks.
In that way you can do something like this. Change .Clear() to whatever method suits your needs.
void OnMouseDown()
{
rb.AddForce(-transform.up * thrust,ForceMode.Impulse);
nextKick = 0; // reset timer to force a new kick to be set next frame
nextKickBacks.Clear(); // clear current set kickbacks
}
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.
I'm creating a console game as simple as "I generate a random number, find it", but with many options.
My current code (without what I want here) is availlable on GitHub: https://github.com/crakmaniaque/trouvezmoi
What I want is to create a version of my game which will be timed, so the computer generates numbers, the user finds it, it generates a new one and the player have 90 seconds to find a max lot of random numbers. I can code this easily.
What I will need help is to stop the game (a thread) after 90 seconds and retrieve the number of answers founded from the thread. The Console.Title should also show time remaining. The attempt I've tried works, but the thread is not interrupted if console is asking for number input (Console.ReadLine()). But the timer is for the entire process, not only user input.
private static void timerb()
{
int t = 90;
for (int i = 0; i < 90; i++)
{
Console.Title = t + " seconds remaining";
Thread.Sleep(1000);
t--;
}
}
private static void cGame()
{
Thread t = new Thread(timerb);
t.Start();
while (t.IsAlive)
{
bool good = false;
int rnd = new Random().Next(0,10); // 0 and 10 are sample
while (!good)
{
try
{
Console.Write("Enter a number between x and y >");
int i = int.Parse(Console.ReadLine());
if (i == rnd)
{
good = true;
}
}
catch (FormatException)
{
Console.WriteLine("Invalid answer.");
}
}
}
}
I don't know much about threading and at that point I'm stuck.
Can someone help me with my problem? I'm using .NET 2.0.
Perhaps you are looking for a timer? You could register an event, that would fire after 90 seconds, that would run while the loop is happening. The documentation can be found here: Timer class MSDN documentation.
I believe the usage would be:
Timer timer = new Timer { Interval = new Timespan (0,1,30);
timer.elapsed += //function to fire to kill the app or the game
You'd need to make each console read with a timeout equal to the amount of time left in the game. That solves that issue.
Then, you need a way to signal the timerb thread to shut down when the main game loop has ended. I think the simplest way would be to end the game loop when the remaining time is <= zero. Alternatively, you could make timerb singnal the main thread to shut down when t == 0. Inter-thread communication is always complicated and error-prone, though.
You can signal the timerb thread to shut down by setting a volatile bool shutdown to true and by making timerb poll that variable and shut itself down.
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