ropy/rp_map1.py

453 lines
21 KiB
Python

import bge # Bibliothèque Blender Game Engine (UPBGE)
import bpy # Blender
import random
from rp_lib import * # Bibliothèque Ropy
import os
import runpy # Exécution de script Python légère (sans import)
###############################################################################
# rp_map1.py
# @title: Carte 1 pour Ropy
# @project: Ropy (Blender-EduTech)
# @lang: fr
# @authors: Philippe Roy <philippe.roy@ac-grenoble.fr>
# @copyright: Copyright (C) 2020-2024 Philippe Roy
# @license: GNU GPL
###############################################################################
scene = bge.logic.getCurrentScene()
color_text = (0, 0, 0, 1) # Noir
color_text_red = (0.799, 0.031, 0.038, 1)
color_text_orange = (0.799, 0.176, 0.054, 1)
color_text_yellow = (0.799, 0.617, 0.021, 1)
color_text_white = (1, 1, 1, 1)
###############################################################################
# Missions
###############################################################################
missions_card_description ={}
missions_conf ={}
missions_task ={}
###############################################################################
# Initialisation du niveau :
# Niveau 1 : Les premiers pas de Ropy
# Niveau 2 : Ma première fonction
# Niveau 3 : Sécuriser Ropy
# Niveau 4 : Partir au bout du monde
# Niveau 5 : Faire face à l'inconnu
# Niveau 6 : Se rendre utile
###############################################################################
missions_card=["mission_1-card", "mission_2-card", "mission_3-card", "mission_4-card", "mission_5-card", "mission_6-card"]
# missions_card=["mission_1-card", "mission_2-card", "mission_3-card", "mission_4-card", "mission_5-card", "mission_6-card", "mission_7-card", "mission_8-card"]
# Mission 1
mission_1_title="Mission 1\n Premiers pas"
mission_1_text="""\n\n Il faut aider Ropy à atteindre la case \n de l'objectif (à l'est de la station). \n
-> Voir onglet Rover, page \"Avancer\" \n -> Voir onglet Rover,page \"Tourner\" \n\n
Afin de visualiser le trajet, il faudra \n marquer les cases. \n -> Voir onglet Rover, page \"Baliser\""""
mission_1_task = "- Atteindre l'objectif \n\n- Poser 6 balises \n minimum"
mission_1_init=[-11.0,3.0, "e"] # Rover init position (x,y), orientation ("n|s|e|w")
mission_1_aim=[-7.0,2.0] # Aim position (x,y)
missions_card_description.update({"mission_1-card" : [mission_1_title, mission_1_text]})
missions_conf.update({"1" : [mission_1_init, mission_1_aim]})
missions_task.update({"1" : [mission_1_task]})
# Mission 2
mission_2_title="Mission 2\n Ma première fonction"
mission_2_text="""\n\n La case à atteindre est toujours la \n même (à l'est de la station).\n\n Pour faciliter le codage, vous devez \n créer et utiliser la fonction \n \"mrp_avancer()\" regroupant avancer \n et marquer. \n -> Voir onglet Python, page \"Fonction\"."""
mission_2_task = "- Atteindre l'objectif \n\n- Poser 6 balises \n minimum\n\n- 40 lignes de code \n maximum\n\n- Définition de la \n fonction \n \"mrp_avancer()\""
mission_2_init=[-11.0,3.0, "e"]
mission_2_aim=[-7.0,2.0]
missions_card_description.update({"mission_2-card" : [mission_2_title, mission_2_text]})
missions_conf.update({"2" : [mission_2_init, mission_2_aim]})
missions_task.update({"2" : [mission_2_task]})
# Mission 3
mission_3_title="Mission 3\n Apprendre le danger"
mission_3_text="""\n\n Tout d'abords, il faut provoquer une \n collision avec un obstacle (pente \n ou station) et observer ce qui se \n passe.\n
Il faut donc sécuriser Ropy en \n utilisant une structure alternative. \n -> Voir onglet Python, page \"Si alors\" \n -> Voir onglet Rover, page \"Détecter\""""
mission_3_task = "- Utilisation de \n \"rp_detect()\" dans \n une structure \n alternative \"if\"."
mission_3_init=[-11.0,3.0, "e"] # Rover init position (x,y), orientation ("n|s|e|w")
mission_3_aim=[100.0,100.0] # Aim caché -> pas de de position objectif
missions_card_description.update({"mission_3-card" : [mission_3_title, mission_3_text]})
missions_conf.update({"3" : [mission_3_init, mission_3_aim]})
missions_task.update({"3" : [mission_3_task]})
# Mission 4
mission_4_title="Mission 4\n Partir au bout du monde"
mission_4_text="""\n\n Ropy est maintenant prêt pour \n l'aventure soit atteindre la case \n du nouvel objectif (loin, très loin ...). \n \n Pour un tel voyage, l'utilisation d'une\n boucle du type \"for\" s'impose.
-> Voir onglet Python, page \"Boucle\" \n\n Dans un deuxième temps, il faut créer \n la fonction \"mrp_avancer_nbpas(pas)\"\n afin d'avancer d'un nombre de pas."""
mission_4_task = "- Atteindre l'objectif \n\n- 40 lignes de code \n maximum\n\n- Utilisation de \n boucle \"for\" \n\n- En bonus : création \n de la fonction \"mrp_\n avancer_nbpas(pas)\""
mission_4_init=[-7.0,4.0, "e"]
mission_4_aim=[12.0,9.0]
missions_card_description.update({"mission_4-card" : [mission_4_title, mission_4_text]})
missions_conf.update({"4" : [mission_4_init, mission_4_aim]})
missions_task.update({"4" : [mission_4_task]})
# Mission 5
mission_5_title="Mission 5\n Faire face à l'inconnu"
mission_5_text="""\n\n La case à atteindre est toujours la \n même, mais le lieu de départ change \n à chaque fois. \n\n Pour pallier à l\'aléatoire, vous \n utiliserez les pentes comme obstacle. \n
Il faut alors créer une fonction \n avec une boucle \"while\" (tant que) \n qui permet d'avancer jusqu'à un \n obstacle : \"mrp_avancer_mur()\"."""
mission_5_task = "- Atteindre l'objectif \n\n- 40 lignes de code \n maximum\n\n- Utilisation de \n boucle \"while\" \n\n- Création \n de la fonction \"mrp_\n avancer_mur()\""
mission_5_init=[0.0,0.0, "e"] # Position aléatoire tourné vers l'est -> défini dans le reset
mission_5_aim=[12.0,9.0]
missions_card_description.update({"mission_5-card" : [mission_5_title, mission_5_text]})
missions_conf.update({"5" : [mission_5_init, mission_5_aim]})
missions_task.update({"5" : [mission_5_task]})
# Mission 6
mission_6_title="Mission 6\n Se rendre utile"
mission_6_text="""\n\n Une zone du terrain doit être explorée.\n Il faut procéder à des carottages afin \n d'analyser la roche. \n\n Les lieux de forage (affichés ici en \n permanence) sont définis de manière \n aléatoire (encore !), pour les \n réaliser, Ropy doit donc passer sur \n toutes les cases de la zone."""
mission_6_task = "- Passer sur les 10 \n lieux de forage"
mission_6_init=[0.0,0.0, "e"] # Position aléatoire tourné vers l'est -> défini dans le reset
mission_6_aim=[100.0,100.0] # Aim caché -> ramassage de pierre
missions_card_description.update({"mission_6-card" : [mission_6_title, mission_6_text]})
missions_conf.update({"6" : [mission_6_init, mission_6_aim]})
missions_task.update({"6" : [mission_6_task]})
# Mission 7
# mission_7_title="Mission 7\n FIXME"
# mission_7_text="\n \n FIXME"
# mission_7_task = "FIXME"
# missions_card_description.update({"mission_7-card" : [mission_7_title, mission_7_text]})
# missions_conf.update({"7" : [mission_7_init, mission_7_aim]})
# missions_task.update({"7" : [mission_7_task]})
# Mission 8
# mission_8_title="Mission 8\n FIXME"
# mission_8_text="\n \n FIXME"
# mission_8_task = "FIXME"
# missions_card_description.update({"mission_8-card" : [mission_8_title, mission_8_text]})
# missions_conf.update({"8" : [mission_8_init, mission_8_aim]})
# missions_task.update({"8" : [mission_8_task]})
# Description des cartes missions
def get_missions_card():
return missions_card
def get_missions_description():
return missions_card_description
###############################################################################
# Map
###############################################################################
##
# Initialization
##
def map_init():
# Terrain
file_path = 'asset/map/map1.blend'
inner_path = 'Object'
object_name = 'Landscape'
bpy.ops.wm.append(
filepath=os.path.join(file_path, inner_path, object_name),
directory=os.path.join(file_path, inner_path),
filename=object_name)
scene.objects['Terrain']['size'] = [-15,-11,15,10] # Map size
scene.objects['Terrain-plane'].setVisible(False,True) # Plan de conception (Blender)
scene.objects['Terrain']['map_tile_station']= [[-9,2],[-9,3]]
# Montagne
scene.objects['Terrain']['map_tile_montain'] = [[-9, -1], [1, -9], [-14, -8], [-12, -10], [-11, -10], [-10, -10], [-9, -10], [-11, 1], [-14, 3], [-14, 4], [-14, -7], [-12, 1], [2, -7], [-12, 10],
[-11,10], [-10, 10], [-9, 10], [-8, 10], [-4, 7], [-2, 6], [4, 10], [5, 10], [1, 8], [2, -6], [6, 10], [7, 10], [8, 10], [9, 10], [10, 10], [11, 10],
[14,7], [-14, -1], [13, 8], [-14, 0], [-14, -2], [8, -1], [-15, -7], [-15, -8], [-15, -9], [-15, -6], [-8, -10], [-7, -10], [10, -1], [15, 5], [11, -1],
[14, 9], [15, 10], [12, -1], [4, -9], [5, -9], [6, -9], [3, -10], [7, -10], [-14, -11], [-6, -11], [3, -11], [7, -11], [0, -11], [13, 7],
[13, 6], [13, 5], [14, 10], [13, 9], [14, 8], [14, 6], [12, 10], [15, 9], [15, 3], [9, -1], [7, -1], [15, 8], [15, 7], [6, -1], [2, -5],
[2, -4], [2, -3], [2, -2], [5, -1], [4, -1], [3, -1], [3, 10], [-1, 6], [0, -10], [-13, -9], [1, -8], [-12, -3], [-9, -2], [-12, -5], [-13, -6], [-13, 5],
[-13, 6], [-14, 8], [-13, 2], [-10, -3], [-13, 9], [-7, 9], [-14, 7], [-6, 8], [-5, 7], [-3, 6], [0, 6], [1, 7], [2, 9], [-10, 1], [-9, 0], [-15, 2],
[-14,1], [-14, -10], [-14, -5], [-13, -4], [-13, -3], [2, -1], [13, 4], [14, 3], [15, 6], [13, -1], [14, -2], [-6, -10], [15, -3], [14, 5], [3, -9],
[7,-9], [-13, 10], [-13, 10], [15, -4], [15, -4], [14, -3], [14, -3], [-14, 9], [-14, 9], [-14, 6], [-14, 6], [-14, 5], [-14, 5], [-14, 2], [-14, 2],
[-13,1], [-13, 1], [-10, 0], [-10, 0], [-10, -2], [-10, -2], [-11, -3], [-11, -3], [-12, -4], [-12, -4], [-13, -5], [-13, -5], [-14, -6], [-14, -6],
[-14,-9], [-14, -9], [-13, -10], [-13, -10], [-7, 10], [-7, 10], [-6, 9], [-6, 9], [-5, 8], [-5, 8], [-3, 7], [-3, 7], [0, 7], [0, 7], [1, 9], [1, 9],
[2,10], [2, 10], [13, 10], [13, 10], [13, -2], [13, -2], [-15, -10], [-15, -10], [1, -10], [1, -10], [-15, 1], [-15, 1], [-15, -5], [-15, -5], [-14, -4],
[-14, -4], [-14, -3], [-14, -3], [-15, 8], [-15, 8], [14, 4], [14, 4], [-15, 7], [-15, 7], [2, -8], [2, -8]]
# Visualisation des cases montagne
# for i in range (len (scene.objects['Terrain']['map_tile_montain'])):
# beacon_montain= scene.addObject("Beacon", scene.objects['Terrain'])
# beacon_montain.worldPosition=[scene.objects['Terrain']['map_tile_montain'][i][0],scene.objects['Terrain']['map_tile_montain'][i][1],0.2]
# beacon_montain.setVisible(True,True)
# Détection des emplacements des colisions de montagne
# scene.objects['Terrain']['map_tile_montain']= []
# for obj_i in scene.objects:
# if "terrain_sideCliff" in obj_i.name or "terrain_sideCorner" in obj_i.name or "terrain_sideCornerInner" in obj_i.name:
# scene.objects['Terrain']['map_tile_montain'].append([round(obj_i.worldPosition.x), round(obj_i.worldPosition.y)])
# Couleurs
rp_couleur_init()
# Cacher les zones
scene.objects['Aim'].setVisible(False,True)
scene.objects['Initzone-mission-5'].setVisible(False,True)
scene.objects['Aimzone-mission-6'].setVisible(False,True)
##
# Reset de la map
##
def map_reset():
scene.objects['Points']['step']=0
scene.objects['Points']['nbligne']=0
scene.objects['Points']['battery']=100
scene.objects['Points-Battery'].color = color_text_white
scene.objects['Points-Battery-text'].color = color_text
mission_init = missions_conf[str(scene.objects['Points']['mission'])][0]
mission_aim = missions_conf[str(scene.objects['Points']['mission'])][1]
# Reterasser le terrain (mission 6)
for obj_i in scene.objects:
if "tile_dirtHigh" in obj_i.name:
if obj_i.visible == False:
obj_i.setVisible(True, True)
for i in range (10):
scene.objects["Drill_tile-"+str(i)].setVisible(False, True)
# Cacher les balises
scene.objects['Terrain']['map_tile_beacon']= []
for i in range (200):
beacon = scene.objects["Beacon-"+str(i)]
beacon.worldPosition=[29,1+i,0.2]
beacon.setVisible(False,True)
beacon['activated']=False
# Initialisation du rover
obj = scene.objects['Rover']
obj.worldPosition.x = mission_init[0]
obj.worldPosition.y = mission_init[1]
obj.worldPosition.z = 0.2
applyRotationTo(obj, 0, 0, 0, True)
if mission_init[2] == "n":
obj.applyRotation((0, 0, math.pi), True)
if mission_init[2] == "e":
obj.applyRotation((0, 0, math.pi/2), True)
if mission_init[2] == "w":
obj.applyRotation((0, 0, -math.pi/2), True)
# Initialisation du rover pour les missions 5 et 6 : position aléatoire entre -3 ; 5 et 1 ; -10
if scene.objects['Points']['mission']==5 or scene.objects['Points']['mission']==6:
position_ok=False
while position_ok==False: # Exclusion de certaines cases
obj.worldPosition.x = random.randint(-3,1)
obj.worldPosition.y = random.randint(-10,5)
position_ok=True
if obj.worldPosition.x== 0 and obj.worldPosition.y== -10:
position_ok=False
if obj.worldPosition.x== 1 and obj.worldPosition.y== -8:
position_ok=False
if obj.worldPosition.x== 1 and obj.worldPosition.y== -9:
position_ok=False
if obj.worldPosition.x== 1 and obj.worldPosition.y== -10:
position_ok=False
# Initialisation de l'objectif (mission de 1 à 5)
if scene.objects['Points']['mission']!=6:
scene.objects['Terrain']['map_aim']= [[mission_aim[0],mission_aim[1]]]
obj_aim = scene.objects['Aim']
obj_aim.worldPosition.x = mission_aim[0]
obj_aim.worldPosition.y = mission_aim[1]
obj_aim.worldPosition.z = 0.5
# Initialisation des 10 lieux de forage pour la mission 6 : position aléatoire entre 3 ; 0 et 12 ; 9
if scene.objects['Points']['mission']==6:
obj_aim = scene.objects['Aim'] # Cacher l'objectif unique
obj_aim.worldPosition.x = mission_aim[0]
obj_aim.worldPosition.y = mission_aim[1]
obj_aim.worldPosition.z = 0.5
scene.objects['Terrain']['map_aim']=[] # Liste des cibles
scene.objects['Terrain']['map_aim_hit']=[] # Liste des cibles atteintes
for i in range (10):
position_ok=False
while position_ok==False: # Exclusion de certaines cases
x1= random.randint(3,12)
y1= random.randint(0,9)
if [x1,y1] in scene.objects['Terrain']['map_aim']:
position_ok=False
else:
scene.objects['Drill_aim-'+str(i)].worldPosition.x = x1
scene.objects['Drill_aim-'+str(i)].worldPosition.y = y1
scene.objects['Drill_aim-'+str(i)].worldPosition.z = 0.5
scene.objects['Terrain']['map_aim'].append([x1,y1])
scene.objects['Drill_aim-'+str(i)].setVisible(True,True)
position_ok=True
else:
for i in range (10):
if scene.objects['Drill_aim-'+str(i)].visible:
scene.objects['Drill_aim-'+str(i)].setVisible(False,True)
###############################################################################
# Objectif
###############################################################################
##
# Afficher l'objectif
##
def aim_show():
# Zone de départ
if scene.objects['Points']['mission']==5 or scene.objects['Points']['mission']==6:
scene.objects['Initzone-mission-5'].setVisible(True,True)
else:
scene.objects['Initzone-mission-5'].setVisible(False,True)
# Cible
if scene.objects['Points']['mission']==3 or scene.objects['Points']['mission']==6:
scene.objects['Aim'].setVisible(False,True)
else:
scene.objects['Aim'].setVisible(True,True)
# Zone cible
if scene.objects['Points']['mission']==6:
scene.objects['Aimzone-mission-6'].setVisible(True,True)
else:
scene.objects['Aimzone-mission-6'].setVisible(False,True)
##
# Cacher l'objectif
##
def aim_hide():
scene.objects['Aim'].setVisible(False,True)
scene.objects['Aimzone-mission-6'].setVisible(False,True)
scene.objects['Initzone-mission-5'].setVisible(False,True)
##
# Affichage des tâches
##
def task():
scene.objects['Task_text']['Text']=missions_task[str(scene.objects['Points']['mission'])][0]
##
# Validation de l'objectif atteint
##
def objectif_control(x,y):
# Mission 1
if scene.objects['Points']['mission']==1:
if [x,y] in scene.objects['Terrain']['map_aim']: # Aim
if len(scene.objects['Terrain']['map_tile_beacon'])>=6: # 6 balises posées mini
return True
# Mission 2 (fonction)
if scene.objects['Points']['mission']==2:
if [x,y] in scene.objects['Terrain']['map_aim']: # Aim
# print ("Objectif : ok")
if len(scene.objects['Terrain']['map_tile_beacon'])>=6: # 6 balises posées mini
# print ("6 balises posées : ok")
if scene.objects['Points']['nbligne'] <=40: # 40 lignes de code maxi
# print ("40 lignes de code maxi : ok")
# print (scene.objects['Commands']['functions'])
if scene.objects['Commands']['functions'].count("mrp_avancer")>=3: # Au moins 3 appels de "mrp_avancer()"
# print ("Fonction mrp_avancer : ok")
return True
# Mission 3 (structure alternative)
# Le controle de l'objectif est dans la fonction rp_detect().
# Mission 4 (boucle en for)
if scene.objects['Points']['mission']==4:
if [x,y] in scene.objects['Terrain']['map_aim']: # Aim
if scene.objects['Points']['nbligne'] <=40: # 40 lignes de code maxi
txt=["for" ] # Présence de "for i in range" 2x
if rp_cmd_txtcount(txt, 2):
txt=["in range" ]
if rp_cmd_txtcount(txt, 2):
return True
else:
if scene.objects['Commands']['functions'].count("mrp_avancer_nbpas")>=2: # Au moins 2 appels de "mrp_avancer_nbpas()"
txt=["for" ] # Présence de "for i in range" 1x
if rp_cmd_txtcount(txt, 1):
txt=["in range" ]
if rp_cmd_txtcount(txt, 1):
return True
# Mission 5 (boucle en while)
if scene.objects['Points']['mission']==5:
if [x,y] in scene.objects['Terrain']['map_aim']: # Aim
if scene.objects['Points']['nbligne'] <=40: # 40 lignes de code maxi
txt=["while" ] # Présence de "while" 2x
if rp_cmd_txtcount(txt, 2):
return True
else:
if scene.objects['Commands']['functions'].count("mrp_avancer_mur")>=2: # Au moins 2 appels de "mrp_avancer_mur()"
txt=["while" ] # Présence de "while" 1x
if rp_cmd_txtcount(txt, 1):
return True
# Mission 6 (passer sur tout le terrain)
if scene.objects['Points']['mission']==6:
if [x,y] in scene.objects['Terrain']['map_aim']: # Cibles
if [x,y] not in scene.objects['Terrain']['map_aim_hit']: # Cibles atteintes
rover_drill(x,y)
scene.objects['Terrain']['map_aim_hit'].append([x,y])
if len (scene.objects['Terrain']['map_aim_hit'])==10 : # Toutes les cibles atteintes
return True
# Objectif pas atteint
return False
###############################################################################
# Fonction bas niveau
###############################################################################
##
# Recherche de texte dans le script
##
def rp_cmd_txtcount (text_list, n):
file = open(scene.objects['Commands']['script'], 'r')
file_txt = file.read()
for text in text_list:
if file_txt.count (text) >= n:
file.close()
return True
file.close()
return False
##
# Atteindre une orientation
##
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)