Forums Développement Multimédia

Aller au contenu

- - - - -

simple tir en FPS

CODE Actionscript

6 réponses à ce sujet

#1 xfranfran

    Ceinture Blanche

  • Members
  • Pip
  • 3 messages

Posté 10 December 2011 - 18:52 PM

Bonjour :)

Tout d'abord bravo pour votre Minko ! Il a une approche vraiment intéressante et est Open Source !! Grand seigneur :)

Je tente de réaliser un petit FPS. Mais honte à moi, je n'arrive même pas à créer un simple tir qui lance un projectile dans la direction de la camera. Comment faire tirer simplement ?

Je suis parti sur un double TransformGroup :
- "_projectile_group" : pour gérer la rotation
- "_projectile" : pour gérer la translation

avec cette structure : _projectile_group.addChild(_projectile);

Lorsque je détecte un clic, je positionne le projectile à la même place que la caméra, puis je détermine les angles :

_projectile_group.transform.rotationZ = _camera.rotation.x;
_projectile_group.transform.rotationY = -_camera.rotation.y;

L'avantage c'est que pour déplacer mon projectile, je n'ai plus qu'à mettre dans un enterFrame :
_projectile.transform.appendTranslation(1,0,0);

J'avoue que je comprends pas pourquoi : ..rotationZ = _camera.rotation.x;

En tout cas, ça fonctionne pour certaines valeurs de rotation de la camera, mais pas pour toutes. J'ai des résultats incompréhensibles.. pour mon niveau ^^ Parfois l'axe X est inversé, parfois un tir sur 2 ne part pas dans la bonne direction...

Pourriez m'aiguiller vers une solution ?

Merci d'avance !!

#2 henri4

    Ceinture Blanche

  • Members
  • Pip
  • 14 messages

Posté 30 December 2011 - 15:17 PM

Utilise une autre matrice pour ton perso joueur, avec Z orienté vers le haut et Y comme axe de tir. De toutes façons tu en auras besoin pour bien positionner le flingue et faire d'autres trucs.

Ensuite tu déduis la position de la matrice caméra en fonction de la matrice joueur.

#3 nicoptere

  • Honoris
  • PipPipPipPipPipPipPipPip
  • 3946 messages

Posté 03 January 2012 - 16:30 PM

salut,

(pas encore joué avec Minko mais bon)
je ne sais pas comment tu fais le calcul de l'angle de tir mais en gros.

il faut faire une "unprojection" de la souris 2D sur le near plane de la caméra.
ça te donne un Vector3D ( "mouse" disons ) d'où tu peux déduire un axe camera -> mouse3D qui te donne la direction du tir.

//"unproject" de la souris 2D en 3D ( partie spécifique à Minko que je ne sais pas écrire )
var mouse:Vector3D = view.unproject( view.mouseX, view.mouseY );

//crée un axe entre la caméra et la souris en 3D
var dir:Vector3D = mouse.subtract( camera.position );//idem, camera.position, je ne sais pas dire ça en Minko

//on le normalise pour pouvoir le multiplier par sa vitesse
dir.normalize();

//on multiplie par une vitesse ( 10 )
dir.scaleBy( 10 );

//assigne la position de l'objet
projectile.position = camera.position.add( dir );
 

et on enterframe, tu peux ajouter dir à la position de ton objet.
il partira - normalement - dans la bonne direction.

#4 Jean-Marc Le Roux

    Ceinture Noire

  • Minko
  • PipPipPipPipPipPipPip
  • 210 messages

Posté 04 January 2012 - 02:23 AM

Citation

Je tente de réaliser un petit FPS. Mais honte à moi, je n'arrive même pas à créer un simple tir qui lance un projectile dans la direction de la camera. Comment faire tirer simplement ?

En fait il te faut deux choses:
- la position de la camera
- la rotation de la camera

Ces deux données doivent être obtenues en "world space" (espace global).

Il faut donc différencier deux cas:
1) ta caméra est ajoutée "directement" dans la scène et ne compte pas de TransformGroup parmis ces ancêtres: dans ce cas les propriétés "position" et i"rotation" de ton objet ICamera sont effectivement en espace monde
2) ta caméra subit des transformations imposées par des TransformGroup, et alors ses propriétés "position" et "rotation" doivent être correctement influencées par ses transformations pour les récupérer en world space

Etape 1:

Si tu es dans la situation 1, passe directement à l'étape 2.

