User Control

Jan 12, 2009 at 2:52 PM
Hi

I can't seem to get anything working with user controls. Does everything need to be created in page.xaml for it to have physics applied to it?

For example; I tried creating a simple car as a control with joins on the wheels and that I could control with the arrow keys. When this got put into page.xaml the physics controller and joins just showed as images in both the designer and at run time. The only way I can see around this would be to create everything in page.xaml, though obviously this isn't ideal.

Am I missing an obvious solution or are there plans for user control support in the future?

Great job on the helper anyway, I was tearing my hair out trying to get anywhere with the engine on its own

Cheers

Craig.
Coordinator
Jan 12, 2009 at 2:58 PM
Hi Craig,

Yes, User Controls are supported and you can put your Joint and Static Controllers right inside the User Control XAML, and they will be applied at runtime.

A couple of the demos use this technique, like the Rag Doll demos and the "Simple Physics Game" demo - so you could take a peek at them.

From the description of your problem, it could be you didn't name your User Controls that you dropped onto your main Page.xaml (make sure they have an x:Name="someName" attribute, otherwise the physics helper won't automatically add them to the simulation).

Hope this helps!
Jan 12, 2009 at 3:42 PM

Hi Andy

Heres what I get: linkey

And here the xaml:



Page.xaml

<UserControl x:Class="Physics2.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="3754" Height="496" xmlns:Spritehand_FarseerHelper="clr-namespace:Spritehand.FarseerHelper;assembly=Spritehand.FarseerHelper" xmlns:Physics2="clr-namespace:Physics2">
 <Canvas x:Name="LayoutRoot" Background="White">
  <Spritehand_FarseerHelper:PhysicsStaticHolder Canvas.Left="260" Canvas.Top="420" Body="Floor"/>
  <Path Height="421.116" Width="3647.5" Canvas.Left="23.5" Canvas.Top="19.174" Fill="#FF160202" Stretch="Fill" Stroke="#FF000000" Data="M23.999981,426.00003 C51.615826,412.47095 83.194855,385.32544 106.84751,385.41275 C130.50017,385.50006 238.84753,395.41275 304.84753,400.41275 C354.73172,405.44186 418.49936,415.50009 454.50006,415.50006 C486.58282,409.31921 538.39288,382.44308 588.00012,378.00009 C672.75671,373.51895 692.16339,380.69122 708.00018,388.50006 C740.00018,398.50006 772.52887,416.74335 802.53003,401.74353 C832.53119,386.74371 877.53009,374.74356 915.03009,361.24359 L1257.0005,363.00009 L1314.0006,405.00003 L1489.5007,391.50006 L1584.0009,346.50009 L1644.001,291.00012 L1753.5011,229.50017 L1795.5012,394.50009 L2104.5015,397.50009 L2310.0017,378.00009 C2310.0017,378.00009 2347.502,312.00015 2352.002,310.50015 C2356.502,309.00015 2485.5022,274.50018 2485.5022,274.50018 L2836.5027,258.00018 L2971.5029,292.50015 L3064.5032,363.00012 L3312.0039,318.00015 L3403.5044,315.00015 L3472.5049,307.50015 L3480.0054,198.00023 L3472.5056,19.500401 L3670.5059,37.50037 L3670.5059,436.5 C2455.0083,441.52936 1239.5083,442.03555 23.999981,426.00003 z" x:Name="Floor"/>
  <Physics2:Car Height="156" Width="294" Canvas.Left="138.25" Canvas.Top="171.5" x:Name="Car1"/>
  <Spritehand_FarseerHelper:PhysicsController Canvas.Left="102" Canvas.Top="58.5" x:Name="PhysicsController1"/>
 </Canvas>
</UserControl>


Car.xaml

<UserControl
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 mc:Ignorable="d"
 x:Class="Physics2.Car"
 Height="222" Width="323" xmlns:Spritehand_FarseerHelper="clr-namespace:Spritehand.FarseerHelper;assembly=Spritehand.FarseerHelper">

 <Grid x:Name="LayoutRoot">
  <Path Fill="#FFEE0A0A" Stretch="Fill" Stroke="#FF000000" Data="M0.5,0.5 L239.5,0.5 L239.5,73.5 L319.5,73.5 L319.5,152.5 L0.5,152.5 L0.5,80.5 L0.5,73.5 z" x:Name="CarBody" Margin="0,0,3,61"/>
  <Ellipse HorizontalAlignment="Left" Margin="14,0,0,-1" Width="104" Fill="#FF7E7373" Stroke="#FF000000" StrokeThickness="15" x:Name="RearWheel" Height="104" VerticalAlignment="Bottom"/>
  <Ellipse Margin="0,0,55,1" Fill="#FF7E7373" Stroke="#FF000000" StrokeThickness="15" x:Name="FrontWheel" Height="104" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="104"/>
  <Spritehand_FarseerHelper:PhysicsJoint HorizontalAlignment="Left" Margin="56,0,0,41" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,0.4" BodyOne="RearWheel" BodyTwo="CarBody" CollisionGroup="1" d:LayoutOverrides="Height" x:Name="joinRear"/>
  <Spritehand_FarseerHelper:PhysicsJoint Margin="0,0,98,43" RenderTransformOrigin="0.5,0.4" HorizontalAlignment="Right" VerticalAlignment="Bottom" BodyOne="FrontWheel" BodyTwo="CarBody" CollisionGroup="1" d:LayoutOverrides="Width, Height" x:Name="JoinFront"/>
  <Spritehand_FarseerHelper:PhysicsController Height="19" HorizontalAlignment="Left" Margin="47,50,0,0" VerticalAlignment="Top" Width="25" x:Name="PhysicsController1"/>
 </Grid>
