Parlez-vous Android ? – Game Development – Alexandre THOMAS



Parlez-vous Android ? – Game Development – Alexandre THOMAS

0 0


game


On Github a-thomas / game

Parlez-vous Android ?

Game Development

By Excilys

Alexandre THOMAS

@AleksThomas

althomas@excilys.com

Développeur Android et JEE chez Excilys.

Formateur Android.

http://androidkickstartr.com

AndroidAnnotations

http://androidannotations.org/

Talks

  • 2012: Devoxx France, Devoxx Anvers, PAUG, Open World Forum.
  • 2013: DevConfRussia, DroidCon Paris.

Sommaire

Introduction Android et les jeux-vidéos Graphics Multitouch API Système de fichiers Audio Les Bitmaps Bonus: Accéléromètre et KeyEvents Projet SpaceShip

Introduction

Publiée par NBC News.

Publié par NBC News. L'une de 2005 lors des funérailles de Jean-Paul II, l'autre de 2013, pendant la présentation du nouveau pape François.
http://www.android.com/devices/ Énormément de devices, pas mal de choses à gérer du point de vue du développeur. C'est pas toujours facile. Réel tournant dans le jeu vidéo, démocratisation du jeux vidéo. Révolution du jeux-vidéo...
http://www.ouya.tv/

Les jeux vidéos sous Android

En règle générale,

  • Une activity
  • Une view
  • Gestion manuel des changements de configuration
<activity android:name=".MainActivity"
android:screenOrientation="portrait"
android:configChanges="keyboard|keyboardHidden|orientation">
C'est une approche complètement différente du développement d'applications classiques où l'on utiliser les composants (widget) fournis par le framework. En jeux-vidéo, on dessine tout.

Cycle de vie

OnCreate() on l'utilise comme par habitude pour initialiser nos vues, ou pour récupérer les instances des composants graphiques correspondant à nos vues. On vient se rattacher au cycle de vie de l'activity. Les deux seuls callback qui nous intéresse sont onPause() et onResume()

onResume = resume the game

onPause = pause the game

Graphics

Première méthode

public class CustomView extends View {
    // ...
 
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
 
        // make your changes
        canvas.drawRect(...);
        canvas.drawPath(...)
 
        canvas.restore();
    }
}

Important: Le rendu est fait sur l'UIThread.

La bonne méthode

SurfaceView

  • Vue contenant un objet Surface.
  • Permet d'effectuer le rendu sur un thread différent
  • SurfaceHolder
La SufaceView perce un trou dans la window dans laquelle elle est contenu pour laisser apparaître l'élément "Surface". On accède à la "Surface" via un SurfaceHolder. But: créer le rendu de la vue sur un autre thread qui est par défaut effectuer sur le main thread et ainsi gagner en perfs.

Code

Récupérer le canvas utilisé pour dessiner sur la "Surface"

Canvas canvas = getHolder().lockCanvas()

Affichage de la "Surface" à l'écran

getHolder().unlockCanvasAndPost(Canvas canvas)

Savoir si la surface est prête

SurfaceHolder.Callback.surfaceCreated(SurfaceHolder holder)
SurfaceHolder.Callback.surfaceDestroyed(SurfaceHolder holder)
ou
boolean isCreated = holder.getSurface().isValid();

Exercice

Relatif au projet "Spaceship" !

Exercice

Exercices d'entraînement.

Exercice

Dessiner dans une SurfaceView

Commencez par créer une nouvelle application Android. Créez une vue custom héritant de SurfaceView en partant du code diponible ici. Dessinez une forme (rectangle, cercle, ligne...) afin de vérifier que votre vue est fonctionnelle. Dessiner un triangle qui représentera notre "spaceship" en utilisant un Path.

N'oubliez pas d'effectuer le rendu dans un autre thread.

Multitouch API

uses-feature

<uses-feature android:name="android.hardware.touchscreen.multitouch" 
	android:required="true"/>

Permet de vérifier que le device est compatible.

Autres exemples:

<uses-feature android:name="android.hardware.bluetooth" />
<uses-feature android:name="android.hardware.camera" />
// Récupérer le type d'action
int action = event.getActionMasked();

// Récupérer l'index
int actionIndex = event.getActionIndex();

// Récupérer l'id du pointeur
int pointerId = event.getPointerId(actionIndex);
Différence entre index et pointeurId. L'index est l'index dans un tableau de données relatif au MotionEvent en cours. Le pointerId identifie un pointeur en particulier. A partir de l'index vous pouvez récupérer l'identifiant du pointeur. C'est par le biais de l'index que vous allez pouvoir accéder aux données de l'Event. actionIndex = id données du MotionEvent pointerId = id du pointeur

Actions possibles

switch (action) {
	case MotionEvent.ACTION_UP:
	    // premier touch uniquement
	case MotionEvent.ACTION_POINTER_UP:
	    // autres touch
	case MotionEvent.ACTION_DOWN:
	    // premier touch uniquement
	case MotionEvent.ACTION_POINTER_DOWN:
	    // autres touch
	case MotionEvent.ACTION_MOVE:
	    // tous les touch
}

Versions inférieures à l'api 8

La classe MotionEventCompat, disponible dans la librairie support-v4, intégre la nouvelle API.

Exercice

Multitouch

Dessinez autant de cercles qu'il y a de doigts en contact de l'écran. Supprimez-les au fur et à mesure que l'utilisateur enlève ses doigts. Bonus: faites en sorte que l'utilisateur puisse déplacer les cercles.

