将MovieClip拖约到圆圈

时间:2011-06-28 16:19:52

标签: actionscript-3 trigonometry

......好吧,到一个不完整的圈子。

我有一个可拖动的滑块,如下所示:Arc Slider

蓝色条的实例名称为track,粉色圆点的实例名称为puck

我总是需要将冰球限制在蓝色区域内,这就是我的数学失败对我不利的地方!到目前为止,我只是沿着x轴移动冰球:

private function init():void
{
    zeroPoint = track.x + (track.width/2);
    puck.x = zeroPoint-(puck.width/2);
    puck.buttonMode = true;
    puck.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
}

private function onMouseDown(evt:MouseEvent):void
{
    this.stage.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
    this.stage.addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
}

private function onMouseUp(evt:MouseEvent):void
{
    this.stage.removeEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
}

private function onMouseMove(evt:MouseEvent):void
{
    puck.x = mouseX-(puck.width/2);
    //need to plot puck.y using trig magic...
}

我的想法是,我可以使用不完整圆(50)的半径和相对于弧顶的mouseX来计算三角形,并从那里我可以计算出所需的y位置。问题是,我正在阅读各种三角网站,但仍然不知道从哪里开始。有人可以解释我需要做什么,好像和孩子说话一样吗?

编辑:圆圈破损的事实不应该是一个问题,我可以轻松地将每个方向的运动限制在一定程度,它首先获得度数我无法理解!

Edit2:我正在尝试遵循Bosworth99的回答,这是我为计算放入其功能的弧度而设计的功能:

private function getRadian():Number
{
    var a:Number = mouseX - zeroPoint;
    var b:Number = 50;
    var c:Number = Math.sqrt((a^2)+(b^2));
    return c;
}

4 个答案:

答案 0 :(得分:6)

在我看来,你解决的问题是找到圆圈上的最近点。 Google对此主题提出了很多建议。

您可以通过首先检测鼠标位置和圆心之间的角度来优化它。使用Math.atan2()。如果角度在间隙范围内,只需选择最近的端点:左或右。

EDIT1 以下是此策略的完整示例。

希望有所帮助。

import flash.geom.Point;
import flash.events.Event;
import flash.display.Sprite;

var center:Point = new Point(200, 200);
var radius:uint = 100;

var degreesToRad:Number = Math.PI/180;

// gap angles. degrees are used here just for the sake of simplicity.
// what we use here are stage angles, not the trigonometric ones.
var gapFrom:Number = 45; // degrees
var gapTo:Number = 135; // degrees

// calculate endpoints only once

var endPointFrom:Point = new Point();
endPointFrom.x = center.x+Math.cos(gapFrom*degreesToRad)*radius;
endPointFrom.y = center.y+Math.sin(gapFrom*degreesToRad)*radius;

var endPointTo:Point = new Point();
endPointTo.x = center.x+Math.cos(gapTo*degreesToRad)*radius;
endPointTo.y = center.y+Math.sin(gapTo*degreesToRad)*radius;

// just some drawing
graphics.beginFill(0);
graphics.drawCircle(center.x, center.y, radius);
graphics.moveTo(center.x, center.y);
graphics.lineTo(endPointFrom.x, endPointFrom.y);
graphics.lineTo(endPointTo.x, endPointTo.y);
graphics.lineTo(center.x, center.y);
graphics.endFill();

// something to mark the closest point
var marker:Sprite = new Sprite();
marker.graphics.lineStyle(20, 0xFF0000);
marker.graphics.lineTo(0, 1);
addChild(marker);

var onEnterFrame:Function = function (event:Event) : void
{
    // circle intersection goes here
    var mx:int = stage.mouseX;
    var my:int = stage.mouseY;

    var angle:Number = Math.atan2(center.y-my, center.x-mx);
    // NOTE: in flash rotation is increasing clockwise, 
    // while in trigonometry angles increase counter clockwise
    // so we handle this difference
    angle += Math.PI;

    // calculate the stage angle in degrees
    var clientAngle:Number = angle/Math.PI*180

    // check if we are in a gap
    if (clientAngle >= gapFrom && clientAngle <= gapTo) {
        // we are in a gap, no sines or cosines needed
        if (clientAngle-gapFrom < (gapTo-gapFrom)/2) {        
            marker.x = endPointFrom.x;
            marker.y = endPointFrom.y;
        } else {
            marker.x = endPointTo.x;
            marker.y = endPointTo.y;
        }
        // we are done here
        return;
    }

    // we are not in a gp, calculate closest position on a circle
    marker.x = center.x + Math.cos(angle)*radius;
    marker.y = center.y + Math.sin(angle)*radius;
}
addEventListener(Event.ENTER_FRAME, onEnterFrame);

EDIT2 一些链接

以下以一种出色清晰简洁的方式解释和解决了一些常见问题:http://paulbourke.net/geometry/这个资源在很多天前帮助了我。

线和圆的交点在这里有点过分,但这里是:http://paulbourke.net/geometry/sphereline/

答案 1 :(得分:1)

不是试图沿圆的部分路径移动点,为什么不假装它并使用旋钮/表盘?将它看起来像点在沿着路径移动。

然后将旋钮的旋转设置为:

var deg:Number = Math.atan2(stage.mouseY - knob.y,stage.mouseX - knob.x) / (Math.PI/180);
// code to put upper/lower bounds on degrees    
knob.rotation = deg;

