Un aspetto di cui finora abbiamo parlato poco sono gli effetti sonori. Rispetto a quanto visto per la grafica, questo è un'aspetto che viene gestito in modo molto più semplice: all'interno della classe GameLevels
troviamo, infatti, il metodo initializeSounds()
, che di fatto si occupa di gestire il caricamento degli effetti sonori.
private void initializeSounds() {
if (Options.enableSfx) {
explosionSfx = MusicSfxLoader.newSound("sfx/explosion.wav");
shipFireSfx = MusicSfxLoader.newSound("sfx/laser.wav");
}
if (Options.enableMusic) {
music = MusicSfxLoader.newMusic("music/afterburner.ogg");
music.setLooping(true);
music.play();
music.setVolume(0.9f);
}
}
Attraverso la classe Options
verifichiamo quindi se effetti sonori e musica sono attivi. Se lo sono, li carichiamo attraverso la directory del progetto nel quale li abbiamo posizionati. A tal fine utilizziamo la classe MusicSfxLoader
. Non riportiamo qui il codice perchè è molto semplice e lineare; basti sapere che tutte le classi per l'audio risiedono nel package com.game.framework.sfx.
La schermata PhGameOptionsScreen
utilizza una serie di di GameObject
e GameImage
per realizzare i vari pulsanti dello schermo:
private GameObject background;
private GameObject musicButton;
private GameObject sfxButton;
private GameObject lowDifficultButton;
private GameObject medDifficultButton;
private GameObject hardDifficultButton;
private GameObject backButton;
private GameImage musicImage;
private GameImage sfxImage;
private GameImage diffLowImage;
private GameImage diffMedImage;
private GameImage diffHighImage;
Tra le opzioni, oltre a poter attivare o disattivare musica ed effetti sonori, possiamo anche impostare 3 diversi livelli di difficoltà: low, medium, hard. La logica fondamentale di questo schermo si svolge nel metodo update()
, dove rispondiamo all'evento di touch sui vari oggetti impostando le scelte sulla classe Options
:
@Override
public void update(float deltaTime) {
if(musicButton.isTouched(touchX,touchY) && actionUp) {
Options.enableMusic=!Options.enableMusic;
loadSoundImages();
} else if(sfxButton.isTouched(touchX, touchY) && actionUp) {
Options.enableSfx=!Options.enableSfx;
loadSoundImages();
} else if(lowDifficultButton.isTouched(touchX, touchY) ) {
Options.difficult=Options.Difficult.EASY;
loadDifficultImages();
} else if(medDifficultButton.isTouched(touchX, touchY)) {
Options.difficult=Options.Difficult.NORMAL;
loadDifficultImages();
} else if(hardDifficultButton.isTouched(touchX, touchY)) {
Options.difficult=Options.Difficult.HARD;
loadDifficultImages();
} else if(backButton.isTouched(touchX, touchY)) {
try {
Options.save();
} catch (ResourceException e) {
}
PhGameMenuScreen menuScreen=new PhGameMenuScreen(game, context, gl10);
game.setCurrentScreen(menuScreen);
}
}
La classe Options
ha una serie di metodi statici che consentono di leggere da file di testo i migliori 5 punteggi del giocatore, e le preferenze relative ai volumi di musica ed effetti sonori, così come il livello di difficoltà.
La schermata PhGameScoresScreen
è la più semplice. Utilizza solo un pulsante Back per tornare al menu e si occupa di disegnare la lista dei punteggi letti attraverso Options
. Per disegnare il testo, utilizza la classe PhGameFont
che legge dal nostro Atlas tutte le lettere e numeri disponibili, creando oggetti per esse. Attraverso il metodo drawText()
possiamo scrivere un testo in una certa posizine (x,y) utilizzando il font grafico dell'Atlas:
public class PhGameFont {
private GameObject[] chars;
public PhGameFont(){
chars = new GameObject[38];
//A-P
int x=1;
int y=1;
int width=32;
for(int i=0;i<16;i++){
chars[i]=new GameObject(-1, -1, 31, 31,
GameAtlas.getGameImage(x, y, 31, 31),
GameObject.CollisionMask.NONE);
x+=width;
}
//Q-Z
x=1;
y=33;
for(int i=16;i<26;i++){
chars[i]=new GameObject(-1, -1, 31, 31,
GameAtlas.getGameImage(x, y, 31, 31),
GameObject.CollisionMask.NONE);
x+=width;
}
x=321;
y=33;
width=16;
for(int i=26;i<38;i++){
chars[i]=new GameObject(-1, -1, 15, 28,
GameAtlas.getGameImage(x, y, 15, 31),
GameObject.CollisionMask.NONE);
x+=width;
}
}
public void drawText(StringBuilder word,float x,float y,GL10 gl){
float width=18;
for (int i=0;i<word.length();i++){
int character = word.charAt(i);
if(character>=65 && character<=90){
character-=65;
}else if(character>=48 && character<=57){
character-=48;
character+=26;
}else if(character==46){
character=36;
}else {
character=37;
}
GameObject object=chars[character];
object.setX(x);
object.setY(y);
x+=width;
object.draw(gl);
}
}
}
Nelle prossime lezioni vedremo come realizzare un minimo di intelligenza artificiale, in modo da capire cosa sta alla base della modalità di sparo delle astronavi nemiche.