Veillez à avoir compris la différence entre actionIndex et pointerId

Exercice

Intégrons le multitouch à notre jeu

Faites en sorte que le triangle avance vers le premier doigt de l'utilisateur. Dessinez un cercle à l'endroit où l'utilisateur posera son 2e doigt.

Système de fichiers

Les Ressources

  • Ressources du dossier res/: layout, values, animator, anim, drawables...
  • Assets

Les Assets

Bien plus flexible. Liberté des sous-dossiers.
Your project/
|_ assets/
   |_music/
   |_bfx/
   |_image/
|_src/
|_res/
Susceptible d'avoir un paquet de fichiers pour tout ce qui est images / music / sons. Il est nécessaire de travailler avec une hiérarchie de dossier bien organisé. Le système de ressources de base ne permet pas cette flexibilité. Res folder: si dossier pas supporté -> compile error placer des fichiers ressources dans un sous-dossier d'un dossier de ressources (layout ou autre) => dossier ignoré mais pas de compile errors Assets => flexibilité

Code

try {
    // à partir d'un Context on récupère l'AssetManager
    AssetManager assetManager = getAssets();

    // plusieurs méthodes à disposition pour charger un fichier 
    InputStream inputStream = assetManager.open("dir1/file_name");
    AssetFileDescriptor fd = assetManager.openFd("dir1/file_name");

} catch (IOException e) {
    Log.e(TAG, "problem during files loading", e);
}

Audio

Types de STREAM

  • STREAM_TYPE = flux
  • VOICE_CALL, MUSIC, NOTIFICATIONS...
  • Contrôle du volume:
    Activity.setVolumeControlStream (int streamType)
Flux sur lequel une application peut diffuser du son. Différencier les différents type de son. Sonnerie ? Voix ? Musique ? Notifications etc...

Musique de fond

On va utiliser le MediaPlayer.

try {
    // on set le fichier audio
    mediaPlayer.setDataSource(...);

    // on prépare l'audio
    mediaPlayer.prepare();

    // on commence à jouer l'audio
    mediaPlayer.start();
    mediaPlayer.pause();
    mediaPlayer.stop();

} catch (IOException e) {
    Log.e(TAG, "Problem during audio loading", e);
}
Documentation

Sound effects

Besoin de jouer plusieurs son rapidement et simultanément.

SoundPool

  • Chargement en mémoire,
  • Peu de latence,
  • Jouer simultanément.
// Création d'un SoundPool
// 10 = nb max de sons joués simultanément
// STREAM_MUSIC = type de stream sur lequel seront joué les sons
SoundPool soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, O);
AssetFileDescriptor fd = assetManager.openFd("path_to_the_file");

// pré-charge le son
sound1 = soundPool.load(fd, 1);

// ...

// on joue le son correspondant à l'ID donné
soundPool.play(sound1, 1, 1, 0, 0, 1);

Exercice

Faites du bruit !

Créer une nouvelle classe SoundFXManager pour charger/jouer vos sons SFX. Créer une nouvelle classe MusicPlayer, responsable de jouer la musique de fond.

bfxr.net, pour générer des sons de jeux en ligne.

dig.ccmixter.org, pour télécharger des musiques de jeux libres de droit.

Audio Focus

Être un bon citoyen Android

AudioManager am = ...;

// On demande le focus de l'audio
am.requestAudioFocus(AudioManager.OnAudioFocusChangeListener l, int streamType, ...);

// On restitue le focus de l'audio
am.abandonAudioFocus(AudioManager.OnAudioFocusChangeListener l);

Code

// AudioManager.OnAudioFocusChangeListener
@Override
public void onAudioFocusChange(int focusChange) {
    if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
        // Récupération du son suite à une perte
        // on continue de jouer si on revient d'une pause
        // ou on rejoue le son depuis le début si c'était un stop
        // ou encore on restitue le niveau de volume
        // tout dépend du type de AUDIOFOCUS_LOSS
    } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
        // perte de l'audio focus pour une longue durée
        // mettre en stop
    } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
        // perte de l'audio focus pour une petite durée
        // mettre en pause
    } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
        // perte de l'audio focus pour une petite durée et 
        // possibilité de jouer un son en même temps
        // Baisser le volume
    }
}

Les bitmaps

Créer un bitmap

BitmapFactory.decodeFile("path_name");
//...
BitmapFactory.decodeFileDescriptor(aFileDescriptor);
//...
BitmapFactory.decodeResource(res, R.drawable.my_bitmap);

Dessiner un bitmap

Utiliser le canvas!

// Dessiner un bitmap
Canvas.drawBitmap(...);

Libérer un bitmap

Versions inférieures à Honeycomb 3.0 (Api 11)

Quand un Bitmap est inutilisé -> Bitmap.recycle()

Native heap vs Dalvik's heap.

Recycler un bitmap

Depuis Honeycomb: on peut recycler un bitmap.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inBitmap = recycledBitmap;
// ...
BitmapFactory.decodeFile("my_file_path", options);
// ou
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
// ...

Exercice

Ajouter des images.

http://opengameart.org/

http://www.cgtextures.com/

http://untamed.wild-refuge.net/rmxpresources.php?characters

http://www.lostgarden.com/

Accéléromètre et Key events (Facultatif)

THE END

Merci pour votre attention