[Python] Petit programme de raycasting ( - de 3ko)

Petit programme de raycasting ( - de 3ko) [Python] - Python - Programmation

Marsh Posté le 03-03-2005 à 13:49:25    

(j'ai changé le titre, ça fait moins pompeux [:cupra])
 
 
Je me suis mis sérieusement au Python et je viens d'achever mon premier code original hier soir, donc c'est un peu la fête.   [:diskobeck]  
 
Enfin original est un bien grand mot, puisque je n'ai fait que retranscrire mot pour mot, variables pour variables, un programme Rebol, qui était donné en exemple dans un numéro de Login (Numéro 14 de Septembre 2002, pages 76-77).
 
Le principe du programme est bêtement un moteur de Raycasting, comme celui de Wolfenstein premier du nom.
 
Un screenshot ([:cupra]) :
http://img100.exs.cx/img100/1488/doomwdegrade8lv.png
 
 
Il faut aussi télécharger cette image:
http://img71.exs.cx/img71/421/degrade8ag.gif
 
Ca met un dégradé en fond d'image. Il faut le sauver sur le disque, le placer dans le même dossier que le script et le renommer en "degrade.gif".
 
 
 
 
 
Enfin bref, voici le code:

Code :
  1. #########################
  2. #                       #
  3. # Moteur de Raycasting  #
  4. #                       #
  5. #########################
  6. # Import de Tkinter et des fonctions trigo utilisees
  7. from Tkinter import *
  8. from math import cos, radians
  9. # Definition des variables
  10. px = 9 * 1024
  11. py = 11 * 1024
  12. stride = 5 # pas de deplacement 
  13. heading = 0  # angle de vue 
  14. turn = 10  # nombre de degres par rotation
  15. # Definition du labyrinthe.
  16. # 0 = sol ou l'on peut se deplacer
  17. # Plus grand que 0 = mur, et le chiffre defini la couleur
  18. laby = [[ 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7],
  19.         [ 7, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 8],
  20.         [ 8, 0, 0, 0, 12, 0, 0, 0, 14, 0, 9, 7],
  21.         [ 7, 0, 0, 0, 12, 0, 4, 0, 13, 0, 0, 8],
  22.         [ 8, 0, 4, 11, 11, 0, 3, 0, 0, 0, 0, 7],
  23.         [ 7, 0, 3, 0, 12, 3, 4, 3, 4, 3, 0, 8],
  24.         [ 8, 0, 4, 0, 0, 0, 3, 0, 3, 0, 0, 7],
  25.         [ 7, 0, 3, 0, 0, 0, 4, 0, 4, 0, 9, 8],
  26.         [ 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 7],
  27.         [ 7, 0, 5, 6, 5, 6, 0, 0, 0, 0, 0, 8],
  28.         [ 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7],
  29.         [ 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7]]
  30. # Palette de couleur. La valeur du mur defini la valeur de la couleur.
  31. palette = ["#000080","#008000","#008080",
  32.            "#000080","#800080","#808000","#C0C0C0",
  33.            "#808080","#0000FF","#00FF00","#FFFF00",
  34.            "#0000FF","#FF00FF","#00FFFF","#FFFFFF"]
  35. # Table des cosinus precalcules. On gagne un peu en vitesse.
  36. ctable = []
  37. for a in range(0,(359+180)):
  38.     ctable.append(int((cos(radians(a))*1024)/10))
  39. # Fonction pour trouver le cosinu de l'angle v.
  40. def getangle(v):
  41.     return ctable[v+1]
  42. #####################################
  43. #
  44. # Moteur principal du raycasting
  45. #
  46. def retrace():
  47.     # On efface le labyrinthe
  48.     can1.delete('rectangle')
  49.     # On initialise les valeurs
  50.     xy1x = 0
  51.     xy1y = 0
  52.     xy2x = 0
  53.     xy2y = 0
  54.     angle = heading-44 % 360
  55.     if angle < 0:
  56.         angle = angle+360
  57.     # On parcout les 90 degres de l'angle de vue.
  58.     # On lance un rayon xx/yy et des qu'il touche un mur,
  59.     # on affiche un rectangle
  60.     for a in range(angle,angle+89):
  61.         xx = px
  62.         yy = py
  63.         stepx = getangle(a+90)
  64.         stepy = getangle(a)
  65.         l = 0
  66.         while 1:
  67.             xx = xx-stepx
  68.             yy = yy-stepy
  69.             l = l+1
  70.             colonne = int(xx/1024)
  71.             ligne = int(yy/1024)
  72.             # Instruction de controle:
  73.             # la boucle s'arrete si le rayon touche un mur (<> 0).
  74.             if laby[ligne][colonne] <> 0:
  75.                 break
  76.         # On calcule la hauteur du rectangle
  77.         h = int(900/l)
  78.         xy1y = 100-h
  79.         xy2y = 100+h
  80.         # Le rectangle fait 3 pixels de largeur
  81.         xy2x = xy1x + 3
  82.         # On determine la couleur du rectangle
  83.         color = palette[laby[ligne][colonne]]
  84.         # On affiche le rectangle et on recommence
  85.         can1.create_rectangle(xy1x, xy1y, xy2x, xy2y, fill=color,outline=color,tag='rectangle')
  86.         xy1x = xy2x+1
  87. ##################################
  88. #
  89. # Gestionnaire d'evenements :
  90. #
  91. # On determine les 4 fonctions possibles: avancer, reculer, tourner la tête
  92. # à droite et tourner la tête à gauche.
  93. def depl_avance(event):
  94.     global px, py
  95.     # On avance selon le pas "stride" determie au debut.
  96.     # On calcul le cosinus de l'angle en question.
  97.     newpx = px - getangle(heading+90)*stride
  98.     newpy = py - getangle(heading)*stride
  99.     c = int(newpx/1024)
  100.     l = int(newpy/1024)
  101.     # On controle si l'on peut se deplacer.
  102.     if laby[l][c] == 0:
  103.         px = newpx
  104.         py = newpy
  105.     retrace()
  106. def depl_recule(event):
  107.     global px, py
  108.     newpx = px - getangle(heading+90)*stride*-1
  109.     newpy = py - getangle(heading)*stride*-1
  110.     c = int(newpx/1024)
  111.     l = int(newpy/1024)
  112.     if laby[l][c] == 0:
  113.         px = newpx
  114.         py = newpy
  115.     retrace()
  116. def turn_left(event):
  117.     global heading
  118.     heading = (heading + (360 -turn)) % 360
  119.     retrace()
  120. def turn_right(event):
  121.     global heading
  122.     heading = (heading + turn) % 360
  123.     retrace()
  124. # Creation du canvas:
  125. fen1 = Tk()
  126. can1 = Canvas(fen1,bg="dark grey", height=200, width=360)
  127. # On charge le degrade de fond. C'est une image gif.
  128. # A ameliorer: degrade directement en python.
  129. if open("degrade.gif" ):
  130.     photo = PhotoImage(file="degrade.gif",width=360, height=200)
  131.     item = can1.create_image(0, 0, anchor=NW, image=photo)
  132. can1.pack()
  133. # On trace une premiere fois le labyrinthe
  134. retrace()
  135. # Liaison des evenements
  136. fen1.bind("<Up>",depl_avance)
  137. fen1.bind("<Down>",depl_recule)
  138. fen1.bind("<Left>",turn_left)
  139. fen1.bind("<Right>",turn_right)
  140. # demarrage du receptionnaire d'evenements (boucle principale) :
  141. fen1.mainloop()


 
 
Faut encore que je fasse un degradé en fond d'écran (quelqu'un saurait-il faire ceci ?), que je fusionne les commande avance et recule et que je le commente un peu plus. [:dao]
 
