This is my control:
I am getting this to rotate, with this code:
Global variable, bool IsTriangleAnimRunning = false;
And the rest of the code:
public void AnimateTriangle()
{
var rotation = (int)((100 / 100d) * 45 * 1); // Max 45 degree rotation
var duration = (int)(750 * (100 / 100d)); // Max 750ms rotation
while (IsTriangleAnimRunning != false)
{
MyTriangle.Dispatcher.BeginInvoke(
(Action)(() =>
{
var anim = new DoubleAnimation
{
To = rotation,
Duration = new Duration(TimeSpan.FromMilliseconds(duration)),
AutoReverse = true
};
var rt = new RotateTransform();
MyTriangle.RenderTransform = rt;
rt.BeginAnimation(RotateTransform.AngleProperty, anim);
}), DispatcherPriority.ContextIdle);
Thread.Sleep(duration * 2);
}
}
The event handler for the button, that triggers the animation:
public void HandleTriangleEvents(object sender, RoutedEventArgs a)
{
Thread t_triagle = new Thread(new ThreadStart(this.AnimateTriangle));
Button btn = (Button)sender;
if (btn.Name == "btnStartTriangleAnim")
{
IsTriangleAnimRunning = true;
btnStartTriangleAnim.IsEnabled = false;
t_triagle.Start();
}
else
{
IsTriangleAnimRunning = false;
btnStartTriangleAnim.IsEnabled = true;
t_triagle.Abort();
}
}
It behaves in an unatural way, because, when I stop it, it resets to its regular position. I am assuming it, does this for some reason, that I cannot understand. Also, for some reason, this code does not get it to run constantly, but only once.
Desired functionality:
If, I hit start button, run the thread and keep on rotating, while thread is running. If, I hit, stop, then stop in the current state of rotation. If I hit start, run the thread again and keep rotating back and forth.
--
Tested with Task Async, runs slower and doesn't repeat.
private async Task AnimateTriangle()
{
double rotation = 45d;
double duration = 100d;
var anim = new DoubleAnimation
{
To = rotation,
Duration = TimeSpan.FromMilliseconds(duration),
AutoReverse = true,
RepeatBehavior = RepeatBehavior.Forever
};
var transform = MyTriangle.RenderTransform as RotateTransform;
await Task.Factory.StartNew(() =>
{
while (IsTriangleAnimRunning != false)
{
MyTriangle.Dispatcher.Invoke(() =>
{
if (transform == null)
{
transform = new RotateTransform();
MyTriangle.RenderTransform = transform;
}
transform.BeginAnimation(RotateTransform.AngleProperty, anim);
}, DispatcherPriority.ContextIdle);
if (IsTriangleAnimRunning == false)
{
MyTriangle.Dispatcher.Invoke(() =>
{
if (MyTriangle.RenderTransform is RotateTransform)
{
var angle = transform.Angle; // current animated value
transform.Angle = angle;
transform.BeginAnimation(RotateTransform.AngleProperty, null);
}
}, DispatcherPriority.ContextIdle);
}
}
});
}
You do not need to start a thread.
Start an animation that runs forever, until you reset it by setting a null animation.
Before you reset it, explicitly set the value of the target property to the current animated value.
Reuse the existing RotateTransform instead of re-assigning one each time you start the animation.
private void StartButtonClick(object sender, RoutedEventArgs e)
{
double rotation = 45d;
double duration = 750d;
var anim = new DoubleAnimation
{
To = rotation,
Duration = TimeSpan.FromMilliseconds(duration),
AutoReverse = true,
RepeatBehavior = RepeatBehavior.Forever
};
var transform = MyTriangle.RenderTransform as RotateTransform;
if (transform == null)
{
transform = new RotateTransform();
MyTriangle.RenderTransform = transform;
}
transform.Angle = 0d;
transform.BeginAnimation(RotateTransform.AngleProperty, anim);
}
private void StopButtonClick(object sender, RoutedEventArgs e)
{
if (MyTriangle.RenderTransform is RotateTransform transform)
{
var angle = transform.Angle; // current animated value
transform.Angle = angle;
transform.BeginAnimation(RotateTransform.AngleProperty, null);
}
}
Related
I have two Intel RealSense Cameras capturing and recording data. I am capturing frames for awhile and then taking the data and updating the UI with it, but it comes in spurts. I want to have a seemingly continuous flow of data coming in.
I've tried by making an asynchronous event that goes through the frames and updates as seen below:
private async void ButtonStart_Click(object sender, EventArgs e)
{
ButtonStart.Enabled = false;
try
{
Context context = new Context();
// var dev = context.QueryDevices();
//Declare RealSense pipeline, encapsulating the actual device and sensors
/*
Pipeline pipe = new Pipeline();
//create a config for the pipeline
var cfg = new Config();
//add the T265 Pose stream
cfg.EnableStream(Stream.Pose);
//start the stream
var pp = pipe.Start(cfg);
*/
Config config = new Config();
var dev = ctx.QueryDevices();
int i = 0;
foreach (var device in dev)
{
config.EnableDevice(dev[i].Info[CameraInfo.SerialNumber]);
config.EnableAllStreams();
Pipeline p = new Pipeline(ctx);
p.Start(config);
pipelines[i] = p;
i++;
}
//Pipeline pipe2 = new Pipeline();
//var cfg2 = new Config();
//cfg2.EnableStream(Stream.Color);
//cfg2.EnableStream(Stream.Depth);
// var pp2 = pipe2.Start(cfg2);
//variables for direction change
float lastX = -300;
float lastY = -300;
float lastZ = -300;
string dx = "";
string dy = "";
string dz = "";
//main loop for frames
await Task.Run(() =>
{
while (true)
{
using (var frames = pipelines[1].WaitForFrames())
{
var f = frames.FirstOrDefault(Stream.Pose);
var pose_data = f.As<PoseFrame>().PoseData;
if (lastX != -300)
{
//x
if (lastX > pose_data.translation.x)
{
dx = "LEFT";
}
else if (lastX < pose_data.translation.x)
{
dx = "RIGHT";
}
else
{
dx = "";
}
//y
if (lastY > pose_data.translation.y)
{
dy = "DOWN";
}
else if (lastY < pose_data.translation.y)
{
dy = "UP";
}
else
{
dy = "";
}
//z
if (lastZ > pose_data.translation.z)
{
dz = "FORWARD";
}
else if (lastZ < pose_data.translation.z)
{
dz = "BACKWARD";
}
else
{
dz = "";
}
}
//update the UI
UpdateUI(pose_data.translation.x.ToString(), pose_data.translation.y.ToString(), pose_data.translation.z.ToString(), dx, dy, dz);
//Set last x,y,z to see what change occurred
lastX = pose_data.translation.x;
lastY = pose_data.translation.y;
lastZ = pose_data.translation.z;
dx = "";
dy = "";
dz = "";
}
}
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
} //end catch
ButtonStart.Enabled = true;
}
public void UpdateUI(string x, string y, string z, string dx, string dy, string dz)
{
var timeNow = DateTime.Now;
if ((DateTime.Now - previousTime).Milliseconds <= 15) return;
//object o = new object();
//label for x
synchronizationContext.Post(new SendOrPostCallback(o =>
{
LabelXPosition.Text = o.ToString();
}), x);
//label for y
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelYPosition.Text = o.ToString();
}), y);
//label for z
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelZPosition.Text = o.ToString();
}), z);
//label for dx
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelLR.Text = o.ToString();
}), dx);
//label for dy
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelUD.Text = o.ToString();
}), dy);
//label for dz
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelFB.Text = o.ToString();
}), dz);
previousTime = timeNow;
}
Is there any way I can stop it from "freezing" up while reporting the data?
Device initialization
If device initialization does not need synchronization context then just include entire try body into a task:
private async void ButtonStart_Click(object sender, EventArgs e) {
ButtonStart.Enabled = false;
try
{
await Task.Run(() => {
Context context = new Context();
Config config = new Config();
...
}
} catch {
...
}
}
Initialization will be dispatched in the thread pool thread and UI won't freeze.
Please note that creating methods that are async and return void is a bad practice, event handler is an exception though.
UI dispatch
Inside of UpdateUI you dispatch 6 messages to synchronization context to update 6 labels. You can group everything to a single update:
synchronizationContext.Post(_ => {
LabelXPosition.Text = x.ToString();
LabelYPosition.Text = y.ToString()
...
}, null));
There is a small overhead on capturing your variable to closure, however, you have less events dispatched in the UI thread and also you don't box all of them by casting to an object in Post method (second argument).
DateTime.Now
DateTime.Now is rather expensive operation as it converts time to current timezone. DateTime.UtcNow is more lightweight. However, for your purposes I think Stopwatch will be much more suitable.
Throttling inside UpdateUI
There are 2 concerns here: too small time and functional issue in the algorithm.
You don't update UI if time between frames is less than 15ms:
if ((DateTime.Now - previousTime).Milliseconds <= 15) return;
Please note, that 15ms is a rather small value and I bet none user will be able to handle such refresh rate in your labels :). I think even 150ms will be too fast, so please consider a bigger value.
Also your implementation has functional issue because you drop UI dispatch inside UpdateUI, so you may report incorrect data to user, for example:
1. t=0; x=0; lastX=0
2. t=15; x=100; label=Right; lastX=100
3. t=20; x=300; <don't update>; lastX=300
4. t=30; x=250; label=Left; lastX=250
You reported to user that Pose moved to the Right when x=100 and then reported that it moved Left when x=250 what is not correct.
I'd recommend to move throttling logic to the loop so that you don't have this issue:
val sw = StopWatch.StartNew()
using (var frames = pipelines[1].WaitForFrames())
{
if(sw.ElapsedMilliseconds > 150) continue;
...
sw.Restart()
}
Other considerations
Pull vs Push
You are using push model by dispatching messages in your synchronization context. Instead you can pass these details through the state and update UI using Timer:
// Form fields:
Vector3 _currentLocation;
Vector3 _dispatchedLocation;
System.Windows.Forms.Timer _poseTimer;
private void Form1_Load(object sender, EventArgs e) {
_poseTimer = new System.Windows.Forms.Timer(this.Container){Interval=150};
_poseTimer.Tick += displayPoseChange;
}
private void displayPoseChange(object sender, EventArgs e)
{
// compare _currentLocation to _dispatchedLocation
// update labels
// replace _dispatchedLocation with _currentLocation
}
// ButtonClick method
poseTimer.Start()
...
using (var frames = pipelines[1].WaitForFrames()) {
_currentLocation = pose_data.translation // just update the field
...
}
...
poseTimer.Stop()
...
Rx.NET
You are processing frames from the pipeline and there is a great framework that could be very handy for that: Reactive Extensions
var pipeline = await initializePipeline();
var obs = Observable.FromEvent(
callback => pipeline.Start(callback),
_ => pipeline.Stop());
obs.Where(Stream.Pose) // get only Poses
.Select(f => f.As<PoseFrame>().PoseData) // select only what we need
.Where(pose_data => pose_data.translation.x != -300) // filter out?
.Sample(TimeSpan.FromMilliseconds(100)) // throttle the UI
.Buffer(2, 1) // gives you last and current items
.ObserveOn(this) // dispatch on the UI
.Subscribe(data => {var last=data[0]; var current=data[1]; /* update labels*/});
await obs.GetAwaiter();
Code above shows you the idea but I didn't try to compile it, so you might need to alight types.
More documentation and examples.
You can use Background Worker in order to do the process without freezing the form. If the concern is on the speed of the process execution and it involves a loop, you can use Task-Parallel Library
Example is Parallel.Foreach
Parallel.ForEach(dev, device=> {
config.EnableDevice(dev[i].Info[CameraInfo.SerialNumber]);
config.EnableAllStreams();
Pipeline p = new Pipeline(ctx);
p.Start(config);
pipelines[i] = p;
i++;;
});
I'm using DoubleAnimation and Storyboard to control Opacity of MediaElement. The animation itself works fine, but if I call Disappear and playVid after several seconds, the Opacity of the player remains 0! What's the problem?
public void playVid(string source, bool isMainVid)
{
player.Opacity = 1;
player.Play(); //player.Opacity is 0 here!
}
public void Disappear()
{
DoubleAnimation fadeOut = new DoubleAnimation
{
To = 0,
Duration = new Duration(TimeSpan.FromMilliseconds(1000))
};
fadeOut.Completed += (s, e) =>
{
player.Stop();
};
var storyboard = new Storyboard();
storyboard.Children.Add(fadeOut);
Storyboard.SetTargetName(fadeOut, player.Name);
Storyboard.SetTargetProperty(fadeOut, new PropertyPath(OpacityProperty));
storyboard.Begin(mainGrid, HandoffBehavior.SnapshotAndReplace); //mainGrid is player's parent
}
Use FillBehavior equal to Stop, but also set the Opacity of your player to the final opacity value (in the Completed handler). Otherwise, it will be reset to its value before the animation.
var fadeOut = new DoubleAnimation
{
To = 0,
Duration = new Duration(TimeSpan.FromMilliseconds(1000)),
FillBehavior = FillBehavior.Stop
};
fadeOut.Completed += (s, e) =>
{
player.Stop();
player.Opacity = 0;
};
See this post for other approaches.
I'm trying to capture video on Windows Phone using the AudioVideoCaptureDevice but when I try to set the captureSource I get a error message saying " Operation not valid due to the state of the object". Can you tell me what to do right in the following code?
Code snippet:
// Viewfinder for capturing video.
private VideoBrush videoRecorderBrush;
// Source and device for capturing video.
private CaptureSource _cs;
private VideoCaptureDevice _cd;
private AudioVideoCaptureDevice vcDevice;
double w, h;
// File details for storing the recording.
private IsolatedStorageFileStream isoVideoFile;
private FileSink fileSink;
private string isoVideoFileName = "iClips_Video.mp4";
private StorageFile sfVideoFile;
// For managing button and application state.
private enum ButtonState { Initialized, Stopped, Ready, Recording, Playback, Paused, NoChange, CameraNotSupported };
private ButtonState currentAppState;
// Constructor
public MainPage()
{
try
{
InitializeComponent();
//setup recording
// Prepare ApplicationBar and buttons.
PhoneAppBar = (ApplicationBar)ApplicationBar;
PhoneAppBar.IsVisible = true;
StartRecording = ((ApplicationBarIconButton)ApplicationBar.Buttons[0]);
StopPlaybackRecording = ((ApplicationBarIconButton)ApplicationBar.Buttons[1]);
StartPlayback = ((ApplicationBarIconButton)ApplicationBar.Buttons[2]);
PausePlayback = ((ApplicationBarIconButton)ApplicationBar.Buttons[3]);
SetScreenResolution();
//initialize the camera task
cameraCaptureTask = new CameraCaptureTask();
cameraCaptureTask.Completed += new EventHandler<PhotoResult>(cameraCaptureTask_Completed);
if (isFilePresent("username") && isFilePresent("Password"))
{
if (isFilePresent("IsProfilePhotoOnServer"))
{
connectToSocket();
}
else
{
SignUpProfilePhoto();
}
}
else
{
SignIn();
}
}
catch (Exception ex)
{
this.Dispatcher.BeginInvoke(delegate()
{
MessageBox.Show("Constructor Error:\n"+ ex.Message);
});
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Initialize the video recorder.
InitializeVideoRecorder();
//prepare shutter hot keys
CameraButtons.ShutterKeyHalfPressed += OnButtonHalfPress;
CameraButtons.ShutterKeyPressed += OnButtonFullPress;
CameraButtons.ShutterKeyReleased += OnButtonRelease;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Dispose of camera and media objects.
DisposeVideoPlayer();
DisposeVideoRecorder();
base.OnNavigatedFrom(e);
CameraButtons.ShutterKeyHalfPressed -= OnButtonHalfPress;
CameraButtons.ShutterKeyPressed -= OnButtonFullPress;
CameraButtons.ShutterKeyReleased -= OnButtonRelease;
}
protected override void OnOrientationChanged(OrientationChangedEventArgs e)
{
if (vcDevice != null)
{
if (e.Orientation == PageOrientation.LandscapeLeft)
{
txtDebug.Text = "LandscapeLeft";
videoRecorderBrush.RelativeTransform =
new CompositeTransform() { CenterX = 0.5, CenterY = 0.5, Rotation = 90 };
//rotate logo
if (logo != null)
{
RotateTransform rt = new RotateTransform();
rt.Angle = 90;
//default rotation is around top left corner of the control,
//but you sometimes want to rotate around the center of the control
//to do that, you need to set the RenderTransFormOrigin
//of the item you're going to rotate
//I did not test this approach, maybe You're going to need to use actual coordinates
//so this bit is for information purposes only
logo.RenderTransformOrigin = new Point(0.5, 0.5);
logo.RenderTransform = rt;
}
//rotate sign in link
if (MyGrid != null)
{
RotateTransform rt = new RotateTransform();
rt.Angle = 90;
//default rotation is around top left corner of the control,
//but you sometimes want to rotate around the center of the control
//to do that, you need to set the RenderTransFormOrigin
//of the item you're going to rotate
//I did not test this approach, maybe You're going to need to use actual coordinates
//so this bit is for information purposes only
MyGrid.RenderTransformOrigin = new Point(0.5, 0.5);
MyGrid.RenderTransform = rt;
}
}
if (e.Orientation == PageOrientation.PortraitUp)
{
txtDebug.Text = "PortraitUp";
videoRecorderBrush.RelativeTransform =
new CompositeTransform() { CenterX = 0.5, CenterY = 0.5, Rotation = 90 };
//rotate logo
if (logo != null)
{
RotateTransform rt = new RotateTransform();
rt.Angle = 0;
//default rotation is around top left corner of the control,
//but you sometimes want to rotate around the center of the control
//to do that, you need to set the RenderTransFormOrigin
//of the item you're going to rotate
//I did not test this approach, maybe You're going to need to use actual coordinates
//so this bit is for information purposes only
logo.RenderTransformOrigin = new Point(0.5, 0.5);
logo.RenderTransform = rt;
}
//rotate sign in link
if (MyGrid != null)
{
RotateTransform rt = new RotateTransform();
rt.Angle = 0;
//default rotation is around top left corner of the control,
//but you sometimes want to rotate around the center of the control
//to do that, you need to set the RenderTransFormOrigin
//of the item you're going to rotate
//I did not test this approach, maybe You're going to need to use actual coordinates
//so this bit is for information purposes only
MyGrid.RenderTransformOrigin = new Point(0.5, 0.5);
MyGrid.RenderTransform = rt;
}
}
if (e.Orientation == PageOrientation.LandscapeRight)
{
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "LandscapeRight";
// Rotate for LandscapeRight orientation.
//videoRecorderBrush.RelativeTransform =
//new CompositeTransform() { CenterX = 0.5, CenterY = 0.5, Rotation = 180 };
//rotate logo
if (logo != null)
{
RotateTransform rt = new RotateTransform();
rt.Angle = -90;
//default rotation is around top left corner of the control,
//but you sometimes want to rotate around the center of the control
//to do that, you need to set the RenderTransFormOrigin
//of the item you're going to rotate
//I did not test this approach, maybe You're going to need to use actual coordinates
//so this bit is for information purposes only
logo.RenderTransformOrigin = new Point(0.5, 0.5);
logo.RenderTransform = rt;
}
//rotate MyGrid
if (MyGrid != null)
{
RotateTransform rt = new RotateTransform();
rt.Angle = -90;
//default rotation is around top left corner of the control,
//but you sometimes want to rotate around the center of the control
//to do that, you need to set the RenderTransFormOrigin
//of the item you're going to rotate
//I did not test this approach, maybe You're going to need to use actual coordinates
//so this bit is for information purposes only
MyGrid.RenderTransformOrigin = new Point(0.5, 0.5);
MyGrid.RenderTransform = rt;
}
});
}
if (e.Orientation == PageOrientation.PortraitDown)
{
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "PortraitDown";
videoRecorderBrush.RelativeTransform =
new CompositeTransform() { CenterX = 0.5, CenterY = 0.5, Rotation = 270 };
});
}
}
}
// Hardware shutter button Hot-Actions.
private void OnButtonHalfPress(object sender, EventArgs e)
{
//toggle between video- play and pause
try
{
this.Dispatcher.BeginInvoke(delegate()
{
if (StartPlayback.IsEnabled)
{
PlayVideo();
}
if (PausePlayback.IsEnabled)
{
PauseVideo();
}
});
}
catch (Exception focusError)
{
// Cannot focus when a capture is in progress.
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = focusError.Message;
});
}
}
private void OnButtonFullPress(object sender, EventArgs e)
{
// Focus when a capture is not in progress.
try
{
this.Dispatcher.BeginInvoke(delegate()
{
if (vcDevice != null)
{
//stopVideoPlayer if it's playing back
if (currentAppState == ButtonState.Playback || currentAppState == ButtonState.Paused)
{
DisposeVideoPlayer();
StartVideoPreview();
}
if (StartRecording.IsEnabled)
{
StartVideoRecording();
}
else
{
StopVideoRecording();
}
}
});
}
catch (Exception focusError)
{
// Cannot focus when a capture is in progress.
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = focusError.Message;
});
}
}
private void OnButtonRelease(object sender, EventArgs e)
{
try
{
this.Dispatcher.BeginInvoke(delegate()
{
});
}
catch (Exception focusError)
{
// Cannot focus when a capture is in progress.
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = focusError.Message;
});
}
}
// Update the buttons and text on the UI thread based on app state.
private void UpdateUI(ButtonState currentButtonState, string statusMessage)
{
// Run code on the UI thread.
Dispatcher.BeginInvoke(delegate
{
switch (currentButtonState)
{
// When the camera is not supported by the phone.
case ButtonState.CameraNotSupported:
StartRecording.IsEnabled = false;
StopPlaybackRecording.IsEnabled = false;
StartPlayback.IsEnabled = false;
PausePlayback.IsEnabled = false;
break;
// First launch of the application, so no video is available.
case ButtonState.Initialized:
StartRecording.IsEnabled = true;
StopPlaybackRecording.IsEnabled = false;
StartPlayback.IsEnabled = false;
PausePlayback.IsEnabled = false;
break;
// Ready to record, so video is available for viewing.
case ButtonState.Ready:
StartRecording.IsEnabled = true;
StopPlaybackRecording.IsEnabled = false;
StartPlayback.IsEnabled = true;
PausePlayback.IsEnabled = false;
break;
// Video recording is in progress.
case ButtonState.Recording:
StartRecording.IsEnabled = false;
StopPlaybackRecording.IsEnabled = true;
StartPlayback.IsEnabled = false;
PausePlayback.IsEnabled = false;
break;
// Video playback is in progress.
case ButtonState.Playback:
StartRecording.IsEnabled = false;
StopPlaybackRecording.IsEnabled = true;
StartPlayback.IsEnabled = false;
PausePlayback.IsEnabled = true;
break;
// Video playback has been paused.
case ButtonState.Paused:
StartRecording.IsEnabled = false;
StopPlaybackRecording.IsEnabled = true;
StartPlayback.IsEnabled = true;
PausePlayback.IsEnabled = false;
break;
default:
break;
}
// Display a message.
txtDebug.Text = statusMessage;
// Note the current application state.
currentAppState = currentButtonState;
});
}
public async void InitializeVideoRecorder()
{
try
{
if (_cs == null)
{
_cs = new CaptureSource();
fileSink = new FileSink();
_cd = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
CameraSensorLocation location = CameraSensorLocation.Back;
var captureResolutions =
AudioVideoCaptureDevice.GetAvailableCaptureResolutions(location);
vcDevice = await AudioVideoCaptureDevice.OpenAsync(location, captureResolutions[0]);
vcDevice.RecordingFailed += OnCaptureFailed;
vcDevice.VideoEncodingFormat = CameraCaptureVideoFormat.H264;
vcDevice.AudioEncodingFormat = CameraCaptureAudioFormat.Aac;
// Initialize the camera if it exists on the phone.
if (vcDevice != null)
{
//initialize fileSink
await InitializeFileSink();
// Create the VideoBrush for the viewfinder.
videoRecorderBrush = new VideoBrush();
videoRecorderBrush.SetSource(_cs);
// Display the viewfinder image on the rectangle.
viewfinderRectangle.Fill = videoRecorderBrush;
_cs.Start();
// Set the button state and the message.
UpdateUI(ButtonState.Initialized, "Tap record to start recording...");
}
else
{
// Disable buttons when the camera is not supported by the phone.
UpdateUI(ButtonState.CameraNotSupported, "A camera is not supported on this phone.");
}
}
}
catch(Exception ex)
{
MessageBox.Show("InitializeVideoRecorder Error:\n" + ex.Message);
}
}
public async Task InitializeFileSink()
{
StorageFolder isoStore = ApplicationData.Current.LocalFolder;
sfVideoFile = await isoStore.CreateFileAsync(
isoVideoFileName,
CreationCollisionOption.ReplaceExisting);
}
private void OnCaptureFailed(AudioVideoCaptureDevice sender, CaptureFailedEventArgs args)
{
MessageBox.Show(args.ToString());
}
private void OnCaptureSourceFailed(object sender, ExceptionRoutedEventArgs e)
{
MessageBox.Show(e.ErrorException.Message.ToString());
}
// Set the recording state: display the video on the viewfinder.
private void StartVideoPreview()
{
try
{
// Display the video on the viewfinder.
if (_cs.VideoCaptureDevice != null
&& _cs.State == CaptureState.Stopped)
{
// Add captureSource to videoBrush.
videoRecorderBrush.SetSource(_cs);
// Add videoBrush to the visual tree.
viewfinderRectangle.Fill = videoRecorderBrush;
_cs.Start();
// Set the button states and the message.
UpdateUI(ButtonState.Ready, "Ready to record.");
//Create optional Resolutions
}
}
// If preview fails, display an error.
catch (Exception e)
{
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "ERROR: " + e.Message.ToString();
});
}
}
// Set recording state: start recording.
private void StartVideoRecording()
{
try
{
// Connect fileSink to captureSource.
if (_cs.VideoCaptureDevice != null
&& _cs.State == CaptureState.Started)
{
_cs.Stop();
// Connect the input and output of fileSink.
fileSink.CaptureSource = _cs;
fileSink.IsolatedStorageFileName = isoVideoFileName;
}
// Begin recording.
if (_cs.VideoCaptureDevice != null
&& _cs.State == CaptureState.Stopped)
{
_cs.Start();
}
// Set the button states and the message.
UpdateUI(ButtonState.Recording, "Recording...");
StartTimer();
}
// If recording fails, display an error.
catch (Exception e)
{
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "ERROR: " + e.Message.ToString();
});
}
}
// Set the recording state: stop recording.
private void StopVideoRecording()
{
try
{
// Stop recording.
if (_cs.VideoCaptureDevice != null
&& _cs.State == CaptureState.Started)
{
_cs.Stop();
// Disconnect fileSink.
fileSink.CaptureSource = null;
fileSink.IsolatedStorageFileName = null;
// Set the button states and the message.
UpdateUI(ButtonState.Stopped, "Preparing viewfinder...");
StopTimer();
StartVideoPreview();
}
}
// If stop fails, display an error.
catch (Exception e)
{
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "ERROR: " + e.Message.ToString();
});
}
}
// Start the video recording.
private void StartRecording_Click(object sender, EventArgs e)
{
// Avoid duplicate taps.
StartRecording.IsEnabled = false;
StartVideoRecording();
}
private void DisposeVideoRecorder()
{
if (_cs != null)
{
// Stop captureSource if it is running.
if (_cs.VideoCaptureDevice != null
&& _cs.State == CaptureState.Started)
{
_cs.Stop();
}
// Remove the event handler for captureSource.
_cs.CaptureFailed -= OnCaptureFailed;
// Remove the video recording objects.
_cs = null;
vcDevice = null;
fileSink = null;
videoRecorderBrush = null;
}
}
private void OnCaptureFailed(object sender, ExceptionRoutedEventArgs e)
{
throw new NotImplementedException();
}
//STOP WATCH
private void StartTimer()
{
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
dispatcherTimer.Start();
startTime = System.DateTime.Now;
}
private void StopTimer()
{
dispatcherTimer.Stop();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
System.DateTime now = System.DateTime.Now;
txtRecTime.Text = now.Subtract(startTime).ToString();
}
the error is thrown inside initializeVideoRecorder().
Seems like you're code is too long to analyze as #john mentioned in the comment. But since you've suggested to post some helpful links here you go: Following these steps here in msdn, would work without any issues as I've tried it once.
If you really need a sample to kick off, there's one from msdn. Hope you'll maximize the utilities.
I figured out I have to pass the capture device to the videobrush to make it work. I'm still not sure where and if I should use CaptureSource when capturing with AudioVideoCaptureDevice. Anyway here is the solution code:
// Create the VideoBrush for the viewfinder.
videoRecorderBrush = new VideoBrush();
videoRecorderBrush.SetSource(vcDevice); //substitute vcDevice with captureSource - vcDevice is the reference of AudioVideoCaptureDevice
// Display the viewfinder image on the rectangle.
viewfinderRectangle.Fill = videoRecorderBrush;
I am trying to create an animation but am having problems executing some code after the animation has occured. The animation code is as follows...
public static void Friend(Canvas canvas)
{
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "friend")
{
var slideDown = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) - element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1)),
AutoReverse = true
};
element.BeginAnimation(Canvas.BottomProperty, slideDown);
slideDown.Completed += (sender, e) => Test(sender, e, element, canvas);
}
}
}
The event to happen afterwards is...
var random = new Random();
element.Source = Core.StreamImage(Wardrobe.Friends[Wardrobe.RandomFriend()]);
Canvas.SetLeft(element, random.Next(0, (int)(canvas.ActualWidth - element.Width)));
// then reverse the previous animation.
As you can see, the event needs to keep the context of the animation in order for it to work but event handlers don't allow this. I have tried adding this code directly underneath the element.BeginAnimation but it is executed prematurely.
I have also tried splitting the parts of the animation up into seperate functions but of course you can't have multiple animations on one object as the functions don't get executed in order resulting in only the last section being played.
Here is the code i used to fix the problem i was having...
public static void Friend(Canvas canvas)
{
var random = new Random();
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "friend")
{
var slideDown = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) - element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1))
};
slideDown.Completed += (sender, e) =>
{
element.Source = Core.StreamImage(Wardrobe.Friends[Wardrobe.RandomFriend()]);
Canvas.SetLeft(element, random.Next(0, (int)(canvas.ActualWidth - element.Width)));
var slideUp = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) + element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1))
};
element.BeginAnimation(Canvas.BottomProperty, slideUp);
};
element.BeginAnimation(Canvas.BottomProperty, slideDown);
}
}
}
Try moving the event above the execution
public static void Friend(Canvas canvas)
{
foreach (var element in canvas.Children.OfType<Image>())
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "friend")
{
var slideDown = new DoubleAnimation
{
From = Canvas.GetBottom(element),
To = Canvas.GetBottom(element) - element.Height,
Duration = new Duration(TimeSpan.FromSeconds(1)),
AutoReverse = true
};
slideDown.Completed += (sender, e) => Test(sender, e, element, canvas);
element.BeginAnimation(Canvas.BottomProperty, slideDown);
}
}
}
I'm not 100% but I think the Animation becomes Freezable after it executes, so we cant modify after
BTW, you can have multiple animations on a object but you need to use "StoryBoard".
Storyboard example:
var slideDown = new DoubleAnimation { From = 100, To = 200, Duration = new Duration(TimeSpan.FromSeconds(5)), AutoReverse = true };
var slideLeft = new DoubleAnimation { From = 100, To = 200, BeginTime = TimeSpan.FromSeconds(5), Duration = new Duration(TimeSpan.FromSeconds(5)), AutoReverse = true };
Storyboard storyBoard = new Storyboard();
storyBoard.Children.Add(slideDown);
storyBoard.Children.Add(slideLeft);
Storyboard.SetTargetName(slideDown, myCanvas.Name);
Storyboard.SetTargetName(slideLeft, myCanvas.Name);
Storyboard.SetTargetProperty(slideLeft, new PropertyPath(Canvas.LeftProperty));
Storyboard.SetTargetProperty(slideDown, new PropertyPath(Canvas.TopProperty));
myCanvas.BeginStoryboard(storyBoard);
I'm making a shooting game like Space Invaders. Every time I launched the missile, it's always on the same position. How will I change it depending on the place where the spaceship is.
Here's my codes for now.
class GraphicsApplication
{
private Form f;
private PictureBox pb;
private PictureBox pb1;
private PictureBox pb2;
private Boolean bMove;
Timer Clock = new Timer();
Timer Missile = new Timer();
int x = 0;
public GraphicsApplication()
{
f = new Form();
pb = new PictureBox();
pb1 = new PictureBox();
pb2 = new PictureBox();
bMove = false;
}
public void Launch()
{
f.Size = new Size(600, 600);
f.StartPosition = FormStartPosition.CenterScreen;
f.KeyDown += new KeyEventHandler(f_KeyDown);
f.KeyPress += new KeyPressEventHandler(f_KeyPress);
pb.SetBounds(300, 470, 70, 70);
pb.Image = new Bitmap("spaceship.png");
pb.SizeMode = PictureBoxSizeMode.StretchImage;
f.Controls.Add(pb);
pb1.Image = Image.FromFile("spacedisc.png");
pb1.SetBounds(20, 20, 130, 80);
pb1.SizeMode = PictureBoxSizeMode.StretchImage;
f.Controls.Add(pb1);
pb2.Image = Image.FromFile("missile.png");
pb2.SetBounds(pb.Location.X, pb.Location.Y, 25, 40); //pb2 missile //pb spaceship
pb2.SizeMode = PictureBoxSizeMode.StretchImage;
Clock = new Timer();
Clock.Interval = 40;
Clock.Start();
Clock.Tick += new EventHandler(Clock_Tick);
Missile = new Timer();
Missile.Interval = 40;
Missile.Tick += new EventHandler(Missile_Tick);
f.ShowDialog();
}
private void f_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
Missile.Start();
}
}
public void Missile_Tick(object sender, EventArgs e)
{
if (bMove == true)
{
f.Controls.Add(pb2);
pb2.Top = pb2.Top -= 5;
}
}
private void f_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 'd')
{
pb.Left = pb.Left += 5;
}
if (e.KeyChar == 'a')
{
pb.Left = pb.Left -= 5;
}
}
public void Clock_Tick(object sender, EventArgs e)
{
if(x == 400)
{
bMove = true;
}
else if (x == 30)
{
bMove = false;
}
if (bMove == false)
{
x += 5;
pb1.Location = new Point(20 + x, 20);
}
else
{
x -= 5;
pb1.Location = new Point(x - 20, 20);
}
}
}
}
You probably want something like
pb2.Location.X = pb.Location.X;
pb2.Location.Y = pb.Location.Y;
in your f_KeyDown() function, so that the missile starts in the same location as the spaceship.
You have to position your bullets, rockets...etc. relative to your space ship's gun.
Imagine a gun that is mounted on the ship. You could represent this gun with an object.
For example:
public class Gun
{
private ISpaceshipDesign _spaceshipDesign;
public Gun(ISpaceshipDesign spaceshipDesign)
{
this._spaceshipDesign = spaceshipDesign;
}
public void Fire()
{
//...
}
}
Pass in a reference to your spaceship when creating the gun, so that you know onto which spaceship the gun is mounted.
The spaceship should always know where it is in the 2D-plane (X, Y coördinates). It should also know where on the spaceship the gun is mounted.
public interface ISpaceshipDesign
{
public Point GunLocation { get; }
}
The GunLocation property must return the gun's location relative to the ship's current position. For example:
public Point GunLocation
{
get
{
double x = (double) this.GetValue(Canvas.LeftProperty) + 21;
double y = (double) this.GetValue(Canvas.TopProperty) + 17;
return new Point(x, y);
}
}
You can then access this data in the Gun's Fire() method.
For example:
public void Fire()
{
Point gunLocation = _spaceshipDesign.GunLocation;
// Position your missle using the gun's current coördinates (X, Y).
}
About a year ago I wrote a 10-series part about creating a similar game (Asteroids) in Silverlight. One article discusses how to make the gun fire. You can find it here:
https://github.com/geersch/SilverlightAsteroids/blob/master/src/part-6/README.md
You can choose to mount several guns on the ship, one that fires regular bullets, another one for missles...etc. Each gun would have a different location on the ship. You can alter the Fire() method to be triggered by different keys (A = missle, space = bullets).
Hope this helps.