• Flash/Flex学习笔记(46):正向运动学


    所谓"正向运动学"通俗点讲就是把几个连接部件的一端固定起来,另一个端可以自由(向前/向外)运动。比如人的行走,单个下肢可以理解为脚连接小腿,小腿连接大腿,大腿连接腰。行走的过程,相当于二条腿相对固定于腰部,大腿运动驱动小腿,小腿又驱动脚,从而带动整个连接系统的一系列运动。

    先来一个基本的关节类Segment:(就是一个圆角矩形+二个小圆圈)

    show sourceview source

    print?

    01
    package {

    02
    import flash.display.Sprite;

    03
    import flash.geom.Point;

    04

    05
    public class Segment extends Sprite {

    06

    07
    private var color:uint;

    08
    private var segmentWidth:Number;

    09
    private var segmentHeight:Number;

    10
    public var vx:Number=0;

    11
    public var vy:Number=0;

    12

    13
    public function Segment(segmentWidth:Number,segmentHeight:Number,color:uint=0xffffff) {

    14
    this.segmentWidth=segmentWidth;

    15
    this.segmentHeight=segmentHeight;

    16
    this.color=color;

    17
    init();

    18
    }

    19

    20
    public function init():void {

    21

    22
    // 绘制关节 

    23
    graphics.lineStyle(0);

    24
    graphics.beginFill(color);

    25
    graphics.drawRoundRect(- segmentHeight/2,- segmentHeight/2,segmentWidth+segmentHeight,segmentHeight,segmentHeight,segmentHeight);

    26
    graphics.endFill();

    27

    28
    // 绘制两个“枢轴” 

    29
    graphics.drawCircle(0,0,2);

    30
    graphics.drawCircle(segmentWidth,0,2);

    31
    }

    32

    33
    //获得自由端的坐标

    34
    public function getPin():Point {

    35
    var angle:Number=rotation*Math.PI/180;

    36
    var xPos:Number=x+Math.cos(angle)*segmentWidth;

    37
    var yPos:Number=y+Math.sin(angle)*segmentWidth;

    38
    return new Point(xPos,yPos);

    39
    }

    40
    }

    41
    }

    为了动态控制关节的旋转,再来一个简单的滑块控件类:(下列代码看起来吃力的同学,建议先看Flash/Flex学习笔记(36):自己动手实现一个滑块控件(JimmySilder)

    show sourceview source

    print?

    001
    package {

    002
    import flash.display.Sprite;

    003
    import flash.events.MouseEvent;

    004
    import flash.geom.Rectangle;

    005
    import flash.events.Event;

    006

    007
    public class SimpleSlider extends Sprite {

    008

    009
    private var _Number=6;

    010
    private var _height:Number=100;

    011
    private var _value:Number;

    012
    private var _max:Number=100;

    013
    private var _min:Number=0;

    014
    private var _handle:Sprite;

    015
    private var _back:Sprite;

    016
    private var _backWidth:Number=0;

    017
    private var _handleHeight:Number=20;

    018
    private var _backColor:uint=0xcccccc;

    019
    private var _backBorderColor:uint=0x999999;

    020
    private var _handleColor:uint=0x000000;

    021
    private var _handleBorderColor:uint=0xcccccc;

    022
    private var _handleRadius:Number=2;

    023
    private var _backRadius:Number=2;

    024

    025
    public function SimpleSlider(min:Number = 0, max:Number = 100, value:Number = 100 ) {

    026
    _min=min;

    027
    _max=max;

    028
    _value=Math.min(Math.max(value,min),max);

    029
    init();

    030
    }

    031

    032
    private function init():void {

    033
    _back = new Sprite () ;

    034
    addChild(_back);

    035
    _handle = new Sprite () ;

    036
    _handle.buttonMode=true;

    037
    addChild(_handle);

    038
    _handle.addEventListener( MouseEvent.MOUSE_DOWN , MouseDownHandler );

    039
    draw();

    040
    updatePosition();

    041
    }

    042

    043
    private function draw():void {

    044
    drawBack();

    045
    drawHandle();

    046
    }

    047

    048
    private function drawBack():void {

    049
    _back.graphics.clear();

    050
    _back.graphics.beginFill( _backColor );

    051
    _back.graphics.lineStyle( 0, _backBorderColor );

    052
    _back.graphics.drawRoundRect( 0, 0, _backWidth , _height , _backRadius , _backRadius );

    053
    _back.graphics.endFill();

    054
    _back.x=_width/2-_backWidth/2;

    055
    }

    056

    057
    private function drawHandle():void {

    058
    _handle.graphics.clear();

    059
    _handle.graphics.beginFill( _handleColor );

    060
    _handle.graphics.lineStyle( 0, _handleBorderColor );

    061
    _handle.graphics.drawRect( 0, 0, _width , _handleHeight );

    062
    _handle.graphics.endFill();

    063
    }

    064

    065
    private function updatePosition():void {

    066
    var handleRange:Number=_height-_handleHeight;

    067
    var valueRange:Number=_max-_min;

    068
    _handle.y = handleRange - ( _value - _min ) / valueRange * handleRange ;

    069
    }

    070

    071
    private function updateValue():void {

    072
    var handleRange:Number=_height-_handleHeight;

    073
    var valueRange:Number=_max-_min;

    074
    _value = ( handleRange - _handle.y ) / handleRange * valueRange + _min ;

    075
    dispatchEvent( new Event ( Event.CHANGE ));

    076
    }

    077

    078
    private function MouseUpHandler( e:MouseEvent ):void {

    079
    stage.removeEventListener( MouseEvent.MOUSE_MOVE , MouseMoveHandler );

    080
    stage.removeEventListener( MouseEvent.MOUSE_UP , MouseUpHandler );

    081
    _handle.stopDrag();

    082
    }

    083

    084
    private function MouseDownHandler( e:MouseEvent ):void {

    085
    stage.addEventListener( MouseEvent.MOUSE_MOVE , MouseMoveHandler );

    086
    stage.addEventListener( MouseEvent.MOUSE_UP , MouseUpHandler );

    087
    _handle.startDrag( false , new Rectangle ( 0, 0, 0, _height - _handleHeight ));

    088
    }

    089

    090
    private function MouseMoveHandler( e:MouseEvent ):void {

    091
    updateValue();

    092
    }

    093

    094
    public function invalidate():void {

    095
    draw();

    096
    }

    097

    098
    public function move( x:Number , y:Number ):void {

    099
    this.x=x;

    100
    this.y=y;

    101
    }

    102

    103
    public function setSize( w:Number , h:Number ):void {

    104
    _width=w;

    105
    _height=h;

    106
    draw();

    107
    }

    108

    109
    public function set backBorderColor( n:uint ):void {

    110
    _backBorderColor=n;

    111
    draw();

    112
    }

    113

    114
    public function get backBorderColor():uint {

    115
    return _backBorderColor;

    116
    }

    117

    118
    public function set backColor( n:uint ):void {

    119
    _backColor=n;

    120
    draw();

    121
    }

    122

    123
    public function get backColor():uint {

    124
    return _backColor;

    125
    }

    126

    127
    public function set backRadius( n:Number ):void {

    128
    _backRadius=n;

    129
    }

    130

    131
    public function get backRadius():Number {

    132
    return _backRadius;

    133
    }

    134

    135
    public function set backWidth( n:Number ):void {

    136
    _backWidth=n;

    137
    draw();

    138
    }

    139

    140
    public function get backWidth():Number {

    141
    return _backWidth;

    142
    }

    143

    144
    public function set handleBorderColor( n:uint ):void {

    145
    _handleBorderColor=n;

    146
    draw();

    147
    }

    148

    149
    public function get handleBorderColor():uint {

    150
    return _handleBorderColor;

    151
    }

    152

    153
    public function set handleColor( n:uint ):void {

    154
    _handleColor=n;

    155
    draw();

    156
    }

    157

    158
    public function get handleColor():uint {

    159
    return _handleColor;

    160
    }

    161
    public function set handleRadius( n:Number ):void {

    162
    _handleRadius=n;

    163
    draw();

    164
    }

    165
    public function get handleRadius():Number {

    166
    return _handleRadius;

    167
    }

    168
    public function set handleHeight( n:Number ):void {

    169
    _handleHeight=n;

    170
    draw();

    171
    updatePosition();

    172
    }

    173
    public function get handleHeight():Number {

    174
    return _handleHeight;

    175
    }

    176
    override public function set height( n:Number ):void {

    177
    _height=n;

    178
    draw();

    179
    }

    180
    override public function get height():Number {

    181
    return _height;

    182
    }

    183
    public function set max( n:Number ):void {

    184
    _max=n;

    185
    updatePosition();

    186
    }

    187
    public function get max():Number {

    188
    return _max;

    189
    }

    190
    public function set min( n:Number ):void {

    191
    _min=n;

    192
    updatePosition();

    193
    }

    194
    public function get min():Number {

    195
    return _min;

    196
    }

    197
    public function set value( n:Number ):void {

    198
    _value=n;

    199
    _value=Math.min(_max,Math.max(_value,_min));

    200
    updatePosition();

    201
    }

    202
    public function get value():Number {

    203
    return _value;

    204
    }

    205
    override public function set width( n:Number ):void {

    206
    _width=n;

    207
    draw();

    208
    }

    209
    override public function get width():Number {

    210
    return _width;

    211
    }

    212
    }

    213
    }

    基本测试:

    show sourceview source

    print?

    01
    var segment:Segment=new Segment(100,20);

    02
    addChild(segment);

    03
    segment.x=50;

    04
    segment.y=120;

    05

    06
    var slider:SimpleSlider=new SimpleSlider(-90,90,0);

    07
    addChild(slider);

    08
    slider.x=200;

    09
    slider.y=70;

    10

    11
    slider.addEventListener(Event.CHANGE,onChange);

    12

    13
    function onChange(event:Event):void {

    14
    segment.rotation=slider.value;

    15
    }

    双关节运动测试:

    show sourceview source

    print?

    01
    package {

    02
    import flash.display.Sprite;

    03
    import flash.events.Event;

    04
    public class TwoSegments extends Sprite {

    05
    private var slider0:SimpleSlider;       

    06
    private var slider1:SimpleSlider;

    07
    private var segment0:Segment;

    08
    private var segment1:Segment;

    09

    10
    public function TwoSegments() {

    11
    init();

    12
    }

    13
    private function init():void {

    14
    segment0=new Segment(100,20);

    15
    addChild(segment0);

    16
    segment0.x=50;

    17
    segment0.y=150;

    18
    segment1=new Segment(100,20);

    19
    addChild(segment1);

    20

    21
    //关键:segment1的固定端连接到segment0的自由端

    22
    segment1.x=segment0.getPin().x;

    23
    segment1.y=segment0.getPin().y;

    24

    25
    slider0=new SimpleSlider(-90,90,0);

    26
    addChild(slider0);

    27
    slider0.x=320;

    28
    slider0.y=90;

    29
    slider0.addEventListener(Event.CHANGE,onChange);

    30
    slider1=new SimpleSlider(-90,90,0);

    31
    addChild(slider1);

    32
    slider1.x=340;

    33
    slider1.y=90;

    34
    slider1.addEventListener(Event.CHANGE,onChange);

    35
    }

    36
    private function onChange(event:Event):void {

    37
    segment0.rotation=slider0.value;

    38
    segment1.rotation=slider1.value;

    39
    segment1.x=segment0.getPin().x;

    40
    segment1.y=segment0.getPin().y;

    41
    }

    42
    }

    43
    }

    如果把segment0与segment1分别看做人的胳膊与手臂,上面这个示例显然有二个地方不自然:

    1.没有人的(前)手臂向下做-90度的弯曲(除非脱臼)

    2.人的上肢整体向上抬时,手臂会随着胳膊一起绕肩关节向上旋转,而不应该一直固定于某个角度

    修正的方法很简单,onChange改成下面这样:

    view source

    print?

    1
    private function onChange(event:Event):void {

    2
    segment0.rotation=slider0.value;

    3
    segment1.rotation=slider1.value + segment0.rotation;//注意这行

    4
    segment1.x=segment0.getPin().x;

    5
    segment1.y=segment0.getPin().y;

    6
    }

    同时限制一下slider1的角度范围,改成下面这样:

    view source

    print?

    1
    slider1=new SimpleSlider(-160,0,0);

    单腿原地“踢”模拟

    show sourceview source

    print?

    01
    package {

    02
    import flash.display.Sprite;

    03
    import flash.events.Event;

    04
    public class Walking1 extends Sprite {

    05
    private var segment0:Segment;

    06
    private var segment1:Segment;

    07
    private var cycle:Number=0;

    08
    private var offset:Number = -Math.PI/2;//小腿的运动看上去应该滞后于大腿,所以需要加入反向偏移量

    09

    10
    public function Walking1() {

    11
    init();

    12
    trace(Math.PI/180);

    13
    trace(0.05*180/Math.PI);

    14
    }

    15
    private function init():void {

    16
    segment0=new Segment(100,20);

    17
    addChild(segment0);

    18
    segment0.x=200;

    19
    segment0.y=200;

    20
    segment1=new Segment(100,20);

    21
    addChild(segment1);

    22
    segment1.x=segment0.getPin().x;

    23
    segment1.y=segment0.getPin().y;

    24
    addEventListener(Event.ENTER_FRAME,onEnterFrame);

    25
    }

    26
    private function onEnterFrame(event:Event):void {

    27
    cycle+=.05;

    28
    var angle0:Number=Math.sin(cycle)*45 + 90;//-45到45整体加上90度以后,就变成45到135,即:大腿垂直方向左右摆动45度

    29
    var angle1:Number = Math.sin(cycle + offset) * 45 + 45;//即:小腿相对大腿末端做0-90度的正向旋转。建议大家尝试修改一下这里的+45值的大小,看看效果有什么不同

    30
    segment0.rotation=angle0;

    31
    segment1.rotation=segment0.rotation+angle1;

    32
    segment1.x=segment0.getPin().x;

    33
    segment1.y=segment0.getPin().y;

    34
    }

    35
    }

    36
    }

    双腿原地行走:

    show sourceview source

    print?

    01
    package {

    02
    import flash.display.Sprite;

    03
    import flash.events.Event;

    04
    public class Walking4 extends Sprite {

    05
    private var segment0:Segment;

    06
    private var segment1:Segment;

    07
    private var segment2:Segment;

    08
    private var segment3:Segment;

    09

    10
    private var cycle:Number=0;

    11
    private var offset:Number=- Math.PI/2;//小腿的运动看上去应该滞后于大腿,所以需要加入反向偏移量

    12

    13
    public function Walking4() {

    14
    init();         

    15
    }

    16
    private function init():void {

    17

    18
    segment0=new Segment(100,35);//第一条大腿

    19
    addChild(segment0);

    20
    segment0.x=200;

    21
    segment0.y=50;

    22

    23
    segment1=new Segment(100,20);           

    24
    addChild(segment1);

    25
    segment1.x=segment0.getPin().x;//第一条小腿连接到第一条大腿

    26
    segment1.y=segment0.getPin().y;

    27

    28
    segment2=new Segment(100,35);//第二条大腿

    29
    segment2.x = segment0.x;//第二条大腿与第一条大腿坐标相同,视觉效果上看,就象都固定在腰部

    30
    segment2.y = segment0.y;

    31
    addChild(segment2);

    32

    33

    34
    segment3=new Segment(100,20);           

    35
    addChild(segment3);

    36
    segment3.x=segment2.getPin().x;//第二条小腿连接到第二条大腿

    37
    segment3.y=segment2.getPin().y;

    38

    39
    addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

    40
    }

    41

    42
    private function EnterFrameHandler(event:Event):void {

    43
    walk(segment0, segment1, cycle); 

    44
    walk(segment2, segment3, cycle + Math.PI);//注意这里的:+Math.PI,如果不加这个,二条腿的频率/角度完全相同,将重叠在一起,加上180度以后,正好反相过来,一条腿在前,另一条腿在后

    45
    cycle += .05;

    46
    }

    47

    48
    //把"走"的动作封装起来

    49
    private function walk(segA:Segment, segB:Segment, cyc:Number):void {

    50
    var angleA:Number=Math.sin(cyc)*45+90;

    51
    var angleB:Number=Math.sin(cyc+offset)*45+45;

    52
    segA.rotation=angleA;

    53
    segB.rotation=segA.rotation+angleB;

    54
    segB.x=segA.getPin().x;

    55
    segB.y=segA.getPin().y;

    56
    }

    57
    }

    58
    }

    加入滑块控制条后的样子:

    show sourceview source

    print?

    01
    package {

    02
    import flash.display.Sprite;

    03
    import flash.events.Event;

    04
    public class Walking5 extends Sprite {

    05
    private var segment0:Segment;

    06
    private var segment1:Segment;

    07
    private var segment2:Segment;

    08
    private var segment3:Segment;

    09
    private var speedSlider:SimpleSlider;

    10
    private var thighRangeSlider:SimpleSlider;

    11
    private var thighBaseSlider:SimpleSlider;

    12
    private var calfRangeSlider:SimpleSlider;

    13
    private var calfOffsetSlider:SimpleSlider;

    14
    private var cycle:Number=0;

    15

    16
    public function Walking5() {

    17
    init();

    18
    }

    19

    20
    private function init():void {

    21
    segment0=new Segment(100,30);

    22
    addChild(segment0);

    23
    segment0.x=200;

    24
    segment0.y=100;

    25
    segment1=new Segment(100,20);

    26
    addChild(segment1);

    27
    segment1.x=segment0.getPin().x;

    28
    segment1.y=segment0.getPin().y;

    29
    segment2=new Segment(100,30);

    30
    addChild(segment2);

    31
    segment2.x=200;

    32
    segment2.y=100;

    33
    segment3=new Segment(100,20);

    34
    addChild(segment3);

    35
    segment3.x=segment2.getPin().x;

    36
    segment3.y=segment2.getPin().y;

    37

    38
    //控制速度的滑块

    39
    speedSlider=new SimpleSlider(0,0.5,0.11);

    40
    addChild(speedSlider);

    41
    speedSlider.x=10;

    42
    speedSlider.y=10;

    43

    44
    //控制大腿能分开的最大角度

    45
    thighRangeSlider=new SimpleSlider(0,90,45);

    46
    addChild(thighRangeSlider);

    47
    thighRangeSlider.x=30;

    48
    thighRangeSlider.y=10;

    49

    50
    //大腿旋转的偏移量

    51
    thighBaseSlider=new SimpleSlider(0,180,90);

    52
    addChild(thighBaseSlider);          

    53
    thighBaseSlider.x=50;

    54
    thighBaseSlider.y=10;

    55

    56
    //小腿旋转的偏移量

    57
    calfRangeSlider=new SimpleSlider(0,90,45);

    58
    addChild(calfRangeSlider);          

    59
    calfRangeSlider.x=70;

    60
    calfRangeSlider.y=10;

    61

    62
    //小腿相对大腿滞后的偏移量

    63
    calfOffsetSlider=new SimpleSlider(-3.14,3.14,-1.57);

    64
    addChild(calfOffsetSlider);         

    65
    calfOffsetSlider.x=90;

    66
    calfOffsetSlider.y=10;

    67

    68
    addEventListener(Event.ENTER_FRAME, EnterFrameHandler);

    69
    }

    70

    71
    private function EnterFrameHandler(e:Event):void {

    72
    walk(segment0, segment1, cycle);

    73
    walk(segment2, segment3, cycle + Math.PI);

    74
    cycle+=speedSlider.value;

    75
    }

    76
    private function walk(segA:Segment, segB:Segment,cyc:Number):void {

    77
    var angleA:Number = Math.sin(cyc) * thighRangeSlider.value + thighBaseSlider.value;

    78
    var angleB:Number = Math.sin(cyc +calfOffsetSlider.value) * calfRangeSlider.value + calfRangeSlider.value;

    79
    segA.rotation=angleA;

    80
    segB.rotation=segA.rotation+angleB;

    81
    segB.x=segA.getPin().x;

    82
    segB.y=segA.getPin().y;

    83
    }

    84
    }

    85
    }

    真正的行走:

    show sourceview source

    print?

  • 相关阅读:
    Leetcode 1414. 和为 K 的最少斐波那契数字数目(贪心)
    A ConvNet for the 2020论文阅读笔记
    Leetcode 957. N 天后的牢房(暴力)
    洛谷P1879 [USACO06NOV]Corn Fields G(状压DP)
    CF1553D Backspace(双指针)
    26
    37
    36
    3.4
    3.3
  • 原文地址:https://www.cnblogs.com/happysky97/p/1884593.html
Copyright © 2020-2023  润新知