Sinon des avis sur le code en lui-même ? Des idées d'amélioration, d'optimisation ?
 
 
PS: le code pour le copier coller directement:
 
#########################
#                       #
# Moteur de Raycasting  #
#                       #
#########################
 
# Import de Tkinter et des fonctions trigo utilisees
 
from Tkinter import *
from math import cos, radians
 
# Definition des variables
 
px = 9 * 1024
py = 11 * 1024
stride = 5 # pas de deplacement    
heading = 0  # angle de vue  
turn = 10  # nombre de degres par rotation
 
# Definition du labyrinthe.
# 0 = sol ou l'on peut se deplacer
# Plus grand que 0 = mur, et le chiffre defini la couleur
laby = [[ 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7],
        [ 7, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 8],
        [ 8, 0, 0, 0, 12, 0, 0, 0, 14, 0, 9, 7],
        [ 7, 0, 0, 0, 12, 0, 4, 0, 13, 0, 0, 8],
        [ 8, 0, 4, 11, 11, 0, 3, 0, 0, 0, 0, 7],
        [ 7, 0, 3, 0, 12, 3, 4, 3, 4, 3, 0, 8],
        [ 8, 0, 4, 0, 0, 0, 3, 0, 3, 0, 0, 7],
        [ 7, 0, 3, 0, 0, 0, 4, 0, 4, 0, 9, 8],
        [ 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 7],
        [ 7, 0, 5, 6, 5, 6, 0, 0, 0, 0, 0, 8],
        [ 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7],
        [ 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7]]
 
