codetower/ct.py
2022-03-13 05:32:55 +01:00

513 lines
23 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 importlib
import bge # Bibliothèque Blender Game Engine (UPBGE)
import math
import time
import sys
import webbrowser
import threading # Multithreading
###############################################################################
# ct.py
# @title: Commandes pour le jeu codetower
# @project: Blender-EduTech
# @lang: fr
# @authors: Philippe Roy <philippe.roy@ac-grenoble.fr>
# @copyright: Copyright (C) 2022 Philippe Roy
# @license: GNU GPL
#
# Ce simulateur est un jeu du type tower defense où les tours sont à piloter par la programmation Python.
#
###############################################################################
# Import dynamique des fichiers Python élève et vagues
sys.setrecursionlimit(10**5) # Limite sur la récursivité (valeur par défaut : 1000) -> segfault de Blender
importlib.invalidate_caches()
ct_vg = importlib.import_module('ct_vg')
ct_cmd = importlib.import_module('ct_cmd')
# Récupérer la scène UPBGE
scene = bge.logic.getCurrentScene()
# scene_non_contruct = [] # Liste des tuiles non constructibles
# Couleurs
couleur_magenta = [0.800, 0.005, 0.315,1] # bouton non activable : magenta
couleur_orange = [0.799, 0.130, 0.063,1] # bouton activable : orange
couleur_blanc = [0.8, 0.8, 0.8, 1] # bouton focus : blanc
couleur_jaune = [0.8, 0.619, 0.021, 1] # bouton activé : jaune
couleur_cmd = [0.8, 0.8, 0.8, 1] # blanc
couleur_cmd_hl = [0.8, 0.619, 0.021, 1] # jaune
couleur_lien = [0.024, 0.006, 0.8, 1] # bleu
couleur_lien_hl = [0.8, 0.005, 0.315, 1] # majenta
# Constantes UPBGE
JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED
JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED
ACTIVATE = bge.logic.KX_INPUT_ACTIVE
# JUST_DEACTIVATED = bge.logic.KX_SENSOR_JUST_DEACTIVATED
###############################################################################
# Construction
###############################################################################
# Commande positionnement tour
def cmd_construct_tour(cont):
obj = cont.owner
obj_Hl= scene.objects[obj.name+"-Hl"]
if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive and scene.objects['Terrain']['manip_mode']==0:
if scene.objects['Terrain']['construct_mode']==True:
scene.objects['Terrain']['construct_mode']=False
obj.worldScale=[1, 1, 1]
obj.color = couleur_cmd
obj_Hl.worldScale=[1, 1, 1]
obj_Hl.color = couleur_cmd
scene.objects['Tour_construc_mode'].setVisible(False,False)
scene.objects['Tour_construc_mode'].color = couleur_cmd
scene.objects['Texte_info'].setVisible(False,False)
else:
scene.objects['Terrain']['construct_mode']=True
obj.worldScale=[1.25, 1.25, 1.25]
obj.color = couleur_cmd_hl
obj_Hl.worldScale=[1.25, 1.25, 1.25]
obj_Hl.color = couleur_cmd_hl
scene.objects['Tour_construc_mode'].setVisible(True,False)
scene.objects['Tour_construc_mode'].color = couleur_cmd
scene.objects['Texte_info'].setVisible(True,False)
scene.objects['Texte_info']['Text']="Position tour : "
# Affiche position
def construct_tour_pos(cont):
obj = cont.owner
if scene.objects['Terrain']['construct_mode']==True:
hitObject = cont.sensors['MO'].hitObject
hitPosition = cont.sensors['MO'].hitPosition
if hitObject is not None :
if [round(hitPosition.x),round(hitPosition.y)] in scene.objects['Terrain']['scene_non_contruct']:
pass
else:
scene.objects['Tour_construc_mode'].worldPosition.x=round(hitPosition.x)
scene.objects['Tour_construc_mode'].worldPosition.y=round(hitPosition.y)
scene.objects['Tour_construc_mode'].worldPosition.z=0.2
scene.objects['Texte_info']['Text']="Position tour : "+str(round(hitPosition.x))+","+str(round(hitPosition.y))
###############################################################################
# Terrain
###############################################################################
# Mise à jour de l'affichage des compteurs
def points_maj (cont):
# Affichage
scene.objects['Vies_texte']['Text']=str(scene.objects['Points']['vies'])+"/"+str(scene.objects['Points']['vies_max'])
scene.objects['Pieces_texte']['Text']=str(scene.objects['Points']['pieces'])
scene.objects['Level_texte']['Text']=str(scene.objects['Points']['level'])+"/"+str(scene.objects['Points']['level_max'])
scene.objects['Minions_texte']['Text']=str(scene.objects['Points']['minions'])
# Fin de la vague
if scene.objects['Terrain']['thread_vague']==False:
if scene.objects['Points']['minions']==0:
terrain_fin ()
# Initialisation lors du chargement du terrain
def terrain_init (cont):
# Mise à zéro des compteurs
scene.objects['Points']['vies']=10
scene.objects['Points']['vies_max']=10
scene.objects['Points']['pieces']=0
scene.objects['Points']['level']=0
scene.objects['Points']['level_max']=1
scene.objects['Points']['minions']=0
# Initialisation lors du chargement du terrain
def terrain_init (cont):
# Mise à zéro des compteurs
# scene.objects['Vies_texte']['Text']="10/10"
# scene.objects['Pieces_texte']['Text']="0"
# scene.objects['Level_texte']['Text']="1"
# scene.objects['Minions_texte']['Text']="0"
# Recherche les tuiles non constructibles
scene.objects['Terrain']['scene_non_contruct'] = []
for i in range (len(scene.objects)):
if "tile_straight" in scene.objects[i].name:
scene.objects['Terrain']['scene_non_contruct'].append([scene.objects[i].worldPosition.x, scene.objects[i].worldPosition.y])
if "tile_cornerRound" in scene.objects[i].name:
scene.objects['Terrain']['scene_non_contruct'].append([scene.objects[i].worldPosition.x, scene.objects[i].worldPosition.y])
# Mise en route et pause du cycle
def terrain_run (cont):
if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive and scene.objects['Terrain']['manip_mode']==0:
# Pause
if scene.objects['Terrain']['run'] == True:
scene.objects['Terrain']['run']=False
scene.objects['Pause'].setVisible(False,False)
scene.objects['Pause-Hl'].setVisible(False,False)
scene.objects['Run-Hl'].setVisible(True,False)
else:
# Run
scene.objects['Terrain']['run']=True
scene.objects['Run'].setVisible(False,False)
scene.objects['Run-Hl'].setVisible(False,False)
scene.objects['Pause-Hl'].setVisible(True,False)
if scene.objects['Terrain']['thread_run']==False: # Lancement du thread utilisateur
# Supprimer les tours
for i in range (len(scene.objects)):
if scene.objects[i].name=="Tour":
scene.objects[i].endObject()
scene.objects['Stop'].setVisible(True,False)
scene.objects['Terrain']['thread_run']=True
# importlib.reload(ct_cmd) # Lecture dynamique du script python (risque de Segfault de Blender)
# importlib.reload(ct_vg) # Lecture dynamique du script python (risque de Segfault de Blender)
ct_cmd.start() # Execution des commandes
ct_vg.start() # Lancement des vagues
# Arrêt et réinitialisation du cycle
def terrain_stop (cont):
if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive and scene.objects['Terrain']['manip_mode']==0:
# Arrêt des threads utilisateurs
scene.objects['Terrain']['run']=False
scene.objects['Terrain']['thread_run']=False
ct_cmd.stop() # Stop des commandes
ct_vg.stop() # Stop des vagues
# Supprimer les enemis
for i in range (len(scene.objects)):
if scene.objects[i].name=="Ufo":
scene.objects[i].endObject()
# Commandes
scene.objects['Pause'].setVisible(False,False)
scene.objects['Run'].setVisible(True,False)
scene.objects['Stop'].setVisible(False,False)
scene.objects['Stop-Hl'].setVisible(False,False)
# Fin naturelle du cycle
def terrain_fin ():
scene.objects['Terrain']['run']=False
scene.objects['Terrain']['thread_run']=False
# Commandes
scene.objects['Pause'].setVisible(False,False)
scene.objects['Run'].setVisible(True,False)
scene.objects['Stop'].setVisible(False,False)
scene.objects['Stop-Hl'].setVisible(False,False)
###############################################################################
# Commandes
###############################################################################
# Init
def cmd_init():
# scene.objects['Terrain']['run']=False
scene.objects['Run-Hl'].setVisible(False,False)
scene.objects['Pause'].setVisible(False,False)
scene.objects['Pause-Hl'].setVisible(False,False)
scene.objects['Stop'].setVisible(False,False)
scene.objects['Stop-Hl'].setVisible(False,False)
scene.objects['Construc-Hl'].setVisible(False,False)
scene.objects['Tour_construc_mode'].setVisible(False,False)
scene.objects['Texte_info'].setVisible(False,False)
# scene.objects['Raz-vue-Hl'].setVisible(False,False)
# scene.objects['Aide-cmd-Hl'].setVisible(False,False)
# Le highlight des commandes
def cmd_hl(cont):
obj = cont.owner
# Activation
if cont.sensors['MO'].status == JUST_ACTIVATED and scene.objects['Terrain']['manip_mode']==0:
if obj.name!="Run" and obj.name!="Pause" and obj.name!="Run-Hl" and obj.name!="Pause-Hl" and obj.name!="Stop" and obj.name!="Stop-Hl":
obj.setVisible(False,True)
scene.objects[obj.name+'-Hl'].setVisible(True,True)
# Run et pause
if obj.name=="Pause" or obj.name=="Run":
if scene.objects['Terrain']['run'] == True:
scene.objects['Pause'].setVisible(False,False)
scene.objects['Pause-Hl'].setVisible(True,False)
else:
scene.objects['Run'].setVisible(False,False)
scene.objects['Run-Hl'].setVisible(True,False)
# Stop
if obj.name=="Stop":
if scene.objects['Terrain']['thread_run']==True:
scene.objects['Stop'].setVisible(False,False)
scene.objects['Stop-Hl'].setVisible(True,False)
# Désactivation
if cont.sensors['MO'].status == JUST_RELEASED and scene.objects['Terrain']['manip_mode']==0:
if obj.name!="Run" and obj.name!="Pause" and obj.name!="Run-Hl" and obj.name!="Pause-Hl" and obj.name!="Stop" and obj.name!="Stop-Hl":
scene.objects[obj.name+'-Hl'].setVisible(False,True)
obj.setVisible(True,True)
# Run et pause
if obj.name=="Pause" or obj.name=="Run":
if scene.objects['Terrain']['run'] == True:
scene.objects['Pause-Hl'].setVisible(False,False)
scene.objects['Pause'].setVisible(True,False)
else:
scene.objects['Run-Hl'].setVisible(False,False)
scene.objects['Run'].setVisible(True,False)
# Stop
if obj.name=="Stop":
if scene.objects['Terrain']['thread_run']==True:
scene.objects['Stop-Hl'].setVisible(False,False)
scene.objects['Stop'].setVisible(True,False)
###############################################################################
# Gestion du clavier
###############################################################################
# Mode : Pan(1) avec Shift, Zoom (2) avec Ctrl
def mode(cont):
obj = cont.owner
keyboard = bge.logic.keyboard
# Shift -> mode 1 : Pan (clic milieu)
if JUST_ACTIVATED in keyboard.inputs[bge.events.LEFTSHIFTKEY].queue:
obj['manip_mode']=1
if JUST_ACTIVATED in keyboard.inputs[bge.events.RIGHTSHIFTKEY].queue:
obj['manip_mode']=1
# Ctrl -> mode 2 : Zoom (clic milieu)
if JUST_ACTIVATED in keyboard.inputs[bge.events.LEFTCTRLKEY].queue:
obj['manip_mode']=2
if JUST_ACTIVATED in keyboard.inputs[bge.events.RIGHTCTRLKEY].queue:
obj['manip_mode']=2
# Pas de modificateur -> mode 0 : Pas de Orbit (mode 0) ici
if JUST_RELEASED in keyboard.inputs[bge.events.LEFTSHIFTKEY].queue:
obj['manip_mode']=0
if JUST_RELEASED in keyboard.inputs[bge.events.RIGHTSHIFTKEY].queue:
obj['manip_mode']=0
if JUST_RELEASED in keyboard.inputs[bge.events.LEFTCTRLKEY].queue:
obj['manip_mode']=0
if JUST_RELEASED in keyboard.inputs[bge.events.RIGHTCTRLKEY].queue:
obj['manip_mode']=0
# Touche Home -> Reset de la vue
if JUST_ACTIVATED in keyboard.inputs[bge.events.HOMEKEY].queue:
scene.objects['Camera'].worldPosition.x = scene.objects['Camera']['init_lx']
scene.objects['Camera'].worldPosition.y = scene.objects['Camera']['init_ly']
scene.objects['Camera'].worldPosition.z = scene.objects['Camera']['init_lz']
applyRotationTo(scene.objects['Terrain'], 0, 0, 0)
# Touche F5 -> Run et Pause
if JUST_ACTIVATED in keyboard.inputs[bge.events.F5KEY].queue:
if scene.objects['Terrain']['run'] == True:
scene.objects['Terrain']['run']=False
scene.objects['Pause'].setVisible(False,False)
scene.objects['Run'].setVisible(True,False)
else:
scene.objects['Terrain']['run']=True
scene.objects['Run'].setVisible(False,False)
scene.objects['Pause'].setVisible(True,False)
if scene.objects['Terrain']['thread_run']==False: # Lancement du thread du jeu
# Supprimer les tours
for i in range (len(scene.objects)):
if scene.objects[i].name=="Tour":
scene.objects[i].endObject()
scene.objects['Stop'].setVisible(True,False)
scene.objects['Terrain']['thread_run']=True
# importlib.reload(ct_cmd) # Lecture dynamique du script python (risque de Segfault de Blender)
# importlib.reload(ct_vg) # Lecture dynamique du script python (risque de Segfault de Blender)
ct_cmd.start() # Execution des commandes
ct_vg.start() # Lancement des vagues
# Touche F6 -> Stop / Init
if JUST_ACTIVATED in keyboard.inputs[bge.events.F6KEY].queue:
if scene.objects['Terrain']['thread_run']==True:
# Arrêt des threads utilisateurs
scene.objects['Terrain']['run']=False
scene.objects['Terrain']['thread_run']=False
ct_cmd.stop() # Stop des commandes
ct_vg.stop() # Stop des vagues
# Supprimer les enemis
for i in range (len(scene.objects)):
if scene.objects[i].name=="Ufo":
scene.objects[i].endObject()
# Commandes
scene.objects['Pause'].setVisible(False,False)
scene.objects['Run'].setVisible(True,False)
scene.objects['Stop'].setVisible(False,False)
scene.objects['Stop-Hl'].setVisible(False,False)
###############################################################################
# Manipulation 3D du système
###############################################################################
# Mémorisation de la position et orientation initiales du modèle 3D et de la caméra
def manip_init(cont):
scene.objects['Camera']['init_lx']=scene.objects['Camera'].worldPosition.x
scene.objects['Camera']['init_ly']=scene.objects['Camera'].worldPosition.y
scene.objects['Camera']['init_lz']=scene.objects['Camera'].worldPosition.z
scene.objects['Terrain']['init_lx']=scene.objects['Terrain'].worldPosition.x
scene.objects['Terrain']['init_ly']=scene.objects['Terrain'].worldPosition.y
scene.objects['Terrain']['init_lz']=scene.objects['Terrain'].worldPosition.z
scene.objects['Terrain']['init_rx']=scene.objects['Terrain'].worldOrientation.to_euler().x
scene.objects['Terrain']['init_ry']=scene.objects['Terrain'].worldOrientation.to_euler().y
scene.objects['Terrain']['init_rz']=scene.objects['Terrain'].worldOrientation.to_euler().z
# 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)
# Reset de la manipulation de la vue
def manip_reset(cont):
if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive and scene.objects['Terrain']['manip_mode']==0:
scene.objects['Camera'].worldPosition.x = scene.objects['Camera']['init_lx']
scene.objects['Camera'].worldPosition.y = scene.objects['Camera']['init_ly']
scene.objects['Camera'].worldPosition.z = scene.objects['Camera']['init_lz']
applyRotationTo(scene.objects['Terrain'], 0, 0, 0)
# Position de départ pour la manipulation de la vue
def manip_start(cont):
obj = cont.owner
obj['click_x']=cont.sensors['ClickM'].position[0]
obj['click_y']=cont.sensors['ClickM'].position[1]
# Cacher le cercle de la manipulation Orbit
def manip_stop(cont):
# scene.objects['Orbit'].setVisible(False,False)
pass
# Manipulation du modèle ou de la caméra
def manip(cont):
obj = cont.owner
sensibilite_orbit=0.00005 # Base : 0.0005
sensibilite_pan=0.001 # Base : 0.005
sensibilite_zoom=0.005 # Base : 0.01
delta_x=cont.sensors['DownM'].position[0]-obj['click_x']
delta_y=cont.sensors['DownM'].position[1]-obj['click_y']
# Orbit (1280 * 720 px) Pas de Orbit ici
# if obj['manip_mode']==0:
# scene.objects['Orbit'].color=couleur_cmd
# scene.objects['Orbit'].setVisible(True,False)
# dist_orbit = math.sqrt(((1280/2)-obj['click_x'])**2+((720/2)-obj['click_y'])**2)
# if dist_orbit<235 : # Orbit sur x et z
# n=10
# pas_x=(delta_x*40*sensibilite_orbit)/n
# pas_y=(((1280/2)-cont.sensors['DownM'].position[0])+((720/2)-cont.sensors['DownM'].position[1]))*0.005
# pas_z=(delta_y*40*sensibilite_orbit)/n
# for i in range (n):
# bge.render.drawLine([scene.objects['Orbit'].worldPosition.x+pas_x*i, scene.objects['Orbit'].worldPosition.y+abs(pas_y*math.sin((3.14*i)/n)), scene.objects['Orbit'].worldPosition.z-pas_z*i],
# [scene.objects['Orbit'].worldPosition.x+pas_x*(i+1), scene.objects['Orbit'].worldPosition.y+abs(pas_y*math.sin((3.14*(i+1))/n)), scene.objects['Orbit'].worldPosition.z-pas_z*(i+1)],
# [0.8, 0.619, 0.021])
# scene.objects['Terrain'].applyRotation((delta_y*sensibilite_orbit, 0, delta_x*sensibilite_orbit), True)
# else: # Orbit sur y
# scene.objects['Orbit'].color=couleur_cmd_hl
# if abs(delta_x) >= abs(delta_y):
# scene.objects['Terrain'].applyRotation((0, delta_x*sensibilite_orbit, 0), True)
# else:
# scene.objects['Terrain'].applyRotation((0, delta_y*sensibilite_orbit, 0), True)
# Pan
if obj['manip_mode']==1: # Shift
scene.objects['Camera'].applyMovement((delta_x*-sensibilite_pan, delta_y*sensibilite_pan, 0), True)
# Zoom
if obj['manip_mode']==2: # Ctrl
scene.objects['Camera'].applyMovement((0, 0, (delta_x+delta_y)*sensibilite_zoom), True)
# Manipulation du modèle ou de la caméra
def manip_wheel(cont):
obj = cont.owner
sensibilite_wheel = 5 # Base : 20
if cont.sensors['WheelUp'].positive:
scene.objects['Camera'].applyMovement((0, 0, -sensibilite_wheel), True)
if cont.sensors['WheelDown'].positive:
scene.objects['Camera'].applyMovement((0, 0, sensibilite_wheel), True)
###############################################################################
# Aide
###############################################################################
# # Ouvrir la page d'aide
# def aide(cont):
# if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive :
# # scene.replace('Scene-Aide') # Bug Eevee -> même scene mais camera différente
# scene.active_camera=scene.objects['Aide-Camera']
# scene.objects['Apropos-Lien_projet'].color= couleur_lien
# scene.objects['Apropos-Lien_maquette'].color= couleur_lien
# scene.objects['Apropos-Lien_a4'].color= couleur_lien
# scene.objects['Apropos-Lien_blender'].color= couleur_lien
# scene.objects['Apropos-Lien_upbge'].color= couleur_lien
# scene.objects['Apropos-Lien_cc'].color= couleur_lien
# scene.objects['Apropos-Lien_gpl'].color= couleur_lien
# # Fermer la page d'aide
# def aide_fermer(cont):
# if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive :
# # bge.logic.addScene('Scene') # Bug Eevee -> même scene mais camera différente
# scene.active_camera=scene.objects['Camera']
# # Aller sur les liens
# def aide_apropos(cont):
# if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive :
# obj = cont.owner
# if obj.name == "Apropos-Lien_maquette" :
# webbrowser.open('https://www.a4.fr/wiki/')
# if obj.name == "Apropos-Lien_projet" :
# webbrowser.open('https://gitlab.com/blender-edutech')
# if obj.name == "Apropos-Lien_a4" :
# webbrowser.open('https://www.a4.fr')
# if obj.name == "Apropos-Lien_blender" :
# webbrowser.open('https://blender.org')
# if obj.name == "Apropos-Lien_upbge" :
# webbrowser.open('https://upbge.org')
# if obj.name == "Apropos-Lien_cc" :
# webbrowser.open('https://creativecommons.org/licenses/by-sa/4.0/')
# if obj.name == "Apropos-Lien_gpl" :
# webbrowser.open('https://www.gnu.org/licenses/gpl-3.0.html')
# # Le highlight des liens
# def aide_apropos_hl(cont):
# if cont.sensors['MO'].status == JUST_ACTIVATED :
# obj = cont.owner
# obj.color = couleur_lien_hl
# if cont.sensors['MO'].status == JUST_RELEASED :
# obj = cont.owner
# obj.color = couleur_lien