2023-04-28 21:52:25 +02:00
import bge # Bibliothèque Blender Game Engine (BGE)
2023-04-29 02:04:11 +02:00
import bpy # Blender
import serial # Liaison série
import time
2023-04-28 21:52:25 +02:00
###############################################################################
2023-04-29 02:04:11 +02:00
# 3-labyrinthe-imu.py
# @title: Module (unique) de la scène 3D du labyrinthe à bille pilotable avec une centrale inertielle (capteur IMU)
2023-04-28 21:52:25 +02:00
# @project: Blender-EduTech - Tutoriel : Tutoriel 3 Labyrinthe à bille - Interfacer avec une carte Arduino
# @lang: fr
# @authors: Philippe Roy <philippe.roy@ac-grenoble.fr>
# @copyright: Copyright (C) 2023 Philippe Roy
# @license: GNU GPL
#
# Commandes déclenchées par UPBGE pour le scène du labyrinthe
#
###############################################################################
# Récupérer la scène 3D
scene = bge . logic . getCurrentScene ( )
2023-04-29 02:04:11 +02:00
eevee = bpy . context . scene . eevee
fps_time = 0.0
2023-04-28 21:52:25 +02:00
# print("Objets de la scene : ", scene.objects) # Lister les objets de la scène
# Constantes
JUST_ACTIVATED = bge . logic . KX_INPUT_JUST_ACTIVATED
JUST_RELEASED = bge . logic . KX_INPUT_JUST_RELEASED
ACTIVATE = bge . logic . KX_INPUT_ACTIVE
###############################################################################
# Communication avec la carte Arduino
###############################################################################
2023-04-29 02:04:11 +02:00
serial_baud = 115200 # 7 fps
# serial_baud=38400 # 6 fps
# serial_baud=9600 # 2 fps
2023-04-29 03:19:38 +02:00
# serial_comm = serial.Serial('COM4',serial_baud, timeout=0.016) # Windows
serial_comm = serial . Serial ( ' /dev/ttyACM0 ' , serial_baud , timeout = 0.016 ) # GNU/Linux
2023-04-29 02:04:11 +02:00
print ( serial_comm )
2023-04-28 21:52:25 +02:00
###############################################################################
2023-04-29 02:04:11 +02:00
# Gestion de la centrale inertielle (capteur IMU (inertial measurement unit))
2023-04-28 21:52:25 +02:00
###############################################################################
2023-04-29 02:04:11 +02:00
# Extraction d'un texte compris entre deux bornes textuelles
def txt_extrac ( txt , borne_avant , borne_apres ) :
# print ("find sur (", txt ,") avec (",borne_avant,") -> ", txt.find(borne_avant))
# print ("find sur (", txt ,") avec (",borne_apres,") -> ", txt.find(borne_apres))
if txt . find ( borne_avant ) > 0 and txt . find ( borne_apres ) > 0 :
txt1 = txt . split ( borne_avant , 2 )
txt2 = txt1 [ 1 ] . split ( borne_apres , 2 )
return ( txt2 [ 0 ] )
else :
return ( " " )
# Atteindre une orientation (bas niveau)
def applyRotationTo ( obj , rx = None , ry = None , rz = None , Local = True ) :
rres = 0.001 # resolution rotation
# x
if rx is not None :
while ( abs ( rx - obj . worldOrientation . to_euler ( ) . x ) > rres ) :
if obj . worldOrientation . to_euler ( ) . x - rx > rres :
obj . applyRotation ( ( - rres , 0 , 0 ) , Local )
if rx - obj . worldOrientation . to_euler ( ) . x > rres :
obj . applyRotation ( ( rres , 0 , 0 ) , Local )
# print ("delta x ",rx-obj.worldOrientation.to_euler().x)
# y
if ry is not None :
while ( abs ( ry - obj . worldOrientation . to_euler ( ) . y ) > rres ) :
if obj . worldOrientation . to_euler ( ) . y - ry > rres :
obj . applyRotation ( ( 0 , - rres , 0 ) , Local )
if ry - obj . worldOrientation . to_euler ( ) . y > rres :
obj . applyRotation ( ( 0 , rres , 0 ) , Local )
# print ("delta y ",ry-obj.worldOrientation.to_euler().y)
# z
if rz is not None :
while ( abs ( rz - obj . worldOrientation . to_euler ( ) . z ) > rres ) :
if obj . worldOrientation . to_euler ( ) . z - rz > rres :
obj . applyRotation ( ( 0 , 0 , - rres ) , Local )
if rz - obj . worldOrientation . to_euler ( ) . z > rres :
obj . applyRotation ( ( 0 , 0 , rres ) , Local )
# print ("delta z ",rz-obj.worldOrientation.to_euler().z)
def capteur ( cont ) :
# global fps_time
2023-04-28 21:52:25 +02:00
obj = cont . owner # obj est l'objet associé au contrôleur donc 'Plateau'
2023-04-29 02:04:11 +02:00
resolution = 0.2
2023-04-28 21:52:25 +02:00
# Touche ESC -> Quitter
2023-04-29 02:04:11 +02:00
keyboard = bge . logic . keyboard
2023-04-28 21:52:25 +02:00
if keyboard . inputs [ bge . events . ESCKEY ] . status [ 0 ] == ACTIVATE :
2023-04-29 02:04:11 +02:00
serial_comm . close ( )
2023-04-28 21:52:25 +02:00
bge . logic . endGame ( )
2023-04-29 02:04:11 +02:00
# Désactivation du capteur pendant la chute
if scene . objects [ ' Bille ' ] [ ' chute ' ] :
return
# Lecture de la liaison série : programme Arduino : 3-labyrinthe-imu.ino
2023-04-29 03:19:38 +02:00
serial_msg = str ( serial_comm . readline ( ) )
# print (serial_msg)
if serial_msg . find ( " , " ) > 0 :
# if ("\r\n") in serial_msg:
# print (serial_msg)
txt = serial_msg . split ( ' , ' , 2 )
# print (txt)
x_txt = txt [ 0 ] [ 2 : ]
y_txt = txt [ 1 ] [ : - 5 ]
x = - ( float ( x_txt ) / 57.3 ) * resolution # 1/ 360 / (2 * pi)
y = - ( float ( y_txt ) / 57.3 ) * resolution # 1/ 360 / (2 * pi)
# Roll et Pitch
# applyRotationTo(scene.objects['Plateau'], x,0, 0)
applyRotationTo ( scene . objects [ ' Plateau ' ] , x , y , 0 )
# obj.applyRotation((0,0,-obj.worldOrientation.to_euler().z), False)
2023-04-28 21:52:25 +02:00
###############################################################################
# Gameplay
###############################################################################
# Initialisation de la scène
def init ( cont ) :
obj = cont . owner # obj est l'objet associé au contrôleur donc 'Bille'
2023-04-29 03:19:38 +02:00
# eevee_qualite(0)
2023-04-28 21:52:25 +02:00
# Mémorisation de la position de départ de la bille
obj [ ' init_x ' ] = obj . worldPosition . x
obj [ ' init_y ' ] = obj . worldPosition . y
obj [ ' init_z ' ] = obj . worldPosition . z
# Cacher le panneau de la victoire et suspendre la physique du panneau cliquable
scene . objects [ ' Panneau victoire ' ] . setVisible ( False , True )
scene . objects [ ' Panneau victoire - plan ' ] . suspendPhysics ( True )
scene . objects [ ' Bouton fermer ' ] . color = ( 0 , 0 , 0 , 1 ) # Noir
# Cycle (boucle de contrôle de la bille)
def cycle ( cont ) :
obj = cont . owner # obj est l'objet associé au contrôleur donc 'Bille'
obj [ ' z ' ] = obj . worldPosition . z # la propriété z est mis à jour avec la position globale en z de la bille
2023-04-29 02:04:11 +02:00
obj [ ' vitesse z ' ] = obj . worldLinearVelocity . z # la propriété z est mis à jour avec la position globale en z de la bille
2023-04-28 21:52:25 +02:00
2023-04-29 02:04:11 +02:00
# Chute ?
if obj [ ' z ' ] < - 10 and scene . objects [ ' Panneau victoire ' ] . visible == False :
obj [ ' chute ' ] = True
scene . objects [ ' Plateau ' ] [ ' chute ' ] = True
2023-04-28 21:52:25 +02:00
2023-04-29 02:04:11 +02:00
# Redémarrer la partie
def chute ( cont ) :
obj = cont . owner # obj est l'objet associé au contrôleur donc 'Bille'
obj_plateau = scene . objects [ ' Plateau ' ] # obj_plateau est l'objet 'Plateau'
print ( " Chuuuu.....te " )
2023-04-28 21:52:25 +02:00
2023-04-29 02:04:11 +02:00
# Replacement du plateau (tous les angles à 0 en plusieurs fois)
2023-04-29 03:19:38 +02:00
# while obj_plateau.worldOrientation.to_euler().x != 0 and obj_plateau.worldOrientation.to_euler().y !=0 and obj_plateau.worldOrientation.to_euler().z !=0 :
# obj_plateau.applyRotation((-obj_plateau.worldOrientation.to_euler().x, -obj_plateau.worldOrientation.to_euler().y, -obj_plateau.worldOrientation.to_euler().z), False)
2023-04-29 02:04:11 +02:00
# Mettre la bille à la position de départ avec une vitesse nulle
obj . worldLinearVelocity = ( 0 , 0 , 0 )
obj . worldAngularVelocity = ( 0 , 0 , 0 )
obj . worldPosition . x = obj [ ' init_x ' ]
obj . worldPosition . y = obj [ ' init_y ' ]
obj . worldPosition . z = obj [ ' init_z ' ] + 0.5 # On repose la bille
time . sleep ( 0.1 )
obj [ ' chute ' ] = False
2023-04-29 03:19:38 +02:00
scene . objects [ ' Plateau ' ] [ ' chute ' ] = False
2023-04-28 21:52:25 +02:00
# Victoire (colision de la bille avec l'arrivée)
def victoire ( cont ) :
scene . objects [ ' Panneau victoire ' ] . setVisible ( True , True ) # Afficher le panneau de la victoire
scene . objects [ ' Panneau victoire - plan ' ] . restorePhysics ( ) # Restaurer la physique du panneau cliquable
start = 1
end = 100
layer = 0
priority = 1
blendin = 1.0
mode = bge . logic . KX_ACTION_MODE_PLAY
layerWeight = 0.0
ipoFlags = 0
speed = 1
scene . objects [ ' Panneau victoire ' ] . playAction ( ' Panneau victoireAction ' , start , end , layer , priority , blendin , mode , layerWeight , ipoFlags , speed )
# Highlight du bouton Fermer
def victoire_fermer_hl ( cont ) :
obj = cont . owner
# Activation
if cont . sensors [ ' MO ' ] . status == JUST_ACTIVATED :
obj . color = ( 1 , 1 , 1 , 1 ) # Blanc
# Désactivation
if cont . sensors [ ' MO ' ] . status == JUST_RELEASED :
obj . color = ( 0 , 0 , 0 , 1 ) # Noir
# Fermer le panneau de la victoire (clic)
def victoire_fermer ( cont ) :
if cont . sensors [ ' Click ' ] . status == JUST_ACTIVATED and cont . sensors [ ' MO ' ] . positive :
scene . objects [ ' Panneau victoire ' ] . setVisible ( False , True ) # Cacher le panneau de la victoire
scene . objects [ ' Panneau victoire - plan ' ] . suspendPhysics ( True ) # Suspendre la physique du panneau cliquable
scene . objects [ ' Bille ' ] [ ' z ' ] = - 21 # On provoque le redémarrage si la bille est ressortie
###############################################################################
2023-04-29 02:04:11 +02:00
# Qualité du rendu EEVEE de 0 à 4
###############################################################################
def eevee_qualite ( qualite ) :
# Inconvenant
if qualite == 0 :
eevee . use_eevee_smaa = False # Subpixel Morphological Antialiasing
eevee . use_ssr = False # Screen space reflection
eevee . use_gtao = False # Ambient occlusion
eevee . taa_render_samples = 1
eevee . taa_samples = 1
eevee . use_volumetric_lights = False
eevee . use_volumetric_shadows = False
eevee . shadow_cascade_size = ' 64 '
eevee . shadow_cube_size = ' 64 '
# Basse
if qualite == 1 :
eevee . use_eevee_smaa = True
eevee . smaa_quality = ' LOW '
eevee . use_ssr = True # Screen space reflection
eevee . use_ssr_refraction = False # Screen space refractions
eevee . use_ssr_halfres = True
eevee . use_gtao = False
eevee . taa_render_samples = 32
eevee . taa_samples = 8
eevee . use_volumetric_lights = True
eevee . use_volumetric_shadows = False
eevee . shadow_cascade_size = ' 1024 '
eevee . shadow_cube_size = ' 512 '
# Moyenne
if qualite == 2 :
eevee . use_eevee_smaa = True
eevee . smaa_quality = ' MEDIUM '
eevee . use_ssr = True # Screen space reflection
eevee . use_ssr_refraction = True # Screen space refractions
eevee . use_ssr_halfres = True
eevee . use_gtao = False
eevee . taa_render_samples = 64
eevee . taa_samples = 16
eevee . use_volumetric_lights = True
eevee . use_volumetric_shadows = False
eevee . shadow_cascade_size = ' 1024 '
eevee . shadow_cube_size = ' 512 '
# Haute
if qualite == 3 :
eevee . use_eevee_smaa = True
eevee . smaa_quality = ' HIGH '
eevee . use_ssr = True
eevee . use_ssr_refraction = True
eevee . use_ssr_halfres = False
eevee . use_gtao = False
eevee . taa_render_samples = 64
eevee . taa_samples = 16
eevee . use_volumetric_lights = True
eevee . use_volumetric_shadows = False
eevee . shadow_cascade_size = ' 1024 '
eevee . shadow_cube_size = ' 512 '
# Épique
if qualite == 4 :
eevee . use_eevee_smaa = True
eevee . smaa_quality = ' ULTRA '
eevee . use_ssr = True
eevee . use_ssr_refraction = True
eevee . use_ssr_halfres = False
eevee . use_gtao = True
eevee . taa_render_samples = 64
eevee . taa_samples = 16
eevee . use_volumetric_lights = True
eevee . use_volumetric_shadows = True
eevee . shadow_cascade_size = ' 4096 '
eevee . shadow_cube_size = ' 4096 '
###############################################################################
2023-04-28 21:52:25 +02:00
# Gestion du Joystick USB
###############################################################################
def joystick ( cont ) :
obj = cont . owner
joystickIndex = 0 #int from 0 to 6
joy = bge . logic . joysticks [ joystickIndex ]
events = joy . activeButtons
axis = joy . axisValues [ 0 : 4 ]
resolution = 0.01
leftStick_x = axis [ 0 ] ; leftStick_y = axis [ 1 ]
rightStick_x = axis [ 2 ] ; rightStick_y = axis [ 3 ]
#if any button is pressed
# if events:
# print(events) #spit out integer index of pressed buttons
# if 0 in events:
# doSomething()
# Up
if leftStick_y < - 0.1 :
obj . applyRotation ( ( - resolution , 0 , 0 ) , False )
# Down
if leftStick_y > 0.1 :
obj . applyRotation ( ( resolution , 0 , 0 ) , False )
# Left
if leftStick_x < - 0.1 :
obj . applyRotation ( ( 0 , - resolution , 0 ) , False )
# Right
if leftStick_x > 0.1 :
obj . applyRotation ( ( 0 , resolution , 0 ) , False )