</UserControl>



Any ideas?

Also, I'm assuming I can put a physics controller in the user control to handle the movement of the car? Will it conflict or anything?

Cheers for the quick response and your help.

Craig.

Coordinator
Jan 12, 2009 at 10:48 PM
Hey Craig, it actually looks like your demo is OK - it's just that it is taking _forever_ to compute the Geometry of the "Floor" path object. One of the things the PhysicsController does is determine the outline of each shape and translate that into a Geometry object in Farseer. To do this, it has an algorithm to basically trace the outline of the UI element. For an element as big as "Floor" (about 3650 x 420 pixels), that can take a long time.

So what I would suggest is, for a quick fix you can resize Floor so that it is much smaller. Then, as a better fix, create a few "terrain" type user controls for each bit of terrain shape, and then re-use those User Controls on your main page. The reason this will be faster is because the PhysicsController will cache the outline for a user control and then re-use that outline the next time it encounters the same type of User Control.

Hope this helps!
Jan 13, 2009 at 8:17 AM
Hi Andy

I think it is past calculating that path as the whole car actually acts as a physics object (it drops then collides with the floor) but the joins and physics controller are showing like images (only on the car control).

Cheers

Craig
Jan 14, 2009 at 10:32 AM

As an update this is what I have found so far (some of these things might be stating the obvious, and all of them are probably because I am missing something):

  • Anything to do with physics doesn't work on a user control if the root is a grid - it must be a canvas. Changing it to a canvas stopped the controller and joins showing as images.
  • A physics controller on a user control does not work when put into a normal page. It compains that each UIElement is too small to map.
  • A normal user control has every object inside it mapped for collisions but not as an independant physics sprite, so I can't do something like 

    PhysicsController1.PhysicsObjects("car.RearWheel").BodyObject.ApplyTorque(-6000)

All I am really hoping to achieve at the minute is to be able to make a car in a user control. This car would have a function Accelerate that a parent page could call which would accelerate the wheels, or the parent page be able to accelerate the wheels inside the control.

Hope this makes sense.

Cheers

Craig.

Coordinator
Jan 14, 2009 at 12:09 PM

Hey Craig,

Sorry for the delay in getting back - it looks like you figured out most of the issues yourself. Because the Physics Helper relies on certain things in your XAML, it can be tricky, as you found out. I need to create a "Set of Rules" for using the PhysicsHelper to avoid frustration.

Things (some that you mentioned) such as:

  • always use a Canvas element for a Container - not a Grid or other layout container.
  • always specify a Height and Width on the elements you want translated into Physics objects
  • you can only have ONE PhysicsController in your simulation, and it should exist on the main Canvas (not nested inside a User Control). The Physics Controller specifies Gravity and other settings which need to be Application-Wide.
  • All Physics Objects (including those nested inside User Controls) are available using the PhysicsController.PhysicsObjects Collection. To get to the Physics Object, and for example, apply torque, you would use:

    physicsController1.PhysicsObjects["head1"].BodyObject.ApplyTorque(-6000);

    ... in cases where you add multiple instances of the same User Control into the simulation, each nested physics object will have a numeric suffix appended to it's name, such as "carBody_1", "carBody_2", and so on. If you are ever unsure of what a Physics Object ended up being named, place a breakpoint inside the PhysicsController_Initialized event and place a watch on the PhysicsController.PhysicsObjects  collection. This will show you the name of each object in the simulation.

So, to correct your original car sample:



Page.xaml

