Bienvenidos sean a este post, hoy retomaremos a la app Asteroides y agregaremos la posibilidad de fragmentar nuestros asteroides cada vez que reciben un impacto, para ello debemos tener nuestra app, sino la tenes te recomiendo estos posts para crearla:
- Asteroides
- Acerca De
- Preferencias del usuario
- PreferenceFragment
- Almacenamiento de preferencias
- Lista de puntuaciones
- Lista de puntuaciones avanzada
- Asteroides, creando la actividad principal
- Continuamos con la actividad principal de Asteroides
- Tuneando a Asteroides
- Introduciendo movimiento en Asteroides
- Manejo de la nave con la pantalla tactil
- Añadiendo gestos a Asteroides
- Misil en Asteroides
- Efecto de audio en Asteroides
Nuestra primera modificacion sera en la clase VistaJuego donde modificaremos la siguiente linea:
Drawable drawableNave, drawableAsteroide, drawableMisil;
Por esta:
Drawable drawableNave, drawableMisil;
y Agregaremos al principio de la clase la siguiente linea:
private Drawable drawableAsteroide[] = new Drawable[3];
Esta modificacion cambiara a drawableAsteroide de ser una variable local a una array global de tres elementos, nuestra siguiente modificacion sera en el constructor donde deberemos modificar el siguiente bloque:
drawableAsteroide = context.getResources().getDrawable(R.drawable.asteroide1);
por este otro:
drawableAsteroide[0] = context.getResources().getDrawable(R.drawable.asteroide1);
drawableAsteroide[1] = context.getResources().getDrawable(R.drawable.asteroide2);
drawableAsteroide[2] = context.getResources().getDrawable(R.drawable.asteroide3);
Este se encargara de dibujarnos en pantalla a los tres asteroides, esto por medio de getResources(), el cual busca en los recursos y por medio de getDrawable() el cual es un medio para mostrar graficos, si cumplen los requisitos, en nuestra vista. La siguiente modificacion sera cambiar los asteroides vectoriales para mostrar el mismo efecto, para ello debemos modificar el siguiente bloque:
ShapeDrawable dAsteroide = new ShapeDrawable(
new PathShape(pathAsteroide,1,1));
dAsteroide.getPaint().setColor(Color.WHITE);
dAsteroide.getPaint().setStyle(Paint.Style.STROKE);
dAsteroide.setIntrinsicWidth(50);
dAsteroide.setIntrinsicHeight(50);
drawableAsteroide = dAsteroide;
Por el siguiente:
for(int i=0; i < 3; i++){
ShapeDrawable dAsteroide = new ShapeDrawable(
new PathShape(pathAsteroide,1,1));
dAsteroide.getPaint().setColor(Color.WHITE);
dAsteroide.getPaint().setStyle(Paint.Style.STROKE);
dAsteroide.setIntrinsicHeight(50 - i * 14);
dAsteroide.setIntrinsicWidth(50 - i * 14);
drawableAsteroide[i] = dAsteroide;
}
Este bloque se encargara de dibujar a nuestros tres asteroides, como pueden ver las variaciones son simples, el for para la cantidad de asteroides, el seteo de la altura y el ancho intrinseco donde estara afectado por el valor de i y por ultimo lo almacenamos en la ubicacion que corresponda de nuestro nuevo array, para la siguiente modificacion debemos ir al metodo destruyeAsteroide() donde modificaremos el actual metodo:
private void destruyeAsteroide(int i){
synchronized (asteroides){
asteroides.removeElementAt(i);
misilActivo = false;
soundPool.play(idExplosion,1,1,0,0,1);
}
}
Por este otro:
private void destruyeAsteroide(int i){
int tam;
if (asteroides.get(i).getDrawable()!=drawableAsteroide[2]){
if(asteroides.get(i).getDrawable()==drawableAsteroide[1]){
tam=2;
} else {
tam=1;
}
for(int n = 0; n < numFragmentos; n++){
Grafico asteroide = new Grafico(this,drawableAsteroide[tam]);
asteroide.setCenY(asteroides.get(i).getCenY());
asteroide.setCenX(asteroides.get(i).getCenX());
asteroide.setIncX(Math.random() * 7-2-tam);
asteroide.setIncY(Math.random() * 7-2-tam);
asteroide.setAngulo((int) (Math.random() * 360));
asteroide.setRotacion((int) (Math.random() * 8-4));
asteroides.add(asteroide);
}
}
synchronized (asteroides){
asteroides.removeElementAt(i);
misilActivo = false;
soundPool.play(idExplosion,1,1,0,0,1);
}
}
Esta modificacion nos ayudara a dividir el asteroide cada vez que sea impactado, el primer condicional verificara que el indice de asteroides sea distinto al tercer elemento de drawableAsteroide, en caso de ser verdadero verfiicara nuevamente si el indice de asteroides es igual al segundo elemento de drawableAsteroides si es verdad asignara el valor de dos a tam y en caso contrario el valor uno a tam. nuestro siguiente paso sera un bucle for el cual se encargara de crear en base a la cantidad de fragmentos asignados por medio de numFragmentos, luego creara un nuevo grafico al cual se seteara los valores de X e Y por medio de setCenX y setCenY y despues asignara los valores de incremento por medio de setIncX y setIncY, por ultimo asignara el angulo del elemento y la velocidad de rotacion, por ultimo agregara este asteroide a los asteroides, despues sera la misma rutina vista anteriormente para elimiar el asteroide y con un sonido.
Nuestra siguiente modificacion sera en esta linea:
Grafico asteroide = new Grafico(this, drawableAsteroide);
Por esta otra:
Grafico asteroide = new Grafico(this, drawableAsteroide[0]);
Esta ultima modificacion debemos hacerla por el cambio de variable a array que hicimos anteriormente en caso contrario nos dara un error de elementos no coincidentes, con todas estas modificaciones podemos probar nuestra app como vemos en el siguiente video
En el video podremos ver como se fragmenta los asteroides cada vez que son impactados por nuestro misil, manteniendo la explosion pertinente, veamos como quedo finalmente el codigo de nuestra clase VistaJuego:
package org.example.asteroides;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.graphics.drawable.shapes.RectShape;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.AudioManager;
import android.media.SoundPool;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import java.util.List;
import java.util.Vector;
public class VistaJuego extends View implements SensorEventListener {
// //// ASTEROIDES //////
private Drawable drawableAsteroide[] = new Drawable[3];
private Vector<Grafico> asteroides;
private int numAsteroides = 5;
private int numFragmentos = 3;
// //// NAVE //////
private Grafico nave;
private int giroNave;
private double aceleracionNave;
private static final int MAX_VELOCIDAD_NAVE = 20;
private static final int PASO_GIRO_NAVE = 5;
private static final float PASO_ACELERACION_NAVE = 0.5f;
// //// THREAD y TIEMPO //////
private ThreadJuego hilo = new ThreadJuego();
private static int PERIODO_PROCESO = 50;
private long ultimoProceso = 0;
// //// MISIL //////
private Grafico misil;
private static int PASO_VELOCIDAD_MISIL = 12;
private boolean misilActivo = false;
private int tiempoMisil;
// //// MULTIMEDIA //////
SoundPool soundPool;
int idDisparo, idExplosion;
public VistaJuego(Context context, AttributeSet attrs){
super(context, attrs);
Drawable drawableNave, drawableMisil;
SharedPreferences pref = PreferenceManager
.getDefaultSharedPreferences(getContext());
if (pref.getString("graficos","1").equals("0")){
Path pathAsteroide = new Path();
pathAsteroide.moveTo((float) 0.3, (float) 0.0);
pathAsteroide.lineTo((float) 0.6, (float) 0.0);
pathAsteroide.lineTo((float) 0.6, (float) 0.3);
pathAsteroide.lineTo((float) 0.8, (float) 0.2);
pathAsteroide.lineTo((float) 1.0, (float) 0.4);
pathAsteroide.lineTo((float) 0.8, (float) 0.6);
pathAsteroide.lineTo((float) 0.9, (float) 0.9);
pathAsteroide.lineTo((float) 0.8, (float) 1.0);
pathAsteroide.lineTo((float) 0.4, (float) 1.0);
pathAsteroide.lineTo((float) 0.0, (float) 0.6);
pathAsteroide.lineTo((float) 0.0, (float) 0.2);
pathAsteroide.lineTo((float) 0.3, (float) 0.0);
for(int i=0; i < 3; i++){
ShapeDrawable dAsteroide = new ShapeDrawable(
new PathShape(pathAsteroide,1,1));
dAsteroide.getPaint().setColor(Color.WHITE);
dAsteroide.getPaint().setStyle(Paint.Style.STROKE);
dAsteroide.setIntrinsicHeight(50 - i * 14);
dAsteroide.setIntrinsicWidth(50 - i * 14);
drawableAsteroide[i] = dAsteroide;
}
setBackgroundColor(Color.BLACK);
} else {
drawableAsteroide[0] = context.getResources()
.getDrawable(R.drawable.asteroide1);
drawableAsteroide[1] = context.getResources()
.getDrawable(R.drawable.asteroide2);
drawableAsteroide[2] = context.getResources()
.getDrawable(R.drawable.asteroide3);
}
if (pref.getString("graficos","1").equals("0")){
Path pathNave = new Path();
pathNave.moveTo((float)0.0,(float)0.0);
pathNave.lineTo((float)1.0,(float)0.5);
pathNave.lineTo((float)0.0,(float)1.0);
pathNave.lineTo((float)0.0,(float)0.0);
ShapeDrawable dNave = new ShapeDrawable(
new PathShape(pathNave,1,1));
dNave.getPaint().setColor(Color.WHITE);
dNave.getPaint().setStyle(Paint.Style.STROKE);
dNave.setIntrinsicHeight(15);
dNave.setIntrinsicWidth(20);
drawableNave = dNave;
} else {
drawableNave = context.getResources().getDrawable(
R.drawable.nave);
}
if (pref.getString("graficos","1").equals("0")){
ShapeDrawable dMisil = new ShapeDrawable(new RectShape());
dMisil.getPaint().setColor(Color.WHITE);
dMisil.getPaint().setStyle(Paint.Style.STROKE);
dMisil.setIntrinsicWidth(15);
dMisil.setIntrinsicHeight(3);
drawableMisil = dMisil;
} else {
drawableMisil = context.getResources().getDrawable(
R.drawable.misil1);
}
asteroides = new Vector<Grafico>();
nave = new Grafico(this,drawableNave);
misil = new Grafico(this,drawableMisil);
for(int i = 0; i < numAsteroides; i++) {
Grafico asteroide = new Grafico(this, drawableAsteroide[0]);
asteroide.setIncY(Math.random() * 4 - 2);
asteroide.setIncX(Math.random() * 4 - 2);
asteroide.setAngulo((int) (Math.random() * 360));
asteroide.setRotacion((int) (Math.random() * 8 - 4));
asteroides.add(asteroide);
}
SensorManager mSensorManager = (SensorManager)
context.getSystemService(Context.SENSOR_SERVICE);
List<Sensor> listaSensores = mSensorManager.getSensorList(
Sensor.TYPE_ORIENTATION);
if (!listaSensores.isEmpty()){
Sensor orientacionSensor = listaSensores.get(0);
mSensorManager.registerListener(this,orientacionSensor,
SensorManager.SENSOR_DELAY_GAME);
}
soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC,0);
idDisparo = soundPool.load(context,R.raw.disparo,0);
idExplosion = soundPool.load(context,R.raw.explosion,0);
}
@Override
public boolean onKeyDown(int codigoTecla, KeyEvent evento){
super.onKeyDown(codigoTecla, evento);
boolean procesada = true;
switch(codigoTecla) {
case KeyEvent.KEYCODE_DPAD_UP:
aceleracionNave = +PASO_ACELERACION_NAVE;
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
//Toast.makeText(this, "Izquierda", Toast.LENGTH_SHORT).show();
giroNave = -PASO_GIRO_NAVE;
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
giroNave = +PASO_GIRO_NAVE;
break;
case KeyEvent.KEYCODE_ENTER:
activaMisil();
break;
default:
procesada = false;
break;
}
return procesada;
}
@Override
public boolean onKeyUp(int codigoTecla, KeyEvent evento){
super.onKeyUp(codigoTecla, evento);
boolean procesada = true;
switch(codigoTecla) {
case KeyEvent.KEYCODE_DPAD_UP:
aceleracionNave = 0;
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
giroNave = 0;
break;
default:
procesada = false;
break;
}
return procesada;
}
@Override
protected void onSizeChanged(int ancho, int alto, int ancho_anter,
int alto_anter){
super.onSizeChanged(ancho,alto,ancho_anter,alto_anter);
nave.setCenY(alto/2);
nave.setCenX(ancho/2);
for(Grafico asteroide: asteroides){
do {
asteroide.setCenX((int) (Math.random() * ancho));
asteroide.setCenY((int) (Math.random() * alto));
} while(asteroide.distancia(nave) < (ancho+alto)/5);
}
ultimoProceso = System.currentTimeMillis();
hilo.start();
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
synchronized (asteroides){
for(Grafico asteroide: asteroides){
asteroide.dibujaGrafico(canvas);
}
}
nave.dibujaGrafico(canvas);
if (misilActivo)
misil.dibujaGrafico(canvas);
}
protected void actualizarFisica(){
long ahora = System.currentTimeMillis();
if (ultimoProceso + PERIODO_PROCESO > ahora){
return;
}
double retardo = (ahora - ultimoProceso) / PERIODO_PROCESO;
ultimoProceso = ahora;
nave.setAngulo((int) (nave.getAngulo() + giroNave * retardo));
double nIncX = nave.getIncX() + aceleracionNave *
Math.cos(Math.toRadians(nave.getAngulo())) * retardo;
double nIncY = nave.getIncY() + aceleracionNave *
Math.sin(Math.toRadians(nave.getAngulo())) * retardo;
if (Math.hypot(nIncX,nIncY) <= MAX_VELOCIDAD_NAVE){
nave.setIncX(nIncX);
nave.setIncY(nIncY);
}
nave.incrementaPos(retardo);
for(Grafico asteroide: asteroides){
asteroide.incrementaPos(retardo);
}
if(misilActivo){
misil.incrementaPos(retardo);
tiempoMisil-=retardo;
if(tiempoMisil<0){
misilActivo=false;
} else {
for(int i=0; i < asteroides.size(); i++)
if(misil.verificaColision(asteroides.elementAt(i))){
destruyeAsteroide(i);
break;
}
}
}
}
class ThreadJuego extends Thread {
@Override
public void run(){
while(true){
actualizarFisica();
}
}
}
private float mX=0, mY=0;
private boolean disparo=false;
@Override
public boolean onTouchEvent(MotionEvent event){
super.onTouchEvent(event);
float x = event.getX();
float y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
disparo=true;
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dy<6 && dx>6) {
giroNave = Math.round((x - mX) / 2);
disparo = false;
} else if(dx<6 && dy>6){
aceleracionNave = Math.round((mY - y)/2);
disparo = false;
}
break;
case MotionEvent.ACTION_UP:
giroNave = 0;
aceleracionNave = 0;
if (disparo) {
activaMisil();
}
break;
}
mX = x; mY = y;
return true;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy){}
private boolean hayValorinicial = false;
private float valorInicial;
@Override
public void onSensorChanged(SensorEvent evento){
float valor = evento.values[2];
if (!hayValorinicial){
valorInicial=valor;
hayValorinicial=true;
}
giroNave = (int) (valor - valorInicial)/3;
}
private void destruyeAsteroide(int i){
int tam;
if (asteroides.get(i).getDrawable()!=drawableAsteroide[2]){
if(asteroides.get(i).getDrawable()==drawableAsteroide[1]){
tam=2;
} else {
tam=1;
}
for(int n = 0; n < numFragmentos; n++){
Grafico asteroide = new Grafico(this,drawableAsteroide[tam]);
asteroide.setCenY(asteroides.get(i).getCenY());
asteroide.setCenX(asteroides.get(i).getCenX());
asteroide.setIncX(Math.random() * 7-2-tam);
asteroide.setIncY(Math.random() * 7-2-tam);
asteroide.setAngulo((int) (Math.random() * 360));
asteroide.setRotacion((int) (Math.random() * 8-4));
asteroides.add(asteroide);
}
}
synchronized (asteroides){
asteroides.removeElementAt(i);
misilActivo = false;
soundPool.play(idExplosion,1,1,0,0,1);
}
}
private void activaMisil(){
misil.setCenX(nave.getCenX());
misil.setCenY(nave.getCenY());
misil.setAngulo(nave.getAngulo());
misil.setIncX(Math.cos(Math.toRadians(misil.getAngulo())) *
PASO_VELOCIDAD_MISIL);
misil.setIncY(Math.sin(Math.toRadians(misil.getAngulo())) *
PASO_VELOCIDAD_MISIL);
tiempoMisil = (int) Math.min(this.getWidth() / Math.abs(misil.
getIncX()), this.getHeight() / Math.abs(misil.getIncY())) - 2;
misilActivo = true;
soundPool.play(idDisparo,1,1,1,0,1);
}
}
En resumen, hoy hemos visto como pudimos agregar un efecto mas hermoso para el impacto de nuestro misil con los asteroides, hemos visto como trabaja, hemos visto las modificaciones necesarias, espero les haya sido util sigueme en Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Tambien podes donar
Es para mantenimiento del sitio, gracias!
$1.50
Excelente explicación me ayudo mucho
Me gustaLe gusta a 1 persona
Muchisimas gracias a vos, esto me da mas fuerzas para seguir!!
Me gustaMe gusta