Ranking
Original Post
[Tuto]Lua scripting et toribash
Tuto original de Melmoth
Comme promis, je vais commencer à vous parler un peu des possibilités offertes par l'addition du scripting lua dans Toribash, dans une série de 12 tutos. Ce thread peut aussi vous servir à poser les questions concernant le scripting lua dans TB. J'essaierai de répondre avec autant de rigueur que possible.

Ecrire des scripts demande une certaine connaissance de l'informatique. Parfois même des notions mathématiques et une relative connaissance de l'anglais. Je vais détailler un maximum les bases algorithmiques, mais je ne peux prétendre à vous enseigner la "logique" algorithmique. Si vous n'avez absolument aucune base de développement, ce tutorial sera TRES difficile à appréhender, mais -normalement- pas insurmontable.


Chapitre 01 - Qu'est ce qu'un script lua ? Que permet lua dans Toribash ? Que ne permet pas lua dans Toribash ?

Résumé : Cours purement théorique, pour situer de quoi on parle.

Lua est un langage de script incorporé dans de nombreuses applications actuelles (World of Warcraft pour ne citer que la plus connue) afin d'enrichir le contenu ou de customiser les fonctionnalités du jeu ou de l'application. C'est donc un langage de programmation (comme C ou Java), à ceci près qu'il n'est pas compilé (aucun ".exe" n'est créé) mais interprété : c'est (dans notre cas) Toribash qui lit et traduit le code lua. Je vulgarise à mort, mais en gros, c'est ça.

Dans le cas de Toribash, donc, les fichiers lua interviennent à plusieurs niveaux. Le Torishop in game et les tutoriaux in game, par exemple, sont codés en lua. Ce qui nous intéressera majoritairement, nous, ce sont les fichiers ".lua" que l'on place directement dans le dossier "Toribash3.8xx\data\script", et qui sont ensuite accessibles par le menu in game "Setup->Scripts".

Pour éditer les fichiers .lua, le bloc note suffit. Mais je vous conseille d'utiliser un éditeur un peu plus avancé, NotePad ++ par exemple.