你可以通过把它扔进一个输入框架事件来测试它,但你显然想要设置一些逻辑来控制旋钮开始移动的方式以及何时应该停止。

答案 2 :(得分:1)

100%工作代码。

enter code here

const length:int = 100;

var dragging:Boolean = false;
var tx:int;
var ty:int;



var p1:Sprite = new Sprite();
var p2:Sprite = new Sprite();

p1.graphics.beginFill(0);
p1.graphics.drawCircle(0, 0, 10);
p1.graphics.endFill();

p2.graphics.copyFrom(p1.graphics);

p1.x = stage.stageWidth / 2;
p1.y = stage.stageHeight / 2;

p2.x = p1.x + length;
p2.y = p1.y;

addChild(p1);
addChild(p2);

p2.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);

function mouseDown(event:MouseEvent):void
{
    dragging = true;
}

function mouseUp(event:MouseEvent):void
{
    dragging = false;
}

function mouseMove(event:MouseEvent):void
{
if (dragging)
{
    tx = event.stageX - p1.x;
    ty = event.stageY - p1.y;
    if (tx * tx + ty * ty > length * length)
    {
        p2.x = p1.x + tx / Math.sqrt(tx * tx + ty * ty) * length;
        p2.y = p1.y + ty / Math.sqrt(tx * tx + ty * ty) * length;
    }
    else
    {
        p2.x = event.stageX;
        p2.y = event.stageY;
    }
}
}

答案 3 :(得分:0)

这样的事情应该成功:

private function projectLocation(center:point, radius:uint, radian:Number):Point 
    {
        var result:Point = new Point();

        //obtain X
        result.x = center.x + radius * Math.cos(radian));

        //obtain Y
        result.y = center.y + radius * Math.sin(radian));

        return result;
    }

显然,根据需要进行修改,但您只需要发送一个中心点,半径然后一个弧度(您可以使用angle * (Math.PI / 180)获得)。如果它们没有改变,你可以很容易地在前两个参数中进行硬编码。什么是弧度变化,这就是你需要随着时间的推移而改变的东西,因为你的鼠标正在拖动(由mouseX距离定义到中心点 - 正面或负面)。

希望这有助于您入门 -

更新

这就是我如何解决这个问题 - 这是一个有点儿的错误,因为当序列开始时,冰球重置为0度。话虽如此,我只是看到了 - @Nox做对了。我将使用projectLocation函数发布我到达的内容;)

package com.b99.testBed.knob 
{
    import com.b99.testBed.Main;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;

    /**
     * ...
     * @author bosworth99
     */
    public class Knob extends Sprite
    {

        private var _puck       :Sprite;
        private var _track      :Sprite;
        private const DIAMETER  :uint = 100;
        private const RADIUS    :uint = DIAMETER / 2;

        public function Knob() 
        {
            super();
            init();
        }

        private function init():void
        {
            assembleDisplayObjects();
            addEventHandlers();
        }

        private function assembleDisplayObjects():void
        {
            _track = new Sprite();
            with (_track) 
            {
                graphics.beginFill(0xffffff, 1);
                graphics.lineStyle(1, 0x000000);
                graphics.drawEllipse(-RADIUS, -RADIUS, DIAMETER, DIAMETER);
                graphics.endFill();
            }
            this.addChild(_track);
            _track.x = Main.stage.stageWidth / 2;
            _track.y = Main.stage.stageHeight / 2;

            _puck = new Sprite();
            with (_puck) 
            {
                graphics.beginFill(0x2DFE07, 1);
                graphics.drawEllipse(-8, -8, 16, 16);
                graphics.endFill();
                x   = _track.x;
                y   = _track.y - _track.width / 2;
                buttonMode = true;
            }
            this.addChild(_puck);
        }

        private function addEventHandlers():void
        {
            Main.stage.addEventListener(MouseEvent.MOUSE_DOWN, activate);
            Main.stage.addEventListener(MouseEvent.MOUSE_UP, deactivate);
        }

        private function deactivate(e:MouseEvent):void 
        {
            Main.stage.removeEventListener(MouseEvent.MOUSE_MOVE, update);
        }

        private var _origin:uint;
        private function activate(e:MouseEvent):void 
        {
            Main.stage.addEventListener(MouseEvent.MOUSE_MOVE, update);
            _origin = mouseX;
        }

        private function update(e:MouseEvent):void 
        {
            var distance:Number;
            (mouseX < _origin)? distance = -(_origin - mouseX) : distance = mouseX - _origin;
            if(distance > 40){distance = 40};
            if(distance < -220){distance = -220};

            var angle:Number  = distance;  //modify?
            var radian:Number = angle * (Math.PI / 180);
            var center:Point  = new Point(_track.x, _track.y);
            var loc:Point     = projectLocation(center, RADIUS, radian);

            _puck.x = loc.x;
            _puck.y = loc.y;
        }

        private function projectLocation(center:Point, radius:uint, radian:Number):Point 
        {
            var result:Point = new Point();

            //obtain X
            result.x = center.x + radius * Math.cos(radian);

            //obtain Y
            result.y = center.y + radius * Math.sin(radian);

            return result;
        }

    }

}

主要区别在于我通过水平(x)移动获得角度,而不是检查光标角度。 Thos,与@Nox非常好的相比,手动捕获值感觉有点hacky。如果我继续清理,我会继续前进;)

好问题 - 干杯