I have a PictueBox and I have some dice, I would like to play an animation for the "rolling" of the dice, I did a .gif with the dice, but after the dice stop rolling, I want the actual dice number that I got, I have a random funcion that handles that.
My question is, I press the "Roll Dice" button, it plays the animation and after the animation ends I should set int the picturebox the dice that actually came. but it immediately chnages to the dice number that actually came, skipping the animation;
This is how it works:
dice1.Image = Resources.DiceAnimation; //Here the gif is called to be played
int x = rollDice(); //Here I roll the dice
switch (x){
case 1: dice.Image = resources.diceFace1; //Image set depending on x
break
case 2: //etc...
}
There might be two things needed to do that.
Firstly, you may need to ensure that your PictureBox receives a gif image and it knows it. To do this, please check this answer and this answer. The posts have code to show GifImage frame by frame:
public class GifImage
{
private Image gifImage;
private FrameDimension dimension;
private int frameCount;
private int currentFrame = -1;
private bool reverse;
private int step = 1;
public GifImage(string path)
{
gifImage = Image.FromFile(path);
//initialize
dimension = new FrameDimension(gifImage.FrameDimensionsList[0]);
//gets the GUID
//total frames in the animation
frameCount = gifImage.GetFrameCount(dimension);
}
public bool ReverseAtEnd {
//whether the gif should play backwards when it reaches the end
get { return reverse; }
set { reverse = value; }
}
public Image GetNextFrame()
{
currentFrame += step;
//if the animation reaches a boundary...
if (currentFrame >= frameCount || currentFrame < 1) {
if (reverse) {
step *= -1;
//...reverse the count
//apply it
currentFrame += step;
}
else {
currentFrame = 0;
//...or start over
}
}
return GetFrame(currentFrame);
}
public Image GetFrame(int index)
{
gifImage.SelectActiveFrame(dimension, index);
//find the frame
return (Image)gifImage.Clone();
//return a copy of it
}
}
Use it like this (note that you need a Timer object):
private GifImage gifImage = null;
private string filePath = #"C:\Users\Jeremy\Desktop\ExampleAnimation.gif";
public Form1()
{
InitializeComponent();
//a) Normal way
//pictureBox1.Image = Image.FromFile(filePath);
//b) We control the animation
gifImage = new GifImage(filePath);
gifImage.ReverseAtEnd = false; //dont reverse at end
}
private void button1_Click(object sender, EventArgs e)
{
//Start the time/animation
timer1.Enabled = true;
}
//The event that is animating the Frames
private void timer1_Tick(object sender, EventArgs e)
{
pictureBox1.Image = gifImage.GetNextFrame();
}
Secondly, to know how long you want to run your GIF image, you may need to Get Frame Duration of GIF image like this:
double delayIn10Ms; //declare somewhere
//Initialize on your form load
PropertyItem item = img.GetPropertyItem (0x5100); // FrameDelay in libgdiplus
// Time is in 1/100th of a second
delayIn10Ms = (item.Value [0] + item.Value [1] * 256) * 10;
Then use the delayIn10Ms time plus, probably, a little bit more time to stop your timer. You may also want to check when was the last time your timer Ticks and store it. If it exceeds the given delay time, then you should stop your timer and start it again on dice roll, after image assignment in your switch case.
DateTime currentTick = DateTime.Min;
DateTime startTick = DateTime.Min;
private void timer1_Tick(object sender, EventArgs e)
{
currentTick = DateTime.Now;
if ((currentTick - startTick).TotalSeconds / 100 < delayIn10Ms)
pictureBox1.Image = gifImage.GetNextFrame();
else
timer1.Stop(); //stop the timer
}
//And somewhere else you have
timer1.Start(); //to start the timer
int x = rollDice(); //Here I roll the dice
switch (x){
case 1: dice.Image = resources.diceFace1; //Image set depending on x
break
case 2: //etc...
}
You can make a timer with the Interval property set to the length of the animation and set it's Tag to 0 and in the timer write the code:
if(timer.Tag == "0")
timer.Tag == "1";
else if(timer.Tag == "1")
{
int x = rollDice();
switch (x)
{
case 1: dice.Image = resources.diceFace1; break;
case 2: //etc...
}
timer.Tag == "0";
timer.Stop();
}
Related
I'm trying to code a Simon Says game and I ran into a problem. I cannot figure out how to make my Randomizer timer (that randomizes which boxes are lit up) to run by waves. I want it to run once, then when it's referred to again - run twice and so on.
This is RandomPlace():
private PictureBox RandomPlace()
{
PictureBox p = new PictureBox();
Random rnd = new Random();
switch (rnd.Next(1, 5))
{
case 1:
p = TopRight;
break;
case 2:
p = TopLeft;
break;
case 3:
p = BottomRight;
break;
case 4:
p = BottomLeft;
break;
}
return p;
} //Gets a random PictureBox
This is RandomImage():
private void RandomImage()
{
TopLeft.Enabled = false;
TopRight.Enabled = false;
BottomLeft.Enabled = false;
BottomRight.Enabled = false;
PictureBox a = RandomPlace();
if (a == TopLeft)
{
TopLeft.Image = Resources.TopLeftLit;
label1.Text = "TopLeft";
Thread.Sleep(500);
TopLeft.Image = Resources.TopLeft;
label2.Text = "TopLeftAFTERSLEEP";
Thread.Sleep(500);
pattern[patternRightNow] = 1;
patternRightNow++;
}
if (a == TopRight)
{
TopRight.Image = Resources.TopRightLit;
label1.Text = "TopRight";
Thread.Sleep(500);
TopRight.Image = Resources.TopRight;
label2.Text = "TopRightAFTERSLEEP"; //FIGURE OUT HOW TO RESET PICTURE
Thread.Sleep(500);
pattern[patternRightNow] = 2;
patternRightNow++;
}
if (a == BottomLeft)
{
this.BottomLeft.Image = Resources.BottomLeftLit;
label1.Text = "BottomLeft";
Thread.Sleep(500);
this.BottomLeft.Image = Resources.BottomLeft;
label2.Text = "BottomLeftAFTERSLEEP";
Thread.Sleep(500);
pattern[patternRightNow] = 3;
patternRightNow++;
}
if (a == BottomRight)
{
this.BottomRight.Image = Resources.BottomRightLit;
label1.Text = "BottomRight";
Thread.Sleep(500);
this.BottomRight.Image = Resources.BottomRight;
label2.Text = "BottomRightAFTERSLEEP";
Thread.Sleep(500);
pattern[patternRightNow] = 4;
patternRightNow++;
}
} //Lits up the random PictureBoxes and sets them back to normal
This is Randomizer_Tick():
rivate void Randomizer_Tick(object sender, EventArgs e)
{
RandomImage();
patternRightNow = 0;
tickCount++;
Randomizer.Stop();
ClickCheck();
} //Use RandomImage() to lit up random PictureBoxes on 5 waves, wave 1 - 1 PictureBox, wave 2 - 2 PictureBoxes and so on.
This is ClickCheck():
private void ClickCheck()
{
TopLeft.Enabled = true;
TopRight.Enabled = true;
BottomLeft.Enabled = true;
BottomRight.Enabled = true;
if (tickCount == clickCount)
{
CheckIfWin();
Randomizer.Start();
}
} //Enables the PictureBoxes to be pressed, after the user input reaches the current wave's amount of PictureBoxes lit up, disable PictureBoxes again and start the randomizer
Instead of using timers, I advise you to look at Tasks. It's much easier to create such statemachines.
Fill a list with colors and play it. Wait until the user pressed enough buttons and check the results.
For example: (haven't tested it, it's just for example/pseudo code)
It might contain some typo's.
private List<int> _sequence = new List<int>();
private List<int> _userInput = new List<int>();
private Random _rnd = new Random(DataTime.UtcNow.Milliseconds);
private bool _running = true;
private bool _inputEnabled = false;
private TaskCompletionSource<object> _userInputReady;
public async void ButtonStart_Click(object sender, EventArgs e)
{
if(_running)
return;
while(_running)
{
// add a color/button
_sequence.Add(_rnd.Next(4));
// play the sequence
for(int color in _sequence)
{
// light-up your image, whatever
Console.WriteLine(color);
// wait here...
await Task.Delay(300);
// turn-off your image, whatever
}
// clear userinput
_userInput.Clear();
_userInputReady = new TaskCompletionSource<object>();
_inputEnabled = true;
// wait for user input.
await _userInputReady.Task;
// compare the user input to the sequence.
for(int i=0;i<_sequence.Count;i++)
{
if(_sequence[i] != _userInput[i])
{
_running = false;
break;
}
}
}
Console.WriteLine("Not correct");
}
// one handler for all buttons. Use the .Tag property to store 0, 1, 2, 3
public void ButtonClick(object sender, EventArgs e)
{
if(!_inputEnabled)
return;
var button = (Button)sender;
// add user input to the queue
_userInput.Add((int)button.Tag);
if(_userInput.Count >= _sequence.Count)
_userInputReady.SetResult(null);
}
`So I have a total of 6 images that when animated, creates the illusion of a person walking. The problem is its not smooth, refresh(), invalidate(),update() have failed me. How do I go about this.
namespace Runner
{
public partial class Form1 : Form
{
Keys moveRight;
Keys moveLeft;
public static bool isMovingR = false;
public static bool isMovingL = false;
Bitmap stnd = new Bitmap(Properties.Resources.Standing);
static Bitmap wlk_1_RL = new Bitmap(Properties.Resources.Walk_1_RL);
static Bitmap wlk_2_RL = new Bitmap(Properties.Resources.Walk_2_RL);
static Bitmap wlk_3_RL = new Bitmap(Properties.Resources.Walk_3_RL);
static Bitmap wlk_4_LL = new Bitmap(Properties.Resources.Walk_4_LL);
static Bitmap wlk_5_LL = new Bitmap(Properties.Resources.Walk_5__LL);
static Bitmap wlk_6_LL = new Bitmap(Properties.Resources.Walk_6_LL);
Graphics gfx;
Animation animate = new Animation(new Bitmap[] { wlk_1_RL, wlk_2_RL, wlk_3_RL,
wlk_4_LL, wlk_5_LL, wlk_6_LL });
Timer timer = new Timer();
int imageX = 5;
int imageY = 234;
public Form1()
{
InitializeComponent();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
moveRight = Keys.D;
moveLeft = Keys.A;
if (Keys.D == moveRight)
{
isMovingR = true;
timer.Enabled = true;
timer.Interval = 50;
timer.Tick += timer1_Tick;
//imageX += 5;
Refresh();
} else if (Keys.A == moveLeft)
{
isMovingL = true;
imageX -= 5;
Refresh();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
gfx = this.CreateGraphics();
gfx.DrawImage(animate.Frame2Draw(), imageX, imageY);
//Refresh(); Invalidate(); Update();
}
}
}
Now, the problem is being able to make the animation consistent and smooth
UPDATE...animation class
public class Animation
{
int slctdImg = 0;
public static Bitmap[] images;
Bitmap frame2Draw;
public Animation(Bitmap[] frames)
{
foreach (Bitmap btm in frames)
{
btm.MakeTransparent();
}
images = frames;
}
public Bitmap Frame2Draw()
{
if (slctdImg < images.Length)
{
frame2Draw = images[slctdImg];
slctdImg++;
}
if (slctdImg >= images.Length)
{
slctdImg = 0;
frame2Draw = images[slctdImg];
}
return frame2Draw;
}
}
Many issues:
I wonder why you would call MakeTransparent on each Tick?? I doubt it does what you expect.. It is changing pixels and rather expensive; you ought to cache the images instead.
Nor why you create an array of bitmap on each Tick??? And since it is always created the same way it always displays only the 1st image.. This should answer your question.
Further issues:
Using this.CreateGraphics(); will fail to create a persistent result although that may not be your aim as you try to animate.
Remember that a Timer.Interval can't run faster than 15-30 ms; also that winforms is notoriously bad at animation.
Remember that c# is zero-based, so this slctdImg > images.Length should probably be slctdImg >= images.Length
Here is what you should do instead:
Move the instantiation either to the form load or maybe to a key event.
Move the drawing to the Paint event of the form.
In the Tick count up frames and/or position and trigger the Paint by calling Invalidate on the form!
Update:
One more issue is the way you hook up the Tick event each time the right key is pressed. Hooking up an event multiple times will result in it running multiple times; this will create to gaps/jumps in your animation..
Either add an unhook each time before hooking up (it will fail quietly the 1st time)
timer.Tick -= timer1_Tick;
timer.Tick += timer1_Tick;
or (better) hook it up only once in the original set up!
I need to add PictureBox's (pictureBox11 to pictureBox30) to an array.
So instead of adding PictureBox's like this:
PictureBox[] Coins = new PictureBox[20];
Coins[0] = pictureBox11;
...
Coins[19] = pictureBox30;
I wrote a for cycle like this (DOESN'T WORK) :
for (int i = 11; i < 31; i++)
{
for (int j = 0; j < Coins.Length; j++)
{
Coins[j] = (PictureBox)Controls.Find(
"pictureBox" + i.ToString(), true)[0];
}
}
There might be a small stupid mistake somewhere because I use the same cycle for another thing and it works, idk, maybe I'm just blind and cant see the mistake.
Maybe it is relevant, so I will include the code I am assigning for the array elements:
for (int i = 0; i < Coins.Length; i++)
{
if (player.Bounds.IntersectsWith(Coins[i].Bounds))
{
Coins[i].Visible = false;
}
}
EVERYTHING works fine if I add them as shown in first code, but it is not very practical.
Why isn't the second code (the for cycle) I wrote working for me?
Is there a better way to add multiple pictureboxes to an array?
I am guessing you are making this more complicated than it has to be. To try and understand better, I assume that when you say I need to add pictureboxes (pictureBox11 to pictureBox30) to an array. that you mean there are 30+ PictureBoxs on your form and each PictureBox uses a naming convention such that each is named “pictureBoxX” where “X” is 1,2,3…30,31. Then you want to get a (consecutive?) group of “PictureBoxes” on the form to make invisible. I hope this is correct.
To simply make the picture boxes invisible, I do not think an array is needed. Simply loop through the picture boxes and make it invisible if the name matches a string of the form “pictureBoxX “. I used IndexsAreValid method to validate the start and end indexes. It is also used in the code for an array implementation below this code.
Make PictureBoxes invisible without an array
private void SetPictureBoxesInvisible(int start, int end) {
int size = -1;
string targetString = "";
if (IndexsAreValid(start, end, out size)) {
for (int i = start; i < end + 1; i++) {
try {
targetString = "pictureBox" + i;
PictureBox target = (PictureBox)Controls.Find(targetString, true)[0];
if (target != null) {
target.Visible = false;
}
}
catch (IndexOutOfRangeException e) {
return;
}
}
}
}
If you must have a PictureBox array returned, then the code below should work.
First, to get an array of PictureBoxs as you want you need an array to store them. But first you need to know how big to make it. From your posted code it appears that you want to get picture boxes 11-30 and put them in an array. So we can get the size from these numbers… i.e. 30-11=19 +1 = 20. That’s about all you need. Simply create the array and loop through all the picture boxes and grab pictureBox11-pictureBox30. When done we can use this array to make these `PictureBoxes” invisible.
I created a method IsValidPic similar to a tryParse to validate if the given index (1,2,3..30) is valid. If it is out of range, I simply ignore that value. This gives you the ability to grab an individual picture box in case the desired picture boxes are not contiguous. I used a few buttons to test the methods.
Hope this helps.
private PictureBox[] GetPictureBoxes(int start, int end) {
int size = - 1;
if (IndexsAreValid(start, end, out size)) {
PictureBox curPic = null;
PictureBox[] allPics = new PictureBox[size];
int index = 0;
for (int i = start; i <= end; i++) {
if (IsValidPic(i, out curPic)) {
allPics[index] = curPic;
index++;
}
}
return allPics;
}
else {
return new PictureBox[0];
}
}
private Boolean IndexsAreValid(int start, int end, out int size) {
if (start < 1 || end < 1) {
size = -1;
return false;
}
if (start > end) {
size = -1;
return false;
}
size = end - start + 1;
return true;
}
private Boolean IsValidPic(int index, out PictureBox picture) {
string targetName = "pictureBox" + index;
try {
PictureBox target = (PictureBox)Controls.Find(targetName, true)[0];
if (target != null) {
picture = target;
return true;
}
picture = null;
return false;
}
catch (IndexOutOfRangeException e) {
picture = null;
return false;
}
}
private void ResetAll() {
foreach (PictureBox pb in this.Controls.OfType<PictureBox>()) {
pb.Visible = true;
}
}
private void button1_Click(object sender, EventArgs e) {
TurnInvisible(2, 3);
}
private void button3_Click(object sender, EventArgs e) {
TurnInvisible(11, 30);
}
private void button4_Click(object sender, EventArgs e) {
TurnInvisible(1,7);
}
private void TurnInvisible(int start, int end) {
PictureBox[] pictureBoxesToChange = GetPictureBoxes(start, end);
foreach (PictureBox pb in pictureBoxesToChange) {
if (pb != null)
pb.Visible = false;
}
}
private void button2_Click(object sender, EventArgs e) {
ResetAll();
}
I am kinda stuck and I need help.
My goal is to make a little "game". It should have 3 pictureboxes and it should randomly display/change numbers (pictures) 1 to 6 every 0,5sec. When I hit STOP, it should stop the numbers and I should get points (score) based on nubmers. 3 same numbers = +10 points, 2 same numbers = +5 points, no same numbers = -5 points. Then it should display highest score achieved (Max Score).
http://i.imgur.com/kubQBST.png
Please, give me some tips what to do.
Thanks a lot, regards Peter
You can use a Timer with random something like this :
Random rnd1 = new Random(Environment.TickCount);
Image[] Images = new Image[6];
int[] CurrentStatus = new int [3];
Images[0] = Image.FromFile("FileNameFornumber1");
Images[1] = Image.FromFile("FileNameFornumber2");
Images[2] = Image.FromFile("FileNameFornumber3");
Images[3] = Image.FromFile("FileNameFornumber4");
Images[4] = Image.FromFile("FileNameFornumber5");
Images[5] = Image.FromFile("FileNameFornumber6");
//change numbers every tick
private Timer_TickHandler(object sender, EventArgs e)
{
this.CurrentState[0] = rnd1.Next(1, 6);
this.CurrentState[1] = rnd1.Next(1, 6);
this.CurrentState[2] = rnd1.Next(1, 6);
this.PictureBox1.Image = Images[this.CurrentStatus[0]-1];
this.PictureBox2.Image = Images[this.CurrentStatus[1]-1];
this.PictureBox3.Image = Images[this.CurrentStatus[2]-1];
}
you have to write an event for the stop button to deactivate the timer and calculate the score based on the CurrentStatus Array and I think the calculation alg must be like this :
int score = this.CurrentStatus.Sum();
if (this.CurrentStatus[0] == this.CurrentStatus[1] && this.CurrentStatus[1] == this.CurrentStatus[2])
score +=10;
else
{
for (int i=0; i<3; i++)
{
for (int j=i+1; j<3; j++)
{
if (this.CurrentStatus[i] == this.CurrentStatus[j])
{
score+=5;
break;
}
}
}
}
for start make random picture on button press.
How add images into resources
For example
public partial class Form1 : Form
{
List<Bitmap> picturesList = new List<Bitmap>(); //Array of pictures
Random random = new Random();
public Form1()
{
InitializeComponent();
//Load all pictures from resources into array
picturesList.Add(Properties.Resources.pic1);
picturesList.Add(Properties.Resources.pic2);
picturesList.Add(Properties.Resources.pic3);
//Set random image into picture box
RandomChangeImage();
}
public void RandomChangeImage()
{
//Generate random number. (random index between 0 - array.count )
int randomIndex = random.Next(0, picturesList.Count);
//Set random image from array
YourPictureBoxName.Image = picturesList[randomIndex];
}
}
Now you can use RandomChangeImage(); in your code. Aflter you done you can continue with timer.
Now add Timer from toolbox into designer.
Double click on timer.
It generated this
private void timer1_Tick(object sender, EventArgs e)
{
//Call random change image
RandomChangeImage();
}
in you buttonstart_Click call timer1.Start(); for example
private void btnStart_Click(object sender, EventArgs e)
{
timer1.Start();
}
if you want change tick in timer you can use
timer1.Interval = 500; for 0.5s. where you want.
if you want stop timer use
timer1.Stop();
public PbsWheel(AnimatedPictureBox.AnimatedPictureBoxs[] pbs, AnimatedPictureBox.AnimatedPictureBoxs pb, int delta,Label label2)
{
for (int i = 0; i < pbs.Length; i++)
{
if (delta > 0)
{
pbs[i].AnimateRate += 1/60 * 1000;
1/60 * 1000 is 60 frames per second ?
This is how i animate the pictureBoxes the images inside. Im using timer for each picturebox:
public class AnimatedPictureBoxs : PictureBox
{
public static bool images;
List<string> imageFilenames;
Timer t = new Timer();
public AnimatedPictureBoxs()
{
images = false;
AnimateRate = 100; //It's up to you, the smaller, the faster.
t.Tick += Tick_Animate;
}
public int AnimateRate
{
get { return t.Interval; }
set { t.Interval = value; }
}
public void Animate(List<string> imageFilenames)
{
this.imageFilenames = imageFilenames;
t.Start();
}
public void StopAnimate()
{
t.Stop();
i = 0;
}
int i;
private void Tick_Animate(object sender, EventArgs e)
{
if (images == true)
{
imageFilenames = null;
}
if (imageFilenames == null)
{
return;
}
else
{
try
{
if (i >= imageFilenames.Count)
{
i = 0;
}
else
{
Load(imageFilenames[i]);
i = (i + 1) % imageFilenames.Count;
}
}
catch (Exception err)
{
}
}
}
The rate is set to 100 what i want to do is to display and when i move the mouse wheel up down to change the speed of the images animate by frames per second.
pbs is array of pictureBoxes.
pbs[i].AnimateRate += 1/60 * 1000;
Now, AnimateRate is an integer property. It is very badly named. It is not a rate. It is a timer interval. In mathematical terms it is a period. Naming it rate makes it sound as though it will be a rate, or a frequency.
The mathematical relationship between period T and frequency f is:
T = 1/f
So, here's what you should do:
Rename the property as AnimationInterval.
When you need to convert a frequency (i.e. frame rate) to an interval use the formula above.
Note that you need to account for the fact that your frequencies are measured in frames per second, but your intervals are measured in milli-seconds. So your code should be:
pbs[i].AnimationInterval += 1000/60;
That looks very similar to what you had but there is a subtle difference. In mathematics, the formulae are identical. But in C#, the behaviour of the / operator depends on the types of its operands. You supply two integers and so / is integer division. And the result of 1/60 is zero. So your code does not modify the property.
I do think that you will need to modify your logic a little. As it stands, your raw data is an interval. But actually what you wish to control if frame rate. So I believe that you should maintain a variable that holds the frame rate. If you want to modify it, then make the modifications to the frame rate variable. And then set the interval like this:
pbs[i].AnimationInterval = 1000/frameRate;