I have a program that checks if the Kinect is connected to the computer. However, I don't really know if I need to call a method (I would assume so) and where? I've attached the code that I got from an introductory Kinect book. Thanks!
using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Kinect;
namespace test
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
KinectSensor kinectSensor;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
KinectSensor.KinectSensors.StatusChanged += Kinects_StatusChanged;
foreach (KinectSensor kinect in KinectSensor.KinectSensors)
{
if (kinect.Status == KinectStatus.Connected)
{
kinectSensor = kinect;
MessageBox.Show("Connected");
break;
}
}
if (KinectSensor.KinectSensors.Count == 0)
MessageBox.Show("No Kinect Found");
else
Initialize();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void Kinects_StatusChanged(object sender, StatusChangedEventArgs e)
{
switch (e.Status)
{
case KinectStatus.Connected:
if (kinectSensor == null)
{
kinectSensor = e.Sensor;
Initialize();
}
break;
case KinectStatus.Disconnected:
if (kinectSensor == e.Sensor)
{
Clean();
MessageBox.Show("Kinect was disconnected");
}
break;
case KinectStatus.NotReady:
break;
case KinectStatus.NotPowered:
if (kinectSensor == e.Sensor)
{
Clean();
MessageBox.Show("Kinect is not powered anymore.");
}
break;
default:
MessageBox.Show("Unhandled Status: " + e.Status);
break;
}
}
private void Initialize()
{
if (kinectSensor == null)
return;
kinectSensor.Start();
}
private void Clean()
{
if (kinectSensor != null)
{
kinectSensor.Stop();
kinectSensor = null;
}
}
}
}
Download the Kinect for Windows Developer Toolkit. There are multiple examples in there on how to do multiple things, that will get you started and help you understand how to talk to the Kinect.
Once you've connected to the Kinect you need to set it up and then subscribe to the event callbacks. You'll end up with a function that looks something like this:
private void InitializeKinectServices(KinectSensorManager kinectSensorManager, KinectSensor sensor)
{
// configure the color stream
kinectSensorManager.ColorFormat = ColorImageFormat.RgbResolution640x480Fps30;
kinectSensorManager.ColorStreamEnabled = true;
// configure the depth stream
kinectSensorManager.DepthStreamEnabled = true;
kinectSensorManager.TransformSmoothParameters =
new TransformSmoothParameters
{
// as the smoothing value is increased responsiveness to the raw data
// decreases; therefore, increased smoothing leads to increased latency.
Smoothing = 0.5f,
// higher value corrects toward the raw data more quickly,
// a lower value corrects more slowly and appears smoother.
Correction = 0.5f,
// number of frames to predict into the future.
Prediction = 0.5f,
// determines how aggressively to remove jitter from the raw data.
JitterRadius = 0.05f,
// maximum radius (in meters) that filtered positions can deviate from raw data.
MaxDeviationRadius = 0.04f
};
// configure the skeleton stream
sensor.SkeletonFrameReady += OnSkeletonFrameReady;
kinectSensorManager.SkeletonStreamEnabled = true;
// initialize the gesture recognizer
_gestureController = new GestureController();
_gestureController.GestureRecognized += OnGestureRecognized;
kinectSensorManager.KinectSensorEnabled = true;
if (!kinectSensorManager.KinectSensorAppConflict)
{
// additional init
}
}
This is my generic setup function, which is based off of examples from the Developer Toolkit. You will not be able to just plug this into your code and it will work. Having a look at the examples in the Toolkit will give you an understanding of where this happens and how to best manage it.
The KinectExplorer example is a good overall project to look over. It will also give you a clear understanding of how the function above works (it has the same function).
Related
I'm currently working on a project that involve a stepper motor using c# forms and arduino.
I need to know in c# when the motor stops his movement. To do that, I programmed arduino to send a string trough Serial when the movement is finished. Then the string is interpreted by c# so that the app can do other things after that.
I now want to have the same result as the example below, but using async await.
I stored the information that the motor stopped in:
movimentFinished = true;
I solved the problem in a terrible way using: while(movimentFinished = false){await Task.Delay(1)}
The difference between a while loop and an await is going to effect other stuff on my WinApp. The while blocks all the application until movimentFinished == true;
The logic that I want is simple:
MoveMotor();
await "value change"
//do stuff after it finishes
Any help/tip to do that?
Thank you for reading
//this funcion will receive an array from arduino as a reply
private void risposta(object sender, SerialDataReceivedEventArgs e)
{
riceved = port.ReadLine();
rispos = riceved;
this.Invoke(new EventHandler(Reply));
}
//this function will run every time a reply is sent from arduino through serial
private void Reply(object sender, EventArgs e)
{
//string risposValore = rispos.Substring(2, rispos.Length - 2);
char[] pcr = rispos.ToCharArray();
rispos = rispos.Trim();
if (pcr[0] == 'M' && pcr[1] == 'F' && pcr[2] == '1')
{
movementFinished = true;
}
}
//this funcion will send a serial command to arduino to run the motor
public void MoveTheMotor(int position)
{
int command = 1000;
int movement = command + (position)
string com1 = movement.ToString();
port.Write(com1 + "\n");
movementFinished = false;
}
private async void Demo()
{
MoveTheMotor(800);
//this while blocks everything until the value is true
while (movementFinished == false) { await Task.Delay(1); }
//do something after the motor finished
}
Instead of using a boolean, you need a signal. In the blocking world, this would be a ManualResetEventSlim or similar. In the asynchronous world, this would be some form of TaskCompletionSource. As such:
private TaskCompletionSource movementFinished;
private void Reply(object sender, EventArgs e)
{
...
if (pcr[0] == 'M' && pcr[1] == 'F' && pcr[2] == '1')
{
movementFinished.TrySetResult();
}
}
public void MoveTheMotor(int position)
{
...
movementFinished = new TaskCompletionSource();
}
private async void Demo()
{
MoveTheMotor(800);
await movementFinished.Task;
//do something after the motor finished
}
I need to save an image to disk that came from a web-cam between 5 and 10 seconds ago from when the "save" command comes in via serial port.
To get there, I have the webcam going into a pictureBox.Image (using opencv4), and then 2 Bitmap variables. Every 5 seconds a timer ticks, and Stored_bitmap_2 = Stored_bitmap_1, then Stored_bitmap_1 = (bitmap) pictureBox.Image.
When the right serial command comes in, I try to
Stored_image_2.Save("C:\Users\GreenWorld\Desktop\test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
and I get a run-time error of invalid parameter.
When I do the same thing in a stand-alone project with some buttons (inside the button-click event handler), it works every time.
When I do this inside the serialPort_DataReceived handler, I get all kinds of cross-thread errors. So, I moved the save attempt to its own subroutine, and that fixed that but now this.
I am by no means a professional programmer, I'm an engineer with a simple problem and I can usually write a little simplistic code to fix my immediate issue. Please go easy on me in the explanation :-)
Sample code:
using OpenCvSharp;
using OpenCvSharp.Extensions;
namespace Weld_picture
{
public partial class Form1 : Form
{
// Create class-level accessible variables
int Welding_camera_ID = 1;
VideoCapture capture;
Mat frame;
Bitmap image;
private Thread camera;
bool isCameraRunning = false;
string Serial_command = "";
Bitmap Stored_image_1;
Bitmap Stored_image_2;
bool Image_saved = false;
string Station_ID = "GWM-PWS-01";
string File_name = "";
private void CaptureCamera() // from someone else's sample code
{
camera = new Thread(new ThreadStart(CaptureCameraCallback));
camera.Start();
}
private void CaptureCameraCallback() // from someone else's sample code
{
frame = new Mat();
capture = new VideoCapture(Welding_camera_ID);
capture.Open(Welding_camera_ID);
if (capture.IsOpened())
{
while (isCameraRunning)
{
capture.Read(frame);
image = BitmapConverter.ToBitmap(frame);
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = image;
}
}
}
public Form1()
{
InitializeComponent();
SerialPort1.Open();
}
private void SerialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string dummy;
char CR = (char)0x0D;
while (SerialPort1.BytesToRead > 0)
{
Serial_command += (char)SerialPort1.ReadByte();
}
while (Serial_command.IndexOf(CR) > 0)
{
dummy = Serial_command.Substring(0, Serial_command.IndexOf(CR));
Serial_command = Serial_command.Substring(Serial_command.IndexOf(CR) + 1, (Serial_command.Length - (Serial_command.IndexOf(CR) + 1)));
Serial_command.Trim();
dummy.Trim();
proc_Process_serial_data(dummy);
}
}
//*************************************************************************************************************************************
/* the first timer is a 5-second interval. It's the "memory" function so that if/when the save-to-disk is triggered I can store the last-time-shutter-open image */
//*************************************************************************************************************************************
private void Timer_picture_interval_Tick(object sender, EventArgs e)
{
checkBox1.Checked = !checkBox1.Checked;
Timer_picture_interval.Stop();
Stored_image_2 = Stored_image_1;
Stored_image_1 = (Bitmap) pictureBox1.Image;
Timer_picture_interval.Start();
}
//*************************************************************************************************************************************
// the second timer is a 30-second interval. It's the way to turn capture off if the PLC/camera box somehow goes off-line
//*************************************************************************************************************************************
private void Timer_camera_powerdown_Tick(object sender, EventArgs e)
{
if (isCameraRunning)
capture.Release();
isCameraRunning = false;
Timer_picture_interval.Stop();
}
//*************************************************************************************************************************************
private void proc_Process_serial_data(string Serial_string)
{
if (Serial_string.IndexOf("Still here") > 0)
{
if (!isCameraRunning)
CaptureCamera();
isCameraRunning = true;
}
if (Serial_string.IndexOf("Sun's up") > 0)
{
Timer_picture_interval.Start();
Timer_camera_powerdown.Start();
}
if (Serial_string.IndexOf("It's dark") > 0)
{
if ((Stored_image_2 != null) && (!Image_saved)) // in case there's 2 subsequent requests to save the same thing (weld stutter)
{
File_name = "C:\\Users\\GreenWorld\\Desktop\\" + Station_ID + " D" + DateTime.Now.ToString("yyyy_MM_dd THH_mm_ss") + ".jpg";
Stored_image_2.Image.Save("C:\\Users\\GreenWorld\\Desktop\\test.bmp" , System.Drawing.Imaging.ImageFormat.Bmp );
Image_saved = true;
Timer_picture_interval.Stop();
}
Timer_camera_powerdown.Start();
}
}
}
}
You likely be able to fix the cros-threading error by simply calling your proc_Process_serial_data() method within an Invoke() call.
Change:
proc_Process_serial_data(dummy);
To:
this.Invoke((MethodInvoker)delegate ()
{
proc_Process_serial_data(dummy);
});
Also, these two lines aren't actually doing anything:
Serial_command.Trim();
dummy.Trim();
To Trim() the strings, you have capture the returned strings and re-assign them to the original variables:
Serial_command = Serial_command.Trim();
dummy = dummy.Trim();
A digital acoustic sensor is connected to my raspberry.
Everytime it detects sound, it sets the input HIGH.
But it only appears to be active for a couple of milliseconds.
How can i prolong the signal within my program that it stays up for 500ms?
It is a functionality which I know from PLC contollers.
Diagram
Start: sensor input
Q: prolonged signal
,
Here my approach with the answer of Michael:
But still, it doesn't hold for Task.Delay. It goes off directly.
public MainPage()
{
this.InitializeComponent();
GPIOinit();
Serial();
}
private void GPIOinit()
{
const int PIN_AKUSTIK = 19;
GpioController gpio = GpioController.GetDefault(); //GPIO-Controller mit Default belegen
pin_akustik = gpio.OpenPin(PIN_AKUSTIK);
}
public async void Serial()
{
//serial configuration
while (true)
{
//frequency of constant while loop 300ms
Sensor_Akustik();
}
}
public void Sensor_Akustik()
{
pin_akustik.ValueChanged += Pin_ValueChanged;
pin_akustik.SetDriveMode(GpioPinDriveMode.Input);
status_akustik = pin_akustik.Read().ToString();
textblock_DebugAkustik_live.Text = status_akustik;
Messenger.Default.Send<string, Page_Akustik>(status_akustik);
}
private void Pin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
if (args.Edge == GpioPinEdge.RisingEdge)
{
sender.Write(GpioPinValue.High);
Task.Delay(3000).Wait();
}
}
You might refer to following code. You can try to update the latched output value for the pin if the pin is configured as an input.
private void InitGPIO()
{
var gpio = GpioController.GetDefault();
// Show an error if there is no GPIO controller
if (gpio == null)
{
pin = null;
GpioStatus.Text = "There is no GPIO controller on this device.";
return;
}
pin = gpio.OpenPin(LED_PIN);
pin.ValueChanged += Pin_ValueChanged;
pin.SetDriveMode(GpioPinDriveMode.Input);
}
private void Pin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
if(args.Edge == GpioPinEdge.RisingEdge)
{
sender.Write(GpioPinValue.High);
Task.Delay(500).Wait();
}
}
My objective is to obtain a flag that is sent from the Arduino by a button. This flag is going to be sent through serial communication to a C# windows form program where i will be able to obtain the flag.
The data sent to the serial port by the Arduino is "ON" and "OFF", "ON" when the button is clicked and "OFF" when the button is not clicked. This flag will be used to turn on and off the red chart that will be displayed in the windows form.
My problem is how do i get this "ON" and "OFF" from the serial communication keeping in mind that data from the sensors are also being sent to the windows form application.
//C# Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using rtChart;
namespace Distance_Sensor_using_Flight_of_time
{
public partial class Form1 : Form
{
string recvData = "temporary";
bool breakloop = false;
kayChart chartData;
bool buttonPress = false;
string bufferString = "";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//chart1.Series.Add("Series1");
//Connection COM & Baud Rate
string[] ports = SerialPort.GetPortNames();
string[] rates = new string[10] { "300", "600", "1200", "2400", "9600", "14400", "19200", "38400", "57600", "115200" };
cboBaudRate.SelectedText = "9600";
cboCOM.Items.AddRange(ports);
cboBaudRate.Items.AddRange(rates);
if (ports.Length >= 1)
cboCOM.SelectedIndex = 0;
//kayChart real time
chartData = new kayChart(chart1, 60);
btnStart.Enabled = false;
btnSave.Enabled = false;
chart1.Series["Series1"].Enabled = false;
}
private void BtnConnect_Click(object sender, EventArgs e)
{
try
{
if (btnConnect.Text == "Disconnect")
{
if (btnStart.Text == "Stop")
MessageBox.Show("Please click \"Stop\" button first!");
else
{
serialPort1.Close();
btnStart.Enabled = false;
btnConnect.Text = "Connect";
}
}
else
{
serialPort1.PortName = cboCOM.Text;
serialPort1.BaudRate = Convert.ToInt32(cboBaudRate.Text);
serialPort1.Parity = Parity.None;
serialPort1.StopBits = StopBits.One;
serialPort1.DataBits = 8;
serialPort1.Open();
btnStart.Enabled = true;
btnConnect.Text = "Disconnect";
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void serialDataReceive(object sender, SerialDataReceivedEventArgs e)
{
if (!breakloop)
{
SerialPort sData = sender as SerialPort;
recvData = sData.ReadLine();
bufferString = recvData;
//rtbData.Invoke((MethodInvoker)delegate {rtbData.AppendText(recvData); });
//update chart
if (recvData == "ON\r" || recvData == "OFF\r")
{
if (recvData == "ON")
buttonPress = true;
else
buttonPress = false;
}
else
{
double data;
bool result = Double.TryParse(recvData, out data);
if (result)
{
chartData.TriggeredUpdate(data);
if (buttonPress == false)
{
chart1.Invoke((MethodInvoker)delegate { chart1.Series["Series1"].Enabled = false; });
chartData.serieName = "Length";
}
else
{
chart1.Invoke((MethodInvoker)delegate { chart1.Series["Series1"].Enabled = true; });
chartData.serieName = "Series1";
}
}
}
rtbData.Invoke((MethodInvoker)delegate { rtbData.AppendText(recvData); });
}
}
private void BtnStart_Click(object sender, EventArgs e)
{
try
{
if (btnStart.Text == "Start")
{
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialDataReceive);
btnStart.Text = "Stop";
breakloop = false;
}
else
{
btnStart.Text = "Start";
breakloop = true;
//serialPort1.DataReceived += null;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
btnStart.Text = "Start";
}
}
private void RtbData_TextChanged(object sender, EventArgs e)
{
rtbData.SelectionStart = rtbData.Text.Length;
rtbData.ScrollToCaret();
}
}
}
//Arduino Code
/* This example shows how to get single-shot range
measurements from the VL53L0X. The sensor can optionally be
configured with different ranging profiles, as described in
the VL53L0X API user manual, to get better performance for
a certain application. This code is based on the four
"SingleRanging" examples in the VL53L0X API.
The range readings are in units of mm. */
#include <Wire.h>
#include <VL53L0X.h>
VL53L0X sensor;
// Uncomment this line to use long range mode. This
// increases the sensitivity of the sensor and extends its
// potential range, but increases the likelihood of getting
// an inaccurate reading because of reflections from objects
// other than the intended target. It works best in dark
// conditions.
//#define LONG_RANGE
// Uncomment ONE of these two lines to get
// - higher speed at the cost of lower accuracy OR
// - higher accuracy at the cost of lower speed
//#define HIGH_SPEED
#define HIGH_ACCURACY
const int buttonPin = 2;
const int ledPin = 8;
int buttonState = 0;
bool inLoop = false;
void setup()
{
Serial.begin(9600);
Wire.begin();
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
sensor.init();
sensor.setTimeout(500);
#if defined LONG_RANGE
// lower the return signal rate limit (default is 0.25 MCPS)
sensor.setSignalRateLimit(0.1);
// increase laser pulse periods (defaults are 14 and 10 PCLKs)
sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18);
sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14);
#endif
#if defined HIGH_SPEED
// reduce timing budget to 20 ms (default is about 33 ms)
sensor.setMeasurementTimingBudget(20000);
#elif defined HIGH_ACCURACY
// increase timing budget to 200 ms
sensor.setMeasurementTimingBudget(200000);
//sensor.setMeasurementTimingBudget(900000);
#endif
}
void loop()
{
buttonState = digitalRead(buttonPin);
Serial.print(sensor.readRangeSingleMillimeters());
if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); }
Serial.println();
if (buttonState == HIGH && inLoop == false)
{
Serial.write("ON");
Serial.println();
digitalWrite(ledPin, HIGH);
inLoop = true;
}
else if (buttonState == LOW && inLoop == true)
{
Serial.write("OFF");
Serial.println();
digitalWrite(ledPin, LOW);
inLoop = false;
}
}
I expect that the chart will turn red once the button is clicked and blue when the button is not clicked. Thank you in advance.
Although what you are doing may work (ie just trying to figure out what the message is on the receive side), it is far more scale-able if you use some form of message header and a delimiter. So your messages would look like this:
"btnState,true"
"btnState,false"
"data,124"
On the receive side, you will need to do a recvData.Split(',') and then check the first member of the array to find out what they message type is and then parse the second part accordingly.
Also, relating to your code specifically, I am unsure why you chose to use Serial.write for you "ON" and "OFF" instead of sticking to Serial.println. I am not positive without testing, but pretty sure you are trying to use the println as a message delimiter, and I think it may be sending the write and the empty println as two separate messages (which would explain why your on off equality may fail).
I'm a beginner using Visual Studio to make a C# Windows Form. So I have a brick game (like the classic block game with ball and bricks) where if the player doesn't bounce the ball back, it's basically game over. However when this happens, the timer is just stopped and there's no way to restart. The code I have now just says to create a message box with "You lose" and you have to exit the program to play again. If you reach score 30 then that's a win and to play again you have to exit also.
I've tried googling this and the suggestions I see is if it's all set up already in their cases so implementing it to mine without having to re-do it all in someway is hard. I would like to make a simple implementation (or edit) where there is a play again function. How can I modify existing code to do this? Thank you dearly for any help.
This is the section of code that accounts for game-over if statement.
EDIT= Entire code provided.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;
using System.Reflection;
using System.IO;
namespace FinalProjectGame
{
public partial class FrmGame : Form
{
bool goLeft;
bool goRight;
int speed = 10;
int pBallx = 5;
int pBally = 5;
int score = 0;
public FrmGame()
{
InitializeComponent();
}
private void keyisdown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left && btnGamer.Left > 0)
{
goLeft = true;
}
if (e.KeyCode == Keys.Right && btnGamer.Left + btnGamer.Width < 920)
{
goRight = true;
}
}
private void keyisup(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
goLeft = false;
}
if (e.KeyCode == Keys.Right)
{
goRight = false;
}
}
private void TmrMainTimer_Tick(object sender, EventArgs e)
{
pBall.Left += pBallx;
pBall.Top += pBally;
lblScore.Text = "Score: " + score; //keeping score
if (goLeft) { btnGamer.Left -= speed; } //izquierda
if (goRight) { btnGamer.Left += speed; } //derecha
if (btnGamer.Left < 1)
{
goLeft = false; //disables "no-clip"
}
else if (btnGamer.Left + btnGamer.Width > 920)
{
goRight = false;
}
if (pBall.Left + pBall.Width > ClientSize.Width || pBall.Left < 0)
{
pBallx = -pBallx; //left, right wall bounce
}
if (pBall.Top < 0 || pBall.Bounds.IntersectsWith(btnGamer.Bounds))
{
pBally = -pBally; //top, bottom wall bounce
}
foreach (Control x in this.Controls)
//main code brickies subtraction
{
if (x is PictureBox && x.Tag == "blockies")
{
if (pBall.Bounds.IntersectsWith(x.Bounds))
{
this.Controls.Remove(x);
pBally = -pBally;
score++;
} //end of main
//ALT method foreach (Control x in this.Controls)
//{
if (!(x is PictureBox))
continue;
//this is needed if some specific property of the PictureBox is needed.
PictureBox ctl = (PictureBox)x;
if (ctl.Tag.ToString() != "blockies")
continue;
if (!pBall.Bounds.IntersectsWith(ctl.Bounds))
continue;
//this.Controls.Remove(x);
x.Visible = false;
pBally = -pBally;
score++;
//}
}
}//----
//This accounts for user score. Upon completing the game, the user wins. If the user does not capture the ball, it is automatically a game over.
if (score == 30)
{
gameOver();
MessageBox.Show("YES! WINNER!");
}
if (pBall.Top + pBall.Height > ClientSize.Height)
{
gameOver();
MessageBox.Show("SORRY YOU LOSE!");
}
//======
}
private void gameOver()
{
TmrMainTimer.Stop();
}
private void FrmGame_Load(object sender, EventArgs e) //main form load
{
//load the splash screen form that was made with design view
SplashScreen splash = new SplashScreen();
splash.Show();
//using system media to play the audio which is played upon start of the main form
SoundPlayer Sndplayr = new SoundPlayer(Properties.Resources.RoleMusic);
Sndplayr.Play();
}
}
}
If you want to restart that WinForms application, use Application.Restart(). You may close your game form and call restart in your static Main() method afterwards.
private void TmrMainTimer_Tick(object sender, EventArgs e)
{
...
if (pBall.Top + pBall.Height > ClientSize.Height)
{
gameOver();
MessageBox.Show("SORRY YOU LOSE!");
// close the game form
this.Close();
}
}
And in your startup code:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FrmGame());
// restart application
Application.Restart();
}
}
Edit:
The example above ist the easiest way to play that game from start. Of course, there are many other options:
As an extension, you can start your game form from a (maybe hidden) program startup form. If you register an event handler on the FrmGame.FormClosed event, you can restart that form again after the previous game was lost.
As many other guys suggested, a StartAgain() method, that resets your logic, is also an option.
Separate drawing logic (View) from game logic (Controller). For a such a simple game it is not necessary and overkill. But it is always a good idea to keep the things simple and clear. See the MVC pattern for your next project...