Soya/Python game skel-3
import sys, os, os.path, math
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:
 """Les actions 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 donnant des ordres à un personnage.
Ici, nous utilisons un controlleur clavier, mais il pourrait très bien être controlé par
la souris ou l'IA.Notez que la seul méthode s'appelle "next", avec laquelle nous utilisons le
controleur."""
 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 futur."""
 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 disant que Python n'a pas de switch/select se trompent...
 # Souvenez vous en quand vous coderez 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))
 print "foo"
class Character(soya.World):
 print "foo"
 """Un personnage dans votre jeu."""
 def __init__(self, parent, controller):
 soya.World.__init__(self, parent)
 self.set_model(soya.Model.get("cube"))
 # Désactivation du raypicking sur le personnage !!!
 self.solid = 0
 self.controller = controller
 self.speed = soya.Vector(self)
 self.rotation_speed = 0.0
 # Cette variable est utilisé pour la détection des collisions.
 # radius est le rayon approximative d'une sphère autour du personnage,
 # et radius_y est pareil mais dans l'axe Y (vertical) seulement (le personnage
 # est souvent plus grand que large ;-).
 # Nous avons besoin de radius * sqrt(2)/2 < vitesse maximale (ici, 0.35)
 self.radius = 0.5
 self.radius_y = 1.0
 # Center est le point central du personnage (centré sur la sphère mentionnée
 # précédemment)
 self.center = soya.Point(self, 0.0, self.radius_y, 0.0)
 # Ces vecteurs sont les directions -X, X, -Y, Y, -Z, Z du personnage.
 self.left = soya.Vector(self, -1.0, 0.0, 0.0)
 self.right = soya.Vector(self, 1.0, 0.0, 0.0)
 self.down = soya.Vector(self, 0.0, -1.0, 0.0)
 self.up = soya.Vector(self, 0.0, 1.0, 0.0)
 self.front = soya.Vector(self, 0.0, 0.0, -1.0)
 self.back = soya.Vector(self, 0.0, 0.0, 1.0)
 def begin_round(self):
 self.begin_action(self.controller.next())
 soya.World.begin_round(self)
 def begin_action(self, action):
 """Cette méthode amorce l'action ACTION. Il n'effectue pas l'action
(pour cela, voir advance_time). Mais ceci ne fait que décoder l'action et maintenant, il vérifie
les collisions du personnage. Il ne fait que vérifier si le personnage est bien sur le sol
ground, et sinon, il le replace au bon endroit."""
 # Reset
 self.rotation_speed = self.speed.x = self.speed.z = 0.0
 if self.speed.y > 0.0: self.speed.y = 0.0
 # Si self.speed.y < 0.0, le personnage tombe et nous cherchons si il doit
 # continuer à tomber, et tombe de plus en plus vite,
 #mais nous ne remettons pas self.speed.y à 0.0.
 # Détermination 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
 # Détermination 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
 # Calcul du nouveau centre de la sphère du personnage après le mouvement
 # (C'est un point et une addition de vecteur, une translation)
 new_center = self.center + self.speed
 # Création d'un contexte 'raypicking', provenant d'un centre et d'un rayon.
 # Le contexte Raypicking est plus rapide si vous en avez plusieurs
 # dans la même région, en selectionnant tous les objets succeptibles
 # de se heurter au rayon.
 context = scene.RaypickContext(new_center, max(self.radius, 0.1 + self.radius_y))
 # Création du sol, et on verifie si le personnage tombe.
 # Dans ce cas, nous faisons un raypicking vers le bas.
 # Si totu se passe bien, la méthode raypick retourne un 
 # tuple : (collision point, collision normal).
 r = context.raypick(new_center, self.down, 0.1 + self.radius_y, 1, 1)
 if r:
 # Le personnage a ses pieds sur ou en dessous du sol
 # donc nous le positionnons sur le sol.
 ground, ground_normal = r
 ground.convert_to(self)
 self.speed.y = ground.y
 else:
 # Pas de sol => début de la chute
 # Testez la chute avec la crevasse derrière la deuxième maison
 # Nous incrémentons self.speed.y de 0.0 à -0.5 avec -0.05 par étapes.
 self.speed.y = max(self.speed.y - 0.05, -0.5)
 # Le mouvement (défini par le vecteur de vitesse) peut être impossible si
 # le personnage rencontre un mur.
 # collisions personnage - level
 for vec in (self.left, self.right, self.front, self.back, self.up):
 r = context.raypick(new_center, vec, self.radius, 1, 1)
 if r:
 # Le rayon recontre un mur => le personnage ne peut pas continuer le mouvement
 # planifié. Nous calculons le nouveau vecteur et nous l'ajoutons au vecteur
 # vitesse si bien que new_center (pour le raypick suivant, souvenez vous que
 # new_center = self.center + self.speed, ainsi si la vitesse a changé,
 # nous devons l'actualiser.
 collision, wall_normal = r
 hypo = vec.length() * self.radius - new_center.distance_to(collision)
 correction = wall_normal * hypo
 # Formule théorique, mais assez complexe et identique au résultat
 # angle = math.radians(180.0 - vec.angle_to(wall_normal))
 #correction = wall_normal * hypo * math.cos(angle)
 # Ajout du vecteur de correction au vecteur de vitesse et à new_center
 # (Point + addition vectorielle, donc translation)
 self.speed += correction
 new_center += correction
 def advance_time(self, proportion):
 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, 'Le level n\'est pas encoregénéré, veuillez lancer le tutorial game_skel-1.py'
 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()

Soya/Python animation de personnage 1
Mathématiques niveau seconde/Géométrie espace