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