Si tu es dans la situation 2, il y'a plusieurs manières de procéder mais disons que la méthode la plus simple à comprendre et qui marchera "toujours" et de faire des calculs préliminaires pour effectivement transformer les données de ta caméra pour les récupérer en world space.

Pour transformer les données locales de ta caméra - par exemple sa position - en données world space, il faut que tu aies la matrice de monde qui influence cette dernière. Cette matrice de monde, c'est en fait le résultat de toutes les matrices de transformations imposées par les ancêtres de ta caméra. Par exemple en Flash, si tu as un Sprite dans un Sprite dans un Sprite, si les deux parents successifs appliquent une translation en x de -2, alors le troisième Sprite subira en toute logique une translation en x de -4. C'est exactement pareil en 3D, mis à part que toutes les transformations (translation, rotation et scale) sont stockées dans une matrice, et que les matrices peuvent être combinées en les multipliant.

Le calcul peut donc se résumer à:
- récupérer les données locales de la caméra (position et rotation)
- initialiser une matrice vide qui permettra de stocker le résultat de la transformation appliquée par tous les ancêtres
- parcourir tous les ancêtre de la caméra pour remonter jusqu'à la racine
- pour chaque ITransformableScene croisé lors de la remonté, on récupère sa matrice et dans notre matrice résultat on stocke la multiplication entre la matrice du noeud et la matrice résultat obtenue jusqu'ici (=> on "cumule" les effets de chaque transformation rencontrée dans notre matrice résultat)
- une fois arrivée à la racine, notre matrice résultat contient la transformation "globale" appliquée à notre caméra, et on peut donc transformer les donnée locales pour avoir des valeurs en world space

Cette logique est parfaitement valide, mais en terme d'implémentation deux questions se posent assez rapidement:

1) Le traitement ci-dessus fera pas mal de multiplications de matrices alors qu'elles sont déjà faites à chaque fois que la scène est traversée pour être dessinée. C'est dommage et on aimerait pouvoir utiliser les valeurs déjà calculées à ce moment là. Mais comment les récupérer ?

2) Ca semble assez lourd comme traitement pour pas grand chose au final: dans la display list de Flash on a la méthode localToGlobal, alors pourquoi ne pas faire pareil dans Minko ? Et bien on ne peut pas car dans Minko, la "display list" est en fait un scene graph: chaque noeud a plusieurs parents, et il n'existe pas qu'un seul chemin possible entre notre caméra et la "racine" de la scène. Il n'existe donc pas *une* matrice de transformation qui permet de passer de l'espace local à l'espace global, mais éventuellement une infinité. Il n'est donc pas possible d'implémenter une méthode "localToGlobal" sur chaque noeud de la scène.

Pour y répondre, nous avons implémenté un noeud spécial: le GlobalTransformGroup. Ce noeud fait exactement ce qu'il dit: il met à disposition une propriété "localToGlobalTranform : Matrix4x4" qui contient la fameuse matrice qui permet de passer du repère local (le repère utilisé par ce qui est dans ce groupe) au repère global (le repère de la racine de la scène: le "world space"). Si on place notre camera dans un GlobalTransformGroup, alors on peut récupérer facilement la dernière matrice calculée lorsque la scène a été rendue. Deux petites choses à prendre en considérations:

