I have my custom 3D model class (Model) which contains Visual3D element and a Storyboard (sb) to hold animations related to that model. I'm trying to rotate the Visual3D element using the Storyboard but unfortunately it's not working.
Here is the code snippet
public void AnimationRotate(Model model, double duration, double startTime, RepeatBehavior behaviour)
{
//Rotate transform 3D
RotateTransform3D rotateTransform = new RotateTransform3D();
//assign transform to the model
model.Visual3D.Transform = Transform3DHelper.CombineTransform(model.Visual3D.Transform, rotateTransform);
//define the rotation axis
AxisAngleRotation3D rotateAxis = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 180);
//create 3D rotation animation
Rotation3DAnimation rotateAnimation = new Rotation3DAnimation(rotateAxis, TimeSpan.FromSeconds(0.5));
//rotation behaviour
rotateAnimation.RepeatBehavior = behaviour;
//start animation from time
rotateAnimation.BeginTime = TimeSpan.FromSeconds(startTime);
//begin animation - THIS WORKS FINE
// rotateTransform.BeginAnimation(RotateTransform3D.RotationProperty, rotateAnimation);
Storyboard.SetTargetProperty(rotateAnimation, new PropertyPath(RotateTransform3D.RotationProperty));
Storyboard.SetTarget(rotateAnimation, rotateTransform);
//add animation to the storyboard of the model
model.sb.Children.Add(rotateAnimation);
//BUT THIS APPROACH IS NOT WOKRING
model.sb.Begin();
}
The problem is described in this answer.
Instead of using Storyboard.SetTarget you need to register a name for the transform and call Storyboard.SetTargetName. Furthermore you must call Storyboard.Begin(FrameworkElement) and pass a FrameworkElement with the appropriate name scope as parameter (this here).
RegisterName("RotateTransform", rotateTransform);
Storyboard.SetTargetName(rotateAnimation, "RotateTransform");
...
model.sb.Begin(this);
Also I guess you need to clear the Storyboard's Children somewhere, or create a new Storyboard whenever the animation is started.
Related
I followed a tutorial on rotating a picture bound to a button and it should be pretty simple, yet I can't figure it out.
private void rotateMenu()
{
int rotateAngle;
if (menuState) rotateAngle = -90;
else rotateAngle = 90;
DoubleAnimation myanimation = new DoubleAnimation(0, rotateAngle, new Duration(TimeSpan.FromMilliseconds(222)));
var rotateTransform = new RotateTransform(rotateAngle, 24.5, 24.5);
menuButtonImage.RenderTransform = rotateTransform;
rotateTransform.BeginAnimation(RotateTransform.AngleProperty, myanimation);
}
First button press - rotate the picture by 90 degrees. Simple, works.
Now I want to either reverse the rotation or rotate another 90 degrees.
Animation works fine but the outcome always switches back to the picture being rotated 90 degrees, no matter what I set the second rotateAngle to.
Basically what I get is in the first part of the picture, want I need in the second part.
What am I doing wrong here? Why can't I rotate the picture again? I tried with -90, 90 and lots of other values, e.g. 45 degrees, yet no rotation happening?
That's because the picture isn't actually rotated, but it's only rendered rotated. Until the rendertransform gets a new transform, it will stay rotated.
This will rotate it back, So you could try:
DoubleAnimation myanimation2 =
new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(222)));
only use the toValue parameter overload to rotate it back.
I would make something like:
// initial angle is 0
RotateTransform _rotateTransform = new RotateTransform(0.0, 24.5, 24.5);
Duration _rotationSpeed;
private void CreateRotation()
{
_rotationSpeed = new Duration(TimeSpan.FromMilliseconds(222));
menuButtonImage.RenderTransform = _rotateTransform;
}
private void rotateToSide()
{
DoubleAnimation myanimation = new DoubleAnimation(90, _rotationSpeed);
_rotateTransform.BeginAnimation(RotateTransform.AngleProperty, myanimation);
}
private void rotateToDefault()
{
DoubleAnimation myanimation = new DoubleAnimation(0, _rotationSpeed);
_rotateTransform.BeginAnimation(RotateTransform.AngleProperty, myanimation);
}
For your information (as I can read from your code) You don't have to change the Angle property of the rotateTransform object. The rotateTransform.BeginAnimation will alter the property.
I want to move a shape using an animation and am currently using the following code. However this does not result in the object actually moving, it just seems to change the location the object is rendered in, which is expected given we are setting the crossHair.RenterTransform.
EDIT:
To clarify - I am using the animation to simulate what an input file instructions contain and as a result think I can only build the animations in code when parsing the input files. I could be wrong about this but can't see how one could do this in XAML. The input file format is not in XAML.
Because there are a number of sequential moves contained in the input file I an currently using the shapes current position as the start point for the next animation, however this does not work because it seems the animation is not actually moving the shape.
As a work around I am now changing the shapes actual position in the animations completion handler. This seems to be working.
So the question remains as to how can I use the same transform to actually move the shape rather than simply rendering it in a different position ?
private Storyboard MoveCrossHairToPoint(double x, double y)
{
// Adjust for crosshair size so its centered on the x
double xPos = x;
double yPos = y;
double xStart = Canvas.GetLeft(crossHair)+crossHair.Width/2;
double yStart = Canvas.GetTop(crossHair)+crossHair.Height/2;
// Create a NameScope for the page so that
// we can use Storyboards.
NameScope.SetNameScope(this, new NameScope());
// Create a MatrixTransform. This transform
// will be used to move the crossHair.
MatrixTransform crossHairMatrixTransform = new MatrixTransform();
crossHair.RenderTransform = crossHairMatrixTransform;
// Register the transform's name with the page
// so that it can be targeted by a Storyboard.
this.RegisterName("MoveCrossHairMatrixTransform", crossHairMatrixTransform);
// Create the animation path.
PathGeometry animationPath = new PathGeometry();
PathFigure pFigure = new PathFigure();
pFigure.StartPoint = new Point(xStart, yStart);
LineSegment lineSegment = new LineSegment(new Point(x, y),true);
pFigure.Segments.Add(lineSegment);
animationPath.Figures.Add(pFigure);
// Create a path to follow
Path path = new Path();
path.Data = animationPath;
path.Stroke = System.Windows.Media.Brushes.Green;
this.bedCanvas.Children.Add(path);
// Freeze the PathGeometry for performance benefits.
animationPath.Freeze();
// Create a MatrixAnimationUsingPath to move the
// button along the path by animating
// its MatrixTransform.
MatrixAnimationUsingPath matrixAnimation =
new MatrixAnimationUsingPath();
matrixAnimation.PathGeometry = animationPath;
double time = GetTimeForVelocityOverPath(animationPath, this.velocityMove);
matrixAnimation.Duration = TimeSpan.FromSeconds(time);
//matrixAnimation.RepeatBehavior = RepeatBehavior.;
// Set the animation's DoesRotateWithTangent property
// to true so that rotates the rectangle in addition
// to moving it.
matrixAnimation.DoesRotateWithTangent = false;
// Set the animation to target the Matrix property
// of the MatrixTransform named "ButtonMatrixTransform".
Storyboard.SetTargetName(matrixAnimation, "MoveCrossHairMatrixTransform");
Storyboard.SetTargetProperty(matrixAnimation,
new PropertyPath(MatrixTransform.MatrixProperty));
// Create a Storyboard to contain and apply the animation.
Storyboard pathAnimationStoryboard = new Storyboard();
pathAnimationStoryboard.Children.Add(matrixAnimation);
return pathAnimationStoryboard;
}
I am trying to do a basic animation using the DoubleAnimation class in WPF. On a button click the rectangle spins. The cs code is
DoubleAnimation da = new DoubleAnimation();
da.From = 0; // start from 0 to 360, full circle
da.To = 360;
da.Duration = new Duration(TimeSpan.FromSeconds(0.5));
da.RepeatBehavior = RepeatBehavior.Forever;
RotateTransform rt = new RotateTransform();
rectangle1.RenderTransform = rt;
rt.BeginAnimation(RotateTransform.AngleProperty, da);
What i am trying to do is to update the da.Duration = new Duration(TimeSpan.FromSeconds(0.5)) value with the slider value.
Tried with slider value binding, but not able to get this correctly.
Changing the Duration is not enough. How fast the animation is controlled by the inner AnimationClock. This clock is initialized just once after you call BeginAnimation(). So you have to update the inner AnimationClock by using ApplyAnimationClock() method.
Also note that you should also set the From property to the currently animated value, so that when changing the Slider's Value, the Angle will be animated smoothly (without restarting from 0):
//The ValueChanged handler for your Slider
private void slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs e){
da.Duration = new Duration(TimeSpan.FromMilliseconds(yourSlider.Value));
da.From = rt.Angle;
rt.ApplyAnimationClock(RotateTransform.AngleProperty, da.CreateClock());
}
Note that I suppose the slider's Value corresponds to the number of milliseconds, so the Maximum value of the Slider should be large enough (about several thousands) if you don't want your Rectangle to be rotated super fast. Also all the variables you declared should be able to access inside the ValueChanged handler.
One more important note is you should use By property here instead of To, like this:
da.By = 360;
That way you can just update the From without caring about the To (it is always 360 degrees rotated).
I am trying to set up an extremely simple XNA game with a 3d terrain and some 2d GUI objects on top. I chose Nuclex since that seems to be one of the few 2d GUIs that's currently active.
My problem: adding a Nuclex "screen" class to my game results in the 3d terrain and lines drawn on top of it to screw up -- I have three layers of 3d objects: terrain, a wireframe outline of the terrain and some "routes" that hug the terrain. With the screen added, the routes that are partially-submerged into the terrain appear entirely on top of the terrain, and the wireframe grid appears thicker.
This is the tail-end of my Game.Initialize method, as made by following the sample on the Nuclex GuiManager page:
InputManager im = new InputManager();
IGraphicsDeviceService igds = Nuclex.Graphics.GraphicsDeviceServiceHelper.MakeDummyGraphicsDeviceService(GraphicsDevice);
gui = new GuiManager(igds, im);
gui.Initialize();
Viewport vp = GraphicsDevice.Viewport;
Screen main_screen = new Screen(vp.Width, vp.Height);
//main_screen.
this.gui.Screen = main_screen;
main_screen.Desktop.Bounds = new UniRectangle(
new UniScalar(0.1f, 0.0f), new UniScalar(0.1f, 0.0f), // x and y
new UniScalar(0.8f, 0.0f), new UniScalar(0.8f, 0.0f) // width and height
);
LabelControl text = new LabelControl("hello");
text.Bounds = new UniRectangle(10, 10, 200, 30);
main_screen.Desktop.Children.Add(text);
Components.Add(gui);
base.Initialize();
So, any ideas as to what I'm doing wrong?
Thanks!
Right, so thanks to catflier who pointed me in the direction of render states, I fixed the problem:
* First, "RenderState" is an XNA3 term; XNA4 simply allows a few state flags directly on the GraphicsDevice object.
* The problem is that Nuclex is secretly setting the GraphicsDevice.DepthStencilState and leaving it instead of restoring it to its previous value after use (naughty!!).
* So the solution is, at the top of your Draw method, to add a line like so:
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
Im trying to use the TranslateTransform class to move a image on a Grid on Y axis. I need this movment to be smooth so i can not use SetMargin or SetCanvas. I try this in code behind:
public void MoveTo(Image target, double oldY, double newY)
{
var trans = new TranslateTransform();
var anim2 = new DoubleAnimation(0, newY, TimeSpan.FromSeconds(2))
{EasingFunction = new SineEase()};
target.RenderTransform = trans;
trans.BeginAnimation(TranslateTransform.YProperty, anim2);
}
The object i want to use (a Image control) is placed on a Grid.
For the first time everything works fine.
The problems comes when i try to move the object again using the same function.
The object (a Image control) first move to the start position (initial Y coordinate) then the animation begins.
Is it not suposed for the TranslateTransform to change the coordinates (in my case the Margin property) too?
Thank you.
The transform does not change the original values.they are your point of origin. If you want a new point of origin each time you move you can handle the animation completed event. Or from your transform you can get your current offset and make that your new start point for the animation.
In other words your start values would always be your last move to values
The TranslateTransform is a specific kind of render transformation. Rather that changing properties of the control (such as the Margin property), it simply affects how the control is displayed on the screen.
You have to use the By property of DoubleAnimation.
Try that:
//everytime you execute this anmation your object will be moved 2.0 further
double offset = 2.0
var anim2 = new DoubleAnimation(newY, TimeSpan.FromSeconds(2));
anim2.To = null;
anim2.By = offset;
You've explicitly told the animation to start from 0. It's doing what you've told it.
Just remove the explicit zero fromvalue and everything will work.
var anim2 = new DoubleAnimation(newY, TimeSpan.FromSeconds(2))
{ EasingFunction = new SineEase() };