AS3中的碰撞检测问题?

时间:2011-07-09 15:52:21

标签: actionscript-3 actionscript collision-detection

我正在尝试使用箭头键控制的可旋转线。当您单击鼠标时,一个球从光标上掉落,并在它到达该线时停止。

然而,球总是停在线的最高点,横跨一条平行于x轴的线。

我的文档类如下:

package  
{
    import flash.display.MovieClip;
    import flash.events.*
    import flash.display.Stage
    import ball

    public class Engine extends MovieClip 
    {
        public static var stageRef:Stage

        private static var leftKey:Boolean = false
        private static var rightKey:Boolean = false

        public static var pi = Math.PI 
        public static var lineRotate:Number = 0
        public static var spinRate:Number = 60

        public static var ground:line = new line()

        public function Engine() 
        {
            // constructor code
            stage.addEventListener(Event.ENTER_FRAME, loop)
            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler)
            stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler)
            stage.addEventListener(MouseEvent.CLICK, addBall)

            stageRef = stage

            ground.x = 300
            ground.y = 200

            stage.addChild(ground)
        }

        private static function keyDownHandler(e:KeyboardEvent)
        {
            if (e.keyCode == 37) //left
            {
                leftKey = true
            }

            if (e.keyCode == 39)
            {
                rightKey = true
            }
        }

        private static function keyUpHandler(e:KeyboardEvent)
        {
            if (e.keyCode == 37) //left
            {
                leftKey = false
            }

            if (e.keyCode == 39) //right
            {
                rightKey = false
            }
        }

        public function loop(e:Event)
        {
            spin()
        }

        public static function addBall(e:MouseEvent) //adds ball
        {
            var tempBall:ball = new ball()

            tempBall.x = e.stageX
            tempBall.y = e.stageY

            stageRef.addChild(tempBall)
        }

        private static function spin() //spins the "ground" line
        {
            if (leftKey) // minus
            {
                lineRotate -= spinRate
            }

            if (rightKey) // plus
            {
                lineRotate += spinRate
            }

            ground.rotation = lineRotate * (pi / 180) //convert to radians
        }
    }
}

球的等级如下:

package
{
    import flash.display.MovieClip;
    import flash.events.*

    public class ball extends MovieClip 
    {
        public var vX:Number = 0
        public var vY:Number = 2

        private var gravity:Number = 0
        public function ball()
        {
            // constructor code
            addEventListener(Event.ENTER_FRAME, loop)
        }

        public function loop(e:Event)
        {
            this.x += vX
            this.y += vY

            this.vY +=  gravity

            if (this.x > stage.stageWidth || this.x < 0 || this.y < 0 || this.y > stage.stageHeight)
            {
                removeSelf()
            }

            if (Engine.ground.hitTestObject(this))
            {
                trace('yep')
                stopBall()

            }
            else
            {
                trace('nope')
            }
        }

        public function removeSelf()
        {
            removeEventListener(Event.ENTER_FRAME, loop)
            this.parent.removeChild(this)
        }

        public function stopBall()
        {
            gravity = 0
            vY = 0
            vX = 0
        }
    }
}

我已将.swf上传到here.

2 个答案:

答案 0 :(得分:2)

根据文章Collision detection methods: hit-test and hit-testobject alternatives,这是设计:

  

正如您所见,Flash使用了边界   盒子的对象 - 它需要   最高和最低点,   最左边和最右边的点和   在它们周围画一个矩形。

作者提到了几种替代方法来解决此限制,例如使用hitTestPoint或Grant Skinner的基于形状的碰撞检测。

答案 1 :(得分:2)

你可以做的最容易的事情是,对于球是一个hitTestPoint,第三个参数为true,它会打开形状检测。

ground.hitTestPoint(X,Y,TRUE);

http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/DisplayObject.html#hitTestPoint()

好吧,嗯,你可以检查球上的一个点,就像它的底点一样,或者你可以检查球底部的一系列点以便更精确......这是最快的方法如果你没有计划任何比这更复杂的事情,请这样做。但是,如果你想创建一个完整的充实游戏,请将其留给像http://www.box2dflash.org/这样的二维物理库。

为了更大的游戏而忘记skinners碰撞检测(虽然这样的小东西可以存活下来),因为它是基于位图的并且会比box2D更能杀死性能,这是一个很好的掩码示例,但它不是一个好主意出于性能原因使用。

我已经改变了你的代码。我已经在球的底部发现了一堆积分,请记住,电影剪辑中球的0,0位于球中心的中心位置。