<UserControl x:Class="PhysicsHelperRecreate.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="3754" Height="496" xmlns:Spritehand_FarseerHelper="clr-namespace:Spritehand.FarseerHelper;assembly=Spritehand.FarseerHelper" xmlns:PhysicsHelperRecreate="clr-namespace:PhysicsHelperRecreate">
    <Canvas x:Name="LayoutRoot" Background="White">
        <Spritehand_FarseerHelper:PhysicsStaticHolder Canvas.Left="260" Canvas.Top="420" Body="Floor"/>
        <Path Height="175.116" Width="665.5" Canvas.Left="23.5" Canvas.Top="277.174" Fill="#FF160202" Stretch="Fill" Stroke="#FF000000" Data="M23.999981,426.00003 C51.615826,412.47095 83.194855,385.32544 106.84751,385.41275 C130.50017,385.50006 238.84753,395.41275 304.84753,400.41275 C354.73172,405.44186 418.49936,415.50009 454.50006,415.50006 C486.58282,409.31921 538.39288,382.44308 588.00012,378.00009 C672.75671,373.51895 692.16339,380.69122 708.00018,388.50006 C740.00018,398.50006 772.52887,416.74335 802.53003,401.74353 C832.53119,386.74371 877.53009,374.74356 915.03009,361.24359 L1257.0005,363.00009 L1314.0006,405.00003 L1489.5007,391.50006 L1584.0009,346.50009 L1644.001,291.00012 L1753.5011,229.50017 L1795.5012,394.50009 L2104.5015,397.50009 L2310.0017,378.00009 C2310.0017,378.00009 2347.502,312.00015 2352.002,310.50015 C2356.502,309.00015 2485.5022,274.50018 2485.5022,274.50018 L2836.5027,258.00018 L2971.5029,292.50015 L3064.5032,363.00012 L3312.0039,318.00015 L3403.5044,315.00015 L3472.5049,307.50015 L3480.0054,198.00023 L3472.5056,19.500401 L3670.5059,37.50037 L3670.5059,436.5 C2455.0083,441.52936 1239.5083,442.03555 23.999981,426.00003 z" x:Name="Floor"/>
        <Spritehand_FarseerHelper:PhysicsController MousePickEnabled="True" Canvas.Left="102" Canvas.Top="58.5" x:Name="PhysicsController1"/>
        <PhysicsHelperRecreate:car Height="156" Width="294" Canvas.Left="138.25" Canvas.Top="171.5" x:Name="Car1"/>
    </Canvas>
</UserControl>

 


 
Car.xaml

<UserControl
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 mc:Ignorable="d"
 x:Class="PhysicsHelperRecreate.car"
 Height="223" Width="332" xmlns:Spritehand_FarseerHelper="clr-namespace:Spritehand.FarseerHelper;assembly=Spritehand.FarseerHelper">
    <Canvas x:Name="LayoutRoot" Width="355" Height="234">
        <Path Fill="#FFEE0A0A" Stretch="Fill" Stroke="#FF000000" Width="320" Height="150" Data="M0.5,0.5 L239.5,0.5 L239.5,73.5 L319.5,73.5 L319.5,152.5 L0.5,152.5 L0.5,80.5 L0.5,73.5 z" x:Name="CarBody" Canvas.Top="16" Canvas.Left="9"/>
        <Ellipse HorizontalAlignment="Left" Width="104" Fill="#FF7E7373" Stroke="#FF000000" StrokeThickness="15" x:Name="RearWheel" Height="104" VerticalAlignment="Bottom" Canvas.Top="104" Canvas.Left="4"/>
        <Ellipse Fill="#FF7E7373" Stroke="#FF000000" StrokeThickness="15" x:Name="FrontWheel" Height="104" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="104" Canvas.Top="104" Canvas.Left="212"/>
        <Spritehand_FarseerHelper:PhysicsJoint HorizontalAlignment="Left" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,0.4" BodyOne="RearWheel" BodyTwo="CarBody" CollisionGroup="1" d:LayoutOverrides="Height" x:Name="joinRear" Canvas.Top="145" Canvas.Left="45"/>
        <Spritehand_FarseerHelper:PhysicsJoint RenderTransformOrigin="0.5,0.4" HorizontalAlignment="Right" VerticalAlignment="Bottom" BodyOne="FrontWheel" BodyTwo="CarBody" CollisionGroup="1" d:LayoutOverrides="Width, Height" x:Name="JoinFront" Canvas.Top="146" Canvas.Left="254"/>
    </Canvas>
</UserControl>

Jan 14, 2009 at 12:31 PM
Edited Jan 14, 2009 at 12:51 PM
Hi Andy

Thanks for getting back to me.

Sorry if I'm being simple but how would I apply torque to one 'wheel' inside a user control?

Say I have a user control called Car, and within that there are two elipses - 'frontWheel' and 'rearWheel', I just want to apply torque to the rear wheel. I cant figure how to tell the physics controller 'rearWheel inside car' (Car.rearWheel is what I naturally tried) and when I looked at the physics object collection for the page 'Car' was included but none of the elements inside 'Car' were.

<strike>Also with your appended XAML I get the error "The namespace prefix 'PhysicsHelperRecreate' is not defined". Is this what is meant to add all the car elements as individual sprites?
</strike>EDIT: Sorry, this was obvious when I actually looked at the error.

Thanks for your continued support here - apologies for taking up so much of your time.

Cheers

Craig.
Coordinator
Jan 14, 2009 at 1:05 PM
Once you incorporate the changes in the XAML above, you will see 4 Physics Objects in the collection (Floor, CarBody, RearWheel, and FrontWheel).

The reason for the "not defined" error in the XAML is because I recreated your issue in a new Silverlight project with a different name (so a diff Namespace too).

In the XAML I posted above, just replace "PhysicsHelperRecreate" with "Physics2" (which was in your original XAML that you posted). This is how Silverlight resolves the class to use for code-behind logic for the XAML.
Jan 14, 2009 at 2:45 PM
That works a treat but I'm struggeling to see what you did? Is it just down to setting the size on the canvas?

Sorry about the not defined error - I realised my mistake and edited the post (as you can see) but I must of been a fraction too late.

Thanks for helping me sort it.

Cheers

Craig.