Tìm hiểu về cách sử dụng Farseer Physics Engine trong lập trình C# xaml win 8.
Tôi xin giới thiệu với các bạn về một engine đơn giản, dễ sử dụng, có thể áp dụng trong các game sử lý tương tác va đập giữa các object, giả lập trọng lực của vật. Farsser physics engine dùng rất tốt trong các game 2D, nó giúp chúng ta tính toán nhanh các va đập giữa các object, lực ma sát với bề mặt và trọng lực tác dụng lên vật, ví dụ như game bia-a, đá bóng (mini game). Điều thú vị nhất của engine này là chúng ta có thể add ngay thuộc tính engine này vào thẻ xaml trong xaml form và object được set sẽ trở lên sinh động hơn rất nhiều.
Link download farseer physics engine:
https://physicshelperxaml.codeplex.com/
1.Bắt đầu tạo project đơn giản với farsser physics engine
Tạo project C# xaml winrt.
Add reference đến namespace System.Windows.Interactivity
2. Chỉnh sửa file MainPage.xaml.
Để sử dụng farseer dll, trước hết chúng ta cần sử dụng thẻ <Canvas> trong xaml thay cho <Grid> để tọa độ các element trên layout được cố định. Vì thế cần đổi <Grid> sang <Canvas>
<UserControl x:Class="DemoPhysicsEngine.MainPage"
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" d:DesignWidth="640" d:DesignHeight="480">
<Canvas x:Name="LayoutRoot" Background="Black">
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="215" Canvas.Top="433"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="245" Canvas.Top="433"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="433"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="433"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="335" Canvas.Top="433"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="365" Canvas.Top="433"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="230" Canvas.Top="403"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="283"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="313"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="313"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="320" Canvas.Top="343"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="343"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="260" Canvas.Top="343"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="335" Canvas.Top="373"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="373"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="373"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="245" Canvas.Top="373"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="350" Canvas.Top="403"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="320" Canvas.Top="403"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="403"/>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="260" Canvas.Top="403"/>
<Rectangle Fill="White" StrokeThickness="2" Height="29" Width="640" Canvas.Left="0" Canvas.Top="475"/>
<Ellipse Fill="Red" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Top="173" Canvas.Left="255"/>
</Canvas>
</UserControl>
3. Add thêm namespace physics engine
<UserControl x:Class="DemoPhysicsEngine.MainPage"
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"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:pb="clr-namespace:Spritehand.PhysicsBehaviors;assembly=Spritehand.PhysicsBehaviors"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
Namespace của engine là
-
System.Windows.Interactivity
-
Spritehand.PhysicsBehaviors
4. Add thêm PhysicsControllerBehavior
<Canvas x:Name="LayoutRoot" Background="Black">
<i:Interaction.Behaviors>
<pb:PhysicsControllerBehavior MousePickEnabled="True"/>
</i:Interaction.Behaviors>
Như các bạn thấy, chúng ta đã add thêm property PhysicsControllerBehavior vào thẻ <Canvas>.Thuộc tính này cho phép chúng ta có thể kéo tất cả các control con trong thẻ canvas, các control được định nghĩa là PhysicsObject sẽ có các tính chất vật lý như: va đập được với nhau, rơi từ trên cao xuống thấp, ném một vật từ dưới đất lên.
5. Creating PhysicsObjects
Để PhysicsController nhận dạng được các object cần sử lý tính toán vật lý, chúng ta cần add thêm property object [PhysicsObjectBehavior]
Ví dụ như sau:
<Ellipse Fill="Red" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Top="173" Canvas.Left="255">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Ellipse>
hoặc
<Rectangle Fill="White" StrokeThickness="2" Height="29" Width="640" Canvas.Left="0" Canvas.Top="475">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior IsStatic="True"/>
</i:Interaction.Behaviors>
</Rectangle>
Sau khi làm các bước trên, PhysicsController sẽ tự động nhận canvas là một platform để các object trên nó tương tác vật lý với nhau.
Với các control còn lại ta làm tương tự, source code main page lúc đó sẽ như sau:
<UserControl x:Class="DemoPhysicsEngine.MainPage"
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"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:pb="clr-namespace:Spritehand.PhysicsBehaviors;assembly=Spritehand.PhysicsBehaviors"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Canvas x:Name="LayoutRoot" Background="Black">
<i:Interaction.Behaviors>
<pb:PhysicsControllerBehavior MousePickEnabled="True"/>
</i:Interaction.Behaviors>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="215" Canvas.Top="433">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="245" Canvas.Top="433">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="433">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="433">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="335" Canvas.Top="433">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="365" Canvas.Top="433">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="230" Canvas.Top="403">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="283">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="313">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="313">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="320" Canvas.Top="343">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="343">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="260" Canvas.Top="343">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="335" Canvas.Top="373">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="305" Canvas.Top="373">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="275" Canvas.Top="373">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="245" Canvas.Top="373">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="350" Canvas.Top="403">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="320" Canvas.Top="403">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="290" Canvas.Top="403">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Left="260" Canvas.Top="403">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
<Rectangle Fill="White" StrokeThickness="2" Height="29" Width="640" Canvas.Left="0" Canvas.Top="475">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior IsStatic="True"/>
</i:Interaction.Behaviors>
</Rectangle>
<Ellipse Fill="Red" Stroke="#FFB9B9B9" StrokeThickness="2" Height="25" Width="25" Canvas.Top="173" Canvas.Left="255">
<i:Interaction.Behaviors>
<pb:PhysicsObjectBehavior/>
</i:Interaction.Behaviors>
</Ellipse>
</Canvas>
</UserControl>
6. Tổng kết
Với farseer engine, bạn không cần add bất kỳ dòng code behind nào, mọi việc chúng ta làm chỉ đơn giản là add thêm property PhysicsObjectBehavior
là đã mô phỏng được các tính chất vật lý như là va đập hay trọng lực của trái đất.
Source code demo:
https://onedrive.live.com/?cid=2b7007e9ec2ae37b&id=2B7007E9EC2AE37B!2246
Một vài ví dụ áp dụng farseer engine
http://printablecoloringpagesforkids.hskac.com/codeplex-farseer-physics-engine-home.html