package 
{
    import flash.display.MovieClip;
    import flash.events.*;
    import flash.geom.Point;

    public class ball extends MovieClip
    {
        public var vX:Number = 0;
        public var vY:Number = 2;

        private var gravity:Number = 0;
        public function ball()
        {
            // constructor code
            addEventListener(Event.ENTER_FRAME, loop);
        }

        public function loop(e:Event)
        {
            this.x +=  vX;
            this.y +=  vY;

            this.vY +=  gravity;

            if (this.x > stage.stageWidth || this.x < 0 || this.y < 0 || this.y > stage.stageHeight)
            {
                removeSelf();
            }

            /*we will now check something like 18 points on the bottom side of the ball for colision
            instead of just the bottom, you can probably guess why... if you cant, replace i on line 
            43 (var angleInRadians...) with 270 to test and then drop balls on a slopped ground surface... of course
            you should definitely juse a physics engine like http://www.box2dflash.org/ for anything more complex.
            */
            for (var i:int = 180; i<=360; i+=10)
            {
                /*keep in mind that ball.rotation property has 0 at the top of the ball, while here for these we are using the standard
                Cartesian coordinate system. The effect of this on rotation would be that it is +90 and for X(yes, X) it would be Math.SIN(),
                not Math.COS()!!!, and for Y it would be Math.sin() with a minus in front because of the flash coordinate system y axis rotation.
                It's not really related to this, but it's a point that has anoyed me to no end in trig related stuff in flash when I was starting.
                */
                var angleInRadians:Number = i / 180 * Math.PI;
                var posX:Number = this.x + Math.round(Math.cos(angleInRadians) * width / 2);
                var posY:Number = this.y - Math.round(Math.sin(angleInRadians) * height / 2);//minus because y in flash is upside down
                Engine.ground.hitTestPoint(posX,posY, true);

                if (Engine.ground.hitTestPoint(posX,posY, true))
                {
                    stopBall();
                }
                else
                {
                }
            }
            /*if (Engine.ground.hitTestObject(this))
                        {
                            trace('yep')
                            stopBall()

                        }
                        else
                        {
                            trace('nope')
                        }*/
        }

        public function removeSelf()
        {
            removeEventListener(Event.ENTER_FRAME, loop);
            this.parent.removeChild(this);
        }

        public function stopBall()
        {
            gravity = 0;
            vY = 0;
            vX = 0;
        }
    }
}

与上述无关,你需要重新考虑一下你的OOP,你做的事情有点不对劲:)。这个小项目可以应对的项目,但任何更大的项目都会让你头疼。不要把它当作攻击,我想尝试引导你走向“正确”的路径,并向你展示代码中的逻辑谬误,我没有提到这个“pwn”你导致你的oop很糟糕:)。

例如,重力,内部球?哎呀......如果你想让忍者,手里剑,布娃娃等有引力呢?你是否会将Ninjas从球类中分类出来?

<> Ninja延伸球?这应该很有趣。

你认为重力的东西可能会有更好的地方吗? “重力”根本就是球的属性(错误,它本身就是这样,但不是这种均匀的引力你放在这里,它更像是一种渗透,所有现存的东西,因为有一些巨大的身体,我们太接近了)?它应该在引擎......

另外,你做了Engine.ground的事情......现在,这是一个静态变量......这是另一件坏事,非常糟糕的事情:)

原因类似于前面的例子,但有点转过身来。如果你想重新使用球,比如Engine2怎么办?还是Crytek引擎?还是UDK?我认为这可能是有问题的,你不觉得吗?你必须进去改变球类...想象一下,如果你使用的每一个代码都强迫你这样做......

基本上,你可能做过这样的事情:

var bla:ball = new ball(ground);

这样更好,更好......现在我们可以轻松地在crytek,udk和Engine2中使用它......

从技术上讲,您希望在代码中避免使用类似静态变量之类的东西,这种想法称为封装。我们的想法是,您可以将类的内部工作隐藏在与其无关或不应该了解的其他类中。您需要知道的越少,您的课程就越便携,可重复使用(yada yada)。 Engine.ground是一个巨大的封装断路器,因为球绝对不需要知道类引擎,它应该只关注对地面本身的引用......

随着时间的推移你将学习所有这些,编程中的模式,特别是哪些模式可以节省时间,哪些是令人震惊的,以及为什么(静态变量有它们的用途,但仅限于超简单的东西,如果是非常简单的东西,请避免使用Singletons你知道它们是什么吗。

http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming

对不起我给你带来的头痛......

请记住,使用物理引擎来处理更复杂的代码,不要重新发明轮子(为你的项目重用一切对他们有利/足够的东西)......测试这样的东西很有趣,但其他人在你之前做得更好,除非你特别需要深入研究他们的领域,否则你不需要关心每一个细节。你可能正在尝试建立游戏/东西,而不是打扰牛顿动力......

HitTest对于简单的东西来说很好,但这接近简单...... :)我们已经在这里略微进入了物理学,你可能会发现你需要一个更强大的解决方案......

一般来说,通过考虑类之间的封装和依赖关系,尝试使代码“健壮”。如果一个依赖感觉不自然(即球取决于引擎)那么某些东西可能是错误的,并且稍后会破坏,将会破坏另一个游戏或引擎或...我会在这里结束...

保重,玩得开心。