Обнаружение столкновений в среде на основе плиток - PullRequest
1 голос
/ 12 февраля 2010

У меня проблемы с обнаружением столкновений. в основном, когда я ударяю по твердой плитке, тело моего персонажа уже находится на половине пути в плитке. вот мой код.

Свойства wY и wX - это мое позиционирование в игровом мире. Не сценическое позиционирование. dx и dy - скорость, с которой путешествует персонаж. Этот первый фрагмент кода находится в игровом цикле. Точка фокусировки моего персонажа центрирована по оси х

package com.objects 
{
    import flash.display.MovieClip;
    import com.eapi.EngineApi;
    import flash.events.Event;

    /**
     * ...
     * @author Anthony Gordon
     */
    public class Engine extends EngineApi
    {
        public var friction:Number =  0.93;
        protected var Heros:Array;

        public function Engine(w:Number = 540,h:Number = 360, tw:Number = 50, th:Number = 50) 
        {
            super(w, h, tw, th);
            Heros = new Array();
        }

        override protected function loop(e:Event):void
        {
            UpdateObjects();
            Rules();
            CheckHero();
            UpDateMap();
        }

        public function AddHero(g:GameObject):void
        {
            Heros.push(g);
        }

        protected function Rules():void
        {
            //Everything Has friction
            for (var i:Number = 0; i < gameObjects.length; i++)
            {
                var char:GameObject = GameObject(gameObjects[i]);
                char.dx *= friction;
                //char.dy *= friction;

                //Below is the tile positioning of my character
                var cgridx:Number = Math.floor(char.wX / tileW);
                var cgridy:Number = Math.floor(char.wY/ tileH);

                //This is the tile in front of the character
                var nextx:Number = Math.floor((char.wX + char.dx) / tileW);
                var nexty:Number = Math.floor((char.wY + char.dy) / tileH);

                //We assume the character is in the air before we figure it to be false
                char.onGround = false;

                //I am about to remove the vars from cgrid below. Keep a look out for issues in the future
                if (mapHolder[currentMap][nexty][cgridx] == 0 || mapHolder[currentMap][nexty][cgridx] == 2)
                {
                    //If character is falling down
                    if (char.dy > 0)
                    {
                        char.wY = (nexty * tileH) - 1;
                        cgridy = Math.floor(char.wY / tileH);
                        char.dy = 0;
                        char.onGround = true;
                    }
                    else if (char.dy < 0)//If character is going up
                    {
                        char.wY = (nexty * tileH) + (tileH + 1);
                        cgridy = Math.floor(char.wY / tileH);
                        char.dy = 0;
                    }
                }

                //mapHolder is a array that holds an array of maps and their tile numbers
                if (mapHolder[currentMap][cgridy][nextx] == 2)
                {
                    if (char.dx > 0)//if character is going right
                    {
                        char.wX = ((nextx * tileW) - 1);
                    }
                    else if (char.dx < 0)// if character is going left
                    {
                        char.wX = (nextx * tileW) + (tileW + 1);
                    }

                    char.dx = 0;
                }
                //if character is not on ground then keep faling
                if (char.onGround == false)
                {
                    char.dy += .9;
                    if (char.dy > 30) char.dy = 5;
                }
            }
        }

        protected function CheckHero():void
        {
            var char:Hero = Heros[0];
            char.x = char.wX - offsX;
            char.y = char.wY - offsY;

            if (char.wX < 0)
            {
                char.wX = 0;
                char.dx = 0;
            }

            if (char.wY < 0)
            {
                char.wY = 0;
                char.dy = 0;
            }

            offsX = char.wX - (vWidth/2);
            offsY = char.wY - (vHeight/2);

            if (offsX < 0)
            {
                offsX = 0;
            }

            if (offsY < 0)
            {
                offsY = 0;
            }

            //If screen hits the world END STOP!!!
            if ((offsX + vWidth) > wWidth)
            {
                offsX = (wWidth - vWidth);
            }

            if ((offsY + vHeight) > wHeight)
            {
                offsY = (wHeight - vHeight);
            }
            /////

            //If char hits the end, Stop!!
            if (char.wX > wWidth)
            {
                char.wX = char.wX - wWidth;
                char.wX = wWidth;
            }

        }


    }

}

Вот мой класс персонажей

package com.objects 
{
    import flash.display.MovieClip;
    import flash.events.*;
    /**
     * ...
     * @author Anthony Gordon
     */
    [Embed(source='../../../bin/Assets.swf', symbol='Hero')]
    public class Hero extends GameObject
    {   
        private var aKeyPress:Array;
        private var jumpDisabled:Boolean = false;

        public function Hero() 
        {
            wY = 150;
            wX = 90;
            speed = .5;
            aKeyPress = new Array();
            TheGame.sr.addEventListener(KeyboardEvent.KEY_DOWN, keyDownListener);
            TheGame.sr..addEventListener(KeyboardEvent.KEY_UP,keyUpListener);
        }

        private function keyDownListener(e:KeyboardEvent):void {
            //trace("down e.keyCode=" + e.keyCode);         
            aKeyPress[e.keyCode]=true;
        }

        private function keyUpListener(e:KeyboardEvent):void {
            //trace("up e.keyCode=" + e.keyCode);
            aKeyPress[e.keyCode]=false;
        }

        override public function UpdateObject():void
        {
            Controls();
            updatePosition();
        }

        private function Controls():void
        {
            wX += dx;
            wY += dy;

            if (aKeyPress[38])//Key press up
                ;//vy -= speed;         
            else if (aKeyPress[40])//Key press down
                ;//dy += speed;

            if (aKeyPress[37])//left
                dx -= speed;
            else if (aKeyPress[39])//Right
                dx  += speed;

            if (aKeyPress[32]){//space
                jump();
            }                   
        }//End Controls

        private function jump():void
        {
            if (!jumpDisabled)
            {
                if (onGround)
                {
                    dy = -15;
                    jumpDisabled = true;
                }
            }
            else
            {
                jumpDisabled = false;               
            }
        }
    }
}

