Soya/Python game skel-2
import sys, os, os.path
import soya
import soya.widget as widget
import soya.sdlconst as sdlconst
soya.init()
HERE = os.path.dirname(sys.argv[0])
soya.path.append(os.path.join(HERE, "data"))
class Level(soya.World):
 """Un niveau de jeu.
Level est une sous classe de soya.World."""
class Action:
 """Une action que le personnage peut faire."""
 def __init__(self, action):
 self.action = action
ACTION_WAIT = 0
ACTION_ADVANCE = 1
ACTION_ADVANCE_LEFT = 2
ACTION_ADVANCE_RIGHT = 3
ACTION_TURN_LEFT = 4
ACTION_TURN_RIGHT = 5
ACTION_GO_BACK = 6
ACTION_GO_BACK_LEFT = 7
ACTION_GO_BACK_RIGHT = 8
class KeyboardController:
 """Un controleur est un objet qui donne les ordres au personnage.
Ici, nous définissons un controlleur basé sur le clavuer, mais il existe aussi des controleurs basé sur un controleur basé sur la souris, ou l'IA. Notez que l'unique méthode appellée est "next", avec laquelle nous utilisons le générateur de python."""
 def __init__(self):
 self.left_key_down = self.right_key_down = self.up_key_down = self.down_key_down = 0
 def next(self):
 """Retourne l'action suivante"""
 for event in soya.process_event():
 if event[0] == sdlconst.KEYDOWN:
 if (event[1] == sdlconst.K_q) or (event[1] == sdlconst.K_ESCAPE):
 sys.exit() # Quit the game
 elif event[1] == sdlconst.K_LEFT: self.left_key_down = 1
 elif event[1] == sdlconst.K_RIGHT: self.right_key_down = 1
 elif event[1] == sdlconst.K_UP: self.up_key_down = 1
 elif event[1] == sdlconst.K_DOWN: self.down_key_down = 1
 elif event[0] == sdlconst.KEYUP:
 if event[1] == sdlconst.K_LEFT: self.left_key_down = 0
 elif event[1] == sdlconst.K_RIGHT: self.right_key_down = 0
 elif event[1] == sdlconst.K_UP: self.up_key_down = 0
 elif event[1] == sdlconst.K_DOWN: self.down_key_down = 0
 # Les gens disent que Python n'a pas de switch/select case et c'est faux !
 # Souvenez vous en lorsque vous codez un jeu de combat.
 return Action({
 (0, 0, 1, 0) : ACTION_ADVANCE,
 (1, 0, 1, 0) : ACTION_ADVANCE_LEFT,
 (0, 1, 1, 0) : ACTION_ADVANCE_RIGHT,
 (1, 0, 0, 0) : ACTION_TURN_LEFT,
 (0, 1, 0, 0) : ACTION_TURN_RIGHT,
 (0, 0, 0, 1) : ACTION_GO_BACK,
 (1, 0, 0, 1) : ACTION_GO_BACK_LEFT,
 (0, 1, 0, 1) : ACTION_GO_BACK_RIGHT,
 }.get((self.left_key_down, self.right_key_down, self.up_key_down, self.down_key_down), ACTION_WAIT))