Maintenant, LA question. A quoi servent ces scripts lua ?
Réponse : à beaucoup de chose. Les scripts vous permettent d'intervenir à plusieurs niveau, que ce soit au niveau du gameplay ou de l'affichage (2D et 3D). Théoriquement, il est possible de créer un éditeur de mod, un ukebot avec intelligence artificielle, un administrateur automatique de serveur toribash, un "mode story"...
La seule grosse limitation (mais heureusement qu'elle existe) : vous ne pouvez pas interagir avec les toris dans une scène multiplayer, car les positions des toris, les points, les DMs et les conditions de victoire/défaite sont calculés côté serveur, et votre script s'exécute côté client.
Il existe d'autres limitations gênantes, comme l'impossibilité de lire un fichier dans un dossier parent au dossier script, l'impossibilité de modifier la position spatiale d'une goutte de sang... mais la gestion de lua par Toribash est quand même suffisamment riche pour vous offrir satisfaction.

Fin du premier cours, si vous avez des questions, utilisez ce thread. Au prochain cours vous écrirez votre premier programme en lua !


Le saviez-vous ?

Vous pouvez éxécuter un script grâce à la commande chat : "/ls nom_du_script.lua"
J'ai eu l'idée du script atmospheres en essayant de coder un système de sang "réaliste". J'ai remarqué une limitation du nombre de gouttes de sang et je me suis dit "ce serait à peine suffisant pour des flocons de neige !".



Chapitre 02 - Les instructions basiques : les variables, les conditions, les boucles, les opérateurs, l'écriture console, le debugging.


Résumé : Tuto sur les bases de l'algorithmique. Travaux pratiques : affichage des nombres pairs dans la console.

Lua, comme tout langage informatique, contient un ensemble de mots clés, que l'on doit utiliser pour écrire ses instructions. D'une manière générale, l'exécution des instructions se fait dans l'ordre où elles sont écrites, même si nous verrons plus tard que ce n'est pas tout à fait vrai.

Avant toute chose, je vais vous parler un peu des variables. Les variables sont des objets informatiques qui ont un nom, un type et une valeur.
Si j'écris dans mon code lua :
Code:

truc=145
Alors partout dans mon code, je pourrais utiliser le mot "truc" pour désigner le nombre 145. Pour l'instant, ça vous parait pas très utile, on est d'accord. Mais sans variable, on ne peut pas faire d'informatique.



Parmi les instructions de base, on distingue le if, mot clé de la condition. Sa syntaxe en lua est la suivante :
Code:

if (condition) then
liste d'instructions
else (facultatif)
autre liste d'instruction (facultatif)
end
Par exemple, si j'écris :

Code:

truc=145
if (truc<150) then
truc=30
end
Voila ce qui se passe :
Je rentre la valeur 145 dans la variable "truc".
Je teste grâce au if : est-ce que "truc" est inférieur à 150 ?
145 est bien mathématiquement inférieur à 150, donc j'exécute les instructions qui sont entre le "then" et le "end" : je mets la valeur 30 dans "truc".
A la fin de mon code, "truc" est donc égal à 30.

Par contre, si j'ai :
Code:

truc=301
if (truc<150) then
truc=30
end
Comme 301 n'est pas inférieur à 150, je n'exécute pas le "truc=30". A la fin de mon code, "truc" est toujours égal à 301.
Un autre exemple avec l'instruction "else" :

Code:

truc=301
if (truc<150) then
truc=30
else
truc=456
end
Les instructions écrites entre "else" et "end" sont exécutées si et seulement si la condition du if n'est pas vraie.
301 n'est pas inférieur à 150, donc on n'exécute pas le "truc=30". Par contre, on rentre dans les instructions du else et on exécute "truc=456".
A la fin de notre code truc est donc égal à 456.



Voyons maintenant les instructions d'itération. Traduction : les boucles.
Les boucles sont utilisées pour faire des opérations répétitives. On distingue deux mots clés différents pouvant générer une boucle en lua : le for et le while.
Un petit exemple pour comprendre le for :

Code:

for truc=1,8 do
machin=truc
end
est équivalent à :

Code:

machin=1
machin=2
machin=3
machin=4
machin=5
machin=6
machin=7
machin=8
Explication : "for truc=1,8 do" veut dire "pour truc allant de 1 à 8, je fais". Donc on fait les instructions pour truc=1, puis pour truc=2, et ainsi de suite jusqu'à truc=8.
Donc notre variable "machin" aura pour valeur 8 à la fin du for.

Il existe une variante de syntaxe pour le for :

Code:

for truc=1,11,2 do
machin=truc
end
Vous avez remarqué le "2" ? Situé comme il est, il veut dire qu'au lieu de compter "truc" de 1 en 1, on le compte de 2 en 2. Ce code est donc équivalent à :
Code:

machin=1
machin=3
machin=5
machin=7
machin=9
machin=11
Passons au while. Son utilisation est parfois plus naturelle que le for, mais leurs utilisations est globalement similaire. Syntaxe :

Code:

while(condition) do
instructions
end
Veut dire : tant que la condition est vraie, j'exécute les instructions entre le do et le end.
Exemple :
Code:

compteur=0
truc = 0
while(compteur<3) do
compteur = compteur + 1
truc = truc + 3
end
Première arrivée au while, compteur et truc sont à 0. 0 est inférieur à 3, donc on rentre dans les instructions. Deuxième passage par le while, on a donc compteur à 1 et truc à 3.
1 est toujours < 3, donc on fera un troisième passage dans le while avec compteur = 2 et truc = 6. Au quatrième coup, compteur est égal à 3 et 3 n'est pas strictement inférieur à 3. Donc on ne rentre pas dans les instructions du while et on passe à la partie après le while.
C'est la fin de notre programme, et truc est égal à 6.

C'est également la fin de notre second cours.



TP n°1 : Enumérez moi les nombres pairs de 0 à 40, en utilisant un for ou un while.

Correction :

Code:

for nbr=0,40,2 do
run_cmd("echo " .. nbr)
end
Code:

nbr = 0
while(nbr<41) do
run_cmd("echo " .. nbr)
nbr = nbr + 2
end

Le saviez-vous ?


Une variable peut avoir un nom comportant n'importe quelle suite de lettres, de chiffres ou de "_". Les autres caractères sont interdits.
Vous pouvez écrire des lignes de commentaires dans votre code lua en commençant votre ligne par "--". Ces lignes de commentaires ne sont pas interprétées par Toribash, vous pouvez donc vous en servir pour prendre des notes ou pour annoter un passage compliqué dans votre code.



Chapitre 03 - les types de variables, les tables, les fonctions.

Résumé : Tuto sur l'algorithmique "avancée". Travaux pratiques : optimisation du TP précédent.

Nous avons vu que les variables avaient un nom, une valeur et un type, mais je n'ai pas trop parlé des types. Le type d'une variable définit le contexte dans laquelle elle peut être utilisé. Un exemple vaudra mieux que ma définition peu claire : en lua, vous utiliserez en majorité des nombres (sur lesquels ont peut effectuer des opérations mathématiques), des chaînes de caractères (des mots, des phrases, que l'on peut concaténer, afficher, etc.), des booléens (vrai ou faux) et des tableaux (ensemble de nombres ou de chaînes regroupé en un seul élément).

Le lua n'est pas fortement typé, cela signifie que vous n'êtes pas obligé de préciser le type d'une variable lors de sa déclaration.

Ce qu'il est possible de faire avec des variables numériques :

Les comparer avec : == (égal), ~= (différent), < (strictement inférieur), > (strictement supérieur), <= (inférieur ou égal), >= (supérieur ou égal)
Utiliser les opérateurs basiques : +, -, *, / (division), % (division entière),^ (puissance)
Utiliser les fonctions de la bibliothèques 'math' : math.cos(x) retourne le cosinus de x, par exemple. Elles sont détaillées ici en anglais.



Ce qu'il est possible de faire avec des chaînes de caractères :

Les concaténer avec "..". Par exemple "Melmoth " .. "gére l" .. "e steac" .. "k" est équivalent à "Melmoth gére le steack".
Utiliser les fonctions de la bibliothèques 'string' et pattern (compliqué, à vour plus tard).



Ce qu'il est possible de faire avec des booléens :

Utiliser les opérateur 'and', 'or' et 'not'
Les utiliser comme condition dans un if ou un while.



Ce qu'il est possible de faire avec un tableau :

Ajouter un élément, le trier, récupérer sa taille ou utiliser les fonctions de la bibliothèque table.
Récupérer le i-ème élément : si t est notre tableau, t[1] est le premier élèment, t le i-ème .





Exemples concrets d'utilisation

Affichage des nombres pair de 0 à 40 sur une seule ligne :

Code:

chaine_resultat = ""
for i=0,40,2 do
chaine_resultat = chaine_resultat .. " " .. i
end
run_cmd("echo " .. chaine_resultat)
En gros :

initialisation de la chaine, vide
pour tous les nombres pairs de 0 à 40
on concatène notre variable chaine_resultat avec un espace séparateur, puis la valeur de i, qui se convertit tout seul en chaine.
on affiche notre ligne dans le chat.



Utilisation d'un tableau pour stocker une valeur aléatoire correspondant à l'état des joints, puis affichage :
Un petit bout de code utile si vous voulez faire un random ukebot...

Code:

-- initialisation
joints_uke = { }
for i=0,19 do
joints_uke[i] = math.random(1,4)
run_cmd("echo Le joint n°" .. i .. " est a l'etat : " .. joints_uke[i])
end
On verra dans un prochain cours comment réellement faire un random ukebot... c'est facile vous verrez.
Donc ici, on commence par initialiser le tableau vide ( { } )
Pour les 20 joints, on utilise la fonction random entre 1 et 4 pour associer un etat au joint, et on rentre cette valeur dans notre tableau.
On affiche ensuite le numéro du joint et son etat.



Les fonctions.

Attention, ça va se compliquer un peu. Les fonctions sont très utiles pour structurer votre code, le rendre plus lisible, et plus court. Chaque fois qu'on écrit plusieurs fois le même code, il faut se dire qu'on aurait pu faire plus court en utilisant une fonction.

Une fonction a un nom, peut avoir un type, peut avoir une liste de paramètres.
Voici comment définir une fonction :

Code:

function nom_de_lafonction([param1,[param2,[...]]])

....Instructions....

[return une_valeur]
end
Tout qui est entre [] est facultatif.

Voici une fonction basique qui affiche "Hello World" :
Code:

function hw()
run_cmd("echo hello world")
end
Si vous n'écrivez que ça dans votre programme lua, il ne fera rien. Il ne s'agit que de la définition de la fonction, il faut maintenant l'appeler comme ceci, à la suite de la définition.
Code:

hw()
Allez, un exemple compliqué. Une fonction qui retourne une phrase décrivant l'état d'un joint (selon notre tableau de toute à l'heure, pas encore l'état des vrais joints).

Code:

joints_uke = { }

-- notre fonction
function display_joint(no_joint)
resultat =  "Le joint no " .. no_joint .. " est "
if(joints_uke[no_joint] == 1) then
resultat = resultat .. "contract"
elseif(joints_uke[no_joint] == 2) then
resultat = resultat .. "extend"
elseif(joints_uke[no_joint] == 3) then
resultat = resultat .. "hold"
elseif(joints_uke[no_joint] == 4) then
resultat = resultat .. "relax"
end
return resultat
end

-- on remplit notre tableau
for i=0,19 do
joints_uke[i] = math.random(1,4)
end

--On appelle notre fonction pour les joints 0 et 4.
run_cmd("echo " .. display_joint(0))
run_cmd("echo " .. display_joint(4))
Si vous avez compris cet exemple, vous maîtrisez parfaitement les fonctions. Vous pouvez vous attaquer au TP n°2.

Le saviez-vous ?

Pour plus de propreté et de sécurité, vous pouvez mettre le mot clé "local" devant vos déclaration de variables ou de fonction. Cela empêche la variable d'être vue par d'autre script lua, et cela permet d'éviter les conflits de variables entre deux scripts différents.
Une fonction peut en appeler une autre ! Elle peut même s'appeler elle même, mais attention aux boucles infinies qui bloqueront Toribash



Chapitre 04 - Les Hooks


Résumé : comment automatiser une fonction dans Toribash. Travaux pratiques : gestion du clavier pour changer la valeur d'une variable, et hook sur l'affichage 2D.

On passe aux choses amusantes. La leçon d'aujourd'hui concerne les hooks, qui permettent en fait la gestion des évènements et l'automatisation des fonctions (voir chapitre précédent) dans Toribash.
Un exemple pour mieux comprendre, il est possible de détecter l'évènement "appui sur une touche du clavier" et de l'associer à une fonction écrite par vos soins (permettant par exemple la gestion améliorée d'une caméra ou même un jeu 2D intégré à TB !).

La syntaxe d'ajout d'un hook est la suivante :

Code:

add_hook(evenement, nom_du_groupe, votre_fonction)
->  evenement est l'évènement déclencheur du hook. C'est une chaîne de  caractère (chaîne entre guillemets) parmi la liste décrite plus bas.
->  nom_du_groupe sert à préciser le groupe du hook. Cela permet de  'classer' vos hooks en plusieurs groupes distincts afin de ne retirer  les hooks appartenant à un groupe tout en laissant les autres. C'est  également une chaîne de caractère, définie par vos soins.
->  votre_fonction est le nom (sans guillemets, cette fois) de la fonction  que vous voulez appeler quand l'évènement 'evenement' a lieu
Vous pouvez également retirer un hook ou un groupe de hook :

Code:

remove_hook(evenement, nom_du_groupe, votre_fonction) -- suppression d'un hook
remove_hooks(evenement, nom_du_groupe) -- suppression d'un groupe de hooks
La liste des évènements "hookables" est décrite dans startup.lua :

Code:

"new_game" (début d'une nouvelle partie)
"new_mp_game" (début d'une nouvelle partie multiplayer)
"enter_frame" (entrée dans une nouvelle frame)
"end_game" (fin d'une partie ou d'un replay)
"leave_game" (fin d'une partie ou d'un replay, pas vu de différences notables avec le précédent)
"enter_freeze" (entrée dans le mode 'edit')
"exit_freeze" (sortir du mode 'edit')
"key_up" (une touche du clavier a été relachée)
"key_down" (une touche du clavier a été pressée)
"mouse_button_up" (un bouton de la souris a été relaché)
"mouse_button_down" (un bouton de la souris a été pressé)
"mouse_move" (la souris a été bougée)
"player_select" (un joueur a été sélectionné (par clic sur son Tori, en mode SP))
"joint_select" (un joint a été sélectionné (quand la souris passe dessus))
"body_select" (pareil qu'au dessus avec les parties du corps, je crois)
"draw2d" (affichage 2D, intervient 30 à 60 fois par secondes, selon votre FPS, juste après draw3D)
"draw3d" (affichage 3D, intervient 30 à 60 fois par secondes, selon votre FPS)
"play" (passage en mode "replay")
"camera" (camera solicitée)
"console" (qq chose a été écrit dans le chat)
-- Tous les suivants sont pour la gestion de la souris sur un joueur dans la liste d'attente, en MP)
"bout_mouse_down"
"bout_mouse_up"
"bout_mouse_over"
"bout_mouse_outside"
"spec_mouse_down"
"spec_mouse_up"
"spec_mouse_over"
"spec_mouse_outside"
Allez, deux exemples concrets avant le TD afin de bien comprendre comment ça marche.

Exemple 1 : Affichage des coordonnées de la souris

L'affichage des coordonnées du pointeur de la souris peut se faire en deux étapes :
1°) Récupération des coordonnées de la souris (Hook sur le déplacement de la souris)
2°) Affichage de ces coordonnées (Hook sur l'affichage 2D)

Pour afficher les coordonnées, je vais me servir de la fonction draw_text(text,x,y,font), que vous apprendrez en détail dans le cours suivant (dessin 2D).
Place au code, qui, je pense, est assez clair :

Code:

local mouse_pos_x = 0
local mouse_pos_y = 0

-- on ecrira en rouge... explications au cours suivant


local function update_coord(x,y)
   mouse_pos_x = x
   mouse_pos_y = y
end

local function draw_coord()
   set_color(1,0,0,1)
   draw_text("X : " .. mouse_pos_x,20,100,2)
   draw_text("Y : " .. mouse_pos_y,20,120,2)
end

add_hook("mouse_move","coord_souris", update_coord)
add_hook("draw2d","coord_souris", draw_coord)
Lancez le script, bougez la souris... c'est magique.

Exemple 2 : Un menu au clavier

La particularité de cet exemple est la façon dont les hooks communiquent entre eux : vous pouvez effectivement activer ou désactiver le hook sur l'affichage 2D grace au hook sur le clavier. Lisez bien attentivement pour comprendre ce qu'il se passe.

Code:

-- les éléments de notre menu
local list_items_menu = {"Item 1","Item 2","Item 3","Item 4","Item 5"}

-- l'élément sélectionné
local selected_item = 1

-- booleen servant a savoir si on doit ou non afficher le menu
local bool_menu = true

local function menu_clavier()
   --explications des fonctions 2D la semaine prochaine
   --dessin du fond du menu
   set_color(0.1,0.1,0.1,0.5)
   draw_quad(20, 100, 320, 250)

   set_color(0,0.3,0.5,1)
   draw_text("Fermez et ouvrez le menu avec 'A'", 25, 105,1)
   draw_text("Naviguez avec les fleches et validez avec 'L'", 25, 125,1)
   
   set_color(0,0.1,0.3,1)
   for i=1,table.getn(list_items_menu) do
      if(i==selected_item) then
         set_color(0.8,0,0,1)
         draw_text(i .. ". " .. list_items_menu[i], 30, 150+(i*20),1)
         set_color(0,0.1,0.3,1)
      else
         draw_text(i .. ". " .. list_items_menu[i], 30, 150+(i*20),1)
      end
   end
end

local function key_pressed(key)
   -- les return 1 servent à 'annuler' le vrai effet des touches (les flêches ne bougeront pas la caméra si le menu est actif)
   if(key == 273 and bool_menu) then
      selected_item = selected_item - 1
      if (selected_item < 1) then
         selected_item = table.getn(list_items_menu)
      end
      return 1
   elseif(key == 274 and bool_menu) then
      selected_item = selected_item + 1
      if (selected_item > table.getn(list_items_menu)) then
         selected_item = 1
      end
      return 1
   elseif(key == 108 and bool_menu) then
      run_cmd("echo Item choisi : " .. list_items_menu[selected_item])
      return 1
   elseif(key == 113 and not bool_menu) then
      add_hook("draw2d", "menu_clavier", menu_clavier)
      bool_menu = true
      return 1
   elseif(key == 113 and bool_menu) then
      remove_hook("draw2d", "menu_clavier", menu_clavier)
      bool_menu = false
      return 1
   end
   if(bool_menu) then
      return 1
   else
      return 0
   end
end

add_hook("draw2d", "menu_clavier", menu_clavier)
add_hook("key_down","menu_clavier", key_pressed)
C'est tout pour aujourd'hui. N'hésitez pas à poser des questions, je sais que ça se complique, là.

Chapitre 05 - Dessin 2D
Résumé : détail des possibilités offertes par le Hook sur l'affichage 2D. Travaux pratiques : divers effets 2D.

Cours un peu plus "récréatif que la semaine dernière, puisqu'il s'agit d'une application directe et amusante des hooks sur l'affichage 2D. En résumé, vous allez pouvoir dessiner sur le premier plan (devant les toris, donc "par dessus" toute la scène 3D).
Pour commencer, il semblerait qu'il ne soit possible de dessiner uniquement lors d'un hook sur "draw2d".

Les diverses primitives de dessin 2D sont les suivantes :

Code:

Primitives :
draw_disk(number  pos_x, number pos_y, number inner, number outer, integer slices,  integer loops, number start, number sweep, integer blend) -> Dessine  un donut ou un disque à la position voulue
draw_quad(number pos_x,  number pos_y, number width, number height [, integer texture_id]) ->  dessine un rectangle, texturé ou non, à la position voulue

Ecriture de texte :
draw_text(string  text, number pos_x, number pos_y [, integer font_type])  -> écrit du  texte à la position voulue, de la taille voulue.
draw_right_text(string  text, number pos_x, number pos_y [, integer font_type]) -> écrit du  texte aligné à droite à la position voulue, de la taille voulue. pos_x  correspond cette fois à la distance entre la fin du texte et le bord  droit de la fenêtre.
draw_centered_text(string text, number pos_y [,  integer font_type])  -> Ecrit le texte centré horizontalement, à la  hauteur voulue, de la taille voulue

Moins utile :
draw_chat_message(integer index, number pos_x, number pos_y) -> Ecrit le ième message du chat à la position voulue.
draw_chat_messages(number pos_x, number pos_y) -> Ecrit tous les messages du chat à la position voulue
Il existe également d'autres fonctions intéressantes pour le dessin 2D :

Code:

set_color(number  red, number green, number blue, number alpha)  -> fixe la couleur  pour le dessin de la prochaine primitive. Red, Green, Blue et Alpha  (transparence) sont des valeurs décilmales comprises entre 0 et 1.
get_window_size(...) = number,number -> Pour récupérer la taille de la fenêtre (voir exemple dans l'énoncé du TD)
get_screen_pos(number x, number y, number z); = number,number -> pour récupérer les coordonnées 2D courantes d'un point 3D
J'imagine qu'il doit rester quelques points obscurs, notamment sur les disks, les quads à textures et les polices de caractères, que j'ai honteusement oublié de décrire en détail. Je vais le faire en exemple :

Les polices de caractères : pour mieux comprendre, lisez et exécuter le script suivant, vous allez voir les différents types de polices disponibles :

Code:

local function hook2D_display()
   set_color(0.5,0,0,1)
   for i=0,3 do
      draw_text("Ecriture en police " .. i, 10, 100+(60*i),i)
   end
end

add_hook("draw2d", "exemple", hook2D_display)
Les quads, avec ou sans textures : copier une head texture dans votre dossier data/scripts et exécuter le code suivant :

Code:

texid = load_texture("head.tga")

local function hook2D_display()
   set_color(0,0,0,1)
   draw_text("un rectangle noir sans texture :",10,100)
   draw_quad(240, 100, 60, 45)
   draw_text("un rectangle avec texture :",10,160)
   set_color(1,1,1,1) -- nécessaire de remettre une couche de blanc
   draw_quad(240, 160, 60, 45, texid)
end

add_hook("draw2d", "exemple", hook2D_display)
Les disks, explications des divers paramètres

Les différents paramètres des disques permettent en fait le dessin de toutes sortes de formes.
draw_disk(number pos_x, number pos_y, number inner, number outer, integer slices, integer loops, number start, number sweep, integer blend)
inner = Le rayon du trou au milieu du disque
outer = Le rayon extérieur du disque
slices = Nombre de subdivision du disque
loops = Nombre d'anneau concentriques subdivisant le disque
start = L'angle de départ, en degré, de la portion de disque
sweep = L'angle total de la portion de disque
blend = Je crois que c'est encore une couche alpha mais qui se fond avec le noir, pas avec le blanc... à vérifier.

Des exemples :

Code:

local function hook2D_display()
   set_color(0,0,0.3,1)
   -- Disque complet :
   draw_disk(70, 100, 0, 50, 32, 1, 0, 360, 0)
   -- Camembert
   draw_disk(70, 230, 0, 50, 32, 1, 40, 300, 0)
   -- Donut :
   draw_disk(70, 360, 20, 50, 32, 1, 0, 360, 0)
   -- Carre
   draw_disk(170, 230, 40, 50, 4, 1, 45, 360, 0)
   -- Triangle
   draw_disk(170, 360, 40, 50, 3, 1, 180, 360, 0)
end

add_hook("draw2d", "exemple", hook2D_display)
Voila, voila, vous êtes prêts pour le TP.


Chapitre 06 - Modélisation 3D

Résumé : détail des possibilités offertes par le Hook sur l'affichage 3D. Travaux pratiques : divers effets 3D.

Pour ce cours, même principe que la dernière fois, sauf qu'au lieu de dessiner de la 2D au premier plan, on modélisera carrément des objets 3D notre scène 3D.
Pour clarifier les choses une fois pour toute : ce n'est pas du modding, les objets que vous ajoutez n'ont qu'une présence visuelle, et non physique. Il n'y aura donc aucune collision avec les éléments ajoutés (à moins que vous les codiez vous même... bonne chance). Pour ceux qui suivent, c'est bien la technique que j'utilise dans mes atmosphères.

Il n'est possible de modéliser des objets 3D que dans un hook sur draw3D.

La liste des commandes 3D :

Code:

Primitives :

draw_box(  number pos_x, number pos_y, number pos_z, number size_x, number size_y,  number size_z, number rotation_x, rotation_y, rotation_z) --  modélisation d'un pavé

draw_capsule( number pos_x, number pos_y,  number pos_z, number height, number radius, number rotation_x,  rotation_y, rotation_z) -- modélisation d'une capsule (exemple : les  tibias de votre tori sont des capsules)

draw_disk_3d(number  pos_x, number pos_y, number pos_z, number inner, number outer, integer  slices, integer loops, number start, number sweep, integer blend) --  modélisation d'un disque (cercle plein), qui pour moi bugge à mort

draw_sphere( number pos_x, number pos_y, number pos_z, number radius) -- modélisation d'une sphère

Primitives avec matrices de rotation :

draw_box_m( number pos_x, number pos_y, number pos_z, number size_x, number size_y, number size_z, table matrix_rot)

draw_capsule_m( number pos_x, number pos_y, number pos_z, number height, number radius, table matrix_rot)

draw_sphere_m( number pos_x, number pos_y, number pos_z, number radius, table matrix_rot)

Divers : 

set_color(number  red, number green, number blue, number alpha)  -- fixe la couleur pour  le dessin de la prochaine primitive. Red, Green, Blue et Alpha  (transparence) sont des valeurs décilmales comprises entre 0 et 1.

draw_ground_impact(integer player_index) -- peu utile, dessine un disque creux sous le joueur indiqué.
Ok, donc la principale difficulté est d'ordre mathématique : vous devez maitriser les coordonnées spatiales (x,y,z) et surtout les rotations spatiales (pas évident du tout). Je suis pas le mieux placé pour expliquer tout ça, je m'aide pas mal des forums mathématiques quand j'ai un problème.
Des exemples de difficultés que l'on peut rencontrer : déplacer des objets selon un chemin circulaire ou pseudo aléatoire, concevoir des formes complexes à partir de plusieurs primitives et leur faire faire une rotation ensemble... derrière, il y a des maths, et si vous regardez un peu le code des atmos, vous verrez que c'est pas toujours évident.

Mais les exemples simples peuvent aussi être très efficaces. Ensemble je vais vous montrer comment faire un temple basique autour de votre combat.

Commençons par le plus simple : le toit du temple : un pavé plat, horizontal, au dessus des toris.

J'ai choisi une couleur gris clair sympa. Les positions en x et en y sont à 0 afin de centrer le pavé. La hauteur est de 12, ça parait pas mal pour un temple
J'ai choisi une largeur et une longueur de 65, mais rien n'oblige à faire le pavé carré ! L'épaisseur est de 2. Enfin, les 3 "0" qui suivent correspondent à la une rotation nulle sur tous les angles, vu qu'on veut notre toit bien à plat.

On met le tout dans un hook sur draw3d, et ça donne ça :

Code:

local function draw_temple()
set_color(0.9,0.9,0.9,1)
draw_box(0, 0, 12, 65, 65, 2, 0, 0, 0)
end

add_hook("draw3d", "lua_tuto", draw_temple)
On va ajouter les colonnes à l'aide de plusieurs capsules bien réparties. On pourrait les ajouter une par une, mais comme nous sommes fainéants (et intelligents) on va plutôt utiliser des "for". Toutes nos capsules auront la même hauteur, et les mêmes dimensions. Elles auront également toutes une rotation nulle en tout angle. Seuls les paramètres correspondant aux positions et x et en y vont varier d'une colonne à l'autre, c'est ce qu'on va faire varier dans nos for.

Mettons que nous voulons 6 colonnes par "côtés" du temple. Commençons par le for alignant 8 colonnes selon l'axe des y (x restera fixé).
Nos colonnes auront un rayon de 1, donc on peut faire varier leurs positions entre -30 et 30 (je rappelle que la largeur du temple est 65, centré en 0). Dans un "for i=0,5", notre position y variera selon (-30 + 12*i) (soit bien entre -30 et 30).
Bon, c'est un peu décousu comme ça... mais c'est une excellente manière d'avoir des formes géométriques parfaitement droites, donc accrochez vous

Dans le même for, on peut modéliser les colonnes parallèles, celles de l'autre côté du temple. Ca donne ça :

Code:

for i=0,5 do
draw_capsule( 30, (-30 + 12*i), 6, 12, 1, 0, 0, 0)
draw_capsule( -30, (-30 + 12*i), 6, 12, 1, 0, 0, 0)
end
On aurait pu modéliser aussi les 2 autres côtés du temple, en utilisant le même for, deux autres draw_capsule mais avec les coordonnées x et y inversées. L'ennui c'est que chaque angle aurait été doublé, ce qui est inutile est couteux (pas trop, mais mieux vaut prendre tout de suite de bonnes habitudes). On va donc faire un autre for, mais cette fois en supprimant les colonnes extrèmes, donc un for de 0 à 4.

Code:

for i=1,4 do
draw_capsule( (-30 + 12*i), 30, 6, 12, 1, 0, 0, 0)
draw_capsule( (-30 + 12*i), -30, 6, 12, 1, 0, 0, 0)
end
Si vous n'avez pas les shaders, le sol est transparent dans un monde blanc, donc vous voyez le bas de colonnes et c'est moche. On va donc faire un sol opaque qui cache ça : un pavé très fin à altitude 0.

Code:

draw_box(0, 0, 0, 70, 70, 0.001, 0, 0, 0)
Et voila notre temple !

Le code complet :

Code:

local function draw_temple()
set_color(0.9,0.9,0.9,1)
draw_box(0, 0, 12, 65, 65, 2, 0, 0, 0)
draw_box(0, 0, 0, 70, 70, 0.001, 0, 0, 0)

for i=0,5 do
draw_capsule( 30, (-30 + 12*i), 6, 12, 1, 0, 0, 0)
draw_capsule( -30, (-30 + 12*i), 6, 12, 1, 0, 0, 0)
end
   
for i=1,4 do
draw_capsule( (-30 + 12*i), 30, 6, 12, 1, 0, 0, 0)
draw_capsule( (-30 + 12*i), -30, 6, 12, 1, 0, 0, 0)
end
   
end

add_hook("draw3d", "lua_tuto", draw_temple)
Et bien, je crois qu'on peut passer au TD.

Chapitre 07 - Récupérer/Modifier les options, les règles et les informations concernant un replay

Résumé : comment récupérer et changer la valeur des game rules ou des options. Travaux pratiques : affichage de tout ce qu'il est possible de récupérer.

Chapitre 08 - Les entrées/sorties : les fichiers

Résumé : comment sauvegarder et charger des données dans un fichier. Travaux pratiques : sauvegarde et chargement du mod et des game rules.

Chapitre 09 - Customisation des Toris

Résumé : Changer les couleurs des Toris. Travaux pratiques : un ToriArlequin !

Chapitre 10 - Manipuler les Toris et les éléments de la scène

Résumé : Comment récupérer/modifier l'état des joints, comment démembrer/fracturer un joint, comment obtenir ou modifier les coordonnées spatiales des Toris et de la caméra. Travaux pratiques : un extracteur d'opener basique.

Chapitre 11 - Compléments d'info

Résumé : Utiliser les scripts d'interface graphique de Blam, les fonctions plus "exotiques" du scripting lua... tout ce qui ne rentre pas dans les autres tutos va là. Travaux pratiques : un éditeur de shader avec interface graphique avancée.

Chapitre 12 - Tuto ouvert

Résumé : Vous choisissez un projet, je réalise en détaillant tous les aspects.
Last edited by tomibash; Sep 4, 2013 at 06:37 PM.