zoukankan      html  css  js  c++  java
  • Flash/Flex学习笔记(42):坐标旋转

    坐标旋转是个啥概念呢?

    如上图,(蓝色)小球 绕某一中心点旋转a角度后,到达(红色)小球的位置,则红色小球相对中心点的坐标为:

    x1 = dx * cos(a) - dy * sin(a)

    y1 = dy * cos(a) + dx * sin(a)

    这个就是坐标旋转公式,如果要反向旋转,则公式要修正一下,有二种方法:

    1.将a变成-a,即:

    x1 = dx * cos(-a) - dy * sin(-a)

    y1 = dy * cos(-a) + dx * sin(-a)

    2.将正向旋转公式中的相减号交换

    x1 = dx * cos(a) + dy * sin(a);
    y1 = dy * cos(a) - dx * sin(a);

    先来回顾一个经典的小球圆周运动:

    show sourceview source

    print?

    01
    var ball:Ball = new Ball(10);

    02

    03
    var centerX:Number = stage.stageWidth/2;

    04
    var centerY:Number = stage.stageHeight/2;

    05
    var radius:Number = 50;

    06
    var angle:Number = 0;

    07

    08
    addChild(ball);

    09
    addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

    10

    11
    ball.x = centerX + Math.cos(angle) * radius;

    12
    ball.y = centerY + Math.sin(angle) * radius;

    13
    graphics.lineStyle(1,0x999999);

    14
    graphics.moveTo(ball.x,ball.y);

    15

    16
    function EnterFrameHandler(e:Event):void{   

    17
    ball.x = centerX + Math.cos(angle) * radius;

    18
    ball.y = centerY + Math.sin(angle) * radius;

    19
    angle += 0.02;

    20
    if (angle<=2*Math.PI+0.02){

    21
    graphics.lineTo(ball.x,ball.y);

    22
    }

    23
    }

    这个没啥特别的,接下来我们用坐标旋转公式换一种做法验证一下是否有效:

    show sourceview source

    print?

    01
    var ball:Ball = new Ball(10);

    02

    03
    var centerX:Number = stage.stageWidth/2;

    04
    var centerY:Number = stage.stageHeight/2;

    05
    var radius:Number = 50;

    06
    var angle:Number = 0;

    07

    08
    ball.vr = 0.02;//旋转角速度

    09
    ball.x = centerX + radius;

    10
    ball.y = centerY;

    11

    12
    var cos:Number = Math.cos(ball.vr);

    13
    var sin:Number = Math.sin(ball.vr);

    14

    15
    addChild(ball);

    16
    addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

    17

    18
    graphics.lineStyle(1,0x999999);

    19
    graphics.moveTo(ball.x,ball.y);

    20

    21
    var i:Number = 0;

    22

    23
    function EnterFrameHandler(e:Event):void{   

    24
    var dx:Number = ball.x - centerX; 

    25
    var dy:Number = ball.y - centerY; 

    26
    var x2:Number = cos * dx - sin * dy; 

    27
    var y2:Number = cos * dy + sin * dx; 

    28
    ball.x = centerX + x2; 

    29
    ball.y = centerY + y2;

    30
    i++;

    31
    if (i<=(2*Math.PI+ball.vr)/ball.vr){

    32
    trace(i);

    33
    graphics.lineTo(ball.x,ball.y);

    34
    }

    35
    }

    效果完全相同,说明坐标旋转公式完全是有效的,问题来了:原本一个简单的问题,经过这样复杂的处理后,效果并没有变化,为何要化简为繁呢?

    好处1:提高运行效率

    下面演示的多个物体旋转的传统做法:

    show sourceview source

    print?

    01
    var arrBalls:Array = new Array(30);

    02
    var centerX:Number = stage.stageWidth/2;

    03
    var centerY:Number = stage.stageHeight/2;

    04

    05
    for(var i:uint=0,j:uint=arrBalls.length;i<j;i++){

    06
    arrBalls[i] = new Ball(3 + Math.random()*5,Math.random()*0xffffff);

    07
    arrBalls[i].x = centerX + 100 * (Math.random()*2-1);

    08
    arrBalls[i].y = centerY + 100 * (Math.random()*2-1);

    09
    addChild(arrBalls[i]);

    10
    }

    11

    12
    graphics.lineStyle(1);

    13
    graphics.moveTo(centerX,centerY-5);

    14
    graphics.lineTo(centerX,centerY+5);

    15
    graphics.moveTo(centerX-5,centerY);

    16
    graphics.lineTo(centerX+5,centerY);

    17

    18
    addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

    19

    20
    function EnterFrameHandler(e:Event):void{

    21
    for(var i:uint=0,j:uint=arrBalls.length;i<j;i++){

    22
    var ball:Ball = arrBalls[i];

    23
    var dx:Number = ball.x - stage.stageWidth/2;

    24
    var dy:Number = ball.y - stage.stageHeight/2;

    25
    var dist:Number = Math.sqrt(dx*dx + dy*dy); //1次Math调用

    26
    ball.vr = Math.atan2(dy,dx);//2次Math调用

    27
    ball.vr += 0.005;

    28
    ball.x = centerX + dist * Math.cos(ball.vr);//3次Math调用

    29
    ball.y = centerY + dist * Math.sin(ball.vr);//4次Math调用      

    30
    }   

    31
    }

    坐标旋转的新做法:

    show sourceview source

    print?

    01
    var arrBalls:Array = new Array(30);

    02
    var centerX:Number = stage.stageWidth/2;

    03
    var centerY:Number = stage.stageHeight/2;

    04
    var vr:Number = 0.01;

    05

    06
    for(var i:uint=0,j:uint=arrBalls.length;i<j;i++){

    07
    arrBalls[i] = new Ball(3 + Math.random()*5,Math.random()*0xffffff);

    08
    arrBalls[i].x = centerX + 100 * (Math.random()*2-1);

    09
    arrBalls[i].y = centerY + 100 * (Math.random()*2-1);

    10
    arrBalls[i].vr = vr;

    11
    addChild(arrBalls[i]);

    12
    }

    13

    14
    graphics.lineStyle(1);

    15
    graphics.moveTo(centerX,centerY-5);

    16
    graphics.lineTo(centerX,centerY+5);

    17
    graphics.moveTo(centerX-5,centerY);

    18
    graphics.lineTo(centerX+5,centerY);

    19

    20
    addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

    21

    22
    //将Math函数的调用放到到循环体外

    23
    var cos:Number = Math.cos(vr);

    24
    var sin:Number = Math.sin(vr);

    25

    26
    function EnterFrameHandler(e:Event):void{

    27
    for(var i:uint=0,j:uint=arrBalls.length;i<j;i++){

    28
    var ball:Ball = arrBalls[i];

    29
    var dx:Number = ball.x - stage.stageWidth/2;

    30
    var dy:Number = ball.y - stage.stageHeight/2;

    31
    var x2:Number = cos * dx - sin * dy; 

    32
    var y2:Number = cos * dy + sin * dx;

    33
    ball.x = centerX + x2;

    34
    ball.y = centerY + y2;      

    35
    }   

    36
    }

    对比代码可以发现,同样的效果用坐标旋转处理后,Math的调用全部提升到循环外部了,对于30个小球来讲,每一帧至少减少了30 * 4 = 120次的三角函数运算

    好处2:可以方便的处理斜面反弹

    先来看下正向/反向旋转的测试

    show sourceview source

    print?

    01
    var ball:Ball=new Ball(15);

    02
    addChild(ball);

    03

    04
    var centerX:Number=stage.stageWidth/2;

    05
    var centerY:Number=stage.stageHeight/2;

    06
    var radius:Number=100;

    07

    08
    ball.x=centerX+radius;

    09
    ball.y=centerY;

    10

    11

    12

    13
    graphics.lineStyle(1,0xdddddd);

    14
    graphics.moveTo(centerX,centerY);

    15
    graphics.lineTo(ball.x,ball.y);

    16
    graphics.lineStyle(1);

    17
    graphics.moveTo(centerX,centerY -10);

    18
    graphics.lineTo(centerX,centerY +10);

    19
    graphics.moveTo(centerX-10,centerY);

    20
    graphics.lineTo(centerX+10,centerY);

    21

    22
    var angle:Number=30*Math.PI/180;

    23

    24

    25
    btn1.addEventListener(MouseEvent.MOUSE_DOWN,btn1Click);

    26

    27
    //旋转

    28
    function btn1Click(e:MouseEvent):void {

    29
    var cos:Number=Math.cos(angle);

    30
    var sin:Number=Math.sin(angle);

    31
    var dx:Number=ball.x-centerX;

    32
    var dy:Number=ball.y-centerY;

    33
    var x1:Number=dx*cos-dy*sin;

    34
    var y1:Number=dy*cos+dx*sin;

    35
    ball.x=centerX+x1;

    36
    ball.y=centerY+y1;

    37
    graphics.lineStyle(1,0xdddddd);

    38
    graphics.moveTo(centerX,centerY);

    39
    graphics.lineTo(ball.x,ball.y);

    40
    }

    41

    42
    btn2.addEventListener(MouseEvent.MOUSE_DOWN,btn2Click);

    43

    44
    //反转1

    45
    function btn2Click(e:MouseEvent):void {

    46
    var dx:Number=ball.x-centerX;

    47
    var dy:Number=ball.y-centerY;

    48
    var cos:Number=Math.cos(-angle);

    49
    var sin:Number=Math.sin(-angle);

    50
    var x1:Number=dx*cos-dy*sin;

    51
    var y1:Number=dy*cos+dx*sin;

    52
    ball.x=centerX+x1;

    53
    ball.y=centerY+y1;

    54
    graphics.lineStyle(1,0xdddddd);

    55
    graphics.moveTo(centerX,centerY);

    56
    graphics.lineTo(ball.x,ball.y);

    57
    }

    58

    59
    btn3.addEventListener(MouseEvent.MOUSE_DOWN,btn3Click);

    60

    61
    //反转2

    62
    function btn3Click(e:MouseEvent):void{

    63
    var dx:Number=ball.x-centerX;

    64
    var dy:Number=ball.y-centerY;

    65
    var cos:Number=Math.cos(angle);

    66
    var sin:Number=Math.sin(angle);

    67
    //反转公式

    68
    var x1:Number=dx*cos+dy*sin;

    69
    var y1:Number=dy*cos-dx*sin;

    70
    ball.x=centerX+x1;

    71
    ball.y=centerY+y1;

    72
    graphics.lineStyle(1,0xdddddd);

    73
    graphics.moveTo(centerX,centerY);

    74
    graphics.lineTo(ball.x,ball.y);

    75

    76
    }

    对于水平或垂直的反弹运动,实现起来并不复杂,但对于斜面而言,情况就复杂多了,首先:物体反弹并不是光学中的反射,所以用“入射角=反射角”来模拟并不准确,其次我们还要考虑到重力因素/摩擦力因素,这些都会影响到速度的大小和方向。

    如果用坐标旋转的思维方式去考虑这一复杂的问题,解决办法就变得非常简单。

    所有向量(物理学中也常称矢量,虽然这二者在严格意义上讲并不相同)都可应用坐标旋转,我们可以把整个系统(包括斜面以及相对斜面运行物体的速度向量)都通过坐标旋转变成水平面或垂直面,这样就把问题简单化了,等一切按水平或垂直的简单方式处理完成以后,再把系统旋转回最初的样子。

    show sourceview source

    print?

    001
    package {

    002

    003
    import flash.display.Sprite;

    004
    import flash.events.Event;

    005
    import flash.events.MouseEvent;

    006
    import flash.ui.Mouse;

    007
    import flash.ui.MouseCursor;

    008
    import flash.geom.Rectangle;

    009

    010

    011
    public class AngleBounce extends Sprite {

    012

    013
    private var ball:Ball;

    014
    private var line:Sprite;

    015
    private var gravity:Number=0.25;

    016
    private var bounce:Number=-0.6;

    017
    private var rect:Rectangle;

    018

    019
    public function AngleBounce() {

    020
    init();

    021
    }

    022

    023
    private function init():void {

    024
    Mouse.cursor=MouseCursor.BUTTON;

    025
    ball=new Ball(10);

    026
    addChild(ball);

    027
    ball.x=100;

    028
    ball.y=100;

    029
    line=new Sprite  ;

    030
    line.graphics.lineStyle(1);

    031
    line.graphics.lineTo(300,0);

    032
    addChild(line);

    033
    line.x=50;

    034
    line.y=200;

    035
    line.rotation=25;//将line旋转形成斜面

    036
    stage.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);

    037

    038
    rect = line.getBounds(this);//获取line的矩形边界

    039

    040
    graphics.beginFill(0xefefef)

    041
    graphics.drawRect(rect.left,rect.top,rect.width,rect.height);

    042
    graphics.endFill();

    043
    }

    044

    045
    private function MouseDownHandler(e:Event) {

    046
    addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

    047
    }

    048

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

    050

    051
    //line.rotation = (stage.stageWidth/2 - mouseX)*0.1;

    052

    053
    //普通的运动代码 

    054
    ball.vy+=gravity;

    055
    ball.x+=ball.vx;

    056
    ball.y+=ball.vy;

    057

    058

    059
    /*//只有二者(的矩形边界)碰撞了才需要做处理

    060
    if (ball.hitTestObject(line)) {*/

    061

    062
    //也可以换成下面的方法检测          

    063
    if (ball.x > rect.left && ball.x < rect.right && ball.y >rect.top && ball.y < rect.bottom){

    064

    065
    //trace("true");

    066

    067
    //获得角度及正余弦值 

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

    069
    var cos:Number=Math.cos(angle);

    070
    var sin:Number=Math.sin(angle);

    071

    072
    //获得 ball 与 line 的相对位置 

    073
    var dx:Number=ball.x-line.x;

    074
    var dy:Number=ball.y-line.y;

    075

    076
    //反向旋转坐标(得到ball“相对”斜面line的坐标)

    077
    var x2:Number=cos*dx+sin*dy;

    078
    var y2:Number=cos*dy-sin*dx;

    079

    080
    //反向旋转速度向量(得到ball“相对”斜面的速度) 

    081
    var vx2:Number=cos*ball.vx+sin*ball.vy;

    082
    var vy2:Number=cos*ball.vy-sin*ball.vx;

    083

    084
    //实现反弹 

    085
    if (y2>- ball.height/2) {

    086
    y2=- ball.height/2;

    087
    vy2*=bounce;

    088
    //将一切再正向旋转回去

    089
    dx=cos*x2-sin*y2;

    090
    dy=cos*y2+sin*x2;

    091

    092
    ball.vx=cos*vx2-sin*vy2;

    093
    ball.vy=cos*vy2+sin*vx2;

    094

    095
    //重新定位

    096
    ball.x=line.x+dx;

    097
    ball.y=line.y+dy;

    098
    }

    099
    }

    100

    101
    //跑出舞台边界后将其重新放到原始位置

    102
    if (ball.x>=stage.stageWidth-ball.width/2||ball.y>=stage.stageHeight-ball.height/2) {

    103
    ball.x=100;

    104
    ball.y=100;

    105
    ball.vx=0;

    106
    ball.vy=0;

    107
    removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);

    108
    }

    109
    }

    110
    }

    111

    112
    }

    多角度斜面反弹:

    show sourceview source

    print?

    001
    package {

    002
    import flash.display.Sprite;

    003
    import flash.events.Event;

    004
    import flash.display.StageScaleMode;

    005
    import flash.display.StageAlign;

    006
    import flash.geom.Rectangle;

    007
    import flash.events.MouseEvent;

    008
    import flash.ui.Mouse;

    009
    import flash.ui.MouseCursor;

    010

    011
    public class MultiAngleBounce extends Sprite {

    012
    private var ball:Ball;

    013
    private var lines:Array;

    014
    private var numLines:uint=5;

    015
    private var gravity:Number=0.3;

    016
    private var bounce:Number=-0.6;

    017

    018
    public function MultiAngleBounce() {

    019
    init();

    020
    }

    021

    022
    private function init():void {

    023
    stage.scaleMode=StageScaleMode.NO_SCALE;

    024
    stage.align=StageAlign.TOP_LEFT;

    025
    ball=new Ball(20);

    026
    addChild(ball);

    027
    ball.x=100;

    028
    ball.y=50;

    029
    // 创建 5 个 line 影片 

    030
    lines = new Array();

    031
    for (var i:uint = 0; i < numLines; i++) {

    032
    var line:Sprite = new Sprite();

    033
    line.graphics.lineStyle(1);

    034
    line.graphics.moveTo(-50, 0);

    035
    line.graphics.lineTo(50, 0);

    036
    addChild(line);

    037
    lines.push(line);

    038
    }

    039

    040
    // 放置并旋转 

    041
    lines[0].x=100;

    042
    lines[0].y=100;

    043
    lines[0].rotation=30;

    044
    lines[1].x=100;

    045
    lines[1].y=230;

    046
    lines[1].rotation=45;

    047
    lines[2].x=250;

    048
    lines[2].y=180;

    049
    lines[2].rotation=-30;

    050
    lines[3].x=150;

    051
    lines[3].y=330;

    052
    lines[3].rotation=10;

    053
    lines[4].x=230;

    054
    lines[4].y=250;

    055
    lines[4].rotation=-30;

    056
    addEventListener(Event.ENTER_FRAME, onEnterFrame);

    057

    058
    ball.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);

    059
    ball.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);

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

    061
    }

    062

    063

    064
    function MouseOverHandler(e:MouseEvent):void {

    065
    Mouse.cursor=MouseCursor.HAND;

    066
    }

    067

    068
    function MouseDownHandler(e:MouseEvent):void {

    069
    Mouse.cursor=MouseCursor.HAND;

    070
    var bounds:Rectangle = new Rectangle(ball.width,ball.height,stage.stageWidth-2*ball.width,stage.stageHeight-2*ball.height);

    071
    ball.startDrag(true,bounds);

    072
    removeEventListener(Event.ENTER_FRAME, onEnterFrame);

    073
    }

    074

    075
    function MouseUpHandler(e:MouseEvent):void {

    076
    ball.stopDrag();

    077
    ball.vx=0;

    078
    ball.vy=0;

    079
    Mouse.cursor=MouseCursor.AUTO;

    080
    addEventListener(Event.ENTER_FRAME, onEnterFrame);

    081
    }

    082

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

    084
    // normal motion code 

    085
    ball.vy+=gravity;

    086
    ball.x+=ball.vx;

    087
    ball.y+=ball.vy;

    088
    // 舞台四周的反弹 

    089
    if (ball.x+ball.radius>stage.stageWidth) {

    090
    ball.x=stage.stageWidth-ball.radius;

    091
    ball.vx*=bounce;

    092
    } else if (ball.x - ball.radius < 0) {

    093
    ball.x=ball.radius;

    094
    ball.vx*=bounce;

    095
    }

    096
    if (ball.y+ball.radius>stage.stageHeight) {

    097
    ball.y=stage.stageHeight-ball.radius;

    098
    ball.vy*=bounce;

    099
    } else if (ball.y - ball.radius < 0) {

    100
    ball.y=ball.radius;

    101
    ball.vy*=bounce;

    102
    }

    103
    // 检查每条线 

    104
    for (var i:uint = 0; i < numLines; i++) {

    105
    checkLine(lines[i]);

    106
    }

    107
    }

    108
    private function checkLine(line:Sprite):void {

    109
    // 获得 line 的边界 

    110
    var bounds:Rectangle=line.getBounds(this);

    111
    if (ball.x>bounds.left&&ball.x<bounds.right) {

    112
    // 获取角度与正余弦值 

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

    114
    var cos:Number=Math.cos(angle);

    115
    var sin:Number=Math.sin(angle);

    116
    // 获取 ball 与 line 的相对位置 

    117
    var x1:Number=ball.x-line.x;

    118
    var y1:Number=ball.y-line.y;

    119
    // 旋转坐标 

    120
    var y2:Number=cos*y1-sin*x1;

    121
    // 旋转速度向量 

    122
    var vy1:Number=cos*ball.vy-sin*ball.vx;

    123
    // 实现反弹 

    124
    if (y2>- ball.height/2&&y2<vy1) {

    125
    // 旋转坐标 

    126
    var x2:Number=cos*x1+sin*y1;

    127
    // 旋转速度向量 

    128
    var vx1:Number=cos*ball.vx+sin*ball.vy;

    129
    y2=- ball.height/2;

    130
    vy1*=bounce;

    131
    // 将一切旋转回去 

    132
    x1=cos*x2-sin*y2;

    133

    134
    y1=cos*y2+sin*x2;

    135
    ball.vx=cos*vx1-sin*vy1;

    136
    ball.vy=cos*vy1+sin*vx1;

    137
    ball.x=line.x+x1;

    138
    ball.y=line.y+y1;

    139
    }

    140
    }

    141
    }

    142
    }

    143
    }

  • 相关阅读:
    Jlist的用法
    Swing中Timer定时器的使用
    埃氏筛法
    ACM排序题
    《C语言程序设计》9.6
    从字符串中提取数
    字符串排序
    树—线索二叉树的创建&二叉树的后序遍历&中序线索化&中序遍历线索二叉树
    《大话数据结构》中介绍的三种树的存储结构的表示方法的总结
    day08
  • 原文地址:https://www.cnblogs.com/happysky97/p/1884577.html
Copyright © 2011-2022 走看看