# Palette de couleur. La valeur du mur defini la valeur de la couleur.
palette = ["#000080","#008000","#008080",
           "#000080","#800080","#808000","#C0C0C0",
           "#808080","#0000FF","#00FF00","#FFFF00",
           "#0000FF","#FF00FF","#00FFFF","#FFFFFF"]
 
# Table des cosinus precalcules. On gagne un peu en vitesse.
ctable = []
for a in range(0,(359+180)):
    ctable.append(int((cos(radians(a))*1024)/10))
 
# Fonction pour trouver le cosinu de l'angle v.
def getangle(v):
    return ctable[v+1]
 
#####################################
#
# Moteur principal du raycasting
#
 
def retrace():
    # On efface le labyrinthe
    can1.delete('rectangle')
    # On initialise les valeurs
    xy1x = 0
    xy1y = 0
    xy2x = 0
    xy2y = 0
    angle = heading-44 % 360
    if angle < 0:
        angle = angle+360
    # On parcout les 90 degres de l'angle de vue.
    # On lance un rayon xx/yy et des qu'il touche un mur,
    # on affiche un rectangle
    for a in range(angle,angle+89):
        xx = px
        yy = py
        stepx = getangle(a+90)
        stepy = getangle(a)
        l = 0
        while 1:
            xx = xx-stepx
            yy = yy-stepy
            l = l+1
            colonne = int(xx/1024)
            ligne = int(yy/1024)
            # Instruction de controle:
            # la boucle s'arrete si le rayon touche un mur (<> 0).
            if laby[ligne][colonne] <> 0:
                break
        # On calcule la hauteur du rectangle
        h = int(900/l)
        xy1y = 100-h
        xy2y = 100+h
        # Le rectangle fait 3 pixels de largeur
        xy2x = xy1x + 3
        # On determine la couleur du rectangle
        color = palette[laby[ligne][colonne]]
        # On affiche le rectangle et on recommence
        can1.create_rectangle(xy1x, xy1y, xy2x, xy2y, fill=color,outline=color,tag='rectangle')
        xy1x = xy2x+1
 
##################################
#
# Gestionnaire d'evenements :
#
 
# On determine les 4 fonctions possibles: avancer, reculer, tourner la tête
# à droite et tourner la tête à gauche.
 
 
def depl_avance(event):
    global px, py
    # On avance selon le pas "stride" determie au debut.
    # On calcul le cosinus de l'angle en question.
    newpx = px - getangle(heading+90)*stride
    newpy = py - getangle(heading)*stride
    c = int(newpx/1024)
    l = int(newpy/1024)
    # On controle si l'on peut se deplacer.
    if laby[l][c] == 0:
        px = newpx
        py = newpy
    retrace()
def depl_recule(event):
    global px, py
    newpx = px - getangle(heading+90)*stride*-1
    newpy = py - getangle(heading)*stride*-1
    c = int(newpx/1024)
    l = int(newpy/1024)
    if laby[l][c] == 0:
        px = newpx
        py = newpy
    retrace()
def turn_left(event):
    global heading
    heading = (heading + (360 -turn)) % 360
    retrace()
def turn_right(event):
    global heading
    heading = (heading + turn) % 360
    retrace()
 
 
# Creation du canvas:
 
