上一篇文章我們討論了牛頓第二定理,理論上依照牛二定理和微積分的知識就可以求出大多數情況下物體的運動情況,并且上一篇文章中實現的數學引擎未能模擬這樣一個問題:物體遭到一個沖擊力的作用而獲得初速率,這就是這篇文章即將討論的問題。
化學知識
首先看這樣一道習題:光滑水平桌面上一個質量為1kg的物體初始速率為1m/s,遭到初速方向2N的水平推力運動了2s,求2s末物體的速率。
解:按照牛頓定理:a=F/m=2m/s2
按照運動學公式:vt=v0+at=5m/s
十分簡單的一道題。若果將以上兩式聯立,得到的多項式為:
vt-v0=Ft/m
整理成
Ft=mvt-mv0
代入數據也能得到相同的結果。假如我們將Ft定義為一個新的數學量:沖量I,mv定義為一個新的數學量:動量p,這么就獲得了一個新的數學規律:

ΣI=Δp
這個規律稱為動量定律,即:物體所受合力的沖量等于物體的動量變化量。
依據動量定律,力F還可以表示為動量的變化率,即:
F=dp/dt
按照這個定理什么是質點的動量定理,若物體遭到一個沖擊力作用時,我們常常難以曉得這個沖擊力的大小和作用時間,并且可以直接設置一個沖量,就可以求得物體的速率并進一步求出位移,公式如下:
vt=v0+I/m
傳統的熱學教材是以牛頓運動三定理為核心來展開的,并把質量和力作為動力學中最基本的概念什么是質點的動量定理,因而導入動量和能量的概念以及其他守恒定理。但是從現代數學的高度來看,在描述物質的運動和互相作用時,動量和能量的概念要比力的概念基本得多。我看過的《新概念數學教程熱學》就是這樣編排的,先介紹動量,之后從動量推出牛頓定理。2008年廣州市曾向數學班主任推薦過一套日本中學數學教材《卡爾斯魯厄數學課程》也是這個思路,與我國傳統的中學教材有很大不同,似乎我區的太原五中還進行了試點教學。
在《GAME》一書的第一章中作者也談到:大多數數學引擎是以力為基礎創建(force-)的,將動量看成短時間內作用的力,缺點是處理力要比處理動量難;還有些數學引擎基于動量(-),缺點是當幀頻不夠時,原先靜止的物感受發生聯通;極少數數學引擎使用力處理靜止接觸,使用動量處理碰撞。
在中的實現
十分簡單,只需在Body類中添加一個方式即可:

