1 votes

Détection des collisions dans un environnement à base de tuiles

J'ai des problèmes avec ma détection de collision. En fait, lorsque je frappe une tuile solide, le corps de mon personnage est déjà à mi-chemin dans la tuile. Voici mon code.

Les propriétés wY et wX sont le positionnement de mon univers de jeu. Pas le positionnement de la scène. dx et dy sont la vitesse à laquelle le personnage se déplace. Ce premier bout de code se trouve dans une boucle de jeu. Le point focal de mon personnage est centré sur l'axe x.

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;
            }

        }

    }

}

Voici la classe de mon personnage

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;               
            }
        }
    }

}

2voto

Jason Kester Points 2111

Vous devriez faire des tests avant de vous déplacer.

D'après ce que je peux comprendre de votre code ci-dessus, vous déplacez le joueur, puis vous testez la collision. Si c'est le cas, vous devez être prêt à annuler le mouvement s'il touche quelque chose (ce qui n'est pas amusant à faire ou à déboguer).

Plus simplement, vous pourriez appeler une méthode IsBlocked() qui regarde simplement la tuile située dans la direction où le joueur est sur le point de se déplacer.

if (!player.IsBlocked())
{
    player.Move();
}
else
{
    player.HandleCollision();
}

2voto

Tesserex Points 11149

Je ne peux pas vraiment comprendre votre code, mais en créant mon moteur Mega Man, j'ai appris certaines choses que je peux partager. Ma méthode de gestion des collisions consistait à tenter le mouvement, puis à reculer d'une quantité nécessaire. Vous devez également savoir de quelle direction vous avez approché le bloc, ce qui n'est pas trivial.

1) tenter le déménagement. player.position += player.velocity

2) vérifier les collisions à l'aide de l'intersection des boîtes de délimitation.

2a) Trouvez l'intersection des rectangles de délimitation du joueur et du bloc. Je ne vais pas donner de formule pour cela, c'est presque trivial. 2b) Point clé : Vous DEVEZ considérer une intersection de 0 hauteur ou 0 largeur (mais pas les deux) comme une collision ! Sinon votre joueur va "vibrer" contre la surface de collision.

3) En utilisant le rectangle d'intersection, déterminez la direction d'approche. L'algorithme pour cela consiste à comparer les pentes de la vitesse d'approche et de la diagonale du rectangle d'intersection.

4) en fonction de la direction d'approche (horizontale ou verticale), décidez si vous devez reculer de la composante x ou y du mouvement. Utilisez la hauteur ou la largeur de l'intersection comme quantité à reculer.

5) Enregistrez le fait que votre joueur est maintenant bloqué dans cette direction. Notez que vous touchez maintenant le bloc, mais que vous n'y êtes pas intégré. C'est pourquoi une intersection de taille 0 est toujours une collision - si ce n'était pas le cas, à l'image suivante, il penserait qu'il n'y a pas de collision et tomberait à nouveau, ce qui entraînerait un effet de "vibration".

Pour ce qui est d'être "déjà à mi-chemin de la tuile", est-ce que vous comparez accidentellement le point central du joueur au bord de la tuile ?

1voto

Justin Peel Points 17348

C'est une chose difficile à déboguer sans y être, mais voici quelque chose qui m'a frappé.

La différence entre ces deux lignes me paraît étrange :

pour aller à droite

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

pour être allé à gauche

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

Pourquoi avez-vous (tileW + 1) à la fin de la seconde ? J'aurais pensé que ces lignes seraient les mêmes. Je ne sais pas non plus pourquoi il y a un -1 sur la première ligne, mais ces lignes devraient au moins être identiques à mon avis.

De plus, pourquoi vous embrouillez-vous avec dx et dy en premier lieu alors que c'est un truc basé sur les tuiles et que la personne ne peut se déplacer que par incréments d'une tuile d'après ce que je vois. La façon dont vous faites cela me semble très étrange.

0voto

numerical25 Points 3235

J'ai résolu le problème. Ce n'est pas le meilleur mais c'est dix fois mieux qu'avant. Voici ce que j'ai fait

J'ai changé...

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;
                }

A ce...

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;
                }

Les points centraux de mon personnage se trouvent au milieu sur l'axe des y et des x. Pour ce faire, j'ai procédé comme suit

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;
                }

gauche et droite sont les tuiles à gauche et à droite de Hero. Pour la gauche, j'ai obtenu la prochaine tuile à droite en faisant ceci (gauche + 1), puis j'ai obtenu sa position en pixels en multipliant tileW par (gauche + 1) * (tileW). Tous les points de focus de mes tuiles sont en haut à gauche. Donc pour que le personnage soit positionné à droite. J'ai dû ajouter une autre moitié de tuile. Sinon le point central de mon héros serait entre la tuile de gauche et la tuile de droite (c'est à dire gauche+1). Pour la droite, c'était presque pareil. mais vous voyez l'image.... J'espère.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X