Il s’agit maintenant d’utiliser le jetpack en simulant la gravité. Simulation naïve, mais simple.

Impulsion du jetpack

Pour afficher un sprite le hash doit comporter les éléments x, y, w, h et path. Mais on peut aussi y ajouter ce qu’on veut. La variable impulse retiendra l’impulsion du jetpack, en nombre de pixels.

  args.state.hero ||= {
    x: 600,
    y: 200,
    w: 7 * HERO_SCALE,
    h: 17 * HERO_SCALE,
    path: 'sprites/hero.png',
    impulse: 0,
  }

Lorsqu’on appuie sur la touche CONTROL du clavier ou la touche Y de la manette (ou A, B, X, ça dépend de la manette) on fait monter le personnage de 4 pixels. À chaque frame la valeur de impulse va diminuer un peu.

IMPULSE = 4 # Jetpack power
IMPULSE_DECREASE = 0.9 # Jetpack power ratio decrease per frame

  if args.inputs.keyboard.control || args.inputs.controller_one.y
    args.state.hero.impulse = IMPULSE
  end

  args.state.hero.impulse *= IMPULSE_DECREASE

La chute continuelle

Il est temps de mettre à jour la position y du personnage. Pour simuler une espèce de gravité le personnage tombe tout le temps avec l’ajout de -1.2.

FALL = -1.2 # Kind of gravity

  args.state.hero.y += FALL
  args.state.hero.y += args.state.hero.impulse

On surveille les bords de l’écran

On s’occupe aussi de garder le personnage dans les limites de l’écran. DragonRuby ajoute quelques méthodes à la classe Numeric, comme clamp :

  args.state.hero.x = args.state.hero.x.clamp(0, Grid.w - args.state.hero.w)
  args.state.hero.y = args.state.hero.y.clamp(0, Grid.h - args.state.hero.h)

Le programme complet

Pour le moment le programme entier ressemble à ce qui suit :

# https://github.com/lkdjiin/jetpack-hero/tree/1c840ab53dd0d6dc8947c4efe075a78fc5047db8

HERO_SCALE = 4 # Image ratio
FALL = -1.2 # Kind of gravity
RL_SPEED = 5 # Right/left speed
IMPULSE = 4 # Jetpack power
IMPULSE_DECREASE = 0.9 # Jetpack power ratio decrease per frame

def tick args
  args.state.hero ||= {
    x: 600,
    y: 200,
    w: 7 * HERO_SCALE,
    h: 17 * HERO_SCALE,
    path: 'sprites/hero.png',
    impulse: 0,
  }

  if args.inputs.left
    args.state.hero.x -= RL_SPEED
  elsif args.inputs.right
    args.state.hero.x += RL_SPEED
  end

  if args.inputs.keyboard.control || args.inputs.controller_one.y
    args.state.hero.impulse = IMPULSE
  end

  args.state.hero.impulse *= IMPULSE_DECREASE
  args.state.hero.y += FALL
  args.state.hero.y += args.state.hero.impulse

  args.state.hero.x = args.state.hero.x.clamp(0, Grid.w - args.state.hero.w)
  args.state.hero.y = args.state.hero.y.clamp(0, Grid.h - args.state.hero.h)

  args.outputs.solids << { x: 0, y: 0, w: 1280, h: 720, r: 0, g: 0, b: 0 }
  args.outputs.sprites << args.state.hero
end

Pas encore 40 lignes et je trouve déjà que ça devient le boxon :D

Du rangement

Dans un moteur de jeu, et même dans un jeu sans moteur, on retrouve d’une manière ou d’une autre les quatre parties : initialisation, gestion des sorties, gestion des entrées et calcul. Avec DragonRuby le niveau 1 de la structuration d’un programme (juste au-dessus du niveau 0 : «on fourre tout dans tick») est d’utiliser le pattern suivant :

def tick(args)
  defaults(args)
  render(args)
  input(args)
  calc(args)
end

On explose tick en quatre méthodes qui s’occuperont seulement de ce qui les concernent. Voici donc le programme final pour aujourd’hui :

# https://github.com/lkdjiin/jetpack-hero/tree/946a9b9071a86acb3dc4ae2b9c7bef4d5448bf39

HERO_SCALE = 4 # Image ratio
FALL = -1.2 # Kind of gravity
RL_SPEED = 5 # Right/left speed
IMPULSE = 4 # Jetpack power
IMPULSE_DECREASE = 0.9 # Jetpack power ratio decrease per frame

def tick(args)
  defaults(args)
  render(args)
  input(args)
  calc(args)
end

def defaults(args)
  args.state.hero ||= {
    x: 600,
    y: 200,
    w: 7 * HERO_SCALE,
    h: 17 * HERO_SCALE,
    path: 'sprites/hero.png',
    impulse: 0,
  }
end

def render(args)
  args.outputs.solids << { x: 0, y: 0, w: 1280, h: 720, r: 0, g: 0, b: 0 }
  args.outputs.sprites << args.state.hero
end

def input(args)
  if args.inputs.left
    args.state.hero.x -= RL_SPEED
  elsif args.inputs.right
    args.state.hero.x += RL_SPEED
  end

  if args.inputs.keyboard.control || args.inputs.controller_one.y
    args.state.hero.impulse = IMPULSE
  end
end

def calc(args)
  args.state.hero.impulse *= IMPULSE_DECREASE
  args.state.hero.y += FALL
  args.state.hero.y += args.state.hero.impulse
  args.state.hero.x = args.state.hero.x.clamp(0, Grid.w - args.state.hero.w)
  args.state.hero.y = args.state.hero.y.clamp(0, Grid.h - args.state.hero.h)
end

Références

  1. Vous trouverez le code de Jetpack Hero sur github
  2. Documentation de DragonRuby


/ / / / / / / / / /


Cet article fait partie d’une série :

  1. Jetpack Hero
  2. Partie II
  3. Une platforme, des collisions
  4. Première animation du personnage
  5. Ajouter des platformes
  6. Du carburant pour le jetpack
  7. Collecte de minerai
  8. Effets sonores
  9. Du rangement avec la classe Game
  10. Apparition des aliens
  11. Tir du personnage
  12. On dégomme de l’alien
  13. GAME OVER
  14. Les aliens bougent enfin