fen1 = Tk()
can1 = Canvas(fen1,bg="dark grey", height=200, width=360)
# On charge le degrade de fond. C'est une image gif.
# A ameliorer: degrade directement en python.
if open("degrade.gif" ):
    photo = PhotoImage(file="degrade.gif",width=360, height=200)
    item = can1.create_image(0, 0, anchor=NW, image=photo)
can1.pack()
# On trace une premiere fois le labyrinthe
retrace()
# Liaison des evenements
fen1.bind("<Up>",depl_avance)
fen1.bind("<Down>",depl_recule)
fen1.bind("<Left>",turn_left)
fen1.bind("<Right>",turn_right)
 
# demarrage du receptionnaire d'evenements (boucle principale) :
fen1.mainloop()


Message édité par Rasthor le 04-03-2005 à 23:57:50
Reply

Marsh Posté le 03-03-2005 à 13:49:25   

Reply

Marsh Posté le 03-03-2005 à 21:21:01    

Le John Carmack suisse :D

Reply

Marsh Posté le 03-03-2005 à 21:34:04    

printf a écrit :

Le John Carmack suisse :D


 :D  
Je précise encore une fois que je n'ai strictement rien inventé. J'ai repris ce programme de Login, qui l'a surement repris de John Carmack.  :jap:

Reply

Marsh Posté le 03-03-2005 à 23:33:24    

C'est très chouette :)

Reply

Marsh Posté le 04-03-2005 à 00:12:09    

New version !!!
 
 
Pour ceux qui veulent avoir un truc plus "style", téléchargez cette image:
http://img71.exs.cx/img71/421/degrade8ag.gif
http://img71.exs.cx/img71/421/degrade8ag.gif
 
Ca met un dégradé en fond d'image. Il faut le sauver sur le disque, le placer dans le même dossier que le script et le renommer en "degrade.gif".
 
 
Ensuite, il faut modifier le code. Il faut ajouter ces deux lignes entre les lignes 110 et 111:
 
photo = PhotoImage(file="degrade.gif",width=360, height=200)
item = can1.create_image(0, 0, anchor=NW, image=photo)
 
 
 
Et voici le résultat:
http://img100.exs.cx/img100/1488/doomwdegrade8lv.png


Message édité par Rasthor le 04-03-2005 à 02:29:12
Reply

Marsh Posté le 04-03-2005 à 02:30:44    

Est-ce que tu pourrais expliquer/commenter ce que fait exactement le code ( quelle est la logique pour rendre une telle image ) stp ?

Reply

Marsh Posté le 04-03-2005 à 02:39:04    

0x90 a écrit :

Est-ce que tu pourrais expliquer/commenter ce que fait exactement le code ( quelle est la logique pour rendre une telle image ) stp ?


Ouaip, je le ferais demain, promis. ;)

Reply

Marsh Posté le 04-03-2005 à 03:10:58    

Kikoo drapalesque :o

Reply

Marsh Posté le 04-03-2005 à 20:05:03    

Voila, j'ai ajouté quelques commentaires dans le code. Je vais essayer d'en rajouter un peu plus ce soir.
 
 
Si vous avez des questions, n'hésitez pas. :hello:

Reply

Marsh Posté le 04-03-2005 à 20:29:12    

Nan .. pas de questions ..  
Par contre, je dis [:sarah connor] !
J'espère que tu vas continuer à nous abbreuver de ce genre de petits programmes très sympathiques ! :D

Reply

Marsh Posté le 04-03-2005 à 20:29:12   

Reply

Marsh Posté le 04-03-2005 à 20:32:45    

Mr Mala a écrit :

Nan .. pas de questions ..  
Par contre, je dis [:sarah connor] !