////// 施加線性沖量。 /// /// 沖量 public void ApplyLinearImpulse(Vector2 impulse) { if (isStatic) return; dv.X = impulse.X * inverseMass; dv.Y = impulse.Y * inverseMass; LinearVelocity.X = dv.X + LinearVelocity.X; LinearVelocity.Y = dv.Y + LinearVelocity.Y; }
這個方式施加的是線性沖量,在之后提到質心動力學時都會添加角沖量。
示例
接出來,我們就創建一個程序演示新代碼的用法。首先為了讓代碼更清晰,我創建了一個.cs文件,代碼如下:
namespace Stun2DPhysics4SLDemo
{
public abstract class Sprite
{
// 與Sprite鏈接的UserControl對象
protected UserControl ucSpriteUC;
// 父Canvas控件
protected Canvas cnvParent;
// 獲取或設置UseControl的位置
public Point Location { get;set;}
public Sprite(Canvas setCnvParent, Point initialLocation)
{
cnvParent = setCnvParent;
ucSpriteUC = CreateSpriteUC();
Location = initialLocation;
cnvParent.Children.Add(ucSpriteUC);
// 設置UseControl的初始位置
ucSpriteUC.SetValue(Canvas.LeftProperty, Location.X);
ucSpriteUC.SetValue(Canvas.TopProperty, Location.Y);
}
public abstract UserControl CreateSpriteUC();
public virtual void Update()
{
}
}
public abstract class SpritePhysice : Sprite
{
public Body Body { get; private set; }
public SpritePhysice(Canvas cnvParent, Point initialLocation, PhysicsSimulator physicsSimulator)
: base(cnvParent, initialLocation)
{
Body = new Body();
Body.Position = new Vector2((float)initialLocation.X, (float)initialLocation.Y);
physicsSimulator.Add(Body);
}
public override void Update()
{
ucSpriteUC.SetValue(Canvas.LeftProperty, Convert.ToDouble(Body.Position.X));
ucSpriteUC.SetValue(Canvas.TopProperty, Convert.ToDouble(Body.Position.Y));
}
}
public class Box1Sprite : SpritePhysice
{
public Box1Sprite(Canvas cnvParent, Point initialLocation, PhysicsSimulator physicsSimulator)
: base(cnvParent, initialLocation,physicsSimulator)
{
}
public override UserControl CreateSpriteUC()
{
return new Box1();
}
}
public class Box2Sprite : SpritePhysice
{
public Box2Sprite(Canvas cnvParent, Point initialLocation, PhysicsSimulator physicsSimulator)
: base(cnvParent, initialLocation, physicsSimulator)
{
}
public override UserControl CreateSpriteUC()
{
return new Box2();
}
}
}
此文件包含四個類,首先是具象泛型,它鏈接了一個勾畫圖象的,派生類可以重畫具象方式()初始化這個,派生類還可以重畫虛擬方式()編撰更新時的行為。倘若無需化學屬性,就可以從這個類承繼。具象類類承繼自類,額外多了用于數學估算的Body類,并在()方式中用Body的位置控制中的位置。最后兩個和類就代表在屏幕上出現的兩個方形。
后臺.xaml.cs代碼如下:
namespace Stun2DPhysics4SLDemo
{
public partial class MainPage : UserControl
{
// 當一幀繪制結束時保存當前時刻
private DateTime LastTick;
// 物理引擎
PhysicsSimulator physicsSimulator;
// 保存Sprite的集合
private List sprites;
Box1Sprite box1;
Box2Sprite box2;
bool isRunning = false ;
public MainPage()
{
InitializeComponent();
physicsSimulator = new PhysicsSimulator();
physicsSimulator.Gravity = new Vector2(0, 150);
sprites = new List();
box1 = new Box1Sprite(LayoutRoot, new Point(16, 16), physicsSimulator);
box1.Body.LinearVelocity = new Vector2(150, 0);
box2= new Box2Sprite(LayoutRoot, new Point(768, 32), physicsSimulator);
box2.Body.IgnoreGravity = true;
sprites.Add(box1);
sprites.Add(box2);
}
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
// 保存自上一次更新以來流逝的時間
TimeSpan elapsedTime = (DateTime.Now - LastTick);
physicsSimulator.Update((float)elapsedTime.TotalSeconds);
// 對box1施加一個水平向右的風力
const float forceAmount = 50;
Vector2 force = Vector2.Zero;
force += new Vector2(forceAmount, 0);
box1.Body.ApplyForce(force);
for (int i = 0; i < sprites.Count; i++)
{
sprites[i].Update();
}
// 處理物體在邊界上的碰撞,以后會通過引擎中的碰撞檢測實現
if (box1.Body.Position.X > 784 || box1.Body.Position.X < 16)
box1.Body.LinearVelocity.X *= -1;
if (box1.Body.Position.Y > 464 || box1.Body.Position.Y < 16)
box1.Body.LinearVelocity.Y *= -1;
if (box2.Body.Position.X > 768 || box2.Body.Position.X < 32)
box2.Body.LinearVelocity.X *= -1;
if (box2.Body.Position.Y > 448 || box2.Body.Position.Y < 32)
box2.Body.LinearVelocity.Y *= -1;
// 保存當前時刻
LastTick = DateTime.Now;
}
private void btnStart_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (!isRunning)
{
btnStart.Content = "暫停";
// 保存當前時刻
LastTick = DateTime.Now;
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
this.KeyDown += new KeyEventHandler(Page_KeyDown);
isRunning = true;
}
else
{
btnStart.Content = "繼續";
CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering);
this.KeyDown -= new KeyEventHandler(Page_KeyDown);
isRunning = false;
}
}
private void Page_KeyDown(object sender, KeyEventArgs e)
{
// 如果按下A鍵則對box2施加一個向左的沖量
if(e.Key==Key.A)
{
box2.Body.ApplyLinearImpulse (new Vector2(-100,0));
box2.Body.IgnoreGravity = false;
}
}
}
}
對右邊的圓形施加了一個水平向左的恒力模擬風力,在這個風力的影響下,我們可以顯著見到圓形往右聯通的水平射速要比向左運動的大。對于左邊的圓形,一開始是不動的,按下按鍵的A鍵可以對它施加一個水平向左的沖量使它獲得初速率,你可以持續地按下A鍵瞧瞧會發生哪些結果。
這個引擎在XNA中的應用我就不寫教程了,源代碼可在本文的上一級頁面中下載,療效和是一樣的。
文件下載(已下載1007次)
