blender-edutech-tutoriels/labyrinthe/4-arduino_pyserial/4-labyrinthe.py

213 lines
8.1 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import bge # Bibliothèque Blender Game Engine (BGE)
import serial # Liaison série
import time
###############################################################################
# 4-labyrinthe.py
# @title: Module (unique) de la scène 3D du labyrinthe à bille pilotable avec une centrale inertielle (capteur IMU)
# @project: Blender-EduTech - Tutoriel 3 : Labyrinthe à bille - Interfacer avec une carte Arduino par la liaision série
# @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()
# 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
###############################################################################
serial_baud=115200
# serial_comm = serial.Serial('COM4',serial_baud, timeout=0.016) # Windows
serial_comm = serial.Serial('/dev/ttyACM1',serial_baud, timeout=0.016) # GNU/Linux
print (serial_comm)
###############################################################################
# Gestion de la centrale inertielle (capteur IMU (inertial measurement unit))
###############################################################################
# Extraction d'un texte compris entre deux bornes textuelles
def txt_extrac(txt, borne_avant, 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)
# Lecture du capteur IMU
def capteur(cont):
obj = cont.owner # obj est l'objet associé au contrôleur donc 'Plateau'
resolution = 0.2
# Touche ESC -> Quitter
keyboard = bge.logic.keyboard
if keyboard.inputs[bge.events.ESCKEY].status[0] == ACTIVATE:
serial_comm.close()
bge.logic.endGame()
# Lecture de la liaison série : programme Arduino : 3-labyrinthe-imu.ino
obj['Rx']=obj.worldOrientation.to_euler().x
obj['Ry']=obj.worldOrientation.to_euler().y
obj['Rz']=obj.worldOrientation.to_euler().z
serial_msg = str(serial_comm.readline())
if serial_msg.find(",")>0:
txt = serial_msg.split(',',2)
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,y, 0)
###############################################################################
# Gameplay
###############################################################################
# Initialisation de la scène
def init(cont):
obj = cont.owner # obj est l'objet associé au contrôleur donc 'Bille'
# 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
obj['vitesse z']=obj.worldLinearVelocity.z # la propriété z est mis à jour avec la position globale en z de la bille
# Chute ?
if obj['z'] < -10 and scene.objects['Panneau victoire'].visible == False:
print ("Chuuuu.....te")
# 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)
# 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
###############################################################################
# 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)