Merci. Mais c'était facile, vu que j'avais tout sous la main. Par contre, j'ai un autre projet en tête, mais bien, bien plus lourd et plus sérieux que celui-ci. :D
(mais qui n'a rien à voir avec ce genre de programme)

Citation :

J'espère que tu vas continuer à nous abbreuver de ce genre de petits programmes très sympathiques ! :D

Si je trouve des idées, pourquoi pas.  :sol:  
 
A part ça, je suis un peu déçu du faible nombre de posts dans cette rubrique Python.  
 :(  
Y'aurait pourtant de quoi faire nettement plus.


Message édité par Rasthor le 04-03-2005 à 20:33:13
Reply

Marsh Posté le 04-03-2005 à 20:44:06    

Ben faut peut-être lancer la machine ?
D'autant plus qu'en voyant ça, tout doucement envie de m'y mettre moi ! [:huit]

Reply

Marsh Posté le 04-03-2005 à 20:56:38    

Mr Mala a écrit :

Ben faut peut-être lancer la machine ?

Ouaip. :D

Citation :

D'autant plus qu'en voyant ça, tout doucement envie de m'y mettre moi ! [:huit]

C'est clair, c'est graphique, ludique, ça donne envie.
 
Tu programmes dans quel(s) langage(s) actuellement ?

Reply

Marsh Posté le 04-03-2005 à 22:28:26    

Rasthor a écrit :

Tu programmes dans quel(s) langage(s) actuellement ?


 
Ben .. mmmmmmmmh .. je sais pas comment je dois te dire ça .. mmmmmmh ... ActionScript :whistle:  
( entre autre .. mais principalement .. cf ma signature - Chemistry - pour avoir une idée plus précise :p )


Message édité par Mr Mala le 04-03-2005 à 22:28:50
Reply

Marsh Posté le 04-03-2005 à 23:21:42    

Mr Mala a écrit :

Ben .. mmmmmmmmh .. je sais pas comment je dois te dire ça .. mmmmmmh ... ActionScript :whistle:  
( entre autre .. mais principalement .. cf ma signature - Chemistry - pour avoir une idée plus précise :p )

C'est toi qui l'a fait ? Sympa.  :)  
 
Mais Python, c'est mieux. :D

Reply

Marsh Posté le 09-03-2005 à 20:39:34    

C'est vraiment cool ça. Quand est ce qu'on a la version avec textures et avec forme plus arbitraires comme dans Doom 1 ? :D

Reply

Marsh Posté le 10-03-2005 à 00:33:45    

Kristoph a écrit :

C'est vraiment cool ça. Quand est ce qu'on a la version avec textures et avec forme plus arbitraires comme dans Doom 1 ? :D


Je peux vite essayer de coder ça. ;)

Reply

Marsh Posté le 13-04-2006 à 15:41:17    

Rasthor a écrit :

Je peux vite essayer de coder ça. ;)


 
ca serait super cool
tres utilise cette source..

Reply

Marsh Posté le 19-04-2006 à 01:18:18    

juste une petite suggestion... ca a l'air de rien, parce que la c'est du precalcul, mais quand même, pour la route :)
 

Code :
  1. # Table des cosinus precalcules. On gagne un peu en vitesse.
  2. ctable = []
  3. for a in range(0,(359+180)):
  4.     ctable.append(int((cos(radians(a))*1024)/10))
  5. # Fonction pour trouver le cosinu de l'angle v.
  6. def getangle(v):
  7.     return ctable[v+1]


 
le truc, c'est que cos(a) est compris entre -1 et 1 modulo Pi donc, il te suffi de calculer la moitiée... ce qui deviendrait
 

Code :
  1. # Table des cosinus precalcules. On gagne un peu en vitesse.
  2. ctable = []
  3. for a in range(0,180):
  4.     ctable.append(int((cos(radians(a))*512)/5))
  5. # Fonction pour trouver le cosinu de l'angle v.
  6. def getangle(v):
  7.     return ctable[abs(v)+1] # ou simplement un test...


 
on peut même aller jusqu'a ne calculer qu'un quart de cercle...

Reply

Marsh Posté le 22-05-2006 à 21:56:36    

Rendu différent chez moi :??: :  
http://let0.free.fr/temp_ray.png

Reply

Marsh Posté le 22-05-2006 à 22:03:54    

Il est où le problème ?  :??:  
 
 
Bon, sinon, il y a quelque temps, j'ai repris le script python et l'article Rebol que j'avais aussi, j'ai adapté ça en java, ça marche. et y'a 2 semaines, j'ai essayé le "Write Once, Run Every Where" de java, en faisiant une version J2ME
 
et ça marche  [:totoz]  
 
http://img422.imageshack.us/img422/2388/j2me23if.png
 
 [:mullet] Ca sert bien evidement a rien
 
 
(désoler pour le screen un peu grand...)


Message édité par zapan666 le 22-05-2006 à 22:04:32

---------------
my flick r - Just Tab it !
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed