上一篇文章我们讨论了牛顿第二定理,理论上依照牛二定理和微积分的知识就可以求出大多数情况下物体的运动情况,并且上一篇文章中实现的数学引擎未能模拟这样一个问题:物体遭到一个冲击力的作用而获得初速率,这就是这篇文章即将讨论的问题。
化学知识
首先看这样一道习题:光滑水平桌面上一个质量为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 Listsprites; 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次)