Ответы [ 4 ]

2 голосов
/ 12 февраля 2010

Вы должны пройти тестирование, прежде чем двигаться.

Из того, что я могу разобрать из вашего кода выше, вы перемещаете плеер, а затем проверяете его на столкновение. Если это так, то вы должны быть готовы отменить ход, если он что-то ударит (что не очень интересно делать или отлаживать).

Проще говоря, вы можете вызвать метод IsBlocked (), который просто смотрит на тайл, расположенный в направлении, в котором игрок собирается двигаться.

if (!player.IsBlocked())
{
    player.Move();
}
else
{
    player.HandleCollision();
}
2 голосов
/ 12 февраля 2010

Я не могу понять ваш код, но, создав свой движок Mega Man, я узнал кое-что, чем могу поделиться. Мой метод обработки столкновений заключался в том, чтобы попытаться двигаться, а затем отступить на необходимую сумму. Вам также необходимо знать, в каком направлении вы подошли к блоку, что не является тривиальным.

1) попытаться переместить. player.position += player.velocity

2) проверка на наличие столкновений с помощью пересечения ограничительной рамки

2a) Найдите пересечение ограничивающих прямоугольников игрока и блока. Я не собираюсь приводить формулу для этого, это почти тривиально. 2b) Ключевой момент: вы ДОЛЖНЫ рассматривать пересечение 0 высоты или 0 ширины (но не обоих) как столкновение! В противном случае ваш игрок будет «вибрировать» против поверхности столкновения.

3) Используя прямоугольник пересечения, определите направление захода на посадку. Алгоритм для этого включает сравнение наклонов скорости захода на посадку и диагонали прямоугольника пересечения.

4) в зависимости от направления захода на посадку (горизонтального или вертикального) решить, следует ли отступить от компонента движения x или y. Используйте высоту или ширину пересечения в качестве суммы для возврата.

5) Сохраните факт, что ваш игрок теперь заблокирован в этом направлении. Обратите внимание, что вы теперь касаетесь блока, но не внедряетесь в него. Вот почему пересечение нулевого размера все еще является столкновением - если это не так, то в следующем кадре он подумает, что не сталкивается и снова падает, что приводит к "вибрирующему" эффекту.

Что касается "уже на полпути в клетке" - вы случайно сравниваете центральную точку игрока с краем плитки?

1 голос
/ 12 февраля 2010

Что ж, это трудно отладить, не находясь там, но вот что-то, что бросилось мне в глаза.

Разница между этими двумя строками кажется мне странной:

за то, что я идунаправо

char.wX = ((nextx * tileW) - 1);

для перехода влево

char.wX = (nextx * tileW) + (tileW + 1);

Почему у вас (tileW + 1) в конце второго?Я думаю, что эти строки будут одинаковыми.Я не уверен, почему в первой строке тоже есть -1, но, по моему мнению, эти строки должны быть, по крайней мере, одинаковыми.

Кроме того, почему вы сначала возитесь с dx и dy?когда это основано на плитке, и человек может двигаться только с шагом в одну плитку от того, что я вижу.То, как вы это делаете, мне кажется очень странным.

0 голосов
/ 12 февраля 2010

Ну, я исправил проблему. Это не самое большое, но в десять раз лучше, чем раньше. вот что я сделал ..

Я изменился ...

if (mapHolder[currentMap][cgridy][nextx] == 2)
                {
                    if (char.dx > 0)//if character is going right
                    {
                        char.wX = ((nextx * tileW) - 1);
                    }
                    else if (char.dx < 0)// if character is going left
                    {
                        char.wX = (nextx * tileW) + (tileW + 1);
                    }

                    char.dx = 0;
                }

К этому ...

if (mapHolder[currentMap][cgridy][right] == 2)
                {
                    char.wX = right * (tileW) - (tileW / 2);
                    char.dx = 0;
                }
                else if (mapHolder[currentMap][cgridy][left] == 2)
                {
                    char.wX = right * (tileW) + (tileW / 2);
                    char.dx = 0;
                }

Мои центральные точки персонажа находятся посередине как по оси Y, так и по оси X. Для этого я сделал следующее:

if (mapHolder[currentMap][cgridy][left] == 2)
                {
                    char.wX = (left + 1) * (tileW) + (tileW / 2);
                    char.dx = 0;
                }

                if (mapHolder[currentMap][cgridy][right] == 2)
                {
                    char.wX = right * (tileW) - (tileW / 2);
                    char.dx = 0;
                }

слева и справа - плитки слева и справа от Героя. для левой стороны я получил следующую плитку справа, выполнив это (слева + 1). затем я получил в пикселях мою умножающую плитку W (слева + 1) * (tileW). Все точки фокусировки моих плиток находятся слева вверху Таким образом, чтобы персонаж был расположен справа. Мне пришлось добавить еще половину плитки. В противном случае центральная точка моего героя была бы между левой плиткой и плиткой справа (то есть слева + 1). По праву было почти то же самое. но вы получите картину .... я надеюсь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...