- Cette matrice est mise à jour à chaque fois que la scène est parcourue pour être dessinée, donc il vaut mieux l'utiliser après l'appel à Viewport.render() pour avoir des données les plus à jour possible. Sinon on aura la matrice utilisée pour la frame d'avant, ce qui n'est généralement pas très grave mais il faut y penser.
- Comme expliqué précédemment, ce noeud impose une contrainte assez forte: il ne peut pas y avoir plusieurs chemins entre lui et la racine de la scène (lui et ses ancêtres ne peuvent avoir qu'un seul parent). Pour ce qui est de la caméra, c'est de toute façon toujours le cas.

Donc grâce au noeud GlobalTransformGroup, on retombe sur nos pieds en appliquant juste une contrainte sur la partie du graphe qui stocke le noeud qui nous intéresse.


// init. de la scène
private var _camera : ICamera = new FirstPersonCamera();
private var _cameraLocalToGlobal : GlobalTransformGroup = new GlobalTransformGroup(_camera);
private var _cameraTransform : TransformGroup = new TransformGroup(cameraLocalToGlobal);
private var _scene : Group = new Group(_cameraTransform);

private function keyDownHandler(event : KeyboardEvent) : void
{
  if (event.keyCode == Keyboard.SPACE)
  {
    var localToGlobal : Matrix4x4 = _cameraLocalToGlobal.localToGlobalTransform;
    var cameraPosition : Vector4 = localToGlobal.transformVector(_camera.position);
    var cameraRotation : Vector4 = localToGlobal.transformVectorDelta(_camera.rotation);

    var cube : TransformGroup = new TransformGroup(
      new ColorTexture(0xff0000),
      CubeMesh.cubeMesh
    );
   
    cube.name = "cube";
    cube.transform.setRotation(cameraRotation.x, cameraRotation.y, cameraRotation.z)
                  .setTranslation(cameraPosition.x, cameraPosition.y, cameraPosition.z);

    _scene.addChild(cube);
  }
}
 

A priori ça devrait donc permettre de déjà bien positionner le cube.

Etape 2

Ensuite il faut l'animer. Et là par exemple on va essayer de le faire suivre une ligne droite dans la direction dans laquelle il est tourné.

Voilà une petite application qui te permettra de comprendre comment faire je pense:


public class Main
{
private var _viewport : Viewport = new Viewport();
private var _camera : ArcBallCamera = new ArcBallCamera();
private var _scene : Group = new Group(_camera);
private var _cube : TransformGroup = new TransformGroup(
  new ColorTexture(0x0000ff),
  CubeMesh.cubeMesh
);

public function Main()
{
  initializeScene();

  stage.addChild(_viewport);
  stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
  stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
       
protected function initializeScene() : void
{
        var ground : TransformGroup     = new TransformGroup(
                new ColorTexture(0x999999),
                QuadMesh.quadMesh
        );
                       
        camera.rotation.x = -0.4;
                       
        ground.transform.appendRotation(Math.PI * .5, ConstVector4.X_AXIS)
                        .appendUniformScale(5.);
                       
        _cube.transform.appendUniformScale(.5)
                   .appendTranslation(0., 0.25);
                       
        _scene.addChild(ground)
         .addChild(_cube);
}
               
protected function keyDownHandler(event : KeyboardEvent) : void
{
        switch (event.keyCode)
        {
                case Keyboard.UP :
                        _cube.transform.prependTranslation(0., 0., .1);
                        break ;
                case Keyboard.DOWN :
                        _cube.transform.prependTranslation(0., 0., -.1);
                        break ;
                case Keyboard.LEFT :
                        _cube.transform.prependRotation(-.05, ConstVector4.Y_AXIS);
                        break ;
                case Keyboard.RIGHT :
                        _cube.transform.prependRotation(.05, ConstVector4.Y_AXIS);
                        break ;
        }
}

protected function enterFrameHandler(event : Event) : void
{
  _viewport.render(_scene);
}
}
 

Ce qui est intéressant ici c'est comment en appliquant des rotations/translations successives, on arrive assez facilement à déplacer le cube. C'est du au fait que chaque rotation fait en fait tourner l'espace dans lequel le cube évolue, et non pas le cube lui même: donc le cube avance toujours vers z+, mais l'axe z+ "tourne" avec chaque appel à "prependRotation".

Dernière remarque: pourquoi diable imaginer que la caméra pourrait être subir des transformations de ces parents ? Et bien il y'a pas mal de cas pratiques assez immédiats, par exemple appliquer une animation à la caméra, faire une caméra qui suit un objet (en plaçant l'objet et la caméra dans un même TransformGroup qu'on déplace), mettre la caméra dans un squelette animé, etc...

Enfin, dans Minko v2.0, nous allons documenter l'API d'animation procédurale. Cette API existe déjà dans Minko et elle permet de créer des animations qui vont de la simple translation au skinning de squelette. Nous allons ajouté deux nouvelles fonctionnalités:
- créer des chemins (avec plein d'interpolations différentes) et donc faire suivre un certain parcourt à des objets (très pratique par exemple pour animer la caméra ou traiter correctement les résultat du path finding)
- un système de "controllers", qui permettra non seulement de controller les animations avec beaucoup de précision, mais aussi d'avoir des méthodes plus riches que celles proposées par Matrix4x4: des méthodes pour faire un FPS, des méthodes pour faire tourner des objets, et le tout déjà bindé avec la souris/le clavier si possible. Il n'y aura donc plus d'ArcBallCamera par exemple, mais on pourra faire une Camera dans un TransformGroup contrôlé par un ArcBallController. L'intérêt? Les controllers ne seront pas spécifiques à un objet de la scène - comme c'est le cas avec ArcBallCamera qui est juste une caméra. Avec la même logique, on pourra donc tout contrôler :)

J'espère que je ne t'ai pas trop perdu et que ça te servira :)

a+

#5 xfranfran

    Ceinture Blanche

  • Members
  • Pip
  • 3 messages

Posté 08 January 2012 - 16:20 PM

Merci Henri, Nicolas et Jean-Marc pour vos réponses !

Je pense que tu as raison Henri, je devrais déplacer le perso du joueur et caler la caméra par rapport à lui. Mais je perds sur le coup les méthodes walk() et strafe() de la FirstPersonCamera qui me plaisent beaucoup ^^

Aussi je continue mes tests en me basant sur la caméra. J'ai implémenté les explications de Jean-Marc, mais j'ai toujours le même problème sur la direction du projectile. Voici le code que j'ai fait :

package  
{
        import aerys.minko.render.Viewport;
        import aerys.minko.scene.node.camera.FirstPersonCamera;
        import aerys.minko.scene.node.group.GlobalTransformGroup;
        import aerys.minko.scene.node.group.Group;
        import aerys.minko.scene.node.group.TransformGroup;
        import aerys.minko.scene.node.mesh.primitive.CubeMesh;
        import aerys.minko.scene.node.mesh.primitive.QuadMesh;
        import aerys.minko.scene.node.texture.ColorTexture;
        import aerys.minko.type.math.ConstVector4;
        import aerys.minko.type.math.Matrix4x4;
        import aerys.minko.type.math.Vector4;
        import flash.display.Sprite;
        import flash.events.Event;
        import flash.events.KeyboardEvent;
        import flash.events.MouseEvent;
        import flash.geom.Point;
        import flash.ui.Keyboard;
        /**
         * ...
         * @author XFranfran
         */

        public class Tir_FPS extends Sprite
        {
                // scene
                private var _viewport : Viewport = new Viewport();
                private var _camera : FirstPersonCamera = new FirstPersonCamera();
                private var _cameraLocalToGlobal : GlobalTransformGroup = new GlobalTransformGroup(_camera);
                private var _cameraTransform : TransformGroup = new TransformGroup(_cameraLocalToGlobal);
                private var _scene : Group = new Group(_cameraTransform);
               
                // rotation camera
                private var _cursor : Point = new Point();
               
                // deplacement
                private var vitesse_dep : int = 1;
               
                // gestion des touches
                private var b_touche_haut : Boolean = false;
                private var b_touche_bas : Boolean = false;
                private var b_touche_gauche : Boolean = false;
                private var b_touche_droite : Boolean = false;
               
                // sol
                private var _ground : TransformGroup = new TransformGroup( new ColorTexture(0x996633), QuadMesh.quadMesh );
               
                // projectiles
                private var _projectile : TransformGroup;
                private var arr_projectiles : Array = new Array();
               
                public function Tir_FPS()
                {
                        if (stage)
                                initialize();
                        else
                                addEventListener(Event.ADDED_TO_STAGE, initialize);
                }
               
                private function initialize(event : Event = null) : void
                {
                        removeEventListener(Event.ADDED_TO_STAGE, initialize);
                       
                        stage.addChild(_viewport);
                       
                        initialize_scene();
                       
                        stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
                       
                        stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
                        stage.addEventListener(KeyboardEvent.KEY_UP, e_onKeyUp);
                        stage.addEventListener(KeyboardEvent.KEY_DOWN, e_onKeyDown);
                }
               
                private function initialize_scene():void
                {
                        _camera.position.y = 5;
                       
                        _ground.transform.appendRotation(Math.PI * .5, ConstVector4.X_AXIS)
                        .appendUniformScale(1000.);
                       
                        _scene.addChild(_ground);
                }
               
                private function shoot():void
                {
                        // calculs coordonnées camera
                        var localToGlobal : Matrix4x4 = _cameraLocalToGlobal.localToGlobalTransform;
                        var cameraPosition : Vector4 = localToGlobal.transformVector(_camera.position);
                        var cameraRotation : Vector4 = localToGlobal.deltaTransformVector(_camera.rotation);
                       
                        // creation du projectile
                        var _projectile : TransformGroup = new TransformGroup( new ColorTexture(0x00ff00), CubeMesh.cubeMesh );
                       
                        _projectile.transform.setRotation(cameraRotation.x, cameraRotation.y, cameraRotation.z)
                                                                .setTranslation(cameraPosition.x, cameraPosition.y, cameraPosition.z);
                       
                        _scene.addChild(_projectile);
                       
                        // stoquage du projectile
                        arr_projectiles.push( _projectile );
                }
               
                private function enterFrameHandler(event : Event) : void
                {
                        // deplacement camera
                        if( b_touche_haut ) _camera.walk( vitesse_dep );
                        if( b_touche_bas ) _camera.walk( -vitesse_dep );
                        if( b_touche_droite ) _camera.strafe( vitesse_dep );
                        if( b_touche_gauche ) _camera.strafe( -vitesse_dep );
                       
                        // deplacement projectile
                        if ( arr_projectiles.length != 0 )
                        {
                                for ( var i:int = 0 ; i < arr_projectiles.length ; i++ )
                                {
                                        arr_projectiles[i].transform.prependTranslation(0., 0., .1);
                                }
                        }
                       
                        // render
                        _viewport.render(_scene);
                }
               
                private function mouseMoveHandler(event : MouseEvent) : void
                {
                        if ( event.buttonDown )
                        {
                                _camera.rotation.y -= (event.stageX - _cursor.x) * .01;
                                _camera.rotation.x -= (event.stageY - _cursor.y) * .01;
                        }
                       
                        _cursor.x = event.stageX;
                        _cursor.y = event.stageY;
                }
               
                private function e_onKeyUp(e : KeyboardEvent):void
                {
                        if( e.keyCode == Keyboard.UP ) b_touche_haut = false;
                        if( e.keyCode == Keyboard.DOWN ) b_touche_bas = false;
                        if( e.keyCode == Keyboard.RIGHT ) b_touche_droite = false;
                        if( e.keyCode == Keyboard.LEFT ) b_touche_gauche = false;
                       
                        if( e.keyCode == Keyboard.SPACE ) shoot();
                }
               
                private function e_onKeyDown(e : KeyboardEvent):void
                {
                        if( e.keyCode == Keyboard.UP ) b_touche_haut = true;
                        if( e.keyCode == Keyboard.DOWN ) b_touche_bas = true;
                        if( e.keyCode == Keyboard.RIGHT ) b_touche_droite = true;
                        if( e.keyCode == Keyboard.LEFT ) b_touche_gauche = true;
                }
        }

}

J'ai mis le swf à l'url : http://www.lotusgame.../Minko/Tir_FPS/
Un drag permet de bouger la caméra, les flèches de se déplacer et espace de tirer.

Pourquoi ça marche pas ? :P

#6 xfranfran

    Ceinture Blanche

  • Members
  • Pip
  • 3 messages

Posté 16 January 2012 - 11:18 AM

Je voulais commencer petit à petit avec cette problématique de tir de projectiles lié à la direction de la caméra. Mais ce n'est pas mon objectif. Ce que j'aimerai faire, c'est un tir qui part dans la direction de la position de la souris. En gros, le joueur n'est pas obligé de centrer la caméra sur la cible pour lui tirer dessus. Il positionne la caméra pour voir la cible et avec la souris il clique sur la cible, ce qui fait partir un projectile dans sa direction.

Et là, ça me dépasse complètement ^^ J'ai beau regarder le détail de Matrix4x4, j'y comprends rien ^^ A l'aide !! :Hola:

#7 Jean-Marc Le Roux

    Ceinture Noire

  • Minko
  • PipPipPipPipPipPipPip
  • 210 messages

Posté 17 January 2012 - 18:42 PM

Voir le messagexfranfran, le 16 January 2012 - 11:18 AM, dit :

Et là, ça me dépasse complètement ^^ J'ai beau regarder le détail de Matrix4x4, j'y comprends rien ^^ A l'aide !! :Hola:

Je vais essayer de regarder ça avant la fin de la semaine mais là tout de suite on est un peu occupé ^^

a+



1 utilisateur(s) li(sen)t ce sujet

0 membre(s), 1 invité(s), 0 utilisateur(s) anonyme(s)

authorised training centre

Centre de Formation Mediabox - Adobe et Apple Authorised Training Center.

Déclaré auprès de la Direction du Travail et de la Formation Professionnelle

Mediabox : SARL au capital de 62.000€ - Numéro d'activité : 11 75 44555 75 - SIRET : 49371646800035

MEDIABOX, 23, rue de Bruxelles, 75009 PARIS

FFP