class Character(soya.World):
 """Un personnage dans le jeu.
J'ai toujours considéré character.x, character.y, character.z (par exemple, les points de coordonées
 (0.0, 0.0, 0.0) donne dans le système de coordonnée du personnage : 
Point(character, 0.0, 0.0, 0.0)) pour être la position du personnage et non son centre.
Similairement je considère que le vecteur X du personnage (par exemple
Vector(character, 1.0, 0.0, 0.0)) pour être la direction de droite du vecteur y
(par exemple Vector(character, 0.0, 1.0, 0.0)) pour être la direction du haut et le vecteur -Z 
(par exemple Vector(character, 0.0, 0.0, -1.0)) pour être la direction du 'proche'.
(Pourquoi -Z et non Z ? Juste pour avoir indirectement le système de coordonnées !!!)"""
 def __init__(self, parent, controller):
 soya.World.__init__(self, parent)
 # Pour maintenant, le personnage est un simple cube
 self.set_model(soya.Model.get("cube"))
 # Désactivation du raypicking sur le personnage !!!
 # Nous avons besoin d'une caméra à la Tomb-Raider,
 # et pour la detection des collisions (voir prochaine leçon).
 self.solid = 0
 # Le controleur du personnage 
 self.controller = controller
 # La vitesse du personnage : un vecteur défini dans le système de coordonées du personnage.
 # Exemple, si vous voulez que le personnage avance, faire character.speed.z = -1.0
 self.speed = soya.Vector(self)
 # La vitesse de rotation du personnage.(Donc l'axe Y)
 self.rotation_speed = 0.0
 def begin_round(self):
 """Cette méthode est appellée par la MainLoop chaque fois qu'un round commence. Soya gère des rounds de 30ms par défaut, ceci signifie que le personnage sera -perform- à chaque action durant 30ms.
Actuellement, tous les round ne durent pas exactement 30ms, mais il retourne True à un point global de la vue. Exemple, 1000 rounds feront 1000 * 30ms."""
 # Appel d'une nouvelle action venant d'un controlleur, et la commence. 
 self.begin_action(self.controller.next())
 # Délégation du World, pour commencer le round.
 # Ceci doit être appellé après begin_action pour un meilleur effet visuel,
 # chaque appel de begin_action peut influencer World.begin_round (exemple, si begin_action
 # débute une animation, voir leçon 4)
 soya.World.begin_round(self)
 def begin_action(self, action):
 """Cette méthode commence l'action ACTION. Il ne doit pas executer l'action
(voir advance_time pour ça). Mais il doit "décoder" cette action, et verifier pour tout collision qui peut se produire (pas encore, mais nous verrons ça dans la leçon 4).
begin_action se met dans le vecteur de vitesse et dans la variable flottante rotation_speed du personnage (sa vitesse et sa vitesse de rotation (axe Y))."""
 # Réinitialsation
 self.rotation_speed = self.speed.x = self.speed.y = self.speed.z = 0.0
 # Determination de la rotation du personnage
 if action.action in (ACTION_TURN_LEFT, ACTION_ADVANCE_LEFT, ACTION_GO_BACK_LEFT):
 self.rotation_speed = 5.0
 elif action.action in (ACTION_TURN_RIGHT, ACTION_ADVANCE_RIGHT, ACTION_GO_BACK_RIGHT):
 self.rotation_speed = -5.0
 # Determination de la vitesse du personnage
 if action.action in (ACTION_ADVANCE, ACTION_ADVANCE_LEFT, ACTION_ADVANCE_RIGHT):
 self.speed.z = -0.35
 elif action.action in (ACTION_GO_BACK, ACTION_GO_BACK_LEFT, ACTION_GO_BACK_RIGHT):
 self.speed.z = 0.2
 # Vous pouvez utiliser speed.x pour les pas/ mouvement latéraux,
 # et speed.y pour sauter ou tomber (voir leçon 5)
 def advance_time(self, proportion):
 """Cette méthode appelle une ou plusieurs fois entre 2 rounds.
PROPORTION est la proportion du round qui a été épuisé
(Exemple, 1.0 pour un round complet, 0.5 pour une moitié, ...).
Tous lse personnages bougeant DOIVENT se produire dans la méthode, dans l'odre pour prendre l'avantage de la gestion du temps de la MainLoop et prend le meilleur effet graphique."""
 soya.World.advance_time(self, proportion)
 self.add_mul_vector(proportion, self.speed)
 self.rotate_y(proportion * self.rotation_speed)

scene = soya.World()
try:
 level = soya.World.get("level_demo")
except ValueError:
 print»sys.stderr, 'the level of this demo is not yet generated, please run the game_skel-1.py tutorial'
 sys.exit(1)
scene.add(level)
character = Character(level, KeyboardController())
character.set_xyz(216.160568237, -7.93332195282, 213.817764282)
camera = soya.TravelingCamera(scene)
traveling = soya.ThirdPersonTraveling(character)
traveling.distance = 5.0
camera.add_traveling(traveling)
camera.zap()
camera.back = 70.0
soya.set_root_widget(widget.Group())
soya.root_widget.add(camera)
soya.root_widget.add(widget.FPSLabel())
soya.MainLoop(scene).main_loop()