关于物体运动的小研究

最近导师布置了一个任务:做一个与刚体运动有关的赛车类小游戏。
遂开始翻看《Foundation AS3.0 Animation》
草草阅览了一下,发现无非还是那些物理公式。
很快写了一个小程序来进行测试。

首先想到,可以把汽车看成一个整体,阻力简单处理为摩擦力。
那么,需要用到以下的物理知识:
[color=red]汽车发动机功率恒定 P = F * v
牛顿力学公式 a = F / m
动摩擦力始终与物体运动方向相反汽车最终可以保持恒定的速度 v_max = P / f ,其中f为阻力
之后就牵扯到了阻力问题,假设除了地面给的摩擦力之外没有其它阻力,则 f = miu * m * g,其中miu为摩擦系数
[/color]

做用户键盘输入的处理时,想起了很久以前玩的一个匿名作者做出来的不入流的小游戏。在这个小游戏中,用户每按一次键就可以让物体动一次,结果导致按键快的就跑得快,非常影响游戏平衡性。
于是,在这里我采取了一种经典模式——监听 KEY_DOWN 和 KEY_UP 事件,在 KEY_DOWN 时设置对应标记位,在 KEY_UP 时取消标志位,在 onEnterFrame 时根据标志位的不同给予相应处理。[color=red]这种置标记位后处理的模式有一个明显的好处:用户的连续快速多次按键不会对游戏产生影响。[/color]

当用户按下"↑"、"←"、"→"键的时候,汽车发动机将提供牵引力,使其速度发生改变。
对用户按"↓"键的处理,我一开始犯了一个小错误:为汽车发动机提供了一个与运动方向相反的力。这就导致当汽车静止的时候用户按下这个键,汽车将会发生很囧的往复颤抖。之后突然顿悟(果然是很久没碰物理了,脑袋都钝了)——刹车的原理应该是提高摩擦力… 于是,当downKey标志位为true时,简单增大摩擦力就可以了…

[color=blue]于是,onEnterFrame逻辑大概是这样子的:
1、如果速度不为0的话,计算摩擦力(如果用户按了刹车键,摩擦系数将增大)
2、判断用户按键方向(左,左前,前,右前,右),计算牵引力的角度rad
3、计算牵引力,即
F_x = Math.cos(rad) * P / |v|
F_y = Math.sin(rad) * P / |v|
4、计算合力,计算加速度
5、根据加速度改变速度
6、根据速度改变汽车的位置[/color]

使用这样的逻辑在用户体验上还是有很大的问题的,主要表现在:
[color=red]1、用户的向左向右的操作在汽车上反应得很慢,因为其直接影响的是汽车的加速度而不是速度。这种迟滞性在拐弯时特别明显,非常损伤用户体验。
2、用户在汽车静止时按下左、右键,汽车会有剧烈抖动(rotation有剧烈改变)。[/color]

针对问题2,在逻辑中加入静止拐弯逻辑即可。

针对问题1,看来是需要分析一下汽车的运动过程了:如果把汽车看做一个整体的话,拐弯过程显然会失实。但如果不看成一个整体的话,需要考虑的各种力都很多,设计和计算都有可能相当复杂,计算过程可能会占用大量CPU资源。

因此考虑将运动过程简化:
[color=blue]用户按下"→"、"←"键的时候,只改变物体的朝向;
用户按下"↑"键的时候,汽车提供在运动方向上的牵引力;
用户按下"↓”键的时候,阻力增大.[/color]

由于此种运动处理已然事实,加入过多物理计算会显得纠缠不清,故考虑简化参量,将原先设置的物体质量等一系列物理参数全部修改掉。取出对所谓“力”等一系列“物理量”的考虑,直接以加速度为参量进行计算。之前的计算中由于存在非物体运动方向上的力,因此需要用二维矢量来计算。修改逻辑后,平面中不存在非rotation方向的外力了,因此我们可以将其简化为一维浮点数。

[color=blue]根据上述思路,onEnterFrame逻辑更改为:
1、总加速度置0。
2、如果速度不为0的话,将摩擦力产生的负加速度累加至总加速度(要考虑用户有没有按下"↓”)。
3、如果用户按下"↑",则将运动方向牵引力产生的加速度累加至总加速度。
4、如果用户按下了"→"、"←",适当调整物体的rotation
5、根据加速度改变速度
6、根据速度和rotation改变汽车的位置[/color]

其中,阻力是与速度成正相关的,至于具体公式这个尚需推敲。
简单起见,认为阻力与速度成正比。
各种参数都需要仔细斟酌,经过数次调优,最终的程序代码如下:


package {
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;

public class RaceCar extends Sprite
{
public var v:Number;
public function RaceCar()
{
super();
var theShape:Shape = new Shape();
theShape.graphics.beginFill(0x000000);
theShape.graphics.moveTo(40,0);
theShape.graphics.lineTo(-40,20);
theShape.graphics.lineTo(-40,-20);
theShape.graphics.lineTo(40,0);
theShape.graphics.endFill();
addChild(theShape);
v = 0;
}
}

public class MotionTest extends Sprite
{
private var raceCar:RaceCar;
private var bg:Shape;

private var upKey:Boolean;
private var downKey:Boolean;
private var leftKey:Boolean;
private var rightKey:Boolean;

private const a_f_mul:Number = 0.1;
private const a_F:Number = 3;


public function MotionTest()
{

bg = new Shape();
bg.graphics.beginFill(0xffffff);
bg.graphics.drawRect(0,0,800,600);
bg.graphics.endFill();
addChild(bg);

raceCar = new RaceCar();
raceCar.x = 400;
raceCar.y = 300;
addChild(raceCar);

stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP,onKeyUp);
stage.addEventListener(Event.ENTER_FRAME,onEnterFrame);


}

private function onKeyDown(e:KeyboardEvent):void
{
switch(e.keyCode)
{
case 38:
upKey = true;
break;
case 40:
downKey = true;
break;
case 37:
leftKey = true;
break;
case 39:
rightKey = true;
break;
}
}

private function onKeyUp(e:KeyboardEvent):void
{
switch(e.keyCode)
{
case 38:
upKey = false;
break;
case 40:
downKey = false;
break;
case 37:
leftKey = false;
break;
case 39:
rightKey = false;
break;
}
}

private function onEnterFrame(e:Event):void
{
var a_sum:Number = 0;

if(raceCar.v != 0)
{
a_sum -= (downKey)? a_f_mul * raceCar.v * 10 : a_f_mul * raceCar.v;
}

if(upKey) a_sum += a_F;

if(leftKey && !rightKey) raceCar.rotation -= 80 / (10 + raceCar.v) ;

if(rightKey && !leftKey) raceCar.rotation += 80 / (10 + raceCar.v) ;

raceCar.v += a_sum;

if(raceCar.v < 0.1) raceCar.v = 0;

var rad:Number = raceCar.rotation * Math.PI / 180;
raceCar.x += raceCar.v * Math.cos(rad);
raceCar.y += raceCar.v * Math.sin(rad);

if(raceCar.x > 800) raceCar.x -= 800;
if(raceCar.x < 0) raceCar.x += 800;
if(raceCar.y > 600) raceCar.y -= 600;
if(raceCar.y < 0) raceCar.y += 600;

}
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值