Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox



Contrôle d'une camera 3D

Compatible Director MX2004. Cliquer pour en savoir plus sur les compatibilités.Par glurp (Elliot Coene), le 08 septembre 2009

Le résultat en image (1) et en action (2). Cliquez sur la deuxième image pour lui donner le focus, puis bougez la souris pour déplacer la caméra, cliquez-déplacez pour modifier la distance :

cameracontrol.jpg

La 3D sous Director

Je ne ferai pas ici un tutorial sur la 3D sous Director, je sous-entendrai que vous avez déjà quelques bases dans ce domaine.
Pour résumer, un fichier w3d est un “monde” qui contient tout un tas de choses ; les models, les shaders, les textures, les animations, les lumières et les cameras.
Vous pouvez facilement créer plusieurs cameras et jouer avec leurs propriétés pour les différencier.

Quand vous travaillez en 3D sous Director, oubliez le système de sprites et d'acteurs car tout se trouve dans le “monde” que vous placez sur un sprite.
Ensuite, vous créez et définissez la camera courante comme propriété du sprite.

Dans le cadre de ce tutorial, voici le code à placer dans un script de Movie et qui initialise notre monde et notre caméra :

global gWorld
 
on prepareMovie
 
  -- Format de l'application --
  _movie.stage.rect = rect(0,0,640,480)
 
  -- Création de l'acteur 3D --
  gWorld = _movie.newmember(#shockwave3d)
  gWorld.name = "monde3d"
 
  -- Création de la nouvelle camera --
  gWorld.newcamera("tutorial1")
 
  -- Mise en place sur le sprite 1 --
  channel(1).makescriptedsprite(gWorld, point(320,240))
 
end
 
on startmovie
 
  -- Dimensions du sprite 1 --
  sprite(1).width = 640
  sprite(1).height = 480
 
  -- Définition de la camera courante --
  sprite(1).camera = gWorld.camera("tutorial1")
 
  createVisibleParts() -- Crée les repères visuels --
 
end
 
on stopmovie
 
  -- Efface l'acteur 3D --
  gWorld.erase()
 
  -- Réinitialise le sprite 1 --
  channel(1).removeScriptedSprite()
 
end
 
on createVisibleParts
 
  tResource = gWorld.newmodelresource("boite", #box)
  tResource.height = 50
  tResource.width = 50
  tResource.length = 50
 
  gWorld.newmodel("boite1", tResource)
 
  tResource = gWorld.newmodelresource("plane", #plane)
  tResource.width = 500
  tResource.length = 500
 
  gWorld.newmodel("plane1", tResource)
 
end

Et n'oubliez pas de placer le stop à la frame 20 :

on exitframe me
  go to the frame
end

Si vous lancez le programme, vous ne verrez pas encore grand chose pour le moment (la camera “regarde” derrière le point (0,0,0)). Mais les éléments sont biens là, il suffit de manipuler la position z de la camera pour s'en apercevoir.

La camera

Si je me base sur le monde du jeu vidéo, il existe plusieurs types de cameras possible. Mais que ce soit une camera FPS (dans les yeux du personnage), ou à la deuxième voire troisième personne, le comportement est finalement le même.
Il est donc théoriquement possible de créer un comportement de camera universel qui ne différerait que par ses propriétés.
Isolons alors ces différentes propriétés ; qu'est-ce qui définit la particularité d'une camera ?

  1. La cible : les caméras de Director n'ont pas de valeur target, nous allons donc la coder
  2. La distance : celle qui définira le type de vue
  3. L'inclinaison : l'angle que fera la camera avec le sol
  4. La rotation : l'angle que la camera décrit autour de la cible

Maintenant que nous avons défini les propriétés nécessaire, nous allons créer un script parent (voir tutorial sur la programmation orientée objet) “cameraControl_script” qui les regroupera et prendra comme paramètre la référence vers une camera existante.

property pCamera
property pTarget, pDistance, pInclinaison, pRotation
 
on new me, tCamera
 
  me.init(tCamera)
 
  return me
 
end
 
on init me, tCamera
 
  pCamera = tCamera
 
  pTarget = vector(0,0,25)
  pDistance = 500
  pInclinaison = 20
  pRotation = 0  
 
end

Voici notre class créée, mais elle est encore loin d'être terminée.
Il reste à élaborer le contrôle des éléments ; la souris pour la rotation et l'inclinaison de la camera et les touches du clavier pour déplacer la cible.
Pour ce faire, nous allons inscrire l'objet à l'actorList pour recevoir l'événement stepFrame.

property pCamera
property pTarget, pDistance, pInclinaison, pRotation
property pOldMouse
 
on new me, tCamera
 
  me.init(tCamera)
 
  return me
 
end
 
on init me, tCamera
 
  pCamera = tCamera
 
  pTarget = vector(0,0,25)
  pDistance = 500
  pInclinaison = 20
  pRotation = 0  
 
  _movie.actorlist.add(me)
 
  pOldMouse = _mouse.mouseloc
 
end
 
on destroy me
  _movie.actorlist.deleteone(me)
end
 
on stepframe me
 
  if keypressed(123) then -- gauche
    pTarget.x = pTarget.x - 1
  end if
  if keypressed(124) then -- droite
    pTarget.x = pTarget.x + 1
  end if
  if keypressed(125) then -- bas
    pTarget.y = pTarget.y - 1
  end if
  if keypressed(126) then -- haut
    pTarget.y = pTarget.y + 1
  end if
  if keypressed(116) then -- Page Up
    pTarget.z = pTarget.z + 1
  end if
  if keypressed(121) then -- Page Down
    pTarget.z = pTarget.z - 1
  end if
 
  tMouseLoc = _mouse.mouseloc
  tX = tMouseLoc[2] - pOldMouse[2]
  tZ = tMouseLoc[1] - pOldMouse[1]
 
  pRotation = pRotation - tZ
 
  if the mousedown then
    pDistance = pDistance - tX
    if pDistance < 0 then
      pDistance = 0
    end if    
  else
    pInclinaison = pInclinaison - tX       
  end if
 
  pOldMouse = tMouseLoc
 
end

Les propriétés sont désormais manipulées par l'utilisateur, mais le résultat n'est pas encore visible puisque pas appliqué à la camera proprement dite.
Voici donc la partie finalement la plus intéressante de notre code ; l'application bouclée des propriétés de la camera.
Et pour une meilleure visibilité, nous allons utiliser et déplacer notre boite qui représentera notre cible. J'ajoute donc un appel à une fonction setCamera() à la fin de mon stepFrame et définis la fonction comme ceci :

on setCamera me
 
  -- Positionnement de la boite cible --
  gWorld.model("boite1").transform.position = pTarget.duplicate()
 
  tCamTransf = pCamera.transform
  -- Positionnement de la camera sur la cible --
  tCamTransf.position = pTarget.duplicate()
  -- Application des rotations --
  tCamTransf.rotation.z = pRotation
  tCamTransf.rotation.x = pInclinaison  
 
  -- Recule la camera --
  tCamTransf.position = tCamTransf.position + (tCamTransf.zAxis*pDistance)
 
end

ATTENTION : Faites bien attention à spécifier gWorld comme global dans cameraControl_script puisque dans cet exemple j'y fais référence pour déplacer le model boite1.

La class est terminée, il ne reste plus qu'à l'instancier dans le script de movie en ajoutant la ligne suivante à la fin du startMovie :

-- Instance du controle de la camera --
monCameraControl = script("cameraControl_script").new(gWorld.camera("tutorial1"))

Sans oublier de détruire l'instance dans le stopMovie comme ceci :

-- Détruit le contrôle de camera --
  monCameraControl.destroy()
  monCameraControl = void

Sans oublier évidemment de déclarer monCameraControl en global.

Voilà, si tout s'est bien passé, vous devriez pouvoir jouer avec la camera et la cible.
Ceci n'est évidemment qu'un exemple, une base, que je vous laisse optimiser et améliorer selon vos besoins ;-)

NB : Je n'ai volontairement pas bloqué la camera à certains angles, chacun faisant au cas par cas (on pourrait imaginer une batterie de propriétés supplémentaires).
De plus, si votre but est de vraiment utiliser la souris dans un jeu FPS ou autre, il vous faudra utiliser un Xtra pour recentrer le curseur à chaque boucle pour ne pas atteindre les limites de l'écran.

Sources Director MX 2004