Xavier Nayrac

Rubyiste accro au TDD, serial blogger, apprenti data scientist, heureux utilisateur de Vim, accordéoniste.
Si vous vous sentez particulièrement généreux, suivez moi sur Twitter.

Écrire un jeu en 2d avec Ruby et Gosu - partie 7

| Comments

Ça fait plus de 2 mois que je dois terminer ce jeu et cette série d’article, alors au boulot !

Pour cette nouvelle version je voudrais ajouter une musique différente pour le game over. Et aussi pouvoir enfin recommencer une nouvelle partie après un game over.

La totalité des articles:

  1. Installation de Gosu, affichage d’images statiques
  2. Déplacer le joueur et pluie de smileys
  3. Beep, fonte et collecte des smileys
  4. On s’occupe des vies
  5. Musique et game over
  6. Affichage selon un angle
  7. Plusieurs musiques et reset de la partie

Plusieurs musiques

Nous allons devoir gérer plusieurs musiques. Donnons leur des noms plus simple à manipuler que leurs paths :

song.rb
1
2
3
4
module Song
  GameOver = "assets/songs/In early time.ogg"
  Level1   = "assets/songs/Around the Bend.ogg"
end

Et avec quoi écoute-t-on de la musique ? Avec un song player :

song_player.rb
1
2
3
4
5
6
7
8
class SongPlayer
  def play(file)
    @song.stop if @song
    @song = Gosu::Song.new(file)
    @song.volume = 0.25
    @song.play(true)
  end
end

Dans la classe Window on remplace l’ancienne manière de jouer une musique :

1
2
3
@song = Gosu::Song.new("assets/songs/Around the Bend.ogg")
@song.volume = 0.25
@song.play(true)

par notre nouvelle abstraction :

1
2
@song_player = SongPlayer.new
@song_player.play(Song::Level1)

N’oubliez pas d’ajouter les require qui vont bien dans le fichier main.rb :

main.rb
1
2
require_relative 'song'
require_relative 'song_player'

Trouver un bon endroit où mettre le code pour changer la musique après un game over n’est pas si évident que ça. Le moment où l’on capte que le jeu est dans l’état game over est la méthode update_player :

1
2
3
4
def update_player
  @player.update(@items)
  @game_over = true if @player.lives <= 0
end

Et il semblerait vraiment bizarre de modifier la musique alors qu’on est sensé mettre à jour le joueur. Autrement dit, le code suivant ne me plait pas :

1
2
3
4
5
6
7
def update_player
  @player.update(@items)
  if @player.lives <= 0
    @game_over = true
    @song_player.play(Song::GameOver)
  end
end

À bien y réfléchir, le problème vient d’avant. On ne devrait pas changer l’état de @game_over ici, mais plutôt dans la méthode update :

window.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
class Window < Gosu::Window
  def update
    return if @game_over

    @game_over = true if @player.lives <= 0
    update_items
    update_player
  end

  def update_player
    @player.update(@items)
  end
end

Changer la musique se fera donc de cette manière :

1
2
3
4
5
6
7
8
9
10
11
def update
  return if @game_over

  if @player.lives <= 0
    @game_over = true
    @song_player.play(Song::GameOver)
  end

  update_items
  update_player
end

Après extraction d’une nouvelle méthode, nous obtenons un code plus clair :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def update
  return if @game_over

  update_game_over
  update_items
  update_player
end

def update_game_over
  return unless @player.lives <= 0

  @game_over = true
  @song_player.play(Song::GameOver)
end

Nouvelle partie

Ok, il est temps maintenant de pouvoir jouer une nouvelle partie après un game over. Pour cela, il faut regarder si le joueur appuie sur la touche espace pendant l’état game over, et dans ce cas faire un reset :

1
2
3
4
5
6
7
8
9
def update
  reset if @game_over && Button.space?

  return if @game_over

  update_game_over
  update_items
  update_player
end

On peut tout de suite extraire une méthode new_game? :

1
2
3
4
5
6
7
8
def update
  reset if new_game?
  # [...]
end

def new_game?
  @game_over && Button.space?
end

On peut maintenant réfléchir à la méthode reset, qui a son tour appellera la méthode reset du joueur. Ces deux méthodes sont évidentes :

window.rb
1
2
3
4
5
6
7
8
class Window < Gosu::Window
  def reset
    @items = []
    @player.reset
    @song_player.play(Song::Level1)
    @game_over = false
  end
end
player.rb
1
2
3
4
5
6
7
8
class Player
  def reset
    @score = 0
    @lives = 3
    @angle = 0.0
    @velocity = 0.0
  end
end

Pour terminer cette version, on va ajouter un message «press space to start» qui bouge pour attirer l’attention. Pour ça, on varie régulièrement d’une petite quantité la coordonnée y du message.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class UI
  # ...
  PressSpaceLow = 210
  PressSpaceHigh = 270

  def initialize
    # ...
    @y = 240
    @y_velocity = -0.3
  end

  private

  def draw_game_over
    @big_font.draw_rel("Game Over", WindowWidth / 2, WindowHeight / 3,
                       ZOrder::UI, 0.5, 0.5)
    @big_font.draw_rel("Press Space To Start", WindowWidth / 2, y,
                       ZOrder::UI, 0.5, 0.5)
  end

  def y
    @y += y_velocity
  end

  def y_velocity
    if @y < PressSpaceLow || @y > PressSpaceHigh
      @y_velocity = -@y_velocity
    end
    @y_velocity
  end
end

Pour finir, voici le contenu du jeu pour l’instant:

$ tree
.
├── assets
│   ├── fonts
│   │   └── VT323
│   │       ├── OFL.txt
│   │       └── VT323-Regular.ttf
│   ├── images
│   │   ├── background.png
│   │   ├── heart.gif
│   │   ├── player.png
│   │   ├── smiley-green.png
│   │   └── smiley-yellow.png
│   ├── songs
│   │   ├── Around the Bend.ogg
│   │   └── In early time.ogg
│   └── sound
│       ├── collect.wav
│       └── life-lost.wav
├── button.rb
├── main.rb
├── player.rb
├── smiley.rb
├── song_player.rb
├── song.rb
├── ui.rb
├── window.rb
└── z_order.rb

Le code et les assets se trouvent sur Github. La version précise pour cet article est la version 0.7.0.

Articles connexes

Commentaires